Update from https://crrev.com/304121 Includes DEPS updates and port of https://codereview.chromium.org/665223004 to accomodate skia API change on android. Review URL: https://codereview.chromium.org/723343002
diff --git a/DEPS b/DEPS index 6eadb5b..58fb063 100644 --- a/DEPS +++ b/DEPS
@@ -22,27 +22,27 @@ 'libcxx_revision': '48198f9110397fff47fe7c37cbfa296be7d44d3d', 'libcxxabi_revision': '4ad1009ab3a59fa7a6896d74d5e4de5885697f95', 'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac', - 'skia_revision': '7a10fb6bead0f63623307a7ff71b1dd323534a7f', + 'skia_revision': 'bc97c9378bf8b89cc17280a2a04a5c3a9405e6ab', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and V8 without interference from each other. - 'v8_revision': '11b7b24c91c903ee08b2b6b94f0bbb17b966dd92', # from svn revision 25090 + 'v8_revision': '43ced1bd1528ea8b4d9299d2c8f7c6a81921457b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - "angle_revision": "657cd684a615da648df6a851a916f11bb6f9a70e", + "angle_revision": "560eef1627abdef65f71021b906e57dc609f47fa", # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. - 'buildtools_revision': '51ca1f25a2ecea34069cb3ff35394dc53b6fd657', + 'buildtools_revision': 'c27f95bd1d9baaef70c879dea375090dd1496147', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '173f919d0ec99a1a973c9d3a82b474f761d5bce1', + 'pdfium_revision': '4dc95e74e1acc75f4eab08bc771874cd2a9c3a9b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. - 'boringssl_revision': '03a739d8d2cdc2560531a7446ead0f705409670a', + 'boringssl_revision': '2f3ba910a2bdb9e7d19e712a827cdc80c8d8c777', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling lss # and whatever else without interference from each other. @@ -50,7 +50,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling nss # and whatever else without interference from each other. - 'nss_revision': '87b96db4268293187d7cf741907a6d5d1d8080e0', + 'nss_revision': '258342ecf9c65105189092ef6339dc4e7779a7ae', } # Only these hosts are allowed for dependencies in this DEPS file. @@ -87,7 +87,7 @@ Var('chromium_git') + '/chromium/llvm-project/libcxxabi.git' + '@' + Var('libcxxabi_revision'), 'src/tools/grit': - Var('chromium_git') + '/external/grit-i18n.git' + '@' + '740badd5e3e44434a9a47b5d16749daac1e8ea80', # from svn revision 176 + Var('chromium_git') + '/external/grit-i18n.git' + '@' + 'a24a0e647bb718b3540db89864cf586b12331e82', # from svn revision 182 'src/v8': Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), @@ -126,7 +126,7 @@ 'https://boringssl.googlesource.com/boringssl.git' + '@' + Var('boringssl_revision'), 'src/tools/gyp': - Var('chromium_git') + '/external/gyp.git' + '@' + '487c0b6ae8b44932e45347211bca0e8387718436', # from svn revision 1998 + Var('chromium_git') + '/external/gyp.git' + '@' + 'b13d8f243da15ded051e87e663c4f2c2fcc5804c', # from svn revision 1994 }
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn index 510c3d3..d9ff4c5 100644 --- a/base/allocator/BUILD.gn +++ b/base/allocator/BUILD.gn
@@ -37,7 +37,7 @@ } } -if (!is_android) { +if (use_allocator == "tcmalloc") { # tcmalloc currently won't compile on Android. source_set("tcmalloc") { tcmalloc_dir = "//third_party/tcmalloc/chromium"
diff --git a/base/android/java/src/org/chromium/base/SystemMessageHandler.java b/base/android/java/src/org/chromium/base/SystemMessageHandler.java index 10f8d55..fd3dc5a 100644 --- a/base/android/java/src/org/chromium/base/SystemMessageHandler.java +++ b/base/android/java/src/org/chromium/base/SystemMessageHandler.java
@@ -5,12 +5,9 @@ package org.chromium.base; import android.os.Handler; -import android.os.Looper; import android.os.Message; -import android.os.MessageQueue; import android.util.Log; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -25,23 +22,31 @@ private long mMessagePumpDelegateNative = 0; private long mDelayedScheduledTimeTicks = 0; - // The following members are used to detect and trace the presence of sync - // barriers in Android's MessageQueue. Note that this detection is - // experimental, temporary and intended only for diagnostic purposes. - private MessageQueue mMessageQueue; - private Field mMessageQueueMessageField; - private Field mMessageTargetField; - private boolean mQueueHasSyncBarrier; - private long mSyncBarrierTraceId; + // Reflected API for marking a message as asynchronous. This is a workaround + // to provide fair Chromium task dispatch when served by the Android UI + // thread's Looper, avoiding stalls when the Looper has a sync barrier. + // Note: Use of this API is experimental and likely to evolve in the future. + private Method mMessageMethodSetAsynchronous; private SystemMessageHandler(long messagePumpDelegateNative) { mMessagePumpDelegateNative = messagePumpDelegateNative; - tryEnableSyncBarrierDetection(); + + try { + Class<?> messageClass = Class.forName("android.os.Message"); + mMessageMethodSetAsynchronous = messageClass.getMethod( + "setAsynchronous", new Class[]{boolean.class}); + } catch (ClassNotFoundException e) { + Log.e(TAG, "Failed to find android.os.Message class:" + e); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Failed to load Message.setAsynchronous method:" + e); + } catch (RuntimeException e) { + Log.e(TAG, "Exception while loading Message.setAsynchronous method: " + e); + } + } @Override public void handleMessage(Message msg) { - updateWhetherQueueHasBlockingSyncBarrier(); if (msg.what == DELAYED_SCHEDULED_WORK) { mDelayedScheduledTimeTicks = 0; } @@ -51,9 +56,7 @@ @SuppressWarnings("unused") @CalledByNative private void scheduleWork() { - updateWhetherQueueHasBlockingSyncBarrier(); - if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:immediateWorkBlocked"); - sendEmptyMessage(SCHEDULED_WORK); + sendMessage(obtainAsyncMessage(SCHEDULED_WORK)); } @SuppressWarnings("unused") @@ -63,97 +66,39 @@ removeMessages(DELAYED_SCHEDULED_WORK); } mDelayedScheduledTimeTicks = delayedTimeTicks; - updateWhetherQueueHasBlockingSyncBarrier(); - if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:delayedWorkBlocked"); - sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); + sendMessageDelayed(obtainAsyncMessage(DELAYED_SCHEDULED_WORK), millis); } @SuppressWarnings("unused") @CalledByNative private void removeAllPendingMessages() { - updateWhetherQueueHasBlockingSyncBarrier(); removeMessages(SCHEDULED_WORK); removeMessages(DELAYED_SCHEDULED_WORK); } - private void updateWhetherQueueHasBlockingSyncBarrier() { - if (mMessageQueue == null) return; - // As barrier detection is only used for tracing, early out when tracing - // is disabled to avoid any potential performance penalties. - if (!TraceEvent.enabled()) { - mQueueHasSyncBarrier = false; - return; + private Message obtainAsyncMessage(int what) { + Message msg = Message.obtain(); + msg.what = what; + if (mMessageMethodSetAsynchronous != null) { + // If invocation fails, assume this is indicative of future + // failures, and avoid log spam by nulling the reflected method. + try { + mMessageMethodSetAsynchronous.invoke(msg, true); + } catch (IllegalAccessException e) { + Log.e(TAG, "Illegal access to asynchronous message creation, disabling."); + mMessageMethodSetAsynchronous = null; + } catch (IllegalArgumentException e) { + Log.e(TAG, "Illegal argument for asynchronous message creation, disabling."); + mMessageMethodSetAsynchronous = null; + } catch (InvocationTargetException e) { + Log.e(TAG, "Invocation exception during asynchronous message creation, disabling."); + mMessageMethodSetAsynchronous = null; + } catch (RuntimeException e) { + Log.e(TAG, "Runtime exception during asynchronous message creation, disabling."); + mMessageMethodSetAsynchronous = null; + } } - Message queueHead = (Message) getField(mMessageQueue, mMessageQueueMessageField); - setqueueHasSyncBarrier(isSyncBarrierMessage(queueHead)); - } - - private boolean isSyncBarrierMessage(Message message) { - if (message == null) return false; - // Sync barrier messages have null targets. - return getField(message, mMessageTargetField) == null; - } - - private void tryEnableSyncBarrierDetection() { - assert mMessageQueue == null; - - boolean success = false; - try { - Method getQueueMethod = Looper.class.getMethod("getQueue", new Class[]{}); - mMessageQueue = (MessageQueue) getQueueMethod.invoke(getLooper()); - - mMessageQueueMessageField = mMessageQueue.getClass().getDeclaredField("mMessages"); - mMessageQueueMessageField.setAccessible(true); - - mMessageTargetField = Message.class.getDeclaredField("target"); - mMessageTargetField.setAccessible(true); - - mSyncBarrierTraceId = hashCode(); - - success = true; - } catch (NoSuchMethodException e) { - Log.e(TAG, "Failed to load method: " + e); - } catch (NoSuchFieldException e) { - Log.e(TAG, "Failed to load field: " + e); - } catch (InvocationTargetException e) { - Log.e(TAG, "Failed invocation: " + e); - } catch (IllegalAccessException e) { - Log.e(TAG, "Illegal access to reflected invocation: " + e); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Illegal argument to reflected invocation: " + e); - } catch (RuntimeException e) { - Log.e(TAG, e.toString()); - } finally { - if (!success) disableSyncBarrierDetection(); - } - } - - private void disableSyncBarrierDetection() { - Log.e(TAG, "Unexpected error with sync barrier detection, disabling."); - mMessageQueue = null; - mMessageQueueMessageField = null; - mMessageTargetField = null; - setqueueHasSyncBarrier(false); - } - - private void setqueueHasSyncBarrier(boolean queueHasSyncBarrier) { - if (queueHasSyncBarrier == mQueueHasSyncBarrier) return; - mQueueHasSyncBarrier = queueHasSyncBarrier; - if (mQueueHasSyncBarrier) { - TraceEvent.startAsync("SyncBarrier", mSyncBarrierTraceId); - } else { - TraceEvent.finishAsync("SyncBarrier", mSyncBarrierTraceId); - } - } - - private Object getField(Object object, Field field) { - try { - return field.get(object); - } catch (IllegalAccessException e) { - Log.e(TAG, "Failed field access: " + e); - disableSyncBarrierDetection(); - } - return null; + return msg; } @CalledByNative
diff --git a/base/android/linker/linker_jni.cc b/base/android/linker/linker_jni.cc index b3ed651..1a11639 100644 --- a/base/android/linker/linker_jni.cc +++ b/base/android/linker/linker_jni.cc
@@ -664,7 +664,7 @@ __FUNCTION__, library_name_c_str, apkfile_name_c_str); jboolean mappable = crazy_linker_check_library_is_mappable_in_zip_file( apkfile_name_c_str, library_name_c_str) == CRAZY_STATUS_SUCCESS; - LOG_INFO("%s: %s\n", __FUNCTION__, aligned ? "Aligned" : "NOT aligned"); + LOG_INFO("%s: %s\n", __FUNCTION__, mappable ? "Mappable" : "NOT mappable"); return mappable; }
diff --git a/base/base64.h b/base/base64.h index 43d8f76..def9b67 100644 --- a/base/base64.h +++ b/base/base64.h
@@ -12,11 +12,12 @@ namespace base { -// Encodes the input string in base64. +// Encodes the input string in base64. The encoding can be done in-place. BASE_EXPORT void Base64Encode(const StringPiece& input, std::string* output); // Decodes the base64 input string. Returns true if successful and false -// otherwise. The output string is only modified if successful. +// otherwise. The output string is only modified if successful. The decoding can +// be done in-place. BASE_EXPORT bool Base64Decode(const StringPiece& input, std::string* output); } // namespace base
diff --git a/base/base64_unittest.cc b/base/base64_unittest.cc index 9b23194..91651f4 100644 --- a/base/base64_unittest.cc +++ b/base/base64_unittest.cc
@@ -24,4 +24,17 @@ EXPECT_EQ(kText, decoded); } +TEST(Base64Test, InPlace) { + const std::string kText = "hello world"; + const std::string kBase64Text = "aGVsbG8gd29ybGQ="; + std::string text(kText); + + Base64Encode(text, &text); + EXPECT_EQ(kBase64Text, text); + + bool ok = Base64Decode(text, &text); + EXPECT_TRUE(ok); + EXPECT_EQ(text, kText); +} + } // namespace base
diff --git a/base/files/file.h b/base/files/file.h index 4110d51..7b6366c 100644 --- a/base/files/file.h +++ b/base/files/file.h
@@ -19,6 +19,7 @@ #include "base/base_export.h" #include "base/basictypes.h" #include "base/files/scoped_file.h" +#include "base/gtest_prod_util.h" #include "base/move.h" #include "base/time/time.h" @@ -26,6 +27,8 @@ #include "base/win/scoped_handle.h" #endif +FORWARD_DECLARE_TEST(FileTest, MemoryCorruption); + namespace base { class FilePath; @@ -296,12 +299,59 @@ static std::string ErrorToString(Error error); private: + FRIEND_TEST_ALL_PREFIXES(::FileTest, MemoryCorruption); + +#if defined(OS_POSIX) + // Encloses a single ScopedFD, saving a cheap tamper resistent memory checksum + // alongside it. This checksum is validated at every access, allowing early + // detection of memory corruption. + + // TODO(gavinp): This is in place temporarily to help us debug + // https://crbug.com/424562 , which can't be reproduced in valgrind. Remove + // this code after we have fixed this issue. + class MemoryCheckingScopedFD { + public: + MemoryCheckingScopedFD(); + MemoryCheckingScopedFD(int fd); + ~MemoryCheckingScopedFD(); + + bool is_valid() const { Check(); return file_.is_valid(); } + int get() const { Check(); return file_.get(); } + + void reset() { Check(); file_.reset(); UpdateChecksum(); } + void reset(int fd) { Check(); file_.reset(fd); UpdateChecksum(); } + int release() { + Check(); + int fd = file_.release(); + UpdateChecksum(); + return fd; + } + + private: + FRIEND_TEST_ALL_PREFIXES(::FileTest, MemoryCorruption); + + // Computes the checksum for the current value of |file_|. Returns via an + // out parameter to guard against implicit conversions of unsigned integral + // types. + void ComputeMemoryChecksum(unsigned int* out_checksum) const; + + // Confirms that the current |file_| and |file_memory_checksum_| agree, + // failing a CHECK if they do not. + void Check() const; + + void UpdateChecksum(); + + ScopedFD file_; + unsigned int file_memory_checksum_; + }; +#endif + void SetPlatformFile(PlatformFile file); #if defined(OS_WIN) win::ScopedHandle file_; #elif defined(OS_POSIX) - ScopedFD file_; + MemoryCheckingScopedFD file_; #endif Error error_details_;
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc index 43684b5..3d229e4 100644 --- a/base/files/file_posix.cc +++ b/base/files/file_posix.cc
@@ -483,6 +483,49 @@ } } +File::MemoryCheckingScopedFD::MemoryCheckingScopedFD() { + UpdateChecksum(); +} + +File::MemoryCheckingScopedFD::MemoryCheckingScopedFD(int fd) : file_(fd) { + UpdateChecksum(); +} + +File::MemoryCheckingScopedFD::~MemoryCheckingScopedFD() {} + +// static +void File::MemoryCheckingScopedFD::ComputeMemoryChecksum( + unsigned int* out_checksum) const { + // Use a single iteration of a linear congruentional generator (lcg) to + // provide a cheap checksum unlikely to be accidentally matched by a random + // memory corruption. + + // By choosing constants that satisfy the Hull-Duebell Theorem on lcg cycle + // length, we insure that each distinct fd value maps to a distinct checksum, + // which maximises the utility of our checksum. + + // This code uses "unsigned int" throughout for its defined modular semantics, + // which implicitly gives us a divisor that is a power of two. + + const unsigned int kMultiplier = 13035 * 4 + 1; + COMPILE_ASSERT(((kMultiplier - 1) & 3) == 0, pred_must_be_multiple_of_four); + const unsigned int kIncrement = 1595649551; + COMPILE_ASSERT(kIncrement & 1, must_be_coprime_to_powers_of_two); + + *out_checksum = + static_cast<unsigned int>(file_.get()) * kMultiplier + kIncrement; +} + +void File::MemoryCheckingScopedFD::Check() const { + unsigned int computed_checksum; + ComputeMemoryChecksum(&computed_checksum); + CHECK_EQ(file_memory_checksum_, computed_checksum) << "corrupted fd memory"; +} + +void File::MemoryCheckingScopedFD::UpdateChecksum() { + ComputeMemoryChecksum(&file_memory_checksum_); +} + void File::SetPlatformFile(PlatformFile file) { DCHECK(!file_.is_valid()); file_.reset(file);
diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc index 6616f6a..3bc2db6 100644 --- a/base/files/file_unittest.cc +++ b/base/files/file_unittest.cc
@@ -5,6 +5,7 @@ #include "base/files/file.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" @@ -466,3 +467,71 @@ EXPECT_EQ(0, info.size); } #endif // defined(OS_WIN) + +#if defined(OS_POSIX) && defined(GTEST_HAS_DEATH_TEST) +TEST(FileTest, MemoryCorruption) { + { + // Test that changing the checksum value is detected. + base::File file; + EXPECT_NE(file.file_.file_memory_checksum_, + implicit_cast<unsigned int>(file.GetPlatformFile())); + file.file_.file_memory_checksum_ = file.GetPlatformFile(); + EXPECT_DEATH(file.IsValid(), ""); + + file.file_.UpdateChecksum(); // Do not crash on File::~File(). + } + + { + // Test that changing the file descriptor value is detected. + base::File file; + file.file_.file_.reset(17); + EXPECT_DEATH(file.IsValid(), ""); + + // Do not crash on File::~File(). + ignore_result(file.file_.file_.release()); + file.file_.UpdateChecksum(); + } + + { + // Test that GetPlatformFile() checks for corruption. + base::File file; + file.file_.file_memory_checksum_ = file.GetPlatformFile(); + EXPECT_DEATH(file.GetPlatformFile(), ""); + + file.file_.UpdateChecksum(); // Do not crash on File::~File(). + } + + { + // Test that the base::File destructor checks for corruption. + scoped_ptr<base::File> file(new File()); + file->file_.file_memory_checksum_ = file->GetPlatformFile(); + EXPECT_DEATH(file.reset(), ""); + + // Do not crash on this thread's destructor call. + file->file_.UpdateChecksum(); + } + + { + // Test that the base::File constructor checks for corruption. + base::File file; + file.file_.file_memory_checksum_ = file.GetPlatformFile(); + EXPECT_DEATH(File f(file.Pass()), ""); + + file.file_.UpdateChecksum(); // Do not crash on File::~File(). + } + + { + // Test that doing IO checks for corruption. + base::File file; + file.file_.file_.reset(17); // A fake open FD value. + + EXPECT_DEATH(file.Seek(File::FROM_BEGIN, 0), ""); + EXPECT_DEATH(file.Read(0, NULL, 0), ""); + EXPECT_DEATH(file.ReadAtCurrentPos(NULL, 0), ""); + EXPECT_DEATH(file.Write(0, NULL, 0), ""); + + ignore_result(file.file_.file_.release()); + file.file_.UpdateChecksum(); + } +} +#endif // defined(OS_POSIX)
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc index b8c0eeb..0bf41a5 100644 --- a/base/files/file_util_posix.cc +++ b/base/files/file_util_posix.cc
@@ -28,8 +28,6 @@ #include <glib.h> // for g_get_home_dir() #endif -#include <fstream> - #include "base/basictypes.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" @@ -428,7 +426,7 @@ bool SetPosixFilePermissions(const FilePath& path, int mode) { ThreadRestrictions::AssertIOAllowed(); - DCHECK((mode & ~FILE_PERMISSION_MASK) == 0); + DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0); // Calls stat() so that we can preserve the higher bits like S_ISGID. stat_wrapper_t stat_buf;
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc index ad5a9d5..e17d450 100644 --- a/base/json/json_reader.cc +++ b/base/json/json_reader.cc
@@ -31,7 +31,7 @@ "Dictionary keys must be quoted."; JSONReader::JSONReader() - : parser_(new internal::JSONParser(JSON_PARSE_RFC)) { + : JSONReader(JSON_PARSE_RFC) { } JSONReader::JSONReader(int options)
diff --git a/base/mac/bind_objc_block.h b/base/mac/bind_objc_block.h index 9deb2d2..c31f26e 100644 --- a/base/mac/bind_objc_block.h +++ b/base/mac/bind_objc_block.h
@@ -21,51 +21,32 @@ // BindBlock(^(const std::string& arg0, const std::string& arg1) { // ... // }); +// +// These variadic templates will accommodate any number of arguments, however +// the underlying templates in bind_internal.h and callback.h are limited to +// seven total arguments, and the bound block itself is used as one of these +// arguments, so functionally the templates are limited to binding blocks with +// zero through six arguments. namespace base { namespace internal { -// Helper functions to run the block contained in the parameter. -template<typename R> -R RunBlock(base::mac::ScopedBlock<R(^)()> block) { - R(^extracted_block)() = block.get(); - return extracted_block(); -} - -template<typename R, typename A1> -R RunBlock(base::mac::ScopedBlock<R(^)(A1)> block, A1 a) { - R(^extracted_block)(A1) = block.get(); - return extracted_block(a); -} - -template<typename R, typename A1, typename A2> -R RunBlock(base::mac::ScopedBlock<R(^)(A1, A2)> block, A1 a, A2 b) { - R(^extracted_block)(A1, A2) = block.get(); - return extracted_block(a, b); +// Helper function to run the block contained in the parameter. +template<typename R, typename... Args> +R RunBlock(base::mac::ScopedBlock<R(^)(Args...)> block, Args... args) { + R(^extracted_block)(Args...) = block.get(); + return extracted_block(args...); } } // namespace internal -// Construct a callback with no argument from an objective-C block. -template<typename R> -base::Callback<R(void)> BindBlock(R(^block)()) { - return base::Bind(&base::internal::RunBlock<R>, - base::mac::ScopedBlock<R(^)()>(Block_copy(block))); -} - -// Construct a callback with one argument from an objective-C block. -template<typename R, typename A1> -base::Callback<R(A1)> BindBlock(R(^block)(A1)) { - return base::Bind(&base::internal::RunBlock<R, A1>, - base::mac::ScopedBlock<R(^)(A1)>(Block_copy(block))); -} - -// Construct a callback with two arguments from an objective-C block. -template<typename R, typename A1, typename A2> -base::Callback<R(A1, A2)> BindBlock(R(^block)(A1, A2)) { - return base::Bind(&base::internal::RunBlock<R, A1, A2>, - base::mac::ScopedBlock<R(^)(A1, A2)>(Block_copy(block))); +// Construct a callback from an objective-C block with up to six arguments (see +// note above). +template<typename R, typename... Args> +base::Callback<R(Args...)> BindBlock(R(^block)(Args...)) { + return base::Bind(&base::internal::RunBlock<R, Args...>, + base::mac::ScopedBlock<R(^)(Args...)>(Block_copy(block))); } } // namespace base
diff --git a/base/mac/bind_objc_block_unittest.mm b/base/mac/bind_objc_block_unittest.mm index c72fd4a..5d15eba 100644 --- a/base/mac/bind_objc_block_unittest.mm +++ b/base/mac/bind_objc_block_unittest.mm
@@ -64,4 +64,36 @@ EXPECT_EQ(result, "fortytwo"); } +TEST(BindObjcBlockTest, TestThreeArguments) { + std::string result; + std::string* ptr = &result; + base::Callback<void(const std::string&, + const std::string&, + const std::string&)> c = + base::BindBlock(^(const std::string& a, + const std::string& b, + const std::string& c) { + *ptr = a + b + c; + }); + c.Run("six", "times", "nine"); + EXPECT_EQ(result, "sixtimesnine"); +} + +TEST(BindObjcBlockTest, TestSixArguments) { + std::string result1; + std::string* ptr = &result1; + int result2; + int* ptr2 = &result2; + base::Callback<void(int, int, const std::string&, const std::string&, + int, const std::string&)> c = + base::BindBlock(^(int a, int b, const std::string& c, + const std::string& d, int e, const std::string& f) { + *ptr = c + d + f; + *ptr2 = a + b + e; + }); + c.Run(1, 2, "infinite", "improbability", 3, "drive"); + EXPECT_EQ(result1, "infiniteimprobabilitydrive"); + EXPECT_EQ(result2, 6); +} + } // namespace
diff --git a/base/memory/scoped_ptr_unittest.cc b/base/memory/scoped_ptr_unittest.cc index 3f169a7..ca7cfbf 100644 --- a/base/memory/scoped_ptr_unittest.cc +++ b/base/memory/scoped_ptr_unittest.cc
@@ -669,7 +669,9 @@ TEST(ScopedPtrTest, SelfResetWithCustomDeleterOptOut) { // A custom deleter should be able to opt out of self-reset abort behavior. struct NoOpDeleter { +#if !defined(NDEBUG) typedef void AllowSelfReset; +#endif inline void operator()(int*) {} }; scoped_ptr<int> owner(new int);
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index 01402d0..2f4a03c 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc
@@ -106,9 +106,11 @@ typedef MessagePumpLibevent MessagePumpForIO; #endif +#if !defined(OS_NACL_SFI) MessagePumpForIO* ToPumpIO(MessagePump* pump) { return static_cast<MessagePumpForIO*>(pump); } +#endif // !defined(OS_NACL_SFI) } // namespace
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 5ed9d9e..43e7462 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h
@@ -286,7 +286,7 @@ base::HistogramBase::kUmaTargetedHistogramFlag)) // The samples should always be strictly less than |boundary_value|. For more -// details, see the comment for the |HISTOGRAM_ENUMERATION| macro, above. +// details, see the comment for the |LOCAL_HISTOGRAM_ENUMERATION| macro, above. #define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \ base::HistogramBase::kUmaTargetedHistogramFlag)
diff --git a/base/observer_list.h b/base/observer_list.h index c77ec15..ef45269 100644 --- a/base/observer_list.h +++ b/base/observer_list.h
@@ -100,7 +100,8 @@ // Remove an observer from the list if it is in the list. void RemoveObserver(ObserverType* obs); - bool HasObserver(ObserverType* observer) const; + // Determine whether a particular observer is in the list. + bool HasObserver(const ObserverType* observer) const; void Clear(); @@ -176,7 +177,8 @@ } template <class ObserverType> -bool ObserverListBase<ObserverType>::HasObserver(ObserverType* observer) const { +bool ObserverListBase<ObserverType>::HasObserver( + const ObserverType* observer) const { for (size_t i = 0; i < observers_.size(); ++i) { if (observers_[i] == observer) return true;
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc index 11f59be..65ef934 100644 --- a/base/observer_list_unittest.cc +++ b/base/observer_list_unittest.cc
@@ -182,6 +182,9 @@ observer_list.AddObserver(&a); observer_list.AddObserver(&b); + EXPECT_TRUE(observer_list.HasObserver(&a)); + EXPECT_FALSE(observer_list.HasObserver(&c)); + FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); observer_list.AddObserver(&evil);
diff --git a/base/profiler/scoped_profile.cc b/base/profiler/scoped_profile.cc index 8b0ae59..4f8bc2d 100644 --- a/base/profiler/scoped_profile.cc +++ b/base/profiler/scoped_profile.cc
@@ -11,15 +11,6 @@ namespace tracked_objects { -ScopedProfile::ScopedProfile(const Location& location) - : birth_(ThreadData::TallyABirthIfActive(location)) { - if (!birth_) - return; - - ThreadData::PrepareForStartOfRun(birth_); - stopwatch_.Start(); -} - ScopedProfile::ScopedProfile(const Location& location, Mode mode) : birth_(NULL) { if (mode == DISABLED)
diff --git a/base/profiler/scoped_profile.h b/base/profiler/scoped_profile.h index 6a76486..c1e2830 100644 --- a/base/profiler/scoped_profile.h +++ b/base/profiler/scoped_profile.h
@@ -25,10 +25,10 @@ // Defines the containing scope as a profiled region. This allows developers to // profile their code and see results on their about:profiler page, as well as // on the UMA dashboard. -#define TRACK_RUN_IN_THIS_SCOPED_REGION(dispatch_function_name) \ - ::tracked_objects::ScopedProfile LINE_BASED_VARIABLE_NAME_FOR_PROFILING( \ - FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name)) - +#define TRACK_RUN_IN_THIS_SCOPED_REGION(dispatch_function_name) \ + ::tracked_objects::ScopedProfile LINE_BASED_VARIABLE_NAME_FOR_PROFILING( \ + FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name), \ + ::tracked_objects::ScopedProfile::ENABLED) namespace tracked_objects { class Births; @@ -42,8 +42,6 @@ ENABLED // Create and tally a task. }; - // TODO(vadimt): Remove this constructor. - explicit ScopedProfile(const Location& location); ScopedProfile(const Location& location, Mode mode); ~ScopedProfile();
diff --git a/base/profiler/scoped_tracker.cc b/base/profiler/scoped_tracker.cc index 1d4f0bf..26b17c0 100644 --- a/base/profiler/scoped_tracker.cc +++ b/base/profiler/scoped_tracker.cc
@@ -15,7 +15,7 @@ // Executes |callback|, augmenting it with provided |location|. void ExecuteAndTrackCallback(const Location& location, const base::Closure& callback) { - ScopedProfile tracking_profile(location); + ScopedProfile tracking_profile(location, ScopedProfile::ENABLED); callback.Run(); }
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc index 44846a1..a49e777 100644 --- a/base/test/launcher/unit_test_launcher.cc +++ b/base/test/launcher/unit_test_launcher.cc
@@ -479,15 +479,6 @@ fflush(stdout); force_single_process = true; } - - if (RunningOnValgrind()) { - fprintf(stdout, - "Valgrind detected, switching to single process mode.\n" - "Pass --test-launcher-debug-launcher to valgrind the launcher " - "itself.\n"); - fflush(stdout); - force_single_process = true; - } } if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc index d9e2bd9..d97a3f4 100644 --- a/base/threading/platform_thread_linux.cc +++ b/base/threading/platform_thread_linux.cc
@@ -27,6 +27,7 @@ namespace { +#if !defined(OS_NACL) int ThreadNiceValue(ThreadPriority priority) { switch (priority) { case kThreadPriority_RealtimeAudio: @@ -42,6 +43,7 @@ return 0; } } +#endif // !defined(OS_NACL) } // namespace
diff --git a/base/time/time.h b/base/time/time.h index 641f465..9cb007e 100644 --- a/base/time/time.h +++ b/base/time/time.h
@@ -232,6 +232,10 @@ static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; + // The representation of Jan 1, 1970 UTC in microseconds since the + // platform-dependent epoch. + static const int64 kTimeTToMicrosecondsOffset; + #if !defined(OS_WIN) // On Mac & Linux, this value is the delta from the Windows epoch of 1601 to // the Posix delta of 1970. This is used for migrating between the old @@ -492,10 +496,6 @@ bool is_local, Time* parsed_time); - // The representation of Jan 1, 1970 UTC in microseconds since the - // platform-dependent epoch. - static const int64 kTimeTToMicrosecondsOffset; - // Time in microseconds in UTC. int64 us_; };
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index c69fada..5e30762 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc
@@ -116,7 +116,7 @@ void DeathData::RecordDeath(const int32 queue_duration, const int32 run_duration, - int32 random_number) { + const uint32 random_number) { // We'll just clamp at INT_MAX, but we should note this in the UI as such. if (count_ < INT_MAX) ++count_; @@ -307,7 +307,7 @@ (void)VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(&random_number_, sizeof(random_number_)); MSAN_UNPOISON(&random_number_, sizeof(random_number_)); - random_number_ += static_cast<int32>(this - static_cast<ThreadData*>(0)); + random_number_ += static_cast<uint32>(this - static_cast<ThreadData*>(0)); random_number_ ^= (Now() - TrackedTime()).InMilliseconds(); DCHECK(!next_); @@ -453,10 +453,10 @@ int32 run_duration = stopwatch.RunDurationMs(); // Stir in some randomness, plus add constant in case durations are zero. - const int32 kSomePrimeNumber = 2147483647; + const uint32 kSomePrimeNumber = 2147483647; random_number_ += queue_duration + run_duration + kSomePrimeNumber; // An address is going to have some randomness to it as well ;-). - random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0)); + random_number_ ^= static_cast<uint32>(&birth - reinterpret_cast<Births*>(0)); // We don't have queue durations without OS timer. OS timer is automatically // used for task-post-timing, so the use of an alternate timer implies all
diff --git a/base/tracked_objects.h b/base/tracked_objects.h index 50bea47..723fb91 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h
@@ -273,7 +273,7 @@ // |duration|, and has had a queueing delay of |queue_duration|. void RecordDeath(const int32 queue_duration, const int32 run_duration, - int random_number); + const uint32 random_number); // Metrics accessors, used only for serialization and in tests. int count() const; @@ -685,7 +685,7 @@ // representative sample in each DeathData instance. We can't start off with // much randomness (because we can't call RandInt() on all our threads), so // we stir in more and more as we go. - int32 random_number_; + uint32 random_number_; // Record of what the incarnation_counter_ was when this instance was created. // If the incarnation_counter_ has changed, then we avoid pushing into the
diff --git a/base/win/scoped_comptr_unittest.cc b/base/win/scoped_comptr_unittest.cc index d8d12be..711c52f 100644 --- a/base/win/scoped_comptr_unittest.cc +++ b/base/win/scoped_comptr_unittest.cc
@@ -41,8 +41,8 @@ EXPECT_TRUE(SUCCEEDED(unk.CreateInstance(CLSID_ShellLink))); ScopedComPtr<IUnknown> unk2; unk2.Attach(unk.Detach()); - EXPECT_TRUE(unk == NULL); - EXPECT_TRUE(unk2 != NULL); + EXPECT_TRUE(unk.get() == NULL); + EXPECT_TRUE(unk2.get() != NULL); ScopedComPtr<IMalloc> mem_alloc; EXPECT_TRUE(SUCCEEDED(CoGetMalloc(1, mem_alloc.Receive()))); @@ -55,26 +55,26 @@ // test ScopedComPtr& constructor ScopedComPtr<IMalloc> copy1(mem_alloc); - EXPECT_TRUE(copy1.IsSameObject(mem_alloc)); - EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid but different - EXPECT_FALSE(copy1.IsSameObject(unk)); // unk is NULL + EXPECT_TRUE(copy1.IsSameObject(mem_alloc.get())); + EXPECT_FALSE(copy1.IsSameObject(unk2.get())); // unk2 is valid but different + EXPECT_FALSE(copy1.IsSameObject(unk.get())); // unk is NULL IMalloc* naked_copy = copy1.Detach(); copy1 = naked_copy; // Test the =(T*) operator. naked_copy->Release(); copy1.Release(); - EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid, copy1 is not + EXPECT_FALSE(copy1.IsSameObject(unk2.get())); // unk2 is valid, copy1 is not // test Interface* constructor - ScopedComPtr<IMalloc> copy2(static_cast<IMalloc*>(mem_alloc)); - EXPECT_TRUE(copy2.IsSameObject(mem_alloc)); + ScopedComPtr<IMalloc> copy2(static_cast<IMalloc*>(mem_alloc.get())); + EXPECT_TRUE(copy2.IsSameObject(mem_alloc.get())); - EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc))); - EXPECT_TRUE(unk != NULL); + EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc.get()))); + EXPECT_TRUE(unk.get() != NULL); unk.Release(); - EXPECT_TRUE(unk == NULL); - EXPECT_TRUE(unk.IsSameObject(copy1)); // both are NULL + EXPECT_TRUE(unk.get() == NULL); + EXPECT_TRUE(unk.IsSameObject(copy1.get())); // both are NULL } TEST(ScopedComPtrTest, ScopedComPtrVector) { @@ -98,7 +98,7 @@ bleh.push_back(p2); EXPECT_EQ(p->adds, 4); EXPECT_EQ(p->releases, 1); - EXPECT_EQ(bleh[0], p.get()); + EXPECT_EQ(bleh[0].get(), p.get()); bleh.pop_back(); EXPECT_EQ(p->adds, 4); EXPECT_EQ(p->releases, 2);
diff --git a/build/all.gyp b/build/all.gyp index 22f0d26..d47fc79 100644 --- a/build/all.gyp +++ b/build/all.gyp
@@ -42,13 +42,13 @@ # NOTE: This list of targets is present because # mojo_base.gyp:mojo_base cannot be built on iOS, as # javascript-related targets cause v8 to be built. - '../mojo/edk/mojo_edk.gyp:mojo_public_bindings_unittests', - '../mojo/edk/mojo_edk.gyp:mojo_public_environment_unittests', - '../mojo/edk/mojo_edk.gyp:mojo_public_system_perftests', - '../mojo/edk/mojo_edk.gyp:mojo_public_system_unittests', - '../mojo/edk/mojo_edk.gyp:mojo_public_utility_unittests', + '../mojo/edk/mojo_edk_tests.gyp:mojo_public_bindings_unittests', + '../mojo/edk/mojo_edk_tests.gyp:mojo_public_environment_unittests', + '../mojo/edk/mojo_edk_tests.gyp:mojo_public_system_perftests', + '../mojo/edk/mojo_edk_tests.gyp:mojo_public_system_unittests', + '../mojo/edk/mojo_edk_tests.gyp:mojo_public_utility_unittests', '../mojo/edk/mojo_edk.gyp:mojo_system_impl', - '../mojo/edk/mojo_edk.gyp:mojo_system_unittests', + '../mojo/edk/mojo_edk_tests.gyp:mojo_system_unittests', '../mojo/mojo_base.gyp:mojo_common_lib', '../mojo/mojo_base.gyp:mojo_common_unittests', '../mojo/public/mojo_public.gyp:mojo_cpp_bindings', @@ -276,6 +276,11 @@ '../athena/main/athena_main.gyp:*', ], }], + ['envoy==1', { + 'dependencies': [ + '../envoy/envoy.gyp:*', + ], + }], ], }, # target_name: All { @@ -354,6 +359,7 @@ ['OS=="win"', { 'dependencies': [ '../chrome/chrome.gyp:app_installer', + '../chrome/chrome.gyp:app_installer_unittests', '../chrome/chrome.gyp:crash_service', '../chrome/chrome.gyp:installer_util_unittests', # ../chrome/test/mini_installer requires mini_installer. @@ -844,9 +850,10 @@ '../ui/gfx/gfx_tests.gyp:gfx_unittests_apk', ], 'conditions': [ - ['"<(libpeer_target_type)"=="static_library"', { + ['enable_webrtc==1 and "<(libpeer_target_type)"=="static_library"', { 'dependencies': [ '../components/devtools_bridge.gyp:devtools_bridge_tests_apk', + '../components/devtools_bridge.gyp:devtools_bridge_tests2_apk', ], }], ], @@ -1180,6 +1187,7 @@ 'dependencies': [ '../base/base.gyp:base_unittests', '../chrome/chrome.gyp:app_installer', + '../chrome/chrome.gyp:app_installer_unittests', '../chrome/chrome.gyp:browser_tests', '../chrome/chrome.gyp:sync_integration_tests', '../chrome/chrome.gyp:crash_service',
diff --git a/build/android/findbugs_filter/findbugs_known_bugs.txt b/build/android/findbugs_filter/findbugs_known_bugs.txt index f641e15..ee27544 100644 --- a/build/android/findbugs_filter/findbugs_known_bugs.txt +++ b/build/android/findbugs_filter/findbugs_known_bugs.txt
@@ -24,3 +24,4 @@ M V EI: org.chromium.content_public.browser.LoadUrlParams.getPostData() may expose internal representation by returning LoadUrlParams.mPostData At LoadUrlParams.java M D NP: Read of unwritten public or protected field data in org.chromium.components.devtools_bridge.SessionDependencyFactory$DataChannelObserverAdapter.onMessage(DataChannel$Buffer) At SessionDependencyFactory.java M D NP: Read of unwritten public or protected field mandatory in org.chromium.components.devtools_bridge.SessionDependencyFactory.createPeerConnection(RTCConfiguration, AbstractPeerConnection$Observer) At SessionDependencyFactory.java +M V EI2: org.chromium.net.ChromiumUrlRequest.setUploadData(String, byte[]) may expose internal representation by storing an externally mutable object into ChromiumUrlRequest.mUploadData At ChromiumUrlRequest.java
diff --git a/build/android/gyp/java_cpp_enum.py b/build/android/gyp/java_cpp_enum.py index 8ae5f36..b10e7c5 100755 --- a/build/android/gyp/java_cpp_enum.py +++ b/build/android/gyp/java_cpp_enum.py
@@ -13,14 +13,21 @@ from util import build_utils +# List of C++ types that are compatible with the Java code generated by this +# script. +ENUM_FIXED_TYPE_WHITELIST = ['char', 'unsigned char', + 'short', 'unsigned short', + 'int', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t'] + class EnumDefinition(object): def __init__(self, original_enum_name=None, class_name_override=None, - enum_package=None, entries=None): + enum_package=None, entries=None, fixed_type=None): self.original_enum_name = original_enum_name self.class_name_override = class_name_override self.enum_package = enum_package self.entries = collections.OrderedDict(entries or []) self.prefix_to_strip = None + self.fixed_type = fixed_type def AppendEntry(self, key, value): if key in self.entries: @@ -40,6 +47,9 @@ assert self.class_name assert self.enum_package assert self.entries + if self.fixed_type and self.fixed_type not in ENUM_FIXED_TYPE_WHITELIST: + raise Exception('Fixed type %s for enum %s not whitelisted.' % + (self.fixed_type, self.class_name)) def _AssignEntryIndices(self): # Enums, if given no value, are given the value of the previous enum + 1. @@ -110,12 +120,17 @@ class HeaderParser(object): single_line_comment_re = re.compile(r'\s*//') multi_line_comment_start_re = re.compile(r'\s*/\*') - enum_start_re = re.compile(r'^\s*enum\s+(\w+)\s+{\s*$') enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?') enum_end_re = re.compile(r'^\s*}\s*;\.*$') generator_directive_re = re.compile( r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$') + optional_class_or_struct_re = r'(class|struct)?' + enum_name_re = r'(\w+)' + optional_fixed_type_re = r'(\:\s*(\w+\s*\w+?))?' + enum_start_re = re.compile(r'^\s*enum\s+' + optional_class_or_struct_re + + '\s*' + enum_name_re + '\s*' + optional_fixed_type_re + '\s*{\s*$') + def __init__(self, lines): self._lines = lines self._enum_definitions = [] @@ -162,7 +177,8 @@ if self._generator_directives.empty: return self._current_definition = EnumDefinition( - original_enum_name=enum_start.groups()[0]) + original_enum_name=enum_start.groups()[1], + fixed_type=enum_start.groups()[3]) self._in_enum = True elif generator_directive: directive_name = generator_directive.groups()[0]
diff --git a/build/android/gyp/java_cpp_enum_tests.py b/build/android/gyp/java_cpp_enum_tests.py index bb8150d..3aa386e 100755 --- a/build/android/gyp/java_cpp_enum_tests.py +++ b/build/android/gyp/java_cpp_enum_tests.py
@@ -14,7 +14,8 @@ import sys import unittest -from java_cpp_enum import EnumDefinition, GenerateOutput, HeaderParser +from java_cpp_enum import EnumDefinition, GenerateOutput, GetScriptName +from java_cpp_enum import HeaderParser sys.path.append(os.path.join(os.path.dirname(__file__), "gyp")) from util import build_utils @@ -31,7 +32,7 @@ // found in the LICENSE file. // This file is autogenerated by -// build/android/gyp/java_cpp_enum_tests.py +// %s // From // path/to/file @@ -42,7 +43,7 @@ public static final int E2 = 2 << 2; } """ - self.assertEqual(expected, output) + self.assertEqual(expected % GetScriptName(), output) def testParseSimpleEnum(self): test_data = """ @@ -150,6 +151,78 @@ with self.assertRaises(Exception): HeaderParser(test_data).ParseDefinitions() + def testParseEnumClass(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum class Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseEnumStruct(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum struct Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseFixedTypeEnum(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum Foo : int { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual('int', definition.fixed_type) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseFixedTypeEnumClass(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum class Foo: unsigned short { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual('unsigned short', definition.fixed_type) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseUnknownFixedTypeRaises(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum class Foo: foo_type { + FOO_A, + }; + """.split('\n') + with self.assertRaises(Exception): + HeaderParser(test_data).ParseDefinitions() + def testEnumValueAssignmentNoneDefined(self): definition = EnumDefinition(original_enum_name='c', enum_package='p') definition.AppendEntry('A', None)
diff --git a/build/android/gyp/package_resources.py b/build/android/gyp/package_resources.py index 6444ed9..9b6ec68 100755 --- a/build/android/gyp/package_resources.py +++ b/build/android/gyp/package_resources.py
@@ -37,6 +37,11 @@ parser.add_option('--android-manifest', help='AndroidManifest.xml path') parser.add_option('--version-code', help='Version code for apk.') parser.add_option('--version-name', help='Version name for apk.') + parser.add_option( + '--shared-resources', + action='store_true', + help='Make a resource package that can be loaded by a different' + 'application at runtime to access the package\'s resources.') parser.add_option('--resource-zips', help='zip files containing resources to be packaged') parser.add_option('--asset-dir', @@ -132,6 +137,8 @@ if options.no_compress: for ext in options.no_compress.split(','): package_command += ['-0', ext] + if options.shared_resources: + package_command.append('--shared-lib') if os.path.exists(options.asset_dir): package_command += ['-A', options.asset_dir]
diff --git a/build/android/gyp/process_resources.py b/build/android/gyp/process_resources.py index 6bf71f3..45b0947 100755 --- a/build/android/gyp/process_resources.py +++ b/build/android/gyp/process_resources.py
@@ -38,6 +38,11 @@ parser.add_option('--android-manifest', help='AndroidManifest.xml path') parser.add_option('--custom-package', help='Java package for R.java') + parser.add_option( + '--shared-resources', + action='store_true', + help='Make a resource package that can be loaded by a different' + 'application at runtime to access the package\'s resources.') parser.add_option('--resource-dirs', help='Directories containing resources of this target.') @@ -236,6 +241,8 @@ package_command += ['--custom-package', options.custom_package] if options.proguard_file: package_command += ['-G', options.proguard_file] + if options.shared_resources: + package_command.append('--shared-lib') build_utils.CheckOutput(package_command, print_stderr=False) if options.extra_res_packages:
diff --git a/build/android/pylib/base/base_test_result.py b/build/android/pylib/base/base_test_result.py index 1f45214..f9e61a9 100644 --- a/build/android/pylib/base/base_test_result.py +++ b/build/android/pylib/base/base_test_result.py
@@ -23,18 +23,20 @@ class BaseTestResult(object): """Base class for a single test result.""" - def __init__(self, name, test_type, log=''): + def __init__(self, name, test_type, duration=0, log=''): """Construct a BaseTestResult. Args: name: Name of the test which defines uniqueness. test_type: Type of the test result as defined in ResultType. + duration: Time it took for the test to run in milliseconds. log: An optional string listing any errors. """ assert name assert test_type in ResultType.GetTypes() self._name = name self._test_type = test_type + self._duration = duration self._log = log def __str__(self): @@ -66,6 +68,10 @@ """Get the test result type.""" return self._test_type + def GetDuration(self): + """Get the test duration.""" + return self._duration + def GetLog(self): """Get the test log.""" return self._log
diff --git a/build/android/pylib/base/base_test_runner.py b/build/android/pylib/base/base_test_runner.py index 1f76149..6e51b43 100644 --- a/build/android/pylib/base/base_test_runner.py +++ b/build/android/pylib/base/base_test_runner.py
@@ -9,10 +9,8 @@ # model. import logging -import time from pylib import ports -from pylib.chrome_test_server_spawner import SpawningServer from pylib.device import device_utils from pylib.forwarder import Forwarder from pylib.valgrind_tools import CreateTool @@ -42,7 +40,6 @@ self._forwarder_device_port = 8000 self.forwarder_base_url = ('http://localhost:%d' % self._forwarder_device_port) - self._spawning_server = None # We will allocate port for test server spawner when calling method # LaunchChromeTestServerSpawner and allocate port for test server when # starting it in TestServerThread. @@ -146,45 +143,4 @@ if self._http_server: self._UnmapPorts([(self._forwarder_device_port, self._http_server.port)]) self._http_server.ShutdownHttpServer() - if self._spawning_server: - self._spawning_server.Stop() - def CleanupSpawningServerState(self): - """Tells the spawning server to clean up any state. - - If the spawning server is reused for multiple tests, this should be called - after each test to prevent tests affecting each other. - """ - if self._spawning_server: - self._spawning_server.CleanupState() - - def LaunchChromeTestServerSpawner(self): - """Launches test server spawner.""" - server_ready = False - error_msgs = [] - # TODO(pliard): deflake this function. The for loop should be removed as - # well as IsHttpServerConnectable(). spawning_server.Start() should also - # block until the server is ready. - # Try 3 times to launch test spawner server. - for _ in xrange(0, 3): - self.test_server_spawner_port = ports.AllocateTestServerPort() - self._ForwardPorts( - [(self.test_server_spawner_port, self.test_server_spawner_port)]) - self._spawning_server = SpawningServer(self.test_server_spawner_port, - self.device, - self.tool) - self._spawning_server.Start() - server_ready, error_msg = ports.IsHttpServerConnectable( - '127.0.0.1', self.test_server_spawner_port, path='/ping', - expected_read='ready') - if server_ready: - break - else: - error_msgs.append(error_msg) - self._spawning_server.Stop() - # Wait for 2 seconds then restart. - time.sleep(2) - if not server_ready: - logging.error(';'.join(error_msgs)) - raise Exception('Can not start the test spawner server.') - self._PushTestServerPortInfoToDevice()
diff --git a/build/android/pylib/base/test_collection.py b/build/android/pylib/base/test_collection.py new file mode 100644 index 0000000..e914652 --- /dev/null +++ b/build/android/pylib/base/test_collection.py
@@ -0,0 +1,80 @@ +# Copyright 2013 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 threading + +class TestCollection(object): + """A threadsafe collection of tests. + + Args: + tests: List of tests to put in the collection. + """ + + def __init__(self, tests=None): + if not tests: + tests = [] + self._lock = threading.Lock() + self._tests = [] + self._tests_in_progress = 0 + # Used to signal that an item is available or all items have been handled. + self._item_available_or_all_done = threading.Event() + for t in tests: + self.add(t) + + def _pop(self): + """Pop a test from the collection. + + Waits until a test is available or all tests have been handled. + + Returns: + A test or None if all tests have been handled. + """ + while True: + # Wait for a test to be available or all tests to have been handled. + self._item_available_or_all_done.wait() + with self._lock: + # Check which of the two conditions triggered the signal. + if self._tests_in_progress == 0: + return None + try: + return self._tests.pop(0) + except IndexError: + # Another thread beat us to the available test, wait again. + self._item_available_or_all_done.clear() + + def add(self, test): + """Add an test to the collection. + + Args: + test: A test to add. + """ + with self._lock: + self._tests.append(test) + self._item_available_or_all_done.set() + self._tests_in_progress += 1 + + def test_completed(self): + """Indicate that a test has been fully handled.""" + with self._lock: + self._tests_in_progress -= 1 + if self._tests_in_progress == 0: + # All tests have been handled, signal all waiting threads. + self._item_available_or_all_done.set() + + def __iter__(self): + """Iterate through tests in the collection until all have been handled.""" + while True: + r = self._pop() + if r is None: + break + yield r + + def __len__(self): + """Return the number of tests currently in the collection.""" + return len(self._tests) + + def test_names(self): + """Return a list of the names of the tests currently in the collection.""" + with self._lock: + return list(t.test for t in self._tests)
diff --git a/build/android/pylib/base/test_dispatcher.py b/build/android/pylib/base/test_dispatcher.py index 7b00ccd..929c408 100644 --- a/build/android/pylib/base/test_dispatcher.py +++ b/build/android/pylib/base/test_dispatcher.py
@@ -24,6 +24,7 @@ from pylib import android_commands from pylib import constants from pylib.base import base_test_result +from pylib.base import test_collection from pylib.device import device_errors from pylib.utils import reraiser_thread from pylib.utils import watchdog_timer @@ -65,92 +66,16 @@ self.tries = tries -class _TestCollection(object): - """A threadsafe collection of tests. - - Args: - tests: List of tests to put in the collection. - """ - - def __init__(self, tests=None): - if not tests: - tests = [] - self._lock = threading.Lock() - self._tests = [] - self._tests_in_progress = 0 - # Used to signal that an item is available or all items have been handled. - self._item_available_or_all_done = threading.Event() - for t in tests: - self.add(t) - - def _pop(self): - """Pop a test from the collection. - - Waits until a test is available or all tests have been handled. - - Returns: - A test or None if all tests have been handled. - """ - while True: - # Wait for a test to be available or all tests to have been handled. - self._item_available_or_all_done.wait() - with self._lock: - # Check which of the two conditions triggered the signal. - if self._tests_in_progress == 0: - return None - try: - return self._tests.pop(0) - except IndexError: - # Another thread beat us to the available test, wait again. - self._item_available_or_all_done.clear() - - def add(self, test): - """Add an test to the collection. - - Args: - test: A test to add. - """ - with self._lock: - self._tests.append(test) - self._item_available_or_all_done.set() - self._tests_in_progress += 1 - - def test_completed(self): - """Indicate that a test has been fully handled.""" - with self._lock: - self._tests_in_progress -= 1 - if self._tests_in_progress == 0: - # All tests have been handled, signal all waiting threads. - self._item_available_or_all_done.set() - - def __iter__(self): - """Iterate through tests in the collection until all have been handled.""" - while True: - r = self._pop() - if r is None: - break - yield r - - def __len__(self): - """Return the number of tests currently in the collection.""" - return len(self._tests) - - def test_names(self): - """Return a list of the names of the tests currently in the collection.""" - with self._lock: - return list(t.test for t in self._tests) - - -def _RunTestsFromQueue(runner, test_collection, out_results, watcher, +def _RunTestsFromQueue(runner, collection, out_results, watcher, num_retries, tag_results_with_device=False): - """Runs tests from the test_collection until empty using the given runner. + """Runs tests from the collection until empty using the given runner. Adds TestRunResults objects to the out_results list and may add tests to the out_retry list. Args: runner: A TestRunner object used to run the tests. - test_collection: A _TestCollection from which to get _Test objects to run. + collection: A TestCollection from which to get _Test objects to run. out_results: A list to add TestRunResults to. watcher: A watchdog_timer.WatchdogTimer object, used as a shared timeout. num_retries: Number of retries for a test. @@ -174,7 +99,7 @@ new_test_run_results.AddResult(test_result) return new_test_run_results - for test in test_collection: + for test in collection: watcher.Reset() try: if runner.device_serial not in android_commands.GetAttachedDevices(): @@ -192,18 +117,18 @@ pass_results.AddResults(result.GetPass()) out_results.append(pass_results) logging.warning('Will retry test, try #%s.' % test.tries) - test_collection.add(_Test(test=retry, tries=test.tries)) + collection.add(_Test(test=retry, tries=test.tries)) else: # All tests passed or retry limit reached. Either way, record results. out_results.append(result) except: # An unhandleable exception, ensure tests get run by another device and # reraise this exception on the main thread. - test_collection.add(test) + collection.add(test) raise finally: # Retries count as separate tasks so always mark the popped test as done. - test_collection.test_completed() + collection.test_completed() def _SetUp(runner_factory, device, out_runners, threadsafe_counter): @@ -238,7 +163,7 @@ Args: runners: A list of TestRunner objects. - test_collection_factory: A callable to generate a _TestCollection object for + test_collection_factory: A callable to generate a TestCollection object for each test runner. num_retries: Number of retries for a test. timeout: Watchdog timeout in seconds. @@ -384,16 +309,17 @@ tests_expanded = ApplyMaxPerRun(tests, max_per_run) if shard: - # Generate a shared _TestCollection object for all test runners, so they + # Generate a shared TestCollection object for all test runners, so they # draw from a common pool of tests. - shared_test_collection = _TestCollection([_Test(t) for t in tests_expanded]) + shared_test_collection = test_collection.TestCollection( + [_Test(t) for t in tests_expanded]) test_collection_factory = lambda: shared_test_collection tag_results_with_device = False log_string = 'sharded across devices' else: - # Generate a unique _TestCollection object for each test runner, but use + # Generate a unique TestCollection object for each test runner, but use # the same set of tests. - test_collection_factory = lambda: _TestCollection( + test_collection_factory = lambda: test_collection.TestCollection( [_Test(t) for t in tests_expanded]) tag_results_with_device = True log_string = 'replicated on each device'
diff --git a/build/android/pylib/base/test_dispatcher_unittest.py b/build/android/pylib/base/test_dispatcher_unittest.py index d349f32..b57cca9 100644 --- a/build/android/pylib/base/test_dispatcher_unittest.py +++ b/build/android/pylib/base/test_dispatcher_unittest.py
@@ -18,6 +18,7 @@ android_commands.GetAttachedDevices = lambda: ['0', '1'] from pylib import constants from pylib.base import base_test_result +from pylib.base import test_collection from pylib.base import test_dispatcher from pylib.utils import watchdog_timer @@ -83,7 +84,7 @@ @staticmethod def _RunTests(mock_runner, tests): results = [] - tests = test_dispatcher._TestCollection( + tests = test_collection.TestCollection( [test_dispatcher._Test(t) for t in tests]) test_dispatcher._RunTestsFromQueue(mock_runner, tests, results, watchdog_timer.WatchdogTimer(None), 2) @@ -129,7 +130,7 @@ """Tests test_dispatcher._RunAllTests and test_dispatcher._CreateRunners.""" def setUp(self): self.tests = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - shared_test_collection = test_dispatcher._TestCollection( + shared_test_collection = test_collection.TestCollection( [test_dispatcher._Test(t) for t in self.tests]) self.test_collection_factory = lambda: shared_test_collection
diff --git a/build/android/pylib/base/test_server.py b/build/android/pylib/base/test_server.py new file mode 100644 index 0000000..085a51e --- /dev/null +++ b/build/android/pylib/base/test_server.py
@@ -0,0 +1,19 @@ +# 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. + +class TestServer(object): + """Base class for any server that needs to be set up for the tests.""" + + def __init__(self, *args, **kwargs): + pass + + def SetUp(self): + raise NotImplementedError + + def Reset(self): + raise NotImplementedError + + def TearDown(self): + raise NotImplementedError +
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py index cef098f..29da601 100644 --- a/build/android/pylib/constants.py +++ b/build/android/pylib/constants.py
@@ -189,13 +189,12 @@ 'pylib.device.device_utils_test', ] }, -# TODO(mkosiba) Enable after fixing these tests. -# 'gyp_py_unittests': { -# 'path': os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp'), -# 'test_modules': [ -# 'java_cpp_enum_tests' -# ] -# }, + 'gyp_py_unittests': { + 'path': os.path.join(DIR_SOURCE_ROOT, 'build', 'android', 'gyp'), + 'test_modules': [ + 'java_cpp_enum_tests', + ] + }, } LOCAL_MACHINE_TESTS = ['junit', 'python'] @@ -217,6 +216,10 @@ os.environ['CHROMIUM_OUT_DIR'] = build_directory +def SetOutputDirectort(output_directory): + os.environ['CHROMIUM_OUTPUT_DIR'] = output_directory + + def GetOutDirectory(build_type=None): """Returns the out directory where the output binaries are built. @@ -224,6 +227,10 @@ build_type: Build type, generally 'Debug' or 'Release'. Defaults to the globally set build type environment variable BUILDTYPE. """ + if 'CHROMIUM_OUTPUT_DIR' in os.environ: + return os.path.abspath(os.path.join( + DIR_SOURCE_ROOT, os.environ.get('CHROMIUM_OUTPUT_DIR'))) + return os.path.abspath(os.path.join( DIR_SOURCE_ROOT, os.environ.get('CHROMIUM_OUT_DIR', 'out'), GetBuildType() if build_type is None else build_type))
diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py index 97f387a..e7a8418 100644 --- a/build/android/pylib/device/adb_wrapper.py +++ b/build/android/pylib/device/adb_wrapper.py
@@ -152,14 +152,14 @@ self._DeviceAdbCmd(['pull', remote, local], timeout, retries) _VerifyLocalFileExists(local) - def Shell(self, command, expect_rc=None, timeout=_DEFAULT_TIMEOUT, + def Shell(self, command, expect_rc=0, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): """Runs a shell command on the device. Args: command: The shell command to run. - expect_rc: (optional) If set checks that the command's return code matches - this value. + expect_rc: (optional) Check that the command's return code matches this + value. Default is 0. If set to None the test is skipped. timeout: (optional) Timeout per try in seconds. retries: (optional) Number of retries to attempt.
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py index f4bce41..f6bebce 100644 --- a/build/android/pylib/device/device_utils.py +++ b/build/android/pylib/device/device_utils.py
@@ -215,9 +215,6 @@ CommandTimeoutError on timeout. DeviceUnreachableError on missing device. """ - return self._GetExternalStoragePathImpl() - - def _GetExternalStoragePathImpl(self): if 'external_storage' in self._cache: return self._cache['external_storage'] @@ -285,7 +282,8 @@ return self.GetProp('sys.boot_completed') == '1' def wifi_enabled(): - return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi']) + return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], + check_return=False) self.adb.WaitForDevice() timeout_retry.WaitFor(sd_card_ready) @@ -442,7 +440,7 @@ timeout = self._default_timeout try: - output = self.adb.Shell(cmd, expect_rc=0) + output = self.adb.Shell(cmd) except device_errors.AdbShellCommandFailedError as e: if check_return: raise @@ -531,6 +529,22 @@ raise device_errors.CommandFailedError(l, device=str(self)) @decorators.WithTimeoutAndRetriesFromInstance() + def StartInstrumentation(self, component, finish=True, raw=False, + extras=None, timeout=None, retries=None): + if extras is None: + extras = {} + + cmd = ['am', 'instrument'] + if finish: + cmd.append('-w') + if raw: + cmd.append('-r') + for k, v in extras.iteritems(): + cmd.extend(['-e', k, v]) + cmd.append(component) + return self.RunShellCommand(cmd, check_return=True) + + @decorators.WithTimeoutAndRetriesFromInstance() def BroadcastIntent(self, intent, timeout=None, retries=None): """Send a broadcast intent. @@ -768,7 +782,7 @@ zip_proc.start() zip_proc.join() - zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() + zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath() try: self.adb.Push(zip_file.name, zip_on_device) self.RunShellCommand(
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py index fa4ffaa..65547d4 100755 --- a/build/android/pylib/device/device_utils_test.py +++ b/build/android/pylib/device/device_utils_test.py
@@ -26,6 +26,7 @@ from pylib.device import device_errors from pylib.device import device_utils from pylib.device import intent +from pylib.utils import mock_calls # RunCommand from third_party/android_testrunner/run_command.py is mocked # below, so its path needs to be in sys.path. @@ -218,82 +219,7 @@ '0123456789abcdef', default_timeout=1, default_retries=0) -class Args: - def __init__(self, *args, **kwargs): - self.args = args - self.kwargs = kwargs - - def __eq__(self, other): - return (self.args, self.kwargs) == (other.args, other.kwargs) - - def __repr__(self): - return '%s(%s)' % (type(self).__name__, str(self)) - - def __str__(self): - toks = (['%r' % v for v in self.args] + - ['%s=%r' % (k, self.kwargs[k]) for k in sorted(self.kwargs)]) - return ', '.join(toks) - - -class MockCallSequence(object): - def __init__(self, test_case, obj, method, calls): - def assert_and_return(*args, **kwargs): - received_args = Args(*args, **kwargs) - test_case.assertTrue( - self._calls, - msg=('Unexpected call\n' - ' received: %s(%s)\n' % (self._method, received_args))) - expected_args, return_value = self._calls.pop(0) - test_case.assertTrue( - received_args == expected_args, - msg=('Call does not match expected args\n' - ' received: %s(%s)\n' - ' expected: %s(%s)\n' - % (self._method, received_args, - self._method, expected_args))) - if isinstance(return_value, Exception): - raise return_value - else: - return return_value - - self._calls = list(calls) - self._test_case = test_case - self._method = method - self._patched = mock.patch.object(obj, self._method, - side_effect=assert_and_return) - - def __enter__(self): - return self._patched.__enter__() - - def __exit__(self, exc_type, exc_val, exc_tb): - self._patched.__exit__(exc_type, exc_val, exc_tb) - if exc_type is None: - missing = ''.join(' expected: %s(%s)\n' - % (self._method, expected_args) - for expected_args, _ in self._calls) - self._test_case.assertTrue( - not missing, - msg=('Expected calls not found\n' + missing)) - - -class _ShellError: - def __init__(self, output=None, return_code=1): - if output is None: - self.output = 'Permission denied\r\n' - else: - self.output = output - self.return_code = return_code - - -class _CmdTimeout: - def __init__(self, msg=None): - if msg is None: - self.msg = 'Operation timed out' - else: - self.msg = msg - - -class DeviceUtilsNewImplTest(unittest.TestCase): +class DeviceUtilsNewImplTest(mock_calls.TestCase): def setUp(self): test_serial = '0123456789abcdef' @@ -302,66 +228,52 @@ self.adb.GetDeviceSerial.return_value = test_serial self.device = device_utils.DeviceUtils( self.adb, default_timeout=10, default_retries=0) + self.watchMethodCalls(self.call.adb) - def assertShellCallSequence(self, calls): - '''Assert that we expect a sequence of calls to adb.Shell. + def ShellError(self, output=None, exit_code=1): + def action(cmd, *args, **kwargs): + raise device_errors.AdbShellCommandFailedError( + cmd, exit_code, output, str(self.device)) + if output is None: + output = 'Permission denied\n' + return action - Args: - calls: a sequence of (cmd, return_value) pairs, where |cmd| is the - expected shell command to run on the device (with any quoting already - applied), and |return_value| is either a string to give as mock output - or a _ShellError object to raise an AdbShellCommandFailedError. - ''' - def mk_expected_call(cmd, return_value): - expected_args = Args(cmd, expect_rc=0) - if isinstance(return_value, _ShellError): - return_value = device_errors.AdbShellCommandFailedError(cmd, - return_value.return_code, return_value.output, str(self.device)) - elif isinstance(return_value, _CmdTimeout): - return_value = device_errors.CommandTimeoutError(return_value.msg, - str(self.device)) - return (expected_args, return_value) + def TimeoutError(self, msg=None): + if msg is None: + msg = 'Operation timed out' + return mock.Mock(side_effect=device_errors.CommandTimeoutError( + msg, str(self.device))) - expected_calls = (mk_expected_call(a, r) for a, r in calls) - return MockCallSequence(self, self.adb, 'Shell', expected_calls) - - def assertShellCall(self, cmd, return_value=''): - return self.assertShellCallSequence([(cmd, return_value)]) - - -class DeviceUtilsHybridImplTest(DeviceUtilsOldImplTest): - - def setUp(self): - super(DeviceUtilsHybridImplTest, self).setUp() - self.device.adb = self.adb = mock.Mock(spec=adb_wrapper.AdbWrapper) + def CommandError(self, msg=None): + if msg is None: + msg = 'Command failed' + return mock.Mock(side_effect=device_errors.CommandFailedError( + msg, str(self.device))) class DeviceUtilsIsOnlineTest(DeviceUtilsNewImplTest): def testIsOnline_true(self): - self.adb.GetState = mock.Mock(return_value='device') - self.assertTrue(self.device.IsOnline()) - self.adb.GetState.assert_called_once_with() + with self.assertCall(self.call.adb.GetState(), 'device'): + self.assertTrue(self.device.IsOnline()) def testIsOnline_false(self): - self.adb.GetState = mock.Mock(return_value='offline') - self.assertFalse(self.device.IsOnline()) - self.adb.GetState.assert_called_once_with() + with self.assertCall(self.call.adb.GetState(), 'offline'): + self.assertFalse(self.device.IsOnline()) def testIsOnline_error(self): - self.adb.GetState = mock.Mock( - side_effect=device_errors.CommandFailedError('falied')) - self.assertFalse(self.device.IsOnline()) - self.adb.GetState.assert_called_once_with() + with self.assertCall(self.call.adb.GetState(), self.CommandError()): + self.assertFalse(self.device.IsOnline()) + class DeviceUtilsHasRootTest(DeviceUtilsNewImplTest): def testHasRoot_true(self): - with self.assertShellCall('ls /root', 'foo\r\n'): + with self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n'): self.assertTrue(self.device.HasRoot()) def testHasRoot_false(self): - with self.assertShellCall('ls /root', _ShellError()): + with self.assertCall(self.call.adb.Shell('ls /root'), self.ShellError()): self.assertFalse(self.device.HasRoot()) @@ -395,25 +307,26 @@ class DeviceUtilsIsUserBuildTest(DeviceUtilsNewImplTest): def testIsUserBuild_yes(self): - with self.assertShellCall('getprop ro.build.type', 'user\r\n'): + with self.assertCall( + self.call.device.GetProp('ro.build.type', cache=True), 'user'): self.assertTrue(self.device.IsUserBuild()) def testIsUserBuild_no(self): - with self.assertShellCall('getprop ro.build.type', 'userdebug\r\n'): + with self.assertCall( + self.call.device.GetProp('ro.build.type', cache=True), 'userdebug'): self.assertFalse(self.device.IsUserBuild()) class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsNewImplTest): def testGetExternalStoragePath_succeeds(self): - fakeStoragePath = '/fake/storage/path' - with self.assertShellCall('echo $EXTERNAL_STORAGE', - '%s\r\n' % fakeStoragePath): - self.assertEquals(fakeStoragePath, + with self.assertCall( + self.call.adb.Shell('echo $EXTERNAL_STORAGE'), '/fake/storage/path\n'): + self.assertEquals('/fake/storage/path', self.device.GetExternalStoragePath()) def testGetExternalStoragePath_fails(self): - with self.assertShellCall('echo $EXTERNAL_STORAGE', '\r\n'): + with self.assertCall(self.call.adb.Shell('echo $EXTERNAL_STORAGE'), '\n'): with self.assertRaises(device_errors.CommandFailedError): self.device.GetExternalStoragePath() @@ -421,148 +334,150 @@ class DeviceUtilsGetApplicationPathTest(DeviceUtilsNewImplTest): def testGetApplicationPath_exists(self): - with self.assertShellCall('pm path android', - 'package:/path/to/android.apk\n'): + with self.assertCall(self.call.adb.Shell('pm path android'), + 'package:/path/to/android.apk\n'): self.assertEquals('/path/to/android.apk', self.device.GetApplicationPath('android')) def testGetApplicationPath_notExists(self): - with self.assertShellCall('pm path not.installed.app', - ''): + with self.assertCall(self.call.adb.Shell('pm path not.installed.app'), ''): self.assertEquals(None, self.device.GetApplicationPath('not.installed.app')) def testGetApplicationPath_fails(self): - with self.assertShellCall('pm path android', - 'ERROR. Is package manager running?\n'): + with self.assertCall(self.call.adb.Shell('pm path android'), + self.CommandError('ERROR. Is package manager running?\n')): with self.assertRaises(device_errors.CommandFailedError): self.device.GetApplicationPath('android') +@mock.patch('time.sleep', mock.Mock()) class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsNewImplTest): def testWaitUntilFullyBooted_succeedsNoWifi(self): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', ''), + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), ''), # pm_ready - ('pm path android', 'package:this.is.a.test.package\r\n'), + (self.call.device.GetApplicationPath('android'), + 'package:/some/fake/path'), # boot_completed - ('getprop sys.boot_completed', '1\r\n')]): + (self.call.device.GetProp('sys.boot_completed'), '1')): self.device.WaitUntilFullyBooted(wifi=False) - self.adb.WaitForDevice.assert_called_once_with() def testWaitUntilFullyBooted_succeedsWithWifi(self): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', ''), + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), ''), # pm_ready - ('pm path android', 'package:this.is.a.test.package\r\n'), + (self.call.device.GetApplicationPath('android'), + 'package:/some/fake/path'), # boot_completed - ('getprop sys.boot_completed', '1\r\n'), + (self.call.device.GetProp('sys.boot_completed'), '1'), # wifi_enabled - ('dumpsys wifi', 'stuff\r\nWi-Fi is enabled\r\nmore stuff\r\n')]): + (self.call.adb.Shell('dumpsys wifi'), + 'stuff\nWi-Fi is enabled\nmore stuff\n')): self.device.WaitUntilFullyBooted(wifi=True) - self.adb.WaitForDevice.assert_called_once_with() def testWaitUntilFullyBooted_sdCardReadyFails_noPath(self): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '\r\n')]): + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), self.CommandError())): with self.assertRaises(device_errors.CommandFailedError): self.device.WaitUntilFullyBooted(wifi=False) - def testWaitUntilFullyBooted_sdCardReadyFails_emptyPath(self): - with mock.patch('time.sleep'): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', _ShellError()), - # sc_card_ready - ('test -d /fake/storage/path', _ShellError()), - # sc_card_ready - ('test -d /fake/storage/path', _CmdTimeout())]): - with self.assertRaises(device_errors.CommandTimeoutError): - self.device.WaitUntilFullyBooted(wifi=False) + def testWaitUntilFullyBooted_sdCardReadyFails_notExists(self): + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), self.ShellError()), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), self.ShellError()), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), + self.TimeoutError())): + with self.assertRaises(device_errors.CommandTimeoutError): + self.device.WaitUntilFullyBooted(wifi=False) def testWaitUntilFullyBooted_devicePmFails(self): - with mock.patch('time.sleep'): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', ''), - # pm_ready - ('pm path android', 'Error. Is package manager running?\r\n'), - # pm_ready - ('pm path android', 'Error. Is package manager running?\r\n'), - # pm_ready - ('pm path android', _CmdTimeout())]): - with self.assertRaises(device_errors.CommandTimeoutError): - self.device.WaitUntilFullyBooted(wifi=False) + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), ''), + # pm_ready + (self.call.device.GetApplicationPath('android'), self.CommandError()), + # pm_ready + (self.call.device.GetApplicationPath('android'), self.CommandError()), + # pm_ready + (self.call.device.GetApplicationPath('android'), self.TimeoutError())): + with self.assertRaises(device_errors.CommandTimeoutError): + self.device.WaitUntilFullyBooted(wifi=False) def testWaitUntilFullyBooted_bootFails(self): - with mock.patch('time.sleep'): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', ''), - # pm_ready - ('pm path android', 'package:this.is.a.test.package\r\n'), - # boot_completed - ('getprop sys.boot_completed', '0\r\n'), - # boot_completed - ('getprop sys.boot_completed', '0\r\n'), - # boot_completed - ('getprop sys.boot_completed', _CmdTimeout())]): - with self.assertRaises(device_errors.CommandTimeoutError): - self.device.WaitUntilFullyBooted(wifi=False) + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), ''), + # pm_ready + (self.call.device.GetApplicationPath('android'), + 'package:/some/fake/path'), + # boot_completed + (self.call.device.GetProp('sys.boot_completed'), '0'), + # boot_completed + (self.call.device.GetProp('sys.boot_completed'), '0'), + # boot_completed + (self.call.device.GetProp('sys.boot_completed'), self.TimeoutError())): + with self.assertRaises(device_errors.CommandTimeoutError): + self.device.WaitUntilFullyBooted(wifi=False) def testWaitUntilFullyBooted_wifiFails(self): - with mock.patch('time.sleep'): - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', ''), - # pm_ready - ('pm path android', 'package:this.is.a.test.package\r\n'), - # boot_completed - ('getprop sys.boot_completed', '1\r\n'), - # wifi_enabled - ('dumpsys wifi', 'stuff\r\nmore stuff\r\n'), - # wifi_enabled - ('dumpsys wifi', 'stuff\r\nmore stuff\r\n'), - # wifi_enabled - ('dumpsys wifi', _CmdTimeout())]): - with self.assertRaises(device_errors.CommandTimeoutError): - self.device.WaitUntilFullyBooted(wifi=True) + with self.assertCalls( + self.call.adb.WaitForDevice(), + # sd_card_ready + (self.call.device.GetExternalStoragePath(), '/fake/storage/path'), + (self.call.adb.Shell('test -d /fake/storage/path'), ''), + # pm_ready + (self.call.device.GetApplicationPath('android'), + 'package:/some/fake/path'), + # boot_completed + (self.call.device.GetProp('sys.boot_completed'), '1'), + # wifi_enabled + (self.call.adb.Shell('dumpsys wifi'), 'stuff\nmore stuff\n'), + # wifi_enabled + (self.call.adb.Shell('dumpsys wifi'), 'stuff\nmore stuff\n'), + # wifi_enabled + (self.call.adb.Shell('dumpsys wifi'), self.TimeoutError())): + with self.assertRaises(device_errors.CommandTimeoutError): + self.device.WaitUntilFullyBooted(wifi=True) +@mock.patch('time.sleep', mock.Mock()) class DeviceUtilsRebootTest(DeviceUtilsNewImplTest): def testReboot_nonBlocking(self): - self.adb.Reboot = mock.Mock() - self.device.IsOnline = mock.Mock(return_value=False) - self.device.Reboot(block=False) - self.adb.Reboot.assert_called_once_with() - self.device.IsOnline.assert_called_once_with() + with self.assertCalls( + self.call.adb.Reboot(), + (self.call.device.IsOnline(), True), + (self.call.device.IsOnline(), False)): + self.device.Reboot(block=False) def testReboot_blocking(self): - self.adb.Reboot = mock.Mock() - self.device.IsOnline = mock.Mock(return_value=False) - with self.assertShellCallSequence([ - # sc_card_ready - ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'), - ('test -d /fake/storage/path', ''), - # pm_ready - ('pm path android', 'package:this.is.a.test.package\r\n'), - # boot_completed - ('getprop sys.boot_completed', '1\r\n')]): + with self.assertCalls( + self.call.adb.Reboot(), + (self.call.device.IsOnline(), True), + (self.call.device.IsOnline(), False), + self.call.device.WaitUntilFullyBooted()): self.device.Reboot(block=True) - self.adb.Reboot.assert_called_once_with() - self.device.IsOnline.assert_called_once_with() - self.adb.WaitForDevice.assert_called_once_with() class DeviceUtilsInstallTest(DeviceUtilsOldImplTest): @@ -656,24 +571,31 @@ class DeviceUtilsRunShellCommandTest(DeviceUtilsNewImplTest): + + def setUp(self): + super(DeviceUtilsRunShellCommandTest, self).setUp() + self.device.NeedsSU = mock.Mock(return_value=False) + def testRunShellCommand_commandAsList(self): - with self.assertShellCall('pm list packages'): + with self.assertCall(self.call.adb.Shell('pm list packages'), ''): self.device.RunShellCommand(['pm', 'list', 'packages']) def testRunShellCommand_commandAsListQuoted(self): - with self.assertShellCall("echo 'hello world' '$10'"): + with self.assertCall(self.call.adb.Shell("echo 'hello world' '$10'"), ''): self.device.RunShellCommand(['echo', 'hello world', '$10']) def testRunShellCommand_commandAsString(self): - with self.assertShellCall('echo "$VAR"'): + with self.assertCall(self.call.adb.Shell('echo "$VAR"'), ''): self.device.RunShellCommand('echo "$VAR"') def testNewRunShellImpl_withEnv(self): - with self.assertShellCall('VAR=some_string echo "$VAR"'): + with self.assertCall( + self.call.adb.Shell('VAR=some_string echo "$VAR"'), ''): self.device.RunShellCommand('echo "$VAR"', env={'VAR': 'some_string'}) def testNewRunShellImpl_withEnvQuoted(self): - with self.assertShellCall('PATH="$PATH:/other/path" run_this'): + with self.assertCall( + self.call.adb.Shell('PATH="$PATH:/other/path" run_this'), ''): self.device.RunShellCommand('run_this', env={'PATH': '$PATH:/other/path'}) def testNewRunShellImpl_withEnv_failure(self): @@ -681,132 +603,129 @@ self.device.RunShellCommand('some_cmd', env={'INVALID NAME': 'value'}) def testNewRunShellImpl_withCwd(self): - with self.assertShellCall('cd /some/test/path && ls'): + with self.assertCall(self.call.adb.Shell('cd /some/test/path && ls'), ''): self.device.RunShellCommand('ls', cwd='/some/test/path') def testNewRunShellImpl_withCwdQuoted(self): - with self.assertShellCall("cd '/some test/path with/spaces' && ls"): + with self.assertCall( + self.call.adb.Shell("cd '/some test/path with/spaces' && ls"), ''): self.device.RunShellCommand('ls', cwd='/some test/path with/spaces') def testRunShellCommand_withSu(self): - with self.assertShellCallSequence([ - ('su -c ls /root && ! ls /root', ''), - ('su -c setprop service.adb.root 0', '')]): - self.device.RunShellCommand('setprop service.adb.root 0', as_root=True) - - def testRunShellCommand_withRoot(self): - with self.assertShellCallSequence([ - ('su -c ls /root && ! ls /root', _ShellError()), - ('setprop service.adb.root 0', '')]): + with self.assertCalls( + (self.call.device.NeedsSU(), True), + (self.call.adb.Shell('su -c setprop service.adb.root 0'), '')): self.device.RunShellCommand('setprop service.adb.root 0', as_root=True) def testRunShellCommand_manyLines(self): cmd = 'ls /some/path' - with self.assertShellCall(cmd, 'file1\r\nfile2\r\nfile3\r\n'): + with self.assertCall(self.call.adb.Shell(cmd), 'file1\nfile2\nfile3\n'): self.assertEquals(['file1', 'file2', 'file3'], self.device.RunShellCommand(cmd)) def testRunShellCommand_singleLine_success(self): cmd = 'echo $VALUE' - with self.assertShellCall(cmd, 'some value\r\n'): + with self.assertCall(self.call.adb.Shell(cmd), 'some value\n'): self.assertEquals('some value', self.device.RunShellCommand(cmd, single_line=True)) def testRunShellCommand_singleLine_successEmptyLine(self): cmd = 'echo $VALUE' - with self.assertShellCall(cmd, '\r\n'): + with self.assertCall(self.call.adb.Shell(cmd), '\n'): self.assertEquals('', self.device.RunShellCommand(cmd, single_line=True)) def testRunShellCommand_singleLine_successWithoutEndLine(self): cmd = 'echo -n $VALUE' - with self.assertShellCall(cmd, 'some value'): + with self.assertCall(self.call.adb.Shell(cmd), 'some value'): self.assertEquals('some value', self.device.RunShellCommand(cmd, single_line=True)) def testRunShellCommand_singleLine_successNoOutput(self): cmd = 'echo -n $VALUE' - with self.assertShellCall(cmd, ''): + with self.assertCall(self.call.adb.Shell(cmd), ''): self.assertEquals('', self.device.RunShellCommand(cmd, single_line=True)) def testRunShellCommand_singleLine_failTooManyLines(self): cmd = 'echo $VALUE' - with self.assertShellCall(cmd, 'some value\r\nanother value\r\n'): + with self.assertCall(self.call.adb.Shell(cmd), + 'some value\nanother value\n'): with self.assertRaises(device_errors.CommandFailedError): self.device.RunShellCommand(cmd, single_line=True) def testRunShellCommand_checkReturn_success(self): cmd = 'echo $ANDROID_DATA' - output = '/data\r\n' - with self.assertShellCall(cmd, output): + output = '/data\n' + with self.assertCall(self.call.adb.Shell(cmd), output): self.assertEquals([output.rstrip()], self.device.RunShellCommand(cmd, check_return=True)) def testRunShellCommand_checkReturn_failure(self): cmd = 'ls /root' - output = 'opendir failed, Permission denied\r\n' - with self.assertShellCall(cmd, _ShellError(output)): + output = 'opendir failed, Permission denied\n' + with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)): with self.assertRaises(device_errors.AdbShellCommandFailedError): self.device.RunShellCommand(cmd, check_return=True) def testRunShellCommand_checkReturn_disabled(self): cmd = 'ls /root' - output = 'opendir failed, Permission denied\r\n' - with self.assertShellCall(cmd, _ShellError(output)): + output = 'opendir failed, Permission denied\n' + with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)): self.assertEquals([output.rstrip()], self.device.RunShellCommand(cmd, check_return=False)) +@mock.patch('time.sleep', mock.Mock()) class DeviceUtilsKillAllTest(DeviceUtilsNewImplTest): def testKillAll_noMatchingProcesses(self): - output = 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - with self.assertShellCallSequence([('ps', output)]): + with self.assertCall(self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'): with self.assertRaises(device_errors.CommandFailedError): self.device.KillAll('test_process') def testKillAll_nonblocking(self): - with self.assertShellCallSequence([ - ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'u0_a1 1234 174 123456 54321 ffffffff 456789ab ' - 'this.is.a.test.process\r\n'), - ('kill -9 1234', '')]): + with self.assertCalls( + (self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'), + (self.call.adb.Shell('kill -9 1234'), '')): self.assertEquals(1, - self.device.KillAll('this.is.a.test.process', blocking=False)) + self.device.KillAll('some.process', blocking=False)) def testKillAll_blocking(self): - with mock.patch('time.sleep'): - with self.assertShellCallSequence([ - ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'u0_a1 1234 174 123456 54321 ffffffff 456789ab ' - 'this.is.a.test.process\r\n'), - ('kill -9 1234', ''), - ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'u0_a1 1234 174 123456 54321 ffffffff 456789ab ' - 'this.is.a.test.process\r\n'), - ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n')]): - self.assertEquals(1, - self.device.KillAll('this.is.a.test.process', blocking=True)) + with self.assertCalls( + (self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'), + (self.call.adb.Shell('kill -9 1234'), ''), + (self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'), + (self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n')): + self.assertEquals(1, + self.device.KillAll('some.process', blocking=True)) def testKillAll_root(self): - with self.assertShellCallSequence([ - ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'u0_a1 1234 174 123456 54321 ffffffff 456789ab ' - 'this.is.a.test.process\r\n'), - ('su -c ls /root && ! ls /root', ''), - ('su -c kill -9 1234', '')]): + with self.assertCalls( + (self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'), + (self.call.device.NeedsSU(), True), + (self.call.adb.Shell('su -c kill -9 1234'), '')): self.assertEquals(1, - self.device.KillAll('this.is.a.test.process', as_root=True)) + self.device.KillAll('some.process', as_root=True)) def testKillAll_sigterm(self): - with self.assertShellCallSequence([ - ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'u0_a1 1234 174 123456 54321 ffffffff 456789ab ' - 'this.is.a.test.process\r\n'), - ('kill -15 1234', '')]): + with self.assertCalls( + (self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'), + (self.call.adb.Shell('kill -15 1234'), '')): self.assertEquals(1, - self.device.KillAll('this.is.a.test.process', signum=signal.SIGTERM)) + self.device.KillAll('some.process', signum=signal.SIGTERM)) class DeviceUtilsStartActivityTest(DeviceUtilsOldImplTest): @@ -974,6 +893,48 @@ self.device.StartActivity(test_intent) +class DeviceUtilsStartInstrumentationTest(DeviceUtilsNewImplTest): + + def testStartInstrumentation_nothing(self): + with self.assertCalls( + self.call.device.RunShellCommand( + ['am', 'instrument', 'test.package/.TestInstrumentation'], + check_return=True)): + self.device.StartInstrumentation( + 'test.package/.TestInstrumentation', + finish=False, raw=False, extras=None) + + def testStartInstrumentation_finish(self): + with self.assertCalls( + (self.call.device.RunShellCommand( + ['am', 'instrument', '-w', 'test.package/.TestInstrumentation'], + check_return=True), + ['OK (1 test)'])): + output = self.device.StartInstrumentation( + 'test.package/.TestInstrumentation', + finish=True, raw=False, extras=None) + self.assertEquals(['OK (1 test)'], output) + + def testStartInstrumentation_raw(self): + with self.assertCalls( + self.call.device.RunShellCommand( + ['am', 'instrument', '-r', 'test.package/.TestInstrumentation'], + check_return=True)): + self.device.StartInstrumentation( + 'test.package/.TestInstrumentation', + finish=False, raw=True, extras=None) + + def testStartInstrumentation_extras(self): + with self.assertCalls( + self.call.device.RunShellCommand( + ['am', 'instrument', '-e', 'foo', 'Foo', '-e', 'bar', 'Bar', + 'test.package/.TestInstrumentation'], + check_return=True)): + self.device.StartInstrumentation( + 'test.package/.TestInstrumentation', + finish=False, raw=False, extras={'foo': 'Foo', 'bar': 'Bar'}) + + class DeviceUtilsBroadcastIntentTest(DeviceUtilsOldImplTest): def testBroadcastIntent_noExtras(self): @@ -1056,93 +1017,61 @@ def testPushChangedFilesIndividually_empty(self): test_files = [] - self.device._PushChangedFilesIndividually(test_files) - self.assertEqual(0, self.adb.Push.call_count) + with self.assertCalls(): + self.device._PushChangedFilesIndividually(test_files) def testPushChangedFilesIndividually_single(self): test_files = [('/test/host/path', '/test/device/path')] - self.device._PushChangedFilesIndividually(test_files) - self.adb.Push.assert_called_once_with( - '/test/host/path', '/test/device/path') + with self.assertCalls(self.call.adb.Push(*test_files[0])): + self.device._PushChangedFilesIndividually(test_files) def testPushChangedFilesIndividually_multiple(self): test_files = [ ('/test/host/path/file1', '/test/device/path/file1'), ('/test/host/path/file2', '/test/device/path/file2')] - self.device._PushChangedFilesIndividually(test_files) - self.assertEqual(2, self.adb.Push.call_count) - self.adb.Push.assert_any_call( - '/test/host/path/file1', '/test/device/path/file1') - self.adb.Push.assert_any_call( - '/test/host/path/file2', '/test/device/path/file2') + with self.assertCalls( + self.call.adb.Push(*test_files[0]), + self.call.adb.Push(*test_files[1])): + self.device._PushChangedFilesIndividually(test_files) -@mock.patch('pylib.device.commands.install_commands.Installed', new=None) -@mock.patch('pylib.device.commands.install_commands.InstallCommands', new=None) -class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsHybridImplTest): - - def setUp(self): - super(DeviceUtilsPushChangedFilesZippedTest, self).setUp() +class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsNewImplTest): def testPushChangedFilesZipped_empty(self): test_files = [] - self.device._PushChangedFilesZipped(test_files) - self.assertEqual(0, self.adb.Push.call_count) + with self.assertCalls(): + self.device._PushChangedFilesZipped(test_files) + + def _testPushChangedFilesZipped_spec(self, test_files): + mock_zip_temp = mock.mock_open() + mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip' + with self.assertCalls( + (mock.call.tempfile.NamedTemporaryFile(suffix='.zip'), mock_zip_temp), + (mock.call.multiprocessing.Process( + target=device_utils.DeviceUtils._CreateDeviceZip, + args=('/test/temp/file/tmp.zip', test_files)), mock.Mock()), + (self.call.device.GetExternalStoragePath(), + '/test/device/external_dir'), + self.call.adb.Push( + '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip'), + self.call.device.RunShellCommand( + ['unzip', '/test/device/external_dir/tmp.zip'], + as_root=True, + env={'PATH': '$PATH:/data/local/tmp/bin'}, + check_return=True), + (self.call.device.IsOnline(), True), + self.call.device.RunShellCommand( + ['rm', '/test/device/external_dir/tmp.zip'], check_return=True)): + self.device._PushChangedFilesZipped(test_files) def testPushChangedFilesZipped_single(self): - test_files = [('/test/host/path/file1', '/test/device/path/file1')] - - self.device._GetExternalStoragePathImpl = mock.Mock( - return_value='/test/device/external_dir') - self.device.IsOnline = mock.Mock(return_value=True) - self.device.RunShellCommand = mock.Mock() - mock_zip_temp = mock.mock_open() - mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip' - with mock.patch('multiprocessing.Process') as mock_zip_proc, ( - mock.patch('tempfile.NamedTemporaryFile', mock_zip_temp)): - self.device._PushChangedFilesZipped(test_files) - - mock_zip_proc.assert_called_once_with( - target=device_utils.DeviceUtils._CreateDeviceZip, - args=('/test/temp/file/tmp.zip', test_files)) - self.adb.Push.assert_called_once_with( - '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip') - self.assertEqual(2, self.device.RunShellCommand.call_count) - self.device.RunShellCommand.assert_any_call( - ['unzip', '/test/device/external_dir/tmp.zip'], - as_root=True, - env={'PATH': '$PATH:/data/local/tmp/bin'}, - check_return=True) - self.device.RunShellCommand.assert_any_call( - ['rm', '/test/device/external_dir/tmp.zip'], check_return=True) + self._testPushChangedFilesZipped_spec( + [('/test/host/path/file1', '/test/device/path/file1')]) def testPushChangedFilesZipped_multiple(self): - test_files = [('/test/host/path/file1', '/test/device/path/file1'), - ('/test/host/path/file2', '/test/device/path/file2')] - - self.device._GetExternalStoragePathImpl = mock.Mock( - return_value='/test/device/external_dir') - self.device.IsOnline = mock.Mock(return_value=True) - self.device.RunShellCommand = mock.Mock() - mock_zip_temp = mock.mock_open() - mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip' - with mock.patch('multiprocessing.Process') as mock_zip_proc, ( - mock.patch('tempfile.NamedTemporaryFile', mock_zip_temp)): - self.device._PushChangedFilesZipped(test_files) - - mock_zip_proc.assert_called_once_with( - target=device_utils.DeviceUtils._CreateDeviceZip, - args=('/test/temp/file/tmp.zip', test_files)) - self.adb.Push.assert_called_once_with( - '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip') - self.assertEqual(2, self.device.RunShellCommand.call_count) - self.device.RunShellCommand.assert_any_call( - ['unzip', '/test/device/external_dir/tmp.zip'], - as_root=True, - env={'PATH': '$PATH:/data/local/tmp/bin'}, - check_return=True) - self.device.RunShellCommand.assert_any_call( - ['rm', '/test/device/external_dir/tmp.zip'], check_return=True) + self._testPushChangedFilesZipped_spec( + [('/test/host/path/file1', '/test/device/path/file1'), + ('/test/host/path/file2', '/test/device/path/file2')]) class DeviceUtilsFileExistsTest(DeviceUtilsOldImplTest): @@ -1370,23 +1299,27 @@ self.device.WriteFile('/test/file/no.permissions.to.write', 'new test file contents', as_root=True) + class DeviceUtilsWriteTextFileTest(DeviceUtilsNewImplTest): def testWriteTextFileTest_basic(self): - with self.assertShellCall('echo some.string > /test/file/to.write'): + with self.assertCall( + self.call.adb.Shell('echo some.string > /test/file/to.write'), ''): self.device.WriteTextFile('/test/file/to.write', 'some.string') def testWriteTextFileTest_quoted(self): - with self.assertShellCall( - "echo 'some other string' > '/test/file/to write'"): + with self.assertCall( + self.call.adb.Shell("echo 'some other string' > '/test/file/to write'"), + ''): self.device.WriteTextFile('/test/file/to write', 'some other string') - def testWriteTextFileTest_asRoot(self): - with self.assertShellCallSequence([ - ('su -c ls /root && ! ls /root', ''), - ('su -c echo string > /test/file', '')]): + def testWriteTextFileTest_withSU(self): + with self.assertCalls( + (self.call.device.NeedsSU(), True), + (self.call.adb.Shell('su -c echo string > /test/file'), '')): self.device.WriteTextFile('/test/file', 'string', as_root=True) + class DeviceUtilsLsTest(DeviceUtilsOldImplTest): def testLs_nothing(self): @@ -1512,29 +1445,29 @@ class DeviceUtilsGetPropTest(DeviceUtilsNewImplTest): def testGetProp_exists(self): - with self.assertShellCall('getprop this.is.a.test.property', - 'test_property_value\r\n'): - self.assertEqual('test_property_value', - self.device.GetProp('this.is.a.test.property')) + with self.assertCall( + self.call.adb.Shell('getprop test.property'), 'property_value\n'): + self.assertEqual('property_value', + self.device.GetProp('test.property')) def testGetProp_doesNotExist(self): - with self.assertShellCall('getprop this.property.does.not.exist', - '\r\n'): - self.assertEqual('', self.device.GetProp('this.property.does.not.exist')) + with self.assertCall( + self.call.adb.Shell('getprop property.does.not.exist'), '\n'): + self.assertEqual('', self.device.GetProp('property.does.not.exist')) def testGetProp_cachedRoProp(self): - with self.assertShellCall('getprop ro.build.type', - 'userdebug\r\n'): + with self.assertCall( + self.call.adb.Shell('getprop ro.build.type'), 'userdebug\n'): self.assertEqual('userdebug', self.device.GetProp('ro.build.type', cache=True)) self.assertEqual('userdebug', self.device.GetProp('ro.build.type', cache=True)) def testGetProp_retryAndCache(self): - with self.assertShellCallSequence([ - ('getprop ro.build.type', _ShellError()), - ('getprop ro.build.type', _ShellError()), - ('getprop ro.build.type', 'userdebug\r\n')]): + with self.assertCalls( + (self.call.adb.Shell('getprop ro.build.type'), self.ShellError()), + (self.call.adb.Shell('getprop ro.build.type'), self.ShellError()), + (self.call.adb.Shell('getprop ro.build.type'), 'userdebug\n')): self.assertEqual('userdebug', self.device.GetProp('ro.build.type', cache=True, retries=3)) @@ -1546,46 +1479,55 @@ class DeviceUtilsSetPropTest(DeviceUtilsNewImplTest): def testSetProp(self): - with self.assertShellCall( - "setprop this.is.a.test.property 'test property value'"): - self.device.SetProp('this.is.a.test.property', 'test property value') + with self.assertCall( + self.call.adb.Shell("setprop test.property 'test value'"), ''): + self.device.SetProp('test.property', 'test value') + + def testSetProp_check_succeeds(self): + with self.assertCalls( + (self.call.adb.Shell('setprop test.property new_value'), ''), + (self.call.adb.Shell('getprop test.property'), 'new_value')): + self.device.SetProp('test.property', 'new_value', check=True) + + def testSetProp_check_fails(self): + with self.assertCalls( + (self.call.adb.Shell('setprop test.property new_value'), ''), + (self.call.adb.Shell('getprop test.property'), 'old_value')): + with self.assertRaises(device_errors.CommandFailedError): + self.device.SetProp('test.property', 'new_value', check=True) class DeviceUtilsGetPidsTest(DeviceUtilsNewImplTest): def testGetPids_noMatches(self): - with self.assertShellCall( - 'ps', - 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'user 1000 100 1024 1024 ffffffff 00000000 no.match\r\n'): + with self.assertCall(self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'user 1000 100 1024 1024 ffffffff 00000000 no.match\n'): self.assertEqual({}, self.device.GetPids('does.not.match')) def testGetPids_oneMatch(self): - with self.assertShellCall( - 'ps', - 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'user 1000 100 1024 1024 ffffffff 00000000 not.a.match\r\n' - 'user 1001 100 1024 1024 ffffffff 00000000 one.match\r\n'): + with self.assertCall(self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'user 1000 100 1024 1024 ffffffff 00000000 not.a.match\n' + 'user 1001 100 1024 1024 ffffffff 00000000 one.match\n'): self.assertEqual({'one.match': '1001'}, self.device.GetPids('one.match')) def testGetPids_mutlipleMatches(self): - with self.assertShellCall( - 'ps', - 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'user 1000 100 1024 1024 ffffffff 00000000 not\r\n' - 'user 1001 100 1024 1024 ffffffff 00000000 one.match\r\n' - 'user 1002 100 1024 1024 ffffffff 00000000 two.match\r\n' - 'user 1003 100 1024 1024 ffffffff 00000000 three.match\r\n'): + with self.assertCall(self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'user 1000 100 1024 1024 ffffffff 00000000 not\n' + 'user 1001 100 1024 1024 ffffffff 00000000 one.match\n' + 'user 1002 100 1024 1024 ffffffff 00000000 two.match\n' + 'user 1003 100 1024 1024 ffffffff 00000000 three.match\n'): self.assertEqual( {'one.match': '1001', 'two.match': '1002', 'three.match': '1003'}, self.device.GetPids('match')) def testGetPids_exactMatch(self): - with self.assertShellCall( - 'ps', - 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n' - 'user 1000 100 1024 1024 ffffffff 00000000 not.exact.match\r\n' - 'user 1234 100 1024 1024 ffffffff 00000000 exact.match\r\n'): + with self.assertCall(self.call.adb.Shell('ps'), + 'USER PID PPID VSIZE RSS WCHAN PC NAME\n' + 'user 1000 100 1024 1024 ffffffff 00000000 not.exact.match\n' + 'user 1234 100 1024 1024 ffffffff 00000000 exact.match\n'): self.assertEqual( {'not.exact.match': '1000', 'exact.match': '1234'}, self.device.GetPids('exact.match')) @@ -1668,6 +1610,7 @@ class DeviceUtilsStrTest(DeviceUtilsOldImplTest): + def testStr_noAdbCalls(self): with self.assertNoAdbCalls(): self.assertEqual('0123456789abcdef', str(self.device))
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py index 5e45043..5f40a94 100644 --- a/build/android/pylib/forwarder.py +++ b/build/android/pylib/forwarder.py
@@ -270,6 +270,7 @@ pid_file.seek(0) pid_file.write( '%s:%s' % (pid_for_lock, str(_GetProcessStartTime(pid_for_lock)))) + pid_file.truncate() def _InitDeviceLocked(self, device, tool): """Initializes the device_forwarder daemon for a specific device (once).
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py index 75892ee..56e7449 100644 --- a/build/android/pylib/gtest/test_runner.py +++ b/build/android/pylib/gtest/test_runner.py
@@ -7,9 +7,11 @@ import re from pylib import pexpect +from pylib import ports from pylib.base import base_test_result from pylib.base import base_test_runner from pylib.device import device_errors +from pylib.local import local_test_server_spawner from pylib.perf import perf_control @@ -53,6 +55,13 @@ if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name): self._perf_controller = perf_control.PerfControl(self.device) + if _TestSuiteRequiresMockTestServer(self.test_package.suite_name): + self._servers = [ + local_test_server_spawner.LocalTestServerSpawner( + ports.AllocateTestServerPort(), self.device, self.tool)] + else: + self._servers = [] + #override def InstallTestPackage(self): self.test_package.Install(self.device) @@ -149,7 +158,8 @@ test_results = self._ParseTestOutput( self.test_package.SpawnTestProcess(self.device)) finally: - self.CleanupSpawningServerState() + for s in self._servers: + s.Reset() # Calculate unknown test results. all_tests = set(test.split(':')) all_tests_ran = set([t.GetName() for t in test_results.GetAll()]) @@ -164,8 +174,8 @@ def SetUp(self): """Sets up necessary test enviroment for the test suite.""" super(TestRunner, self).SetUp() - if _TestSuiteRequiresMockTestServer(self.test_package.suite_name): - self.LaunchChromeTestServerSpawner() + for s in self._servers: + s.SetUp() if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name): self._perf_controller.SetHighPerfMode() self.tool.SetupEnvironment() @@ -173,6 +183,8 @@ #override def TearDown(self): """Cleans up the test enviroment for the test suite.""" + for s in self._servers: + s.TearDown() if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name): self._perf_controller.SetDefaultPerfMode() self.test_package.ClearApplicationState(self.device)
diff --git a/build/android/pylib/instrumentation/test_jar.py b/build/android/pylib/instrumentation/test_jar.py index c81258f..d83d00a 100644 --- a/build/android/pylib/instrumentation/test_jar.py +++ b/build/android/pylib/instrumentation/test_jar.py
@@ -23,7 +23,7 @@ import unittest_util # pylint: disable=F0401 # If you change the cached output of proguard, increment this number -PICKLE_FORMAT_VERSION = 2 +PICKLE_FORMAT_VERSION = 3 class TestJar(object): @@ -55,6 +55,16 @@ if not self._GetCachedProguardData(): self._GetProguardData() + @staticmethod + def _CalculateMd5(path): + # TODO(jbudorick): Move MD5sum calculations out of here and + # AndroidCommands to their own module. + out = cmd_helper.GetCmdOutput( + [os.path.join(constants.GetOutDirectory(), + 'md5sum_bin_host'), + path]) + return out + def _GetCachedProguardData(self): if (os.path.exists(self._pickled_proguard_name) and (os.path.getmtime(self._pickled_proguard_name) > @@ -64,7 +74,9 @@ try: with open(self._pickled_proguard_name, 'r') as r: d = pickle.loads(r.read()) - if d['VERSION'] == PICKLE_FORMAT_VERSION: + jar_md5 = self._CalculateMd5(self._jar_path) + if (d['JAR_MD5SUM'] == jar_md5 and + d['VERSION'] == PICKLE_FORMAT_VERSION): self._test_methods = d['TEST_METHODS'] return True except: @@ -182,7 +194,8 @@ logging.info('Storing proguard output to %s', self._pickled_proguard_name) d = {'VERSION': PICKLE_FORMAT_VERSION, - 'TEST_METHODS': self._test_methods} + 'TEST_METHODS': self._test_methods, + 'JAR_MD5SUM': self._CalculateMd5(self._jar_path)} with open(self._pickled_proguard_name, 'w') as f: f.write(pickle.dumps(d))
diff --git a/build/android/pylib/instrumentation/test_result.py b/build/android/pylib/instrumentation/test_result.py index a0eea65..24e80a8 100644 --- a/build/android/pylib/instrumentation/test_result.py +++ b/build/android/pylib/instrumentation/test_result.py
@@ -18,7 +18,8 @@ dur: Duration of the test run in milliseconds. log: A string listing any errors. """ - super(InstrumentationTestResult, self).__init__(full_name, test_type, log) + super(InstrumentationTestResult, self).__init__( + full_name, test_type, dur, log) name_pieces = full_name.rsplit('#') if len(name_pieces) > 1: self._test_name = name_pieces[1] @@ -27,8 +28,3 @@ self._class_name = full_name self._test_name = full_name self._start_date = start_date - self._dur = dur - - def GetDur(self): - """Get the test duration.""" - return self._dur
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py index 3bac503..60e00f3 100644 --- a/build/android/pylib/instrumentation/test_runner.py +++ b/build/android/pylib/instrumentation/test_runner.py
@@ -369,16 +369,11 @@ Returns: The raw output of am instrument as a list of lines. """ - # Build the 'am instrument' command - instrumentation_path = ( - '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner)) - - cmd = ['am', 'instrument', '-r'] - for k, v in self._GetInstrumentationArgs().iteritems(): - cmd.extend(['-e', k, v]) - cmd.extend(['-e', 'class', test]) - cmd.extend(['-w', instrumentation_path]) - return self.device.RunShellCommand(cmd, timeout=timeout, retries=0) + extras = self._GetInstrumentationArgs() + extras['class'] = test + return self.device.StartInstrumentation( + '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner), + raw=True, extras=extras, timeout=timeout, retries=0) @staticmethod def _ParseAmInstrumentRawOutput(raw_output): @@ -506,7 +501,7 @@ self.tool.GetTimeoutScale()) if (self.device.GetProp('ro.build.version.sdk') < constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN): - timeout *= 4 + timeout *= 10 start_ms = 0 duration_ms = 0
diff --git a/build/android/pylib/instrumentation/test_runner_test.py b/build/android/pylib/instrumentation/test_runner_test.py index 039bb03..0f2845e 100755 --- a/build/android/pylib/instrumentation/test_runner_test.py +++ b/build/android/pylib/instrumentation/test_runner_test.py
@@ -144,7 +144,7 @@ self.assertEqual('test.package.TestClass#testMethod', result.GetName()) self.assertEqual(base_test_result.ResultType.UNKNOWN, result.GetType()) self.assertEqual('', result.GetLog()) - self.assertEqual(1000, result.GetDur()) + self.assertEqual(1000, result.GetDuration()) def testGenerateTestResult_testPassed(self): statuses = [ @@ -253,17 +253,18 @@ def test_RunTest_verifyAdbShellCommand(self): self.instance.options.test_runner = 'MyTestRunner' - self.instance.device.RunShellCommand = mock.Mock() + self.instance.device.StartInstrumentation = mock.Mock() self.instance.test_pkg.GetPackageName = mock.Mock( return_value='test.package') self.instance._GetInstrumentationArgs = mock.Mock( return_value={'test_arg_key': 'test_arg_value'}) self.instance._RunTest('test.package.TestClass#testMethod', 100) - self.instance.device.RunShellCommand.assert_called_with( - ['am', 'instrument', '-r', - '-e', 'test_arg_key', 'test_arg_value', - '-e', 'class', 'test.package.TestClass#testMethod', - '-w', 'test.package/MyTestRunner'], + self.instance.device.StartInstrumentation.assert_called_with( + 'test.package/MyTestRunner', raw=True, + extras={ + 'test_arg_key': 'test_arg_value', + 'class': 'test.package.TestClass#testMethod' + }, timeout=100, retries=0) if __name__ == '__main__':
diff --git a/build/android/pylib/linker/test_case.py b/build/android/pylib/linker/test_case.py index 446bc84..f64a58b 100644 --- a/build/android/pylib/linker/test_case.py +++ b/build/android/pylib/linker/test_case.py
@@ -340,7 +340,7 @@ base_test_result.BaseTestResult( self.tagged_name, status, - logs)) + log=logs)) return results
diff --git a/build/android/pylib/local/__init__.py b/build/android/pylib/local/__init__.py new file mode 100644 index 0000000..4d6aabb --- /dev/null +++ b/build/android/pylib/local/__init__.py
@@ -0,0 +1,3 @@ +# 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.
diff --git a/build/android/pylib/local/local_test_server_spawner.py b/build/android/pylib/local/local_test_server_spawner.py new file mode 100644 index 0000000..77f552e --- /dev/null +++ b/build/android/pylib/local/local_test_server_spawner.py
@@ -0,0 +1,45 @@ +# 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. + +from pylib import chrome_test_server_spawner +from pylib import forwarder +from pylib.base import test_server + + +class LocalTestServerSpawner(test_server.TestServer): + + def __init__(self, port, device, tool): + super(LocalTestServerSpawner, self).__init__() + self._device = device + self._spawning_server = chrome_test_server_spawner.SpawningServer( + port, device, tool) + self._tool = tool + + @property + def server_address(self): + return self._spawning_server.server.server_address + + @property + def port(self): + return self.server_address[1] + + #override + def SetUp(self): + self._device.WriteFile( + '%s/net-test-server-ports' % self._device.GetExternalStoragePath(), + '%s:0' % str(self.port)) + forwarder.Forwarder.Map( + [(self.port, self.port)], self._device, self._tool) + self._spawning_server.Start() + + #override + def Reset(self): + self._spawning_server.CleanupState() + + #override + def TearDown(self): + self.Reset() + self._spawning_server.Stop() + forwarder.Forwarder.UnmapDevicePort(self.port, self._device) +
diff --git a/build/android/pylib/uiautomator/test_package.py b/build/android/pylib/uiautomator/test_package.py index fd9120e..cb51fdf 100644 --- a/build/android/pylib/uiautomator/test_package.py +++ b/build/android/pylib/uiautomator/test_package.py
@@ -11,6 +11,11 @@ class TestPackage(test_jar.TestJar): + + UIAUTOMATOR_PATH = 'uiautomator/' + UIAUTOMATOR_DEVICE_DIR = os.path.join(constants.TEST_EXECUTABLE_DIR, + UIAUTOMATOR_PATH) + def __init__(self, jar_path, jar_info_path): test_jar.TestJar.__init__(self, jar_info_path) @@ -24,4 +29,5 @@ # Override. def Install(self, device): - device.PushChangedFiles([(self._jar_path, constants.TEST_EXECUTABLE_DIR)]) + device.PushChangedFiles([(self._jar_path, self.UIAUTOMATOR_DEVICE_DIR + + self.GetPackageName())])
diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py index c0f8f15..f4e5730 100644 --- a/build/android/pylib/uiautomator/test_runner.py +++ b/build/android/pylib/uiautomator/test_runner.py
@@ -73,7 +73,8 @@ package=self._package), blocking=True, force_stop=True) - cmd = ['uiautomator', 'runtest', self.test_pkg.GetPackageName(), + cmd = ['uiautomator', 'runtest', + self.test_pkg.UIAUTOMATOR_PATH + self.test_pkg.GetPackageName(), '-e', 'class', test] return self.device.RunShellCommand(cmd, timeout=timeout, retries=0)
diff --git a/build/android/pylib/utils/flakiness_dashboard_results_uploader.py b/build/android/pylib/utils/flakiness_dashboard_results_uploader.py index 246c83b..ff286b6 100644 --- a/build/android/pylib/utils/flakiness_dashboard_results_uploader.py +++ b/build/android/pylib/utils/flakiness_dashboard_results_uploader.py
@@ -131,7 +131,7 @@ test_result = json_results_generator.TestResult( test=single_test_result.GetName(), failed=failed, - elapsed_time=single_test_result.GetDur() / 1000) + elapsed_time=single_test_result.GetDuration() / 1000) # The WebKit TestResult object sets the modifier it based on test name. # Since we don't use the same test naming convention as WebKit the # modifier will be wrong, so we need to overwrite it.
diff --git a/build/android/pylib/utils/isolator.py b/build/android/pylib/utils/isolator.py index 9ee5671..afbee2a 100644 --- a/build/android/pylib/utils/isolator.py +++ b/build/android/pylib/utils/isolator.py
@@ -32,7 +32,6 @@ 'component': 'static_library', 'fastbuild': '0', 'icu_use_data_file_flag': '1', - 'libpeer_target_type': 'static_library', 'lsan': '0', # TODO(maruel): This may not always be true. 'target_arch': 'arm',
diff --git a/build/android/pylib/utils/mock_calls.py b/build/android/pylib/utils/mock_calls.py new file mode 100755 index 0000000..1f9d8e3 --- /dev/null +++ b/build/android/pylib/utils/mock_calls.py
@@ -0,0 +1,175 @@ +#!/usr/bin/env python +# 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. + +""" +A test facility to assert call sequences while mocking their behavior. +""" + +import os +import sys +import unittest + +from pylib import constants + +sys.path.append(os.path.join( + constants.DIR_SOURCE_ROOT, 'third_party', 'pymock')) +import mock # pylint: disable=F0401 + + +class TestCase(unittest.TestCase): + """Adds assertCalls to TestCase objects.""" + class _AssertCalls(object): + def __init__(self, test_case, expected_calls, watched): + def call_action(pair): + if isinstance(pair, type(mock.call)): + return (pair, None) + else: + return pair + + def do_check(call): + def side_effect(*args, **kwargs): + received_call = call(*args, **kwargs) + self._test_case.assertTrue( + self._expected_calls, + msg=('Unexpected call: %s' % str(received_call))) + expected_call, action = self._expected_calls.pop(0) + self._test_case.assertTrue( + received_call == expected_call, + msg=('Expected call missmatch:\n' + ' expected: %s\n' + ' received: %s\n' + % (str(expected_call), str(received_call)))) + if callable(action): + return action(*args, **kwargs) + else: + return action + return side_effect + + self._test_case = test_case + self._expected_calls = [call_action(pair) for pair in expected_calls] + watched = watched.copy() # do not pollute the caller's dict + watched.update((call.parent.name, call.parent) + for call, _ in self._expected_calls) + self._patched = [test_case.patch_call(call, side_effect=do_check(call)) + for call in watched.itervalues()] + + def __enter__(self): + for patch in self._patched: + patch.__enter__() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + for patch in self._patched: + patch.__exit__(exc_type, exc_val, exc_tb) + if exc_type is None: + missing = ''.join(' expected: %s\n' % str(call) + for call, _ in self._expected_calls) + self._test_case.assertFalse( + missing, + msg='Expected calls not found:\n' + missing) + + def __init__(self, *args, **kwargs): + super(TestCase, self).__init__(*args, **kwargs) + self.call = mock.call.self + self._watched = {} + + def call_target(self, call): + """Resolve a self.call instance to the target it represents. + + Args: + call: a self.call instance, e.g. self.call.adb.Shell + + Returns: + The target object represented by the call, e.g. self.adb.Shell + + Raises: + ValueError if the path of the call does not start with "self", i.e. the + target of the call is external to the self object. + AttributeError if the path of the call does not specify a valid + chain of attributes (without any calls) starting from "self". + """ + path = call.name.split('.') + if path.pop(0) != 'self': + raise ValueError("Target %r outside of 'self' object" % call.name) + target = self + for attr in path: + target = getattr(target, attr) + return target + + def patch_call(self, call, **kwargs): + """Patch the target of a mock.call instance. + + Args: + call: a mock.call instance identifying a target to patch + Extra keyword arguments are processed by mock.patch + + Returns: + A context manager to mock/unmock the target of the call + """ + if call.name.startswith('self.'): + target = self.call_target(call.parent) + _, attribute = call.name.rsplit('.', 1) + return mock.patch.object(target, attribute, **kwargs) + else: + return mock.patch(call.name, **kwargs) + + def watchCalls(self, calls): + """Add calls to the set of watched calls. + + Args: + calls: a sequence of mock.call instances identifying targets to watch + """ + self._watched.update((call.name, call) for call in calls) + + def watchMethodCalls(self, call): + """Watch all public methods of the target identified by a self.call. + + Args: + call: a self.call instance indetifying an object + """ + target = self.call_target(call) + self.watchCalls(getattr(call, method) + for method in dir(target.__class__) + if not method.startswith('_')) + + def clearWatched(self): + """Clear the set of watched calls.""" + self._watched = {} + + def assertCalls(self, *calls): + """A context manager to assert that a sequence of calls is made. + + During the assertion, a number of functions and methods will be "watched", + and any calls made to them is expected to appear---in the exact same order, + and with the exact same arguments---as specified by the argument |calls|. + + By default, the targets of all expected calls are watched. Further targets + to watch may be added using watchCalls and watchMethodCalls. + + Optionaly, each call may be accompanied by an action. If the action is a + (non-callable) value, this value will be used as the return value given to + the caller when the matching call is found. Alternatively, if the action is + a callable, the action will be then called with the same arguments as the + intercepted call, so that it can provide a return value or perform other + side effects. If the action is missing, a return value of None is assumed. + + Note that mock.Mock objects are often convenient to use as a callable + action, e.g. to raise exceptions or return other objects which are + themselves callable. + + Args: + calls: each argument is either a pair (expected_call, action) or just an + expected_call, where expected_call is a mock.call instance. + + Raises: + AssertionError if the watched targets do not receive the exact sequence + of calls specified. Missing calls, extra calls, and calls with + mismatching arguments, all cause the assertion to fail. + """ + return self._AssertCalls(self, calls, self._watched) + + def assertCall(self, call, action=None): + return self.assertCalls((call, action)) +
diff --git a/build/android/pylib/utils/mock_calls_test.py b/build/android/pylib/utils/mock_calls_test.py new file mode 100755 index 0000000..1b474af --- /dev/null +++ b/build/android/pylib/utils/mock_calls_test.py
@@ -0,0 +1,159 @@ +#!/usr/bin/env python +# 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. + +""" +Unit tests for the contents of mock_calls.py. +""" + +import logging +import os +import sys +import unittest + +from pylib import constants +from pylib.utils import mock_calls + +sys.path.append(os.path.join( + constants.DIR_SOURCE_ROOT, 'third_party', 'pymock')) +import mock # pylint: disable=F0401 + + +class _DummyAdb(object): + def __str__(self): + return '0123456789abcdef' + + def Push(self, host_path, device_path): + logging.debug('(device %s) pushing %r to %r', self, host_path, device_path) + + def IsOnline(self): + logging.debug('(device %s) checking device online', self) + return True + + def Shell(self, cmd): + logging.debug('(device %s) running command %r', self, cmd) + return "nice output\n" + + def Reboot(self): + logging.debug('(device %s) rebooted!', self) + + +class TestCaseWithAssertCallsTest(mock_calls.TestCase): + def setUp(self): + self.adb = _DummyAdb() + + def ShellError(self): + def action(cmd): + raise ValueError('(device %s) command %r is not nice' % (self.adb, cmd)) + return action + + def get_answer(self): + logging.debug("called 'get_answer' of %r object", self) + return 42 + + def echo(self, thing): + logging.debug("called 'echo' of %r object", self) + return thing + + def testCallTarget_succeds(self): + self.assertEquals(self.adb.Shell, + self.call_target(self.call.adb.Shell)) + + def testCallTarget_failsExternal(self): + with self.assertRaises(ValueError): + self.call_target(mock.call.sys.getcwd) + + def testCallTarget_failsUnknownAttribute(self): + with self.assertRaises(AttributeError): + self.call_target(self.call.adb.Run) + + def testCallTarget_failsIntermediateCalls(self): + with self.assertRaises(AttributeError): + self.call_target(self.call.adb.RunShell('cmd').append) + + def testPatchCall_method(self): + self.assertEquals(42, self.get_answer()) + with self.patch_call(self.call.get_answer, return_value=123): + self.assertEquals(123, self.get_answer()) + self.assertEquals(42, self.get_answer()) + + def testPatchCall_attribute_method(self): + with self.patch_call(self.call.adb.Shell, return_value='hello'): + self.assertEquals('hello', self.adb.Shell('echo hello')) + + def testPatchCall_global(self): + with self.patch_call(mock.call.os.getcwd, return_value='/some/path'): + self.assertEquals('/some/path', os.getcwd()) + + def testPatchCall_withSideEffect(self): + with self.patch_call(self.call.adb.Shell, side_effect=ValueError): + with self.assertRaises(ValueError): + self.adb.Shell('echo hello') + + def testAssertCalls_succeeds_simple(self): + self.assertEquals(42, self.get_answer()) + with self.assertCall(self.call.get_answer(), 123): + self.assertEquals(123, self.get_answer()) + self.assertEquals(42, self.get_answer()) + + def testAssertCalls_succeeds_multiple(self): + with self.assertCalls( + (mock.call.os.getcwd(), '/some/path'), + (self.call.echo('hello'), 'hello'), + (self.call.get_answer(), 11), + self.call.adb.Push('this_file', 'that_file'), + (self.call.get_answer(), 12)): + self.assertEquals(os.getcwd(), '/some/path') + self.assertEquals('hello', self.echo('hello')) + self.assertEquals(11, self.get_answer()) + self.adb.Push('this_file', 'that_file') + self.assertEquals(12, self.get_answer()) + + def testAsserCalls_succeeds_withAction(self): + with self.assertCall( + self.call.adb.Shell('echo hello'), self.ShellError()): + with self.assertRaises(ValueError): + self.adb.Shell('echo hello') + + def testAssertCalls_fails_tooManyCalls(self): + with self.assertRaises(AssertionError): + with self.assertCalls(self.call.adb.IsOnline()): + self.adb.IsOnline() + self.adb.IsOnline() + + def testAssertCalls_fails_tooFewCalls(self): + with self.assertRaises(AssertionError): + with self.assertCalls(self.call.adb.IsOnline()): + pass + + def testAssertCalls_succeeds_extraCalls(self): + # we are not watching Reboot, so the assertion succeeds + with self.assertCalls(self.call.adb.IsOnline()): + self.adb.IsOnline() + self.adb.Reboot() + + def testAssertCalls_fails_extraCalls(self): + self.watchCalls([self.call.adb.Reboot]) + # this time we are also watching Reboot, so the assertion fails + with self.assertRaises(AssertionError): + with self.assertCalls(self.call.adb.IsOnline()): + self.adb.IsOnline() + self.adb.Reboot() + + def testAssertCalls_succeeds_NoCalls(self): + self.watchMethodCalls(self.call.adb) # we are watching all adb methods + with self.assertCalls(): + pass + + def testAssertCalls_fails_NoCalls(self): + self.watchMethodCalls(self.call.adb) + with self.assertRaises(AssertionError): + with self.assertCalls(): + self.adb.IsOnline() + + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + unittest.main(verbosity=2) +
diff --git a/build/android/test_runner.py b/build/android/test_runner.py index 2e39c3c..d5c9b35 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py
@@ -64,6 +64,11 @@ group.add_option('--build-directory', dest='build_directory', help=('Path to the directory in which build files are' ' located (should not include build type)')) + group.add_option('--output-directory', dest='output_directory', + help=('Path to the directory in which build files are' + ' located (must include build type). This will take' + ' precedence over --debug, --release and' + ' --build-directory')) group.add_option('--num_retries', dest='num_retries', type='int', default=2, help=('Number of retries for a test before ' @@ -95,6 +100,8 @@ constants.SetBuildType(options.build_type) if options.build_directory: constants.SetBuildDirectory(options.build_directory) + if options.output_directory: + constants.SetOutputDirectort(options.output_directory) if options.environment not in constants.VALID_ENVIRONMENTS: error_func('--environment must be one of: %s' % ', '.join(constants.VALID_ENVIRONMENTS))
diff --git a/build/common.gypi b/build/common.gypi index 753abfe..3ddb01a 100644 --- a/build/common.gypi +++ b/build/common.gypi
@@ -505,25 +505,9 @@ # tool explicitly. # See third_party/cld_2/cld_2.gyp for more information. # 0: Small tables, lower accuracy - # 1: Medium tables, medium accuracy # 2: Large tables, high accuracy 'cld2_table_size%': 2, - # The data acquisition mode for CLD2. Possible values are: - # static: CLD2 data is statically linked to the executable. - # standalone: CLD2 data is provided in a standalone file that is - # bundled with the executable. - # component: CLD2 data is provided as a Chrome "component" and is - # downloaded via the component updater. - # - # For more information on switching the CLD2 data source, see: - # https://sites.google.com/a/chromium.org/dev/developers/how-tos/compact-language-detector-cld-data-source-configuration - # - # This string will be exposed in chrome://translate-internals under the - # heading "CLD Data Source". This allows easy determination of which - # data source the browser was built with. - 'cld2_data_source%': 'static', - # Enable spell checker. 'enable_spellcheck%': 1, @@ -564,12 +548,6 @@ # components. 'use_icu_alternatives_on_android%': 0, - # XInput2 multitouch support is enabled by default (use_xi2_mt=2). - # Setting to zero value disables XI2 MT. When XI2 MT is enabled, - # the input value also defines the required XI2 minor minimum version. - # For example, use_xi2_mt=2 means XI2.2 or above version is required. - 'use_xi2_mt%': 2, - # Use of precompiled headers on Windows. # # This variable may be explicitly set to 1 (enabled) or 0 @@ -813,6 +791,7 @@ 'enable_basic_printing%': 0, 'enable_print_preview%': 0, 'enable_session_service%': 0, + 'enable_spellcheck%': 0, 'enable_themes%': 0, 'enable_webrtc%': 0, 'notifications%': 0, @@ -1113,7 +1092,6 @@ 'chromecast%': '<(chromecast)', 'enable_viewport%': '<(enable_viewport)', 'enable_hidpi%': '<(enable_hidpi)', - 'use_xi2_mt%':'<(use_xi2_mt)', 'image_loader_extension%': '<(image_loader_extension)', 'fastbuild%': '<(fastbuild)', 'dont_embed_build_metadata%': '<(dont_embed_build_metadata)', @@ -1186,7 +1164,6 @@ 'enable_google_now%': '<(enable_google_now)', 'cld_version%': '<(cld_version)', 'cld2_table_size%': '<(cld2_table_size)', - 'cld2_data_source%': '<(cld2_data_source)', 'enable_captive_portal_detection%': '<(enable_captive_portal_detection)', 'disable_file_support%': '<(disable_file_support)', 'disable_ftp_support%': '<(disable_ftp_support)', @@ -1488,7 +1465,6 @@ # IPC fuzzer is disabled by default. 'enable_ipc_fuzzer%': 0, - # Force disable libstdc++ debug mode. 'disable_glibcxx_debug%': 0, @@ -1509,6 +1485,12 @@ 'ozone_platform_ozonex%': 0, 'ozone_platform_test%': 0, + # Whether the browser is non-native (using Views Toolkit) on Mac. + 'mac_views_browser%': 0, + + # Experiment: http://crbug.com/426914 + 'envoy%': 0, + 'conditions': [ ['buildtype=="Official"', { # Continue to embed build meta data in Official builds, basically the @@ -2070,8 +2052,11 @@ ], }], ['OS=="android"', { - 'grit_defines': ['-t', 'android', - '-E', 'ANDROID_JAVA_TAGGED_ONLY=true'], + 'grit_defines': [ + '-t', 'android', + '-E', 'ANDROID_JAVA_TAGGED_ONLY=true', + '--no-output-all-resource-defines', + ], }], ['OS=="mac" or OS=="ios"', { 'grit_defines': ['-D', 'scale_factors=2x'], @@ -2080,7 +2065,8 @@ 'grit_defines': [ '-t', 'ios', # iOS uses a whitelist to filter resources. - '-w', '<(DEPTH)/build/ios/grit_whitelist.txt' + '-w', '<(DEPTH)/build/ios/grit_whitelist.txt', + '--no-output-all-resource-defines', ], # Enable host builds when generating with ninja-ios. @@ -2489,7 +2475,7 @@ 'mac_debug_optimization%': '0', # Use -O0 unless overridden }, { # See http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Optimize-Options.html - 'mac_release_optimization%': '3', # Use -O3 unless overridden + 'mac_release_optimization%': '2', # Use -O2 unless overridden 'mac_debug_optimization%': '0', # Use -O0 unless overridden }], ['OS=="android"', { @@ -2527,9 +2513,6 @@ # code generated by flex (used in angle) contains that keyword. # http://crbug.com/255186 '-Wno-deprecated-register', - - # TODO(hans): Clean this up. Or disable with finer granularity. - '-Wno-unused-local-typedef', ], }, 'includes': [ 'set_clang_warning_flags.gypi', ], @@ -2635,9 +2618,6 @@ ['enable_pre_sync_backup==1', { 'defines': ['ENABLE_PRE_SYNC_BACKUP'], }], - ['use_xi2_mt!=0 and use_x11==1', { - 'defines': ['USE_XI2_MT=<(use_xi2_mt)'], - }], ['image_loader_extension==1', { 'defines': ['IMAGE_LOADER_EXTENSION=1'], }], @@ -2925,11 +2905,6 @@ ['cld_version!=0', { 'defines': ['CLD_VERSION=<(cld_version)'], }], - ['cld_version==2', { - # This is used to populate the "CLD Data Source" field in: - # chrome://translate-internals - 'defines': ['CLD2_DATA_SOURCE=<(cld2_data_source)'], - }], ['enable_basic_printing==1 or enable_print_preview==1', { # Convenience define for ENABLE_BASIC_PRINTING || ENABLE_PRINT_PREVIEW. 'defines': ['ENABLE_PRINTING=1'], @@ -4209,6 +4184,13 @@ '-fsanitize=undefined', # -fsanitize=vptr is incompatible with -fno-rtti. '-fno-sanitize=vptr', + # Employ the experimental PBQP register allocator to avoid + # slow compilation on files with too many basic blocks. + # See http://crbug.com/426271. + '-mllvm -regalloc=pbqp', + # Speculatively use coalescing to slightly improve the code + # generated by PBQP regallocator. May increase compile time. + '-mllvm -pbqp-coalescing', ], 'ldflags': [ '-fsanitize=undefined',
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn index ecbef88..c51709c 100644 --- a/build/config/BUILD.gn +++ b/build/config/BUILD.gn
@@ -121,9 +121,6 @@ } if (use_x11) { defines += [ "USE_X11=1" ] - if (use_xi2_mt > 0) { - defines += [ "USE_XI2_MT=$use_xi2_mt" ] - } } if (use_allocator != "tcmalloc") { defines += [ "NO_TCMALLOC" ]
diff --git a/build/config/allocator.gni b/build/config/allocator.gni index acc21d1..2d2fed1 100644 --- a/build/config/allocator.gni +++ b/build/config/allocator.gni
@@ -1,8 +1,9 @@ # 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. - -if (is_android || cpu_arch == "mipsel" || is_mac || is_asan) { + +# TODO(GYP): Make tcmalloc work on win. +if (is_android || cpu_arch == "mipsel" || is_mac || is_asan || is_win) { _default_allocator = "none" } else { _default_allocator = "tcmalloc"
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index de29cce..d6711b8 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -260,6 +260,7 @@ _resource_packaged_apk_path = _base_apk_path + ".ap_" _packaged_apk_path = _base_apk_path + ".unfinished.apk" + _shared_resources = defined(invoker.shared_resources) && invoker.shared_resources _configuration_name = "Release" @@ -295,6 +296,10 @@ "--apk-path", rebase_path(_resource_packaged_apk_path, root_build_dir), ] + + if (_shared_resources) { + args += ["--shared-resources"] + } } action("${target_name}__package") { @@ -718,6 +723,11 @@ args += ["--v14-verify-only"] } + if (defined(invoker.shared_resources) && + invoker.shared_resources) { + args += ["--shared-resources"] + } + if (defined(invoker.all_resources_zip_path)) { all_resources_zip = invoker.all_resources_zip_path outputs += [ all_resources_zip ]
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 9c9287d..d28c481 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -477,6 +477,8 @@ # that the resources are v14-compliant (see # build/android/gyp/generate_v14_compatible_resources.py). Defaults to # false. +# shared_resources: If true make a resource package that can be loaded by a +# different application at runtime to access the package's resources. # # Example # android_resources("foo_resources") { @@ -518,6 +520,10 @@ if (defined(invoker.v14_verify_only)) { v14_verify_only = invoker.v14_verify_only } + + if (defined(invoker.shared_resources)) { + shared_resources = invoker.shared_resources + } } group(target_name) {
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 3d1b341..1c51cd0 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -745,9 +745,6 @@ # TODO(thakis): Remove, http://crbug.com/263960 "-Wno-reserved-user-defined-literal", - - # TODO(hans): Clean this up. Or disable with finer granularity. - "-Wno-unused-local-typedef", ] } if (gcc_version >= 48) { @@ -918,15 +915,28 @@ cflags = common_optimize_on_cflags ldflags = common_optimize_on_ldflags if (is_win) { + cflags -= [ + "/Os", + ] cflags += [ "/Ot", # Favor speed over size. - "/GL", # Whole program optimization. - # Disable Warning 4702 ("Unreachable code") for the WPO/PGO builds. - # Probably anything that this would catch that wouldn't be caught in a - # normal build isn't going to actually be a bug, so the incremental value - # of C4702 for PGO builds is likely very small. - "/wd4702", ] + if (is_official_build) { + # TODO(GYP): TODO(dpranke): Should these only be on in an official + # build, or on all the time? For now we'll require official build so + # that the compile is clean. + cflags += [ + "/GL", # Whole program optimization. + # Disable Warning 4702 ("Unreachable code") for the WPO/PGO builds. + # Probably anything that this would catch that wouldn't be caught in a + # normal build isn't going to actually be a bug, so the incremental + # value of C4702 for PGO builds is likely very small. + "/wd4702", + ] + ldflags += [ + "/LTCG", + ] + } } else { cflags += [ "-O2",
diff --git a/build/config/features.gni b/build/config/features.gni index a395b40..575a89c 100644 --- a/build/config/features.gni +++ b/build/config/features.gni
@@ -77,9 +77,8 @@ use_seccomp_bpf = (is_linux || is_android) && (cpu_arch == "x86" || cpu_arch == "x64" || cpu_arch == "arm") -# Enable notifications everywhere except Android/iOS. -# Android is http://crbug.com/115320 -enable_notifications = !is_android && !is_ios +# Enable notifications everywhere except iOS. +enable_notifications = !is_ios # TODO(brettw) this should be moved to net and only dependents get this define. disable_ftp_support = is_ios @@ -105,17 +104,6 @@ enable_configuration_policy = true -# The data acquisition mode for CLD2. Possible values are: -# static: CLD2 data is statically linked to the executable. -# standalone: CLD2 data is provided in a standalone file that is -# bundled with the executable. -# component: CLD2 data is provided as a Chrome "component" and is -# downloaded via the component updater. -# -# For more information on switching the CLD2 data source, see: -# https://sites.google.com/a/chromium.org/dev/developers/how-tos/compact-language-detector-cld-data-source-configuration -cld2_data_source = "static" - # Enables support for background apps. enable_background = !is_ios && !is_android
diff --git a/build/config/linux/pkg-config.py b/build/config/linux/pkg-config.py index 60304d4..a4f4703 100644 --- a/build/config/linux/pkg-config.py +++ b/build/config/linux/pkg-config.py
@@ -25,6 +25,10 @@ # # When using a sysroot, you must also specify the architecture via # "-a <arch>" where arch is either "x86" or "x64". +# +# Additionally, you can specify the option --atleast-version. This will skip +# the normal outputting of a dictionary and instead print true or false, +# depending on the return value of pkg-config for the given package. # If this is run on non-Linux platforms, just return nothing and indicate # success. This allows us to "kind of emulate" a Linux build from other @@ -106,6 +110,8 @@ parser.add_option('-v', action='append', dest='strip_out', type='string') parser.add_option('-s', action='store', dest='sysroot', type='string') parser.add_option('-a', action='store', dest='arch', type='string') +parser.add_option('--atleast-version', action='store', + dest='atleast_version', type='string') (options, args) = parser.parse_args() # Make a list of regular expressions to strip out. @@ -120,6 +126,18 @@ else: prefix = '' +if options.atleast_version: + # When asking for the return value, just run pkg-config and print the return + # value, no need to do other work. + if not subprocess.call([options.pkg_config, + "--atleast-version=" + options.atleast_version] + + args, + env=os.environ): + print "true" + else: + print "false" + sys.exit(0) + try: flag_string = subprocess.check_output( [ options.pkg_config, "--cflags", "--libs-only-l", "--libs-only-L" ] +
diff --git a/build/config/linux/pkg_config.gni b/build/config/linux/pkg_config.gni index 378863e..687afc5 100644 --- a/build/config/linux/pkg_config.gni +++ b/build/config/linux/pkg_config.gni
@@ -33,25 +33,29 @@ pkg_config = "" } +pkg_config_script = "//build/config/linux/pkg-config.py" + +# Define the args we pass to the pkg-config script for other build files that +# need to invoke it manually. +if (sysroot != "") { + # Pass the sysroot if we're using one (it requires the CPU arch also). + pkg_config_args = ["-s", sysroot, "-a", cpu_arch] +} else if (pkg_config != "") { + pkg_config_args = ["-p", pkg_config] +} else { + pkg_config_args = [] +} + template("pkg_config") { assert(defined(invoker.packages), "Variable |packages| must be defined to be a list in pkg_config.") config(target_name) { - if (sysroot != "") { - # Pass the sysroot if we're using one (it requires the CPU arch also). - args = ["-s", sysroot, "-a", cpu_arch] + invoker.packages - } else if (pkg_config != "") { - args = ["-p", pkg_config] + invoker.packages - } else { - args = invoker.packages - } - + args = pkg_config_args + invoker.packages if (defined(invoker.extra_args)) { args += invoker.extra_args } - pkgresult = exec_script("//build/config/linux/pkg-config.py", - args, "value") + pkgresult = exec_script(pkg_config_script, args, "value") include_dirs = pkgresult[0] cflags = pkgresult[1]
diff --git a/build/config/ui.gni b/build/config/ui.gni index f23cd00..dc7a059 100644 --- a/build/config/ui.gni +++ b/build/config/ui.gni
@@ -27,10 +27,6 @@ # of a replacement for GDI or GTK. use_aura = is_win || is_linux - # XInput2 multitouch support. Zero means disabled, nonzero indicates the - # minimum XI2 version. For example, use_xi2_mt=2 means XI2.2 or above. - use_xi2_mt = 2 - # True means the UI is built using the "views" framework. toolkit_views = is_win || is_chromeos || use_aura }
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt index 39e5628..aa579ce 100644 --- a/build/ios/grit_whitelist.txt +++ b/build/ios/grit_whitelist.txt
@@ -1,17 +1,11 @@ IDR_ABOUT_DOM_DISTILLER_CSS IDR_ABOUT_DOM_DISTILLER_HTML IDR_ABOUT_DOM_DISTILLER_JS +IDR_ABOUT_STATS_HTML +IDR_ABOUT_STATS_JS IDR_ABOUT_VERSION_CSS IDR_ABOUT_VERSION_HTML IDR_ABOUT_VERSION_JS -IDR_APPLE_FLAGS_HTML -IDR_AUTOFILL_CC_AMEX -IDR_AUTOFILL_CC_DINERS -IDR_AUTOFILL_CC_DISCOVER -IDR_AUTOFILL_CC_GENERIC -IDR_AUTOFILL_CC_JCB -IDR_AUTOFILL_CC_MASTERCARD -IDR_AUTOFILL_CC_VISA IDR_CONTEXTUAL_SEARCH_PROMO_HTML IDR_CONTROLLED_SETTING_MANDATORY IDR_CRASHES_HTML @@ -22,6 +16,10 @@ IDR_DEFAULT_FAVICON_32 IDR_DEFAULT_FAVICON_64 IDR_DIR_HEADER_HTML +IDR_DISTILLER_CSS +IDR_DISTILLER_JS +IDR_DOM_DISTILLER_VIEWER_HTML +IDR_DOM_DISTILLER_VIEWER_JS IDR_FLAGS_FAVICON IDR_FLAGS_HTML IDR_FLAGS_JS @@ -34,11 +32,11 @@ IDR_INCOGNITO_TAB_HTML IDR_INFOBAR_AUTOFILL_CC IDR_INFOBAR_AUTOLOGIN -IDR_INFOBAR_GEOLOCATION IDR_INFOBAR_RESTORE_SESSION IDR_INFOBAR_SAVE_PASSWORD IDR_INFOBAR_TRANSLATE_IOS IDR_INFOBAR_WARNING +IDR_IS_DISTILLABLE_JS IDR_LOCATION_BAR_HTTP IDR_NET_ERROR_HTML IDR_NET_EXPORT_HTML @@ -51,6 +49,7 @@ IDR_OMNIBOX_CLEAR_OTR_IOS IDR_OMNIBOX_CLEAR_OTR_PRESSED_IOS IDR_OMNIBOX_CLEAR_PRESSED_IOS +IDR_OMNIBOX_EXTENSION_APP IDR_OMNIBOX_HISTORY IDR_OMNIBOX_HISTORY_INCOGNITO IDR_OMNIBOX_HTTP @@ -68,6 +67,7 @@ IDR_OMNIBOX_SEARCH_SECURED IDR_OMNIBOX_STAR IDR_OMNIBOX_STAR_INCOGNITO +IDR_OTHER_DEVICES_JS IDR_PAGEINFO_BAD IDR_PAGEINFO_GOOD IDR_PAGEINFO_INFO @@ -76,7 +76,6 @@ IDR_POLICY_CSS IDR_POLICY_HTML IDR_POLICY_JS -IDR_PRERENDER IDR_PRINTER_FAVICON IDR_PRODUCT_LOGO_26 IDR_SAD_FAVICON @@ -84,8 +83,6 @@ IDR_SECURITY_INTERSTITIAL_HTML IDR_SIGNIN_INTERNALS_INDEX_HTML IDR_SIGNIN_INTERNALS_INDEX_JS -IDR_SSL_BLOCKING_HTML -IDR_SSL_ROAD_BLOCK_HTML IDR_SYNC_INTERNALS_ABOUT_JS IDR_SYNC_INTERNALS_CHROME_SYNC_JS IDR_SYNC_INTERNALS_DATA_JS @@ -100,84 +97,9 @@ IDR_TOOLBAR_SHADOW_FULL_BLEED IDR_TRANSLATE_JS IDR_UBER_UTILS_JS -IDR_WEBUI_CSS_ALERT_OVERLAY -IDR_WEBUI_CSS_APPS_COMMON -IDR_WEBUI_CSS_APPS_TOPBUTTON_BAR -IDR_WEBUI_CSS_BUBBLE -IDR_WEBUI_CSS_BUBBLE_BUTTON -IDR_WEBUI_CSS_BUTTER_BAR -IDR_WEBUI_CSS_CHROME -IDR_WEBUI_CSS_DIALOGS -IDR_WEBUI_CSS_LIST -IDR_WEBUI_CSS_MENU -IDR_WEBUI_CSS_MENU_BUTTON -IDR_WEBUI_CSS_OVERLAY -IDR_WEBUI_CSS_SPINNER -IDR_WEBUI_CSS_TABLE -IDR_WEBUI_CSS_TABS -IDR_WEBUI_CSS_THROBBER -IDR_WEBUI_CSS_TRASH -IDR_WEBUI_CSS_TREE -IDR_WEBUI_CSS_TREE_JS -IDR_WEBUI_CSS_UI_ACCOUNT_TWEAKS -IDR_WEBUI_CSS_WIDGETS -IDR_WEBUI_I18N_PROCESS_JS IDR_WEBUI_I18N_TEMPLATE2_JS -IDR_WEBUI_I18N_TEMPLATE_JS -IDR_WEBUI_IMAGES_SELECT IDR_WEBUI_JSTEMPLATE_JS -IDR_WEBUI_JS_ASSERT -IDR_WEBUI_JS_CR -IDR_WEBUI_JS_CR_EVENT_TARGET -IDR_WEBUI_JS_CR_LINK_CONTROLLER -IDR_WEBUI_JS_CR_UI -IDR_WEBUI_JS_CR_UI_ALERT_OVERLAY -IDR_WEBUI_JS_CR_UI_ARRAY_DATA_MODEL -IDR_WEBUI_JS_CR_UI_AUTOCOMPLETE_LIST -IDR_WEBUI_JS_CR_UI_BUBBLE -IDR_WEBUI_JS_CR_UI_BUBBLE_BUTTON -IDR_WEBUI_JS_CR_UI_CARD_SLIDER -IDR_WEBUI_JS_CR_UI_COMMAND -IDR_WEBUI_JS_CR_UI_CONTEXT_MENU_BUTTON -IDR_WEBUI_JS_CR_UI_CONTEXT_MENU_HANDLER -IDR_WEBUI_JS_CR_UI_DIALOGS -IDR_WEBUI_JS_CR_UI_DRAG_WRAPPER -IDR_WEBUI_JS_CR_UI_FOCUS_GRID -IDR_WEBUI_JS_CR_UI_FOCUS_MANAGER -IDR_WEBUI_JS_CR_UI_FOCUS_OUTLINE_MANAGER -IDR_WEBUI_JS_CR_UI_FOCUS_ROW -IDR_WEBUI_JS_CR_UI_GRID -IDR_WEBUI_JS_CR_UI_LIST -IDR_WEBUI_JS_CR_UI_LIST_ITEM -IDR_WEBUI_JS_CR_UI_LIST_SELECTION_CONTROLLER -IDR_WEBUI_JS_CR_UI_LIST_SELECTION_MODEL -IDR_WEBUI_JS_CR_UI_LIST_SINGLE_SELECTION_MODEL -IDR_WEBUI_JS_CR_UI_MENU -IDR_WEBUI_JS_CR_UI_MENU_BUTTON -IDR_WEBUI_JS_CR_UI_MENU_ITEM -IDR_WEBUI_JS_CR_UI_OVERLAY -IDR_WEBUI_JS_CR_UI_PAGE_MANAGER_PAGE -IDR_WEBUI_JS_CR_UI_PAGE_MANAGER_PAGE_MANAGER -IDR_WEBUI_JS_CR_UI_POSITION_UTIL -IDR_WEBUI_JS_CR_UI_REPEATING_BUTTON -IDR_WEBUI_JS_CR_UI_SPLITTER -IDR_WEBUI_JS_CR_UI_TABLE -IDR_WEBUI_JS_CR_UI_TABLE_COLUMN -IDR_WEBUI_JS_CR_UI_TABLE_COLUMN_MODEL -IDR_WEBUI_JS_CR_UI_TABLE_HEADER -IDR_WEBUI_JS_CR_UI_TABLE_LIST -IDR_WEBUI_JS_CR_UI_TABLE_SPLITTER -IDR_WEBUI_JS_CR_UI_TABS -IDR_WEBUI_JS_CR_UI_TOUCH_HANDLER -IDR_WEBUI_JS_CR_UI_TREE -IDR_WEBUI_JS_EVENT_TRACKER -IDR_WEBUI_JS_I18N_TEMPLATE_NO_PROCESS IDR_WEBUI_JS_LOAD_TIME_DATA -IDR_WEBUI_JS_MEDIA_COMMON -IDR_WEBUI_JS_PARSE_HTML_SUBSET -IDR_WEBUI_JS_UI_ACCOUNT_TWEAKS -IDR_WEBUI_JS_UTIL -IDR_WEBUI_JS_WEBUI_RESOURCE_TEST IDS_ABOUT_MAC IDS_ABOUT_VERSION_COMMAND_LINE IDS_ABOUT_VERSION_COMPANY_NAME @@ -1151,6 +1073,7 @@ IDS_SYNC_UNRECOVERABLE_ERROR_HELP_URL IDS_SYNC_UPGRADE_CLIENT IDS_SYSTEM_FLAGS_OWNER_ONLY +IDS_TERMS_HTML IDS_TIME_DAYS_1ST_DEFAULT IDS_TIME_DAYS_1ST_FEW IDS_TIME_DAYS_1ST_MANY
diff --git a/build/java_apk.gypi b/build/java_apk.gypi index 18e6ccf..4243554 100644 --- a/build/java_apk.gypi +++ b/build/java_apk.gypi
@@ -44,6 +44,8 @@ # shared library to be included in this apk. A stripped copy of the # library will be included in the apk. # resource_dir - The directory for resources. +# shared_resources - Make a resource package that can be loaded by a different +# application at runtime to access the package's resources. # R_package - A custom Java package to generate the resource file R.java in. # By default, the package given in AndroidManifest.xml will be used. # use_chromium_linker - Enable the content dynamic linker that allows sharing the @@ -126,6 +128,7 @@ 'resource_zip_path': '<(intermediate_dir)/<(_target_name).resources.zip', 'resource_packaged_apk_name': '<(apk_name)-resources.ap_', 'resource_packaged_apk_path': '<(intermediate_dir)/<(resource_packaged_apk_name)', + 'shared_resources%': 0, 'unsigned_apk_path': '<(intermediate_dir)/<(apk_name)-unsigned.apk', 'final_apk_path%': '<(PRODUCT_DIR)/apks/<(apk_name).apk', 'incomplete_apk_path': '<(intermediate_dir)/<(apk_name)-incomplete.apk', @@ -575,7 +578,10 @@ 'additional_res_packages=': [], }], ['res_v14_verify_only == 1', { - 'process_resources_options': ['--v14-verify-only'] + 'process_resources_options+': ['--v14-verify-only'] + }], + ['shared_resources == 1', { + 'process_resources_options+': ['--shared-resources'] }], ], }, @@ -825,10 +831,16 @@ 'action_name': 'package_resources', 'message': 'packaging resources for <(_target_name)', 'variables': { + 'package_resources_options': [], 'package_resource_zip_input_paths': [ '<(resource_zip_path)', '>@(dependencies_res_zip_paths)', ], + 'conditions': [ + ['shared_resources == 1', { + 'package_resources_options+': ['--shared-resources'] + }], + ], }, 'conditions': [ ['is_test_apk == 1', { @@ -868,6 +880,8 @@ '--no-compress', '<(extensions_to_not_compress)', '--apk-path', '<(resource_packaged_apk_path)', + + '<@(package_resources_options)', ], }, {
diff --git a/build/landmines.py b/build/landmines.py index 2ddf346..cb3bdab 100755 --- a/build/landmines.py +++ b/build/landmines.py
@@ -54,6 +54,79 @@ return os.path.abspath(ret) +def extract_gn_build_commands(build_ninja_file): + """Extracts from a build.ninja the commands to run GN. + + The commands to run GN are the gn rule and build.ninja build step at the + top of the build.ninja file. We want to keep these when deleting GN builds + since we want to preserve the command-line flags to GN. + + On error, returns the empty string.""" + result = "" + with open(build_ninja_file, 'r') as f: + # Read until the second blank line. The first thing GN writes to the file + # is the "rule gn" and the second is the section for "build build.ninja", + # separated by blank lines. + num_blank_lines = 0 + while num_blank_lines < 2: + line = f.readline() + if len(line) == 0: + return '' # Unexpected EOF. + result += line + if line[0] == '\n': + num_blank_lines = num_blank_lines + 1 + return result + +def delete_build_dir(build_dir): + # GN writes a build.ninja.d file. Note that not all GN builds have args.gn. + build_ninja_d_file = os.path.join(build_dir, 'build.ninja.d') + if not os.path.exists(build_ninja_d_file): + shutil.rmtree(build_dir) + return + + # GN builds aren't automatically regenerated when you sync. To avoid + # messing with the GN workflow, erase everything but the args file, and + # write a dummy build.ninja file that will automatically rerun GN the next + # time Ninja is run. + build_ninja_file = os.path.join(build_dir, 'build.ninja') + build_commands = extract_gn_build_commands(build_ninja_file) + + try: + gn_args_file = os.path.join(build_dir, 'args.gn') + with open(gn_args_file, 'r') as f: + args_contents = f.read() + except IOError: + args_contents = '' + + shutil.rmtree(build_dir) + + # Put back the args file (if any). + os.mkdir(build_dir) + if args_contents != '': + with open(gn_args_file, 'w') as f: + f.write(args_contents) + + # Write the build.ninja file sufficiently to regenerate itself. + with open(os.path.join(build_dir, 'build.ninja'), 'w') as f: + if build_commands != '': + f.write(build_commands) + else: + # Couldn't parse the build.ninja file, write a default thing. + f.write('''rule gn +command = gn -q gen //out/%s/ +description = Regenerating ninja files + +build build.ninja: gn +generator = 1 +depfile = build.ninja.d +''' % (os.path.split(build_dir)[1])) + + # Write a .d file for the build which references a nonexistant file. This + # will make Ninja always mark the build as dirty. + with open(build_ninja_d_file, 'w') as f: + f.write('build.ninja: nonexistant_file.gn\n') + + def clobber_if_necessary(new_landmines): """Does the work of setting, planting, and triggering landmines.""" out_dir = get_build_dir(landmine_utils.builder()) @@ -82,7 +155,7 @@ if os.path.isfile(path): os.unlink(path) elif os.path.isdir(path): - shutil.rmtree(path) + delete_build_dir(path) # Save current set of landmines for next time. with open(landmines_path, 'w') as f:
diff --git a/build/secondary/third_party/nss/BUILD.gn b/build/secondary/third_party/nss/BUILD.gn index 30364d1..a285ca1 100644 --- a/build/secondary/third_party/nss/BUILD.gn +++ b/build/secondary/third_party/nss/BUILD.gn
@@ -1159,6 +1159,10 @@ "USE_HW_AES", "INTEL_GCM", ] + sources -= [ + "nss/lib/freebl/mpi/mpi_amd64.c", + ] + } else if (cpu_arch == "x64") { sources -= [ "nss/lib/freebl/intel-aes-x86-masm.asm",
diff --git a/build/tree_truth.sh b/build/tree_truth.sh index 03d0523..617092d 100755 --- a/build/tree_truth.sh +++ b/build/tree_truth.sh
@@ -87,9 +87,10 @@ # Print a summary of the last 10 commits for each repo. tt_brief_summary() { echo "@@@BUILD_STEP Brief summary of recent CLs in every branch@@@" - for p in $@; do - echo $project - (cd $CHROME_SRC/../$p && git log -n 10 --format=" %H %s %an, %ad" | cat) + for project in $@; do + echo $project: + local full_path=$CHROME_SRC/../$project + (cd $full_path && git log -n 10 --format=" %H %s %an, %ad" | cat) echo "=================================================================" done }
diff --git a/build/whitespace_file.txt b/build/whitespace_file.txt index 78ed34c..526203f 100644 --- a/build/whitespace_file.txt +++ b/build/whitespace_file.txt
@@ -145,4 +145,3 @@ Oh god the bots are red! I'm blind! Mmmm, cronuts. If you stand on your head, you will get footprints in your hair. -
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index f753895..3b3ed6d 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -64,6 +64,7 @@ "base/switches.h", "base/tiling_data.cc", "base/tiling_data.h", + "base/time_util.h", "base/unique_notifier.cc", "base/unique_notifier.h", "base/util.h", @@ -112,11 +113,14 @@ "debug/unittest_only_benchmark.h", "debug/unittest_only_benchmark_impl.cc", "debug/unittest_only_benchmark_impl.h", + "input/input_handler.cc", "input/input_handler.h", "input/page_scale_animation.cc", "input/page_scale_animation.h", "input/layer_selection_bound.cc", "input/layer_selection_bound.h", + "input/scroll_elasticity_helper.cc", + "input/scroll_elasticity_helper.h", "input/selection_bound_type.h", "input/top_controls_manager.cc", "input/top_controls_manager.h", @@ -346,8 +350,6 @@ "resources/picture_layer_tiling_set.h", "resources/picture_pile.cc", "resources/picture_pile.h", - "resources/picture_pile_base.cc", - "resources/picture_pile_base.h", "resources/picture_pile_impl.cc", "resources/picture_pile_impl.h", "resources/pixel_buffer_raster_worker_pool.cc", @@ -532,6 +534,7 @@ "test/fake_picture_layer_impl.h", "test/fake_picture_layer_tiling_client.cc", "test/fake_picture_layer_tiling_client.h", + "test/fake_picture_pile.h", "test/fake_picture_pile_impl.cc", "test/fake_picture_pile_impl.h", "test/fake_proxy.cc",
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc index e49cf1e..f5abfa0 100644 --- a/cc/animation/animation.cc +++ b/cc/animation/animation.cc
@@ -9,6 +9,7 @@ #include "base/debug/trace_event.h" #include "base/strings/string_util.h" #include "cc/animation/animation_curve.h" +#include "cc/base/time_util.h" namespace { @@ -154,17 +155,18 @@ return false; return run_state_ == Running && iterations_ >= 0 && - iterations_ * curve_->Duration() / std::abs(playback_rate_) <= - (monotonic_time + time_offset_ - start_time_ - total_paused_time_) - .InSecondsF(); + TimeUtil::Scale(curve_->Duration(), + iterations_ / std::abs(playback_rate_)) <= + (monotonic_time + time_offset_ - start_time_ - total_paused_time_); } bool Animation::InEffect(base::TimeTicks monotonic_time) const { - return ConvertToActiveTime(monotonic_time) >= 0 || + return ConvertToActiveTime(monotonic_time) >= base::TimeDelta() || (fill_mode_ == FillModeBoth || fill_mode_ == FillModeBackwards); } -double Animation::ConvertToActiveTime(base::TimeTicks monotonic_time) const { +base::TimeDelta Animation::ConvertToActiveTime( + base::TimeTicks monotonic_time) const { base::TimeTicks trimmed = monotonic_time + time_offset_; // If we're paused, time is 'stuck' at the pause time. @@ -181,56 +183,60 @@ needs_synchronized_start_time()) trimmed = base::TimeTicks() + time_offset_; - return (trimmed - base::TimeTicks()).InSecondsF(); + return (trimmed - base::TimeTicks()); } -double Animation::TrimTimeToCurrentIteration( +base::TimeDelta Animation::TrimTimeToCurrentIteration( base::TimeTicks monotonic_time) const { // Check for valid parameters DCHECK(playback_rate_); DCHECK_GE(iteration_start_, 0); - double active_time = ConvertToActiveTime(monotonic_time); - double start_offset = iteration_start_ * curve_->Duration(); + base::TimeDelta active_time = ConvertToActiveTime(monotonic_time); + base::TimeDelta start_offset = + TimeUtil::Scale(curve_->Duration(), iteration_start_); // Return start offset if we are before the start of the animation - if (active_time < 0) + if (active_time < base::TimeDelta()) return start_offset; - // Always return zero if we have no iterations. if (!iterations_) - return 0; + return base::TimeDelta(); // Don't attempt to trim if we have no duration. - if (curve_->Duration() <= 0) - return 0; + if (curve_->Duration() <= base::TimeDelta()) + return base::TimeDelta(); - double repeated_duration = iterations_ * curve_->Duration(); - double active_duration = repeated_duration / std::abs(playback_rate_); + base::TimeDelta repeated_duration = + TimeUtil::Scale(curve_->Duration(), iterations_); + base::TimeDelta active_duration = + TimeUtil::Scale(repeated_duration, 1.0 / std::abs(playback_rate_)); // Check if we are past active duration if (iterations_ > 0 && active_time >= active_duration) active_time = active_duration; // Calculate the scaled active time - double scaled_active_time; + base::TimeDelta scaled_active_time; if (playback_rate_ < 0) scaled_active_time = - (active_time - active_duration) * playback_rate_ + start_offset; + TimeUtil::Scale((active_time - active_duration), playback_rate_) + + start_offset; else - scaled_active_time = active_time * playback_rate_ + start_offset; + scaled_active_time = + TimeUtil::Scale(active_time, playback_rate_) + start_offset; // Calculate the iteration time - double iteration_time; + base::TimeDelta iteration_time; if (scaled_active_time - start_offset == repeated_duration && fmod(iterations_ + iteration_start_, 1) == 0) iteration_time = curve_->Duration(); else - iteration_time = fmod(scaled_active_time, curve_->Duration()); + iteration_time = TimeUtil::Mod(scaled_active_time, curve_->Duration()); // Calculate the current iteration int iteration; - if (scaled_active_time <= 0) + if (scaled_active_time <= base::TimeDelta()) iteration = 0; else if (iteration_time == curve_->Duration()) iteration = ceil(iteration_start_ + iterations_ - 1);
diff --git a/cc/animation/animation.h b/cc/animation/animation.h index 7856497..80c0763 100644 --- a/cc/animation/animation.h +++ b/cc/animation/animation.h
@@ -142,7 +142,8 @@ // Takes the given absolute time, and using the start time and the number // of iterations, returns the relative time in the current iteration. - double TrimTimeToCurrentIteration(base::TimeTicks monotonic_time) const; + base::TimeDelta TrimTimeToCurrentIteration( + base::TimeTicks monotonic_time) const; scoped_ptr<Animation> CloneAndInitialize(RunState initial_run_state) const; @@ -169,7 +170,7 @@ int group_id, TargetProperty target_property); - double ConvertToActiveTime(base::TimeTicks monotonic_time) const; + base::TimeDelta ConvertToActiveTime(base::TimeTicks monotonic_time) const; scoped_ptr<AnimationCurve> curve_;
diff --git a/cc/animation/animation_curve.h b/cc/animation/animation_curve.h index 57c42c7..c03feb8 100644 --- a/cc/animation/animation_curve.h +++ b/cc/animation/animation_curve.h
@@ -6,6 +6,7 @@ #define CC_ANIMATION_ANIMATION_CURVE_H_ #include "base/memory/scoped_ptr.h" +#include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/output/filter_operations.h" #include "ui/gfx/transform.h" @@ -30,7 +31,7 @@ virtual ~AnimationCurve() {} - virtual double Duration() const = 0; + virtual base::TimeDelta Duration() const = 0; virtual CurveType Type() const = 0; virtual scoped_ptr<AnimationCurve> Clone() const = 0;
diff --git a/cc/animation/animation_unittest.cc b/cc/animation/animation_unittest.cc index b1592d6..2522f19 100644 --- a/cc/animation/animation_unittest.cc +++ b/cc/animation/animation_unittest.cc
@@ -41,166 +41,257 @@ TEST(AnimationTest, TrimTimeZeroIterations) { scoped_ptr<Animation> anim(CreateAnimation(0)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeOneIteration) { scoped_ptr<Animation> anim(CreateAnimation(1)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeOneHalfIteration) { scoped_ptr<Animation> anim(CreateAnimation(1.5)); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.9, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.9))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.9, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.9)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeInfiniteIterations) { scoped_ptr<Animation> anim(CreateAnimation(-1)); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeReverse) { scoped_ptr<Animation> anim(CreateAnimation(-1)); anim->set_direction(Animation::Reverse); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); + EXPECT_EQ( + 1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0)).InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateInfiniteIterations) { scoped_ptr<Animation> anim(CreateAnimation(-1)); anim->set_direction(Animation::Alternate); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateOneIteration) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_direction(Animation::Alternate); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateTwoIterations) { scoped_ptr<Animation> anim(CreateAnimation(2)); anim->set_direction(Animation::Alternate); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateTwoHalfIterations) { scoped_ptr<Animation> anim(CreateAnimation(2.5)); anim->set_direction(Animation::Alternate); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.50))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.75))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.50)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.75)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateReverseInfiniteIterations) { scoped_ptr<Animation> anim(CreateAnimation(-1)); anim->set_direction(Animation::AlternateReverse); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateReverseOneIteration) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_direction(Animation::AlternateReverse); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateReverseTwoIterations) { scoped_ptr<Animation> anim(CreateAnimation(2)); anim->set_direction(Animation::AlternateReverse); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeStartTime) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_start_time(TicksFromSecondsF(4)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(6.0))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(6.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeStartTimeReverse) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_start_time(TicksFromSecondsF(4)); anim->set_direction(Animation::Reverse); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(6.0))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(6.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeTimeOffset) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_time_offset(TimeDelta::FromMilliseconds(4000)); anim->set_start_time(TicksFromSecondsF(4)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeTimeOffsetReverse) { @@ -208,20 +299,28 @@ anim->set_time_offset(TimeDelta::FromMilliseconds(4000)); anim->set_start_time(TicksFromSecondsF(4)); anim->set_direction(Animation::Reverse); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeNegativeTimeOffset) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_time_offset(TimeDelta::FromMilliseconds(-4000)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeNegativeTimeOffsetReverse) { @@ -229,103 +328,149 @@ anim->set_time_offset(TimeDelta::FromMilliseconds(-4000)); anim->set_direction(Animation::Reverse); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(5.0)).InSecondsF()); } TEST(AnimationTest, TrimTimePauseResume) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.5)); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); anim->SetRunState(Animation::Running, TicksFromSecondsF(1024.0)); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); + EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5)) + .InSecondsF()); } TEST(AnimationTest, TrimTimePauseResumeReverse) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_direction(Animation::Reverse); anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0)); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.25)); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); anim->SetRunState(Animation::Running, TicksFromSecondsF(1024.0)); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.75))); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.75)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeSuspendResume) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); anim->Suspend(TicksFromSecondsF(0.5)); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); anim->Resume(TicksFromSecondsF(1024)); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); + EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeSuspendResumeReverse) { scoped_ptr<Animation> anim(CreateAnimation(1)); anim->set_direction(Animation::Reverse); anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0)); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); anim->Suspend(TicksFromSecondsF(0.75)); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); anim->Resume(TicksFromSecondsF(1024)); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.25))); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0)) + .InSecondsF()); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeZeroDuration) { scoped_ptr<Animation> anim(CreateAnimation(0, 0)); anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); } TEST(AnimationTest, TrimTimeStarting) { scoped_ptr<Animation> anim(CreateAnimation(1, 5.0)); anim->SetRunState(Animation::Starting, TicksFromSecondsF(0.0)); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); anim->set_time_offset(TimeDelta::FromMilliseconds(2000)); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); anim->set_start_time(TicksFromSecondsF(1.0)); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeNeedsSynchronizedStartTime) { scoped_ptr<Animation> anim(CreateAnimation(1, 5.0)); anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0)); anim->set_needs_synchronized_start_time(true); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); anim->set_time_offset(TimeDelta::FromMilliseconds(2000)); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); anim->set_start_time(TicksFromSecondsF(1.0)); anim->set_needs_synchronized_start_time(false); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); } TEST(AnimationTest, IsFinishedAtZeroIterations) { @@ -437,223 +582,362 @@ TEST(AnimationTest, TrimTimePlaybackNormal) { scoped_ptr<Animation> anim(CreateAnimation(1, 1, 1)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackSlow) { scoped_ptr<Animation> anim(CreateAnimation(1, 1, 0.5)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackFast) { scoped_ptr<Animation> anim(CreateAnimation(1, 4, 2)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)).InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)).InSecondsF()); + EXPECT_EQ( + 4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); + EXPECT_EQ( + 4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackNormalReverse) { scoped_ptr<Animation> anim(CreateAnimation(1, 2, -1)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0)).InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackSlowReverse) { scoped_ptr<Animation> anim(CreateAnimation(1, 2, -0.5)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0))); - EXPECT_EQ(1.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3))); - EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0)).InSecondsF()); + EXPECT_EQ(1.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(1.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)) + .InSecondsF()); + EXPECT_EQ( + 0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3)).InSecondsF()); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackFastReverse) { scoped_ptr<Animation> anim(CreateAnimation(1, 2, -2)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0)).InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackFastInfiniteIterations) { scoped_ptr<Animation> anim(CreateAnimation(-1, 4, 4)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1000.0))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1000.5))); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)).InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)).InSecondsF()); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1000.0)) + .InSecondsF()); + EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1000.5)) + .InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackNormalDoubleReverse) { scoped_ptr<Animation> anim(CreateAnimation(1, 1, -1)); anim->set_direction(Animation::Reverse); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); } TEST(AnimationTest, TrimTimePlaybackFastDoubleReverse) { scoped_ptr<Animation> anim(CreateAnimation(1, 4, -2)); anim->set_direction(Animation::Reverse); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); + EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ( + 0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF()); + EXPECT_EQ( + 1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)).InSecondsF()); + EXPECT_EQ( + 2, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)).InSecondsF()); + EXPECT_EQ( + 3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)).InSecondsF()); + EXPECT_EQ( + 4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)).InSecondsF()); + EXPECT_EQ( + 4, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)).InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFast) { scoped_ptr<Animation> anim(CreateAnimation(2, 2, 2)); anim->set_direction(Animation::Alternate); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFastReverse) { scoped_ptr<Animation> anim(CreateAnimation(2, 2, 2)); anim->set_direction(Animation::AlternateReverse); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFastDoubleReverse) { scoped_ptr<Animation> anim(CreateAnimation(2, 2, -2)); anim->set_direction(Animation::AlternateReverse); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateReverseThreeIterationsPlaybackFastAlternateReverse) { scoped_ptr<Animation> anim(CreateAnimation(3, 2, -2)); anim->set_direction(Animation::AlternateReverse); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.75))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.25))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.75)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.25)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeAlternateReverseTwoIterationsPlaybackNormalAlternate) { scoped_ptr<Animation> anim(CreateAnimation(2, 2, -1)); anim->set_direction(Animation::Alternate); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.5)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeIterationStart) { scoped_ptr<Animation> anim(CreateAnimation(2, 1, 1)); anim->set_iteration_start(0.5); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeIterationStartAlternate) { scoped_ptr<Animation> anim(CreateAnimation(2, 1, 1)); anim->set_direction(Animation::Alternate); anim->set_iteration_start(0.3); - EXPECT_EQ(0.3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(0.3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.8, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.7))); - EXPECT_EQ(0.7, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.2))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.7))); + EXPECT_EQ(0.3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(0.3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.8, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.7)) + .InSecondsF()); + EXPECT_EQ(0.7, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.2)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.7)) + .InSecondsF()); } TEST(AnimationTest, TrimTimeIterationStartAlternateThreeIterations) { scoped_ptr<Animation> anim(CreateAnimation(3, 1, 1)); anim->set_direction(Animation::Alternate); anim->set_iteration_start(1); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.5)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5)) + .InSecondsF()); } TEST(AnimationTest, @@ -661,11 +945,16 @@ scoped_ptr<Animation> anim(CreateAnimation(3, 1, -1)); anim->set_direction(Animation::Alternate); anim->set_iteration_start(1); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); - EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0))); - EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0)) + .InSecondsF()); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.0)) + .InSecondsF()); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(3.5)) + .InSecondsF()); } TEST(AnimationTest, InEffectFillMode) {
diff --git a/cc/animation/keyframed_animation_curve.cc b/cc/animation/keyframed_animation_curve.cc index 333a5c9..a6dc8c5 100644 --- a/cc/animation/keyframed_animation_curve.cc +++ b/cc/animation/keyframed_animation_curve.cc
@@ -205,8 +205,9 @@ InsertKeyframe(keyframe.Pass(), &keyframes_); } -double KeyframedColorAnimationCurve::Duration() const { - return keyframes_.back()->Time() - keyframes_.front()->Time(); +base::TimeDelta KeyframedColorAnimationCurve::Duration() const { + return base::TimeDelta::FromSecondsD(keyframes_.back()->Time() - + keyframes_.front()->Time()); } scoped_ptr<AnimationCurve> KeyframedColorAnimationCurve::Clone() const { @@ -252,8 +253,9 @@ InsertKeyframe(keyframe.Pass(), &keyframes_); } -double KeyframedFloatAnimationCurve::Duration() const { - return keyframes_.back()->Time() - keyframes_.front()->Time(); +base::TimeDelta KeyframedFloatAnimationCurve::Duration() const { + return base::TimeDelta::FromSecondsD(keyframes_.back()->Time() - + keyframes_.front()->Time()); } scoped_ptr<AnimationCurve> KeyframedFloatAnimationCurve::Clone() const { @@ -297,8 +299,9 @@ InsertKeyframe(keyframe.Pass(), &keyframes_); } -double KeyframedTransformAnimationCurve::Duration() const { - return keyframes_.back()->Time() - keyframes_.front()->Time(); +base::TimeDelta KeyframedTransformAnimationCurve::Duration() const { + return base::TimeDelta::FromSecondsD(keyframes_.back()->Time() - + keyframes_.front()->Time()); } scoped_ptr<AnimationCurve> KeyframedTransformAnimationCurve::Clone() const { @@ -408,8 +411,9 @@ InsertKeyframe(keyframe.Pass(), &keyframes_); } -double KeyframedFilterAnimationCurve::Duration() const { - return keyframes_.back()->Time() - keyframes_.front()->Time(); +base::TimeDelta KeyframedFilterAnimationCurve::Duration() const { + return base::TimeDelta::FromSecondsD(keyframes_.back()->Time() - + keyframes_.front()->Time()); } scoped_ptr<AnimationCurve> KeyframedFilterAnimationCurve::Clone() const {
diff --git a/cc/animation/keyframed_animation_curve.h b/cc/animation/keyframed_animation_curve.h index a6e6740..ff746fe 100644 --- a/cc/animation/keyframed_animation_curve.h +++ b/cc/animation/keyframed_animation_curve.h
@@ -5,6 +5,7 @@ #ifndef CC_ANIMATION_KEYFRAMED_ANIMATION_CURVE_H_ #define CC_ANIMATION_KEYFRAMED_ANIMATION_CURVE_H_ +#include "base/time/time.h" #include "cc/animation/animation_curve.h" #include "cc/animation/timing_function.h" #include "cc/animation/transform_operations.h" @@ -126,7 +127,7 @@ } // AnimationCurve implementation - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<AnimationCurve> Clone() const override; // BackgrounColorAnimationCurve implementation @@ -156,7 +157,7 @@ } // AnimationCurve implementation - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<AnimationCurve> Clone() const override; // FloatAnimationCurve implementation @@ -187,7 +188,7 @@ } // AnimationCurve implementation - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<AnimationCurve> Clone() const override; // TransformAnimationCurve implementation @@ -224,7 +225,7 @@ } // AnimationCurve implementation - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<AnimationCurve> Clone() const override; // FilterAnimationCurve implementation
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc index b779a37..f621ad3 100644 --- a/cc/animation/layer_animation_controller.cc +++ b/cc/animation/layer_animation_controller.cc
@@ -148,7 +148,8 @@ if (!animation->InEffect(monotonic_time)) continue; - double trimmed = animation->TrimTimeToCurrentIteration(monotonic_time); + double trimmed = + animation->TrimTimeToCurrentIteration(monotonic_time).InSecondsF(); switch (animation->target_property()) { case Animation::Opacity: { AnimationEvent event(AnimationEvent::PropertyUpdate, @@ -212,7 +213,11 @@ if (!HasActiveValueObserver()) return; - DCHECK(last_tick_time_ != base::TimeTicks()); + // Animate hasn't been called, this happens if an observer has been added + // between the Commit and Draw phases. + if (last_tick_time_ == base::TimeTicks()) + return; + if (start_ready_animations) PromoteStartedAnimations(last_tick_time_, events); @@ -856,8 +861,9 @@ if (!animations_[i]->InEffect(monotonic_time)) continue; - double trimmed = - animations_[i]->TrimTimeToCurrentIteration(monotonic_time); + double trimmed = animations_[i] + ->TrimTimeToCurrentIteration(monotonic_time) + .InSecondsF(); switch (animations_[i]->target_property()) { case Animation::Transform: {
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc index 49ac0a5..4fa3d84 100644 --- a/cc/animation/layer_animation_controller_unittest.cc +++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -673,14 +673,11 @@ controller->PushAnimationUpdatesTo(controller_impl.get()); controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset)); - double duration_in_seconds = - controller_impl->GetAnimation(Animation::ScrollOffset) - ->curve() - ->Duration(); - TimeDelta duration = TimeDelta::FromMicroseconds( - duration_in_seconds * base::Time::kMicrosecondsPerSecond); + TimeDelta duration = controller_impl->GetAnimation(Animation::ScrollOffset) + ->curve() + ->Duration(); EXPECT_EQ( - duration_in_seconds, + duration, controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration()); controller->Animate(kInitialTickTime); @@ -755,12 +752,11 @@ controller->PushAnimationUpdatesTo(controller_impl.get()); controller_impl->ActivateAnimations(); EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset)); - double duration_in_seconds = - controller_impl->GetAnimation(Animation::ScrollOffset) - ->curve() - ->Duration(); + TimeDelta duration = controller_impl->GetAnimation(Animation::ScrollOffset) + ->curve() + ->Duration(); EXPECT_EQ( - duration_in_seconds, + duration, controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration()); controller->Animate(kInitialTickTime); @@ -776,8 +772,6 @@ const AnimationEvent* event = GetMostRecentPropertyUpdateEvent(events.get()); EXPECT_FALSE(event); - TimeDelta duration = TimeDelta::FromMicroseconds( - duration_in_seconds * base::Time::kMicrosecondsPerSecond); controller->NotifyAnimationStarted((*events)[0]); controller->Animate(kInitialTickTime + duration / 2); @@ -820,7 +814,7 @@ target_value, EaseInOutTimingFunction::Create().Pass())); curve->SetInitialValue(initial_value); - double duration_in_seconds = curve->Duration(); + double duration_in_seconds = curve->Duration().InSecondsF(); scoped_ptr<Animation> animation( Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc index 641c9e0..e0b1a3b 100644 --- a/cc/animation/scroll_offset_animation_curve.cc +++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -83,8 +83,8 @@ progress, initial_value_.y(), target_value_.y())); } -double ScrollOffsetAnimationCurve::Duration() const { - return total_animation_duration_.InSecondsF(); +base::TimeDelta ScrollOffsetAnimationCurve::Duration() const { + return total_animation_duration_; } AnimationCurve::CurveType ScrollOffsetAnimationCurve::Type() const {
diff --git a/cc/animation/scroll_offset_animation_curve.h b/cc/animation/scroll_offset_animation_curve.h index 0c8692a..50dfb17 100644 --- a/cc/animation/scroll_offset_animation_curve.h +++ b/cc/animation/scroll_offset_animation_curve.h
@@ -29,7 +29,7 @@ void UpdateTarget(double t, const gfx::ScrollOffset& new_target); // AnimationCurve implementation - double Duration() const override; + base::TimeDelta Duration() const override; CurveType Type() const override; scoped_ptr<AnimationCurve> Clone() const override;
diff --git a/cc/animation/scroll_offset_animation_curve_unittest.cc b/cc/animation/scroll_offset_animation_curve_unittest.cc index d89784a..d57814e 100644 --- a/cc/animation/scroll_offset_animation_curve_unittest.cc +++ b/cc/animation/scroll_offset_animation_curve_unittest.cc
@@ -19,39 +19,39 @@ EaseInOutTimingFunction::Create().Pass())); curve->SetInitialValue(target_value); - EXPECT_DOUBLE_EQ(0.0, curve->Duration()); + EXPECT_DOUBLE_EQ(0.0, curve->Duration().InSecondsF()); // x decreases, y stays the same. curve->SetInitialValue(gfx::ScrollOffset(136.f, 200.f)); - EXPECT_DOUBLE_EQ(0.1, curve->Duration()); + EXPECT_DOUBLE_EQ(0.1, curve->Duration().InSecondsF()); // x increases, y stays the same. curve->SetInitialValue(gfx::ScrollOffset(19.f, 200.f)); - EXPECT_DOUBLE_EQ(0.15, curve->Duration()); + EXPECT_DOUBLE_EQ(0.15, curve->Duration().InSecondsF()); // x stays the same, y decreases. curve->SetInitialValue(gfx::ScrollOffset(100.f, 344.f)); - EXPECT_DOUBLE_EQ(0.2, curve->Duration()); + EXPECT_DOUBLE_EQ(0.2, curve->Duration().InSecondsF()); // x stays the same, y increases. curve->SetInitialValue(gfx::ScrollOffset(100.f, 191.f)); - EXPECT_DOUBLE_EQ(0.05, curve->Duration()); + EXPECT_DOUBLE_EQ(0.05, curve->Duration().InSecondsF()); // x decreases, y decreases. curve->SetInitialValue(gfx::ScrollOffset(32500.f, 500.f)); - EXPECT_DOUBLE_EQ(3.0, curve->Duration()); + EXPECT_DOUBLE_EQ(3.0, curve->Duration().InSecondsF()); // x decreases, y increases. curve->SetInitialValue(gfx::ScrollOffset(150.f, 119.f)); - EXPECT_DOUBLE_EQ(0.15, curve->Duration()); + EXPECT_DOUBLE_EQ(0.15, curve->Duration().InSecondsF()); // x increases, y decreases. curve->SetInitialValue(gfx::ScrollOffset(0.f, 14600.f)); - EXPECT_DOUBLE_EQ(2.0, curve->Duration()); + EXPECT_DOUBLE_EQ(2.0, curve->Duration().InSecondsF()); // x increases, y increases. curve->SetInitialValue(gfx::ScrollOffset(95.f, 191.f)); - EXPECT_DOUBLE_EQ(0.05, curve->Duration()); + EXPECT_DOUBLE_EQ(0.05, curve->Duration().InSecondsF()); } TEST(ScrollOffsetAnimationCurveTest, GetValue) { @@ -63,22 +63,22 @@ EaseInOutTimingFunction::Create().Pass())); curve->SetInitialValue(initial_value); - double duration = curve->Duration(); - EXPECT_GT(curve->Duration(), 0); - EXPECT_LT(curve->Duration(), 0.1); + double duration_in_seconds = curve->Duration().InSecondsF(); + EXPECT_GT(curve->Duration().InSecondsF(), 0); + EXPECT_LT(curve->Duration().InSecondsF(), 0.1); EXPECT_EQ(AnimationCurve::ScrollOffset, curve->Type()); - EXPECT_EQ(duration, curve->Duration()); + EXPECT_EQ(duration_in_seconds, curve->Duration().InSecondsF()); EXPECT_VECTOR2DF_EQ(initial_value, curve->GetValue(-1.0)); EXPECT_VECTOR2DF_EQ(initial_value, curve->GetValue(0.0)); EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(6.f, 30.f), - curve->GetValue(duration/2.0)); - EXPECT_VECTOR2DF_EQ(target_value, curve->GetValue(duration)); - EXPECT_VECTOR2DF_EQ(target_value, curve->GetValue(duration+1.0)); + curve->GetValue(duration_in_seconds / 2.0)); + EXPECT_VECTOR2DF_EQ(target_value, curve->GetValue(duration_in_seconds)); + EXPECT_VECTOR2DF_EQ(target_value, curve->GetValue(duration_in_seconds + 1.0)); // Verify that GetValue takes the timing function into account. - gfx::ScrollOffset value = curve->GetValue(duration/4.0); + gfx::ScrollOffset value = curve->GetValue(duration_in_seconds / 4.0); EXPECT_NEAR(3.0333f, value.x(), 0.00015f); EXPECT_NEAR(37.4168f, value.y(), 0.00015f); } @@ -92,30 +92,30 @@ target_value, EaseInOutTimingFunction::Create().Pass())); curve->SetInitialValue(initial_value); - double duration = curve->Duration(); + double duration_in_seconds = curve->Duration().InSecondsF(); scoped_ptr<AnimationCurve> clone(curve->Clone().Pass()); EXPECT_EQ(AnimationCurve::ScrollOffset, clone->Type()); - EXPECT_EQ(duration, clone->Duration()); + EXPECT_EQ(duration_in_seconds, clone->Duration().InSecondsF()); EXPECT_VECTOR2DF_EQ(initial_value, clone->ToScrollOffsetAnimationCurve()->GetValue(-1.0)); EXPECT_VECTOR2DF_EQ(initial_value, clone->ToScrollOffsetAnimationCurve()->GetValue(0.0)); - EXPECT_VECTOR2DF_EQ( - gfx::ScrollOffset(6.f, 30.f), - clone->ToScrollOffsetAnimationCurve()->GetValue(duration / 2.0)); + EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(6.f, 30.f), + clone->ToScrollOffsetAnimationCurve()->GetValue( + duration_in_seconds / 2.0)); EXPECT_VECTOR2DF_EQ( target_value, - clone->ToScrollOffsetAnimationCurve()->GetValue(duration)); - EXPECT_VECTOR2DF_EQ( - target_value, - clone->ToScrollOffsetAnimationCurve()->GetValue(duration + 1.0)); + clone->ToScrollOffsetAnimationCurve()->GetValue(duration_in_seconds)); + EXPECT_VECTOR2DF_EQ(target_value, + clone->ToScrollOffsetAnimationCurve()->GetValue( + duration_in_seconds + 1.0)); // Verify that the timing function was cloned correctly. - gfx::ScrollOffset value = - clone->ToScrollOffsetAnimationCurve()->GetValue(duration / 4.0); + gfx::ScrollOffset value = clone->ToScrollOffsetAnimationCurve()->GetValue( + duration_in_seconds / 4.0); EXPECT_NEAR(3.0333f, value.x(), 0.00015f); EXPECT_NEAR(37.4168f, value.y(), 0.00015f); } @@ -127,20 +127,20 @@ ScrollOffsetAnimationCurve::Create( target_value, EaseInOutTimingFunction::Create().Pass())); curve->SetInitialValue(initial_value); - EXPECT_EQ(1.0, curve->Duration()); + EXPECT_EQ(1.0, curve->Duration().InSecondsF()); EXPECT_EQ(1800.0, curve->GetValue(0.5).y()); EXPECT_EQ(3600.0, curve->GetValue(1.0).y()); curve->UpdateTarget(0.5, gfx::ScrollOffset(0.0, 9900.0)); - EXPECT_EQ(2.0, curve->Duration()); + EXPECT_EQ(2.0, curve->Duration().InSecondsF()); EXPECT_EQ(1800.0, curve->GetValue(0.5).y()); EXPECT_NEAR(5566.49, curve->GetValue(1.0).y(), 0.01); EXPECT_EQ(9900.0, curve->GetValue(2.0).y()); curve->UpdateTarget(1.0, gfx::ScrollOffset(0.0, 7200.0)); - EXPECT_NEAR(1.674, curve->Duration(), 0.01); + EXPECT_NEAR(1.674, curve->Duration().InSecondsF(), 0.01); EXPECT_NEAR(5566.49, curve->GetValue(1.0).y(), 0.01); EXPECT_EQ(7200.0, curve->GetValue(1.674).y()); }
diff --git a/cc/base/time_util.h b/cc/base/time_util.h new file mode 100644 index 0000000..dc07e74 --- /dev/null +++ b/cc/base/time_util.h
@@ -0,0 +1,30 @@ +// 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 CC_BASE_TIME_UTIL_H_ +#define CC_BASE_TIME_UTIL_H_ + +namespace base { +class TimeDelta; +} + +namespace cc { + +class CC_EXPORT TimeUtil { + public: + static base::TimeDelta Scale(base::TimeDelta time_delta, double value) { + return base::TimeDelta::FromInternalValue(static_cast<int64>( + static_cast<double>(time_delta.ToInternalValue()) * value)); + } + + static base::TimeDelta Mod(base::TimeDelta dividend, + base::TimeDelta divisor) { + return base::TimeDelta::FromInternalValue(dividend.ToInternalValue() % + divisor.ToInternalValue()); + } +}; + +} // namespace cc + +#endif // CC_BASE_TIME_UTIL_H_
diff --git a/cc/blink/web_scroll_offset_animation_curve_impl.cc b/cc/blink/web_scroll_offset_animation_curve_impl.cc index e789631..ba55d5b 100644 --- a/cc/blink/web_scroll_offset_animation_curve_impl.cc +++ b/cc/blink/web_scroll_offset_animation_curve_impl.cc
@@ -39,7 +39,7 @@ } double WebScrollOffsetAnimationCurveImpl::duration() const { - return curve_->Duration(); + return curve_->Duration().InSecondsF(); } scoped_ptr<cc::AnimationCurve>
diff --git a/cc/cc.gyp b/cc/cc.gyp index 308a61d..fac01a7 100644 --- a/cc/cc.gyp +++ b/cc/cc.gyp
@@ -92,6 +92,7 @@ 'base/switches.h', 'base/tiling_data.cc', 'base/tiling_data.h', + 'base/time_util.h', 'base/unique_notifier.cc', 'base/unique_notifier.h', 'base/util.h', @@ -140,11 +141,14 @@ 'debug/unittest_only_benchmark.h', 'debug/unittest_only_benchmark_impl.cc', 'debug/unittest_only_benchmark_impl.h', + 'input/input_handler.cc', 'input/input_handler.h', 'input/page_scale_animation.cc', 'input/page_scale_animation.h', 'input/layer_selection_bound.cc', 'input/layer_selection_bound.h', + 'input/scroll_elasticity_helper.cc', + 'input/scroll_elasticity_helper.h', 'input/selection_bound_type.h', 'input/top_controls_manager.cc', 'input/top_controls_manager.h', @@ -381,8 +385,6 @@ 'resources/picture_layer_tiling_set.h', 'resources/picture_pile.cc', 'resources/picture_pile.h', - 'resources/picture_pile_base.cc', - 'resources/picture_pile_base.h', 'resources/picture_pile_impl.cc', 'resources/picture_pile_impl.h', 'resources/pixel_buffer_raster_worker_pool.cc', @@ -403,6 +405,7 @@ 'resources/raster_worker_pool.h', 'resources/rasterizer.cc', 'resources/rasterizer.h', + 'resources/recording_source.h', 'resources/release_callback.h', 'resources/resource.cc', 'resources/resource.h',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 75267ef..db58318 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp
@@ -170,6 +170,7 @@ 'test/fake_picture_layer_impl.h', 'test/fake_picture_layer_tiling_client.cc', 'test/fake_picture_layer_tiling_client.h', + 'test/fake_picture_pile.h', 'test/fake_picture_pile_impl.cc', 'test/fake_picture_pile_impl.h', 'test/fake_proxy.cc',
diff --git a/cc/debug/benchmark_instrumentation.cc b/cc/debug/benchmark_instrumentation.cc index 74caae8..1cb5bc3 100644 --- a/cc/debug/benchmark_instrumentation.cc +++ b/cc/debug/benchmark_instrumentation.cc
@@ -12,16 +12,7 @@ // tools/perf/measurements/rendering_stats.py accordingly. // The benchmarks search for events and their arguments by name. -void IssueMainThreadRenderingStatsEvent( - const RenderingStats::MainThreadRenderingStats& stats) { - TRACE_EVENT_INSTANT1("benchmark", - "BenchmarkInstrumentation::MainThreadRenderingStats", - TRACE_EVENT_SCOPE_THREAD, - "data", stats.AsTraceableData()); -} - -void IssueImplThreadRenderingStatsEvent( - const RenderingStats::ImplThreadRenderingStats& stats) { +void IssueImplThreadRenderingStatsEvent(const RenderingStats& stats) { TRACE_EVENT_INSTANT1("benchmark", "BenchmarkInstrumentation::ImplThreadRenderingStats", TRACE_EVENT_SCOPE_THREAD,
diff --git a/cc/debug/benchmark_instrumentation.h b/cc/debug/benchmark_instrumentation.h index bd524b9..944ac66 100644 --- a/cc/debug/benchmark_instrumentation.h +++ b/cc/debug/benchmark_instrumentation.h
@@ -42,10 +42,7 @@ DISALLOW_COPY_AND_ASSIGN(ScopedBeginFrameTask); }; -void IssueMainThreadRenderingStatsEvent( - const RenderingStats::MainThreadRenderingStats& stats); -void IssueImplThreadRenderingStatsEvent( - const RenderingStats::ImplThreadRenderingStats& stats); +void IssueImplThreadRenderingStatsEvent(const RenderingStats& stats); void CC_EXPORT IssueDisplayRenderingStatsEvent(); } // namespace benchmark_instrumentation
diff --git a/cc/debug/picture_record_benchmark.cc b/cc/debug/picture_record_benchmark.cc index 5f832ae..ab50c7c 100644 --- a/cc/debug/picture_record_benchmark.cc +++ b/cc/debug/picture_record_benchmark.cc
@@ -10,8 +10,10 @@ #include "base/values.h" #include "cc/layers/layer.h" #include "cc/layers/picture_layer.h" +#include "cc/resources/picture.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_common.h" +#include "third_party/skia/include/core/SkBBHFactory.h" #include "ui/gfx/geometry/rect.h" namespace cc {
diff --git a/cc/debug/rasterize_and_record_benchmark.cc b/cc/debug/rasterize_and_record_benchmark.cc index f24dce3..a2052ab 100644 --- a/cc/debug/rasterize_and_record_benchmark.cc +++ b/cc/debug/rasterize_and_record_benchmark.cc
@@ -15,6 +15,7 @@ #include "cc/debug/rasterize_and_record_benchmark_impl.h" #include "cc/layers/layer.h" #include "cc/layers/picture_layer.h" +#include "cc/resources/picture_pile.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_common.h" #include "ui/gfx/geometry/rect.h" @@ -106,7 +107,7 @@ gfx::Size tile_grid_size = host_->settings().default_tile_size; SkTileGridFactory::TileGridInfo tile_grid_info; - PicturePileBase::ComputeTileGridInfo(tile_grid_size, &tile_grid_info); + PicturePile::ComputeTileGridInfo(tile_grid_size, &tile_grid_info); gfx::Rect visible_content_rect = gfx::ScaleToEnclosingRect( layer->visible_content_rect(), 1.f / layer->contents_scale_x());
diff --git a/cc/debug/rasterize_and_record_benchmark_impl.cc b/cc/debug/rasterize_and_record_benchmark_impl.cc index 2ffac1b..07058d0 100644 --- a/cc/debug/rasterize_and_record_benchmark_impl.cc +++ b/cc/debug/rasterize_and_record_benchmark_impl.cc
@@ -121,6 +121,10 @@ return base_client_->GetRecycledTwinTiling(tiling); } + TilePriority::PriorityBin GetMaxTilePriorityBin() const override { + return base_client_->GetMaxTilePriorityBin(); + } + size_t GetMaxTilesForInterestArea() const override { return base_client_->GetMaxTilesForInterestArea(); }
diff --git a/cc/debug/rendering_stats.cc b/cc/debug/rendering_stats.cc index 558c882..9c62a3c 100644 --- a/cc/debug/rendering_stats.cc +++ b/cc/debug/rendering_stats.cc
@@ -18,9 +18,8 @@ void RenderingStats::TimeDeltaList::AddToTracedValue( base::debug::TracedValue* list_value) const { - std::list<base::TimeDelta>::const_iterator iter; - for (iter = values.begin(); iter != values.end(); ++iter) { - list_value->AppendDouble(iter->InMillisecondsF()); + for (const auto& value : values) { + list_value->AppendDouble(value.InMillisecondsF()); } } @@ -32,43 +31,17 @@ return values.empty() ? base::TimeDelta() : values.back(); } -RenderingStats::MainThreadRenderingStats::MainThreadRenderingStats() - : painted_pixel_count(0), recorded_pixel_count(0) { -} - -RenderingStats::MainThreadRenderingStats::~MainThreadRenderingStats() { -} - -scoped_refptr<base::debug::ConvertableToTraceFormat> -RenderingStats::MainThreadRenderingStats::AsTraceableData() const { - scoped_refptr<base::debug::TracedValue> record_data = - new base::debug::TracedValue(); - record_data->SetDouble("paint_time", paint_time.InSecondsF()); - record_data->SetInteger("painted_pixel_count", painted_pixel_count); - record_data->SetDouble("record_time", record_time.InSecondsF()); - record_data->SetInteger("recorded_pixel_count", recorded_pixel_count); - return record_data; -} - -void RenderingStats::MainThreadRenderingStats::Add( - const MainThreadRenderingStats& other) { - paint_time += other.paint_time; - painted_pixel_count += other.painted_pixel_count; - record_time += other.record_time; - recorded_pixel_count += other.recorded_pixel_count; -} - -RenderingStats::ImplThreadRenderingStats::ImplThreadRenderingStats() +RenderingStats::RenderingStats() : frame_count(0), visible_content_area(0), approximated_visible_content_area(0) { } -RenderingStats::ImplThreadRenderingStats::~ImplThreadRenderingStats() { +RenderingStats::~RenderingStats() { } scoped_refptr<base::debug::ConvertableToTraceFormat> -RenderingStats::ImplThreadRenderingStats::AsTraceableData() const { +RenderingStats::AsTraceableData() const { scoped_refptr<base::debug::TracedValue> record_data = new base::debug::TracedValue(); record_data->SetInteger("frame_count", frame_count); @@ -102,8 +75,7 @@ return record_data; } -void RenderingStats::ImplThreadRenderingStats::Add( - const ImplThreadRenderingStats& other) { +void RenderingStats::Add(const RenderingStats& other) { frame_count += other.frame_count; visible_content_area += other.visible_content_area; approximated_visible_content_area += other.approximated_visible_content_area; @@ -119,9 +91,4 @@ other.commit_to_activate_duration_estimate); } -void RenderingStats::Add(const RenderingStats& other) { - main_stats.Add(other.main_stats); - impl_stats.Add(other.impl_stats); -} - } // namespace cc
diff --git a/cc/debug/rendering_stats.h b/cc/debug/rendering_stats.h index 7b1898a..193454c 100644 --- a/cc/debug/rendering_stats.h +++ b/cc/debug/rendering_stats.h
@@ -31,51 +31,27 @@ base::TimeDelta GetLastTimeDelta() const; private: - std::list<base::TimeDelta> values; + std::vector<base::TimeDelta> values; }; - struct CC_EXPORT MainThreadRenderingStats { - // Note: when adding new members, please remember to update Add in - // rendering_stats.cc. + RenderingStats(); + ~RenderingStats(); - base::TimeDelta paint_time; - int64 painted_pixel_count; - base::TimeDelta record_time; - int64 recorded_pixel_count; + // Note: when adding new members, please remember to update Add in + // rendering_stats.cc. - MainThreadRenderingStats(); - ~MainThreadRenderingStats(); - scoped_refptr<base::debug::ConvertableToTraceFormat> AsTraceableData() - const; - void Add(const MainThreadRenderingStats& other); - }; + int64 frame_count; + int64 visible_content_area; + int64 approximated_visible_content_area; - struct CC_EXPORT ImplThreadRenderingStats { - // Note: when adding new members, please remember to update Add in - // rendering_stats.cc. + TimeDeltaList draw_duration; + TimeDeltaList draw_duration_estimate; + TimeDeltaList begin_main_frame_to_commit_duration; + TimeDeltaList begin_main_frame_to_commit_duration_estimate; + TimeDeltaList commit_to_activate_duration; + TimeDeltaList commit_to_activate_duration_estimate; - int64 frame_count; - int64 visible_content_area; - int64 approximated_visible_content_area; - - TimeDeltaList draw_duration; - TimeDeltaList draw_duration_estimate; - TimeDeltaList begin_main_frame_to_commit_duration; - TimeDeltaList begin_main_frame_to_commit_duration_estimate; - TimeDeltaList commit_to_activate_duration; - TimeDeltaList commit_to_activate_duration_estimate; - - ImplThreadRenderingStats(); - ~ImplThreadRenderingStats(); - scoped_refptr<base::debug::ConvertableToTraceFormat> AsTraceableData() - const; - void Add(const ImplThreadRenderingStats& other); - }; - - MainThreadRenderingStats main_stats; - ImplThreadRenderingStats impl_stats; - - // Add fields of |other| to the fields in this structure. + scoped_refptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const; void Add(const RenderingStats& other); };
diff --git a/cc/debug/rendering_stats_instrumentation.cc b/cc/debug/rendering_stats_instrumentation.cc index f5f2e07..c707e23 100644 --- a/cc/debug/rendering_stats_instrumentation.cc +++ b/cc/debug/rendering_stats_instrumentation.cc
@@ -18,14 +18,7 @@ RenderingStatsInstrumentation::~RenderingStatsInstrumentation() {} -RenderingStats::MainThreadRenderingStats -RenderingStatsInstrumentation::main_thread_rendering_stats() { - base::AutoLock scoped_lock(lock_); - return main_thread_rendering_stats_; -} - -RenderingStats::ImplThreadRenderingStats -RenderingStatsInstrumentation::impl_thread_rendering_stats() { +RenderingStats RenderingStatsInstrumentation::impl_thread_rendering_stats() { base::AutoLock scoped_lock(lock_); return impl_thread_rendering_stats_; } @@ -33,23 +26,15 @@ RenderingStats RenderingStatsInstrumentation::GetRenderingStats() { base::AutoLock scoped_lock(lock_); RenderingStats rendering_stats; - rendering_stats.main_stats = main_thread_rendering_stats_accu_; - rendering_stats.main_stats.Add(main_thread_rendering_stats_); - rendering_stats.impl_stats = impl_thread_rendering_stats_accu_; - rendering_stats.impl_stats.Add(impl_thread_rendering_stats_); + rendering_stats = impl_thread_rendering_stats_accu_; + rendering_stats.Add(impl_thread_rendering_stats_); return rendering_stats; } -void RenderingStatsInstrumentation::AccumulateAndClearMainThreadStats() { - base::AutoLock scoped_lock(lock_); - main_thread_rendering_stats_accu_.Add(main_thread_rendering_stats_); - main_thread_rendering_stats_ = RenderingStats::MainThreadRenderingStats(); -} - void RenderingStatsInstrumentation::AccumulateAndClearImplThreadStats() { base::AutoLock scoped_lock(lock_); impl_thread_rendering_stats_accu_.Add(impl_thread_rendering_stats_); - impl_thread_rendering_stats_ = RenderingStats::ImplThreadRenderingStats(); + impl_thread_rendering_stats_ = RenderingStats(); } base::TimeTicks RenderingStatsInstrumentation::StartRecording() const { @@ -79,26 +64,6 @@ impl_thread_rendering_stats_.frame_count += count; } -void RenderingStatsInstrumentation::AddPaint(base::TimeDelta duration, - int64 pixels) { - if (!record_rendering_stats_) - return; - - base::AutoLock scoped_lock(lock_); - main_thread_rendering_stats_.paint_time += duration; - main_thread_rendering_stats_.painted_pixel_count += pixels; -} - -void RenderingStatsInstrumentation::AddRecord(base::TimeDelta duration, - int64 pixels) { - if (!record_rendering_stats_) - return; - - base::AutoLock scoped_lock(lock_); - main_thread_rendering_stats_.record_time += duration; - main_thread_rendering_stats_.recorded_pixel_count += pixels; -} - void RenderingStatsInstrumentation::AddVisibleContentArea(int64 area) { if (!record_rendering_stats_) return;
diff --git a/cc/debug/rendering_stats_instrumentation.h b/cc/debug/rendering_stats_instrumentation.h index 734583a..8d95929 100644 --- a/cc/debug/rendering_stats_instrumentation.h +++ b/cc/debug/rendering_stats_instrumentation.h
@@ -18,19 +18,12 @@ static scoped_ptr<RenderingStatsInstrumentation> Create(); virtual ~RenderingStatsInstrumentation(); - // Return copy of current main thread rendering stats. - RenderingStats::MainThreadRenderingStats main_thread_rendering_stats(); - // Return copy of current impl thread rendering stats. - RenderingStats::ImplThreadRenderingStats impl_thread_rendering_stats(); + RenderingStats impl_thread_rendering_stats(); // Return the accumulated, combined rendering stats. RenderingStats GetRenderingStats(); - // Add current main thread rendering stats to accumulator and - // clear current stats. - void AccumulateAndClearMainThreadStats(); - // Add current impl thread rendering stats to accumulator and // clear current stats. void AccumulateAndClearImplThreadStats(); @@ -48,8 +41,6 @@ base::TimeDelta EndRecording(base::TimeTicks start_time) const; void IncrementFrameCount(int64 count); - void AddPaint(base::TimeDelta duration, int64 pixels); - void AddRecord(base::TimeDelta duration, int64 pixels); void AddVisibleContentArea(int64 area); void AddApproximatedVisibleContentArea(int64 area); void AddDrawDuration(base::TimeDelta draw_duration, @@ -65,10 +56,8 @@ RenderingStatsInstrumentation(); private: - RenderingStats::MainThreadRenderingStats main_thread_rendering_stats_; - RenderingStats::MainThreadRenderingStats main_thread_rendering_stats_accu_; - RenderingStats::ImplThreadRenderingStats impl_thread_rendering_stats_; - RenderingStats::ImplThreadRenderingStats impl_thread_rendering_stats_accu_; + RenderingStats impl_thread_rendering_stats_; + RenderingStats impl_thread_rendering_stats_accu_; bool record_rendering_stats_;
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc new file mode 100644 index 0000000..336d0d4 --- /dev/null +++ b/cc/input/input_handler.cc
@@ -0,0 +1,13 @@ +// 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 "cc/input/input_handler.h" + +namespace cc { + +InputHandlerScrollResult::InputHandlerScrollResult() + : did_scroll(false), did_overscroll_root(false) { +} + +} // namespace cc
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h index b3205a8..7b83e4b 100644 --- a/cc/input/input_handler.h +++ b/cc/input/input_handler.h
@@ -6,6 +6,7 @@ #define CC_INPUT_INPUT_HANDLER_H_ #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/base/swap_promise_monitor.h" @@ -23,6 +24,22 @@ namespace cc { class LayerScrollOffsetDelegate; +class ScrollElasticityHelper; + +struct CC_EXPORT InputHandlerScrollResult { + InputHandlerScrollResult(); + // Did any layer scroll as a result this ScrollBy call? + bool did_scroll; + // Was any of the scroll delta argument to this ScrollBy call not used? + bool did_overscroll_root; + // The total overscroll that has been accumulated by all ScrollBy calls that + // have had overscroll since the last ScrollBegin call. This resets upon a + // ScrollBy with no overscroll. + gfx::Vector2dF accumulated_root_overscroll; + // The amount of the scroll delta argument to this ScrollBy call that was not + // used for scrolling. + gfx::Vector2dF unused_scroll_delta; +}; class CC_EXPORT InputHandlerClient { public: @@ -32,13 +49,6 @@ virtual void Animate(base::TimeTicks time) = 0; virtual void MainThreadHasStoppedFlinging() = 0; - // Called when scroll deltas reaching the root scrolling layer go unused. - // The accumulated overscroll is scoped by the most recent call to - // InputHandler::ScrollBegin. - virtual void DidOverscroll(const gfx::PointF& causal_event_viewport_point, - const gfx::Vector2dF& accumulated_overscroll, - const gfx::Vector2dF& latest_overscroll_delta) = 0; - protected: InputHandlerClient() {} @@ -85,15 +95,16 @@ // should be in viewport (logical pixel) coordinates. Otherwise they are in // scrolling layer's (logical pixel) space. If there is no room to move the // layer in the requested direction, its first ancestor layer that can be - // scrolled will be moved instead. If no layer can be moved in the requested - // direction at all, then false is returned. If any layer is moved, then - // true is returned. + // scrolled will be moved instead. The return value's |did_scroll| field is + // set to false if no layer can be moved in the requested direction at all, + // and set to true if any layer is moved. // If the scroll delta hits the root layer, and the layer can no longer move, // the root overscroll accumulated within this ScrollBegin() scope is reported - // to the client. + // in the return value's |accumulated_overscroll| field. // Should only be called if ScrollBegin() returned ScrollStarted. - virtual bool ScrollBy(const gfx::Point& viewport_point, - const gfx::Vector2dF& scroll_delta) = 0; + virtual InputHandlerScrollResult ScrollBy( + const gfx::Point& viewport_point, + const gfx::Vector2dF& scroll_delta) = 0; virtual bool ScrollVerticallyByPage(const gfx::Point& viewport_point, ScrollDirection direction) = 0; @@ -140,6 +151,8 @@ virtual scoped_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor( ui::LatencyInfo* latency) = 0; + virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0; + protected: InputHandler() {} virtual ~InputHandler() {}
diff --git a/cc/input/scroll_elasticity_helper.cc b/cc/input/scroll_elasticity_helper.cc new file mode 100644 index 0000000..54da888 --- /dev/null +++ b/cc/input/scroll_elasticity_helper.cc
@@ -0,0 +1,101 @@ +// 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 "cc/input/scroll_elasticity_helper.h" + +#include "cc/layers/layer_impl.h" +#include "cc/trees/layer_tree_host_impl.h" +#include "cc/trees/layer_tree_impl.h" + +namespace cc { + +ScrollElasticityHelper::ScrollElasticityHelper(LayerTreeHostImpl* layer_tree) + : layer_tree_host_impl_(layer_tree), timer_active_(false) { +} + +ScrollElasticityHelper::~ScrollElasticityHelper() { +} + +bool ScrollElasticityHelper::AllowsHorizontalStretching() { + // The WebKit implementation has this interface because it is written in terms + // of overscrolling on a per-layer basis, not for the whole layer tree. In + // that implementation, this always returns true for the frame view's + // scrollable area. + // TODO(ccameron): This is function is redundant and may be removed. + return true; +} + +bool ScrollElasticityHelper::AllowsVerticalStretching() { + // TODO(ccameron): This is function is redundant and may be removed. + return true; +} + +gfx::Vector2dF ScrollElasticityHelper::StretchAmount() { + return stretch_offset_; +} + +bool ScrollElasticityHelper::PinnedInDirection( + const gfx::Vector2dF& direction) { + gfx::ScrollOffset scroll_offset = + layer_tree_host_impl_->active_tree()->TotalScrollOffset(); + gfx::ScrollOffset max_scroll_offset = + layer_tree_host_impl_->active_tree()->TotalMaxScrollOffset(); + bool result = false; + if (direction.x() < 0) + result |= scroll_offset.x() <= 0; + if (direction.x() > 0) + result |= scroll_offset.x() >= max_scroll_offset.x(); + if (direction.y() < 0) + result |= scroll_offset.y() <= 0; + if (direction.y() > 0) + result |= scroll_offset.y() >= max_scroll_offset.y(); + return result; +} + +bool ScrollElasticityHelper::CanScrollHorizontally() { + return layer_tree_host_impl_->active_tree()->TotalMaxScrollOffset().x() > 0; +} + +bool ScrollElasticityHelper::CanScrollVertically() { + return layer_tree_host_impl_->active_tree()->TotalMaxScrollOffset().y() > 0; +} + +gfx::Vector2dF ScrollElasticityHelper::AbsoluteScrollPosition() { + // TODO(ccameron): This is function is redundant and may be removed. + return stretch_offset_; +} + +void ScrollElasticityHelper::ImmediateScrollBy(const gfx::Vector2dF& scroll) { + // TODO(ccameron): This is function is redundant and may be removed. +} + +void ScrollElasticityHelper::ImmediateScrollByWithoutContentEdgeConstraints( + const gfx::Vector2dF& scroll) { + stretch_offset_ += scroll; + + // TODO(ccameron): Update the transform of the appropriate layer in the + // LayerTreeHostImpl, and request that a frame be drawn. +} + +void ScrollElasticityHelper::StartSnapRubberbandTimer() { + if (timer_active_) + return; + timer_active_ = true; + layer_tree_host_impl_->SetNeedsAnimate(); +} + +void ScrollElasticityHelper::StopSnapRubberbandTimer() { + timer_active_ = false; +} + +void ScrollElasticityHelper::SnapRubberbandTimerFired() { + if (timer_active_) + layer_tree_host_impl_->SetNeedsAnimate(); +} + +void ScrollElasticityHelper::AdjustScrollPositionToBoundsIfNecessary() { + // TODO(ccameron): This is function is redundant and may be removed. +} + +} // namespace cc
diff --git a/cc/input/scroll_elasticity_helper.h b/cc/input/scroll_elasticity_helper.h new file mode 100644 index 0000000..645b20c --- /dev/null +++ b/cc/input/scroll_elasticity_helper.h
@@ -0,0 +1,84 @@ +// 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 CC_INPUT_SCROLL_ELASTICITY_HELPER_H_ +#define CC_INPUT_SCROLL_ELASTICITY_HELPER_H_ + +#include "base/time/time.h" +#include "cc/base/cc_export.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace cc { + +class LayerTreeHostImpl; + +// ScrollElasticityHelper is based on +// WebKit/Source/platform/mac/ScrollElasticityController.h +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +// Interface between a LayerTreeHostImpl and the ScrollElasticityController. It +// would be possible, in principle, for LayerTreeHostImpl to implement this +// interface itself. This artificial boundary is introduced to reduce the amount +// of logic and state held directly inside LayerTreeHostImpl. +class CC_EXPORT ScrollElasticityHelper { + public: + explicit ScrollElasticityHelper(LayerTreeHostImpl* layer_tree); + ~ScrollElasticityHelper(); + + bool AllowsHorizontalStretching(); + bool AllowsVerticalStretching(); + // The amount that the view is stretched past the normal allowable bounds. + // The "overhang" amount. + gfx::Vector2dF StretchAmount(); + bool PinnedInDirection(const gfx::Vector2dF& direction); + bool CanScrollHorizontally(); + bool CanScrollVertically(); + + // Return the absolute scroll position, not relative to the scroll origin. + gfx::Vector2dF AbsoluteScrollPosition(); + + void ImmediateScrollBy(const gfx::Vector2dF& scroll); + void ImmediateScrollByWithoutContentEdgeConstraints( + const gfx::Vector2dF& scroll); + void StartSnapRubberbandTimer(); + void StopSnapRubberbandTimer(); + void SnapRubberbandTimerFired(); + + // If the current scroll position is within the overhang area, this function + // will cause + // the page to scroll to the nearest boundary point. + void AdjustScrollPositionToBoundsIfNecessary(); + + private: + LayerTreeHostImpl* layer_tree_host_impl_; + gfx::Vector2dF stretch_offset_; + bool timer_active_; +}; + +} // namespace cc + +#endif // CC_INPUT_SCROLL_ELASTICITY_HELPER_H_
diff --git a/cc/layers/content_layer.cc b/cc/layers/content_layer.cc index d3e9a28..88031e4 100644 --- a/cc/layers/content_layer.cc +++ b/cc/layers/content_layer.cc
@@ -57,13 +57,6 @@ if (!updater_.get()) return; - - if (host) { - updater_->set_rendering_stats_instrumentation( - host->rendering_stats_instrumentation()); - } else { - updater_->set_rendering_stats_instrumentation(nullptr); - } } void ContentLayer::SetTexturePriorities( @@ -108,7 +101,6 @@ } else { updater_ = BitmapContentLayerUpdater::Create( painter.Pass(), - rendering_stats_instrumentation(), id()); } updater_->SetOpaque(contents_opaque());
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h index 403393e..2e54083 100644 --- a/cc/layers/draw_properties.h +++ b/cc/layers/draw_properties.h
@@ -6,6 +6,7 @@ #define CC_LAYERS_DRAW_PROPERTIES_H_ #include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkXfermode.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/transform.h" @@ -17,6 +18,7 @@ struct CC_EXPORT DrawProperties { DrawProperties() : opacity(0.f), + blend_mode(SkXfermode::kSrcOver_Mode), opacity_is_animating(false), screen_space_opacity_is_animating(false), target_space_transform_is_animating(false), @@ -53,6 +55,10 @@ // opacity, or when opacity is compounded by the hierarchy. float opacity; + // DrawProperties::blend_mode may be different than LayerType::blend_mode, + // when a RenderSurface re-parents the layer's blend_mode. + SkXfermode::Mode blend_mode; + // xxx_is_animating flags are used to indicate whether the DrawProperties // are actually meaningful on the main thread. When the properties are // animating, the main thread may not have the same values that are used
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index f09761b..a99ede8 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc
@@ -255,14 +255,11 @@ } void LayerImpl::PopulateSharedQuadState(SharedQuadState* state) const { - state->SetAll(draw_properties_.target_space_transform, - draw_properties_.content_bounds, - draw_properties_.visible_content_rect, - draw_properties_.clip_rect, - draw_properties_.is_clipped, - draw_properties_.opacity, - blend_mode_, - sorting_context_id_); + state->SetAll( + draw_properties_.target_space_transform, draw_properties_.content_bounds, + draw_properties_.visible_content_rect, draw_properties_.clip_rect, + draw_properties_.is_clipped, draw_properties_.opacity, + draw_properties_.blend_mode, sorting_context_id_); } bool LayerImpl::WillDraw(DrawMode draw_mode,
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index 5fe679c..12f1dca 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h
@@ -323,6 +323,9 @@ return draw_properties_.screen_space_transform; } float draw_opacity() const { return draw_properties_.opacity; } + SkXfermode::Mode draw_blend_mode() const { + return draw_properties_.blend_mode; + } bool draw_opacity_is_animating() const { return draw_properties_.opacity_is_animating; }
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc index fcd0844..f42e228 100644 --- a/cc/layers/layer_perftest.cc +++ b/cc/layers/layer_perftest.cc
@@ -40,7 +40,7 @@ virtual void SetUp() override { layer_tree_host_ = FakeLayerTreeHost::Create(&fake_client_); layer_tree_host_->InitializeSingleThreaded( - &fake_client_, base::MessageLoopProxy::current()); + &fake_client_, base::MessageLoopProxy::current(), nullptr); } virtual void TearDown() override {
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index ecdd916..246a658 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc
@@ -42,7 +42,9 @@ public: explicit MockLayerTreeHost(FakeLayerTreeHostClient* client) : LayerTreeHost(client, nullptr, nullptr, LayerTreeSettings()) { - InitializeSingleThreaded(client, base::MessageLoopProxy::current()); + InitializeSingleThreaded(client, + base::MessageLoopProxy::current(), + nullptr); } MOCK_METHOD0(SetNeedsCommit, void()); @@ -935,7 +937,8 @@ shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(), LayerTreeSettings(), - base::MessageLoopProxy::current()).Pass(); + base::MessageLoopProxy::current(), + nullptr); } scoped_ptr<LayerTreeHost> Create(LayerTreeSettings settings) { @@ -945,7 +948,8 @@ shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(), settings, - base::MessageLoopProxy::current()).Pass(); + base::MessageLoopProxy::current(), + nullptr); } private:
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc index fcaacc7..1c766ba 100644 --- a/cc/layers/picture_image_layer_impl_unittest.cc +++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -60,9 +60,9 @@ } TestablePictureImageLayerImpl* layer = new TestablePictureImageLayerImpl(tree, id); - layer->pile_ = FakePicturePileImpl::CreateInfiniteFilledPile(); - layer->SetBounds(layer->pile_->tiling_size()); - layer->SetContentBounds(layer->pile_->tiling_size()); + layer->raster_source_ = FakePicturePileImpl::CreateInfiniteFilledPile(); + layer->SetBounds(layer->raster_source_->GetSize()); + layer->SetContentBounds(layer->raster_source_->GetSize()); return make_scoped_ptr(layer); }
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc index c56ff8b..5d45cae 100644 --- a/cc/layers/picture_layer.cc +++ b/cc/layers/picture_layer.cc
@@ -7,6 +7,7 @@ #include "base/auto_reset.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer_impl.h" +#include "cc/resources/picture_pile.h" #include "cc/trees/layer_tree_impl.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -19,6 +20,7 @@ PictureLayer::PictureLayer(ContentLayerClient* client) : client_(client), + recording_source_(new PicturePile), instrumentation_object_tracker_(id()), update_source_frame_number_(-1), can_use_lcd_text_last_frame_(can_use_lcd_text()) { @@ -37,38 +39,39 @@ int source_frame_number = layer_tree_host()->source_frame_number(); gfx::Size impl_bounds = layer_impl->bounds(); - gfx::Size pile_bounds = pile_.tiling_size(); + gfx::Size recording_source_bounds = recording_source_->GetSize(); - // If update called, then pile size must match bounds pushed to impl layer. + // If update called, then recording source size must match bounds pushed to + // impl layer. DCHECK_IMPLIES(update_source_frame_number_ == source_frame_number, - impl_bounds == pile_bounds) - << " bounds " << impl_bounds.ToString() << " pile " - << pile_bounds.ToString(); + impl_bounds == recording_source_bounds) + << " bounds " << impl_bounds.ToString() << " recording source " + << recording_source_bounds.ToString(); if (update_source_frame_number_ != source_frame_number && - pile_bounds != impl_bounds) { + recording_source_bounds != impl_bounds) { // Update may not get called for the layer (if it's not in the viewport - // for example, even though it has resized making the pile no longer - // valid. In this case just destroy the pile. - pile_.SetEmptyBounds(); + // for example, even though it has resized making the recording source no + // longer valid. In this case just destroy the recording source. + recording_source_->SetEmptyBounds(); } // Unlike other properties, invalidation must always be set on layer_impl. // See PictureLayerImpl::PushPropertiesTo for more details. layer_impl->invalidation_.Clear(); - layer_impl->invalidation_.Swap(&pile_invalidation_); - layer_impl->UpdatePile(PicturePileImpl::CreateFromOther(&pile_)); + layer_impl->invalidation_.Swap(&recording_invalidation_); + layer_impl->UpdateRasterSource(recording_source_->CreateRasterSource()); } void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) { Layer::SetLayerTreeHost(host); if (host) { - pile_.SetMinContentsScale(host->settings().minimum_contents_scale); - pile_.SetTileGridSize(host->settings().default_tile_grid_size); - pile_.set_slow_down_raster_scale_factor( + // TODO(hendrikw): Perhaps use and initialization function to do this work. + recording_source_->SetMinContentsScale( + host->settings().minimum_contents_scale); + recording_source_->SetTileGridSize(host->settings().default_tile_grid_size); + recording_source_->SetSlowdownRasterScaleFactor( host->debug_state().slow_down_raster_scale_factor); - pile_.set_show_debug_picture_borders( - host->debug_state().show_picture_borders); } } @@ -97,7 +100,8 @@ gfx::Size layer_size = paint_properties().bounds; if (last_updated_visible_content_rect_ == visible_content_rect() && - pile_.tiling_size() == layer_size && pending_invalidation_.IsEmpty()) { + recording_source_->GetSize() == layer_size && + pending_invalidation_.IsEmpty()) { // Only early out if the visible content rect of this layer hasn't changed. return updated; } @@ -110,7 +114,7 @@ // Calling paint in WebKit can sometimes cause invalidations, so save // off the invalidation prior to calling update. - pending_invalidation_.Swap(&pile_invalidation_); + pending_invalidation_.Swap(&recording_invalidation_); pending_invalidation_.Clear(); if (layer_tree_host()->settings().record_full_layer) { @@ -124,32 +128,26 @@ // to the impl side so that it drops tiles that may not have a recording // for them. DCHECK(client_); - updated |= - pile_.UpdateAndExpandInvalidation(client_, - &pile_invalidation_, - SafeOpaqueBackgroundColor(), - contents_opaque(), - client_->FillsBoundsCompletely(), - layer_size, - visible_layer_rect, - update_source_frame_number_, - Picture::RECORD_NORMALLY, - rendering_stats_instrumentation()); + updated |= recording_source_->UpdateAndExpandInvalidation( + client_, &recording_invalidation_, SafeOpaqueBackgroundColor(), + contents_opaque(), client_->FillsBoundsCompletely(), layer_size, + visible_layer_rect, update_source_frame_number_, + Picture::RECORD_NORMALLY); last_updated_visible_content_rect_ = visible_content_rect(); if (updated) { SetNeedsPushProperties(); } else { - // If this invalidation did not affect the pile, then it can be cleared as - // an optimization. - pile_invalidation_.Clear(); + // If this invalidation did not affect the recording source, then it can be + // cleared as an optimization. + recording_invalidation_.Clear(); } return updated; } void PictureLayer::SetIsMask(bool is_mask) { - pile_.set_is_mask(is_mask); + recording_source_->SetIsMask(is_mask); } bool PictureLayer::SupportsLCDText() const { @@ -166,7 +164,7 @@ } skia::RefPtr<SkPicture> PictureLayer::GetPicture() const { - // We could either flatten the PicturePile into a single SkPicture, + // We could either flatten the RecordingSource into a single SkPicture, // or paint a fresh one depending on what we intend to do with the // picture. For now we just paint a fresh one to get consistent results. if (!DrawsContent()) @@ -185,7 +183,7 @@ } bool PictureLayer::IsSuitableForGpuRasterization() const { - return pile_.is_suitable_for_gpu_rasterization(); + return recording_source_->IsSuitableForGpuRasterization(); } void PictureLayer::ClearClient() {
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h index 232dba2..2969ee1 100644 --- a/cc/layers/picture_layer.h +++ b/cc/layers/picture_layer.h
@@ -9,12 +9,12 @@ #include "cc/debug/devtools_instrumentation.h" #include "cc/debug/micro_benchmark_controller.h" #include "cc/layers/layer.h" -#include "cc/resources/picture_pile.h" #include "cc/trees/occlusion_tracker.h" namespace cc { class ContentLayerClient; +class RecordingSource; class ResourceUpdateQueue; class CC_EXPORT PictureLayer : public Layer { @@ -39,7 +39,9 @@ ContentLayerClient* client() { return client_; } - PicturePile* GetPicturePileForTesting() { return &pile_; } + RecordingSource* GetRecordingSourceForTesting() { + return recording_source_.get(); + } protected: explicit PictureLayer(ContentLayerClient* client); @@ -50,13 +52,13 @@ private: ContentLayerClient* client_; - PicturePile pile_; + scoped_ptr<RecordingSource> recording_source_; devtools_instrumentation:: ScopedLayerObjectTracker instrumentation_object_tracker_; // Invalidation to use the next time update is called. InvalidationRegion pending_invalidation_; // Invalidation from the last time update was called. - Region pile_invalidation_; + Region recording_invalidation_; gfx::Rect last_updated_visible_content_rect_; int update_source_frame_number_;
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index f9678cc..6d89140 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc
@@ -71,7 +71,7 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), twin_layer_(nullptr), - pile_(PicturePileImpl::Create()), + raster_source_(PicturePileImpl::Create()), ideal_page_scale_(0.f), ideal_device_scale_(0.f), ideal_source_scale_(0.f), @@ -122,9 +122,9 @@ twin_layer_ = layer_impl; layer_impl->twin_layer_ = this; - layer_impl->UpdatePile(pile_); + layer_impl->UpdateRasterSource(raster_source_); - DCHECK(!pile_->is_solid_color() || !tilings_->num_tilings()); + DCHECK(!raster_source_->IsSolidColor() || !tilings_->num_tilings()); // Tilings would be expensive to push, so we swap. layer_impl->tilings_.swap(tilings_); layer_impl->tilings_->SetClient(layer_impl); @@ -132,7 +132,7 @@ tilings_->SetClient(this); // Ensure that the recycle tree doesn't have any unshared tiles. - if (tilings_ && pile_->is_solid_color()) + if (tilings_ && raster_source_->IsSolidColor()) tilings_->RemoveAllTilings(); // Remove invalidated tiles from what will become a recycle tree. @@ -158,9 +158,10 @@ needs_push_properties_ = true; } -void PictureLayerImpl::UpdatePile(scoped_refptr<PicturePileImpl> pile) { +void PictureLayerImpl::UpdateRasterSource( + scoped_refptr<RasterSource> raster_source) { bool could_have_tilings = CanHaveTilings(); - pile_.swap(pile); + raster_source_.swap(raster_source); // Need to call UpdateTiles again if CanHaveTilings changed. if (could_have_tilings != CanHaveTilings()) { @@ -174,26 +175,24 @@ DCHECK(!needs_post_commit_initialization_); // The bounds and the pile size may differ if the pile wasn't updated (ie. // PictureLayer::Update didn't happen). In that case the pile will be empty. - DCHECK_IMPLIES(!pile_->tiling_size().IsEmpty(), - bounds() == pile_->tiling_size()) + DCHECK_IMPLIES(!raster_source_->GetSize().IsEmpty(), + bounds() == raster_source_->GetSize()) << " bounds " << bounds().ToString() << " pile " - << pile_->tiling_size().ToString(); + << raster_source_->GetSize().ToString(); SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); - if (pile_->is_solid_color()) { + if (raster_source_->IsSolidColor()) { PopulateSharedQuadState(shared_quad_state); AppendDebugBorderQuad( render_pass, bounds(), shared_quad_state, append_quads_data); - SolidColorLayerImpl::AppendSolidQuads(render_pass, - occlusion_in_content_space, - shared_quad_state, - visible_content_rect(), - pile_->solid_color(), - append_quads_data); + SolidColorLayerImpl::AppendSolidQuads( + render_pass, occlusion_in_content_space, shared_quad_state, + visible_content_rect(), raster_source_->GetSolidColor(), + append_quads_data); return; } @@ -210,14 +209,11 @@ occlusion_in_content_space.GetOcclusionWithGivenDrawTransform( scaled_draw_transform); - shared_quad_state->SetAll(scaled_draw_transform, - scaled_content_bounds, - scaled_visible_content_rect, - draw_properties().clip_rect, - draw_properties().is_clipped, - draw_properties().opacity, - blend_mode(), - sorting_context_id_); + shared_quad_state->SetAll( + scaled_draw_transform, scaled_content_bounds, scaled_visible_content_rect, + draw_properties().clip_rect, draw_properties().is_clipped, + draw_properties().opacity, draw_properties().blend_mode, + sorting_context_id_); if (current_draw_mode_ == DRAW_MODE_RESOURCELESS_SOFTWARE) { AppendDebugBorderQuad( @@ -241,16 +237,9 @@ PictureDrawQuad* quad = render_pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - quad->SetNew(shared_quad_state, - geometry_rect, - opaque_rect, - visible_geometry_rect, - texture_rect, - texture_size, - RGBA_8888, - quad_content_rect, - max_contents_scale, - pile_); + quad->SetNew(shared_quad_state, geometry_rect, opaque_rect, + visible_geometry_rect, texture_rect, texture_size, RGBA_8888, + quad_content_rect, max_contents_scale, raster_source_); return; } @@ -382,16 +371,10 @@ resource_provider->memory_efficient_texture_format(); PictureDrawQuad* quad = render_pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - quad->SetNew(shared_quad_state, - geometry_rect, - opaque_rect, - visible_geometry_rect, - texture_rect, - iter.texture_size(), - format, - iter->content_rect(), - iter->contents_scale(), - pile_); + quad->SetNew(shared_quad_state, geometry_rect, opaque_rect, + visible_geometry_rect, texture_rect, iter.texture_size(), + format, iter->content_rect(), iter->contents_scale(), + raster_source_); has_draw_quad = true; break; } @@ -506,7 +489,7 @@ draw_properties().screen_space_transform_is_animating; if (draw_transform_is_animating()) - pile_->set_likely_to_be_used_for_transform_animation(); + raster_source_->SetShouldAttemptToUseDistanceFieldText(); should_update_tile_priorities_ = true; @@ -515,7 +498,7 @@ void PictureLayerImpl::UpdateTilePriorities( const Occlusion& occlusion_in_content_space) { - DCHECK(!pile_->is_solid_color() || !tilings_->num_tilings()); + DCHECK(!raster_source_->IsSolidColor() || !tilings_->num_tilings()); double current_frame_time_in_seconds = (layer_tree_impl()->CurrentBeginFrameArgs().frame_time - base::TimeTicks()).InSecondsF(); @@ -607,7 +590,7 @@ } void PictureLayerImpl::DidBeginTracing() { - pile_->DidBeginTracing(); + raster_source_->DidBeginTracing(); } void PictureLayerImpl::ReleaseResources() { @@ -623,13 +606,13 @@ } skia::RefPtr<SkPicture> PictureLayerImpl::GetPicture() { - return pile_->GetFlattenedPicture(); + return raster_source_->GetFlattenedPicture(); } scoped_refptr<Tile> PictureLayerImpl::CreateTile(PictureLayerTiling* tiling, const gfx::Rect& content_rect) { - DCHECK(!pile_->is_solid_color()); - if (!pile_->CanRaster(tiling->contents_scale(), content_rect)) + DCHECK(!raster_source_->IsSolidColor()); + if (!raster_source_->CoversRect(content_rect, tiling->contents_scale())) return scoped_refptr<Tile>(); int flags = 0; @@ -640,21 +623,17 @@ // memory savings that we can get. Note that we don't handle solid color // masks, so we shouldn't bother analyzing those. // Bugs: crbug.com/397198, crbug.com/396908 - if (!pile_->is_mask()) + if (!raster_source_->IsMask()) flags = Tile::USE_PICTURE_ANALYSIS; return layer_tree_impl()->tile_manager()->CreateTile( - pile_.get(), - content_rect.size(), - content_rect, - tiling->contents_scale(), - id(), - layer_tree_impl()->source_frame_number(), + raster_source_.get(), content_rect.size(), content_rect, + tiling->contents_scale(), id(), layer_tree_impl()->source_frame_number(), flags); } RasterSource* PictureLayerImpl::GetRasterSource() { - return pile_.get(); + return raster_source_.get(); } const Region* PictureLayerImpl::GetPendingInvalidation() { @@ -685,6 +664,12 @@ return recycled_twin->tilings_->TilingAtScale(tiling->contents_scale()); } +TilePriority::PriorityBin PictureLayerImpl::GetMaxTilePriorityBin() const { + if (!HasValidTilePriorities()) + return TilePriority::EVENTUALLY; + return TilePriority::NOW; +} + size_t PictureLayerImpl::GetMaxTilesForInterestArea() const { return layer_tree_impl()->settings().max_tiles_for_interest_area; } @@ -714,7 +699,7 @@ int max_texture_size = layer_tree_impl()->resource_provider()->max_texture_size(); - if (pile_->is_mask()) { + if (raster_source_->IsMask()) { // Masks are not tiled, so if we can't cover the whole mask with one tile, // don't make any tiles at all. Returning an empty size signals this. if (content_bounds.width() > max_texture_size || @@ -803,10 +788,9 @@ bool synced_high_res_tiling = false; if (CanHaveTilings()) { - synced_high_res_tiling = tilings_->SyncTilings(*other->tilings_, - pile_->tiling_size(), - invalidation_, - MinimumContentsScale()); + synced_high_res_tiling = + tilings_->SyncTilings(*other->tilings_, raster_source_->GetSize(), + invalidation_, MinimumContentsScale()); } else { RemoveAllTilings(); } @@ -830,7 +814,7 @@ return; if (!CanHaveTilingWithScale(tiling->contents_scale())) return; - tilings_->AddTiling(tiling->contents_scale(), pile_->tiling_size()); + tilings_->AddTiling(tiling->contents_scale(), raster_source_->GetSize()); // If this tree needs update draw properties, then the tiling will // get updated prior to drawing or activation. If this tree does not @@ -849,7 +833,7 @@ void PictureLayerImpl::GetContentsResourceId( ResourceProvider::ResourceId* resource_id, gfx::Size* resource_size) const { - DCHECK_EQ(bounds().ToString(), pile_->tiling_size().ToString()); + DCHECK_EQ(bounds().ToString(), raster_source_->GetSize().ToString()); gfx::Rect content_rect(bounds()); PictureLayerTilingSet::CoverageIterator iter( tilings_.get(), 1.f, content_rect, ideal_contents_scale_); @@ -899,9 +883,9 @@ "contents_scale: " << contents_scale; PictureLayerTiling* tiling = - tilings_->AddTiling(contents_scale, pile_->tiling_size()); + tilings_->AddTiling(contents_scale, raster_source_->GetSize()); - DCHECK(pile_->HasRecordings()); + DCHECK(raster_source_->HasRecordings()); if (PictureLayerImpl* twin_layer = GetPendingOrActiveTwinLayer()) twin_layer->SyncTiling(tiling); @@ -1100,7 +1084,7 @@ float maximum_scale = draw_properties().maximum_animation_contents_scale; if (maximum_scale) { gfx::Size bounds_at_maximum_scale = gfx::ToCeiledSize( - gfx::ScaleSize(pile_->tiling_size(), maximum_scale)); + gfx::ScaleSize(raster_source_->GetSize(), maximum_scale)); if (bounds_at_maximum_scale.GetArea() <= layer_tree_impl()->device_viewport_size().GetArea()) can_raster_at_maximum_scale = true; @@ -1120,7 +1104,7 @@ // If this layer would create zero or one tiles at this content scale, // don't create a low res tiling. gfx::Size raster_bounds = gfx::ToCeiledSize( - gfx::ScaleSize(pile_->tiling_size(), raster_contents_scale_)); + gfx::ScaleSize(raster_source_->GetSize(), raster_contents_scale_)); gfx::Size tile_size = CalculateTileSize(raster_bounds); bool tile_covers_bounds = tile_size.width() >= raster_bounds.width() && tile_size.height() >= raster_bounds.height(); @@ -1227,8 +1211,8 @@ // then it will end up having less than one pixel of content in that // dimension. Bump the minimum contents scale up in this case to prevent // this from happening. - int min_dimension = - std::min(pile_->tiling_size().width(), pile_->tiling_size().height()); + int min_dimension = std::min(raster_source_->GetSize().width(), + raster_source_->GetSize().height()); if (!min_dimension) return setting_min; @@ -1249,11 +1233,11 @@ } bool PictureLayerImpl::CanHaveTilings() const { - if (pile_->is_solid_color()) + if (raster_source_->IsSolidColor()) return false; if (!DrawsContent()) return false; - if (!pile_->HasRecordings()) + if (!raster_source_->HasRecordings()) return false; return true; } @@ -1353,7 +1337,7 @@ state->EndArray(); state->BeginArray("pictures"); - pile_->AsValueInto(state); + raster_source_->AsValueInto(state); state->EndArray(); state->BeginArray("invalidation"); @@ -1362,12 +1346,9 @@ state->BeginArray("coverage_tiles"); for (PictureLayerTilingSet::CoverageIterator iter( - tilings_.get(), - 1.f, - gfx::Rect(pile_->tiling_size()), + tilings_.get(), 1.f, gfx::Rect(raster_source_->GetSize()), ideal_contents_scale_); - iter; - ++iter) { + iter; ++iter) { state->BeginDictionary(); state->BeginArray("geometry_rect"); @@ -1403,10 +1384,8 @@ return IsOnActiveOrPendingTree() && IsDrawnRenderSurfaceLayerListMember(); } -bool PictureLayerImpl::AllTilesRequiredForActivationAreReadyToDraw() const { - if (!layer_tree_impl()->IsPendingTree()) - return true; - +bool PictureLayerImpl::AllTilesRequiredAreReadyToDraw( + TileRequirementCheck is_tile_required_callback) const { if (!HasValidTilePriorities()) return true; @@ -1438,11 +1417,9 @@ // be out of date. It is updated in the raster/eviction iterators. // TODO(vmpstr): Remove the comment once you can't access this information // from the tile. - if (tiling->IsTileRequiredForActivation(tile) && !tile->IsReadyToDraw()) { - TRACE_EVENT_INSTANT0("cc", - "PictureLayerImpl::" - "AllTilesRequiredForActivationAreReadyToDraw not " - "ready to activate", + if ((tiling->*is_tile_required_callback)(tile) && + !tile->IsReadyToDraw()) { + TRACE_EVENT_INSTANT0("cc", "Tile required, but not ready to draw.", TRACE_EVENT_SCOPE_THREAD); return false; } @@ -1452,6 +1429,22 @@ return true; } +bool PictureLayerImpl::AllTilesRequiredForActivationAreReadyToDraw() const { + if (!layer_tree_impl()->IsPendingTree()) + return true; + + return AllTilesRequiredAreReadyToDraw( + &PictureLayerTiling::IsTileRequiredForActivationIfVisible); +} + +bool PictureLayerImpl::AllTilesRequiredForDrawAreReadyToDraw() const { + if (!layer_tree_impl()->IsActiveTree()) + return true; + + return AllTilesRequiredAreReadyToDraw( + &PictureLayerTiling::IsTileRequiredForDrawIfVisible); +} + PictureLayerImpl::LayerRasterTileIterator::LayerRasterTileIterator() : layer_(nullptr), current_stage_(arraysize(stages_)) { } @@ -1586,11 +1579,12 @@ tree_priority_(tree_priority), current_category_(PictureLayerTiling::EVENTUALLY), current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), - current_tiling_(CurrentTilingRange().start - 1u) { - // TODO(vmpstr): Once tile priorities are determined by the iterators, ensure - // that layers that don't have valid tile priorities have lowest priorities so - // they evict their tiles first (crbug.com/381704) - DCHECK(layer_->tilings_); + current_tiling_(0u) { + // Early out if the layer has no tilings. + if (!layer_->tilings_ || !layer_->tilings_->num_tilings()) + return; + + current_tiling_ = CurrentTilingRange().start - 1u; do { if (!AdvanceToNextTiling()) break;
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h index 024e315..b341bde 100644 --- a/cc/layers/picture_layer_impl.h +++ b/cc/layers/picture_layer_impl.h
@@ -126,6 +126,7 @@ const PictureLayerTiling* tiling) const override; PictureLayerTiling* GetRecycledTwinTiling( const PictureLayerTiling* tiling) override; + TilePriority::PriorityBin GetMaxTilePriorityBin() const override; size_t GetMaxTilesForInterestArea() const override; float GetSkewportTargetTimeInSeconds() const override; int GetSkewportExtrapolationLimitInContentPixels() const override; @@ -149,9 +150,11 @@ // Virtual for testing. virtual bool HasValidTilePriorities() const; bool AllTilesRequiredForActivationAreReadyToDraw() const; + bool AllTilesRequiredForDrawAreReadyToDraw() const; protected: friend class LayerRasterTileIterator; + using TileRequirementCheck = bool (PictureLayerTiling::*)(const Tile*) const; PictureLayerImpl(LayerTreeImpl* tree_impl, int id); PictureLayerTiling* AddTiling(float contents_scale); @@ -169,7 +172,7 @@ void ResetRasterScale(); gfx::Rect GetViewportForTilePriorityInContentSpace() const; PictureLayerImpl* GetRecycledTwinLayer() const; - void UpdatePile(scoped_refptr<PicturePileImpl> pile); + void UpdateRasterSource(scoped_refptr<RasterSource> raster_source); void DoPostCommitInitializationIfNeeded() { if (needs_post_commit_initialization_) @@ -180,6 +183,11 @@ bool CanHaveTilings() const; bool CanHaveTilingWithScale(float contents_scale) const; void SanityCheckTilingState() const; + // Checks if all tiles required for a certain action (e.g. activation) are + // ready to draw. is_tile_required_callback gets called on all candidate + // tiles and returns true if the tile is required for the action. + bool AllTilesRequiredAreReadyToDraw( + TileRequirementCheck is_tile_required_callback) const; bool ShouldAdjustRasterScaleDuringScaleAnimations() const; @@ -193,7 +201,7 @@ PictureLayerImpl* twin_layer_; scoped_ptr<PictureLayerTilingSet> tilings_; - scoped_refptr<PicturePileImpl> pile_; + scoped_refptr<RasterSource> raster_source_; Region invalidation_; float ideal_page_scale_;
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc index aeea2bb..7f1ce85 100644 --- a/cc/layers/picture_layer_impl_perftest.cc +++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -58,7 +58,7 @@ pending_tree->DetachLayerTree(); scoped_ptr<FakePictureLayerImpl> pending_layer = - FakePictureLayerImpl::CreateWithPile(pending_tree, 7, pile); + FakePictureLayerImpl::CreateWithRasterSource(pending_tree, 7, pile); pending_layer->SetDrawsContent(true); pending_tree->SetRootLayer(pending_layer.Pass());
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index a46386c..a75feb0 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -134,7 +134,7 @@ pending_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting(); } - void SetupPendingTree(scoped_refptr<PicturePileImpl> pile) { + void SetupPendingTree(scoped_refptr<RasterSource> raster_source) { host_impl_.CreatePendingTree(); host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f); LayerTreeImpl* pending_tree = host_impl_.pending_tree(); @@ -147,14 +147,14 @@ if (old_pending_root) { pending_layer.reset( static_cast<FakePictureLayerImpl*>(old_pending_root.release())); - pending_layer->SetPile(pile); + pending_layer->SetRasterSource(raster_source); } else { - pending_layer = - FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pile); + pending_layer = FakePictureLayerImpl::CreateWithRasterSource( + pending_tree, id_, raster_source); pending_layer->SetDrawsContent(true); } // The bounds() just mirror the pile size. - pending_layer->SetBounds(pending_layer->pile()->tiling_size()); + pending_layer->SetBounds(pending_layer->raster_source()->GetSize()); pending_tree->SetRootLayer(pending_layer.Pass()); pending_layer_ = static_cast<FakePictureLayerImpl*>( @@ -268,8 +268,8 @@ std::vector<SkRect>::const_iterator rect_iter = rects.begin(); for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) { MockCanvas mock_canvas(1000, 1000); - active_pile->RasterDirect(&mock_canvas, (*tile_iter)->content_rect(), - 1.0f); + active_pile->PlaybackToSharedCanvas(&mock_canvas, + (*tile_iter)->content_rect(), 1.0f); // This test verifies that when drawing the contents of a specific tile // at content scale 1.0, the playback canvas never receives content from @@ -658,10 +658,10 @@ ++iter) { EXPECT_FALSE(iter.full_tile_geometry_rect().IsEmpty()); // Ensure there is a recording for this tile. - bool in_pending = pending_pile->CanRaster(tiling->contents_scale(), - iter.full_tile_geometry_rect()); - bool in_active = active_pile->CanRaster(tiling->contents_scale(), - iter.full_tile_geometry_rect()); + bool in_pending = pending_pile->CoversRect(iter.full_tile_geometry_rect(), + tiling->contents_scale()); + bool in_active = active_pile->CoversRect(iter.full_tile_geometry_rect(), + tiling->contents_scale()); if (in_pending && !in_active) EXPECT_EQ(pending_pile.get(), iter->raster_source()); @@ -1132,8 +1132,15 @@ } TEST_F(PictureLayerImplTest, DontAddLowResForSmallLayers) { - gfx::Size tile_size(host_impl_.settings().default_tile_size); - SetupDefaultTrees(tile_size); + gfx::Size layer_bounds(host_impl_.settings().default_tile_size); + gfx::Size tile_size(100, 100); + + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + scoped_refptr<FakePicturePileImpl> active_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + + SetupTrees(pending_pile, active_pile); float low_res_factor = host_impl_.settings().low_res_contents_scale_factor; float device_scale = 1.f; @@ -1181,8 +1188,8 @@ ResetTilingsAndRasterScales(); // Mask layers dont create low res since they always fit on one tile. - pending_layer_->pile()->set_is_mask(true); - active_layer_->pile()->set_is_mask(true); + pending_pile->SetIsMask(true); + active_pile->SetIsMask(true); SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, @@ -1197,7 +1204,7 @@ scoped_refptr<FakePicturePileImpl> valid_pile = FakePicturePileImpl::CreateFilledPile(tile_size, gfx::Size(1000, 1000)); - valid_pile->set_is_mask(true); + valid_pile->SetIsMask(true); SetupPendingTree(valid_pile); SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false); @@ -1224,7 +1231,7 @@ scoped_refptr<FakePicturePileImpl> huge_pile = FakePicturePileImpl::CreateFilledPile( tile_size, gfx::Size(max_texture_size + 1, 10)); - huge_pile->set_is_mask(true); + huge_pile->SetIsMask(true); SetupPendingTree(huge_pile); SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false); @@ -1249,7 +1256,7 @@ scoped_refptr<FakePicturePileImpl> valid_pile = FakePicturePileImpl::CreateFilledPile(tile_size, gfx::Size(1000, 1000)); - valid_pile->set_is_mask(true); + valid_pile->SetIsMask(true); SetupPendingTree(valid_pile); float ideal_contents_scale = 1.3f; @@ -1499,7 +1506,7 @@ EXPECT_EQ(0.f, active_layer_->ideal_contents_scale()); // Push non-solid-color pending pile makes active layer can have tilings. - active_layer_->UpdatePile(pending_pile); + active_layer_->UpdateRasterSource(pending_pile); ASSERT_TRUE(active_layer_->CanHaveTilings()); // Update properties with non-solid color pile should allow tilings. @@ -2044,7 +2051,8 @@ LayerTreeImpl* pending_tree = host_impl_.pending_tree(); scoped_ptr<FakePictureLayerImpl> pending_layer = - FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pending_pile); + FakePictureLayerImpl::CreateWithRasterSource(pending_tree, id_, + pending_pile); pending_layer->SetDrawsContent(true); pending_tree->SetRootLayer(pending_layer.Pass()); @@ -3708,9 +3716,10 @@ scoped_refptr<FakePicturePileImpl> pending_pile = FakePicturePileImpl::CreateFilledPile(tile_size, bounds); - pending_pile->set_is_mask(true); - scoped_ptr<FakePictureLayerImpl> mask = FakePictureLayerImpl::CreateWithPile( - host_impl_.pending_tree(), 3, pending_pile); + pending_pile->SetIsMask(true); + scoped_ptr<FakePictureLayerImpl> mask = + FakePictureLayerImpl::CreateWithRasterSource(host_impl_.pending_tree(), 3, + pending_pile); mask->SetBounds(bounds); mask->SetContentBounds(bounds); @@ -4485,31 +4494,23 @@ FakeLayerTreeHostClient host_client(FakeLayerTreeHostClient::DIRECT_3D); scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(&host_client); host->SetRootLayer(layer); - PicturePile* pile = layer->GetPicturePileForTesting(); + RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); host_impl_.SetViewportSize(layer_bounds); int frame_number = 0; - FakeRenderingStatsInstrumentation stats_instrumentation; client.set_fill_with_nonsolid_color(!test_for_solid); Region invalidation(layer_rect); - pile->UpdateAndExpandInvalidation(&client, - &invalidation, - SK_ColorWHITE, - false, - false, - layer_bounds, - layer_rect, - frame_number++, - Picture::RECORD_NORMALLY, - &stats_instrumentation); + recording_source->UpdateAndExpandInvalidation( + &client, &invalidation, SK_ColorWHITE, false, false, layer_bounds, + layer_rect, frame_number++, Picture::RECORD_NORMALLY); - scoped_refptr<PicturePileImpl> pending_pile = - PicturePileImpl::CreateFromOther(pile); + scoped_refptr<RasterSource> pending_raster_source = + recording_source->CreateRasterSource(); - SetupPendingTree(pending_pile); + SetupPendingTree(pending_raster_source); ActivateTree(); active_layer_->set_fixed_tile_size(tile_size); @@ -4560,31 +4561,23 @@ FakeLayerTreeHostClient host_client(FakeLayerTreeHostClient::DIRECT_3D); scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(&host_client); host->SetRootLayer(layer); - PicturePile* pile = layer->GetPicturePileForTesting(); + RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); host_impl_.SetViewportSize(layer_bounds); int frame_number = 0; - FakeRenderingStatsInstrumentation stats_instrumentation; client.set_fill_with_nonsolid_color(true); Region invalidation1(layer_rect); - pile->UpdateAndExpandInvalidation(&client, - &invalidation1, - SK_ColorWHITE, - false, - false, - layer_bounds, - layer_rect, - frame_number++, - Picture::RECORD_NORMALLY, - &stats_instrumentation); + recording_source->UpdateAndExpandInvalidation( + &client, &invalidation1, SK_ColorWHITE, false, false, layer_bounds, + layer_rect, frame_number++, Picture::RECORD_NORMALLY); - scoped_refptr<PicturePileImpl> pending_pile1 = - PicturePileImpl::CreateFromOther(pile); + scoped_refptr<RasterSource> raster_source1 = + recording_source->CreateRasterSource(); - SetupPendingTree(pending_pile1); + SetupPendingTree(raster_source1); ActivateTree(); host_impl_.active_tree()->UpdateDrawProperties(); @@ -4595,21 +4588,14 @@ client.set_fill_with_nonsolid_color(false); Region invalidation2(layer_rect); - pile->UpdateAndExpandInvalidation(&client, - &invalidation2, - SK_ColorWHITE, - false, - false, - layer_bounds, - layer_rect, - frame_number++, - Picture::RECORD_NORMALLY, - &stats_instrumentation); + recording_source->UpdateAndExpandInvalidation( + &client, &invalidation2, SK_ColorWHITE, false, false, layer_bounds, + layer_rect, frame_number++, Picture::RECORD_NORMALLY); - scoped_refptr<PicturePileImpl> pending_pile2 = - PicturePileImpl::CreateFromOther(pile); + scoped_refptr<RasterSource> raster_source2 = + recording_source->CreateRasterSource(); - SetupPendingTree(pending_pile2); + SetupPendingTree(raster_source2); ActivateTree(); // We've switched to a solid color, so we should end up with no tilings.
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc index ca622aa..7873b48 100644 --- a/cc/layers/picture_layer_unittest.cc +++ b/cc/layers/picture_layer_unittest.cc
@@ -66,23 +66,23 @@ layer->PushPropertiesTo(layer_impl.get()); EXPECT_FALSE(layer_impl->CanHaveTilings()); EXPECT_TRUE(layer_impl->bounds() == gfx::Size(0, 0)); - EXPECT_EQ(gfx::Size(), layer_impl->pile()->tiling_size()); - EXPECT_FALSE(layer_impl->pile()->HasRecordings()); + EXPECT_EQ(gfx::Size(), layer_impl->raster_source()->GetSize()); + EXPECT_FALSE(layer_impl->raster_source()->HasRecordings()); } } TEST(PictureLayerTest, SuitableForGpuRasterization) { MockContentLayerClient client; scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client); - PicturePile* pile = layer->GetPicturePileForTesting(); + RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); // Layer is suitable for gpu rasterization by default. - EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); + EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); // Veto gpu rasterization. - pile->SetUnsuitableForGpuRasterizationForTesting(); - EXPECT_FALSE(pile->is_suitable_for_gpu_rasterization()); + recording_source->SetUnsuitableForGpuRasterizationForTesting(); + EXPECT_FALSE(recording_source->IsSuitableForGpuRasterization()); EXPECT_FALSE(layer->IsSuitableForGpuRasterization()); } @@ -99,7 +99,7 @@ // Tile-grid is set according to its setting. SkTileGridFactory::TileGridInfo info = - layer->GetPicturePileForTesting()->GetTileGridInfoForTesting(); + layer->GetRecordingSourceForTesting()->GetTileGridInfoForTesting(); EXPECT_EQ(info.fTileInterval.width(), 123 - 2 * info.fMargin.width()); EXPECT_EQ(info.fTileInterval.height(), 123 - 2 * info.fMargin.height()); }
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc index 68398fe..8175efa 100644 --- a/cc/layers/render_surface_unittest.cc +++ b/cc/layers/render_surface_unittest.cc
@@ -92,6 +92,9 @@ owning_layer->CreateRenderSurface(); ASSERT_TRUE(owning_layer->render_surface()); owning_layer->draw_properties().render_target = owning_layer.get(); + + SkXfermode::Mode blend_mode = SkXfermode::kSoftLight_Mode; + owning_layer->SetBlendMode(blend_mode); RenderSurfaceImpl* render_surface = owning_layer->render_surface(); root_layer->AddChild(owning_layer.Pass()); @@ -131,6 +134,7 @@ EXPECT_RECT_EQ(content_rect, gfx::Rect(shared_quad_state->visible_content_rect)); EXPECT_EQ(1.f, shared_quad_state->opacity); + EXPECT_EQ(blend_mode, shared_quad_state->blend_mode); } class TestRenderPassSink : public RenderPassSink {
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc index 25fa708..8a998f3 100644 --- a/cc/layers/scrollbar_layer_unittest.cc +++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -632,7 +632,9 @@ next_id_(1), total_ui_resource_created_(0), total_ui_resource_deleted_(0) { - InitializeSingleThreaded(client, base::MessageLoopProxy::current()); + InitializeSingleThreaded(client, + base::MessageLoopProxy::current(), + nullptr); } UIResourceId CreateUIResource(UIResourceClient* content) override {
diff --git a/cc/layers/solid_color_layer_impl_unittest.cc b/cc/layers/solid_color_layer_impl_unittest.cc index 768fae4..320345f 100644 --- a/cc/layers/solid_color_layer_impl_unittest.cc +++ b/cc/layers/solid_color_layer_impl_unittest.cc
@@ -102,6 +102,31 @@ ->opacity()); } +TEST(SolidColorLayerImplTest, VerifyCorrectBlendModeInQuad) { + const SkXfermode::Mode blend_mode = SkXfermode::kMultiply_Mode; + + scoped_ptr<RenderPass> render_pass = RenderPass::Create(); + + gfx::Size layer_size = gfx::Size(100, 100); + gfx::Rect visible_content_rect = gfx::Rect(layer_size); + + FakeImplProxy proxy; + TestSharedBitmapManager shared_bitmap_manager; + FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager); + scoped_ptr<SolidColorLayerImpl> layer = + SolidColorLayerImpl::Create(host_impl.active_tree(), 1); + layer->SetBounds(layer_size); + layer->SetContentBounds(layer_size); + layer->draw_properties().blend_mode = blend_mode; + + AppendQuadsData data; + layer->AppendQuads(render_pass.get(), Occlusion(), &data); + + ASSERT_EQ(render_pass->quad_list.size(), 1U); + EXPECT_EQ(blend_mode, + render_pass->quad_list.front()->shared_quad_state->blend_mode); +} + TEST(SolidColorLayerImplTest, VerifyOpaqueRect) { gfx::Size layer_size = gfx::Size(100, 100); gfx::Rect visible_content_rect = gfx::Rect(layer_size);
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc index f1909b4..ab6289e 100644 --- a/cc/layers/surface_layer.cc +++ b/cc/layers/surface_layer.cc
@@ -44,6 +44,7 @@ SurfaceLayer::SurfaceLayer(const SatisfyCallback& satisfy_callback, const RequireCallback& require_callback) : Layer(), + surface_scale_(1.f), satisfy_callback_(satisfy_callback), require_callback_(require_callback) { } @@ -53,9 +54,13 @@ DCHECK(destroy_sequence_.is_null()); } -void SurfaceLayer::SetSurfaceId(SurfaceId surface_id) { +void SurfaceLayer::SetSurfaceId(SurfaceId surface_id, + float scale, + const gfx::Size& size) { SatisfyDestroySequence(); surface_id_ = surface_id; + surface_size_ = size; + surface_scale_ = scale; CreateNewDestroySequence(); UpdateDrawsContent(HasDrawableContent()); @@ -88,6 +93,15 @@ layer_impl->SetSurfaceId(surface_id_); } +void SurfaceLayer::CalculateContentsScale(float ideal_contents_scale, + float* contents_scale_x, + float* contents_scale_y, + gfx::Size* content_bounds) { + *content_bounds = surface_size_; + *contents_scale_x = surface_scale_; + *contents_scale_y = surface_scale_; +} + void SurfaceLayer::CreateNewDestroySequence() { DCHECK(destroy_sequence_.is_null()); if (layer_tree_host()) {
diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h index 4292549..9b73589 100644 --- a/cc/layers/surface_layer.h +++ b/cc/layers/surface_layer.h
@@ -9,6 +9,7 @@ #include "cc/layers/layer.h" #include "cc/surfaces/surface_id.h" #include "cc/surfaces/surface_sequence.h" +#include "ui/gfx/size.h" namespace cc { @@ -29,12 +30,16 @@ const SatisfyCallback& satisfy_callback, const RequireCallback& require_callback); - void SetSurfaceId(SurfaceId surface_id); + void SetSurfaceId(SurfaceId surface_id, float scale, const gfx::Size& size); // Layer overrides. scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; void SetLayerTreeHost(LayerTreeHost* host) override; void PushPropertiesTo(LayerImpl* layer) override; + void CalculateContentsScale(float ideal_contents_scale, + float* contents_scale_x, + float* contents_scale_y, + gfx::Size* content_bounds) override; protected: SurfaceLayer(const SatisfyCallback& satisfy_callback, @@ -47,6 +52,8 @@ void SatisfyDestroySequence(); SurfaceId surface_id_; + gfx::Size surface_size_; + float surface_scale_; SurfaceSequence destroy_sequence_; SatisfyCallback satisfy_callback_; RequireCallback require_callback_;
diff --git a/cc/layers/surface_layer_unittest.cc b/cc/layers/surface_layer_unittest.cc index 99d0e17..73415c0 100644 --- a/cc/layers/surface_layer_unittest.cc +++ b/cc/layers/surface_layer_unittest.cc
@@ -65,7 +65,7 @@ scoped_refptr<SurfaceLayer> layer(SurfaceLayer::Create( base::Bind(&SatisfyCallback, &blank_change), base::Bind(&RequireCallback, &required_id, &required_seq))); - layer->SetSurfaceId(SurfaceId(1)); + layer->SetSurfaceId(SurfaceId(1), 1.f, gfx::Size(1, 1)); layer_tree_host_->set_surface_id_namespace(1); layer_tree_host_->SetRootLayer(layer); @@ -74,7 +74,7 @@ scoped_refptr<SurfaceLayer> layer2(SurfaceLayer::Create( base::Bind(&SatisfyCallback, &blank_change), base::Bind(&RequireCallback, &required_id, &required_seq))); - layer2->SetSurfaceId(SurfaceId(1)); + layer2->SetSurfaceId(SurfaceId(1), 1.f, gfx::Size(1, 1)); layer_tree_host2->set_surface_id_namespace(2); layer_tree_host2->SetRootLayer(layer2); @@ -110,6 +110,36 @@ EXPECT_EQ(2u, required_seq.size()); } +static void CalcDrawProps(FakeLayerTreeHost* host, float device_scale_factor) { + RenderSurfaceLayerList render_surface_layer_list; + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + host->root_layer(), gfx::Size(500, 500), &render_surface_layer_list); + inputs.device_scale_factor = device_scale_factor; + LayerTreeHostCommon::CalculateDrawProperties(&inputs); +} + +// Check that setting content scale on the surface works. +TEST_F(SurfaceLayerTest, ScaleSurface) { + SurfaceSequence blank_change; + SurfaceId required_id; + std::set<SurfaceSequence> required_seq; + scoped_refptr<SurfaceLayer> layer(SurfaceLayer::Create( + base::Bind(&SatisfyCallback, &blank_change), + base::Bind(&RequireCallback, &required_id, &required_seq))); + gfx::Size surface_size(10, 15); + layer->SetSurfaceId(SurfaceId(1), 2.f, surface_size); + layer->SetBounds(gfx::Size(25, 45)); + layer_tree_host_->SetRootLayer(layer); + + CalcDrawProps(layer_tree_host_.get(), 5.f); + EXPECT_EQ(2.f, layer->contents_scale_x()); + EXPECT_EQ(2.f, layer->contents_scale_y()); + EXPECT_EQ(surface_size.ToString(), layer->content_bounds().ToString()); + + layer_tree_host_->SetRootLayer(nullptr); + layer_tree_host_.reset(); +} + // Check that SurfaceSequence is sent through swap promise. class SurfaceLayerSwapPromise : public LayerTreeTest { public: @@ -121,7 +151,7 @@ layer_ = SurfaceLayer::Create( base::Bind(&SatisfyCallback, &satisfied_sequence_), base::Bind(&RequireCallback, &required_id_, &required_set_)); - layer_->SetSurfaceId(SurfaceId(1)); + layer_->SetSurfaceId(SurfaceId(1), 1.f, gfx::Size(1, 1)); // Layer hasn't been added to tree so no SurfaceSequence generated yet. EXPECT_EQ(0u, required_set_.size());
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc index d355b62..f2ee269 100644 --- a/cc/layers/texture_layer_unittest.cc +++ b/cc/layers/texture_layer_unittest.cc
@@ -52,7 +52,9 @@ public: explicit MockLayerTreeHost(FakeLayerTreeHostClient* client) : LayerTreeHost(client, nullptr, nullptr, LayerTreeSettings()) { - InitializeSingleThreaded(client, base::MessageLoopProxy::current()); + InitializeSingleThreaded(client, + base::MessageLoopProxy::current(), + nullptr); } MOCK_METHOD0(SetNeedsCommit, void());
diff --git a/cc/layers/tiled_layer_unittest.cc b/cc/layers/tiled_layer_unittest.cc index 9c814be..7433a14 100644 --- a/cc/layers/tiled_layer_unittest.cc +++ b/cc/layers/tiled_layer_unittest.cc
@@ -82,7 +82,8 @@ : LayerTreeHost(client, manager, NULL, settings), output_surface_created_(false) { LayerTreeHost::InitializeThreaded(base::MessageLoopProxy::current(), - impl_task_runner); + impl_task_runner, + nullptr); } bool output_surface_created_; @@ -1663,8 +1664,7 @@ : FakeTiledLayer(manager) { scoped_ptr<TrackingLayerPainter> painter(TrackingLayerPainter::Create()); tracking_layer_painter_ = painter.get(); - layer_updater_ = BitmapContentLayerUpdater::Create( - painter.Pass(), &stats_instrumentation_, 0); + layer_updater_ = BitmapContentLayerUpdater::Create(painter.Pass(), 0); } TrackingLayerPainter* tracking_layer_painter() const { @@ -1677,7 +1677,6 @@ TrackingLayerPainter* tracking_layer_painter_; scoped_refptr<BitmapContentLayerUpdater> layer_updater_; - FakeRenderingStatsInstrumentation stats_instrumentation_; }; TEST_F(TiledLayerTest, NonIntegerContentsScaleIsNotDistortedDuringPaint) {
diff --git a/cc/layers/ui_resource_layer_unittest.cc b/cc/layers/ui_resource_layer_unittest.cc index 784ff35..09d57a9 100644 --- a/cc/layers/ui_resource_layer_unittest.cc +++ b/cc/layers/ui_resource_layer_unittest.cc
@@ -36,7 +36,9 @@ virtual void SetUp() { layer_tree_host_ = FakeLayerTreeHost::Create(&fake_client_); layer_tree_host_->InitializeSingleThreaded( - &fake_client_, base::MessageLoopProxy::current()); + &fake_client_, + base::MessageLoopProxy::current(), + nullptr); } virtual void TearDown() {
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc index 8251a09..a7e1bde 100644 --- a/cc/layers/video_layer_impl.cc +++ b/cc/layers/video_layer_impl.cc
@@ -159,14 +159,9 @@ SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); - shared_quad_state->SetAll(transform, - rotated_size, - visible_content_rect(), - clip_rect(), - is_clipped(), - draw_opacity(), - blend_mode(), - sorting_context_id()); + shared_quad_state->SetAll(transform, rotated_size, visible_content_rect(), + clip_rect(), is_clipped(), draw_opacity(), + draw_blend_mode(), sorting_context_id()); AppendDebugBorderQuad( render_pass, rotated_size, shared_quad_state, append_quads_data);
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 4eb1324..cb6189e 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc
@@ -98,6 +98,8 @@ switch (mode) { case SkXfermode::kSrcOver_Mode: return BlendModeNormal; + case SkXfermode::kScreen_Mode: + return BlendModeScreen; case SkXfermode::kOverlay_Mode: return BlendModeOverlay; case SkXfermode::kDarken_Mode: @@ -128,7 +130,7 @@ return BlendModeLuminosity; default: NOTREACHED(); - return BlendModeNormal; + return BlendModeNone; } } @@ -865,99 +867,8 @@ return background_with_filters; } -scoped_ptr<ScopedResource> -GLRenderer::ApplyInverseTransformForBackgroundFilters( - DrawingFrame* frame, - const RenderPassDrawQuad* quad, - const gfx::Transform& contents_device_transform, - skia::RefPtr<SkImage> filtered_device_background, - const gfx::Rect& backdrop_bounding_rect) { - // This method draws a background filter, which applies a filter to any pixels - // behind the quad and seen through its background. The algorithm works as - // follows: - // 1. Read the pixels in the bounding box into a buffer. - // Moved to GLRenderer::GetBackdropBoundingBoxForRenderPassQuad(). - // 2. Read the pixels in the bounding box into a buffer R. - // Moved to GLRenderer::GetBackdropTexture(). - // 3. Apply the background filter to R, so that it is applied in the pixels' - // coordinate space. Moved to GLRenderer::ApplyBackgroundFilters(). - // 4. Apply the quad's inverse transform to map the pixels in R into the - // quad's content space. This implicitly clips R by the content bounds of the - // quad since the destination texture has bounds matching the quad's content. - // 5. Draw the background texture for the contents using the same transform as - // used to draw the contents itself. This is done without blending to replace - // the current background pixels with the new filtered background. - // 6. Draw the contents of the quad over drop of the new background with - // blending, as per usual. The filtered background pixels will show through - // any non-opaque pixels in this draws. - // - // Pixel copies in this algorithm occur at steps 2, 3, 4, and 5. - - // TODO(danakj): When this algorithm changes, update - // LayerTreeHost::PrioritizeTextures() accordingly. - - DCHECK(filtered_device_background); - - GrTexture* texture = filtered_device_background->getTexture(); - - scoped_ptr<ScopedResource> background_texture = - ScopedResource::Create(resource_provider_); - background_texture->Allocate( - quad->rect.size(), - ResourceProvider::TextureHintImmutableFramebuffer, - RGBA_8888); - - const RenderPass* target_render_pass = frame->current_render_pass; - bool using_background_texture = - UseScopedTexture(frame, background_texture.get(), quad->rect); - - if (using_background_texture) { - // Copy the readback pixels from device to the background texture for the - // surface. - - gfx::Transform contents_device_transform_inverse( - gfx::Transform::kSkipInitialization); - bool did_invert = contents_device_transform.GetInverse( - &contents_device_transform_inverse); - DCHECK(did_invert); - gfx::Transform device_to_framebuffer_transform; - QuadRectTransform( - &device_to_framebuffer_transform, gfx::Transform(), quad->rect); - device_to_framebuffer_transform.PreconcatTransform( - contents_device_transform_inverse); - -#ifndef NDEBUG - GLC(gl_, gl_->ClearColor(0, 0, 1, 1)); - gl_->Clear(GL_COLOR_BUFFER_BIT); -#endif - - // The background_texture is oriented the same as the frame buffer. - // The transform we are copying with has a vertical flip, as well as - // the |device_to_framebuffer_transform|, which cancel each other out. So do - // not flip the contents in the shader to maintain orientation. - bool flip_vertically = false; - - CopyTextureToFramebuffer(frame, - texture->getTextureHandle(), - backdrop_bounding_rect, - device_to_framebuffer_transform, - flip_vertically); - } - - UseRenderPass(frame, target_render_pass); - - if (!using_background_texture) - return nullptr; - return background_texture.Pass(); -} - void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, const RenderPassDrawQuad* quad) { - SkXfermode::Mode blend_mode = quad->shared_quad_state->blend_mode; - SetBlendEnabled( - CanApplyBlendModeUsingBlendFunc(blend_mode) && - (quad->ShouldDrawWithBlending() || !IsDefaultBlendMode(blend_mode))); - ScopedResource* contents_texture = render_pass_textures_.get(quad->render_pass_id); if (!contents_texture || !contents_texture->id()) @@ -983,62 +894,59 @@ SetupQuadForAntialiasing(contents_device_transform, quad, &surface_quad, edge); - bool need_background_texture = !CanApplyBlendModeUsingBlendFunc(blend_mode) || - ShouldApplyBackgroundFilters(frame, quad); + SkXfermode::Mode blend_mode = quad->shared_quad_state->blend_mode; + bool use_shaders_for_blending = + !CanApplyBlendModeUsingBlendFunc(blend_mode) || + ShouldApplyBackgroundFilters(frame, quad) || + settings_->force_blending_with_shaders; scoped_ptr<ScopedResource> background_texture; skia::RefPtr<SkImage> background_image; gfx::Rect background_rect; - if (need_background_texture) { + if (use_shaders_for_blending) { // Compute a bounding box around the pixels that will be visible through // the quad. background_rect = GetBackdropBoundingBoxForRenderPassQuad( frame, quad, contents_device_transform, use_aa); - } - if (!background_rect.IsEmpty()) { - // The pixels from the filtered background should completely replace the - // current pixel values. - bool disable_blending = blend_enabled(); - if (disable_blending) - SetBlendEnabled(false); + if (!background_rect.IsEmpty()) { + // The pixels from the filtered background should completely replace the + // current pixel values. + if (blend_enabled()) + SetBlendEnabled(false); - // Read the pixels in the bounding box into a buffer R. - scoped_ptr<ScopedResource> scoped_background_texture = - GetBackdropTexture(background_rect); + // Read the pixels in the bounding box into a buffer R. + // This function allocates a texture, which should contribute to the + // amount of memory used by render surfaces: + // LayerTreeHost::CalculateMemoryForRenderSurfaces. + background_texture = GetBackdropTexture(background_rect); - skia::RefPtr<SkImage> background_with_filters; - if (ShouldApplyBackgroundFilters(frame, quad) && - scoped_background_texture) { - // Apply the background filters to R, so that it is applied in the pixels' - // coordinate space. - background_with_filters = - ApplyBackgroundFilters(frame, quad, scoped_background_texture.get()); - } - - if (CanApplyBlendModeUsingBlendFunc(blend_mode) && - background_with_filters) { - // The background with filters will be copied to the frame buffer. - // Apply the quad's inverse transform to map the pixels in R into the - // quad's content space. This implicitly clips R by the content bounds of - // the quad since the destination texture has bounds matching the quad's - // content. - background_texture = ApplyInverseTransformForBackgroundFilters( - frame, quad, contents_device_transform, background_with_filters, - background_rect); - } else if (!CanApplyBlendModeUsingBlendFunc(blend_mode)) { - if (background_with_filters) { - // The background with filters will be used as backdrop for blending. - background_image = background_with_filters; - } else { - background_texture = scoped_background_texture.Pass(); + if (ShouldApplyBackgroundFilters(frame, quad) && background_texture) { + // Apply the background filters to R, so that it is applied in the + // pixels' coordinate space. + background_image = + ApplyBackgroundFilters(frame, quad, background_texture.get()); } } - if (disable_blending) - SetBlendEnabled(true); + if (!background_texture) { + // Something went wrong with reading the backdrop. + DCHECK(!background_image); + use_shaders_for_blending = false; + } else if (background_image) { + background_texture.reset(); + } else if (CanApplyBlendModeUsingBlendFunc(blend_mode) && + ShouldApplyBackgroundFilters(frame, quad)) { + // Something went wrong with applying background filters to the backdrop. + use_shaders_for_blending = false; + background_texture.reset(); + } } + SetBlendEnabled( + !use_shaders_for_blending && + (quad->ShouldDrawWithBlending() || !IsDefaultBlendMode(blend_mode))); + // TODO(senorblanco): Cache this value so that we don't have to do it for both // the surface and its replica. Apply filters to the contents texture. skia::RefPtr<SkImage> filter_image; @@ -1071,25 +979,6 @@ } } - if (background_texture && ShouldApplyBackgroundFilters(frame, quad)) { - // Draw the background texture if it has some filters applied. - DCHECK(CanApplyBlendModeUsingBlendFunc(blend_mode)); - DCHECK(background_texture->size() == quad->rect.size()); - ResourceProvider::ScopedReadLockGL lock(resource_provider_, - background_texture->id()); - - // The background_texture is oriented the same as the frame buffer. The - // transform we are copying with has a vertical flip, so flip the contents - // in the shader to maintain orientation - bool flip_vertically = true; - - CopyTextureToFramebuffer(frame, - lock.texture_id(), - quad->rect, - quad->quadTransform(), - flip_vertically); - } - scoped_ptr<ResourceProvider::ScopedSamplerGL> mask_resource_lock; unsigned mask_texture_id = 0; SamplerType mask_sampler = SamplerTypeNA; @@ -1100,9 +989,6 @@ mask_sampler = SamplerTypeFromTextureTarget(mask_resource_lock->target()); } - // TODO(danakj): use the background_texture and blend the background in with - // this draw instead of having a separate copy of the background texture. - scoped_ptr<ResourceProvider::ScopedSamplerGL> contents_resource_lock; if (filter_image) { GrTexture* texture = filter_image->getTexture(); @@ -1116,7 +1002,7 @@ contents_resource_lock->target()); } - if (CanApplyBlendModeUsingBlendFunc(blend_mode)) { + if (!use_shaders_for_blending) { if (!use_blend_equation_advanced_coherent_ && use_blend_equation_advanced_) GLC(gl_, gl_->BlendBarrierKHR()); @@ -1143,10 +1029,10 @@ int shader_backdrop_location = -1; int shader_backdrop_rect_location = -1; - BlendMode shader_blend_mode = ((background_texture || background_image) && - !CanApplyBlendModeUsingBlendFunc(blend_mode)) + DCHECK_EQ(background_texture || background_image, use_shaders_for_blending); + BlendMode shader_blend_mode = use_shaders_for_blending ? BlendModeFromSkXfermode(blend_mode) - : BlendModeNormal; + : BlendModeNone; if (use_aa && mask_texture_id && !use_color_matrix) { const RenderPassMaskProgramAA* program = GetRenderPassMaskProgramAA( @@ -1423,7 +1309,7 @@ if (filter_image) GLC(gl_, gl_->Flush()); - if (CanApplyBlendModeUsingBlendFunc(blend_mode)) + if (!use_shaders_for_blending) RestoreBlendFuncToDefault(blend_mode); } @@ -2096,8 +1982,8 @@ } SkCanvas canvas(on_demand_tile_raster_bitmap_); - quad->picture_pile->PlaybackToCanvas(&canvas, quad->content_rect, - quad->contents_scale); + quad->raster_source->PlaybackToCanvas(&canvas, quad->content_rect, + quad->contents_scale); uint8_t* bitmap_pixels = NULL; SkBitmap on_demand_tile_raster_bitmap_dest; @@ -2451,43 +2337,6 @@ GLC(gl_, gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0)); } -void GLRenderer::CopyTextureToFramebuffer(const DrawingFrame* frame, - int texture_id, - const gfx::Rect& rect, - const gfx::Transform& draw_matrix, - bool flip_vertically) { - TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( - gl_, &highp_threshold_cache_, highp_threshold_min_, rect.bottom_right()); - - const RenderPassProgram* program = - GetRenderPassProgram(tex_coord_precision, BlendModeNormal); - SetUseProgram(program->program()); - - GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); - - if (flip_vertically) { - GLC(gl_, - gl_->Uniform4f(program->vertex_shader().tex_transform_location(), - 0.f, - 1.f, - 1.f, - -1.f)); - } else { - GLC(gl_, - gl_->Uniform4f(program->vertex_shader().tex_transform_location(), - 0.f, - 0.f, - 1.f, - 1.f)); - } - - SetShaderOpacity(1.f, program->fragment_shader().alpha_location()); - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_)); - GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, texture_id)); - DrawQuadGeometry( - frame, draw_matrix, rect, program->vertex_shader().matrix_location()); -} - void GLRenderer::Finish() { TRACE_EVENT0("cc", "GLRenderer::Finish"); GLC(gl_, gl_->Finish());
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index 4930204..ecadebb 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h
@@ -167,12 +167,6 @@ DrawingFrame* frame, const RenderPassDrawQuad* quad, ScopedResource* background_texture); - scoped_ptr<ScopedResource> ApplyInverseTransformForBackgroundFilters( - DrawingFrame* frame, - const RenderPassDrawQuad* quad, - const gfx::Transform& contents_device_transform, - skia::RefPtr<SkImage> backdrop_bitmap, - const gfx::Rect& backdrop_bounding_rect); void DrawRenderPassQuad(DrawingFrame* frame, const RenderPassDrawQuad* quad); void DrawSolidColorQuad(const DrawingFrame* frame, @@ -208,12 +202,6 @@ int matrix_location); void SetUseProgram(unsigned program); - void CopyTextureToFramebuffer(const DrawingFrame* frame, - int texture_id, - const gfx::Rect& rect, - const gfx::Transform& draw_matrix, - bool flip_vertically); - bool UseScopedTexture(DrawingFrame* frame, const ScopedResource* resource, const gfx::Rect& viewport_rect);
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc index dc68675..2af953e 100644 --- a/cc/output/gl_renderer_unittest.cc +++ b/cc/output/gl_renderer_unittest.cc
@@ -57,8 +57,11 @@ static inline SkXfermode::Mode BlendModeToSkXfermode(BlendMode blend_mode) { switch (blend_mode) { + case BlendModeNone: case BlendModeNormal: return SkXfermode::kSrcOver_Mode; + case BlendModeScreen: + return SkXfermode::kScreen_Mode; case BlendModeOverlay: return SkXfermode::kOverlay_Mode; case BlendModeDarken: @@ -1421,6 +1424,7 @@ for (int i = 0; i < NumBlendModes; ++i) { BlendMode blend_mode = static_cast<BlendMode>(i); SkXfermode::Mode xfer_mode = BlendModeToSkXfermode(blend_mode); + settings_.force_blending_with_shaders = (blend_mode != BlendModeNone); // RenderPassProgram render_passes_in_draw_order_.clear(); child_pass = AddRenderPass(&render_passes_in_draw_order_, @@ -1690,7 +1694,7 @@ // If use_aa incorrectly ignores clipping, it will use the // RenderPassProgramAA shader instead of the RenderPassProgram. - TestRenderPassProgram(TexCoordPrecisionMedium, BlendModeNormal); + TestRenderPassProgram(TexCoordPrecisionMedium, BlendModeNone); } TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h index 0a0d456..04bd4a3 100644 --- a/cc/output/output_surface.h +++ b/cc/output/output_surface.h
@@ -125,11 +125,6 @@ // processing should be stopped, or lowered in priority. virtual void UpdateSmoothnessTakesPriority(bool prefer_smoothness) {} - // Requests a BeginFrame notification from the output surface. The - // notification will be delivered by calling - // OutputSurfaceClient::BeginFrame until the callback is disabled. - virtual void SetNeedsBeginFrame(bool enable) {} - bool HasClient() { return !!client_; } // Get the class capable of informing cc of hardware overlay capability.
diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h index 99b174b..60750b4 100644 --- a/cc/output/output_surface_client.h +++ b/cc/output/output_surface_client.h
@@ -9,7 +9,6 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "cc/base/cc_export.h" -#include "cc/output/begin_frame_args.h" #include "cc/output/context_provider.h" #include "ui/gfx/geometry/rect.h" @@ -33,7 +32,6 @@ virtual void CommitVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) = 0; virtual void SetNeedsRedrawRect(const gfx::Rect& damage_rect) = 0; - virtual void BeginFrame(const BeginFrameArgs& args) = 0; virtual void DidSwapBuffers() = 0; virtual void DidSwapBuffersComplete() = 0; virtual void ReclaimResources(const CompositorFrameAck* ack) = 0;
diff --git a/cc/output/output_surface_unittest.cc b/cc/output/output_surface_unittest.cc index b1f4149..4336f07 100644 --- a/cc/output/output_surface_unittest.cc +++ b/cc/output/output_surface_unittest.cc
@@ -49,10 +49,6 @@ CommitVSyncParameters(timebase, interval); } - void BeginFrameForTesting() { - client_->BeginFrame(CreateExpiredBeginFrameArgsForTesting()); - } - void DidSwapBuffersForTesting() { client_->DidSwapBuffers(); } void OnSwapBuffersCompleteForTesting() { client_->DidSwapBuffersComplete(); }
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h index 722c782..a10d0ab 100644 --- a/cc/output/program_binding.h +++ b/cc/output/program_binding.h
@@ -59,7 +59,7 @@ void Initialize(ContextProvider* context_provider, TexCoordPrecision precision, SamplerType sampler, - BlendMode blend_mode = BlendModeNormal) { + BlendMode blend_mode = BlendModeNone) { DCHECK(context_provider); DCHECK(!initialized_);
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc index a0ce823..d112ed1 100644 --- a/cc/output/renderer_pixeltest.cc +++ b/cc/output/renderer_pixeltest.cc
@@ -1418,14 +1418,9 @@ blue_quad->SetNew(blue_shared_state, viewport, // Intentionally bigger than clip. - gfx::Rect(), - viewport, - gfx::RectF(viewport), - viewport.size(), - texture_format, - viewport, - 1.f, - PicturePileImpl::CreateFromOther(blue_pile.get())); + gfx::Rect(), viewport, gfx::RectF(viewport), + viewport.size(), texture_format, viewport, 1.f, + blue_pile.get()); // One viewport-filling green quad. scoped_refptr<FakePicturePileImpl> green_pile = @@ -1441,16 +1436,9 @@ PictureDrawQuad* green_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - green_quad->SetNew(green_shared_state, - viewport, - gfx::Rect(), - viewport, - gfx::RectF(0.f, 0.f, 1.f, 1.f), - viewport.size(), - texture_format, - viewport, - 1.f, - PicturePileImpl::CreateFromOther(green_pile.get())); + green_quad->SetNew(green_shared_state, viewport, gfx::Rect(), viewport, + gfx::RectF(0.f, 0.f, 1.f, 1.f), viewport.size(), + texture_format, viewport, 1.f, green_pile.get()); RenderPassList pass_list; pass_list.push_back(pass.Pass()); @@ -1487,16 +1475,9 @@ PictureDrawQuad* green_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - green_quad->SetNew(green_shared_state, - viewport, - gfx::Rect(), - viewport, - gfx::RectF(0, 0, 1, 1), - viewport.size(), - texture_format, - viewport, - 1.f, - PicturePileImpl::CreateFromOther(green_pile.get())); + green_quad->SetNew(green_shared_state, viewport, gfx::Rect(), viewport, + gfx::RectF(0, 0, 1, 1), viewport.size(), texture_format, + viewport, 1.f, green_pile.get()); // One viewport-filling white quad. scoped_refptr<FakePicturePileImpl> white_pile = @@ -1512,16 +1493,9 @@ PictureDrawQuad* white_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - white_quad->SetNew(white_shared_state, - viewport, - gfx::Rect(), - viewport, - gfx::RectF(0, 0, 1, 1), - viewport.size(), - texture_format, - viewport, - 1.f, - PicturePileImpl::CreateFromOther(white_pile.get())); + white_quad->SetNew(white_shared_state, viewport, gfx::Rect(), viewport, + gfx::RectF(0, 0, 1, 1), viewport.size(), texture_format, + viewport, 1.f, white_pile.get()); RenderPassList pass_list; pass_list.push_back(pass.Pass()); @@ -1586,16 +1560,9 @@ content_to_target_transform, viewport, pass.get()); PictureDrawQuad* quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - quad->SetNew(shared_state, - viewport, - gfx::Rect(), - viewport, - gfx::RectF(0, 0, 2, 2), - viewport.size(), - texture_format, - viewport, - 1.f, - PicturePileImpl::CreateFromOther(pile.get())); + quad->SetNew(shared_state, viewport, gfx::Rect(), viewport, + gfx::RectF(0, 0, 2, 2), viewport.size(), texture_format, + viewport, 1.f, pile.get()); RenderPassList pass_list; pass_list.push_back(pass.Pass()); @@ -1643,29 +1610,17 @@ PictureDrawQuad* green_quad1 = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - green_quad1->SetNew(top_right_green_shared_quad_state, - green_rect1, - gfx::Rect(), - green_rect1, - gfx::RectF(green_rect1.size()), - green_rect1.size(), - texture_format, - green_rect1, - 1.f, - PicturePileImpl::CreateFromOther(green_pile.get())); + green_quad1->SetNew(top_right_green_shared_quad_state, green_rect1, + gfx::Rect(), green_rect1, gfx::RectF(green_rect1.size()), + green_rect1.size(), texture_format, green_rect1, 1.f, + green_pile.get()); PictureDrawQuad* green_quad2 = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - green_quad2->SetNew(top_right_green_shared_quad_state, - green_rect2, - gfx::Rect(), - green_rect2, - gfx::RectF(green_rect2.size()), - green_rect2.size(), - texture_format, - green_rect2, - 1.f, - PicturePileImpl::CreateFromOther(green_pile.get())); + green_quad2->SetNew(top_right_green_shared_quad_state, green_rect2, + gfx::Rect(), green_rect2, gfx::RectF(green_rect2.size()), + green_rect2.size(), texture_format, green_rect2, 1.f, + green_pile.get()); // Add a green clipped checkerboard in the bottom right to help test // interleaving picture quad content and solid color content. @@ -1731,16 +1686,10 @@ content_to_target_transform, quad_content_rect, pass.get()); PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - blue_quad->SetNew(blue_shared_state, - quad_content_rect, - gfx::Rect(), - quad_content_rect, - gfx::RectF(quad_content_rect), - content_union_rect.size(), - texture_format, - content_union_rect, - contents_scale, - PicturePileImpl::CreateFromOther(pile.get())); + blue_quad->SetNew(blue_shared_state, quad_content_rect, gfx::Rect(), + quad_content_rect, gfx::RectF(quad_content_rect), + content_union_rect.size(), texture_format, + content_union_rect, contents_scale, pile.get()); // Fill left half of viewport with green. gfx::Transform half_green_content_to_target_transform; @@ -1943,16 +1892,9 @@ blue_content_to_target_transform, viewport, pass.get()); PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>(); - blue_quad->SetNew(blue_shared_state, - viewport, - gfx::Rect(), - viewport, - gfx::RectF(0.f, 0.f, 1.f, 1.f), - viewport.size(), - texture_format, - viewport, - 1.f, - PicturePileImpl::CreateFromOther(blue_pile.get())); + blue_quad->SetNew(blue_shared_state, viewport, gfx::Rect(), viewport, + gfx::RectF(0.f, 0.f, 1.f, 1.f), viewport.size(), + texture_format, viewport, 1.f, blue_pile.get()); RenderPassList pass_list; pass_list.push_back(pass.Pass());
diff --git a/cc/output/shader.cc b/cc/output/shader.cc index b962d4a..e22e446 100644 --- a/cc/output/shader.cc +++ b/cc/output/shader.cc
@@ -669,9 +669,9 @@ } #define BLEND_MODE_UNIFORMS "s_backdropTexture", "backdropRect" -#define UNUSED_BLEND_MODE_UNIFORMS (is_default_blend_mode() ? 2 : 0) +#define UNUSED_BLEND_MODE_UNIFORMS (!has_blend_mode() ? 2 : 0) #define BLEND_MODE_SET_LOCATIONS(X, POS) \ - if (!is_default_blend_mode()) { \ + if (has_blend_mode()) { \ DCHECK_LT(static_cast<size_t>(POS) + 1, arraysize(X)); \ backdrop_location_ = locations[POS]; \ backdrop_rect_location_ = locations[POS + 1]; \ @@ -680,7 +680,7 @@ FragmentTexBlendMode::FragmentTexBlendMode() : backdrop_location_(-1), backdrop_rect_location_(-1), - blend_mode_(BlendModeNormal) { + blend_mode_(BlendModeNone) { } std::string FragmentTexBlendMode::SetBlendModeFunctions( @@ -688,7 +688,7 @@ if (shader_string.find("ApplyBlendMode") == std::string::npos) return shader_string; - if (is_default_blend_mode()) { + if (!has_blend_mode()) { return "#define ApplyBlendMode(X) (X)\n" + shader_string; } @@ -902,6 +902,10 @@ std::string FragmentTexBlendMode::GetBlendFunctionBodyForRGB() const { switch (blend_mode_) { + case BlendModeNormal: + return "result.rgb = src.rgb + dst.rgb * (1.0 - src.a);"; + case BlendModeScreen: + return "result.rgb = src.rgb + (1.0 - src.rgb) * dst.rgb;"; case BlendModeLighten: return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb," " (1.0 - dst.a) * src.rgb + dst.rgb);"; @@ -963,11 +967,11 @@ " srcDstAlpha.a," " srcDstAlpha.rgb);" "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; - default: + case BlendModeNone: + case NumBlendModes: NOTREACHED(); - // simple alpha compositing - return "result.rgb = src.rgb * src.a + dst.rgb * dst.a * (1 - src.a)"; } + return "result = vec4(1.0, 0.0, 0.0, 1.0);"; } FragmentTexAlphaBinding::FragmentTexAlphaBinding()
diff --git a/cc/output/shader.h b/cc/output/shader.h index 0384366..8c1f264 100644 --- a/cc/output/shader.h +++ b/cc/output/shader.h
@@ -39,7 +39,9 @@ }; enum BlendMode { + BlendModeNone, BlendModeNormal, + BlendModeScreen, BlendModeOverlay, BlendModeDarken, BlendModeLighten, @@ -305,7 +307,7 @@ BlendMode blend_mode() const { return blend_mode_; } void set_blend_mode(BlendMode blend_mode) { blend_mode_ = blend_mode; } - bool is_default_blend_mode() const { return blend_mode_ == BlendModeNormal; } + bool has_blend_mode() const { return blend_mode_ != BlendModeNone; } protected: FragmentTexBlendMode();
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc index 06f1851..442cd8d 100644 --- a/cc/output/software_renderer.cc +++ b/cc/output/software_renderer.cc
@@ -350,8 +350,8 @@ TRACE_EVENT0("cc", "SoftwareRenderer::DrawPictureQuad"); - quad->picture_pile->RasterDirect(current_canvas_, quad->content_rect, - quad->contents_scale); + quad->raster_source->PlaybackToSharedCanvas( + current_canvas_, quad->content_rect, quad->contents_scale); current_canvas_->setDrawFilter(NULL); }
diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc index d3753ca..5755ea8 100644 --- a/cc/quads/draw_quad_unittest.cc +++ b/cc/quads/draw_quad_unittest.cc
@@ -670,18 +670,12 @@ ResourceFormat texture_format = RGBA_8888; gfx::Rect content_rect(30, 40, 20, 30); float contents_scale = 3.141592f; - scoped_refptr<PicturePileImpl> picture_pile = PicturePileImpl::Create(); + scoped_refptr<RasterSource> raster_source = PicturePileImpl::Create(); CREATE_SHARED_STATE(); - CREATE_QUAD_8_NEW(PictureDrawQuad, - opaque_rect, - visible_rect, - tex_coord_rect, - texture_size, - texture_format, - content_rect, - contents_scale, - picture_pile); + CREATE_QUAD_8_NEW(PictureDrawQuad, opaque_rect, visible_rect, tex_coord_rect, + texture_size, texture_format, content_rect, contents_scale, + raster_source); EXPECT_EQ(DrawQuad::PICTURE_CONTENT, copy_quad->material); EXPECT_RECT_EQ(opaque_rect, copy_quad->opaque_rect); EXPECT_RECT_EQ(visible_rect, copy_quad->visible_rect); @@ -690,22 +684,18 @@ EXPECT_EQ(texture_format, copy_quad->texture_format); EXPECT_RECT_EQ(content_rect, copy_quad->content_rect); EXPECT_EQ(contents_scale, copy_quad->contents_scale); - EXPECT_EQ(picture_pile, copy_quad->picture_pile); + EXPECT_EQ(raster_source, copy_quad->raster_source); - CREATE_QUAD_6_ALL(PictureDrawQuad, - tex_coord_rect, - texture_size, - texture_format, - content_rect, - contents_scale, - picture_pile); + CREATE_QUAD_6_ALL(PictureDrawQuad, tex_coord_rect, texture_size, + texture_format, content_rect, contents_scale, + raster_source); EXPECT_EQ(DrawQuad::PICTURE_CONTENT, copy_quad->material); EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect); EXPECT_EQ(texture_size, copy_quad->texture_size); EXPECT_EQ(texture_format, copy_quad->texture_format); EXPECT_RECT_EQ(content_rect, copy_quad->content_rect); EXPECT_EQ(contents_scale, copy_quad->contents_scale); - EXPECT_EQ(picture_pile, copy_quad->picture_pile); + EXPECT_EQ(raster_source, copy_quad->raster_source); } class DrawQuadIteratorTest : public testing::Test { @@ -922,18 +912,12 @@ ResourceFormat texture_format = RGBA_8888; gfx::Rect content_rect(30, 40, 20, 30); float contents_scale = 3.141592f; - scoped_refptr<PicturePileImpl> picture_pile = PicturePileImpl::Create(); + scoped_refptr<PicturePileImpl> raster_source = PicturePileImpl::Create(); CREATE_SHARED_STATE(); - CREATE_QUAD_8_NEW(PictureDrawQuad, - opaque_rect, - visible_rect, - tex_coord_rect, - texture_size, - texture_format, - content_rect, - contents_scale, - picture_pile); + CREATE_QUAD_8_NEW(PictureDrawQuad, opaque_rect, visible_rect, tex_coord_rect, + texture_size, texture_format, content_rect, contents_scale, + raster_source); EXPECT_EQ(0, IterateAndCount(quad_new)); }
diff --git a/cc/quads/picture_draw_quad.cc b/cc/quads/picture_draw_quad.cc index af120a4..bf329c2 100644 --- a/cc/quads/picture_draw_quad.cc +++ b/cc/quads/picture_draw_quad.cc
@@ -26,7 +26,7 @@ ResourceFormat texture_format, const gfx::Rect& content_rect, float contents_scale, - scoped_refptr<PicturePileImpl> picture_pile) { + scoped_refptr<RasterSource> raster_source) { ContentDrawQuadBase::SetNew( shared_quad_state, DrawQuad::PICTURE_CONTENT, @@ -38,7 +38,7 @@ !PlatformColor::SameComponentOrder(texture_format)); this->content_rect = content_rect; this->contents_scale = contents_scale; - this->picture_pile = picture_pile; + this->raster_source = raster_source; this->texture_format = texture_format; } @@ -52,7 +52,7 @@ ResourceFormat texture_format, const gfx::Rect& content_rect, float contents_scale, - scoped_refptr<PicturePileImpl> picture_pile) { + scoped_refptr<RasterSource> raster_source) { ContentDrawQuadBase::SetAll(shared_quad_state, DrawQuad::PICTURE_CONTENT, rect, @@ -65,7 +65,7 @@ texture_format)); this->content_rect = content_rect; this->contents_scale = contents_scale; - this->picture_pile = picture_pile; + this->raster_source = raster_source; this->texture_format = texture_format; } @@ -87,7 +87,7 @@ value->EndArray(); value->SetDouble("contents_scale", contents_scale); value->SetInteger("texture_format", texture_format); - // TODO(piman): picture_pile? + // TODO(piman): raster_source? } } // namespace cc
diff --git a/cc/quads/picture_draw_quad.h b/cc/quads/picture_draw_quad.h index 1bea1a2..62c9f18 100644 --- a/cc/quads/picture_draw_quad.h +++ b/cc/quads/picture_draw_quad.h
@@ -9,7 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/quads/content_draw_quad_base.h" -#include "cc/resources/picture_pile_impl.h" +#include "cc/resources/raster_source.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size.h" @@ -31,7 +31,7 @@ ResourceFormat texture_format, const gfx::Rect& content_rect, float contents_scale, - scoped_refptr<PicturePileImpl> picture_pile); + scoped_refptr<RasterSource> raster_source); void SetAll(const SharedQuadState* shared_quad_state, const gfx::Rect& rect, @@ -43,11 +43,11 @@ ResourceFormat texture_format, const gfx::Rect& content_rect, float contents_scale, - scoped_refptr<PicturePileImpl> picture_pile); + scoped_refptr<RasterSource> raster_source); gfx::Rect content_rect; float contents_scale; - scoped_refptr<PicturePileImpl> picture_pile; + scoped_refptr<RasterSource> raster_source; ResourceFormat texture_format; void IterateResources(const ResourceIteratorCallback& callback) override;
diff --git a/cc/resources/bitmap_content_layer_updater.cc b/cc/resources/bitmap_content_layer_updater.cc index 63ba336..31a62c3 100644 --- a/cc/resources/bitmap_content_layer_updater.cc +++ b/cc/resources/bitmap_content_layer_updater.cc
@@ -32,19 +32,17 @@ scoped_refptr<BitmapContentLayerUpdater> BitmapContentLayerUpdater::Create( scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumentation, int layer_id) { return make_scoped_refptr( new BitmapContentLayerUpdater(painter.Pass(), - stats_instrumentation, layer_id)); } BitmapContentLayerUpdater::BitmapContentLayerUpdater( scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumentation, int layer_id) - : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {} + : ContentLayerUpdater(painter.Pass(), layer_id) { +} BitmapContentLayerUpdater::~BitmapContentLayerUpdater() {} @@ -71,17 +69,11 @@ DCHECK_EQ(paint_rect.height(), canvas_->getBaseLayerSize().height()); } - base::TimeTicks start_time = - rendering_stats_instrumentation_->StartRecording(); PaintContents(canvas_.get(), content_size, paint_rect, contents_width_scale, contents_height_scale); - base::TimeDelta duration = - rendering_stats_instrumentation_->EndRecording(start_time); - rendering_stats_instrumentation_->AddPaint( - duration, paint_rect.width() * paint_rect.height()); } void BitmapContentLayerUpdater::UpdateTexture(ResourceUpdateQueue* queue,
diff --git a/cc/resources/bitmap_content_layer_updater.h b/cc/resources/bitmap_content_layer_updater.h index 5ddc35f..3477600 100644 --- a/cc/resources/bitmap_content_layer_updater.h +++ b/cc/resources/bitmap_content_layer_updater.h
@@ -42,7 +42,6 @@ static scoped_refptr<BitmapContentLayerUpdater> Create( scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumenation, int layer_id); scoped_ptr<LayerUpdater::Resource> CreateResource( @@ -63,7 +62,6 @@ protected: BitmapContentLayerUpdater( scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumenation, int layer_id); ~BitmapContentLayerUpdater() override;
diff --git a/cc/resources/bitmap_skpicture_content_layer_updater.cc b/cc/resources/bitmap_skpicture_content_layer_updater.cc index 5b8e5e4..a78a9df 100644 --- a/cc/resources/bitmap_skpicture_content_layer_updater.cc +++ b/cc/resources/bitmap_skpicture_content_layer_updater.cc
@@ -53,9 +53,8 @@ scoped_ptr<LayerPainter> painter, RenderingStatsInstrumentation* stats_instrumentation, int layer_id) - : SkPictureContentLayerUpdater(painter.Pass(), - stats_instrumentation, - layer_id) {} + : SkPictureContentLayerUpdater(painter.Pass(), layer_id) { +} BitmapSkPictureContentLayerUpdater::~BitmapSkPictureContentLayerUpdater() {}
diff --git a/cc/resources/content_layer_updater.cc b/cc/resources/content_layer_updater.cc index 971362f..206d76b 100644 --- a/cc/resources/content_layer_updater.cc +++ b/cc/resources/content_layer_updater.cc
@@ -5,7 +5,6 @@ #include "cc/resources/content_layer_updater.h" #include "base/debug/trace_event.h" -#include "cc/debug/rendering_stats_instrumentation.h" #include "cc/resources/layer_painter.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkRect.h" @@ -16,12 +15,9 @@ namespace cc { -ContentLayerUpdater::ContentLayerUpdater( - scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumentation, - int layer_id) - : rendering_stats_instrumentation_(stats_instrumentation), - layer_id_(layer_id), +ContentLayerUpdater::ContentLayerUpdater(scoped_ptr<LayerPainter> painter, + int layer_id) + : layer_id_(layer_id), layer_is_opaque_(false), layer_fills_bounds_completely_(false), painter_(painter.Pass()), @@ -30,11 +26,6 @@ ContentLayerUpdater::~ContentLayerUpdater() {} -void ContentLayerUpdater::set_rendering_stats_instrumentation( - RenderingStatsInstrumentation* rsi) { - rendering_stats_instrumentation_ = rsi; -} - void ContentLayerUpdater::PaintContents(SkCanvas* canvas, const gfx::Size& layer_content_size, const gfx::Rect& paint_rect,
diff --git a/cc/resources/content_layer_updater.h b/cc/resources/content_layer_updater.h index 4ba18ba..0f26b82 100644 --- a/cc/resources/content_layer_updater.h +++ b/cc/resources/content_layer_updater.h
@@ -21,15 +21,12 @@ // their respective PaintContents implementations. class CC_EXPORT ContentLayerUpdater : public LayerUpdater { public: - void set_rendering_stats_instrumentation(RenderingStatsInstrumentation* rsi); void SetOpaque(bool opaque) override; void SetFillsBoundsCompletely(bool fills_bounds) override; void SetBackgroundColor(SkColor background_color) override; protected: - ContentLayerUpdater(scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumentation, - int layer_id); + ContentLayerUpdater(scoped_ptr<LayerPainter> painter, int layer_id); ~ContentLayerUpdater() override; // Paints the contents. |content_size| size of the underlying layer in @@ -49,7 +46,6 @@ SkColor background_color() const { return background_color_; } - RenderingStatsInstrumentation* rendering_stats_instrumentation_; int layer_id_; // True when it is known that all output pixels will be opaque.
diff --git a/cc/resources/gpu_raster_worker_pool.cc b/cc/resources/gpu_raster_worker_pool.cc index 04724f1..c913970 100644 --- a/cc/resources/gpu_raster_worker_pool.cc +++ b/cc/resources/gpu_raster_worker_pool.cc
@@ -40,7 +40,7 @@ // Turn on distance fields for layers that have ever animated. bool use_distance_field_text = use_distance_field_text_ || - raster_source->SuitableForDistanceFieldText(); + raster_source->ShouldAttemptToUseDistanceFieldText(); SkSurface* sk_surface = lock_.GetSkSurface(use_distance_field_text); if (!sk_surface)
diff --git a/cc/resources/picture_layer_tiling.cc b/cc/resources/picture_layer_tiling.cc index d8885e7..04b18c3 100644 --- a/cc/resources/picture_layer_tiling.cc +++ b/cc/resources/picture_layer_tiling.cc
@@ -167,18 +167,16 @@ VerifyLiveTilesRect(); } -void PictureLayerTiling::UpdateTilesToCurrentPile( +void PictureLayerTiling::UpdateTilesToCurrentRasterSource( const Region& layer_invalidation, const gfx::Size& new_layer_bounds) { DCHECK(!new_layer_bounds.IsEmpty()); - gfx::Size tile_size = tiling_data_.max_texture_size(); + gfx::Size content_bounds = + gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_)); + gfx::Size tile_size = client_->CalculateTileSize(content_bounds); if (new_layer_bounds != layer_bounds_) { - gfx::Size content_bounds = - gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_)); - - tile_size = client_->CalculateTileSize(content_bounds); if (tile_size.IsEmpty()) { layer_bounds_ = gfx::Size(); content_bounds = gfx::Size(); @@ -699,15 +697,13 @@ return current_occlusion_in_layer_space_.IsOccluded(tile_query_rect); } -bool PictureLayerTiling::IsTileRequiredForActivation(const Tile* tile) const { +bool PictureLayerTiling::IsTileRequiredForActivationIfVisible( + const Tile* tile) const { DCHECK_EQ(PENDING_TREE, client_->GetTree()); - // Note that although this function will determine whether tile is required - // for activation assuming that it is in visible (ie in the viewport). That is - // to say, even if the tile is outside of the viewport, it will be treated as - // if it was inside (there are no explicit checks for this). Hence, this - // function is only called for visible tiles to ensure we don't block - // activation on tiles outside of the viewport. + // This function assumes that the tile is visible (i.e. in the viewport). The + // caller needs to make sure that this condition is met to ensure we don't + // block activation on tiles outside of the viewport. // If we are not allowed to mark tiles as required for activation, then don't // do it. @@ -744,6 +740,21 @@ return true; } +bool PictureLayerTiling::IsTileRequiredForDrawIfVisible( + const Tile* tile) const { + DCHECK_EQ(ACTIVE_TREE, client_->GetTree()); + + // This function assumes that the tile is visible (i.e. in the viewport). + + if (resolution_ != HIGH_RESOLUTION) + return false; + + if (IsTileOccluded(tile)) + return false; + + return true; +} + void PictureLayerTiling::UpdateTileAndTwinPriority(Tile* tile) const { UpdateTilePriority(tile); @@ -756,6 +767,8 @@ tile->set_is_occluded(twin_tree, false); if (twin_tree == PENDING_TREE) tile->set_required_for_activation(false); + else + tile->set_required_for_draw(false); return; } @@ -766,22 +779,31 @@ // TODO(vmpstr): This code should return the priority instead of setting it on // the tile. This should be a part of the change to move tile priority from // tiles into iterators. + TilePriority::PriorityBin max_tile_priority_bin = + client_->GetMaxTilePriorityBin(); WhichTree tree = client_->GetTree(); DCHECK_EQ(TileAt(tile->tiling_i_index(), tile->tiling_j_index()), tile); gfx::Rect tile_bounds = tiling_data_.TileBounds(tile->tiling_i_index(), tile->tiling_j_index()); - if (current_visible_rect_.Intersects(tile_bounds)) { + if (max_tile_priority_bin <= TilePriority::NOW && + current_visible_rect_.Intersects(tile_bounds)) { tile->SetPriority(tree, TilePriority(resolution_, TilePriority::NOW, 0)); - if (tree == PENDING_TREE) - tile->set_required_for_activation(IsTileRequiredForActivation(tile)); + if (tree == PENDING_TREE) { + tile->set_required_for_activation( + IsTileRequiredForActivationIfVisible(tile)); + } else { + tile->set_required_for_draw(IsTileRequiredForDrawIfVisible(tile)); + } tile->set_is_occluded(tree, IsTileOccluded(tile)); return; } if (tree == PENDING_TREE) tile->set_required_for_activation(false); + else + tile->set_required_for_draw(false); tile->set_is_occluded(tree, false); DCHECK_GT(content_to_screen_scale_, 0.f); @@ -789,8 +811,9 @@ current_visible_rect_.ManhattanInternalDistance(tile_bounds) * content_to_screen_scale_; - if (current_soon_border_rect_.Intersects(tile_bounds) || - current_skewport_rect_.Intersects(tile_bounds)) { + if (max_tile_priority_bin <= TilePriority::SOON && + (current_soon_border_rect_.Intersects(tile_bounds) || + current_skewport_rect_.Intersects(tile_bounds))) { tile->SetPriority( tree, TilePriority(resolution_, TilePriority::SOON, distance_to_visible));
diff --git a/cc/resources/picture_layer_tiling.h b/cc/resources/picture_layer_tiling.h index 17da548..eb47b60 100644 --- a/cc/resources/picture_layer_tiling.h +++ b/cc/resources/picture_layer_tiling.h
@@ -29,7 +29,7 @@ namespace cc { class PictureLayerTiling; -class PicturePileImpl; +class RasterSource; class CC_EXPORT PictureLayerTilingClient { public: @@ -48,6 +48,7 @@ const PictureLayerTiling* tiling) const = 0; virtual PictureLayerTiling* GetRecycledTwinTiling( const PictureLayerTiling* tiling) = 0; + virtual TilePriority::PriorityBin GetMaxTilePriorityBin() const = 0; virtual size_t GetMaxTilesForInterestArea() const = 0; virtual float GetSkewportTargetTimeInSeconds() const = 0; virtual int GetSkewportExtrapolationLimitInContentPixels() const = 0; @@ -144,8 +145,8 @@ const gfx::Size& layer_bounds, PictureLayerTilingClient* client); gfx::Size layer_bounds() const { return layer_bounds_; } - void UpdateTilesToCurrentPile(const Region& layer_invalidation, - const gfx::Size& new_layer_bounds); + void UpdateTilesToCurrentRasterSource(const Region& layer_invalidation, + const gfx::Size& new_layer_bounds); void CreateMissingTilesInLiveTilesRect(); void RemoveTilesInRegion(const Region& layer_region); @@ -205,7 +206,8 @@ } bool IsTileOccluded(const Tile* tile) const; - bool IsTileRequiredForActivation(const Tile* tile) const; + bool IsTileRequiredForActivationIfVisible(const Tile* tile) const; + bool IsTileRequiredForDrawIfVisible(const Tile* tile) const; // Iterate over all tiles to fill content_rect. Even if tiles are invalid // (i.e. no valid resource) this tiling should still iterate over them.
diff --git a/cc/resources/picture_layer_tiling_perftest.cc b/cc/resources/picture_layer_tiling_perftest.cc index 8f91763..0d1443e 100644 --- a/cc/resources/picture_layer_tiling_perftest.cc +++ b/cc/resources/picture_layer_tiling_perftest.cc
@@ -59,7 +59,7 @@ void RunInvalidateTest(const std::string& test_name, const Region& region) { timer_.Reset(); do { - picture_layer_tiling_->UpdateTilesToCurrentPile( + picture_layer_tiling_->UpdateTilesToCurrentRasterSource( region, picture_layer_tiling_->tiling_size()); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired());
diff --git a/cc/resources/picture_layer_tiling_set.cc b/cc/resources/picture_layer_tiling_set.cc index b2b86b5..a0000b6 100644 --- a/cc/resources/picture_layer_tiling_set.cc +++ b/cc/resources/picture_layer_tiling_set.cc
@@ -69,8 +69,8 @@ if (PictureLayerTiling* this_tiling = TilingAtScale(contents_scale)) { this_tiling->set_resolution(other.tilings_[i]->resolution()); - this_tiling->UpdateTilesToCurrentPile(layer_invalidation, - new_layer_bounds); + this_tiling->UpdateTilesToCurrentRasterSource(layer_invalidation, + new_layer_bounds); this_tiling->CreateMissingTilesInLiveTilesRect(); if (this_tiling->resolution() == HIGH_RESOLUTION) have_high_res_tiling = true;
diff --git a/cc/resources/picture_layer_tiling_unittest.cc b/cc/resources/picture_layer_tiling_unittest.cc index f793097..786f943 100644 --- a/cc/resources/picture_layer_tiling_unittest.cc +++ b/cc/resources/picture_layer_tiling_unittest.cc
@@ -211,7 +211,7 @@ Region invalidation = SubtractRegions(gfx::Rect(tile_size), gfx::Rect(original_layer_size)); - tiling_->UpdateTilesToCurrentPile(invalidation, gfx::Size(200, 200)); + tiling_->UpdateTilesToCurrentRasterSource(invalidation, gfx::Size(200, 200)); EXPECT_FALSE(tiling_->TileAt(0, 0)); } @@ -266,8 +266,8 @@ // Shrink the tiling so that the last tile row/column is entirely in the // border pixels of the interior tiles. That row/column is removed. Region invalidation; - tiling_->UpdateTilesToCurrentPile(invalidation, - gfx::Size(right + 1, bottom + 1)); + tiling_->UpdateTilesToCurrentRasterSource(invalidation, + gfx::Size(right + 1, bottom + 1)); EXPECT_EQ(2, tiling_->TilingDataForTesting().num_tiles_x()); EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_y()); @@ -284,8 +284,8 @@ // Growing outside the current right/bottom tiles border pixels should create // the tiles again, even though the live rect has not changed size. - tiling_->UpdateTilesToCurrentPile(invalidation, - gfx::Size(right + 2, bottom + 2)); + tiling_->UpdateTilesToCurrentRasterSource(invalidation, + gfx::Size(right + 2, bottom + 2)); EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x()); EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y()); @@ -421,7 +421,7 @@ Region invalidation = SubtractRegions(gfx::Rect(tile_size), gfx::Rect(original_layer_size)); - tiling_->UpdateTilesToCurrentPile(invalidation, gfx::Size(200, 200)); + tiling_->UpdateTilesToCurrentRasterSource(invalidation, gfx::Size(200, 200)); EXPECT_FALSE(tiling_->TileAt(0, 0)); // The original tile was the same size after resize, but it would include new @@ -2178,5 +2178,29 @@ EXPECT_FALSE(recycle_tiling->TileAt(0, 0)); } +TEST_F(PictureLayerTilingIteratorTest, ResizeTilesAndUpdateToCurrent) { + // The tiling has four rows and three columns. + Initialize(gfx::Size(150, 100), 1, gfx::Size(250, 150)); + tiling_->CreateAllTilesForTesting(); + EXPECT_EQ(150, tiling_->TilingDataForTesting().max_texture_size().width()); + EXPECT_EQ(100, tiling_->TilingDataForTesting().max_texture_size().height()); + EXPECT_EQ(4u, tiling_->AllRefTilesForTesting().size()); + + client_.SetTileSize(gfx::Size(250, 200)); + client_.set_tree(PENDING_TREE); + + // Tile size in the tiling should still be 150x100. + EXPECT_EQ(150, tiling_->TilingDataForTesting().max_texture_size().width()); + EXPECT_EQ(100, tiling_->TilingDataForTesting().max_texture_size().height()); + + Region invalidation; + tiling_->UpdateTilesToCurrentRasterSource(invalidation, gfx::Size(250, 150)); + + // Tile size in the tiling should be resized to 250x200. + EXPECT_EQ(250, tiling_->TilingDataForTesting().max_texture_size().width()); + EXPECT_EQ(200, tiling_->TilingDataForTesting().max_texture_size().height()); + EXPECT_EQ(0u, tiling_->AllRefTilesForTesting().size()); +} + } // namespace } // namespace cc
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc index 1ad673b..ab138ca 100644 --- a/cc/resources/picture_pile.cc +++ b/cc/resources/picture_pile.cc
@@ -9,7 +9,7 @@ #include <vector> #include "cc/base/region.h" -#include "cc/debug/rendering_stats_instrumentation.h" +#include "cc/resources/picture_pile_impl.h" #include "cc/resources/raster_worker_pool.h" #include "skia/ext/analysis_canvas.h" @@ -22,6 +22,24 @@ // operations. const int kOpCountThatIsOkToAnalyze = 10; +// Dimensions of the tiles in this picture pile as well as the dimensions of +// the base picture in each tile. +const int kBasePictureSize = 512; +const int kTileGridBorderPixels = 1; +#ifdef NDEBUG +const bool kDefaultClearCanvasSetting = false; +#else +const bool kDefaultClearCanvasSetting = true; +#endif + +// Invalidation frequency settings. kInvalidationFrequencyThreshold is a value +// between 0 and 1 meaning invalidation frequency between 0% and 100% that +// indicates when to stop invalidating offscreen regions. +// kFrequentInvalidationDistanceThreshold defines what it means to be +// "offscreen" in terms of distance to visible in css pixels. +const float kInvalidationFrequencyThreshold = 0.75f; +const int kFrequentInvalidationDistanceThreshold = 512; + // TODO(humper): The density threshold here is somewhat arbitrary; need a // way to set // this from the command line so we can write a benchmark // script and find a sweet spot. @@ -149,8 +167,21 @@ namespace cc { PicturePile::PicturePile() - : is_suitable_for_gpu_rasterization_(true), - pixel_record_distance_(kPixelDistanceToRecord) { + : min_contents_scale_(0), + slow_down_raster_scale_factor_for_debug_(0), + contents_opaque_(false), + contents_fill_bounds_completely_(false), + clear_canvas_with_debug_color_(kDefaultClearCanvasSetting), + has_any_recordings_(false), + is_mask_(false), + is_solid_color_(false), + solid_color_(SK_ColorTRANSPARENT), + pixel_record_distance_(kPixelDistanceToRecord), + is_suitable_for_gpu_rasterization_(true) { + tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize)); + tile_grid_info_.fTileInterval.setEmpty(); + tile_grid_info_.fMargin.setEmpty(); + tile_grid_info_.fOffset.setZero(); } PicturePile::~PicturePile() { @@ -165,8 +196,7 @@ const gfx::Size& layer_size, const gfx::Rect& visible_layer_rect, int frame_number, - Picture::RecordingMode recording_mode, - RenderingStatsInstrumentation* stats_instrumentation) { + Picture::RecordingMode recording_mode) { background_color_ = background_color; contents_opaque_ = contents_opaque; contents_fill_bounds_completely_ = contents_fill_bounds_completely; @@ -174,7 +204,7 @@ bool updated = false; Region resize_invalidation; - gfx::Size old_tiling_size = tiling_size(); + gfx::Size old_tiling_size = GetSize(); if (old_tiling_size != layer_size) { tiling_.SetTilingSize(layer_size); updated = true; @@ -183,26 +213,35 @@ gfx::Rect interest_rect = visible_layer_rect; interest_rect.Inset(-pixel_record_distance_, -pixel_record_distance_); recorded_viewport_ = interest_rect; - recorded_viewport_.Intersect(gfx::Rect(tiling_size())); + recorded_viewport_.Intersect(gfx::Rect(GetSize())); gfx::Rect interest_rect_over_tiles = tiling_.ExpandRectToTileBounds(interest_rect); + gfx::Size min_tiling_size( + std::min(GetSize().width(), old_tiling_size.width()), + std::min(GetSize().height(), old_tiling_size.height())); + gfx::Size max_tiling_size( + std::max(GetSize().width(), old_tiling_size.width()), + std::max(GetSize().height(), old_tiling_size.height())); + if (old_tiling_size != layer_size) { has_any_recordings_ = false; - // Drop recordings that are outside the new layer bounds or that changed - // size. + // Drop recordings that are outside the new or old layer bounds or that + // changed size. Newly exposed areas are considered invalidated. + // Previously exposed areas that are now outside of bounds also need to + // be invalidated, as they may become part of raster when scale < 1. std::vector<PictureMapKey> to_erase; int min_toss_x = tiling_.num_tiles_x(); - if (tiling_size().width() > old_tiling_size.width()) { + if (max_tiling_size.width() > min_tiling_size.width()) { min_toss_x = - tiling_.FirstBorderTileXIndexFromSrcCoord(old_tiling_size.width()); + tiling_.FirstBorderTileXIndexFromSrcCoord(min_tiling_size.width()); } int min_toss_y = tiling_.num_tiles_y(); - if (tiling_size().height() > old_tiling_size.height()) { + if (max_tiling_size.height() > min_tiling_size.height()) { min_toss_y = - tiling_.FirstBorderTileYIndexFromSrcCoord(old_tiling_size.height()); + tiling_.FirstBorderTileYIndexFromSrcCoord(min_tiling_size.height()); } for (PictureMap::const_iterator it = picture_map_.begin(); it != picture_map_.end(); @@ -221,20 +260,22 @@ // If a recording is dropped and not re-recorded below, invalidate that // full recording to cause any raster tiles that would use it to be // dropped. - // If the recording will be replaced below, just invalidate newly exposed - // areas to force raster tiles that include the old recording to know - // there is new recording to display. - gfx::Rect old_tiling_rect_over_tiles = - tiling_.ExpandRectToTileBounds(gfx::Rect(old_tiling_size)); + // If the recording will be replaced below, invalidate newly exposed + // areas and previously exposed areas to force raster tiles that include the + // old recording to know there is new recording to display. + gfx::Rect min_tiling_rect_over_tiles = + tiling_.ExpandRectToTileBounds(gfx::Rect(min_tiling_size)); if (min_toss_x < tiling_.num_tiles_x()) { // The bounds which we want to invalidate are the tiles along the old - // edge of the pile. We'll call this bounding box the OLD EDGE RECT. + // edge of the pile when expanding, or the new edge of the pile when + // shrinking. In either case, it's the difference of the two, so we'll + // call this bounding box the DELTA EDGE RECT. // - // In the picture below, the old edge rect would be the bounding box - // of tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index - // of the same tiles. + // In the picture below, the delta edge rect would be the bounding box of + // tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index of + // the same tiles. // - // old pile edge-v new pile edge-v + // min pile edge-v max pile edge-v // ---------------+ - - - - - - - -+ // mmppssvvyybbeeh|h . // mmppssvvyybbeeh|h . @@ -242,33 +283,33 @@ // nnqqttwwzzccffi|i . // oorruuxxaaddggj|j . // oorruuxxaaddggj|j . - // ---------------+ - - - - - - - -+ <- old pile edge + // ---------------+ - - - - - - - -+ <- min pile edge // . - // - - - - - - - - - - - - - - - -+ <- new pile edge + // - - - - - - - - - - - - - - - -+ <- max pile edge // // If you were to slide a vertical beam from the left edge of the - // old edge rect toward the right, it would either hit the right edge - // of the old edge rect, or the interest rect (expanded to the bounds + // delta edge rect toward the right, it would either hit the right edge + // of the delta edge rect, or the interest rect (expanded to the bounds // of the tiles it touches). The same is true for a beam parallel to - // any of the four edges, sliding accross the old edge rect. We use + // any of the four edges, sliding across the delta edge rect. We use // the union of these four rectangles generated by these beams to - // determine which part of the old edge rect is outside of the expanded + // determine which part of the delta edge rect is outside of the expanded // interest rect. // - // Case 1: Intersect rect is outside the old edge rect. It can be + // Case 1: Intersect rect is outside the delta edge rect. It can be // either on the left or the right. The |left_rect| and |right_rect|, // cover this case, one will be empty and one will cover the full - // old edge rect. In the picture below, |left_rect| would cover the - // old edge rect, and |right_rect| would be empty. + // delta edge rect. In the picture below, |left_rect| would cover the + // delta edge rect, and |right_rect| would be empty. // +----------------------+ |^^^^^^^^^^^^^^^| - // |===> OLD EDGE RECT | | | + // |===> DELTA EDGE RECT | | | // |===> | | INTEREST RECT | // |===> | | | // |===> | | | // +----------------------+ |vvvvvvvvvvvvvvv| // - // Case 2: Interest rect is inside the old edge rect. It will always - // fill the entire old edge rect horizontally since the old edge rect + // Case 2: Interest rect is inside the delta edge rect. It will always + // fill the entire delta edge rect horizontally since the old edge rect // is a single tile wide, and the interest rect has been expanded to the // bounds of the tiles it touches. In this case the |left_rect| and // |right_rect| will be empty, but the case is handled by the |top_rect| @@ -285,19 +326,19 @@ // | | // +-----------------+ // | | - // | OLD EDGE RECT | + // | DELTA EDGE RECT | // +-----------------+ // // Lastly, we need to consider tiles inside the expanded interest rect. // For those tiles, we want to invalidate exactly the newly exposed - // pixels. In the picture below the tiles in the old edge rect have been - // resized and the area covered by periods must be invalidated. The + // pixels. In the picture below the tiles in the delta edge rect have + // been resized and the area covered by periods must be invalidated. The // |exposed_rect| will cover exactly that area. - // v-old pile edge + // v-min pile edge // +---------+-------+ // | ........| // | ........| - // | OLD EDGE.RECT..| + // | DELTA EDGE.RECT.| // | ........| // | ........| // | ........| @@ -308,18 +349,18 @@ int left = tiling_.TilePositionX(min_toss_x); int right = left + tiling_.TileSizeX(min_toss_x); - int top = old_tiling_rect_over_tiles.y(); - int bottom = old_tiling_rect_over_tiles.bottom(); + int top = min_tiling_rect_over_tiles.y(); + int bottom = min_tiling_rect_over_tiles.bottom(); int left_until = std::min(interest_rect_over_tiles.x(), right); int right_until = std::max(interest_rect_over_tiles.right(), left); int top_until = std::min(interest_rect_over_tiles.y(), bottom); int bottom_until = std::max(interest_rect_over_tiles.bottom(), top); - int exposed_left = old_tiling_size.width(); - int exposed_left_until = tiling_size().width(); + int exposed_left = min_tiling_size.width(); + int exposed_left_until = max_tiling_size.width(); int exposed_top = top; - int exposed_bottom = tiling_size().height(); + int exposed_bottom = max_tiling_size.height(); DCHECK_GE(exposed_left, left); gfx::Rect left_rect(left, top, left_until - left, bottom - top); @@ -339,23 +380,23 @@ } if (min_toss_y < tiling_.num_tiles_y()) { // The same thing occurs here as in the case above, but the invalidation - // rect is the bounding box around the bottom row of tiles in the old + // rect is the bounding box around the bottom row of tiles in the min // pile. This would be tiles {o,r,u,x,a,d,g,j} in the above picture. int top = tiling_.TilePositionY(min_toss_y); int bottom = top + tiling_.TileSizeY(min_toss_y); - int left = old_tiling_rect_over_tiles.x(); - int right = old_tiling_rect_over_tiles.right(); + int left = min_tiling_rect_over_tiles.x(); + int right = min_tiling_rect_over_tiles.right(); int top_until = std::min(interest_rect_over_tiles.y(), bottom); int bottom_until = std::max(interest_rect_over_tiles.bottom(), top); int left_until = std::min(interest_rect_over_tiles.x(), right); int right_until = std::max(interest_rect_over_tiles.right(), left); - int exposed_top = old_tiling_size.height(); - int exposed_top_until = tiling_size().height(); + int exposed_top = min_tiling_size.height(); + int exposed_top_until = max_tiling_size.height(); int exposed_left = left; - int exposed_right = tiling_size().width(); + int exposed_right = max_tiling_size.width(); DCHECK_GE(exposed_top, top); gfx::Rect left_rect(left, top, left_until - left, bottom - top); @@ -378,7 +419,7 @@ // Detect cases where the full pile is invalidated, in this situation we // can just drop/invalidate everything. if (invalidation->Contains(gfx::Rect(old_tiling_size)) || - invalidation->Contains(gfx::Rect(tiling_size()))) { + invalidation->Contains(gfx::Rect(GetSize()))) { for (auto& it : picture_map_) updated = it.second.Invalidate(frame_number) || updated; } else { @@ -489,9 +530,7 @@ bool gather_pixel_refs = RasterWorkerPool::GetNumRasterThreads() > 1; { - base::TimeDelta best_duration = base::TimeDelta::Max(); for (int i = 0; i < repeat_count; i++) { - base::TimeTicks start_time = stats_instrumentation->StartRecording(); picture = Picture::Create(record_rect, painter, tile_grid_info_, @@ -505,13 +544,7 @@ // the pile after each invalidation. is_suitable_for_gpu_rasterization_ &= picture->IsSuitableForGpuRasterization(); - base::TimeDelta duration = - stats_instrumentation->EndRecording(start_time); - best_duration = std::min(duration, best_duration); } - int recorded_pixel_count = - picture->LayerRect().width() * picture->LayerRect().height(); - stats_instrumentation->AddRecord(best_duration, recorded_pixel_count); } bool found_tile_for_recorded_picture = false; @@ -536,11 +569,93 @@ return true; } +gfx::Size PicturePile::GetSize() const { + return tiling_.tiling_size(); +} + void PicturePile::SetEmptyBounds() { tiling_.SetTilingSize(gfx::Size()); Clear(); } +void PicturePile::SetMinContentsScale(float min_contents_scale) { + DCHECK(min_contents_scale); + if (min_contents_scale_ == min_contents_scale) + return; + + // Picture contents are played back scaled. When the final contents scale is + // less than 1 (i.e. low res), then multiple recorded pixels will be used + // to raster one final pixel. To avoid splitting a final pixel across + // pictures (which would result in incorrect rasterization due to blending), a + // buffer margin is added so that any picture can be snapped to integral + // final pixels. + // + // For example, if a 1/4 contents scale is used, then that would be 3 buffer + // pixels, since that's the minimum number of pixels to add so that resulting + // content can be snapped to a four pixel aligned grid. + int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1); + buffer_pixels = std::max(0, buffer_pixels); + SetBufferPixels(buffer_pixels); + min_contents_scale_ = min_contents_scale; +} + +// static +void PicturePile::ComputeTileGridInfo(const gfx::Size& tile_grid_size, + SkTileGridFactory::TileGridInfo* info) { + DCHECK(info); + info->fTileInterval.set(tile_grid_size.width() - 2 * kTileGridBorderPixels, + tile_grid_size.height() - 2 * kTileGridBorderPixels); + DCHECK_GT(info->fTileInterval.width(), 0); + DCHECK_GT(info->fTileInterval.height(), 0); + info->fMargin.set(kTileGridBorderPixels, kTileGridBorderPixels); + // Offset the tile grid coordinate space to take into account the fact + // that the top-most and left-most tiles do not have top and left borders + // respectively. + info->fOffset.set(-kTileGridBorderPixels, -kTileGridBorderPixels); +} + +void PicturePile::SetTileGridSize(const gfx::Size& tile_grid_size) { + ComputeTileGridInfo(tile_grid_size, &tile_grid_info_); +} + +void PicturePile::SetSlowdownRasterScaleFactor(int factor) { + slow_down_raster_scale_factor_for_debug_ = factor; +} + +void PicturePile::SetIsMask(bool is_mask) { + is_mask_ = is_mask; +} + +void PicturePile::SetUnsuitableForGpuRasterizationForTesting() { + is_suitable_for_gpu_rasterization_ = false; +} + +bool PicturePile::IsSuitableForGpuRasterization() const { + return is_suitable_for_gpu_rasterization_; +} + +scoped_refptr<RasterSource> PicturePile::CreateRasterSource() const { + return scoped_refptr<RasterSource>( + PicturePileImpl::CreateFromPicturePile(this)); +} + +SkTileGridFactory::TileGridInfo PicturePile::GetTileGridInfoForTesting() const { + return tile_grid_info_; +} + +bool PicturePile::CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const { + bool include_borders = false; + for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); + tile_iter; ++tile_iter) { + PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); + if (map_iter == picture_map_.end()) + return false; + if (!map_iter->second.GetPicture()) + return false; + } + return true; +} + void PicturePile::DetermineIfSolidColor() { is_solid_color_ = false; solid_color_ = SK_ColorTRANSPARENT; @@ -573,4 +688,81 @@ is_solid_color_ = canvas.GetColorIfSolid(&solid_color_); } +gfx::Rect PicturePile::PaddedRect(const PictureMapKey& key) const { + gfx::Rect tile = tiling_.TileBounds(key.first, key.second); + return PadRect(tile); +} + +gfx::Rect PicturePile::PadRect(const gfx::Rect& rect) const { + gfx::Rect padded_rect = rect; + padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(), + -buffer_pixels()); + return padded_rect; +} + +void PicturePile::Clear() { + picture_map_.clear(); + recorded_viewport_ = gfx::Rect(); + has_any_recordings_ = false; + is_solid_color_ = false; +} + +PicturePile::PictureInfo::PictureInfo() : last_frame_number_(0) { +} + +PicturePile::PictureInfo::~PictureInfo() { +} + +void PicturePile::PictureInfo::AdvanceInvalidationHistory(int frame_number) { + DCHECK_GE(frame_number, last_frame_number_); + if (frame_number == last_frame_number_) + return; + + invalidation_history_ <<= (frame_number - last_frame_number_); + last_frame_number_ = frame_number; +} + +bool PicturePile::PictureInfo::Invalidate(int frame_number) { + AdvanceInvalidationHistory(frame_number); + invalidation_history_.set(0); + + bool did_invalidate = !!picture_.get(); + picture_ = NULL; + return did_invalidate; +} + +bool PicturePile::PictureInfo::NeedsRecording(int frame_number, + int distance_to_visible) { + AdvanceInvalidationHistory(frame_number); + + // We only need recording if we don't have a picture. Furthermore, we only + // need a recording if we're within frequent invalidation distance threshold + // or the invalidation is not frequent enough (below invalidation frequency + // threshold). + return !picture_.get() && + ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) || + (GetInvalidationFrequency() < kInvalidationFrequencyThreshold)); +} + +void PicturePile::SetBufferPixels(int new_buffer_pixels) { + if (new_buffer_pixels == buffer_pixels()) + return; + + Clear(); + tiling_.SetBorderTexels(new_buffer_pixels); +} + +void PicturePile::PictureInfo::SetPicture(scoped_refptr<Picture> picture) { + picture_ = picture; +} + +const Picture* PicturePile::PictureInfo::GetPicture() const { + return picture_.get(); +} + +float PicturePile::PictureInfo::GetInvalidationFrequency() const { + return invalidation_history_.count() / + static_cast<float>(INVALIDATION_FRAMES_TRACKED); +} + } // namespace cc
diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h index 7cd2229..ffd9b52 100644 --- a/cc/resources/picture_pile.h +++ b/cc/resources/picture_pile.h
@@ -5,25 +5,21 @@ #ifndef CC_RESOURCES_PICTURE_PILE_H_ #define CC_RESOURCES_PICTURE_PILE_H_ +#include <bitset> + #include "base/memory/ref_counted.h" -#include "cc/resources/picture_pile_base.h" -#include "ui/gfx/geometry/rect.h" +#include "cc/base/tiling_data.h" +#include "cc/resources/recording_source.h" namespace cc { class PicturePileImpl; -class Region; -class RenderingStatsInstrumentation; -class CC_EXPORT PicturePile : public PicturePileBase { +class CC_EXPORT PicturePile : public RecordingSource { public: PicturePile(); ~PicturePile() override; - // Re-record parts of the picture that are invalid. - // Invalidations are in layer space, and will be expanded to cover everything - // that was either recorded/changed or that has no recording, leaving out only - // pieces that we had a recording for and it was not changed. - // Return true iff the pile was modified. + // RecordingSource overrides. bool UpdateAndExpandInvalidation( ContentLayerClient* painter, Region* invalidation, @@ -33,35 +29,92 @@ const gfx::Size& layer_size, const gfx::Rect& visible_layer_rect, int frame_number, - Picture::RecordingMode recording_mode, - RenderingStatsInstrumentation* stats_instrumentation); + Picture::RecordingMode recording_mode) override; + gfx::Size GetSize() const final; + void SetEmptyBounds() override; + void SetMinContentsScale(float min_contents_scale) override; + void SetTileGridSize(const gfx::Size& tile_grid_size) override; + void SetSlowdownRasterScaleFactor(int factor) override; + void SetIsMask(bool is_mask) override; + bool IsSuitableForGpuRasterization() const override; + scoped_refptr<RasterSource> CreateRasterSource() const override; + void SetUnsuitableForGpuRasterizationForTesting() override; + SkTileGridFactory::TileGridInfo GetTileGridInfoForTesting() const override; - void SetEmptyBounds(); + static void ComputeTileGridInfo(const gfx::Size& tile_grid_size, + SkTileGridFactory::TileGridInfo* info); - void set_slow_down_raster_scale_factor(int factor) { - slow_down_raster_scale_factor_for_debug_ = factor; - } + protected: + class CC_EXPORT PictureInfo { + public: + enum { INVALIDATION_FRAMES_TRACKED = 32 }; - void set_show_debug_picture_borders(bool show) { - show_debug_picture_borders_ = show; - } + PictureInfo(); + ~PictureInfo(); - bool is_suitable_for_gpu_rasterization() const { - return is_suitable_for_gpu_rasterization_; - } - void SetUnsuitableForGpuRasterizationForTesting() { - is_suitable_for_gpu_rasterization_ = false; - } + bool Invalidate(int frame_number); + bool NeedsRecording(int frame_number, int distance_to_visible); + void SetPicture(scoped_refptr<Picture> picture); + const Picture* GetPicture() const; - void SetPixelRecordDistanceForTesting(int d) { pixel_record_distance_ = d; } + float GetInvalidationFrequencyForTesting() const { + return GetInvalidationFrequency(); + } + + private: + void AdvanceInvalidationHistory(int frame_number); + float GetInvalidationFrequency() const; + + int last_frame_number_; + scoped_refptr<const Picture> picture_; + std::bitset<INVALIDATION_FRAMES_TRACKED> invalidation_history_; + }; + + typedef std::pair<int, int> PictureMapKey; + typedef base::hash_map<PictureMapKey, PictureInfo> PictureMap; + + // An internal CanRaster check that goes to the picture_map rather than + // using the recorded_viewport hint. + bool CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const; + + void Clear(); + + gfx::Rect PaddedRect(const PictureMapKey& key) const; + gfx::Rect PadRect(const gfx::Rect& rect) const; + + int buffer_pixels() const { return tiling_.border_texels(); } + + // A picture pile is a tiled set of pictures. The picture map is a map of tile + // indices to picture infos. + PictureMap picture_map_; + TilingData tiling_; + + // If non-empty, all pictures tiles inside this rect are recorded. There may + // be recordings outside this rect, but everything inside the rect is + // recorded. + gfx::Rect recorded_viewport_; + float min_contents_scale_; + SkTileGridFactory::TileGridInfo tile_grid_info_; + SkColor background_color_; + int slow_down_raster_scale_factor_for_debug_; + bool contents_opaque_; + bool contents_fill_bounds_completely_; + bool clear_canvas_with_debug_color_; + // A hint about whether there are any recordings. This may be a false + // positive. + bool has_any_recordings_; + bool is_mask_; + bool is_solid_color_; + SkColor solid_color_; + int pixel_record_distance_; private: friend class PicturePileImpl; void DetermineIfSolidColor(); + void SetBufferPixels(int buffer_pixels); bool is_suitable_for_gpu_rasterization_; - int pixel_record_distance_; DISALLOW_COPY_AND_ASSIGN(PicturePile); };
diff --git a/cc/resources/picture_pile_base.cc b/cc/resources/picture_pile_base.cc deleted file mode 100644 index a145b6c..0000000 --- a/cc/resources/picture_pile_base.cc +++ /dev/null
@@ -1,257 +0,0 @@ -// Copyright 2013 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 "cc/resources/picture_pile_base.h" - -#include <algorithm> -#include <set> -#include <vector> - -#include "base/debug/trace_event_argument.h" -#include "base/logging.h" -#include "base/values.h" -#include "cc/debug/traced_value.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/geometry/rect_conversions.h" - -namespace { -// Dimensions of the tiles in this picture pile as well as the dimensions of -// the base picture in each tile. -const int kBasePictureSize = 512; -const int kTileGridBorderPixels = 1; -#ifdef NDEBUG -const bool kDefaultClearCanvasSetting = false; -#else -const bool kDefaultClearCanvasSetting = true; -#endif - -// Invalidation frequency settings. kInvalidationFrequencyThreshold is a value -// between 0 and 1 meaning invalidation frequency between 0% and 100% that -// indicates when to stop invalidating offscreen regions. -// kFrequentInvalidationDistanceThreshold defines what it means to be -// "offscreen" in terms of distance to visible in css pixels. -const float kInvalidationFrequencyThreshold = 0.75f; -const int kFrequentInvalidationDistanceThreshold = 512; - -} // namespace - -namespace cc { - -PicturePileBase::PicturePileBase() - : min_contents_scale_(0), - background_color_(SkColorSetARGBInline(0, 0, 0, 0)), - slow_down_raster_scale_factor_for_debug_(0), - contents_opaque_(false), - contents_fill_bounds_completely_(false), - show_debug_picture_borders_(false), - clear_canvas_with_debug_color_(kDefaultClearCanvasSetting), - has_any_recordings_(false), - is_mask_(false), - is_solid_color_(false), - solid_color_(SK_ColorTRANSPARENT) { - tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize)); - tile_grid_info_.fTileInterval.setEmpty(); - tile_grid_info_.fMargin.setEmpty(); - tile_grid_info_.fOffset.setZero(); -} - -PicturePileBase::PicturePileBase(const PicturePileBase* other) - : picture_map_(other->picture_map_), - tiling_(other->tiling_), - recorded_viewport_(other->recorded_viewport_), - min_contents_scale_(other->min_contents_scale_), - tile_grid_info_(other->tile_grid_info_), - background_color_(other->background_color_), - slow_down_raster_scale_factor_for_debug_( - other->slow_down_raster_scale_factor_for_debug_), - contents_opaque_(other->contents_opaque_), - contents_fill_bounds_completely_(other->contents_fill_bounds_completely_), - show_debug_picture_borders_(other->show_debug_picture_borders_), - clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), - has_any_recordings_(other->has_any_recordings_), - is_mask_(other->is_mask_), - is_solid_color_(other->is_solid_color_), - solid_color_(other->solid_color_) { -} - -PicturePileBase::~PicturePileBase() { -} - -void PicturePileBase::SetMinContentsScale(float min_contents_scale) { - DCHECK(min_contents_scale); - if (min_contents_scale_ == min_contents_scale) - return; - - // Picture contents are played back scaled. When the final contents scale is - // less than 1 (i.e. low res), then multiple recorded pixels will be used - // to raster one final pixel. To avoid splitting a final pixel across - // pictures (which would result in incorrect rasterization due to blending), a - // buffer margin is added so that any picture can be snapped to integral - // final pixels. - // - // For example, if a 1/4 contents scale is used, then that would be 3 buffer - // pixels, since that's the minimum number of pixels to add so that resulting - // content can be snapped to a four pixel aligned grid. - int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1); - buffer_pixels = std::max(0, buffer_pixels); - SetBufferPixels(buffer_pixels); - min_contents_scale_ = min_contents_scale; -} - -// static -void PicturePileBase::ComputeTileGridInfo( - const gfx::Size& tile_grid_size, - SkTileGridFactory::TileGridInfo* info) { - DCHECK(info); - info->fTileInterval.set(tile_grid_size.width() - 2 * kTileGridBorderPixels, - tile_grid_size.height() - 2 * kTileGridBorderPixels); - DCHECK_GT(info->fTileInterval.width(), 0); - DCHECK_GT(info->fTileInterval.height(), 0); - info->fMargin.set(kTileGridBorderPixels, kTileGridBorderPixels); - // Offset the tile grid coordinate space to take into account the fact - // that the top-most and left-most tiles do not have top and left borders - // respectively. - info->fOffset.set(-kTileGridBorderPixels, -kTileGridBorderPixels); -} - -void PicturePileBase::SetTileGridSize(const gfx::Size& tile_grid_size) { - ComputeTileGridInfo(tile_grid_size, &tile_grid_info_); -} - -void PicturePileBase::SetBufferPixels(int new_buffer_pixels) { - if (new_buffer_pixels == buffer_pixels()) - return; - - Clear(); - tiling_.SetBorderTexels(new_buffer_pixels); -} - -void PicturePileBase::Clear() { - picture_map_.clear(); - recorded_viewport_ = gfx::Rect(); - has_any_recordings_ = false; - is_solid_color_ = false; -} - -bool PicturePileBase::HasRecordingAt(int x, int y) { - PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y)); - if (found == picture_map_.end()) - return false; - return !!found->second.GetPicture(); -} - -bool PicturePileBase::CanRaster(float contents_scale, - const gfx::Rect& content_rect) const { - if (tiling_.tiling_size().IsEmpty()) - return false; - gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( - content_rect, 1.f / contents_scale); - layer_rect.Intersect(gfx::Rect(tiling_.tiling_size())); - - // Common case inside of viewport to avoid the slower map lookups. - if (recorded_viewport_.Contains(layer_rect)) { - // Sanity check that there are no false positives in recorded_viewport_. - DCHECK(CanRasterSlowTileCheck(layer_rect)); - return true; - } - - return CanRasterSlowTileCheck(layer_rect); -} - -bool PicturePileBase::CanRasterSlowTileCheck( - const gfx::Rect& layer_rect) const { - bool include_borders = false; - for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); - tile_iter; - ++tile_iter) { - PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); - if (map_iter == picture_map_.end()) - return false; - if (!map_iter->second.GetPicture()) - return false; - } - return true; -} - -gfx::Rect PicturePileBase::PaddedRect(const PictureMapKey& key) const { - gfx::Rect tile = tiling_.TileBounds(key.first, key.second); - return PadRect(tile); -} - -gfx::Rect PicturePileBase::PadRect(const gfx::Rect& rect) const { - gfx::Rect padded_rect = rect; - padded_rect.Inset( - -buffer_pixels(), -buffer_pixels(), -buffer_pixels(), -buffer_pixels()); - return padded_rect; -} - -void PicturePileBase::AsValueInto(base::debug::TracedValue* pictures) const { - gfx::Rect tiling_rect(tiling_.tiling_size()); - std::set<const void*> appended_pictures; - bool include_borders = true; - for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders); - tile_iter; - ++tile_iter) { - PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); - if (map_iter == picture_map_.end()) - continue; - - const Picture* picture = map_iter->second.GetPicture(); - if (picture && (appended_pictures.count(picture) == 0)) { - appended_pictures.insert(picture); - TracedValue::AppendIDRef(picture, pictures); - } - } -} - -PicturePileBase::PictureInfo::PictureInfo() : last_frame_number_(0) {} - -PicturePileBase::PictureInfo::~PictureInfo() {} - -void PicturePileBase::PictureInfo::AdvanceInvalidationHistory( - int frame_number) { - DCHECK_GE(frame_number, last_frame_number_); - if (frame_number == last_frame_number_) - return; - - invalidation_history_ <<= (frame_number - last_frame_number_); - last_frame_number_ = frame_number; -} - -bool PicturePileBase::PictureInfo::Invalidate(int frame_number) { - AdvanceInvalidationHistory(frame_number); - invalidation_history_.set(0); - - bool did_invalidate = !!picture_.get(); - picture_ = NULL; - return did_invalidate; -} - -bool PicturePileBase::PictureInfo::NeedsRecording(int frame_number, - int distance_to_visible) { - AdvanceInvalidationHistory(frame_number); - - // We only need recording if we don't have a picture. Furthermore, we only - // need a recording if we're within frequent invalidation distance threshold - // or the invalidation is not frequent enough (below invalidation frequency - // threshold). - return !picture_.get() && - ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) || - (GetInvalidationFrequency() < kInvalidationFrequencyThreshold)); -} - -void PicturePileBase::PictureInfo::SetPicture(scoped_refptr<Picture> picture) { - picture_ = picture; -} - -const Picture* PicturePileBase::PictureInfo::GetPicture() const { - return picture_.get(); -} - -float PicturePileBase::PictureInfo::GetInvalidationFrequency() const { - return invalidation_history_.count() / - static_cast<float>(INVALIDATION_FRAMES_TRACKED); -} - -} // namespace cc
diff --git a/cc/resources/picture_pile_base.h b/cc/resources/picture_pile_base.h deleted file mode 100644 index 71e5bad..0000000 --- a/cc/resources/picture_pile_base.h +++ /dev/null
@@ -1,139 +0,0 @@ -// Copyright 2013 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 CC_RESOURCES_PICTURE_PILE_BASE_H_ -#define CC_RESOURCES_PICTURE_PILE_BASE_H_ - -#include <bitset> -#include <list> -#include <utility> - -#include "base/containers/hash_tables.h" -#include "cc/base/cc_export.h" -#include "cc/base/region.h" -#include "cc/base/tiling_data.h" -#include "cc/resources/picture.h" -#include "ui/gfx/geometry/size.h" - -namespace base { -namespace debug { -class TracedValue; -} -class Value; -} - -namespace cc { - -class CC_EXPORT PicturePileBase { - public: - PicturePileBase(); - explicit PicturePileBase(const PicturePileBase* other); - - gfx::Size tiling_size() const { return tiling_.tiling_size(); } - void SetMinContentsScale(float min_contents_scale); - - // If non-empty, all pictures tiles inside this rect are recorded. There may - // be recordings outside this rect, but everything inside the rect is - // recorded. - gfx::Rect recorded_viewport() const { return recorded_viewport_; } - - int num_tiles_x() const { return tiling_.num_tiles_x(); } - int num_tiles_y() const { return tiling_.num_tiles_y(); } - gfx::Rect tile_bounds(int x, int y) const { return tiling_.TileBounds(x, y); } - bool HasRecordingAt(int x, int y); - bool CanRaster(float contents_scale, const gfx::Rect& content_rect) const; - - // If this pile contains any valid recordings. May have false positives. - bool HasRecordings() const { return has_any_recordings_; } - - bool is_solid_color() const { return is_solid_color_; } - SkColor solid_color() const { return solid_color_; } - - void set_is_mask(bool is_mask) { is_mask_ = is_mask; } - bool is_mask() const { return is_mask_; } - - static void ComputeTileGridInfo(const gfx::Size& tile_grid_size, - SkTileGridFactory::TileGridInfo* info); - - void SetTileGridSize(const gfx::Size& tile_grid_size); - TilingData& tiling() { return tiling_; } - - void AsValueInto(base::debug::TracedValue* array) const; - - SkTileGridFactory::TileGridInfo GetTileGridInfoForTesting() const { - return tile_grid_info_; - } - - protected: - class CC_EXPORT PictureInfo { - public: - enum { - INVALIDATION_FRAMES_TRACKED = 32 - }; - - PictureInfo(); - ~PictureInfo(); - - bool Invalidate(int frame_number); - bool NeedsRecording(int frame_number, int distance_to_visible); - void SetPicture(scoped_refptr<Picture> picture); - const Picture* GetPicture() const; - - float GetInvalidationFrequencyForTesting() const { - return GetInvalidationFrequency(); - } - - private: - void AdvanceInvalidationHistory(int frame_number); - float GetInvalidationFrequency() const; - - int last_frame_number_; - scoped_refptr<const Picture> picture_; - std::bitset<INVALIDATION_FRAMES_TRACKED> invalidation_history_; - }; - - typedef std::pair<int, int> PictureMapKey; - typedef base::hash_map<PictureMapKey, PictureInfo> PictureMap; - - virtual ~PicturePileBase(); - - int buffer_pixels() const { return tiling_.border_texels(); } - void Clear(); - - gfx::Rect PaddedRect(const PictureMapKey& key) const; - gfx::Rect PadRect(const gfx::Rect& rect) const; - - // An internal CanRaster check that goes to the picture_map rather than - // using the recorded_viewport hint. - bool CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const; - - // A picture pile is a tiled set of pictures. The picture map is a map of tile - // indices to picture infos. - PictureMap picture_map_; - TilingData tiling_; - gfx::Rect recorded_viewport_; - float min_contents_scale_; - SkTileGridFactory::TileGridInfo tile_grid_info_; - SkColor background_color_; - int slow_down_raster_scale_factor_for_debug_; - bool contents_opaque_; - bool contents_fill_bounds_completely_; - bool show_debug_picture_borders_; - bool clear_canvas_with_debug_color_; - // A hint about whether there are any recordings. This may be a false - // positive. - bool has_any_recordings_; - bool is_mask_; - bool is_solid_color_; - SkColor solid_color_; - - private: - void SetBufferPixels(int buffer_pixels); - - DISALLOW_COPY_AND_ASSIGN(PicturePileBase); -}; - -} // namespace cc - -#endif // CC_RESOURCES_PICTURE_PILE_BASE_H_
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc index 0ad746c..7ec9252 100644 --- a/cc/resources/picture_pile_impl.cc +++ b/cc/resources/picture_pile_impl.cc
@@ -20,26 +20,49 @@ return make_scoped_refptr(new PicturePileImpl); } -scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther( - const PicturePileBase* other) { +scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromPicturePile( + const PicturePile* other) { return make_scoped_refptr(new PicturePileImpl(other)); } PicturePileImpl::PicturePileImpl() - : likely_to_be_used_for_transform_animation_(false) { + : background_color_(SK_ColorTRANSPARENT), + contents_opaque_(false), + contents_fill_bounds_completely_(false), + is_solid_color_(false), + solid_color_(SK_ColorTRANSPARENT), + has_any_recordings_(false), + is_mask_(false), + clear_canvas_with_debug_color_(false), + min_contents_scale_(0.f), + slow_down_raster_scale_factor_for_debug_(0), + should_attempt_to_use_distance_field_text_(false) { } -PicturePileImpl::PicturePileImpl(const PicturePileBase* other) - : PicturePileBase(other), - likely_to_be_used_for_transform_animation_(false) { +PicturePileImpl::PicturePileImpl(const PicturePile* other) + : picture_map_(other->picture_map_), + tiling_(other->tiling_), + background_color_(other->background_color_), + contents_opaque_(other->contents_opaque_), + contents_fill_bounds_completely_(other->contents_fill_bounds_completely_), + is_solid_color_(other->is_solid_color_), + solid_color_(other->solid_color_), + recorded_viewport_(other->recorded_viewport_), + has_any_recordings_(other->has_any_recordings_), + is_mask_(other->is_mask_), + clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), + min_contents_scale_(other->min_contents_scale_), + slow_down_raster_scale_factor_for_debug_( + other->slow_down_raster_scale_factor_for_debug_), + should_attempt_to_use_distance_field_text_(false) { } PicturePileImpl::~PicturePileImpl() { } -void PicturePileImpl::RasterDirect(SkCanvas* canvas, - const gfx::Rect& canvas_rect, - float contents_scale) const { +void PicturePileImpl::PlaybackToSharedCanvas(SkCanvas* canvas, + const gfx::Rect& canvas_rect, + float contents_scale) const { RasterCommon(canvas, NULL, canvas_rect, @@ -320,11 +343,88 @@ bool PicturePileImpl::CoversRect(const gfx::Rect& content_rect, float contents_scale) const { - return CanRaster(contents_scale, content_rect); + if (tiling_.tiling_size().IsEmpty()) + return false; + gfx::Rect layer_rect = + gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale); + layer_rect.Intersect(gfx::Rect(tiling_.tiling_size())); + + // Common case inside of viewport to avoid the slower map lookups. + if (recorded_viewport_.Contains(layer_rect)) { + // Sanity check that there are no false positives in recorded_viewport_. + DCHECK(CanRasterSlowTileCheck(layer_rect)); + return true; + } + + return CanRasterSlowTileCheck(layer_rect); } -bool PicturePileImpl::SuitableForDistanceFieldText() const { - return likely_to_be_used_for_transform_animation_; +gfx::Size PicturePileImpl::GetSize() const { + return tiling_.tiling_size(); +} + +bool PicturePileImpl::IsSolidColor() const { + return is_solid_color_; +} + +SkColor PicturePileImpl::GetSolidColor() const { + DCHECK(IsSolidColor()); + return solid_color_; +} + +bool PicturePileImpl::HasRecordings() const { + return has_any_recordings_; +} + +gfx::Rect PicturePileImpl::PaddedRect(const PictureMapKey& key) const { + gfx::Rect padded_rect = tiling_.TileBounds(key.first, key.second); + padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(), + -buffer_pixels()); + return padded_rect; +} + +bool PicturePileImpl::CanRasterSlowTileCheck( + const gfx::Rect& layer_rect) const { + bool include_borders = false; + for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); + tile_iter; ++tile_iter) { + PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); + if (map_iter == picture_map_.end()) + return false; + if (!map_iter->second.GetPicture()) + return false; + } + return true; +} + +void PicturePileImpl::SetShouldAttemptToUseDistanceFieldText() { + should_attempt_to_use_distance_field_text_ = true; +} + +bool PicturePileImpl::ShouldAttemptToUseDistanceFieldText() const { + return should_attempt_to_use_distance_field_text_; +} + +void PicturePileImpl::AsValueInto(base::debug::TracedValue* pictures) const { + gfx::Rect tiling_rect(tiling_.tiling_size()); + std::set<const void*> appended_pictures; + bool include_borders = true; + for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders); + tile_iter; ++tile_iter) { + PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); + if (map_iter == picture_map_.end()) + continue; + + const Picture* picture = map_iter->second.GetPicture(); + if (picture && (appended_pictures.count(picture) == 0)) { + appended_pictures.insert(picture); + TracedValue::AppendIDRef(picture, pictures); + } + } +} + +bool PicturePileImpl::IsMask() const { + return is_mask_; } PicturePileImpl::PixelRefIterator::PixelRefIterator(
diff --git a/cc/resources/picture_pile_impl.h b/cc/resources/picture_pile_impl.h index 9f350a9..6986db4 100644 --- a/cc/resources/picture_pile_impl.h +++ b/cc/resources/picture_pile_impl.h
@@ -13,7 +13,7 @@ #include "base/time/time.h" #include "cc/base/cc_export.h" #include "cc/debug/rendering_stats_instrumentation.h" -#include "cc/resources/picture_pile_base.h" +#include "cc/resources/picture_pile.h" #include "cc/resources/raster_source.h" #include "skia/ext/analysis_canvas.h" #include "skia/ext/refptr.h" @@ -21,12 +21,11 @@ namespace cc { -// TODO(vmpstr): Clean up PicturePileBase and make it a member. -class CC_EXPORT PicturePileImpl : public PicturePileBase, public RasterSource { +class CC_EXPORT PicturePileImpl : public RasterSource { public: static scoped_refptr<PicturePileImpl> Create(); - static scoped_refptr<PicturePileImpl> CreateFromOther( - const PicturePileBase* other); + static scoped_refptr<PicturePileImpl> CreateFromPicturePile( + const PicturePile* other); // RasterSource overrides. See RasterSource header for full description. // When slow-down-raster-scale-factor is set to a value greater than 1, the @@ -35,6 +34,9 @@ void PlaybackToCanvas(SkCanvas* canvas, const gfx::Rect& canvas_rect, float contents_scale) const override; + void PlaybackToSharedCanvas(SkCanvas* canvas, + const gfx::Rect& canvas_rect, + float contents_scale) const override; void PerformSolidColorAnalysis( const gfx::Rect& content_rect, float contents_scale, @@ -44,20 +46,18 @@ std::vector<SkPixelRef*>* pixel_refs) const override; bool CoversRect(const gfx::Rect& content_rect, float contents_scale) const override; - bool SuitableForDistanceFieldText() const override; - - // Raster into the canvas without applying clips. - void RasterDirect(SkCanvas* canvas, - const gfx::Rect& canvas_rect, - float contents_scale) const; + void SetShouldAttemptToUseDistanceFieldText() override; + bool ShouldAttemptToUseDistanceFieldText() const override; + gfx::Size GetSize() const override; + bool IsSolidColor() const override; + SkColor GetSolidColor() const override; + bool HasRecordings() const override; + bool IsMask() const override; // Tracing functionality. - void DidBeginTracing(); - skia::RefPtr<SkPicture> GetFlattenedPicture(); - - void set_likely_to_be_used_for_transform_animation() { - likely_to_be_used_for_transform_animation_ = true; - } + void DidBeginTracing() override; + void AsValueInto(base::debug::TracedValue* array) const override; + skia::RefPtr<SkPicture> GetFlattenedPicture() override; // Iterator used to return SkPixelRefs from this picture pile. // Public for testing. @@ -87,10 +87,31 @@ friend class PicturePile; friend class PixelRefIterator; + // TODO(vmpstr): Change this when pictures are split from invalidation info. + using PictureMapKey = PicturePile::PictureMapKey; + using PictureMap = PicturePile::PictureMap; + using PictureInfo = PicturePile::PictureInfo; + PicturePileImpl(); - explicit PicturePileImpl(const PicturePileBase* other); + explicit PicturePileImpl(const PicturePile* other); ~PicturePileImpl() override; + int buffer_pixels() const { return tiling_.border_texels(); } + + PictureMap picture_map_; + TilingData tiling_; + SkColor background_color_; + bool contents_opaque_; + bool contents_fill_bounds_completely_; + bool is_solid_color_; + SkColor solid_color_; + gfx::Rect recorded_viewport_; + bool has_any_recordings_; + bool is_mask_; + bool clear_canvas_with_debug_color_; + float min_contents_scale_; + int slow_down_raster_scale_factor_for_debug_; + private: typedef std::map<const Picture*, Region> PictureRegionMap; @@ -112,7 +133,13 @@ float contents_scale, bool is_analysis) const; - bool likely_to_be_used_for_transform_animation_; + // An internal CanRaster check that goes to the picture_map rather than + // using the recorded_viewport hint. + bool CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const; + + gfx::Rect PaddedRect(const PictureMapKey& key) const; + + bool should_attempt_to_use_distance_field_text_; DISALLOW_COPY_AND_ASSIGN(PicturePileImpl); };
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc index 24bed3e..25e0d12 100644 --- a/cc/resources/picture_pile_unittest.cc +++ b/cc/resources/picture_pile_unittest.cc
@@ -7,7 +7,7 @@ #include "cc/resources/picture_pile.h" #include "cc/test/fake_content_layer_client.h" -#include "cc/test/fake_rendering_stats_instrumentation.h" +#include "cc/test/fake_picture_pile.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" @@ -15,26 +15,6 @@ namespace cc { namespace { -class TestPicturePile : public PicturePile { - public: - ~TestPicturePile() override {} - - using PicturePile::buffer_pixels; - using PicturePile::CanRasterSlowTileCheck; - using PicturePile::Clear; - - PictureMap& picture_map() { return picture_map_; } - const gfx::Rect& recorded_viewport() const { return recorded_viewport_; } - - bool CanRasterLayerRect(const gfx::Rect& layer_rect) { - return CanRaster(1.f, layer_rect); - } - - typedef PicturePile::PictureInfo PictureInfo; - typedef PicturePile::PictureMapKey PictureMapKey; - typedef PicturePile::PictureMap PictureMap; -}; - class PicturePileTestBase { public: PicturePileTestBase() @@ -56,36 +36,29 @@ UpdateAndExpandInvalidation(&invalidation, tiling_size, viewport_rect); } - gfx::Size tiling_size() const { return pile_.tiling_size(); } - gfx::Rect tiling_rect() const { return gfx::Rect(pile_.tiling_size()); } + gfx::Size tiling_size() const { return pile_.GetSize(); } + gfx::Rect tiling_rect() const { return gfx::Rect(pile_.GetSize()); } bool UpdateAndExpandInvalidation(Region* invalidation, const gfx::Size& layer_size, const gfx::Rect& visible_layer_rect) { frame_number_++; - return pile_.UpdateAndExpandInvalidation(&client_, - invalidation, - background_color_, - contents_opaque_, - false, - layer_size, - visible_layer_rect, - frame_number_, - Picture::RECORD_NORMALLY, - &stats_instrumentation_); + return pile_.UpdateAndExpandInvalidation( + &client_, invalidation, background_color_, contents_opaque_, false, + layer_size, visible_layer_rect, frame_number_, + Picture::RECORD_NORMALLY); } bool UpdateWholePile() { Region invalidation = tiling_rect(); - bool result = UpdateAndExpandInvalidation( - &invalidation, tiling_size(), tiling_rect()); + bool result = UpdateAndExpandInvalidation(&invalidation, tiling_size(), + tiling_rect()); EXPECT_EQ(tiling_rect().ToString(), invalidation.ToString()); return result; } FakeContentLayerClient client_; - FakeRenderingStatsInstrumentation stats_instrumentation_; - TestPicturePile pile_; + FakePicturePile pile_; SkColor background_color_; float min_scale_; int frame_number_; @@ -99,7 +72,7 @@ TEST_F(PicturePileTest, InvalidationOnTileBorderOutsideInterestRect) { // Don't expand the interest rect past what we invalidate. - pile_.SetPixelRecordDistanceForTesting(0); + pile_.SetPixelRecordDistance(0); gfx::Size tile_size(100, 100); pile_.tiling().SetMaxTextureSize(tile_size); @@ -182,8 +155,8 @@ EXPECT_EQ(1, pile_.tiling().num_tiles_x()); EXPECT_EQ(1, pile_.tiling().num_tiles_y()); - TestPicturePile::PictureInfo& picture_info = - pile_.picture_map().find(TestPicturePile::PictureMapKey(0, 0))->second; + FakePicturePile::PictureInfo& picture_info = + pile_.picture_map().find(FakePicturePile::PictureMapKey(0, 0))->second; // We should have a picture. EXPECT_TRUE(!!picture_info.GetPicture()); gfx::Rect picture_rect = gfx::ScaleToEnclosedRect( @@ -204,14 +177,14 @@ EXPECT_EQ(1, pile_.tiling().num_tiles_x()); EXPECT_EQ(1, pile_.tiling().num_tiles_y()); - TestPicturePile::PictureInfo& picture_info = - pile_.picture_map().find(TestPicturePile::PictureMapKey(0, 0))->second; + FakePicturePile::PictureInfo& picture_info = + pile_.picture_map().find(FakePicturePile::PictureMapKey(0, 0))->second; EXPECT_TRUE(!!picture_info.GetPicture()); int expected_inflation = pile_.buffer_pixels(); const Picture* base_picture = picture_info.GetPicture(); - gfx::Rect base_picture_rect(pile_.tiling_size()); + gfx::Rect base_picture_rect(tiling_size()); base_picture_rect.Inset(-expected_inflation, -expected_inflation); EXPECT_EQ(base_picture_rect.ToString(), base_picture->LayerRect().ToString()); @@ -219,7 +192,7 @@ TEST_F(PicturePileTest, InvalidateOnTileBoundaryInflated) { gfx::Size new_tiling_size = - gfx::ToCeiledSize(gfx::ScaleSize(pile_.tiling_size(), 2.f)); + gfx::ToCeiledSize(gfx::ScaleSize(tiling_size(), 2.f)); // This creates initial pictures. SetTilingSize(new_tiling_size); @@ -247,20 +220,20 @@ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureInfo& picture_info = + FakePicturePile::PictureInfo& picture_info = pile_.picture_map() - .find(TestPicturePile::PictureMapKey(i, j)) + .find(FakePicturePile::PictureMapKey(i, j)) ->second; // Expect (1, 1) and (1, 0) to be invalidated once more // than the rest of the tiles. if (i == 1 && (j == 0 || j == 1)) { EXPECT_FLOAT_EQ( - 2.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, + 2.0f / FakePicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, picture_info.GetInvalidationFrequencyForTesting()); } else { EXPECT_FLOAT_EQ( - 1.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, + 1.0f / FakePicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, picture_info.GetInvalidationFrequencyForTesting()); } } @@ -273,7 +246,7 @@ // Everything was invalidated once so far. for (auto& it : pile_.picture_map()) { EXPECT_FLOAT_EQ( - 1.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, + 1.0f / FakePicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, it.second.GetInvalidationFrequencyForTesting()); } @@ -284,14 +257,14 @@ // Everything was invalidated again. for (auto& it : pile_.picture_map()) { EXPECT_FLOAT_EQ( - 2.0f / TestPicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, + 2.0f / FakePicturePile::PictureInfo::INVALIDATION_FRAMES_TRACKED, it.second.GetInvalidationFrequencyForTesting()); } } TEST_F(PicturePileTest, StopRecordingOffscreenInvalidations) { gfx::Size new_tiling_size = - gfx::ToCeiledSize(gfx::ScaleSize(pile_.tiling_size(), 4.f)); + gfx::ToCeiledSize(gfx::ScaleSize(tiling_size(), 4.f)); SetTilingSize(new_tiling_size); gfx::Rect viewport(tiling_size().width(), 1); @@ -304,9 +277,9 @@ // Make sure we have a high invalidation frequency. for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureInfo& picture_info = + FakePicturePile::PictureInfo& picture_info = pile_.picture_map() - .find(TestPicturePile::PictureMapKey(i, j)) + .find(FakePicturePile::PictureMapKey(i, j)) ->second; EXPECT_FLOAT_EQ(1.0f, picture_info.GetInvalidationFrequencyForTesting()) << "i " << i << " j " << j; @@ -320,9 +293,9 @@ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureInfo& picture_info = + FakePicturePile::PictureInfo& picture_info = pile_.picture_map() - .find(TestPicturePile::PictureMapKey(i, j)) + .find(FakePicturePile::PictureMapKey(i, j)) ->second; EXPECT_FLOAT_EQ(1.0f, picture_info.GetInvalidationFrequencyForTesting()); @@ -349,15 +322,15 @@ // Now update with no invalidation and full viewport Region empty_invalidation; - UpdateAndExpandInvalidation( - &empty_invalidation, tiling_size(), tiling_rect()); + UpdateAndExpandInvalidation(&empty_invalidation, tiling_size(), + tiling_rect()); EXPECT_EQ(Region().ToString(), empty_invalidation.ToString()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureInfo& picture_info = + FakePicturePile::PictureInfo& picture_info = pile_.picture_map() - .find(TestPicturePile::PictureMapKey(i, j)) + .find(FakePicturePile::PictureMapKey(i, j)) ->second; // Expect the invalidation frequency to be less than 1, since we just // updated with no invalidations. @@ -388,7 +361,7 @@ // tiles touching it, but is true for adjacent tiles, even if it // overlaps on borders (edge case). gfx::Size new_tiling_size = - gfx::ToCeiledSize(gfx::ScaleSize(pile_.tiling_size(), 4.f)); + gfx::ToCeiledSize(gfx::ScaleSize(tiling_size(), 4.f)); SetTilingSize(new_tiling_size); gfx::Rect tile01_borders = pile_.tiling().TileBoundsWithBorder(0, 1); @@ -422,10 +395,10 @@ // Sanity check some pictures exist and others don't. EXPECT_TRUE(pile_.picture_map() - .find(TestPicturePile::PictureMapKey(0, 1)) + .find(FakePicturePile::PictureMapKey(0, 1)) ->second.GetPicture()); EXPECT_FALSE(pile_.picture_map() - .find(TestPicturePile::PictureMapKey(0, 2)) + .find(FakePicturePile::PictureMapKey(0, 2)) ->second.GetPicture()); EXPECT_TRUE(pile_.CanRasterLayerRect(tile01_noborders)); @@ -454,8 +427,8 @@ // No invalidation, changing viewport. invalidation = Region(); - UpdateAndExpandInvalidation( - &invalidation, tiling_size(), gfx::Rect(5, 5, 5, 5)); + UpdateAndExpandInvalidation(&invalidation, tiling_size(), + gfx::Rect(5, 5, 5, 5)); EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty()); EXPECT_EQ(Region().ToString(), invalidation.ToString()); } @@ -604,9 +577,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -616,14 +589,15 @@ grow_down_tiling_size, CornerSinglePixelRect(corner, grow_down_tiling_size)); - // We should have lost the recordings in the bottom row. + // We should have lost all of the recordings in the bottom row as none of them + // are in the current interest rect (which is either the above or below it). EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 6; ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_EQ(j < 5, it != map.end() && it->second.GetPicture()); } } @@ -645,20 +619,58 @@ base_tiling_size, CornerSinglePixelRect(corner, base_tiling_size)); - // We should have lost the recordings that are now outside the tiling only. + // When shrinking, we should have lost all the recordings in the bottom row + // not touching the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_EQ(j < 6, it != map.end() && it->second.GetPicture()); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + case TOP_RIGHT: + expect_tile = j < 5; + break; + case BOTTOM_LEFT: + // The interest rect in the bottom left tile means we'll record it. + expect_tile = j < 5 || (j == 5 && i == 0); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = j < 5 || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - expected_invalidation.Clear(); + // When shrinking, the previously exposed region is invalidated. + expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), + gfx::Rect(base_tiling_size)); + // The whole bottom row of tiles (except any with the interest rect) are + // dropped. + gfx::Rect bottom_row_minus_existing_corner = gfx::UnionRects( + pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)); + switch (corner) { + case TOP_LEFT: + case TOP_RIGHT: + // No tiles are kept in the changed region because it doesn't + // intersect with the interest rect. + break; + case BOTTOM_LEFT: + bottom_row_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(0, 5)); + break; + case BOTTOM_RIGHT: + bottom_row_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(5, 5)); + break; + } + + expected_invalidation.Union(bottom_row_minus_existing_corner); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); @@ -668,14 +680,16 @@ grow_right_tiling_size, CornerSinglePixelRect(corner, grow_right_tiling_size)); - // We should have lost the recordings in the right column. + // We should have lost all of the recordings in the right column as none of + // them are in the current interest rect (which is either entirely left or + // right of it). EXPECT_EQ(8, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 6; ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_EQ(i < 5, it != map.end() && it->second.GetPicture()); } } @@ -697,20 +711,57 @@ base_tiling_size, CornerSinglePixelRect(corner, base_tiling_size)); - // We should have lost the recordings that are now outside the tiling only. + // When shrinking, we should have lost all the recordings in the right column + // not touching the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_EQ(i < 6, it != map.end() && it->second.GetPicture()); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + case BOTTOM_LEFT: + // No tiles are kept in the changed region because it doesn't + // intersect with the interest rect. + expect_tile = i < 5; + break; + case TOP_RIGHT: + // The interest rect in the top right tile means we'll record it. + expect_tile = i < 5 || (j == 0 && i == 5); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = i < 5 || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - expected_invalidation.Clear(); + // When shrinking, the previously exposed region is invalidated. + expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), + gfx::Rect(base_tiling_size)); + // The whole right column of tiles (except for ones with the interest rect) + // are dropped. + gfx::Rect right_column_minus_existing_corner = gfx::UnionRects( + pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); + switch (corner) { + case TOP_LEFT: + case BOTTOM_LEFT: + break; + case TOP_RIGHT: + right_column_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(5, 0)); + break; + case BOTTOM_RIGHT: + right_column_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(5, 5)); + break; + } + expected_invalidation.Union(right_column_minus_existing_corner); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); @@ -725,9 +776,9 @@ EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < 6; ++i) { for (int j = 0; j < 6; ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_EQ(i < 5 && j < 5, it != map.end() && it->second.GetPicture()); } } @@ -735,7 +786,7 @@ // We invalidated all new pixels in the recording. expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), gfx::Rect(base_tiling_size)); - // But the new pixels don't cover the whole right_column. + // But the new pixels don't cover the whole right column or bottom row. Region right_column_and_bottom_row = UnionRegions(gfx::UnionRects(pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)), @@ -748,22 +799,66 @@ invalidation.Clear(); UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect()); + UpdateAndExpandInvalidation(&invalidation, base_tiling_size, + CornerSinglePixelRect(corner, base_tiling_size)); - // We should have lost the recordings that are now outside the tiling only. + // We should have lost the recordings in the right column and bottom row, + // except where it intersects the interest rect. EXPECT_EQ(6, pile_.tiling().num_tiles_x()); EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_EQ(i < 6 && j < 6, it != map.end() && it->second.GetPicture()); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + expect_tile = i < 5 && j < 5; + break; + case TOP_RIGHT: + // The interest rect in the top right tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5); + break; + case BOTTOM_LEFT: + // The interest rect in the bottom left tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()) + << i << "," << j; } } - // No invalidation when shrinking. - expected_invalidation.Clear(); + // We invalidated all previous pixels in the recording. + expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), + gfx::Rect(base_tiling_size)); + // The whole right column and bottom row of tiles (except for ones with the + // interest rect) are dropped. + Region right_column_and_bottom_row_minus_existing_corner = + right_column_and_bottom_row; + switch (corner) { + case TOP_LEFT: + break; + case BOTTOM_LEFT: + right_column_and_bottom_row_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(0, 5)); + break; + case TOP_RIGHT: + right_column_and_bottom_row_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(5, 0)); + break; + case BOTTOM_RIGHT: + right_column_and_bottom_row_minus_existing_corner.Subtract( + pile_.tiling().TileBounds(5, 5)); + break; + } + expected_invalidation.Union( + right_column_and_bottom_row_minus_existing_corner); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } @@ -796,268 +891,235 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - UpdateAndExpandInvalidation( - &invalidation, - grow_down_tiling_size, - CornerSinglePixelRect(corner, grow_down_tiling_size)); + // In this test (unlike the large resize test), as all growing and shrinking + // happens within tiles, the resulting invalidation is symmetrical, so use + // this enum to repeat the test both ways. + enum ChangeDirection { GROW, SHRINK, LAST_DIRECTION = SHRINK }; - // We should have lost the recordings in the bottom row that do not intersect - // the interest rect. - EXPECT_EQ(6, pile_.tiling().num_tiles_x()); - EXPECT_EQ(6, pile_.tiling().num_tiles_y()); - for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - bool expect_tile; + // Grow downward. + for (int dir = 0; dir <= LAST_DIRECTION; ++dir) { + gfx::Size new_tiling_size = + dir == GROW ? grow_down_tiling_size : base_tiling_size; + UpdateWholePile(); + UpdateAndExpandInvalidation(&invalidation, new_tiling_size, + CornerSinglePixelRect(corner, new_tiling_size)); + + // We should have lost the recordings in the bottom row that do not + // intersect the interest rect. + EXPECT_EQ(6, pile_.tiling().num_tiles_x()); + EXPECT_EQ(6, pile_.tiling().num_tiles_y()); + for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { + for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + case TOP_RIGHT: + expect_tile = j < 5; + break; + case BOTTOM_LEFT: + // The interest rect in the bottom left tile means we'll record it. + expect_tile = j < 5 || (j == 5 && i == 0); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = j < 5 || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); + } + } + + // We invalidated the bottom row outside the new interest rect. The tile + // that insects the interest rect in invalidated only on its newly + // exposed or previously exposed pixels. + if (dir == GROW) { + // Only calculate the expected invalidation while growing, as the tile + // bounds post-growing is the newly exposed / previously exposed sizes. + // Post-shrinking, the tile bounds are smaller, so can't be used. switch (corner) { case TOP_LEFT: case TOP_RIGHT: - expect_tile = j < 5; + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5)); break; case BOTTOM_LEFT: - // The interest rect in the bottom left tile means we'll record it. - expect_tile = j < 5 || (j == 5 && i == 0); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(1, 5), pile_.tiling().TileBounds(5, 5)); + expected_invalidation.Union(SubtractRects( + pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size))); break; case BOTTOM_RIGHT: - // The interest rect in the bottom right tile means we'll record it. - expect_tile = j < 5 || (j == 5 && i == 5); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(4, 5)); + expected_invalidation.Union(SubtractRects( + pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); break; } - EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); } + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); + invalidation.Clear(); } - // We invalidated the bottom row outside the new interest rect. The tile that - // insects the interest rect in invalidated only on its new pixels. - switch (corner) { - case TOP_LEFT: - case TOP_RIGHT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(0, 5), - pile_.tiling().TileBounds(5, 5)); - break; - case BOTTOM_LEFT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(1, 5), - pile_.tiling().TileBounds(5, 5)); - expected_invalidation.Union(SubtractRects(pile_.tiling().TileBounds(0, 5), - gfx::Rect(base_tiling_size))); - break; - case BOTTOM_RIGHT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(0, 5), - pile_.tiling().TileBounds(4, 5)); - expected_invalidation.Union(SubtractRects(pile_.tiling().TileBounds(5, 5), - gfx::Rect(base_tiling_size))); - break; - } - EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); - invalidation.Clear(); + // Grow right. + for (int dir = 0; dir <= LAST_DIRECTION; ++dir) { + gfx::Size new_tiling_size = + dir == GROW ? grow_right_tiling_size : base_tiling_size; + UpdateWholePile(); + UpdateAndExpandInvalidation(&invalidation, new_tiling_size, + CornerSinglePixelRect(corner, new_tiling_size)); - UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, - base_tiling_size, - CornerSinglePixelRect(corner, base_tiling_size)); - - // We should have lost nothing. - EXPECT_EQ(6, pile_.tiling().num_tiles_x()); - EXPECT_EQ(6, pile_.tiling().num_tiles_y()); - for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_TRUE(it != map.end() && it->second.GetPicture()); + // We should have lost the recordings in the right column. + EXPECT_EQ(6, pile_.tiling().num_tiles_x()); + EXPECT_EQ(6, pile_.tiling().num_tiles_y()); + for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { + for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + case BOTTOM_LEFT: + expect_tile = i < 5; + break; + case TOP_RIGHT: + // The interest rect in the top right tile means we'll record it. + expect_tile = i < 5 || (j == 0 && i == 5); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = i < 5 || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); + } } - } - // We invalidated nothing. - expected_invalidation.Clear(); - EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); - invalidation.Clear(); - - UpdateWholePile(); - UpdateAndExpandInvalidation( - &invalidation, - grow_right_tiling_size, - CornerSinglePixelRect(corner, grow_right_tiling_size)); - - // We should have lost the recordings in the right column. - EXPECT_EQ(6, pile_.tiling().num_tiles_x()); - EXPECT_EQ(6, pile_.tiling().num_tiles_y()); - for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - bool expect_tile; + // We invalidated the right column outside the new interest rect. The tile + // that insects the interest rect in invalidated only on its new or + // previously exposed pixels. + if (dir == GROW) { + // Calculate the expected invalidation the first time through the loop. switch (corner) { case TOP_LEFT: case BOTTOM_LEFT: - expect_tile = i < 5; + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); break; case TOP_RIGHT: - // The interest rect in the top right tile means we'll record it. - expect_tile = i < 5 || (j == 0 && i == 5); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5)); + expected_invalidation.Union(SubtractRects( + pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size))); break; case BOTTOM_RIGHT: - // The interest rect in the bottom right tile means we'll record it. - expect_tile = i < 5 || (j == 5 && i == 5); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4)); + expected_invalidation.Union(SubtractRects( + pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); break; } - EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); } + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); + invalidation.Clear(); } - // We invalidated the right column outside the new interest rect. The tile - // that insects the interest rect in invalidated only on its new pixels. - switch (corner) { - case TOP_LEFT: - case BOTTOM_LEFT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 0), - pile_.tiling().TileBounds(5, 5)); - break; - case TOP_RIGHT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 1), - pile_.tiling().TileBounds(5, 5)); - expected_invalidation.Union(SubtractRects(pile_.tiling().TileBounds(5, 0), - gfx::Rect(base_tiling_size))); - break; - case BOTTOM_RIGHT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 0), - pile_.tiling().TileBounds(5, 4)); - expected_invalidation.Union(SubtractRects(pile_.tiling().TileBounds(5, 5), - gfx::Rect(base_tiling_size))); - break; - } - EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); - invalidation.Clear(); + // Grow both. + for (int dir = 0; dir <= LAST_DIRECTION; ++dir) { + gfx::Size new_tiling_size = + dir == GROW ? grow_both_tiling_size : base_tiling_size; + UpdateWholePile(); + UpdateAndExpandInvalidation(&invalidation, new_tiling_size, + CornerSinglePixelRect(corner, new_tiling_size)); - UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, - base_tiling_size, - CornerSinglePixelRect(corner, base_tiling_size)); - - // We should have lost nothing. - EXPECT_EQ(6, pile_.tiling().num_tiles_x()); - EXPECT_EQ(6, pile_.tiling().num_tiles_y()); - for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_TRUE(it != map.end() && it->second.GetPicture()); + // We should have lost the recordings in the right column and bottom row. + // The tile that insects the interest rect in invalidated only on its new + // or previously exposed pixels. + EXPECT_EQ(6, pile_.tiling().num_tiles_x()); + EXPECT_EQ(6, pile_.tiling().num_tiles_y()); + for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { + for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + expect_tile = i < 5 && j < 5; + break; + case TOP_RIGHT: + // The interest rect in the top right tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5); + break; + case BOTTOM_LEFT: + // The interest rect in the bottom left tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()) + << i << "," << j; + } } - } - // We invalidated nothing. - expected_invalidation.Clear(); - EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); - invalidation.Clear(); - - UpdateWholePile(); - UpdateAndExpandInvalidation( - &invalidation, - grow_both_tiling_size, - CornerSinglePixelRect(corner, grow_both_tiling_size)); - - // We should have lost the recordings in the right column and bottom row. The - // tile that insects the interest rect in invalidated only on its new pixels. - EXPECT_EQ(6, pile_.tiling().num_tiles_x()); - EXPECT_EQ(6, pile_.tiling().num_tiles_y()); - for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - bool expect_tile; + // We invalidated the right column and the bottom row outside the new + // interest rect. The tile that insects the interest rect in invalidated + // only on its new or previous exposed pixels. + if (dir == GROW) { + // Calculate the expected invalidation the first time through the loop. switch (corner) { case TOP_LEFT: - expect_tile = i < 5 && j < 5; + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); + expected_invalidation.Union( + gfx::UnionRects(pile_.tiling().TileBounds(0, 5), + pile_.tiling().TileBounds(5, 5))); break; case TOP_RIGHT: - // The interest rect in the top right tile means we'll record it. - expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5)); + expected_invalidation.Union( + gfx::UnionRects(pile_.tiling().TileBounds(0, 5), + pile_.tiling().TileBounds(5, 5))); + expected_invalidation.Union(SubtractRects( + pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size))); break; case BOTTOM_LEFT: - // The interest rect in the bottom left tile means we'll record it. - expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5)); + expected_invalidation.Union( + gfx::UnionRects(pile_.tiling().TileBounds(1, 5), + pile_.tiling().TileBounds(5, 5))); + expected_invalidation.Union(SubtractRects( + pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size))); break; case BOTTOM_RIGHT: - // The interest rect in the bottom right tile means we'll record it. - expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5); + expected_invalidation = gfx::UnionRects( + pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4)); + expected_invalidation.Union( + gfx::UnionRects(pile_.tiling().TileBounds(0, 5), + pile_.tiling().TileBounds(4, 5))); + expected_invalidation.Union(SubtractRegions( + pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); break; } - EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()) - << i << "," << j; } + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); + invalidation.Clear(); } - - // We invalidated the right column and the bottom row outside the new interest - // rect. The tile that insects the interest rect in invalidated only on its - // new pixels. - switch (corner) { - case TOP_LEFT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 0), - pile_.tiling().TileBounds(5, 5)); - expected_invalidation.Union(gfx::UnionRects( - pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5))); - break; - case TOP_RIGHT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 1), - pile_.tiling().TileBounds(5, 5)); - expected_invalidation.Union(gfx::UnionRects( - pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5))); - expected_invalidation.Union(SubtractRects(pile_.tiling().TileBounds(5, 0), - gfx::Rect(base_tiling_size))); - break; - case BOTTOM_LEFT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 0), - pile_.tiling().TileBounds(5, 5)); - expected_invalidation.Union(gfx::UnionRects( - pile_.tiling().TileBounds(1, 5), pile_.tiling().TileBounds(5, 5))); - expected_invalidation.Union(SubtractRects(pile_.tiling().TileBounds(0, 5), - gfx::Rect(base_tiling_size))); - break; - case BOTTOM_RIGHT: - expected_invalidation = gfx::UnionRects(pile_.tiling().TileBounds(5, 0), - pile_.tiling().TileBounds(5, 4)); - expected_invalidation.Union(gfx::UnionRects( - pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(4, 5))); - expected_invalidation.Union(SubtractRegions( - pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); - break; - } - EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); - invalidation.Clear(); - - UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, - base_tiling_size, - CornerSinglePixelRect(corner, base_tiling_size)); - - // We should have lost nothing. - EXPECT_EQ(6, pile_.tiling().num_tiles_x()); - EXPECT_EQ(6, pile_.tiling().num_tiles_y()); - for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_TRUE(it != map.end() && it->second.GetPicture()); - } - } - - // We invalidated nothing. - expected_invalidation.Clear(); - EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); - invalidation.Clear(); } INSTANTIATE_TEST_CASE_P( @@ -1086,9 +1148,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1101,9 +1163,9 @@ EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1127,15 +1189,18 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - EXPECT_EQ(Region().ToString(), invalidation.ToString()); + // We invalidated the previously exposed pixels on the bottom row of tiles. + expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), + gfx::Rect(base_tiling_size)); + EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels)); + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); @@ -1147,9 +1212,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1173,15 +1238,18 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - EXPECT_EQ(Region().ToString(), invalidation.ToString()); + // We invalidated the previously exposed pixels on the right column of tiles. + expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), + gfx::Rect(base_tiling_size)); + EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels)); + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); @@ -1193,9 +1261,9 @@ EXPECT_EQ(8, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1223,15 +1291,20 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - EXPECT_EQ(Region().ToString(), invalidation.ToString()); + // We invalidated the previously exposed pixels on the bottom row and right + // column of tiles. + expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), + gfx::Rect(base_tiling_size)); + EXPECT_TRUE( + expected_invalidation.Contains(bottom_row_and_right_column_new_pixels)); + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } @@ -1256,9 +1329,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1271,9 +1344,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1292,15 +1365,17 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - EXPECT_EQ(Region().ToString(), invalidation.ToString()); + // We invalidated the previously exposed pixels. + expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), + gfx::Rect(base_tiling_size)); + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); @@ -1312,9 +1387,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1333,15 +1408,17 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - EXPECT_EQ(Region().ToString(), invalidation.ToString()); + // We invalidated the previously exposed pixels. + expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), + gfx::Rect(base_tiling_size)); + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); @@ -1353,9 +1430,9 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } @@ -1374,15 +1451,17 @@ EXPECT_EQ(6, pile_.tiling().num_tiles_y()); for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) { for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) { - TestPicturePile::PictureMapKey key(i, j); - TestPicturePile::PictureMap& map = pile_.picture_map(); - TestPicturePile::PictureMap::iterator it = map.find(key); + FakePicturePile::PictureMapKey key(i, j); + FakePicturePile::PictureMap& map = pile_.picture_map(); + FakePicturePile::PictureMap::iterator it = map.find(key); EXPECT_TRUE(it != map.end() && it->second.GetPicture()); } } - // No invalidation when shrinking. - EXPECT_EQ(Region().ToString(), invalidation.ToString()); + // We invalidated the previously exposed pixels. + expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), + gfx::Rect(base_tiling_size)); + EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); } @@ -1458,12 +1537,12 @@ TEST_F(PicturePileTest, SetEmptyBounds) { EXPECT_TRUE(pile_.is_solid_color()); - EXPECT_FALSE(pile_.tiling_size().IsEmpty()); + EXPECT_FALSE(pile_.GetSize().IsEmpty()); EXPECT_FALSE(pile_.picture_map().empty()); EXPECT_TRUE(pile_.HasRecordings()); pile_.SetEmptyBounds(); EXPECT_FALSE(pile_.is_solid_color()); - EXPECT_TRUE(pile_.tiling_size().IsEmpty()); + EXPECT_TRUE(pile_.GetSize().IsEmpty()); EXPECT_TRUE(pile_.picture_map().empty()); EXPECT_FALSE(pile_.HasRecordings()); }
diff --git a/cc/resources/raster_source.h b/cc/resources/raster_source.h index 3e25408..f7ea271 100644 --- a/cc/resources/raster_source.h +++ b/cc/resources/raster_source.h
@@ -9,9 +9,12 @@ #include "base/memory/ref_counted.h" #include "cc/base/cc_export.h" +#include "cc/debug/traced_value.h" +#include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkColor.h" class SkCanvas; +class SkPicture; namespace cc { @@ -30,10 +33,19 @@ // assumed that contents_scale has already been applied to this canvas. // Writes the total number of pixels rasterized and the time spent // rasterizing to the stats if the respective pointer is not nullptr. + // It is assumed that the canvas passed here will only be rasterized by + // this raster source via this call. virtual void PlaybackToCanvas(SkCanvas* canvas, const gfx::Rect& canvas_rect, float contents_scale) const = 0; + // Similar to above, except that the canvas passed here can (or was already) + // rasterized into by another raster source. That is, it is not safe to clear + // the canvas or discard its underlying memory. + virtual void PlaybackToSharedCanvas(SkCanvas* canvas, + const gfx::Rect& canvas_rect, + float contents_scale) const = 0; + // Analyze to determine if the given rect at given scale is of solid color in // this raster source. virtual void PerformSolidColorAnalysis( @@ -41,6 +53,16 @@ float contents_scale, SolidColorAnalysis* analysis) const = 0; + // Returns true iff the whole raster source is of solid color. + virtual bool IsSolidColor() const = 0; + + // Returns the color of the raster source if it is solid color. The results + // are unspecified if IsSolidColor returns false. + virtual SkColor GetSolidColor() const = 0; + + // Returns the size of this raster source. + virtual gfx::Size GetSize() const = 0; + // Populate the given list with all SkPixelRefs that may overlap the given // rect at given scale. virtual void GatherPixelRefs(const gfx::Rect& content_rect, @@ -52,9 +74,24 @@ virtual bool CoversRect(const gfx::Rect& content_rect, float contents_scale) const = 0; + // Returns true if this raster source has anything to rasterize. + virtual bool HasRecordings() const = 0; + + // Informs the raster source that it should attempt to use distance field text + // during rasterization. + virtual void SetShouldAttemptToUseDistanceFieldText() = 0; + // Return true iff this raster source would benefit from using distance // field text. - virtual bool SuitableForDistanceFieldText() const = 0; + virtual bool ShouldAttemptToUseDistanceFieldText() const = 0; + + // Tracing functionality. + virtual void DidBeginTracing() = 0; + virtual void AsValueInto(base::debug::TracedValue* array) const = 0; + virtual skia::RefPtr<SkPicture> GetFlattenedPicture() = 0; + + // TODO(vmpstr): This should be a layer property. + virtual bool IsMask() const = 0; protected: friend class base::RefCountedThreadSafe<RasterSource>;
diff --git a/cc/resources/raster_worker_pool_perftest.cc b/cc/resources/raster_worker_pool_perftest.cc index 0f7a845..fdec054 100644 --- a/cc/resources/raster_worker_pool_perftest.cc +++ b/cc/resources/raster_worker_pool_perftest.cc
@@ -167,7 +167,7 @@ public: typedef std::vector<scoped_refptr<RasterTask>> RasterTaskVector; - enum NamedTaskSet { REQUIRED_FOR_ACTIVATION = 0, ALL = 1 }; + enum NamedTaskSet { ALL, REQUIRED_FOR_ACTIVATION, REQUIRED_FOR_DRAW }; RasterWorkerPoolPerfTestBase() : context_provider_(make_scoped_refptr(new PerfContextProvider)),
diff --git a/cc/resources/raster_worker_pool_unittest.cc b/cc/resources/raster_worker_pool_unittest.cc index 9aee8e7..775acda 100644 --- a/cc/resources/raster_worker_pool_unittest.cc +++ b/cc/resources/raster_worker_pool_unittest.cc
@@ -121,7 +121,7 @@ typedef std::vector<scoped_refptr<RasterTask>> RasterTaskVector; - enum NamedTaskSet { REQUIRED_FOR_ACTIVATION = 0, ALL = 1 }; + enum NamedTaskSet { ALL, REQUIRED_FOR_ACTIVATION, REQUIRED_FOR_DRAW }; RasterWorkerPoolTest() : context_provider_(TestContextProvider::Create()),
diff --git a/cc/resources/rasterizer.h b/cc/resources/rasterizer.h index 526058c..db84c22 100644 --- a/cc/resources/rasterizer.h +++ b/cc/resources/rasterizer.h
@@ -87,7 +87,11 @@ ImageDecodeTask::Vector dependencies_; }; -static const size_t kNumberOfTaskSets = 2; +// kNumberOfTaskSets must be greater or equal to the number of values in +// TileManager::NamedTaskSet. +// TODO(reveman): Use template specialization to make it easy for client code to +// check at compile time that the number of supported task sets is correct. +static const size_t kNumberOfTaskSets = 3; typedef size_t TaskSet; typedef std::bitset<kNumberOfTaskSets> TaskSetCollection;
diff --git a/cc/resources/recording_source.h b/cc/resources/recording_source.h new file mode 100644 index 0000000..cbbeb70 --- /dev/null +++ b/cc/resources/recording_source.h
@@ -0,0 +1,55 @@ +// 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 CC_RESOURCES_RECORDING_SOURCE_H_ +#define CC_RESOURCES_RECORDING_SOURCE_H_ + +#include "cc/base/cc_export.h" +#include "cc/resources/picture.h" +#include "third_party/skia/include/core/SkBBHFactory.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { +class ContentLayerClient; +class Region; +class RasterSource; + +class CC_EXPORT RecordingSource { + public: + virtual ~RecordingSource() {} + // Re-record parts of the picture that are invalid. + // Invalidations are in layer space, and will be expanded to cover everything + // that was either recorded/changed or that has no recording, leaving out only + // pieces that we had a recording for and it was not changed. + // Return true iff the pile was modified. + virtual bool UpdateAndExpandInvalidation( + ContentLayerClient* painter, + Region* invalidation, + SkColor background_color, + bool contents_opaque, + bool contents_fill_bounds_completely, + const gfx::Size& layer_size, + const gfx::Rect& visible_layer_rect, + int frame_number, + Picture::RecordingMode recording_mode) = 0; + + virtual gfx::Size GetSize() const = 0; + virtual void SetEmptyBounds() = 0; + virtual void SetMinContentsScale(float min_contents_scale) = 0; + virtual void SetTileGridSize(const gfx::Size& tile_grid_size) = 0; + virtual void SetSlowdownRasterScaleFactor(int factor) = 0; + virtual void SetIsMask(bool is_mask) = 0; + virtual bool IsSuitableForGpuRasterization() const = 0; + + virtual scoped_refptr<RasterSource> CreateRasterSource() const = 0; + + // TODO(hendrikw): Figure out how to remove this. + virtual void SetUnsuitableForGpuRasterizationForTesting() = 0; + virtual SkTileGridFactory::TileGridInfo GetTileGridInfoForTesting() const = 0; +}; +} + +#endif // CC_RESOURCES_RECORDING_SOURCE_H_
diff --git a/cc/resources/skpicture_content_layer_updater.cc b/cc/resources/skpicture_content_layer_updater.cc index bd524f2..3e6828f 100644 --- a/cc/resources/skpicture_content_layer_updater.cc +++ b/cc/resources/skpicture_content_layer_updater.cc
@@ -16,9 +16,9 @@ SkPictureContentLayerUpdater::SkPictureContentLayerUpdater( scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumentation, int layer_id) - : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {} + : ContentLayerUpdater(painter.Pass(), layer_id) { +} SkPictureContentLayerUpdater::~SkPictureContentLayerUpdater() {} @@ -33,17 +33,11 @@ recorder.beginRecording(paint_rect.width(), paint_rect.height(), NULL, 0); DCHECK_EQ(paint_rect.width(), canvas->getBaseLayerSize().width()); DCHECK_EQ(paint_rect.height(), canvas->getBaseLayerSize().height()); - base::TimeTicks start_time = - rendering_stats_instrumentation_->StartRecording(); PaintContents(canvas, content_size, paint_rect, contents_width_scale, contents_height_scale); - base::TimeDelta duration = - rendering_stats_instrumentation_->EndRecording(start_time); - rendering_stats_instrumentation_->AddRecord( - duration, paint_rect.width() * paint_rect.height()); picture_ = skia::AdoptRef(recorder.endRecording()); }
diff --git a/cc/resources/skpicture_content_layer_updater.h b/cc/resources/skpicture_content_layer_updater.h index ec49aae..33480a8 100644 --- a/cc/resources/skpicture_content_layer_updater.h +++ b/cc/resources/skpicture_content_layer_updater.h
@@ -21,7 +21,6 @@ protected: SkPictureContentLayerUpdater( scoped_ptr<LayerPainter> painter, - RenderingStatsInstrumentation* stats_instrumentation, int layer_id); ~SkPictureContentLayerUpdater() override;
diff --git a/cc/resources/tile.cc b/cc/resources/tile.cc index d6bd3c4..4cbc758 100644 --- a/cc/resources/tile.cc +++ b/cc/resources/tile.cc
@@ -36,6 +36,7 @@ tiling_i_index_(-1), tiling_j_index_(-1), required_for_activation_(false), + required_for_draw_(false), id_(s_next_id_++) { set_raster_source(raster_source); for (int i = 0; i < NUM_TREES; i++)
diff --git a/cc/resources/tile.h b/cc/resources/tile.h index c641367..042d5ae 100644 --- a/cc/resources/tile.h +++ b/cc/resources/tile.h
@@ -85,6 +85,10 @@ void set_required_for_activation(bool is_required) { required_for_activation_ = is_required; } + bool required_for_draw() const { return required_for_draw_; } + void set_required_for_draw(bool is_required) { + required_for_draw_ = is_required; + } bool use_picture_analysis() const { return !!(flags_ & USE_PICTURE_ANALYSIS); @@ -174,6 +178,7 @@ int tiling_i_index_; int tiling_j_index_; bool required_for_activation_; + bool required_for_draw_; Id id_; static Id s_next_id_;
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc index 8b6a72a..1c7d4ca 100644 --- a/cc/resources/tile_manager.cc +++ b/cc/resources/tile_manager.cc
@@ -175,6 +175,20 @@ DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); }; +const char* TaskSetName(TaskSet task_set) { + switch (task_set) { + case TileManager::ALL: + return "ALL"; + case TileManager::REQUIRED_FOR_ACTIVATION: + return "REQUIRED_FOR_ACTIVATION"; + case TileManager::REQUIRED_FOR_DRAW: + return "REQUIRED_FOR_DRAW"; + } + + NOTREACHED(); + return "Invalid TaskSet"; +} + } // namespace RasterTaskCompletionStats::RasterTaskCompletionStats() @@ -225,7 +239,10 @@ ready_to_activate_check_notifier_( task_runner_.get(), base::Bind(&TileManager::CheckIfReadyToActivate, - base::Unretained(this))) { + base::Unretained(this))), + ready_to_draw_check_notifier_(task_runner_.get(), + base::Bind(&TileManager::CheckIfReadyToDraw, + base::Unretained(this))) { rasterizer_->SetClient(this); } @@ -295,85 +312,91 @@ } void TileManager::DidFinishRunningTasks(TaskSet task_set) { - if (task_set == ALL) { - TRACE_EVENT1("cc", "TileManager::DidFinishRunningTasks", "task_set", "ALL"); + TRACE_EVENT1("cc", "TileManager::DidFinishRunningTasks", "task_set", + TaskSetName(task_set)); - bool memory_usage_above_limit = resource_pool_->total_memory_usage_bytes() > - global_state_.soft_memory_limit_in_bytes; + switch (task_set) { + case ALL: { + bool memory_usage_above_limit = + resource_pool_->total_memory_usage_bytes() > + global_state_.soft_memory_limit_in_bytes; - // When OOM, keep re-assigning memory until we reach a steady state - // where top-priority tiles are initialized. - if (all_tiles_that_need_to_be_rasterized_are_scheduled_ && - !memory_usage_above_limit) - return; + // When OOM, keep re-assigning memory until we reach a steady state + // where top-priority tiles are initialized. + if (all_tiles_that_need_to_be_rasterized_are_scheduled_ && + !memory_usage_above_limit) + return; - rasterizer_->CheckForCompletedTasks(); - did_check_for_completed_tasks_since_last_schedule_tasks_ = true; + rasterizer_->CheckForCompletedTasks(); + did_check_for_completed_tasks_since_last_schedule_tasks_ = true; - TileVector tiles_that_need_to_be_rasterized; - AssignGpuMemoryToTiles(&tiles_that_need_to_be_rasterized); + TileVector tiles_that_need_to_be_rasterized; + AssignGpuMemoryToTiles(&tiles_that_need_to_be_rasterized); - // |tiles_that_need_to_be_rasterized| will be empty when we reach a - // steady memory state. Keep scheduling tasks until we reach this state. - if (!tiles_that_need_to_be_rasterized.empty()) { - ScheduleTasks(tiles_that_need_to_be_rasterized); - return; - } - - FreeResourcesForReleasedTiles(); - - resource_pool_->ReduceResourceUsage(); - - // We don't reserve memory for required-for-activation tiles during - // accelerated gestures, so we just postpone activation when we don't - // have these tiles, and activate after the accelerated gesture. - // Likewise if we don't allow any tiles (as is the case when we're - // invisible), if we have tiles that aren't ready, then we shouldn't - // activate as activation can cause checkerboards. - bool allow_rasterize_on_demand = - global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY && - global_state_.memory_limit_policy != ALLOW_NOTHING; - - // Use on-demand raster for any required-for-activation tiles that have not - // been been assigned memory after reaching a steady memory state. This - // ensures that we activate even when OOM. Note that we have to rebuilt the - // queue in case the last AssignGpuMemoryToTiles evicted some tiles that - // would otherwise not be picked up by the old raster queue. - client_->BuildRasterQueue(&raster_priority_queue_, - global_state_.tree_priority); - bool ready_to_activate = true; - while (!raster_priority_queue_.IsEmpty()) { - Tile* tile = raster_priority_queue_.Top(); - ManagedTileState& mts = tile->managed_state(); - - if (tile->required_for_activation() && !mts.draw_info.IsReadyToDraw()) { - // If we can't raster on demand, give up early (and don't activate). - if (!allow_rasterize_on_demand) { - ready_to_activate = false; - break; - } - - mts.draw_info.set_rasterize_on_demand(); - client_->NotifyTileStateChanged(tile); + // |tiles_that_need_to_be_rasterized| will be empty when we reach a + // steady memory state. Keep scheduling tasks until we reach this state. + if (!tiles_that_need_to_be_rasterized.empty()) { + ScheduleTasks(tiles_that_need_to_be_rasterized); + return; } - raster_priority_queue_.Pop(); - } - if (ready_to_activate) { - DCHECK(IsReadyToActivate()); + FreeResourcesForReleasedTiles(); + + resource_pool_->ReduceResourceUsage(); + + // We don't reserve memory for required-for-activation tiles during + // accelerated gestures, so we just postpone activation when we don't + // have these tiles, and activate after the accelerated gesture. + // Likewise if we don't allow any tiles (as is the case when we're + // invisible), if we have tiles that aren't ready, then we shouldn't + // activate as activation can cause checkerboards. + bool allow_rasterize_on_demand = + global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY && + global_state_.memory_limit_policy != ALLOW_NOTHING; + + // Use on-demand raster for any required-for-activation tiles that have + // not + // been been assigned memory after reaching a steady memory state. This + // ensures that we activate even when OOM. Note that we have to rebuilt + // the + // queue in case the last AssignGpuMemoryToTiles evicted some tiles that + // would otherwise not be picked up by the old raster queue. + client_->BuildRasterQueue(&raster_priority_queue_, + global_state_.tree_priority); + bool ready_to_activate = true; + while (!raster_priority_queue_.IsEmpty()) { + Tile* tile = raster_priority_queue_.Top(); + ManagedTileState& mts = tile->managed_state(); + + if (tile->required_for_activation() && !mts.draw_info.IsReadyToDraw()) { + // If we can't raster on demand, give up early (and don't activate). + if (!allow_rasterize_on_demand) { + ready_to_activate = false; + break; + } + + mts.draw_info.set_rasterize_on_demand(); + client_->NotifyTileStateChanged(tile); + } + raster_priority_queue_.Pop(); + } + + if (ready_to_activate) { + DCHECK(IsReadyToActivate()); + ready_to_activate_check_notifier_.Schedule(); + } + raster_priority_queue_.Reset(); + return; + } + case REQUIRED_FOR_ACTIVATION: ready_to_activate_check_notifier_.Schedule(); - } - raster_priority_queue_.Reset(); - return; + return; + case REQUIRED_FOR_DRAW: + ready_to_draw_check_notifier_.Schedule(); + return; } - if (task_set == REQUIRED_FOR_ACTIVATION) { - TRACE_EVENT1("cc", - "TileManager::DidFinishRunningTasks", - "task_set", - "REQUIRED_FOR_ACTIVATION"); - ready_to_activate_check_notifier_.Schedule(); - } + NOTREACHED(); } void TileManager::ManageTiles(const GlobalStateThatImpactsTilePriority& state) { @@ -671,6 +694,8 @@ TaskSetCollection task_sets; if (tile->required_for_activation()) task_sets.set(REQUIRED_FOR_ACTIVATION); + if (tile->required_for_draw()) + task_sets.set(REQUIRED_FOR_DRAW); task_sets.set(ALL); raster_queue_.items.push_back( RasterTaskQueue::Item(mts.raster_task.get(), task_sets)); @@ -840,10 +865,19 @@ TRACE_EVENT0("cc", "TileManager::IsReadyToActivate"); const std::vector<PictureLayerImpl*>& layers = client_->GetPictureLayers(); - for (std::vector<PictureLayerImpl*>::const_iterator it = layers.begin(); - it != layers.end(); - ++it) { - if (!(*it)->AllTilesRequiredForActivationAreReadyToDraw()) + for (const auto& layer : layers) { + if (!layer->AllTilesRequiredForActivationAreReadyToDraw()) + return false; + } + + return true; +} + +bool TileManager::IsReadyToDraw() const { + const std::vector<PictureLayerImpl*>& layers = client_->GetPictureLayers(); + + for (const auto& layer : layers) { + if (!layer->AllTilesRequiredForDrawAreReadyToDraw()) return false; } @@ -860,6 +894,16 @@ client_->NotifyReadyToActivate(); } +void TileManager::CheckIfReadyToDraw() { + TRACE_EVENT0("cc", "TileManager::CheckIfReadyToDraw"); + + rasterizer_->CheckForCompletedTasks(); + did_check_for_completed_tasks_since_last_schedule_tasks_ = true; + + if (IsReadyToDraw()) + client_->NotifyReadyToDraw(); +} + TileManager::MemoryUsage::MemoryUsage() : memory_bytes_(0), resource_count_(0) { }
diff --git a/cc/resources/tile_manager.h b/cc/resources/tile_manager.h index f5bea38..92821e0 100644 --- a/cc/resources/tile_manager.h +++ b/cc/resources/tile_manager.h
@@ -47,6 +47,9 @@ // Called when all tiles marked as required for activation are ready to draw. virtual void NotifyReadyToActivate() = 0; + // Called when all tiles marked as required for draw are ready to draw. + virtual void NotifyReadyToDraw() = 0; + // Called when the visible representation of a tile might have changed. Some // examples are: // - Tile version initialized. @@ -87,8 +90,9 @@ public RefCountedManager<Tile> { public: enum NamedTaskSet { - REQUIRED_FOR_ACTIVATION = 0, - ALL = 1, + ALL, + REQUIRED_FOR_ACTIVATION, + REQUIRED_FOR_DRAW // Adding additional values requires increasing kNumberOfTaskSets in // rasterizer.h }; @@ -231,7 +235,9 @@ MemoryUsage* usage); bool TilePriorityViolatesMemoryPolicy(const TilePriority& priority); bool IsReadyToActivate() const; + bool IsReadyToDraw() const; void CheckIfReadyToActivate(); + void CheckIfReadyToDraw(); TileManagerClient* client_; scoped_refptr<base::SequencedTaskRunner> task_runner_; @@ -272,6 +278,7 @@ std::vector<scoped_refptr<RasterTask>> orphan_raster_tasks_; UniqueNotifier ready_to_activate_check_notifier_; + UniqueNotifier ready_to_draw_check_notifier_; RasterTilePriorityQueue raster_priority_queue_; EvictionTilePriorityQueue eviction_priority_queue_;
diff --git a/cc/resources/tile_manager_perftest.cc b/cc/resources/tile_manager_perftest.cc index 43c1031..7262926 100644 --- a/cc/resources/tile_manager_perftest.cc +++ b/cc/resources/tile_manager_perftest.cc
@@ -159,7 +159,7 @@ pending_tree->DetachLayerTree(); scoped_ptr<FakePictureLayerImpl> pending_layer = - FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pile); + FakePictureLayerImpl::CreateWithRasterSource(pending_tree, id_, pile); pending_layer->SetDrawsContent(true); pending_tree->SetRootLayer(pending_layer.Pass()); @@ -361,8 +361,8 @@ FakePicturePileImpl::CreateFilledPile(kDefaultTileSize, layer_bounds); while (static_cast<int>(layers.size()) < layer_count) { scoped_ptr<FakePictureLayerImpl> layer = - FakePictureLayerImpl::CreateWithPile(host_impl_.pending_tree(), - next_id, pile); + FakePictureLayerImpl::CreateWithRasterSource( + host_impl_.pending_tree(), next_id, pile); layer->SetBounds(layer_bounds); layers.push_back(layer.get()); pending_root_layer_->AddChild(layer.Pass());
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc index 2f0735c..a253672 100644 --- a/cc/resources/tile_manager_unittest.cc +++ b/cc/resources/tile_manager_unittest.cc
@@ -109,14 +109,14 @@ if (old_pending_root) { pending_layer.reset( static_cast<FakePictureLayerImpl*>(old_pending_root.release())); - pending_layer->SetPile(pile); + pending_layer->SetRasterSource(pile); } else { pending_layer = - FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pile); + FakePictureLayerImpl::CreateWithRasterSource(pending_tree, id_, pile); pending_layer->SetDrawsContent(true); } // The bounds() just mirror the pile size. - pending_layer->SetBounds(pending_layer->pile()->tiling_size()); + pending_layer->SetBounds(pending_layer->raster_source()->GetSize()); pending_tree->SetRootLayer(pending_layer.Pass()); pending_layer_ = static_cast<FakePictureLayerImpl*>( @@ -193,9 +193,9 @@ // Invalidate the pending tree. pending_layer_->set_invalidation(invalidation); - pending_layer_->HighResTiling()->UpdateTilesToCurrentPile( + pending_layer_->HighResTiling()->UpdateTilesToCurrentRasterSource( invalidation, gfx::Size(1000, 1000)); - pending_layer_->LowResTiling()->UpdateTilesToCurrentPile( + pending_layer_->LowResTiling()->UpdateTilesToCurrentRasterSource( invalidation, gfx::Size(1000, 1000)); active_layer_->ResetAllTilesPriorities(); @@ -344,8 +344,8 @@ scoped_refptr<FakePicturePileImpl> pending_pile = FakePicturePileImpl::CreateFilledPile(tile_size, gfx::Size(1000, 1000)); scoped_ptr<FakePictureLayerImpl> pending_child = - FakePictureLayerImpl::CreateWithPile( - host_impl_.pending_tree(), id_ + 1, pending_pile); + FakePictureLayerImpl::CreateWithRasterSource(host_impl_.pending_tree(), + id_ + 1, pending_pile); pending_layer_->AddChild(pending_child.Pass()); FakePictureLayerImpl* pending_child_raw = static_cast<FakePictureLayerImpl*>( host_impl_.pending_tree()->LayerById(id_ + 1)); @@ -441,9 +441,9 @@ // Invalidate the pending tree. pending_layer_->set_invalidation(invalidation); - pending_layer_->HighResTiling()->UpdateTilesToCurrentPile( + pending_layer_->HighResTiling()->UpdateTilesToCurrentRasterSource( invalidation, gfx::Size(1000, 1000)); - pending_layer_->LowResTiling()->UpdateTilesToCurrentPile( + pending_layer_->LowResTiling()->UpdateTilesToCurrentRasterSource( invalidation, gfx::Size(1000, 1000)); active_layer_->ResetAllTilesPriorities(); @@ -566,8 +566,8 @@ pending_layer_->CreateDefaultTilingsAndTiles(); scoped_ptr<FakePictureLayerImpl> pending_child = - FakePictureLayerImpl::CreateWithPile( - host_impl_.pending_tree(), 2, pending_pile); + FakePictureLayerImpl::CreateWithRasterSource(host_impl_.pending_tree(), 2, + pending_pile); pending_layer_->AddChild(pending_child.Pass()); FakePictureLayerImpl* pending_child_layer = @@ -669,6 +669,104 @@ EXPECT_EQ(expected_occluded_count, occluded_count); } +TEST_F(TileManagerTilePriorityQueueTest, + EvictionTilePriorityQueueWithTransparentLayer) { + gfx::Size tile_size(102, 102); + gfx::Size layer_bounds(1000, 1000); + + scoped_refptr<FakePicturePileImpl> pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + SetupPendingTree(pending_pile); + pending_layer_->CreateDefaultTilingsAndTiles(); + + scoped_ptr<FakePictureLayerImpl> pending_child = + FakePictureLayerImpl::CreateWithRasterSource(host_impl_.pending_tree(), 2, + pending_pile); + pending_layer_->AddChild(pending_child.Pass()); + + // Create a fully transparent child layer so that its tile priorities are not + // considered to be valid. + FakePictureLayerImpl* pending_child_layer = + static_cast<FakePictureLayerImpl*>(pending_layer_->children()[0]); + pending_child_layer->SetDrawsContent(true); + pending_child_layer->CreateDefaultTilingsAndTiles(); + pending_child_layer->SetOpacity(0.0); + pending_child_layer->layer_tree_impl()->UpdateDrawProperties(); + pending_child_layer->DoPostCommitInitializationIfNeeded(); + + // Renew all of the tile priorities. + gfx::Rect viewport(layer_bounds); + pending_layer_->HighResTiling()->ComputeTilePriorityRects( + PENDING_TREE, viewport, 1.0f, 1.0, Occlusion()); + pending_layer_->LowResTiling()->ComputeTilePriorityRects( + PENDING_TREE, viewport, 1.0f, 1.0, Occlusion()); + pending_child_layer->HighResTiling()->ComputeTilePriorityRects( + PENDING_TREE, viewport, 1.0f, 1.0, Occlusion()); + pending_child_layer->LowResTiling()->ComputeTilePriorityRects( + PENDING_TREE, viewport, 1.0f, 1.0, Occlusion()); + + // Populate all tiles directly from the tilings. + std::set<Tile*> all_pending_tiles; + std::vector<Tile*> pending_high_res_tiles = + pending_layer_->HighResTiling()->AllTilesForTesting(); + all_pending_tiles.insert(pending_high_res_tiles.begin(), + pending_high_res_tiles.end()); + EXPECT_EQ(16u, pending_high_res_tiles.size()); + + std::vector<Tile*> pending_low_res_tiles = + pending_layer_->LowResTiling()->AllTilesForTesting(); + all_pending_tiles.insert(pending_low_res_tiles.begin(), + pending_low_res_tiles.end()); + EXPECT_EQ(1u, pending_low_res_tiles.size()); + + std::set<Tile*> all_pending_child_tiles; + std::vector<Tile*> pending_child_high_res_tiles = + pending_child_layer->HighResTiling()->AllTilesForTesting(); + all_pending_child_tiles.insert(pending_child_high_res_tiles.begin(), + pending_child_high_res_tiles.end()); + EXPECT_EQ(16u, pending_child_high_res_tiles.size()); + + std::vector<Tile*> pending_child_low_res_tiles = + pending_child_layer->LowResTiling()->AllTilesForTesting(); + all_pending_child_tiles.insert(pending_child_low_res_tiles.begin(), + pending_child_low_res_tiles.end()); + EXPECT_EQ(1u, pending_child_low_res_tiles.size()); + + std::set<Tile*> all_tiles = all_pending_tiles; + all_tiles.insert(all_pending_child_tiles.begin(), + all_pending_child_tiles.end()); + + tile_manager()->InitializeTilesWithResourcesForTesting( + std::vector<Tile*>(all_tiles.begin(), all_tiles.end())); + + EXPECT_TRUE(pending_layer_->HasValidTilePriorities()); + EXPECT_FALSE(pending_child_layer->HasValidTilePriorities()); + + // Verify that eviction queue returns tiles also from layers without valid + // tile priorities and that the tile priority bin of those tiles is (at most) + // EVENTUALLY. + TreePriority tree_priority = NEW_CONTENT_TAKES_PRIORITY; + std::set<Tile*> new_content_tiles; + size_t tile_count = 0; + EvictionTilePriorityQueue queue; + host_impl_.BuildEvictionQueue(&queue, tree_priority); + while (!queue.IsEmpty()) { + Tile* tile = queue.Top(); + const TilePriority& pending_priority = tile->priority(PENDING_TREE); + EXPECT_NE(std::numeric_limits<float>::infinity(), + pending_priority.distance_to_visible); + if (all_pending_child_tiles.find(tile) != all_pending_child_tiles.end()) + EXPECT_EQ(TilePriority::EVENTUALLY, pending_priority.priority_bin); + else + EXPECT_EQ(TilePriority::NOW, pending_priority.priority_bin); + new_content_tiles.insert(tile); + ++tile_count; + queue.Pop(); + } + EXPECT_EQ(tile_count, new_content_tiles.size()); + EXPECT_EQ(all_tiles, new_content_tiles); +} + TEST_F(TileManagerTilePriorityQueueTest, RasterTilePriorityQueueEmptyLayers) { SetupDefaultTrees(gfx::Size(1000, 1000));
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc index 4a9f184..dd6cdbe 100644 --- a/cc/scheduler/begin_frame_source.cc +++ b/cc/scheduler/begin_frame_source.cc
@@ -79,9 +79,9 @@ "new state", needs_begin_frames); if (needs_begin_frames_ != needs_begin_frames) { + needs_begin_frames_ = needs_begin_frames; OnNeedsBeginFramesChange(needs_begin_frames); } - needs_begin_frames_ = needs_begin_frames; } void BeginFrameSourceMixIn::AddObserver(BeginFrameObserver* obs) {
diff --git a/cc/scheduler/begin_frame_source.h b/cc/scheduler/begin_frame_source.h index 3d849a4..28359d3 100644 --- a/cc/scheduler/begin_frame_source.h +++ b/cc/scheduler/begin_frame_source.h
@@ -117,6 +117,9 @@ virtual void AddObserver(BeginFrameObserver* obs) = 0; virtual void RemoveObserver(BeginFrameObserver* obs) = 0; + // Tells the Source that client is ready to handle BeginFrames messages. + virtual void SetClientReady() = 0; + // Tracing support - Recommend (but not required) to call this implementation // in any override. virtual void AsValueInto(base::debug::TracedValue* dict) const = 0; @@ -137,8 +140,9 @@ bool NeedsBeginFrames() const override; void SetNeedsBeginFrames(bool needs_begin_frames) override; void DidFinishFrame(size_t remaining_frames) override {} - void AddObserver(BeginFrameObserver* obs) override; - void RemoveObserver(BeginFrameObserver* obs) override; + void AddObserver(BeginFrameObserver* obs) final; + void RemoveObserver(BeginFrameObserver* obs) final; + void SetClientReady() override {} // Tracing support - Recommend (but not required) to call this implementation // in any override.
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 88c27c1..5c515b2 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc
@@ -5,6 +5,7 @@ #include "cc/scheduler/scheduler.h" #include <algorithm> + #include "base/auto_reset.h" #include "base/debug/trace_event.h" #include "base/debug/trace_event_argument.h" @@ -28,12 +29,14 @@ scheduler->primary_frame_source_internal_ = BackToBackBeginFrameSource::Create(scheduler->task_runner_.get()); return scheduler->primary_frame_source_internal_.get(); - } else if (scheduler->settings_.begin_frame_scheduling_enabled) { + } else if (scheduler->settings_.use_external_begin_frame_source) { TRACE_EVENT1("cc", "Scheduler::Scheduler()", "PrimaryFrameSource", - "SchedulerClient"); - return scheduler->client_->ExternalBeginFrameSource(); + "ExternalBeginFrameSource"); + DCHECK(scheduler->primary_frame_source_internal_) + << "Need external BeginFrameSource"; + return scheduler->primary_frame_source_internal_.get(); } else { TRACE_EVENT1("cc", "Scheduler::Scheduler()", @@ -62,9 +65,9 @@ "SyntheticBeginFrameSource"); DCHECK(!(scheduler->background_frame_source_internal_)); scheduler->background_frame_source_internal_ = - SyntheticBeginFrameSource::Create(scheduler->task_runner_.get(), - scheduler->Now(), - base::TimeDelta::FromSeconds(1)); + SyntheticBeginFrameSource::Create( + scheduler->task_runner_.get(), scheduler->Now(), + scheduler->settings_.background_frame_interval); return scheduler->background_frame_source_internal_.get(); } @@ -74,11 +77,12 @@ int layer_tree_host_id, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, base::PowerMonitor* power_monitor, + scoped_ptr<BeginFrameSource> external_begin_frame_source, SchedulerFrameSourcesConstructor* frame_sources_constructor) : frame_source_(), primary_frame_source_(NULL), background_frame_source_(NULL), - primary_frame_source_internal_(), + primary_frame_source_internal_(external_begin_frame_source.Pass()), background_frame_source_internal_(), vsync_observer_(NULL), settings_(scheduler_settings), @@ -114,6 +118,7 @@ primary_frame_source_ = frame_sources_constructor->ConstructPrimaryFrameSource(this); frame_source_->AddSource(primary_frame_source_); + primary_frame_source_->SetClientReady(); // Background ticking frame source background_frame_source_ = @@ -125,6 +130,8 @@ Scheduler::~Scheduler() { TeardownPowerMonitoring(); + if (frame_source_->NeedsBeginFrames()) + frame_source_->SetNeedsBeginFrames(false); } base::TimeTicks Scheduler::Now() const { @@ -197,6 +204,11 @@ ProcessScheduledActions(); } +void Scheduler::NotifyReadyToDraw() { + // Empty for now, until we take action based on the notification as part of + // crbugs 352894, 383157, 421923. +} + void Scheduler::SetNeedsCommit() { state_machine_.SetNeedsCommit(); ProcessScheduledActions();
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index f470d17..523c246 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h
@@ -34,7 +34,6 @@ class SchedulerClient { public: - virtual BeginFrameSource* ExternalBeginFrameSource() = 0; virtual void WillBeginImplFrame(const BeginFrameArgs& args) = 0; virtual void ScheduledActionSendBeginMainFrame() = 0; virtual DrawResult ScheduledActionDrawAndSwapIfPossible() = 0; @@ -81,13 +80,15 @@ const SchedulerSettings& scheduler_settings, int layer_tree_host_id, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - base::PowerMonitor* power_monitor) { + base::PowerMonitor* power_monitor, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { SchedulerFrameSourcesConstructor frame_sources_constructor; return make_scoped_ptr(new Scheduler(client, scheduler_settings, layer_tree_host_id, task_runner, power_monitor, + external_begin_frame_source.Pass(), &frame_sources_constructor)); } @@ -107,6 +108,7 @@ void SetVisible(bool visible); void SetCanDraw(bool can_draw); void NotifyReadyToActivate(); + void NotifyReadyToDraw(); void SetNeedsCommit(); @@ -173,6 +175,7 @@ int layer_tree_host_id, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, base::PowerMonitor* power_monitor, + scoped_ptr<BeginFrameSource> external_begin_frame_source, SchedulerFrameSourcesConstructor* frame_sources_constructor); // virtual for testing - Don't call these in the constructor or
diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc index ab9add0..84f0fcd 100644 --- a/cc/scheduler/scheduler_settings.cc +++ b/cc/scheduler/scheduler_settings.cc
@@ -10,18 +10,22 @@ namespace cc { SchedulerSettings::SchedulerSettings() - : begin_frame_scheduling_enabled(true), + : use_external_begin_frame_source(false), + forward_begin_frames_to_children(false), main_frame_before_activation_enabled(false), impl_side_painting(false), timeout_and_draw_when_animation_checkerboards(true), maximum_number_of_failed_draws_before_draw_is_forced_(3), using_synchronous_renderer_compositor(false), throttle_frame_production(true), - disable_hi_res_timer_tasks_on_battery(false) { + disable_hi_res_timer_tasks_on_battery(false), + background_frame_interval(base::TimeDelta::FromSeconds(1)) { } SchedulerSettings::SchedulerSettings(const LayerTreeSettings& settings) - : begin_frame_scheduling_enabled(settings.begin_frame_scheduling_enabled), + : use_external_begin_frame_source(settings.use_external_begin_frame_source), + forward_begin_frames_to_children( + settings.forward_begin_frames_to_children), main_frame_before_activation_enabled( settings.main_frame_before_activation_enabled), impl_side_painting(settings.impl_side_painting), @@ -33,7 +37,9 @@ settings.using_synchronous_renderer_compositor), throttle_frame_production(settings.throttle_frame_production), disable_hi_res_timer_tasks_on_battery( - settings.disable_hi_res_timer_tasks_on_battery) { + settings.disable_hi_res_timer_tasks_on_battery), + background_frame_interval(base::TimeDelta::FromSecondsD( + 1.0 / settings.background_animation_rate)) { } SchedulerSettings::~SchedulerSettings() {} @@ -42,8 +48,10 @@ SchedulerSettings::AsValue() const { scoped_refptr<base::debug::TracedValue> state = new base::debug::TracedValue(); - state->SetBoolean("begin_frame_scheduling_enabled", - begin_frame_scheduling_enabled); + state->SetBoolean("use_external_begin_frame_source", + use_external_begin_frame_source); + state->SetBoolean("forward_begin_frames_to_children", + forward_begin_frames_to_children); state->SetBoolean("main_frame_before_activation_enabled", main_frame_before_activation_enabled); state->SetBoolean("impl_side_painting", impl_side_painting); @@ -56,6 +64,8 @@ state->SetBoolean("throttle_frame_production", throttle_frame_production); state->SetBoolean("disable_hi_res_timer_tasks_on_battery", disable_hi_res_timer_tasks_on_battery); + state->SetInteger("background_frame_interval", + background_frame_interval.InMicroseconds()); return state; }
diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h index 8607991..e3fd425 100644 --- a/cc/scheduler/scheduler_settings.h +++ b/cc/scheduler/scheduler_settings.h
@@ -6,6 +6,7 @@ #define CC_SCHEDULER_SCHEDULER_SETTINGS_H_ #include "base/memory/ref_counted.h" +#include "base/time/time.h" #include "base/values.h" #include "cc/base/cc_export.h" @@ -24,7 +25,8 @@ explicit SchedulerSettings(const LayerTreeSettings& settings); ~SchedulerSettings(); - bool begin_frame_scheduling_enabled; + bool use_external_begin_frame_source; + bool forward_begin_frames_to_children; bool main_frame_before_activation_enabled; bool impl_side_painting; bool timeout_and_draw_when_animation_checkerboards; @@ -33,6 +35,8 @@ bool throttle_frame_production; bool disable_hi_res_timer_tasks_on_battery; + base::TimeDelta background_frame_interval; + scoped_refptr<base::debug::ConvertableToTraceFormat> AsValue() const; };
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 2b8210d..94016f3 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc
@@ -423,9 +423,6 @@ } bool SchedulerStateMachine::ShouldAnimate() const { - if (!can_draw_) - return false; - // If a commit occurred after our last call, we need to do animation again. if (HasAnimatedThisFrame() && !did_commit_after_animating_) return false; @@ -765,29 +762,18 @@ if (!HasInitializedOutputSurface()) return false; - // If we can't draw, don't tick until we are notified that we can draw again. - if (!can_draw_) - return false; - // The forced draw respects our normal draw scheduling, so we need to // request a BeginImplFrame for it. if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) return true; - // There's no need to produce frames if we are not visible. - if (!visible_) - return false; - // We need to draw a more complete frame than we did the last BeginImplFrame, // so request another BeginImplFrame in anticipation that we will have // additional visible tiles. if (swap_used_incomplete_tile_) return true; - if (needs_animate_) - return true; - - return needs_redraw_; + return needs_animate_ || needs_redraw_; } // These are cases where we are very likely to draw soon, but might not
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index e3530c8..85160ed 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -703,6 +703,7 @@ state.SetVisible(true); state.SetCanDraw(false); state.OnBeginImplFrame(CreateBeginFrameArgsForTesting()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); @@ -711,6 +712,7 @@ state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); }
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 7da6267..91900a2 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc
@@ -1,6 +1,7 @@ // Copyright 2011 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 "cc/scheduler/scheduler.h" #include <string> @@ -47,13 +48,11 @@ class FakeSchedulerClient : public SchedulerClient { public: - struct FakeBeginFrameSourceForFakeSchedulerClient - : public FakeBeginFrameSource { - FakeSchedulerClient* client_; - - explicit FakeBeginFrameSourceForFakeSchedulerClient( - FakeSchedulerClient* client) + class FakeExternalBeginFrameSource : public BeginFrameSourceMixIn { + public: + explicit FakeExternalBeginFrameSource(FakeSchedulerClient* client) : client_(client) {} + virtual ~FakeExternalBeginFrameSource() {} void OnNeedsBeginFramesChange(bool needs_begin_frames) override { if (needs_begin_frames) { @@ -63,6 +62,13 @@ } client_->states_.push_back(client_->scheduler_->AsValue()); } + + void TestOnBeginFrame(const BeginFrameArgs& args) { + return CallOnBeginFrame(args); + } + + private: + FakeSchedulerClient* client_; }; class FakePowerMonitorSource : public base::PowerMonitorSource { @@ -86,7 +92,7 @@ redraw_will_happen_if_update_visible_tiles_happens_(false), now_src_(TestNowSource::Create()), task_runner_(new OrderedSimpleTaskRunner(now_src_, true)), - fake_frame_source_(this), + fake_external_begin_frame_source_(nullptr), fake_power_monitor_source_(new FakePowerMonitorSource), power_monitor_(make_scoped_ptr<base::PowerMonitorSource>( fake_power_monitor_source_)), @@ -108,8 +114,21 @@ } TestScheduler* CreateScheduler(const SchedulerSettings& settings) { - scheduler_ = TestScheduler::Create( - now_src_, this, settings, 0, task_runner_, &power_monitor_); + scoped_ptr<FakeExternalBeginFrameSource> fake_external_begin_frame_source; + if (settings.use_external_begin_frame_source && + settings.throttle_frame_production) { + fake_external_begin_frame_source.reset( + new FakeExternalBeginFrameSource(this)); + fake_external_begin_frame_source_ = + fake_external_begin_frame_source.get(); + } + scheduler_ = TestScheduler::Create(now_src_, + this, + settings, + 0, + task_runner_, + &power_monitor_, + fake_external_begin_frame_source.Pass()); DCHECK(scheduler_); return scheduler_.get(); } @@ -119,7 +138,10 @@ void set_log_anticipated_draw_time_change(bool log) { log_anticipated_draw_time_change_ = log; } - bool needs_begin_frames() { return fake_frame_source_.NeedsBeginFrames(); } + bool needs_begin_frames() { + DCHECK(ExternalBeginFrame()); + return fake_external_begin_frame_source_->NeedsBeginFrames(); + } int num_draws() const { return num_draws_; } int num_actions_() const { return static_cast<int>(actions_.size()); } const char* Action(int i) const { return actions_[i]; } @@ -129,11 +151,12 @@ } bool ExternalBeginFrame() { - return scheduler_->settings().begin_frame_scheduling_enabled && + return scheduler_->settings().use_external_begin_frame_source && scheduler_->settings().throttle_frame_production; } - FakeBeginFrameSource* ExternalBeginFrameSource() override { - return &fake_frame_source_; + + FakeExternalBeginFrameSource* fake_external_begin_frame_source() const { + return fake_external_begin_frame_source_; } base::PowerMonitor* PowerMonitor() { return &power_monitor_; } @@ -145,12 +168,11 @@ void AdvanceFrame() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "FakeSchedulerClient::AdvanceFrame"); - // EXPECT_TRUE(needs_begin_frames()); if (ExternalBeginFrame()) { // Creep the time forward so that any BeginFrameArgs is not equal to the // last one otherwise we violate the BeginFrameSource contract. now_src_->AdvanceNowMicroseconds(1); - fake_frame_source_.TestOnBeginFrame( + fake_external_begin_frame_source_->TestOnBeginFrame( CreateBeginFrameArgsForTesting(now_src_)); EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); } @@ -288,7 +310,7 @@ std::vector<scoped_refptr<base::debug::ConvertableToTraceFormat>> states_; scoped_refptr<TestNowSource> now_src_; scoped_refptr<OrderedSimpleTaskRunner> task_runner_; - FakeBeginFrameSourceForFakeSchedulerClient fake_frame_source_; + FakeExternalBeginFrameSource* fake_external_begin_frame_source_; FakePowerMonitorSource* fake_power_monitor_source_; base::PowerMonitor power_monitor_; scoped_ptr<TestScheduler> scheduler_; @@ -327,14 +349,13 @@ EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); client->task_runner().RunTasksWhile(client->ImplFrameDeadlinePending(true)); EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - - // EXPECT_FALSE(client->needs_begin_frames()); } TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginImplFrame) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -348,6 +369,7 @@ TEST(SchedulerTest, RequestCommit) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -413,6 +435,7 @@ TEST(SchedulerTest, RequestCommitAfterBeginMainFrameSent) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -511,8 +534,9 @@ // 2. the scheduler drawing twice inside a single tick TEST(SchedulerTest, RequestRedrawInsideDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -548,8 +572,9 @@ // Test that requesting redraw inside a failed draw doesn't lose the request. TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -626,8 +651,9 @@ // happen inside a ScheduledActionDrawAndSwap TEST(SchedulerTest, RequestCommitInsideDraw) { SchedulerClientThatSetNeedsCommitInsideDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -671,8 +697,9 @@ // Tests that when a draw fails then the pending commit should not be dropped. TEST(SchedulerTest, RequestCommitInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -718,8 +745,9 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) { SchedulerClientThatSetNeedsCommitInsideDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -760,8 +788,9 @@ // Test manage tiles is independant of draws. TEST(SchedulerTest, ManageTiles) { SchedulerClientNeedsManageTilesInDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -866,8 +895,9 @@ // initiates it, then the state machine should not ManageTiles on that frame. TEST(SchedulerTest, ManageTilesOncePerFrame) { FakeSchedulerClient client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -986,6 +1016,7 @@ FakeSchedulerClient client; SchedulerSettings scheduler_settings; scheduler_settings.impl_side_painting = true; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1045,8 +1076,9 @@ TEST(SchedulerTest, TriggerBeginFrameDeadlineEarly) { SchedulerClientNeedsManageTilesInDraw client; - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -1096,8 +1128,9 @@ base::TimeDelta::FromMilliseconds( begin_main_frame_to_commit_estimate_in_ms), base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms)); - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -1163,8 +1196,9 @@ base::TimeDelta::FromMilliseconds(32), base::TimeDelta::FromMilliseconds(32)); client.set_log_anticipated_draw_time_change(true); - SchedulerSettings default_scheduler_settings; - TestScheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; + TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanDraw(true); scheduler->SetCanStart(); @@ -1179,7 +1213,7 @@ BeginFrameArgs frame_args = CreateBeginFrameArgsForTesting(client.now_src()); frame_args.interval = base::TimeDelta::FromMilliseconds(1000); - client.ExternalBeginFrameSource()->TestOnBeginFrame(frame_args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(frame_args); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); client.task_runner().RunPendingTasks(); // Run posted deadline. @@ -1192,7 +1226,7 @@ // the NotifyReadyToCommit for now. EXPECT_FALSE(scheduler->CommitPending()); scheduler->SetNeedsCommit(); - client.ExternalBeginFrameSource()->TestOnBeginFrame(frame_args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(frame_args); EXPECT_TRUE(scheduler->CommitPending()); // Draw and swap the frame, but don't ack the swap to simulate the Browser @@ -1235,6 +1269,7 @@ TEST(SchedulerTest, BeginRetroFrame) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1251,7 +1286,7 @@ // This is the first BeginFrame, which will be handled immediately. BeginFrameArgs args = CreateBeginFrameArgsForTesting(client.now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); @@ -1260,9 +1295,9 @@ // Queue BeginFrames while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); // If we don't swap on the deadline, we wait for the next BeginImplFrame. client.task_runner().RunPendingTasks(); // Run posted deadline. @@ -1308,6 +1343,7 @@ TEST(SchedulerTest, BeginRetroFrame_SwapThrottled) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1328,7 +1364,7 @@ // This is the first BeginFrame, which will be handled immediately. BeginFrameArgs args = CreateBeginFrameArgsForTesting(client.now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); @@ -1338,7 +1374,7 @@ // Queue BeginFrame while we are still handling the previous BeginFrame. EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); EXPECT_NO_ACTION(client); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); client.Reset(); @@ -1369,7 +1405,7 @@ // Queue BeginFrame while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); EXPECT_NO_ACTION(client); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); EXPECT_TRUE(client.needs_begin_frames()); @@ -1395,12 +1431,12 @@ client.Reset(); } -void BeginFramesNotFromClient(bool begin_frame_scheduling_enabled, +void BeginFramesNotFromClient(bool use_external_begin_frame_source, bool throttle_frame_production) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; - scheduler_settings.begin_frame_scheduling_enabled = - begin_frame_scheduling_enabled; + scheduler_settings.use_external_begin_frame_source = + use_external_begin_frame_source; scheduler_settings.throttle_frame_production = throttle_frame_production; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); @@ -1408,11 +1444,12 @@ scheduler->SetCanDraw(true); InitializeOutputSurfaceAndFirstCommit(scheduler, &client); + DCHECK(!client.fake_external_begin_frame_source()); + // SetNeedsCommit should begin the frame on the next BeginImplFrame // without calling SetNeedsBeginFrame. client.Reset(); scheduler->SetNeedsCommit(); - EXPECT_FALSE(client.needs_begin_frames()); EXPECT_NO_ACTION(client); client.Reset(); @@ -1422,21 +1459,18 @@ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // If we don't swap on the deadline, we wait for the next BeginFrame. client.task_runner().RunPendingTasks(); // Run posted deadline. EXPECT_NO_ACTION(client); EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // NotifyReadyToCommit should trigger the commit. scheduler->NotifyBeginMainFrameStarted(); scheduler->NotifyReadyToCommit(); EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // BeginImplFrame should prepare the draw. @@ -1444,14 +1478,12 @@ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionAnimate", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // BeginImplFrame deadline should draw. client.task_runner().RunTasksWhile(client.ImplFrameDeadlinePending(true)); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // The following BeginImplFrame deadline should SetNeedsBeginFrame(false) @@ -1465,37 +1497,37 @@ // when the BeginFrame is no longer needed. client.task_runner().RunPendingTasks(); // Run posted deadline. EXPECT_NO_ACTION(client); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); } TEST(SchedulerTest, SyntheticBeginFrames) { - bool begin_frame_scheduling_enabled = false; + bool use_external_begin_frame_source = false; bool throttle_frame_production = true; - BeginFramesNotFromClient(begin_frame_scheduling_enabled, + BeginFramesNotFromClient(use_external_begin_frame_source, throttle_frame_production); } TEST(SchedulerTest, VSyncThrottlingDisabled) { - bool begin_frame_scheduling_enabled = true; + bool use_external_begin_frame_source = true; bool throttle_frame_production = false; - BeginFramesNotFromClient(begin_frame_scheduling_enabled, + BeginFramesNotFromClient(use_external_begin_frame_source, throttle_frame_production); } TEST(SchedulerTest, SyntheticBeginFrames_And_VSyncThrottlingDisabled) { - bool begin_frame_scheduling_enabled = false; + bool use_external_begin_frame_source = false; bool throttle_frame_production = false; - BeginFramesNotFromClient(begin_frame_scheduling_enabled, + BeginFramesNotFromClient(use_external_begin_frame_source, throttle_frame_production); } -void BeginFramesNotFromClient_SwapThrottled(bool begin_frame_scheduling_enabled, - bool throttle_frame_production) { +void BeginFramesNotFromClient_SwapThrottled( + bool use_external_begin_frame_source, + bool throttle_frame_production) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; - scheduler_settings.begin_frame_scheduling_enabled = - begin_frame_scheduling_enabled; + scheduler_settings.use_external_begin_frame_source = + use_external_begin_frame_source; scheduler_settings.throttle_frame_production = throttle_frame_production; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); @@ -1503,6 +1535,8 @@ scheduler->SetCanDraw(true); InitializeOutputSurfaceAndFirstCommit(scheduler, &client); + DCHECK(!client.fake_external_begin_frame_source()); + // To test swap ack throttling, this test disables automatic swap acks. scheduler->SetMaxSwapsPending(1); client.SetAutomaticSwapAck(false); @@ -1510,7 +1544,6 @@ // SetNeedsCommit should begin the frame on the next BeginImplFrame. client.Reset(); scheduler->SetNeedsCommit(); - EXPECT_FALSE(client.needs_begin_frames()); EXPECT_NO_ACTION(client); client.Reset(); @@ -1519,14 +1552,12 @@ EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // NotifyReadyToCommit should trigger the pending commit and draw. scheduler->NotifyBeginMainFrameStarted(); scheduler->NotifyReadyToCommit(); EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // Swapping will put us into a swap throttled state. @@ -1534,7 +1565,6 @@ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2); EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // While swap throttled, BeginFrames should trigger BeginImplFrames, @@ -1543,14 +1573,12 @@ client.task_runner().RunPendingTasks(); // Run posted BeginFrame. EXPECT_ACTION("WillBeginImplFrame", client, 0, 1); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // Take us out of a swap throttled state. scheduler->DidSwapBuffersComplete(); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 0, 1); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); // BeginImplFrame deadline should draw. @@ -1559,35 +1587,35 @@ EXPECT_ACTION("ScheduledActionAnimate", client, 0, 2); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 1, 2); EXPECT_FALSE(scheduler->BeginImplFrameDeadlinePending()); - EXPECT_FALSE(client.needs_begin_frames()); client.Reset(); } TEST(SchedulerTest, SyntheticBeginFrames_SwapThrottled) { - bool begin_frame_scheduling_enabled = false; + bool use_external_begin_frame_source = false; bool throttle_frame_production = true; - BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled, + BeginFramesNotFromClient_SwapThrottled(use_external_begin_frame_source, throttle_frame_production); } TEST(SchedulerTest, VSyncThrottlingDisabled_SwapThrottled) { - bool begin_frame_scheduling_enabled = true; + bool use_external_begin_frame_source = true; bool throttle_frame_production = false; - BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled, + BeginFramesNotFromClient_SwapThrottled(use_external_begin_frame_source, throttle_frame_production); } TEST(SchedulerTest, SyntheticBeginFrames_And_VSyncThrottlingDisabled_SwapThrottled) { - bool begin_frame_scheduling_enabled = false; + bool use_external_begin_frame_source = false; bool throttle_frame_production = false; - BeginFramesNotFromClient_SwapThrottled(begin_frame_scheduling_enabled, + BeginFramesNotFromClient_SwapThrottled(use_external_begin_frame_source, throttle_frame_production); } TEST(SchedulerTest, DidLoseOutputSurfaceAfterOutputSurfaceIsInitialized) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1605,6 +1633,7 @@ TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginFrameStarted) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1643,6 +1672,7 @@ FakeSchedulerClient client; SchedulerSettings scheduler_settings; scheduler_settings.impl_side_painting = impl_side_painting; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1710,6 +1740,7 @@ FakeSchedulerClient client; SchedulerSettings scheduler_settings; scheduler_settings.impl_side_painting = impl_side_painting; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1760,6 +1791,7 @@ TEST(SchedulerTest, DidLoseOutputSurfaceAfterSetNeedsManageTiles) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1790,6 +1822,7 @@ TEST(SchedulerTest, DidLoseOutputSurfaceAfterBeginRetroFramePosted) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1806,7 +1839,7 @@ client.Reset(); BeginFrameArgs args = CreateBeginFrameArgsForTesting(client.now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); @@ -1814,9 +1847,9 @@ // Queue BeginFrames while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); // If we don't swap on the deadline, we wait for the next BeginImplFrame. client.Reset(); @@ -1848,6 +1881,7 @@ TEST(SchedulerTest, DidLoseOutputSurfaceDuringBeginRetroFrameRunning) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1864,7 +1898,7 @@ client.Reset(); BeginFrameArgs args = CreateBeginFrameArgsForTesting(client.now_src()); args.deadline += base::TimeDelta::FromHours(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); EXPECT_ACTION("WillBeginImplFrame", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client, 1, 2); EXPECT_TRUE(scheduler->BeginImplFrameDeadlinePending()); @@ -1872,9 +1906,9 @@ // Queue BeginFrames while we are still handling the previous BeginFrame. args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); args.frame_time += base::TimeDelta::FromSeconds(1); - client.ExternalBeginFrameSource()->TestOnBeginFrame(args); + client.fake_external_begin_frame_source()->TestOnBeginFrame(args); // If we don't swap on the deadline, we wait for the next BeginImplFrame. client.Reset(); @@ -1921,7 +1955,6 @@ StopBeginFrameAfterDidLoseOutputSurfaceWithSyntheticBeginFrameSource) { FakeSchedulerClient client; SchedulerSettings scheduler_settings; - scheduler_settings.begin_frame_scheduling_enabled = false; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -1963,6 +1996,7 @@ FakeSchedulerClient client; SchedulerSettings scheduler_settings; scheduler_settings.impl_side_painting = true; + scheduler_settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); @@ -2063,6 +2097,7 @@ SimulateWindowsLowResolutionTimerOnBattery_PrioritizeImplLatencyOff) { FakeSchedulerClient client; SchedulerSettings settings; + settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(settings); scheduler->SetCanStart(); @@ -2108,6 +2143,7 @@ FakeSchedulerClient client; SchedulerSettings settings; settings.disable_hi_res_timer_tasks_on_battery = true; + settings.use_external_begin_frame_source = true; TestScheduler* scheduler = client.CreateScheduler(settings); scheduler->SetCanStart();
diff --git a/cc/surfaces/display.h b/cc/surfaces/display.h index 5f80d64..5935534 100644 --- a/cc/surfaces/display.h +++ b/cc/surfaces/display.h
@@ -63,7 +63,6 @@ void CommitVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) override; void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override {} - void BeginFrame(const BeginFrameArgs& args) override {} void DidSwapBuffers() override; void DidSwapBuffersComplete() override; void ReclaimResources(const CompositorFrameAck* ack) override {}
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc index 5ab8942..fba3166 100644 --- a/cc/test/animation_test_common.cc +++ b/cc/test/animation_test_common.cc
@@ -124,14 +124,16 @@ } FakeFloatAnimationCurve::FakeFloatAnimationCurve() - : duration_(1.0) {} + : duration_(base::TimeDelta::FromSecondsD(1.0)) { +} FakeFloatAnimationCurve::FakeFloatAnimationCurve(double duration) - : duration_(duration) {} + : duration_(base::TimeDelta::FromSecondsD(duration)) { +} FakeFloatAnimationCurve::~FakeFloatAnimationCurve() {} -double FakeFloatAnimationCurve::Duration() const { +base::TimeDelta FakeFloatAnimationCurve::Duration() const { return duration_; } @@ -144,11 +146,12 @@ } FakeTransformTransition::FakeTransformTransition(double duration) - : duration_(duration) {} + : duration_(base::TimeDelta::FromSecondsD(duration)) { +} FakeTransformTransition::~FakeTransformTransition() {} -double FakeTransformTransition::Duration() const { +base::TimeDelta FakeTransformTransition::Duration() const { return duration_; } @@ -175,18 +178,18 @@ return make_scoped_ptr(new FakeTransformTransition(*this)); } - FakeFloatTransition::FakeFloatTransition(double duration, float from, float to) - : duration_(duration), from_(from), to_(to) {} + : duration_(base::TimeDelta::FromSecondsD(duration)), from_(from), to_(to) { +} FakeFloatTransition::~FakeFloatTransition() {} -double FakeFloatTransition::Duration() const { +base::TimeDelta FakeFloatTransition::Duration() const { return duration_; } float FakeFloatTransition::GetValue(double time) const { - time /= duration_; + time /= duration_.InSecondsF(); if (time >= 1.0) time = 1.0; return (1.0 - time) * from_ + time * to_;
diff --git a/cc/test/animation_test_common.h b/cc/test/animation_test_common.h index 328ae84..a69f0c9 100644 --- a/cc/test/animation_test_common.h +++ b/cc/test/animation_test_common.h
@@ -26,12 +26,12 @@ explicit FakeFloatAnimationCurve(double duration); ~FakeFloatAnimationCurve() override; - double Duration() const override; + base::TimeDelta Duration() const override; float GetValue(double now) const override; scoped_ptr<AnimationCurve> Clone() const override; private: - double duration_; + base::TimeDelta duration_; }; class FakeTransformTransition : public TransformAnimationCurve { @@ -39,7 +39,7 @@ explicit FakeTransformTransition(double duration); ~FakeTransformTransition() override; - double Duration() const override; + base::TimeDelta Duration() const override; gfx::Transform GetValue(double time) const override; bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds) const override; @@ -51,7 +51,7 @@ scoped_ptr<AnimationCurve> Clone() const override; private: - double duration_; + base::TimeDelta duration_; }; class FakeFloatTransition : public FloatAnimationCurve { @@ -59,13 +59,13 @@ FakeFloatTransition(double duration, float from, float to); ~FakeFloatTransition() override; - double Duration() const override; + base::TimeDelta Duration() const override; float GetValue(double time) const override; scoped_ptr<AnimationCurve> Clone() const override; private: - double duration_; + base::TimeDelta duration_; float from_; float to_; };
diff --git a/cc/test/data/background_filter_blur_off_axis.png b/cc/test/data/background_filter_blur_off_axis.png index 050ec54..5077171 100644 --- a/cc/test/data/background_filter_blur_off_axis.png +++ b/cc/test/data/background_filter_blur_off_axis.png Binary files differ
diff --git a/cc/test/data/background_filter_blur_outsets.png b/cc/test/data/background_filter_blur_outsets.png index 9234496..3293ab8 100644 --- a/cc/test/data/background_filter_blur_outsets.png +++ b/cc/test/data/background_filter_blur_outsets.png Binary files differ
diff --git a/cc/test/data/blending_and_filter.png b/cc/test/data/blending_and_filter.png index 07e7fea..f231475 100644 --- a/cc/test/data/blending_and_filter.png +++ b/cc/test/data/blending_and_filter.png Binary files differ
diff --git a/cc/test/data/blending_render_pass.png b/cc/test/data/blending_render_pass.png index 76fe369..9186864 100644 --- a/cc/test/data/blending_render_pass.png +++ b/cc/test/data/blending_render_pass.png Binary files differ
diff --git a/cc/test/data/blending_render_pass_mask.png b/cc/test/data/blending_render_pass_mask.png index 8b63f35..21d792a 100644 --- a/cc/test/data/blending_render_pass_mask.png +++ b/cc/test/data/blending_render_pass_mask.png Binary files differ
diff --git a/cc/test/data/blending_transparent.png b/cc/test/data/blending_transparent.png index dc20808..f98607e 100644 --- a/cc/test/data/blending_transparent.png +++ b/cc/test/data/blending_transparent.png Binary files differ
diff --git a/cc/test/data/blending_with_root.png b/cc/test/data/blending_with_root.png index 519c642..276df1d 100644 --- a/cc/test/data/blending_with_root.png +++ b/cc/test/data/blending_with_root.png Binary files differ
diff --git a/cc/test/data/filter_on_scaled_layer.png b/cc/test/data/filter_on_scaled_layer.png new file mode 100644 index 0000000..83cb2c5 --- /dev/null +++ b/cc/test/data/filter_on_scaled_layer.png Binary files differ
diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h index d7b4704..3c44994 100644 --- a/cc/test/fake_layer_tree_host_impl_client.h +++ b/cc/test/fake_layer_tree_host_impl_client.h
@@ -23,6 +23,7 @@ void DidSwapBuffersCompleteOnImplThread() override {} void OnCanDrawStateChanged(bool can_draw) override {} void NotifyReadyToActivate() override {} + void NotifyReadyToDraw() override {} void SetNeedsRedrawOnImplThread() override {} void SetNeedsRedrawRectOnImplThread(const gfx::Rect& damage_rect) override {} void SetNeedsAnimateOnImplThread() override {}
diff --git a/cc/test/fake_output_surface.cc b/cc/test/fake_output_surface.cc index df9c0f7..46efba0 100644 --- a/cc/test/fake_output_surface.cc +++ b/cc/test/fake_output_surface.cc
@@ -20,9 +20,7 @@ : OutputSurface(context_provider), client_(NULL), num_sent_frames_(0), - needs_begin_frame_(false), - has_external_stencil_test_(false), - fake_weak_ptr_factory_(this) { + has_external_stencil_test_(false) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -35,8 +33,7 @@ : OutputSurface(software_device.Pass()), client_(NULL), num_sent_frames_(0), - has_external_stencil_test_(false), - fake_weak_ptr_factory_(this) { + has_external_stencil_test_(false) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -50,8 +47,7 @@ : OutputSurface(context_provider, software_device.Pass()), client_(NULL), num_sent_frames_(0), - has_external_stencil_test_(false), - fake_weak_ptr_factory_(this) { + has_external_stencil_test_(false) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -82,24 +78,6 @@ client_->DidSwapBuffers(); } -void FakeOutputSurface::SetNeedsBeginFrame(bool enable) { - needs_begin_frame_ = enable; - OutputSurface::SetNeedsBeginFrame(enable); - - if (enable) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&FakeOutputSurface::OnBeginFrame, - fake_weak_ptr_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(16)); - } -} - -void FakeOutputSurface::OnBeginFrame() { - client_->BeginFrame(CreateBeginFrameArgsForTesting()); -} - - bool FakeOutputSurface::BindToClient(OutputSurfaceClient* client) { if (OutputSurface::BindToClient(client)) { client_ = client;
diff --git a/cc/test/fake_output_surface.h b/cc/test/fake_output_surface.h index 59c9a8c..f28fccc 100644 --- a/cc/test/fake_output_surface.h +++ b/cc/test/fake_output_surface.h
@@ -94,9 +94,6 @@ void SwapBuffers(CompositorFrame* frame) override; - void SetNeedsBeginFrame(bool enable) override; - bool needs_begin_frame() const { return needs_begin_frame_; } - bool BindToClient(OutputSurfaceClient* client) override; using OutputSurface::ReleaseGL; @@ -137,18 +134,13 @@ scoped_ptr<SoftwareOutputDevice> software_device, bool delegated_rendering); - void OnBeginFrame(); - OutputSurfaceClient* client_; CompositorFrame last_sent_frame_; size_t num_sent_frames_; - bool needs_begin_frame_; bool has_external_stencil_test_; TransferableResourceArray resources_held_by_parent_; scoped_ptr<ManagedMemoryPolicy> memory_policy_to_set_at_bind_; gfx::Rect last_swap_rect_; - - base::WeakPtrFactory<FakeOutputSurface> fake_weak_ptr_factory_; }; } // namespace cc
diff --git a/cc/test/fake_output_surface_client.cc b/cc/test/fake_output_surface_client.cc index 77c7f8a..073a29f 100644 --- a/cc/test/fake_output_surface_client.cc +++ b/cc/test/fake_output_surface_client.cc
@@ -16,10 +16,6 @@ output_surface_->ReleaseContextProvider(); } -void FakeOutputSurfaceClient::BeginFrame(const BeginFrameArgs& args) { - begin_frame_count_++; -} - void FakeOutputSurfaceClient::DidSwapBuffers() { swap_count_++; }
diff --git a/cc/test/fake_output_surface_client.h b/cc/test/fake_output_surface_client.h index a7620b6..310842d 100644 --- a/cc/test/fake_output_surface_client.h +++ b/cc/test/fake_output_surface_client.h
@@ -16,7 +16,6 @@ public: FakeOutputSurfaceClient() : output_surface_(NULL), - begin_frame_count_(0), swap_count_(0), deferred_initialize_called_(false), did_lose_output_surface_called_(false), @@ -24,7 +23,6 @@ explicit FakeOutputSurfaceClient(OutputSurface* output_surface) : output_surface_(output_surface), - begin_frame_count_(0), swap_count_(0), deferred_initialize_called_(false), did_lose_output_surface_called_(false), @@ -35,7 +33,6 @@ void CommitVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) override {} void SetNeedsRedrawRect(const gfx::Rect& damage_rect) override {} - void BeginFrame(const BeginFrameArgs& args) override; void DidSwapBuffers() override; void DidSwapBuffersComplete() override {} void ReclaimResources(const CompositorFrameAck* ack) override {} @@ -50,7 +47,6 @@ void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override; void SetTreeActivationCallback(const base::Closure&) override {} - int begin_frame_count() { return begin_frame_count_; } int swap_count() { return swap_count_; } bool deferred_initialize_called() { @@ -65,7 +61,6 @@ private: OutputSurface* output_surface_; - int begin_frame_count_; int swap_count_; bool deferred_initialize_called_; bool did_lose_output_surface_called_;
diff --git a/cc/test/fake_picture_layer_impl.cc b/cc/test/fake_picture_layer_impl.cc index 73c1ae3..bf55ebf 100644 --- a/cc/test/fake_picture_layer_impl.cc +++ b/cc/test/fake_picture_layer_impl.cc
@@ -10,31 +10,33 @@ namespace cc { -FakePictureLayerImpl::FakePictureLayerImpl(LayerTreeImpl* tree_impl, - int id, - scoped_refptr<PicturePileImpl> pile) +FakePictureLayerImpl::FakePictureLayerImpl( + LayerTreeImpl* tree_impl, + int id, + scoped_refptr<RasterSource> raster_source) : PictureLayerImpl(tree_impl, id), append_quads_count_(0), did_become_active_call_count_(0), has_valid_tile_priorities_(false), use_set_valid_tile_priorities_flag_(false), release_resources_count_(0) { - pile_ = pile; - SetBounds(pile_->tiling_size()); - SetContentBounds(pile_->tiling_size()); + raster_source_ = raster_source; + SetBounds(raster_source_->GetSize()); + SetContentBounds(raster_source_->GetSize()); } -FakePictureLayerImpl::FakePictureLayerImpl(LayerTreeImpl* tree_impl, - int id, - scoped_refptr<PicturePileImpl> pile, - const gfx::Size& layer_bounds) +FakePictureLayerImpl::FakePictureLayerImpl( + LayerTreeImpl* tree_impl, + int id, + scoped_refptr<RasterSource> raster_source, + const gfx::Size& layer_bounds) : PictureLayerImpl(tree_impl, id), append_quads_count_(0), did_become_active_call_count_(0), has_valid_tile_priorities_(false), use_set_valid_tile_priorities_flag_(false), release_resources_count_(0) { - pile_ = pile; + raster_source_ = raster_source; SetBounds(layer_bounds); SetContentBounds(layer_bounds); } @@ -97,12 +99,13 @@ return result; } -void FakePictureLayerImpl::SetPile(scoped_refptr<PicturePileImpl> pile) { - pile_.swap(pile); +void FakePictureLayerImpl::SetRasterSource( + scoped_refptr<RasterSource> raster_source) { + raster_source_.swap(raster_source); if (tilings()) { for (size_t i = 0; i < num_tilings(); ++i) { - tilings()->tiling_at(i)->UpdateTilesToCurrentPile(Region(), - pile_->tiling_size()); + tilings()->tiling_at(i)->UpdateTilesToCurrentRasterSource( + Region(), raster_source_->GetSize()); } } } @@ -190,6 +193,65 @@ : PictureLayerImpl::HasValidTilePriorities(); } +size_t FakePictureLayerImpl::CountTilesRequired( + TileRequirementCheck is_tile_required_callback) const { + if (!HasValidTilePriorities()) + return 0; + + if (!tilings_) + return 0; + + if (visible_rect_for_tile_priority_.IsEmpty()) + return 0; + + gfx::Rect rect = GetViewportForTilePriorityInContentSpace(); + rect.Intersect(visible_rect_for_tile_priority_); + + size_t count = 0; + + for (size_t i = 0; i < tilings_->num_tilings(); ++i) { + PictureLayerTiling* tiling = tilings_->tiling_at(i); + if (tiling->resolution() != HIGH_RESOLUTION && + tiling->resolution() != LOW_RESOLUTION) + continue; + + for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter; + ++iter) { + const Tile* tile = *iter; + // A null tile (i.e. missing recording) can just be skipped. + // TODO(vmpstr): Verify this is true if we create tiles in raster + // iterators. + if (!tile) + continue; + + // We can't check tile->required_for_activation, because that value might + // be out of date. It is updated in the raster/eviction iterators. + // TODO(vmpstr): Remove the comment once you can't access this information + // from the tile. + if ((tiling->*is_tile_required_callback)(tile)) + ++count; + } + } + + return count; +} + +size_t FakePictureLayerImpl::CountTilesRequiredForActivation() const { + if (!layer_tree_impl()->IsPendingTree()) + return 0; + + return CountTilesRequired( + &PictureLayerTiling::IsTileRequiredForActivationIfVisible); +} + +size_t FakePictureLayerImpl::CountTilesRequiredForDraw() const { + if (!layer_tree_impl()->IsActiveTree()) + return 0; + + return CountTilesRequired( + &PictureLayerTiling::IsTileRequiredForDrawIfVisible); +} + void FakePictureLayerImpl::ReleaseResources() { PictureLayerImpl::ReleaseResources(); ++release_resources_count_;
diff --git a/cc/test/fake_picture_layer_impl.h b/cc/test/fake_picture_layer_impl.h index 4223811..b10b357 100644 --- a/cc/test/fake_picture_layer_impl.h +++ b/cc/test/fake_picture_layer_impl.h
@@ -17,20 +17,23 @@ return make_scoped_ptr(new FakePictureLayerImpl(tree_impl, id)); } - // Create layer from a pile that covers the entire layer. - static scoped_ptr<FakePictureLayerImpl> CreateWithPile( - LayerTreeImpl* tree_impl, int id, scoped_refptr<PicturePileImpl> pile) { - return make_scoped_ptr(new FakePictureLayerImpl(tree_impl, id, pile)); - } - - // Create layer from a pile that only covers part of the layer. - static scoped_ptr<FakePictureLayerImpl> CreateWithPartialPile( + // Create layer from a raster source that covers the entire layer. + static scoped_ptr<FakePictureLayerImpl> CreateWithRasterSource( LayerTreeImpl* tree_impl, int id, - scoped_refptr<PicturePileImpl> pile, + scoped_refptr<RasterSource> raster_source) { + return make_scoped_ptr( + new FakePictureLayerImpl(tree_impl, id, raster_source)); + } + + // Create layer from a raster source that only covers part of the layer. + static scoped_ptr<FakePictureLayerImpl> CreateWithPartialRasterSource( + LayerTreeImpl* tree_impl, + int id, + scoped_refptr<RasterSource> raster_source, const gfx::Size& layer_bounds) { return make_scoped_ptr( - new FakePictureLayerImpl(tree_impl, id, pile, layer_bounds)); + new FakePictureLayerImpl(tree_impl, id, raster_source, layer_bounds)); } scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; @@ -50,6 +53,11 @@ use_set_valid_tile_priorities_flag_ = true; } + size_t CountTilesRequired( + TileRequirementCheck is_tile_required_callback) const; + size_t CountTilesRequiredForActivation() const; + size_t CountTilesRequiredForDraw() const; + using PictureLayerImpl::AddTiling; using PictureLayerImpl::CleanUpTilingsOnActiveLayer; using PictureLayerImpl::CanHaveTilings; @@ -58,7 +66,7 @@ using PictureLayerImpl::GetViewportForTilePriorityInContentSpace; using PictureLayerImpl::SanityCheckTilingState; using PictureLayerImpl::GetRecycledTwinLayer; - using PictureLayerImpl::UpdatePile; + using PictureLayerImpl::UpdateRasterSource; using PictureLayerImpl::UpdateIdealScales; using PictureLayerImpl::MaximumTilingContentsScale; @@ -82,8 +90,8 @@ size_t num_tilings() const { return tilings_->num_tilings(); } PictureLayerTilingSet* tilings() { return tilings_.get(); } - PicturePileImpl* pile() { return pile_.get(); } - void SetPile(scoped_refptr<PicturePileImpl> pile); + RasterSource* raster_source() { return raster_source_.get(); } + void SetRasterSource(scoped_refptr<RasterSource> raster_source); size_t append_quads_count() { return append_quads_count_; } const Region& invalidation() const { return invalidation_; } @@ -113,13 +121,12 @@ } protected: - FakePictureLayerImpl( - LayerTreeImpl* tree_impl, - int id, - scoped_refptr<PicturePileImpl> pile); FakePictureLayerImpl(LayerTreeImpl* tree_impl, int id, - scoped_refptr<PicturePileImpl> pile, + scoped_refptr<RasterSource> raster_source); + FakePictureLayerImpl(LayerTreeImpl* tree_impl, + int id, + scoped_refptr<RasterSource> raster_source, const gfx::Size& layer_bounds); FakePictureLayerImpl(LayerTreeImpl* tree_impl, int id);
diff --git a/cc/test/fake_picture_layer_tiling_client.cc b/cc/test/fake_picture_layer_tiling_client.cc index 2149ff4..a218a21 100644 --- a/cc/test/fake_picture_layer_tiling_client.cc +++ b/cc/test/fake_picture_layer_tiling_client.cc
@@ -17,6 +17,7 @@ twin_tiling_(NULL), recycled_twin_tiling_(NULL), allow_create_tile_(true), + max_tile_priority_bin_(TilePriority::NOW), max_tiles_for_interest_area_(10000), skewport_target_time_in_seconds_(1.0f), skewport_extrapolation_limit_in_content_pixels_(2000) { @@ -32,6 +33,7 @@ twin_tiling_(NULL), recycled_twin_tiling_(NULL), allow_create_tile_(true), + max_tile_priority_bin_(TilePriority::NOW), max_tiles_for_interest_area_(10000), skewport_target_time_in_seconds_(1.0f) { } @@ -60,6 +62,11 @@ return tile_size_; } +TilePriority::PriorityBin FakePictureLayerTilingClient::GetMaxTilePriorityBin() + const { + return max_tile_priority_bin_; +} + size_t FakePictureLayerTilingClient::GetMaxTilesForInterestArea() const { return max_tiles_for_interest_area_; }
diff --git a/cc/test/fake_picture_layer_tiling_client.h b/cc/test/fake_picture_layer_tiling_client.h index 3df4ca4..c8cc4e2 100644 --- a/cc/test/fake_picture_layer_tiling_client.h +++ b/cc/test/fake_picture_layer_tiling_client.h
@@ -25,6 +25,7 @@ const gfx::Rect& rect) override; RasterSource* GetRasterSource() override; gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override; + TilePriority::PriorityBin GetMaxTilePriorityBin() const override; size_t GetMaxTilesForInterestArea() const override; float GetSkewportTargetTimeInSeconds() const override; int GetSkewportExtrapolationLimitInContentPixels() const override; @@ -47,6 +48,9 @@ void set_text_rect(const gfx::Rect& rect) { text_rect_ = rect; } void set_allow_create_tile(bool allow) { allow_create_tile_ = allow; } void set_invalidation(const Region& region) { invalidation_ = region; } + void set_max_tile_priority_bin(TilePriority::PriorityBin bin) { + max_tile_priority_bin_ = bin; + } void set_max_tiles_for_interest_area(size_t area) { max_tiles_for_interest_area_ = area; } @@ -73,6 +77,7 @@ gfx::Rect text_rect_; bool allow_create_tile_; Region invalidation_; + TilePriority::PriorityBin max_tile_priority_bin_; size_t max_tiles_for_interest_area_; float skewport_target_time_in_seconds_; int skewport_extrapolation_limit_in_content_pixels_;
diff --git a/cc/test/fake_picture_pile.h b/cc/test/fake_picture_pile.h new file mode 100644 index 0000000..e486e3b --- /dev/null +++ b/cc/test/fake_picture_pile.h
@@ -0,0 +1,53 @@ +// 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 CC_TEST_FAKE_PICTURE_PILE_H_ +#define CC_TEST_FAKE_PICTURE_PILE_H_ + +#include "cc/resources/picture_pile.h" + +namespace cc { + +class FakePicturePile : public PicturePile { + public: + ~FakePicturePile() override {} + + using PicturePile::buffer_pixels; + using PicturePile::CanRasterSlowTileCheck; + using PicturePile::Clear; + + PictureMap& picture_map() { return picture_map_; } + const gfx::Rect& recorded_viewport() const { return recorded_viewport_; } + + bool CanRasterLayerRect(gfx::Rect layer_rect) { + layer_rect.Intersect(gfx::Rect(tiling_.tiling_size())); + if (recorded_viewport_.Contains(layer_rect)) + return true; + return CanRasterSlowTileCheck(layer_rect); + } + + bool HasRecordings() const { return has_any_recordings_; } + + void SetRecordedViewport(const gfx::Rect& viewport) { + recorded_viewport_ = viewport; + } + + void SetHasAnyRecordings(bool has_recordings) { + has_any_recordings_ = has_recordings; + } + + TilingData& tiling() { return tiling_; } + + bool is_solid_color() const { return is_solid_color_; } + SkColor solid_color() const { return solid_color_; } + + void SetPixelRecordDistance(int d) { pixel_record_distance_ = d; } + + typedef PicturePile::PictureInfo PictureInfo; + typedef PicturePile::PictureMapKey PictureMapKey; + typedef PicturePile::PictureMap PictureMap; +}; +} // namespace cc + +#endif // CC_TEST_FAKE_PICTURE_PILE_H_
diff --git a/cc/test/fake_picture_pile_impl.cc b/cc/test/fake_picture_pile_impl.cc index 32a6589..3d31c12 100644 --- a/cc/test/fake_picture_pile_impl.cc +++ b/cc/test/fake_picture_pile_impl.cc
@@ -4,9 +4,12 @@ #include "cc/test/fake_picture_pile_impl.h" +#include <algorithm> #include <limits> #include <utility> +#include "cc/resources/picture_pile.h" +#include "cc/test/fake_picture_pile.h" #include "cc/test/impl_side_painting_settings.h" #include "testing/gtest/include/gtest/gtest.h" @@ -14,62 +17,71 @@ FakePicturePileImpl::FakePicturePileImpl() {} +FakePicturePileImpl::FakePicturePileImpl(const PicturePile* other) + : PicturePileImpl(other), + tile_grid_info_(other->GetTileGridInfoForTesting()) { +} + FakePicturePileImpl::~FakePicturePileImpl() {} scoped_refptr<FakePicturePileImpl> FakePicturePileImpl::CreateFilledPile( const gfx::Size& tile_size, const gfx::Size& layer_bounds) { - scoped_refptr<FakePicturePileImpl> pile(new FakePicturePileImpl()); - pile->tiling().SetTilingSize(layer_bounds); - pile->tiling().SetMaxTextureSize(tile_size); - pile->SetTileGridSize(ImplSidePaintingSettings().default_tile_grid_size); - pile->recorded_viewport_ = gfx::Rect(layer_bounds); - pile->has_any_recordings_ = true; - for (int x = 0; x < pile->tiling().num_tiles_x(); ++x) { - for (int y = 0; y < pile->tiling().num_tiles_y(); ++y) - pile->AddRecordingAt(x, y); + FakePicturePile pile; + pile.tiling().SetTilingSize(layer_bounds); + pile.tiling().SetMaxTextureSize(tile_size); + pile.SetTileGridSize(ImplSidePaintingSettings().default_tile_grid_size); + pile.SetRecordedViewport(gfx::Rect(layer_bounds)); + pile.SetHasAnyRecordings(true); + + auto pile_impl = make_scoped_refptr(new FakePicturePileImpl(&pile)); + for (int x = 0; x < pile_impl->tiling().num_tiles_x(); ++x) { + for (int y = 0; y < pile_impl->tiling().num_tiles_y(); ++y) + pile_impl->AddRecordingAt(x, y); } - return pile; + return pile_impl; } scoped_refptr<FakePicturePileImpl> FakePicturePileImpl::CreateEmptyPile( const gfx::Size& tile_size, const gfx::Size& layer_bounds) { - scoped_refptr<FakePicturePileImpl> pile(new FakePicturePileImpl()); - pile->tiling().SetTilingSize(layer_bounds); - pile->tiling().SetMaxTextureSize(tile_size); - pile->SetTileGridSize(ImplSidePaintingSettings().default_tile_grid_size); - pile->recorded_viewport_ = gfx::Rect(); - pile->has_any_recordings_ = false; - return pile; + FakePicturePile pile; + pile.tiling().SetTilingSize(layer_bounds); + pile.tiling().SetMaxTextureSize(tile_size); + pile.SetTileGridSize(ImplSidePaintingSettings().default_tile_grid_size); + pile.SetRecordedViewport(gfx::Rect()); + pile.SetHasAnyRecordings(false); + return make_scoped_refptr(new FakePicturePileImpl(&pile)); } scoped_refptr<FakePicturePileImpl> FakePicturePileImpl::CreateEmptyPileThatThinksItHasRecordings( const gfx::Size& tile_size, const gfx::Size& layer_bounds) { - scoped_refptr<FakePicturePileImpl> pile(new FakePicturePileImpl()); - pile->tiling().SetTilingSize(layer_bounds); - pile->tiling().SetMaxTextureSize(tile_size); - pile->SetTileGridSize(ImplSidePaintingSettings().default_tile_grid_size); + FakePicturePile pile; + pile.tiling().SetTilingSize(layer_bounds); + pile.tiling().SetMaxTextureSize(tile_size); + pile.SetTileGridSize(ImplSidePaintingSettings().default_tile_grid_size); // This simulates a false positive for this flag. - pile->recorded_viewport_ = gfx::Rect(); - pile->has_any_recordings_ = true; - return pile; + pile.SetRecordedViewport(gfx::Rect()); + pile.SetHasAnyRecordings(true); + return make_scoped_refptr(new FakePicturePileImpl(&pile)); } scoped_refptr<FakePicturePileImpl> FakePicturePileImpl::CreateInfiniteFilledPile() { - scoped_refptr<FakePicturePileImpl> pile(new FakePicturePileImpl()); + FakePicturePile pile; gfx::Size size(std::numeric_limits<int>::max(), std::numeric_limits<int>::max()); - pile->tiling().SetTilingSize(size); - pile->tiling().SetMaxTextureSize(size); - pile->SetTileGridSize(size); - pile->recorded_viewport_ = gfx::Rect(size); - pile->has_any_recordings_ = true; - pile->AddRecordingAt(0, 0); - return pile; + pile.tiling().SetTilingSize(size); + pile.tiling().SetMaxTextureSize(size); + pile.SetTileGridSize(size); + pile.SetRecordedViewport(gfx::Rect(size)); + pile.SetHasAnyRecordings(true); + + auto pile_impl = make_scoped_refptr(new FakePicturePileImpl(&pile)); + pile_impl->AddRecordingAt(0, 0); + return pile_impl; } void FakePicturePileImpl::AddRecordingAt(int x, int y) { @@ -103,6 +115,13 @@ EXPECT_FALSE(HasRecordingAt(x, y)); } +bool FakePicturePileImpl::HasRecordingAt(int x, int y) const { + PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y)); + if (found == picture_map_.end()) + return false; + return !!found->second.GetPicture(); +} + void FakePicturePileImpl::RerecordPile() { for (int y = 0; y < num_tiles_y(); ++y) { for (int x = 0; x < num_tiles_x(); ++x) { @@ -112,4 +131,37 @@ } } +void FakePicturePileImpl::SetMinContentsScale(float min_contents_scale) { + if (min_contents_scale_ == min_contents_scale) + return; + + // Picture contents are played back scaled. When the final contents scale is + // less than 1 (i.e. low res), then multiple recorded pixels will be used + // to raster one final pixel. To avoid splitting a final pixel across + // pictures (which would result in incorrect rasterization due to blending), a + // buffer margin is added so that any picture can be snapped to integral + // final pixels. + // + // For example, if a 1/4 contents scale is used, then that would be 3 buffer + // pixels, since that's the minimum number of pixels to add so that resulting + // content can be snapped to a four pixel aligned grid. + int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1); + buffer_pixels = std::max(0, buffer_pixels); + SetBufferPixels(buffer_pixels); + min_contents_scale_ = min_contents_scale; +} + +void FakePicturePileImpl::SetBufferPixels(int new_buffer_pixels) { + if (new_buffer_pixels == buffer_pixels()) + return; + + Clear(); + tiling_.SetBorderTexels(new_buffer_pixels); +} + +void FakePicturePileImpl::Clear() { + picture_map_.clear(); + recorded_viewport_ = gfx::Rect(); +} + } // namespace cc
diff --git a/cc/test/fake_picture_pile_impl.h b/cc/test/fake_picture_pile_impl.h index 10c769a..f1a9d31 100644 --- a/cc/test/fake_picture_pile_impl.h +++ b/cc/test/fake_picture_pile_impl.h
@@ -72,12 +72,24 @@ is_solid_color_ = is_solid_color; } + bool HasRecordingAt(int x, int y) const; + void SetIsMask(bool mask) { is_mask_ = mask; } + + int num_tiles_x() const { return tiling_.num_tiles_x(); } + int num_tiles_y() const { return tiling_.num_tiles_y(); } + + void SetMinContentsScale(float scale); + void SetBufferPixels(int new_buffer_pixels); + void Clear(); + protected: FakePicturePileImpl(); + explicit FakePicturePileImpl(const PicturePile* other); ~FakePicturePileImpl() override; FakeContentLayerClient client_; SkPaint default_paint_; + SkTileGridFactory::TileGridInfo tile_grid_info_; }; } // namespace cc
diff --git a/cc/test/fake_tile_manager_client.h b/cc/test/fake_tile_manager_client.h index 4b42468..94cfca6 100644 --- a/cc/test/fake_tile_manager_client.h +++ b/cc/test/fake_tile_manager_client.h
@@ -19,6 +19,7 @@ // TileManagerClient implementation. const std::vector<PictureLayerImpl*>& GetPictureLayers() const override; void NotifyReadyToActivate() override {} + void NotifyReadyToDraw() override {} void NotifyTileStateChanged(const Tile* tile) override {} void BuildRasterQueue(RasterTilePriorityQueue* queue, TreePriority tree_priority) override {}
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index d1d1962..373959d 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc
@@ -15,6 +15,7 @@ #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" #include "cc/test/animation_test_common.h" +#include "cc/test/begin_frame_args_test.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_output_surface.h" #include "cc/test/test_context_provider.h" @@ -53,9 +54,51 @@ raster_worker_pool, resource_pool, staging_resource_pool); } -base::TimeDelta TestHooks::LowFrequencyAnimationInterval() const { - return base::TimeDelta::FromMilliseconds(16); -} +class ExternalBeginFrameSourceForTest + : public BeginFrameSourceMixIn, + public NON_EXPORTED_BASE(base::NonThreadSafe) { + public: + explicit ExternalBeginFrameSourceForTest(double refresh_rate) + : milliseconds_per_frame_(1000.0 / refresh_rate), + is_ready_(false), + weak_ptr_factory_(this) { + DetachFromThread(); + } + + virtual ~ExternalBeginFrameSourceForTest() { + DCHECK(CalledOnValidThread()); + } + + virtual void OnNeedsBeginFramesChange(bool needs_begin_frames) override { + DCHECK(CalledOnValidThread()); + if (needs_begin_frames) { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&ExternalBeginFrameSourceForTest::TestOnBeginFrame, + weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(milliseconds_per_frame_)); + } + } + + virtual void SetClientReady() override { + DCHECK(CalledOnValidThread()); + is_ready_ = true; + } + + bool is_ready() const { + return is_ready_; + } + + void TestOnBeginFrame() { + DCHECK(CalledOnValidThread()); + CallOnBeginFrame(CreateBeginFrameArgsForTesting()); + } + + private: + double milliseconds_per_frame_; + bool is_ready_; + base::WeakPtrFactory<ExternalBeginFrameSourceForTest> weak_ptr_factory_; +}; // Adapts ThreadProxy for test. Injects test hooks for testing. class ThreadProxyForTest : public ThreadProxy { @@ -64,9 +107,14 @@ TestHooks* test_hooks, LayerTreeHost* host, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) { + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { return make_scoped_ptr(new ThreadProxyForTest( - test_hooks, host, main_task_runner, impl_task_runner)); + test_hooks, + host, + main_task_runner, + impl_task_runner, + external_begin_frame_source.Pass())); } ~ThreadProxyForTest() override {} @@ -109,8 +157,11 @@ TestHooks* test_hooks, LayerTreeHost* host, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) - : ThreadProxy(host, main_task_runner, impl_task_runner), + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) + : ThreadProxy(host, main_task_runner, + impl_task_runner, + external_begin_frame_source.Pass()), test_hooks_(test_hooks) {} }; @@ -214,10 +265,17 @@ } void NotifyReadyToActivate() override { - if (block_notify_ready_to_activate_for_testing_) + if (block_notify_ready_to_activate_for_testing_) { notify_ready_to_activate_was_blocked_ = true; - else + } else { client_->NotifyReadyToActivate(); + test_hooks_->NotifyReadyToActivateOnThread(this); + } + } + + void NotifyReadyToDraw() override { + client_->NotifyReadyToDraw(); + test_hooks_->NotifyReadyToDrawOnThread(this); } void BlockNotifyReadyToActivateForTesting(bool block) override { @@ -269,10 +327,6 @@ test_hooks_->UpdateAnimationState(this, has_unfinished_animation); } - base::TimeDelta LowFrequencyAnimationInterval() const override { - return test_hooks_->LowFrequencyAnimationInterval(); - } - private: TestHooks* test_hooks_; bool block_notify_ready_to_activate_for_testing_; @@ -361,7 +415,8 @@ LayerTreeHostClientForTesting* client, const LayerTreeSettings& settings, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) { + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { scoped_ptr<LayerTreeHostForTesting> layer_tree_host( new LayerTreeHostForTesting(test_hooks, client, settings)); if (impl_task_runner.get()) { @@ -369,10 +424,14 @@ ThreadProxyForTest::Create(test_hooks, layer_tree_host.get(), main_task_runner, - impl_task_runner)); + impl_task_runner, + external_begin_frame_source.Pass())); } else { layer_tree_host->InitializeForTesting(SingleThreadProxy::Create( - layer_tree_host.get(), client, main_task_runner)); + layer_tree_host.get(), + client, + main_task_runner, + external_begin_frame_source.Pass())); } return layer_tree_host.Pass(); } @@ -417,6 +476,7 @@ LayerTreeTest::LayerTreeTest() : output_surface_(nullptr), + external_begin_frame_source_(nullptr), beginning_(false), end_when_begin_returns_(false), timed_out_(false), @@ -546,13 +606,22 @@ void LayerTreeTest::DoBeginTest() { client_ = LayerTreeHostClientForTesting::Create(this); + scoped_ptr<ExternalBeginFrameSourceForTest> external_begin_frame_source; + if (settings_.use_external_begin_frame_source && + settings_.throttle_frame_production) { + external_begin_frame_source.reset( + new ExternalBeginFrameSourceForTest(settings_.refresh_rate)); + external_begin_frame_source_ = external_begin_frame_source.get(); + } + DCHECK(!impl_thread_ || impl_thread_->message_loop_proxy().get()); layer_tree_host_ = LayerTreeHostForTesting::Create( this, client_.get(), settings_, base::MessageLoopProxy::current(), - impl_thread_ ? impl_thread_->message_loop_proxy() : NULL); + impl_thread_ ? impl_thread_->message_loop_proxy() : NULL, + external_begin_frame_source.Pass()); ASSERT_TRUE(layer_tree_host_); started_ = true; @@ -675,6 +744,7 @@ // Spend less time waiting for BeginFrame because the output is // mocked out. settings_.refresh_rate = 200.0; + settings_.background_animation_rate = 200.0; settings_.impl_side_painting = impl_side_painting; InitializeSettings(&settings_); @@ -720,6 +790,12 @@ output_surface->capabilities().delegated_rendering); } output_surface_ = output_surface.get(); + + if (settings_.use_external_begin_frame_source && + settings_.throttle_frame_production) { + DCHECK(external_begin_frame_source_); + DCHECK(external_begin_frame_source_->is_ready()); + } return output_surface.Pass(); }
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 36d669c..531e28d 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h
@@ -17,6 +17,7 @@ } namespace cc { +class ExternalBeginFrameSourceForTest; class FakeLayerTreeHostClient; class FakeOutputSurface; class LayerImpl; @@ -57,6 +58,8 @@ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) {} virtual void SwapBuffersCompleteOnThread(LayerTreeHostImpl* host_impl) {} virtual void UpdateVisibleTilesOnThread(LayerTreeHostImpl* host_impl) {} + virtual void NotifyReadyToActivateOnThread(LayerTreeHostImpl* host_impl) {} + virtual void NotifyReadyToDrawOnThread(LayerTreeHostImpl* host_impl) {} virtual void AnimateLayers(LayerTreeHostImpl* host_impl, base::TimeTicks monotonic_time) {} virtual void UpdateAnimationState(LayerTreeHostImpl* host_impl, @@ -84,7 +87,6 @@ virtual void DidDeferCommit() {} virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* host_impl, bool visible) {} - virtual base::TimeDelta LowFrequencyAnimationInterval() const; virtual void ScheduleComposite() {} // Hooks for SchedulerClient. @@ -209,6 +211,7 @@ scoped_ptr<LayerTreeHostClientForTesting> client_; scoped_ptr<LayerTreeHost> layer_tree_host_; FakeOutputSurface* output_surface_; + ExternalBeginFrameSourceForTest* external_begin_frame_source_; bool beginning_; bool end_when_begin_returns_; @@ -236,12 +239,15 @@ } \ class SingleThreadDirectNoImplNeedsSemicolon##TEST_FIXTURE_NAME {} -#define SINGLE_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ - SINGLE_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ +#define SINGLE_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) \ TEST_F(TEST_FIXTURE_NAME, RunSingleThread_DirectRenderer_ImplSidePaint) { \ RunTest(false, false, true); \ } \ - class SingleThreadDirectNeedsSemicolon##TEST_FIXTURE_NAME {} + class SingleThreadDirectImplNeedsSemicolon##TEST_FIXTURE_NAME {} + +#define SINGLE_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ + SINGLE_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ + SINGLE_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) #define SINGLE_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) \ TEST_F(TEST_FIXTURE_NAME, \ @@ -250,34 +256,44 @@ } \ class SingleThreadDelegatingNoImplNeedsSemicolon##TEST_FIXTURE_NAME {} +#define SINGLE_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) \ + TEST_F(TEST_FIXTURE_NAME, \ + RunSingleThread_DelegatingRenderer_ImplSidePaint) { \ + RunTest(false, true, true); \ + } \ + class SingleThreadDelegatingImplNeedsSemicolon##TEST_FIXTURE_NAME {} + #define SINGLE_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ SINGLE_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ - TEST_F(TEST_FIXTURE_NAME, \ - RunSingleThread_DelegatingRenderer_ImplSidePaint) { \ - RunTest(false, true, true); \ - } \ - class SingleThreadDelegatingNeedsSemicolon##TEST_FIXTURE_NAME {} - -#define SINGLE_THREAD_TEST_F(TEST_FIXTURE_NAME) \ - SINGLE_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME); \ - SINGLE_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) + SINGLE_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) #define SINGLE_THREAD_NOIMPL_TEST_F(TEST_FIXTURE_NAME) \ SINGLE_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ SINGLE_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) +#define SINGLE_THREAD_IMPL_TEST_F(TEST_FIXTURE_NAME) \ + SINGLE_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME); \ + SINGLE_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) + +#define SINGLE_THREAD_TEST_F(TEST_FIXTURE_NAME) \ + SINGLE_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME); \ + SINGLE_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) + #define MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) \ TEST_F(TEST_FIXTURE_NAME, RunMultiThread_DirectRenderer_MainThreadPaint) { \ RunTest(true, false, false); \ } \ class MultiThreadDirectNoImplNeedsSemicolon##TEST_FIXTURE_NAME {} -#define MULTI_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ - MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ +#define MULTI_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) \ TEST_F(TEST_FIXTURE_NAME, RunMultiThread_DirectRenderer_ImplSidePaint) { \ RunTest(true, false, true); \ } \ - class MultiThreadDirectNeedsSemicolon##TEST_FIXTURE_NAME {} + class MultiThreadDirectImplNeedsSemicolon##TEST_FIXTURE_NAME {} + +#define MULTI_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ + MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ + MULTI_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) #define MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) \ TEST_F(TEST_FIXTURE_NAME, \ @@ -286,17 +302,24 @@ } \ class MultiThreadDelegatingNoImplNeedsSemicolon##TEST_FIXTURE_NAME {} -#define MULTI_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ - MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ +#define MULTI_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) \ TEST_F(TEST_FIXTURE_NAME, RunMultiThread_DelegatingRenderer_ImplSidePaint) { \ RunTest(true, true, true); \ } \ - class MultiThreadDelegatingNeedsSemicolon##TEST_FIXTURE_NAME {} + class MultiThreadDelegatingImplNeedsSemicolon##TEST_FIXTURE_NAME {} + +#define MULTI_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ + MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ + MULTI_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) #define MULTI_THREAD_NOIMPL_TEST_F(TEST_FIXTURE_NAME) \ MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) +#define MULTI_THREAD_IMPL_TEST_F(TEST_FIXTURE_NAME) \ + MULTI_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME); \ + MULTI_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) + #define MULTI_THREAD_TEST_F(TEST_FIXTURE_NAME) \ MULTI_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME); \ MULTI_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) @@ -306,6 +329,10 @@ SINGLE_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) +#define SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) \ + SINGLE_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME); \ + MULTI_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) + #define SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ SINGLE_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME); \ MULTI_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME) @@ -315,6 +342,11 @@ SINGLE_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) +#define SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_IMPL_TEST_F( \ + TEST_FIXTURE_NAME) \ + SINGLE_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME); \ + MULTI_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) + #define SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) \ SINGLE_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME); \ MULTI_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME) @@ -323,6 +355,10 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME); \ SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_NOIMPL_TEST_F(TEST_FIXTURE_NAME) +#define SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(TEST_FIXTURE_NAME) \ + SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME); \ + SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_IMPL_TEST_F(TEST_FIXTURE_NAME) + #define SINGLE_AND_MULTI_THREAD_TEST_F(TEST_FIXTURE_NAME) \ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TEST_FIXTURE_NAME); \ SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_TEST_F(TEST_FIXTURE_NAME)
diff --git a/cc/test/scheduler_test_common.cc b/cc/test/scheduler_test_common.cc index ef4cefd..61ef0cf 100644 --- a/cc/test/scheduler_test_common.cc +++ b/cc/test/scheduler_test_common.cc
@@ -84,7 +84,7 @@ scheduler->primary_frame_source_internal_ = TestBackToBackBeginFrameSource::Create(now_src_, test_task_runner_); return scheduler->primary_frame_source_internal_.get(); - } else if (scheduler->settings_.begin_frame_scheduling_enabled) { + } else if (scheduler->settings_.use_external_begin_frame_source) { return SchedulerFrameSourcesConstructor::ConstructPrimaryFrameSource( scheduler); } else { @@ -128,12 +128,14 @@ int layer_tree_host_id, const scoped_refptr<OrderedSimpleTaskRunner>& test_task_runner, base::PowerMonitor* power_monitor, - TestSchedulerFrameSourcesConstructor* frame_sources_constructor) + TestSchedulerFrameSourcesConstructor* frame_sources_constructor, + scoped_ptr<BeginFrameSource> external_begin_frame_source) : Scheduler(client, scheduler_settings, layer_tree_host_id, test_task_runner, power_monitor, + external_begin_frame_source.Pass(), frame_sources_constructor), now_src_(now_src) { }
diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h index d9cb3cf..ea6b2ef 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h
@@ -162,16 +162,19 @@ const SchedulerSettings& scheduler_settings, int layer_tree_host_id, const scoped_refptr<OrderedSimpleTaskRunner>& task_runner, - base::PowerMonitor* power_monitor) { + base::PowerMonitor* power_monitor, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { TestSchedulerFrameSourcesConstructor frame_sources_constructor( task_runner.get(), now_src.get()); - return make_scoped_ptr(new TestScheduler(now_src, - client, - scheduler_settings, - layer_tree_host_id, - task_runner, - power_monitor, - &frame_sources_constructor)); + return make_scoped_ptr(new TestScheduler( + now_src, + client, + scheduler_settings, + layer_tree_host_id, + task_runner, + power_monitor, + &frame_sources_constructor, + external_begin_frame_source.Pass())); } // Extra test helper functionality @@ -195,7 +198,8 @@ int layer_tree_host_id, const scoped_refptr<OrderedSimpleTaskRunner>& test_task_runner, base::PowerMonitor* power_monitor, - TestSchedulerFrameSourcesConstructor* frame_sources_constructor); + TestSchedulerFrameSourcesConstructor* frame_sources_constructor, + scoped_ptr<BeginFrameSource> external_begin_frame_source); scoped_refptr<TestNowSource> now_src_; };
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index af7eb5b..f85d4e7 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc
@@ -32,6 +32,7 @@ #include "cc/layers/render_surface.h" #include "cc/resources/prioritized_resource_manager.h" #include "cc/resources/ui_resource_request.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_host_impl.h" @@ -71,12 +72,15 @@ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, const LayerTreeSettings& settings, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) { + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { DCHECK(main_task_runner.get()); DCHECK(impl_task_runner.get()); scoped_ptr<LayerTreeHost> layer_tree_host(new LayerTreeHost( client, shared_bitmap_manager, gpu_memory_buffer_manager, settings)); - layer_tree_host->InitializeThreaded(main_task_runner, impl_task_runner); + layer_tree_host->InitializeThreaded(main_task_runner, + impl_task_runner, + external_begin_frame_source.Pass()); return layer_tree_host.Pass(); } @@ -86,11 +90,13 @@ SharedBitmapManager* shared_bitmap_manager, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, const LayerTreeSettings& settings, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { scoped_ptr<LayerTreeHost> layer_tree_host(new LayerTreeHost( client, shared_bitmap_manager, gpu_memory_buffer_manager, settings)); layer_tree_host->InitializeSingleThreaded(single_thread_client, - main_task_runner); + main_task_runner, + external_begin_frame_source.Pass()); return layer_tree_host.Pass(); } @@ -139,16 +145,23 @@ void LayerTreeHost::InitializeThreaded( scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) { - InitializeProxy( - ThreadProxy::Create(this, main_task_runner, impl_task_runner)); + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { + InitializeProxy(ThreadProxy::Create(this, + main_task_runner, + impl_task_runner, + external_begin_frame_source.Pass())); } void LayerTreeHost::InitializeSingleThreaded( LayerTreeHostSingleThreadClient* single_thread_client, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { InitializeProxy( - SingleThreadProxy::Create(this, single_thread_client, main_task_runner)); + SingleThreadProxy::Create(this, + single_thread_client, + main_task_runner, + external_begin_frame_source.Pass())); } void LayerTreeHost::InitializeForTesting(scoped_ptr<Proxy> proxy_for_testing) { @@ -960,7 +973,6 @@ size_t LayerTreeHost::CalculateMemoryForRenderSurfaces( const RenderSurfaceLayerList& update_list) { size_t readback_bytes = 0; - size_t max_background_texture_bytes = 0; size_t contents_texture_bytes = 0; // Start iteration at 1 to skip the root surface as it does not have a texture @@ -974,17 +986,16 @@ RGBA_8888); contents_texture_bytes += bytes; - if (render_surface_layer->background_filters().IsEmpty()) + if (render_surface_layer->background_filters().IsEmpty() && + render_surface_layer->uses_default_blend_mode()) continue; - if (bytes > max_background_texture_bytes) - max_background_texture_bytes = bytes; if (!readback_bytes) { readback_bytes = Resource::MemorySizeBytes(device_viewport_size_, RGBA_8888); } } - return readback_bytes + max_background_texture_bytes + contents_texture_bytes; + return readback_bytes + contents_texture_bytes; } void LayerTreeHost::PaintMasksForRenderSurface(Layer* render_surface_layer,
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index a56a6cd..7820cc2 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h
@@ -48,6 +48,7 @@ namespace cc { class AnimationRegistrar; +class BeginFrameSource; class HeadsUpDisplayLayer; class Layer; class LayerTreeHostImpl; @@ -92,7 +93,8 @@ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, const LayerTreeSettings& settings, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); static scoped_ptr<LayerTreeHost> CreateSingleThreaded( LayerTreeHostClient* client, @@ -100,7 +102,8 @@ SharedBitmapManager* shared_bitmap_manager, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, const LayerTreeSettings& settings, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); virtual ~LayerTreeHost(); void SetLayerTreeHostClientReady(); @@ -318,10 +321,12 @@ const LayerTreeSettings& settings); void InitializeThreaded( scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); void InitializeSingleThreaded( LayerTreeHostSingleThreadClient* single_thread_client, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); void InitializeForTesting(scoped_ptr<Proxy> proxy_for_testing); void SetOutputSurfaceLostForTesting(bool is_lost) { output_surface_lost_ = is_lost;
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index fd15b52..0ff6608 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc
@@ -1883,6 +1883,7 @@ render_surface->SetDrawOpacityIsAnimating(animating_opacity_to_target); animating_opacity_to_target = false; layer_draw_properties.opacity = 1.f; + layer_draw_properties.blend_mode = SkXfermode::kSrcOver_Mode; layer_draw_properties.opacity_is_animating = animating_opacity_to_target; layer_draw_properties.screen_space_opacity_is_animating = animating_opacity_to_screen; @@ -2007,6 +2008,7 @@ layer_draw_properties.screen_space_transform_is_animating = animating_transform_to_screen; layer_draw_properties.opacity = accumulated_draw_opacity; + layer_draw_properties.blend_mode = layer->blend_mode(); layer_draw_properties.opacity_is_animating = animating_opacity_to_target; layer_draw_properties.screen_space_opacity_is_animating = animating_opacity_to_screen;
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index e0436ba..b40f66d 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -1392,6 +1392,36 @@ EXPECT_EQ(gfx::Rect(), parent->drawable_content_rect()); } +TEST_F(LayerTreeHostCommonTest, RenderSurfaceForBlendMode) { + scoped_refptr<Layer> parent = Layer::Create(); + scoped_refptr<LayerWithForcedDrawsContent> child = + make_scoped_refptr(new LayerWithForcedDrawsContent()); + + scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost()); + host->SetRootLayer(parent); + + const gfx::Transform identity_matrix; + const SkXfermode::Mode blend_mode = SkXfermode::kMultiply_Mode; + SetLayerPropertiesForTesting(child.get(), identity_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(10, 10), true, false); + + parent->AddChild(child); + child->SetBlendMode(blend_mode); + + RenderSurfaceLayerList render_surface_layer_list; + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + parent.get(), parent->bounds(), &render_surface_layer_list); + LayerTreeHostCommon::CalculateDrawProperties(&inputs); + + // Since the child layer has a blend mode other than normal, it should get + // its own render surface. Also, layer's draw_properties should contain the + // default blend mode, since the render surface becomes responsible for + // applying the blend mode. + ASSERT_TRUE(child->render_surface()); + EXPECT_EQ(1U, child->render_surface()->layer_list().size()); + EXPECT_EQ(SkXfermode::kSrcOver_Mode, child->draw_properties().blend_mode); +} + TEST_F(LayerTreeHostCommonTest, ForceRenderSurface) { scoped_refptr<Layer> parent = Layer::Create(); scoped_refptr<Layer> render_surface1 = Layer::Create();
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index d2041ac..696da7a 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -29,6 +29,7 @@ #include "cc/debug/rendering_stats_instrumentation.h" #include "cc/debug/traced_value.h" #include "cc/input/page_scale_animation.h" +#include "cc/input/scroll_elasticity_helper.h" #include "cc/input/top_controls_manager.h" #include "cc/layers/append_quads_data.h" #include "cc/layers/heads_up_display_layer_impl.h" @@ -173,77 +174,6 @@ } // namespace -class LayerTreeHostImplTimeSourceAdapter : public TimeSourceClient { - public: - static scoped_ptr<LayerTreeHostImplTimeSourceAdapter> Create( - LayerTreeHostImpl* layer_tree_host_impl, - scoped_refptr<DelayBasedTimeSource> time_source) { - return make_scoped_ptr( - new LayerTreeHostImplTimeSourceAdapter(layer_tree_host_impl, - time_source)); - } - ~LayerTreeHostImplTimeSourceAdapter() override { - time_source_->SetClient(NULL); - time_source_->SetActive(false); - } - - void OnTimerTick() override { - // In single threaded mode we attempt to simulate changing the current - // thread by maintaining a fake thread id. When we switch from one - // thread to another, we construct DebugScopedSetXXXThread objects that - // update the thread id. This lets DCHECKS that ensure we're on the - // right thread to work correctly in single threaded mode. The problem - // here is that the timer tasks are run via the message loop, and when - // they run, we've had no chance to construct a DebugScopedSetXXXThread - // object. The result is that we report that we're running on the main - // thread. In multi-threaded mode, this timer is run on the compositor - // thread, so to keep this consistent in single-threaded mode, we'll - // construct a DebugScopedSetImplThread object. There is no need to do - // this in multi-threaded mode since the real thread id's will be - // correct. In fact, setting fake thread id's interferes with the real - // thread id's and causes breakage. - scoped_ptr<DebugScopedSetImplThread> set_impl_thread; - if (!layer_tree_host_impl_->proxy()->HasImplThread()) { - set_impl_thread.reset( - new DebugScopedSetImplThread(layer_tree_host_impl_->proxy())); - } - - layer_tree_host_impl_->Animate( - layer_tree_host_impl_->CurrentBeginFrameArgs().frame_time); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking(true); - bool start_ready_animations = true; - layer_tree_host_impl_->UpdateAnimationState(start_ready_animations); - - if (layer_tree_host_impl_->pending_tree()) { - layer_tree_host_impl_->pending_tree()->UpdateDrawProperties(); - layer_tree_host_impl_->ManageTiles(); - } - - layer_tree_host_impl_->ResetCurrentBeginFrameArgsForNextFrame(); - } - - void SetActive(bool active) { - if (active != time_source_->Active()) - time_source_->SetActive(active); - } - - bool Active() const { return time_source_->Active(); } - - private: - LayerTreeHostImplTimeSourceAdapter( - LayerTreeHostImpl* layer_tree_host_impl, - scoped_refptr<DelayBasedTimeSource> time_source) - : layer_tree_host_impl_(layer_tree_host_impl), - time_source_(time_source) { - time_source_->SetClient(this); - } - - LayerTreeHostImpl* layer_tree_host_impl_; - scoped_refptr<DelayBasedTimeSource> time_source_; - - DISALLOW_COPY_AND_ASSIGN(LayerTreeHostImplTimeSourceAdapter); -}; - LayerTreeHostImpl::FrameData::FrameData() : contains_incomplete_tile(false), has_no_damage(false) {} @@ -274,8 +204,7 @@ SharedBitmapManager* shared_bitmap_manager, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, int id) - : BeginFrameSourceMixIn(), - client_(client), + : client_(client), proxy_(proxy), use_gpu_rasterization_(false), input_handler_client_(NULL), @@ -352,6 +281,8 @@ input_handler_client_->WillShutdown(); input_handler_client_ = NULL; } + if (scroll_elasticity_helper_) + scroll_elasticity_helper_.reset(); // The layer trees must be destroyed before the layer tree host. We've // made a contract with our animation controllers that the registrar @@ -521,6 +452,12 @@ new LatencyInfoSwapPromiseMonitor(latency, NULL, this)); } +ScrollElasticityHelper* LayerTreeHostImpl::CreateScrollElasticityHelper() { + DCHECK(!scroll_elasticity_helper_); + scroll_elasticity_helper_.reset(new ScrollElasticityHelper(this)); + return scroll_elasticity_helper_.get(); +} + void LayerTreeHostImpl::QueueSwapPromiseForMainThreadScrollUpdate( scoped_ptr<SwapPromise> swap_promise) { swap_promises_for_main_thread_scroll_update_.push_back(swap_promise.Pass()); @@ -974,28 +911,6 @@ input_handler_client_->MainThreadHasStoppedFlinging(); } -void LayerTreeHostImpl::UpdateBackgroundAnimateTicking( - bool should_background_tick) { - DCHECK(proxy_->IsImplThread()); - if (should_background_tick) - DCHECK(active_tree_->root_layer()); - - bool enabled = should_background_tick && needs_animate_layers(); - - // Lazily create the time_source adapter so that we can vary the interval for - // testing. - if (!time_source_client_adapter_) { - time_source_client_adapter_ = LayerTreeHostImplTimeSourceAdapter::Create( - this, - DelayBasedTimeSource::Create( - LowFrequencyAnimationInterval(), - proxy_->HasImplThread() ? proxy_->ImplThreadTaskRunner() - : proxy_->MainThreadTaskRunner())); - } - - time_source_client_adapter_->SetActive(enabled); -} - void LayerTreeHostImpl::DidAnimateScrollOffset() { client_->SetNeedsCommitOnImplThread(); client_->RenewTreePriority(); @@ -1271,7 +1186,8 @@ } void LayerTreeHostImpl::GetPictureLayerImplPairs( - std::vector<PictureLayerImpl::Pair>* layer_pairs) const { + std::vector<PictureLayerImpl::Pair>* layer_pairs, + bool need_valid_tile_priorities) const { DCHECK(layer_pairs->empty()); for (std::vector<PictureLayerImpl*>::const_iterator it = picture_layers_.begin(); @@ -1279,24 +1195,25 @@ ++it) { PictureLayerImpl* layer = *it; - // TODO(vmpstr): Iterators and should handle this instead. crbug.com/381704 - if (!layer->HasValidTilePriorities()) + if (!layer->IsOnActiveOrPendingTree() || + (need_valid_tile_priorities && !layer->HasValidTilePriorities())) continue; PictureLayerImpl* twin_layer = layer->GetPendingOrActiveTwinLayer(); // Ignore the twin layer when tile priorities are invalid. - // TODO(vmpstr): Iterators should handle this instead. crbug.com/381704 - if (twin_layer && !twin_layer->HasValidTilePriorities()) + if (need_valid_tile_priorities && twin_layer && + !twin_layer->HasValidTilePriorities()) twin_layer = NULL; // If the current tree is ACTIVE_TREE, then always generate a layer_pair. // If current tree is PENDING_TREE, then only generate a layer_pair if // there is no twin layer. if (layer->GetTree() == ACTIVE_TREE) { - DCHECK(!twin_layer || twin_layer->GetTree() == PENDING_TREE); + DCHECK_IMPLIES(twin_layer, twin_layer->GetTree() == PENDING_TREE); layer_pairs->push_back(PictureLayerImpl::Pair(layer, twin_layer)); } else if (!twin_layer) { + DCHECK(layer->GetTree() == PENDING_TREE); layer_pairs->push_back(PictureLayerImpl::Pair(NULL, layer)); } } @@ -1306,7 +1223,7 @@ TreePriority tree_priority) { TRACE_EVENT0("cc", "LayerTreeHostImpl::BuildRasterQueue"); picture_layer_pairs_.clear(); - GetPictureLayerImplPairs(&picture_layer_pairs_); + GetPictureLayerImplPairs(&picture_layer_pairs_, true); queue->Build(picture_layer_pairs_, tree_priority); } @@ -1314,7 +1231,7 @@ TreePriority tree_priority) { TRACE_EVENT0("cc", "LayerTreeHostImpl::BuildEvictionQueue"); picture_layer_pairs_.clear(); - GetPictureLayerImplPairs(&picture_layer_pairs_); + GetPictureLayerImplPairs(&picture_layer_pairs_, false); queue->Build(picture_layer_pairs_, tree_priority); } @@ -1327,6 +1244,10 @@ client_->NotifyReadyToActivate(); } +void LayerTreeHostImpl::NotifyReadyToDraw() { + client_->NotifyReadyToDraw(); +} + void LayerTreeHostImpl::NotifyTileStateChanged(const Tile* tile) { TRACE_EVENT0("cc", "LayerTreeHostImpl::NotifyTileStateChanged"); @@ -1437,10 +1358,6 @@ client_->SetNeedsRedrawRectOnImplThread(damage_rect); } -void LayerTreeHostImpl::BeginFrame(const BeginFrameArgs& args) { - CallOnBeginFrame(args); -} - void LayerTreeHostImpl::DidSwapBuffers() { client_->DidSwapBuffersOnImplThread(); } @@ -1543,7 +1460,8 @@ if (!settings_.impl_side_painting && debug_state_.continuous_painting) { const RenderingStats& stats = rendering_stats_instrumentation_->GetRenderingStats(); - paint_time_counter_->SavePaintTime(stats.main_stats.paint_time); + paint_time_counter_->SavePaintTime( + stats.begin_main_frame_to_commit_duration.GetLastTimeDelta()); } bool is_new_trace; @@ -1678,13 +1596,6 @@ return true; } -void LayerTreeHostImpl::OnNeedsBeginFramesChange(bool enable) { - if (output_surface_) - output_surface_->SetNeedsBeginFrame(enable); - else - DCHECK(!enable); -} - void LayerTreeHostImpl::WillBeginImplFrame(const BeginFrameArgs& args) { // Sample the frame time now. This time will be used for updating animations // when we draw. @@ -1883,13 +1794,10 @@ // TODO(hendrikw): This requires a different metric when we commit directly // to the active tree. See crbug.com/429311. paint_time_counter_->SavePaintTime( - stats.impl_stats.commit_to_activate_duration.GetLastTimeDelta() + - stats.impl_stats.draw_duration.GetLastTimeDelta()); + stats.commit_to_activate_duration.GetLastTimeDelta() + + stats.draw_duration.GetLastTimeDelta()); } - if (time_source_client_adapter_ && time_source_client_adapter_->Active()) - DCHECK(active_tree_->root_layer()); - scoped_ptr<PageScaleAnimation> page_scale_animation = active_tree_->TakePageScaleAnimation(); if (page_scale_animation) { @@ -2195,7 +2103,7 @@ // TODO(brianderson): Don't use a hard-coded parent draw time. base::TimeDelta parent_draw_time = - (!settings_.begin_frame_scheduling_enabled && + (!settings_.use_external_begin_frame_source && output_surface_->capabilities().adjust_deadline_for_parent) ? BeginFrameArgs::DefaultEstimatedParentDrawTime() : base::TimeDelta(); @@ -2472,9 +2380,10 @@ new_target.SetToMax(gfx::ScrollOffset()); new_target.SetToMin(layer_impl->MaxScrollOffset()); - curve->UpdateTarget(animation->TrimTimeToCurrentIteration( - CurrentBeginFrameArgs().frame_time), - new_target); + curve->UpdateTarget( + animation->TrimTimeToCurrentIteration( + CurrentBeginFrameArgs().frame_time).InSecondsF(), + new_target); return ScrollStarted; } @@ -2628,21 +2537,19 @@ CurrentlyScrollingLayer() != OuterViewportScrollLayer()) return false; - if (InnerViewportScrollLayer()->MaxScrollOffset().y() > 0) - return true; - - if (OuterViewportScrollLayer() && - OuterViewportScrollLayer()->MaxScrollOffset().y() > 0) + if (active_tree()->TotalScrollOffset().y() < + active_tree()->TotalMaxScrollOffset().y()) return true; return false; } -bool LayerTreeHostImpl::ScrollBy(const gfx::Point& viewport_point, - const gfx::Vector2dF& scroll_delta) { +InputHandlerScrollResult LayerTreeHostImpl::ScrollBy( + const gfx::Point& viewport_point, + const gfx::Vector2dF& scroll_delta) { TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBy"); if (!CurrentlyScrollingLayer()) - return false; + return InputHandlerScrollResult(); gfx::Vector2dF pending_delta = scroll_delta; gfx::Vector2dF unused_root_delta; @@ -2763,15 +2670,14 @@ accumulated_root_overscroll_.set_x(0); if (did_scroll_y) accumulated_root_overscroll_.set_y(0); - accumulated_root_overscroll_ += unused_root_delta; - bool did_overscroll = !unused_root_delta.IsZero(); - if (did_overscroll && input_handler_client_) { - input_handler_client_->DidOverscroll( - viewport_point, accumulated_root_overscroll_, unused_root_delta); - } - return did_scroll_content || did_scroll_top_controls; + InputHandlerScrollResult scroll_result; + scroll_result.did_scroll = did_scroll_content || did_scroll_top_controls; + scroll_result.did_overscroll_root = !unused_root_delta.IsZero(); + scroll_result.accumulated_root_overscroll = accumulated_root_overscroll_; + scroll_result.unused_scroll_delta = unused_root_delta; + return scroll_result; } // This implements scrolling by page as described here: @@ -3197,10 +3103,8 @@ iter != copy.end(); ++iter) (*iter).second->ActivateAnimations(); -} -base::TimeDelta LayerTreeHostImpl::LowFrequencyAnimationInterval() const { - return base::TimeDelta::FromSeconds(1); + SetNeedsAnimate(); } std::string LayerTreeHostImpl::LayerTreeAsJson() const { @@ -3287,10 +3191,6 @@ BeginFrameArgs::DefaultInterval()); } -void LayerTreeHostImpl::AsValueInto(base::debug::TracedValue* value) const { - return AsValueWithFrameInto(NULL, value); -} - scoped_refptr<base::debug::ConvertableToTraceFormat> LayerTreeHostImpl::AsValue() const { return AsValueWithFrame(NULL); @@ -3304,6 +3204,10 @@ return state; } +void LayerTreeHostImpl::AsValueInto(base::debug::TracedValue* value) const { + return AsValueWithFrameInto(NULL, value); +} + void LayerTreeHostImpl::AsValueWithFrameInto( FrameData* frame, base::debug::TracedValue* state) const {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index 157fe00..24cb730 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h
@@ -31,7 +31,6 @@ #include "cc/quads/render_pass.h" #include "cc/resources/resource_provider.h" #include "cc/resources/tile_manager.h" -#include "cc/scheduler/begin_frame_source.h" #include "cc/scheduler/draw_result.h" #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkColor.h" @@ -45,7 +44,6 @@ class EvictionTilePriorityQueue; class FrameRateCounter; class LayerImpl; -class LayerTreeHostImplTimeSourceAdapter; class LayerTreeImpl; class MemoryHistory; class PageScaleAnimation; @@ -56,6 +54,7 @@ class RenderPassDrawQuad; class RenderingStatsInstrumentation; class ResourcePool; +class ScrollElasticityHelper; class ScrollbarLayerImplBase; class TextureMailboxDeleter; class TopControlsManager; @@ -76,6 +75,7 @@ virtual void DidSwapBuffersCompleteOnImplThread() = 0; virtual void OnCanDrawStateChanged(bool can_draw) = 0; virtual void NotifyReadyToActivate() = 0; + virtual void NotifyReadyToDraw() = 0; // Please call these 3 functions through // LayerTreeHostImpl's SetNeedsRedraw(), SetNeedsRedrawRect() and // SetNeedsAnimate(). @@ -112,7 +112,6 @@ public OutputSurfaceClient, public TopControlsManagerClient, public ScrollbarAnimationControllerClient, - public BeginFrameSourceMixIn, public base::SupportsWeakPtr<LayerTreeHostImpl> { public: static scoped_ptr<LayerTreeHostImpl> Create( @@ -125,9 +124,6 @@ int id); ~LayerTreeHostImpl() override; - // BeginFrameSourceMixIn implementation - void OnNeedsBeginFramesChange(bool needs_begin_frames) override; - // InputHandler implementation void BindToClient(InputHandlerClient* client) override; InputHandler::ScrollStatus ScrollBegin( @@ -136,8 +132,9 @@ InputHandler::ScrollStatus ScrollAnimated( const gfx::Point& viewport_point, const gfx::Vector2dF& scroll_delta) override; - bool ScrollBy(const gfx::Point& viewport_point, - const gfx::Vector2dF& scroll_delta) override; + InputHandlerScrollResult ScrollBy( + const gfx::Point& viewport_point, + const gfx::Vector2dF& scroll_delta) override; bool ScrollVerticallyByPage(const gfx::Point& viewport_point, ScrollDirection direction) override; void SetRootLayerScrollOffsetDelegate( @@ -156,6 +153,7 @@ bool HaveTouchEventHandlersAt(const gfx::Point& viewport_port) override; scoped_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor( ui::LatencyInfo* latency) override; + ScrollElasticityHelper* CreateScrollElasticityHelper() override; // TopControlsManagerClient implementation. void SetControlsTopOffset(float offset) override; @@ -188,7 +186,6 @@ virtual void UpdateAnimationState(bool start_ready_animations); void ActivateAnimations(); void MainThreadHasStoppedFlinging(); - void UpdateBackgroundAnimateTicking(bool should_background_tick); void DidAnimateScrollOffset(); void SetViewportDamage(const gfx::Rect& damage_rect); @@ -234,6 +231,7 @@ // TileManagerClient implementation. const std::vector<PictureLayerImpl*>& GetPictureLayers() const override; void NotifyReadyToActivate() override; + void NotifyReadyToDraw() override; void NotifyTileStateChanged(const Tile* tile) override; void BuildRasterQueue(RasterTilePriorityQueue* queue, TreePriority tree_priority) override; @@ -251,8 +249,6 @@ void CommitVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) override; void SetNeedsRedrawRect(const gfx::Rect& rect) override; - void BeginFrame(const BeginFrameArgs& args) override; - void SetExternalDrawConstraints( const gfx::Transform& transform, const gfx::Rect& viewport, @@ -329,6 +325,8 @@ virtual void SetVisible(bool visible); bool visible() const { return visible_; } + bool AnimationsAreVisible() { return visible() && CanDraw(); } + void SetNeedsCommit() { client_->SetNeedsCommitOnImplThread(); } void SetNeedsRedraw(); @@ -424,7 +422,7 @@ return begin_impl_frame_interval_; } - void AsValueInto(base::debug::TracedValue* value) const override; + void AsValueInto(base::debug::TracedValue* value) const; void AsValueWithFrameInto(FrameData* frame, base::debug::TracedValue* value) const; scoped_refptr<base::debug::ConvertableToTraceFormat> AsValue() const; @@ -473,8 +471,8 @@ void RegisterPictureLayerImpl(PictureLayerImpl* layer); void UnregisterPictureLayerImpl(PictureLayerImpl* layer); - void GetPictureLayerImplPairs( - std::vector<PictureLayerImpl::Pair>* layers) const; + void GetPictureLayerImplPairs(std::vector<PictureLayerImpl::Pair>* layers, + bool need_valid_tile_priorities) const; void SetTopControlsLayoutHeight(float height); @@ -506,8 +504,6 @@ // Virtual for testing. virtual void AnimateLayers(base::TimeTicks monotonic_time); - virtual base::TimeDelta LowFrequencyAnimationInterval() const; - const AnimationRegistrar::AnimationControllerMap& active_animation_controllers() const { return animation_registrar_->active_animation_controllers(); @@ -627,6 +623,10 @@ int scroll_layer_id_when_mouse_over_scrollbar_; ScopedPtrVector<SwapPromise> swap_promises_for_main_thread_scroll_update_; + // An object to implement the ScrollElasticityHelper interface and + // hold all state related to elasticity. May be NULL if never requested. + scoped_ptr<ScrollElasticityHelper> scroll_elasticity_helper_; + bool tile_priorities_dirty_; // The optional delegate for the root layer scroll offset. @@ -646,9 +646,6 @@ scoped_ptr<PageScaleAnimation> page_scale_animation_; - // This is used for ticking animations slowly when hidden. - scoped_ptr<LayerTreeHostImplTimeSourceAdapter> time_source_client_adapter_; - scoped_ptr<FrameRateCounter> fps_counter_; scoped_ptr<PaintTimeCounter> paint_time_counter_; scoped_ptr<MemoryHistory> memory_history_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 5d2d939..17179cd 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -124,6 +124,7 @@ did_notify_ready_to_activate_ = true; host_impl_->ActivateSyncTree(); } + void NotifyReadyToDraw() override {} void SetNeedsRedrawOnImplThread() override { did_request_redraw_ = true; } void SetNeedsRedrawRectOnImplThread(const gfx::Rect& damage_rect) override { did_request_redraw_ = true; @@ -772,27 +773,40 @@ host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); // Trying to scroll to the left/top will not succeed. - EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0))); - EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10))); - EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, -10))); + EXPECT_FALSE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0)).did_scroll); + EXPECT_FALSE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10)).did_scroll); + EXPECT_FALSE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, -10)).did_scroll); // Scrolling to the right/bottom will succeed. - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 0))); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10))); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 10))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 0)).did_scroll); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 10)).did_scroll); // Scrolling to left/top will now succeed. - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0))); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10))); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, -10))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0)).did_scroll); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10)).did_scroll); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, -10)).did_scroll); // Scrolling diagonally against an edge will succeed. - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, -10))); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0))); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 10))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, -10)).did_scroll); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0)).did_scroll); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 10)).did_scroll); // Trying to scroll more than the available space will also succeed. - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(5000, 5000))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(5000, 5000)).did_scroll); } TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) { @@ -2574,6 +2588,14 @@ EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); host_impl_->ScrollBy(gfx::Point(), scroll_delta); + + // scrolling down at the max extents no longer hides the top controls + EXPECT_EQ(0.f, + settings_.top_controls_height - + host_impl_->active_tree()->total_top_controls_content_offset()); + + // forcefully hide the top controls by 25px + host_impl_->top_controls_manager()->ScrollBy(scroll_delta); host_impl_->ScrollEnd(); EXPECT_EQ(scroll_delta.y(), @@ -3705,6 +3727,7 @@ } TEST_F(LayerTreeHostImplTest, OverscrollRoot) { + InputHandlerScrollResult scroll_result; SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f); @@ -3714,38 +3737,105 @@ // In-bounds scrolling does not affect overscroll. EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel)); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)); + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_FALSE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll()); // Overscroll events are reflected immediately. - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 50)); + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 50)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll()); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); // In-bounds scrolling resets accumulated overscroll for the scrolled axes. - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -50)); + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -50)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_FALSE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, 0), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10)); + EXPECT_FALSE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 0)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 0)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_FALSE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-15, 0)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-15, 0)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(-5, 0), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(-5, -10), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 60)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 60)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(-5, 10), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, -60)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, -60)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll()); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); // Overscroll accumulates within the scope of ScrollBegin/ScrollEnd as long // as no scroll occurs. - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20)); + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20)); + EXPECT_FALSE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, -30), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20)); + EXPECT_FALSE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, -50), host_impl_->accumulated_root_overscroll()); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + // Overscroll resets on valid scroll. - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)); + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_FALSE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, 0), host_impl_->accumulated_root_overscroll()); - host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20)); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + + scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20)); + EXPECT_TRUE(scroll_result.did_scroll); + EXPECT_TRUE(scroll_result.did_overscroll_root); + EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta); EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll()); + EXPECT_EQ(scroll_result.accumulated_root_overscroll, + host_impl_->accumulated_root_overscroll()); + host_impl_->ScrollEnd(); } @@ -5981,7 +6071,8 @@ pile_tile_size, content_layer_bounds)); scoped_ptr<FakePictureLayerImpl> scoped_content_layer = - FakePictureLayerImpl::CreateWithPile(host_impl_->pending_tree(), 3, pile); + FakePictureLayerImpl::CreateWithRasterSource(host_impl_->pending_tree(), + 3, pile); LayerImpl* content_layer = scoped_content_layer.get(); scrolling_layer->AddChild(scoped_content_layer.Pass()); content_layer->SetBounds(content_layer_bounds); @@ -6527,7 +6618,7 @@ gfx::Vector2d scroll_delta(0, -2); EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta)); + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll); // The grand child should have scrolled up to its limit. scroll_info = host_impl_->ProcessScrollDeltas(); @@ -6537,7 +6628,7 @@ // The child should have received the bubbled delta, but the locked // scrolling layer should remain set as the grand child. - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta)); + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll); scroll_info = host_impl_->ProcessScrollDeltas(); ASSERT_EQ(2u, scroll_info->scrolls.size()); ExpectContains(*scroll_info, grand_child->id(), scroll_delta); @@ -6547,7 +6638,7 @@ // The first |ScrollBy| after the fling should re-lock the scrolling // layer to the first layer that scrolled, which is the child. EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin()); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta)); + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll); EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child); // The child should have scrolled up to its limit. @@ -6557,7 +6648,7 @@ ExpectContains(*scroll_info, child->id(), scroll_delta + scroll_delta); // As the locked layer is at it's limit, no further scrolling can occur. - EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), scroll_delta)); + EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll); EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child); host_impl_->ScrollEnd(); } @@ -6955,7 +7046,8 @@ // Scrolling normally should not trigger any forwarding. EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll); host_impl_->ScrollEnd(); EXPECT_EQ(0, set_needs_commit_count); @@ -6967,7 +7059,8 @@ scroll_layer->SetHaveScrollEventHandlers(true); EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll); host_impl_->ScrollEnd(); EXPECT_EQ(0, set_needs_commit_count); @@ -7022,7 +7115,8 @@ // Scroll just the top controls and verify that the scroll succeeds. const float residue = 10; float offset = top_controls_height_ - residue; - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(-offset, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), scroll_layer->TotalScrollOffset().ToString()); @@ -7030,7 +7124,8 @@ // Scroll across the boundary const float content_scroll = 20; offset = residue + content_scroll; - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(-top_controls_height_, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF(0, content_scroll).ToString(), @@ -7038,7 +7133,8 @@ // Now scroll back to the top of the content offset = -content_scroll; - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(-top_controls_height_, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), @@ -7046,13 +7142,15 @@ // And scroll the top controls completely into view offset = -top_controls_height_; - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), scroll_layer->TotalScrollOffset().ToString()); // And attempt to scroll past the end - EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_FALSE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), scroll_layer->TotalScrollOffset().ToString()); @@ -7076,7 +7174,8 @@ // Scroll the top controls partially. const float residue = 35; float offset = top_controls_height_ - residue; - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(-offset, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF().ToString(), scroll_layer->TotalScrollOffset().ToString()); @@ -7144,7 +7243,8 @@ // Scroll the top controls partially. const float residue = 15; float offset = top_controls_height_ - residue; - EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); EXPECT_EQ(-offset, host_impl_->top_controls_manager()->ControlsTopOffset()); EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(), scroll_layer->TotalScrollOffset().ToString()); @@ -7184,6 +7284,68 @@ EXPECT_FALSE(host_impl_->top_controls_manager()->animation()); } +TEST_F(LayerTreeHostImplWithTopControlsTest, + TopControlsScrollDeltaInOverScroll) { + // test varifies that the overscroll delta should not have accumulated in + // the top controls if we do a hide and show without releasing finger. + + LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 200)); + host_impl_->SetViewportSize(gfx::Size(100, 100)); + host_impl_->top_controls_manager()->UpdateTopControlsState(BOTH, SHOWN, + false); + DrawFrame(); + + EXPECT_EQ(InputHandler::ScrollStarted, + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); + EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset()); + + float offset = 50; + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); + EXPECT_EQ(-offset, host_impl_->top_controls_manager()->ControlsTopOffset()); + EXPECT_EQ(gfx::Vector2dF().ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); + EXPECT_EQ(gfx::Vector2dF(0, offset).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset)).did_scroll); + + // Should have fully scrolled + EXPECT_EQ(gfx::Vector2dF(0, scroll_layer->MaxScrollOffset().y()).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + float overscrollamount = 10; + + // Overscroll the content + EXPECT_FALSE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, overscrollamount)) + .did_scroll); + EXPECT_EQ(gfx::Vector2dF(0, 2 * offset).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + EXPECT_EQ(gfx::Vector2dF(0, overscrollamount).ToString(), + host_impl_->accumulated_root_overscroll().ToString()); + + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -2 * offset)) + .did_scroll); + EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + EXPECT_EQ(-offset, host_impl_->top_controls_manager()->ControlsTopOffset()); + + EXPECT_TRUE( + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -offset)).did_scroll); + EXPECT_EQ(gfx::Vector2dF(0, 0).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + // Top controls should be fully visible + EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset()); + + host_impl_->ScrollEnd(); +} + class LayerTreeHostImplVirtualViewportTest : public LayerTreeHostImplTest { public: void SetupVirtualViewportLayers(const gfx::Size& content_size, @@ -7398,7 +7560,7 @@ LayerImpl* pending_layer = pending_tree->root_layer(); std::vector<PictureLayerImpl::Pair> layer_pairs; - host_impl_->GetPictureLayerImplPairs(&layer_pairs); + host_impl_->GetPictureLayerImplPairs(&layer_pairs, true); EXPECT_EQ(1u, layer_pairs.size()); EXPECT_EQ(pending_layer, layer_pairs[0].pending); EXPECT_EQ(nullptr, layer_pairs[0].active); @@ -7415,7 +7577,7 @@ host_impl_->CreatePendingTree(); layer_pairs.clear(); - host_impl_->GetPictureLayerImplPairs(&layer_pairs); + host_impl_->GetPictureLayerImplPairs(&layer_pairs, true); EXPECT_EQ(1u, layer_pairs.size()); EXPECT_EQ(active_layer, layer_pairs[0].active); EXPECT_EQ(pending_layer, layer_pairs[0].pending); @@ -7424,7 +7586,7 @@ host_impl_->ActivateSyncTree(); layer_pairs.clear(); - host_impl_->GetPictureLayerImplPairs(&layer_pairs); + host_impl_->GetPictureLayerImplPairs(&layer_pairs, true); EXPECT_EQ(1u, layer_pairs.size()); EXPECT_EQ(active_layer, layer_pairs[0].active); EXPECT_EQ(nullptr, layer_pairs[0].pending); @@ -7438,7 +7600,7 @@ LayerImpl* new_pending_layer = pending_tree->root_layer()->children()[0]; layer_pairs.clear(); - host_impl_->GetPictureLayerImplPairs(&layer_pairs); + host_impl_->GetPictureLayerImplPairs(&layer_pairs, true); EXPECT_EQ(2u, layer_pairs.size()); // The pair ordering is flaky, so make it consistent.
diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc index 7a9ef21..148769e 100644 --- a/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -52,25 +52,29 @@ const uint32 kUseMasks = 1 << 0; const uint32 kUseAntialiasing = 1 << 1; const uint32 kUseColorMatrix = 1 << 2; +const uint32 kForceShaders = 1 << 3; class LayerTreeHostBlendingPixelTest : public LayerTreePixelTest { public: - LayerTreeHostBlendingPixelTest() { + LayerTreeHostBlendingPixelTest() + : force_antialiasing_(false), force_blending_with_shaders_(false) { pixel_comparator_.reset(new FuzzyPixelOffByOneComparator(true)); } virtual void InitializeSettings(LayerTreeSettings* settings) override { settings->force_antialiasing = force_antialiasing_; + settings->force_blending_with_shaders = force_blending_with_shaders_; } protected: void RunBlendingWithRootPixelTestType(PixelTestType type) { - const int kLaneWidth = 15; - const int kLaneHeight = kBlendModesCount * kLaneWidth; - const int kRootSize = (kBlendModesCount + 2) * kLaneWidth; + const int kLaneWidth = 2; + const int kLaneHeight = kLaneWidth; + const int kRootWidth = (kBlendModesCount + 2) * kLaneWidth; + const int kRootHeight = 2 * kLaneWidth + kLaneHeight; scoped_refptr<SolidColorLayer> background = - CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), kCSSOrange); + CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSOrange); // Orange child layers will blend with the green background for (int i = 0; i < kBlendModesCount; ++i) { @@ -88,15 +92,16 @@ } void RunBlendingWithTransparentPixelTestType(PixelTestType type) { - const int kLaneWidth = 15; - const int kLaneHeight = kBlendModesCount * kLaneWidth; - const int kRootSize = (kBlendModesCount + 2) * kLaneWidth; + const int kLaneWidth = 2; + const int kLaneHeight = 3 * kLaneWidth; + const int kRootWidth = (kBlendModesCount + 2) * kLaneWidth; + const int kRootHeight = 2 * kLaneWidth + kLaneHeight; scoped_refptr<SolidColorLayer> root = - CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), kCSSBrown); + CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSBrown); scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( - gfx::Rect(0, kLaneWidth * 2, kRootSize, kLaneWidth), kCSSOrange); + gfx::Rect(0, kLaneWidth * 2, kRootWidth, kLaneWidth), kCSSOrange); root->AddChild(background); background->SetIsRootForIsolatedGroup(true); @@ -138,7 +143,7 @@ } void SetupMaskLayer(scoped_refptr<Layer> layer) { - const int kMaskOffset = 5; + const int kMaskOffset = 2; gfx::Size bounds = layer->bounds(); scoped_refptr<ImageLayer> mask = ImageLayer::Create(); mask->SetIsDrawable(true); @@ -162,23 +167,22 @@ void SetupColorMatrix(scoped_refptr<Layer> layer) { FilterOperations filter_operations; - filter_operations.Append(FilterOperation::CreateSepiaFilter(1.f)); + filter_operations.Append(FilterOperation::CreateSepiaFilter(.001f)); layer->SetFilters(filter_operations); } - void CreateBlendingColorLayers(int width, - int height, + void CreateBlendingColorLayers(int lane_width, + int lane_height, scoped_refptr<Layer> background, RenderPassOptions flags) { const int kLanesCount = kBlendModesCount + 4; - int lane_width = width / kLanesCount; const SkColor kMiscOpaqueColor = 0xffc86464; const SkColor kMiscTransparentColor = 0x80c86464; const SkXfermode::Mode kCoeffBlendMode = SkXfermode::kScreen_Mode; const SkXfermode::Mode kShaderBlendMode = SkXfermode::kColorBurn_Mode; // add vertical lanes with each of the blend modes for (int i = 0; i < kLanesCount; ++i) { - gfx::Rect child_rect(i * lane_width, 0, lane_width, height); + gfx::Rect child_rect(i * lane_width, 0, lane_width, lane_height); SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; float opacity = 1.f; SkColor color = kMiscOpaqueColor; @@ -216,7 +220,9 @@ void RunBlendingWithRenderPass(PixelTestType type, const base::FilePath::CharType* expected_path, RenderPassOptions flags) { - const int kRootSize = 400; + const int kLaneWidth = 8; + const int kLaneHeight = kLaneWidth * kCSSTestColorsCount; + const int kRootSize = kLaneHeight; scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), SK_ColorWHITE); @@ -226,47 +232,25 @@ background->SetIsRootForIsolatedGroup(true); root->AddChild(background); - CreateBlendingColorLayers(kRootSize, kRootSize, background.get(), flags); + CreateBlendingColorLayers(kLaneWidth, kLaneHeight, background.get(), flags); this->impl_side_painting_ = false; this->force_antialiasing_ = (flags & kUseAntialiasing); + this->force_blending_with_shaders_ = (flags & kForceShaders); if ((flags & kUseAntialiasing) && (type == PIXEL_TEST_GL)) { - // Anti aliasing causes differences up to 7 pixels at the edges. - // Several pixels have 9 units difference on the alpha channel. - int large_error_allowed = (flags & kUseMasks) ? 7 : 9; + // Anti aliasing causes differences up to 8 pixels at the edges. + int large_error_allowed = 8; // Blending results might differ with one pixel. int small_error_allowed = 1; // Most of the errors are one pixel errors. - float percentage_pixels_small_error = (flags & kUseMasks) ? 7.7f : 12.1f; - // Because of anti-aliasing, around 3% of pixels (at the edges) have + float percentage_pixels_small_error = 13.1f; + // Because of anti-aliasing, around 10% of pixels (at the edges) have // bigger errors (from small_error_allowed + 1 to large_error_allowed). - float percentage_pixels_error = (flags & kUseMasks) ? 12.4f : 15.f; + float percentage_pixels_error = 22.5f; // The average error is still close to 1. - float average_error_allowed_in_bad_pixels = - (flags & kUseMasks) ? 1.3f : 1.f; + float average_error_allowed_in_bad_pixels = 1.4f; - // The sepia filter generates more small errors, but the number of large - // errors remains around 3%. - if (flags & kUseColorMatrix) { - percentage_pixels_small_error = (flags & kUseMasks) ? 14.0f : 26.f; - percentage_pixels_error = (flags & kUseMasks) ? 18.5f : 29.f; - average_error_allowed_in_bad_pixels = (flags & kUseMasks) ? 0.9f : 0.7f; - } - - pixel_comparator_.reset( - new FuzzyPixelComparator(false, // discard_alpha - percentage_pixels_error, - percentage_pixels_small_error, - average_error_allowed_in_bad_pixels, - large_error_allowed, - small_error_allowed)); - } else if ((flags & kUseColorMatrix) && (type == PIXEL_TEST_GL)) { - float percentage_pixels_error = 100.f; - float percentage_pixels_small_error = 0.f; - float average_error_allowed_in_bad_pixels = 1.f; - int large_error_allowed = 2; - int small_error_allowed = 0; pixel_comparator_.reset( new FuzzyPixelComparator(false, // discard_alpha percentage_pixels_error, @@ -279,7 +263,8 @@ RunPixelTest(type, root, base::FilePath(expected_path)); } - bool force_antialiasing_ = false; + bool force_antialiasing_; + bool force_blending_with_shaders_; }; TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_GL) { @@ -291,12 +276,13 @@ } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithBackgroundFilter) { - const int kLaneWidth = 15; - const int kLaneHeight = kBlendModesCount * kLaneWidth; - const int kRootSize = (kBlendModesCount + 2) * kLaneWidth; + const int kLaneWidth = 2; + const int kLaneHeight = kLaneWidth; + const int kRootWidth = (kBlendModesCount + 2) * kLaneWidth; + const int kRootHeight = 2 * kLaneWidth + kLaneHeight; scoped_refptr<SolidColorLayer> background = - CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), kCSSOrange); + CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSOrange); // Orange child layers have a background filter set and they will blend with // the green background @@ -377,60 +363,111 @@ TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_GL) { RunBlendingWithRenderPass(PIXEL_TEST_GL, - FILE_PATH_LITERAL("blending_render_pass_cm.png"), + FILE_PATH_LITERAL("blending_render_pass.png"), kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_Software) { RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE, - FILE_PATH_LITERAL("blending_render_pass_cm.png"), + FILE_PATH_LITERAL("blending_render_pass.png"), kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_GL) { RunBlendingWithRenderPass(PIXEL_TEST_GL, - FILE_PATH_LITERAL("blending_render_pass_cm.png"), + FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_Software) { RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE, - FILE_PATH_LITERAL("blending_render_pass_cm.png"), + FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrix_GL) { - RunBlendingWithRenderPass( - PIXEL_TEST_GL, - FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), - kUseMasks | kUseColorMatrix); + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrix_Software) { - RunBlendingWithRenderPass( - PIXEL_TEST_SOFTWARE, - FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), - kUseMasks | kUseColorMatrix); + RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrixAA_GL) { - RunBlendingWithRenderPass( - PIXEL_TEST_GL, - FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), - kUseMasks | kUseAntialiasing | kUseColorMatrix); + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrixAA_Software) { + RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix); +} + +// Tests for render passes forcing shaders for all the blend modes. +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShaders_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass.png"), + kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersAA_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseAntialiasing | kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassShadersWithMask_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassShadersWithMaskAA_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing | kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassShadersColorMatrix_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseColorMatrix | kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassShadersColorMatrixAA_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseAntialiasing | kUseColorMatrix | kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassShadersWithMaskColorMatrix_GL) { + RunBlendingWithRenderPass(PIXEL_TEST_GL, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseColorMatrix | kForceShaders); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassShadersWithMaskColorMatrixAA_GL) { RunBlendingWithRenderPass( - PIXEL_TEST_SOFTWARE, - FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), - kUseMasks | kUseAntialiasing | kUseColorMatrix); + PIXEL_TEST_GL, FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix | kForceShaders); } } // namespace
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc index 40fa2c3..dc8cc4a 100644 --- a/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -136,8 +136,8 @@ blur->SetBackgroundFilters(filters); #if defined(OS_WIN) - // Windows has 153 pixels off by at most 2: crbug.com/225027 - float percentage_pixels_large_error = 0.3825f; // 153px / (200*200) + // Windows has 116 pixels off by at most 2: crbug.com/225027 + float percentage_pixels_large_error = 0.3f; // 116px / (200*200), rounded up float percentage_pixels_small_error = 0.0f; float average_error_allowed_in_bad_pixels = 1.f; int large_error_allowed = 2; @@ -277,6 +277,63 @@ RunPixelTestType(PIXEL_TEST_SOFTWARE); } +TEST_F(LayerTreeHostFiltersPixelTest, ImageFilterScaled_GL) { + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE); + + gfx::Rect rect(50, 50, 100, 100); + + static const int kInset = 3; + for (int i = 0; !rect.IsEmpty(); ++i) { + scoped_refptr<SolidColorLayer> layer = + CreateSolidColorLayer(rect, (i & 1) ? SK_ColorWHITE : SK_ColorRED); + + gfx::Transform transform; + transform.Translate(rect.width() / 2.0, rect.height() / 2.0); + transform.RotateAboutZAxis(30.0); + transform.Translate(-rect.width() / 2.0, -rect.height() / 2.0); + layer->SetTransform(transform); + + background->AddChild(layer); + + rect.Inset(kInset, kInset); + } + + scoped_refptr<SolidColorLayer> filter = + CreateSolidColorLayer(gfx::Rect(100, 0, 100, 200), SK_ColorTRANSPARENT); + + background->AddChild(filter); + + // Apply a scale to |background| so that we can see any scaling artifacts that + // may appear. + gfx::Transform background_transform; + static float scale = 1.1f; + background_transform.Scale(scale, scale); + background->SetTransform(background_transform); + + FilterOperations filters; + filters.Append(FilterOperation::CreateGrayscaleFilter(1.0f)); + filter->SetBackgroundFilters(filters); + +#if defined(OS_WIN) + // Windows has 153 pixels off by at most 2: crbug.com/225027 + float percentage_pixels_large_error = 0.3825f; // 153px / (200*200) + float percentage_pixels_small_error = 0.0f; + float average_error_allowed_in_bad_pixels = 1.f; + int large_error_allowed = 2; + int small_error_allowed = 0; + pixel_comparator_.reset(new FuzzyPixelComparator( + true, // discard_alpha + percentage_pixels_large_error, percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, large_error_allowed, + small_error_allowed)); +#endif + + // TODO(hendrikw): Enable test in software as well: crbug.com/432157 + RunPixelTest(PIXEL_TEST_GL, background, + base::FilePath(FILE_PATH_LITERAL("filter_on_scaled_layer.png"))); +} + } // namespace } // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 0859d86..5c66790 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -67,6 +67,151 @@ class LayerTreeHostTest : public LayerTreeTest {}; +// Test if the LTHI receives ReadyToActivate notifications from the TileManager +// when no raster tasks get scheduled. +class LayerTreeHostTestReadyToActivateEmpty : public LayerTreeHostTest { + public: + LayerTreeHostTestReadyToActivateEmpty() + : did_notify_ready_to_activate_(false), + all_tiles_required_for_activation_are_ready_to_draw_(false), + required_for_activation_count_(0) {} + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { + const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers(); + required_for_activation_count_ = 0; + for (const auto& layer : layers) { + FakePictureLayerImpl* fake_layer = + static_cast<FakePictureLayerImpl*>(layer); + required_for_activation_count_ += + fake_layer->CountTilesRequiredForActivation(); + } + } + + void NotifyReadyToActivateOnThread(LayerTreeHostImpl* impl) override { + did_notify_ready_to_activate_ = true; + const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers(); + all_tiles_required_for_activation_are_ready_to_draw_ = true; + for (const auto& layer : layers) { + if (!layer->AllTilesRequiredForActivationAreReadyToDraw()) + all_tiles_required_for_activation_are_ready_to_draw_ = false; + } + EndTest(); + } + + void AfterTest() override { + EXPECT_TRUE(did_notify_ready_to_activate_); + EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_); + EXPECT_EQ(size_t(0), required_for_activation_count_); + } + + protected: + bool did_notify_ready_to_activate_; + bool all_tiles_required_for_activation_are_ready_to_draw_; + size_t required_for_activation_count_; +}; + +SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToActivateEmpty); + +// Test if the LTHI receives ReadyToActivate notifications from the TileManager +// when some raster tasks flagged as REQUIRED_FOR_ACTIVATION got scheduled. +class LayerTreeHostTestReadyToActivateNonEmpty + : public LayerTreeHostTestReadyToActivateEmpty { + public: + void SetupTree() override { + client_.set_fill_with_nonsolid_color(true); + scoped_refptr<FakePictureLayer> root_layer = + FakePictureLayer::Create(&client_); + root_layer->SetBounds(gfx::Size(1024, 1024)); + root_layer->SetIsDrawable(true); + + layer_tree_host()->SetRootLayer(root_layer); + LayerTreeHostTest::SetupTree(); + } + + void AfterTest() override { + EXPECT_TRUE(did_notify_ready_to_activate_); + EXPECT_TRUE(all_tiles_required_for_activation_are_ready_to_draw_); + EXPECT_LE(size_t(1), required_for_activation_count_); + } + + private: + FakeContentLayerClient client_; +}; + +SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToActivateNonEmpty); + +// Test if the LTHI receives ReadyToDraw notifications from the TileManager when +// no raster tasks get scheduled. +class LayerTreeHostTestReadyToDrawEmpty : public LayerTreeHostTest { + public: + LayerTreeHostTestReadyToDrawEmpty() + : did_notify_ready_to_draw_(false), + all_tiles_required_for_draw_are_ready_to_draw_(false), + required_for_draw_count_(0) {} + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void NotifyReadyToDrawOnThread(LayerTreeHostImpl* impl) override { + did_notify_ready_to_draw_ = true; + const std::vector<PictureLayerImpl*>& layers = impl->GetPictureLayers(); + all_tiles_required_for_draw_are_ready_to_draw_ = true; + for (const auto& layer : layers) { + if (!layer->AllTilesRequiredForDrawAreReadyToDraw()) + all_tiles_required_for_draw_are_ready_to_draw_ = false; + FakePictureLayerImpl* fake_layer = + static_cast<FakePictureLayerImpl*>(layer); + required_for_draw_count_ += fake_layer->CountTilesRequiredForDraw(); + } + + EndTest(); + } + + void AfterTest() override { + EXPECT_TRUE(did_notify_ready_to_draw_); + EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_); + EXPECT_EQ(size_t(0), required_for_draw_count_); + } + + protected: + bool did_notify_ready_to_draw_; + bool all_tiles_required_for_draw_are_ready_to_draw_; + size_t required_for_draw_count_; +}; + +SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToDrawEmpty); + +// Test if the LTHI receives ReadyToDraw notifications from the TileManager when +// some raster tasks flagged as REQUIRED_FOR_DRAW got scheduled. +class LayerTreeHostTestReadyToDrawNonEmpty + : public LayerTreeHostTestReadyToDrawEmpty { + public: + void SetupTree() override { + client_.set_fill_with_nonsolid_color(true); + scoped_refptr<FakePictureLayer> root_layer = + FakePictureLayer::Create(&client_); + root_layer->SetBounds(gfx::Size(1024, 1024)); + root_layer->SetIsDrawable(true); + + layer_tree_host()->SetRootLayer(root_layer); + LayerTreeHostTest::SetupTree(); + } + + void AfterTest() override { + EXPECT_TRUE(did_notify_ready_to_draw_); + EXPECT_TRUE(all_tiles_required_for_draw_are_ready_to_draw_); + EXPECT_LE(size_t(1), required_for_draw_count_); + } + + private: + FakeContentLayerClient client_; +}; + +// Note: With this test setup, we only get tiles flagged as REQUIRED_FOR_DRAW in +// single threaded mode. +SINGLE_THREAD_IMPL_TEST_F(LayerTreeHostTestReadyToDrawNonEmpty); + // Two setNeedsCommits in a row should lead to at least 1 commit and at least 1 // draw with frame 0. class LayerTreeHostTestSetNeedsCommit1 : public LayerTreeHostTest { @@ -1969,7 +2114,8 @@ shared_bitmap_manager.get(), NULL, settings, - base::MessageLoopProxy::current()); + base::MessageLoopProxy::current(), + nullptr); client.SetLayerTreeHost(host.get()); host->Composite(base::TimeTicks::Now()); @@ -1991,7 +2137,8 @@ shared_bitmap_manager.get(), NULL, settings, - base::MessageLoopProxy::current()); + base::MessageLoopProxy::current(), + nullptr); client.SetLayerTreeHost(host.get()); host->Composite(base::TimeTicks::Now()); @@ -2013,7 +2160,8 @@ shared_bitmap_manager.get(), NULL, settings, - base::MessageLoopProxy::current()); + base::MessageLoopProxy::current(), + nullptr); client.SetLayerTreeHost(host.get()); host->Composite(base::TimeTicks::Now()); @@ -2036,7 +2184,8 @@ shared_bitmap_manager.get(), NULL, settings, - base::MessageLoopProxy::current()); + base::MessageLoopProxy::current(), + nullptr); client.SetLayerTreeHost(host.get()); host->Composite(base::TimeTicks::Now()); @@ -2225,7 +2374,7 @@ class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest { public: void InitializeSettings(LayerTreeSettings* settings) override { - settings->begin_frame_scheduling_enabled = true; + settings->use_external_begin_frame_source = true; } void BeginTest() override { @@ -2253,7 +2402,7 @@ : public LayerTreeHostTest { public: void InitializeSettings(LayerTreeSettings* settings) override { - settings->begin_frame_scheduling_enabled = true; + settings->use_external_begin_frame_source = true; settings->using_synchronous_renderer_compositor = true; } @@ -2280,7 +2429,7 @@ : commit_count_(0), commit_abort_count_(0), commit_complete_count_(0) {} void InitializeSettings(LayerTreeSettings* settings) override { - settings->begin_frame_scheduling_enabled = true; + settings->use_external_begin_frame_source = true; } void BeginTest() override { PostSetNeedsCommitToMainThread(); } @@ -4739,12 +4888,12 @@ void BeginTest() override { Layer* root = layer_tree_host()->root_layer(); PictureLayer* layer = static_cast<PictureLayer*>(root->child_at(0)); - PicturePile* pile = layer->GetPicturePileForTesting(); + RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); // Verify default values. EXPECT_TRUE(root->IsSuitableForGpuRasterization()); EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); - EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); + EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); EXPECT_FALSE(layer_tree_host()->has_gpu_rasterization_trigger()); EXPECT_FALSE(layer_tree_host()->UseGpuRasterization()); @@ -4795,12 +4944,12 @@ void BeginTest() override { Layer* root = layer_tree_host()->root_layer(); PictureLayer* layer = static_cast<PictureLayer*>(root->child_at(0)); - PicturePile* pile = layer->GetPicturePileForTesting(); + RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); // Verify default values. EXPECT_TRUE(root->IsSuitableForGpuRasterization()); EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); - EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); + EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); EXPECT_FALSE(layer_tree_host()->has_gpu_rasterization_trigger()); EXPECT_FALSE(layer_tree_host()->UseGpuRasterization()); @@ -4810,8 +4959,8 @@ EXPECT_TRUE(layer_tree_host()->UseGpuRasterization()); // Content-based veto is relevant as well. - pile->SetUnsuitableForGpuRasterizationForTesting(); - EXPECT_FALSE(pile->is_suitable_for_gpu_rasterization()); + recording_source->SetUnsuitableForGpuRasterizationForTesting(); + EXPECT_FALSE(recording_source->IsSuitableForGpuRasterization()); EXPECT_FALSE(layer->IsSuitableForGpuRasterization()); // Veto will take effect when layers are updated. // The results will be verified after commit is completed below. @@ -4860,12 +5009,12 @@ void BeginTest() override { Layer* root = layer_tree_host()->root_layer(); PictureLayer* layer = static_cast<PictureLayer*>(root->child_at(0)); - PicturePile* pile = layer->GetPicturePileForTesting(); + RecordingSource* recording_source = layer->GetRecordingSourceForTesting(); // Verify default values. EXPECT_TRUE(root->IsSuitableForGpuRasterization()); EXPECT_TRUE(layer->IsSuitableForGpuRasterization()); - EXPECT_TRUE(pile->is_suitable_for_gpu_rasterization()); + EXPECT_TRUE(recording_source->IsSuitableForGpuRasterization()); EXPECT_FALSE(layer_tree_host()->has_gpu_rasterization_trigger()); // With gpu rasterization forced, gpu rasterization trigger is irrelevant. @@ -4875,8 +5024,8 @@ EXPECT_TRUE(layer_tree_host()->UseGpuRasterization()); // Content-based veto is irrelevant as well. - pile->SetUnsuitableForGpuRasterizationForTesting(); - EXPECT_FALSE(pile->is_suitable_for_gpu_rasterization()); + recording_source->SetUnsuitableForGpuRasterizationForTesting(); + EXPECT_FALSE(recording_source->IsSuitableForGpuRasterization()); EXPECT_FALSE(layer->IsSuitableForGpuRasterization()); // Veto will take effect when layers are updated. // The results will be verified after commit is completed below.
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc index cfd8a14..9c346e2 100644 --- a/cc/trees/layer_tree_host_unittest_animation.cc +++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -348,8 +348,9 @@ LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree() : active_tree_was_animated_(false) {} - base::TimeDelta LowFrequencyAnimationInterval() const override { - return base::TimeDelta::FromMilliseconds(4); + base::TimeDelta BackgroundAnimationInterval(LayerTreeHostImpl* host_impl) { + return base::TimeDelta::FromSecondsD( + 1.0 / host_impl->settings().background_animation_rate); } void BeginTest() override { @@ -409,10 +410,9 @@ FROM_HERE, base::Bind( &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree:: - UnblockActivations, - base::Unretained(this), - host_impl), - 4 * LowFrequencyAnimationInterval()); + UnblockActivations, + base::Unretained(this), host_impl), + 4 * BackgroundAnimationInterval(host_impl)); } } @@ -447,10 +447,9 @@ FROM_HERE, base::Bind( &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree:: - InitiateNextCommit, - base::Unretained(this), - host_impl), - 4 * LowFrequencyAnimationInterval()); + InitiateNextCommit, + base::Unretained(this), host_impl), + 4 * BackgroundAnimationInterval(host_impl)); } } @@ -505,10 +504,10 @@ const FloatAnimationCurve* curve = animation->curve()->ToFloatAnimationCurve(); float start_opacity = curve->GetValue(0.0); - float end_opacity = curve->GetValue(curve->Duration()); + float end_opacity = curve->GetValue(curve->Duration().InSecondsF()); float linearly_interpolated_opacity = 0.25f * end_opacity + 0.75f * start_opacity; - double time = curve->Duration() * 0.25; + double time = curve->Duration().InSecondsF() * 0.25; // If the linear timing function associated with this animation was not // picked up, then the linearly interpolated opacity would be different // because of the default ease timing function.
diff --git a/cc/trees/layer_tree_host_unittest_no_message_loop.cc b/cc/trees/layer_tree_host_unittest_no_message_loop.cc index aa5a508..a0b2bdf 100644 --- a/cc/trees/layer_tree_host_unittest_no_message_loop.cc +++ b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
@@ -13,6 +13,7 @@ #include "cc/output/output_surface.h" #include "cc/output/output_surface_client.h" #include "cc/resources/resource_provider.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/test/fake_delegated_renderer_layer.h" #include "cc/test/test_context_provider.h" #include "cc/trees/layer_tree_host.h" @@ -99,7 +100,7 @@ LayerTreeSettings settings; settings.single_thread_proxy_scheduler = false; layer_tree_host_ = LayerTreeHost::CreateSingleThreaded( - this, this, NULL, NULL, settings, NULL); + this, this, nullptr, nullptr, settings, nullptr, nullptr); layer_tree_host_->SetViewportSize(size_); layer_tree_host_->SetRootLayer(root_layer_); }
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc index e922f21..6526ff8 100644 --- a/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -8,6 +8,7 @@ #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_picture_layer.h" @@ -1091,13 +1092,6 @@ *received_stop_flinging_ = true; } - void DidOverscroll(const gfx::PointF& causal_event_viewport_point, - const gfx::Vector2dF& accumulated_overscroll, - const gfx::Vector2dF& latest_overscroll_delta) override { - if (!task_runner_->BelongsToCurrentThread()) - ADD_FAILURE() << "DidOverscroll called on wrong thread"; - } - private: base::SingleThreadTaskRunner* task_runner_; bool* received_stop_flinging_; @@ -1129,7 +1123,8 @@ NULL, settings, base::MessageLoopProxy::current(), - impl_thread.message_loop_proxy()); + impl_thread.message_loop_proxy(), + nullptr); impl_thread.message_loop_proxy() ->PostTask(FROM_HERE,
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc index 1d5cf1b..48c4133 100644 --- a/cc/trees/layer_tree_settings.cc +++ b/cc/trees/layer_tree_settings.cc
@@ -16,9 +16,11 @@ : impl_side_painting(false), allow_antialiasing(true), force_antialiasing(false), + force_blending_with_shaders(false), throttle_frame_production(true), single_thread_proxy_scheduler(true), - begin_frame_scheduling_enabled(false), + use_external_begin_frame_source(false), + forward_begin_frames_to_children(false), main_frame_before_activation_enabled(false), using_synchronous_renderer_compositor(false), disable_hi_res_timer_tasks_on_battery(false), @@ -47,6 +49,7 @@ top_controls_show_threshold(0.5f), top_controls_hide_threshold(0.5f), refresh_rate(60.0), + background_animation_rate(1.0), max_partial_texture_updates(std::numeric_limits<size_t>::max()), default_tile_size(gfx::Size(256, 256)), max_untiled_layer_size(gfx::Size(512, 512)),
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h index 74a3b0c..3097e59 100644 --- a/cc/trees/layer_tree_settings.h +++ b/cc/trees/layer_tree_settings.h
@@ -21,9 +21,11 @@ bool impl_side_painting; bool allow_antialiasing; bool force_antialiasing; + bool force_blending_with_shaders; bool throttle_frame_production; bool single_thread_proxy_scheduler; - bool begin_frame_scheduling_enabled; + bool use_external_begin_frame_source; + bool forward_begin_frames_to_children; bool main_frame_before_activation_enabled; bool using_synchronous_renderer_compositor; bool disable_hi_res_timer_tasks_on_battery; @@ -58,6 +60,7 @@ float top_controls_show_threshold; float top_controls_hide_threshold; double refresh_rate; + double background_animation_rate; size_t max_partial_texture_updates; gfx::Size default_tile_size; gfx::Size max_untiled_layer_size;
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 1af657a..f15c7ec 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc
@@ -23,15 +23,20 @@ scoped_ptr<Proxy> SingleThreadProxy::Create( LayerTreeHost* layer_tree_host, LayerTreeHostSingleThreadClient* client, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { - return make_scoped_ptr( - new SingleThreadProxy(layer_tree_host, client, main_task_runner)); + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { + return make_scoped_ptr(new SingleThreadProxy( + layer_tree_host, + client, + main_task_runner, + external_begin_frame_source.Pass())); } SingleThreadProxy::SingleThreadProxy( LayerTreeHost* layer_tree_host, LayerTreeHostSingleThreadClient* client, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) : Proxy(main_task_runner, NULL), layer_tree_host_(layer_tree_host), client_(client), @@ -43,6 +48,7 @@ commit_requested_(false), inside_synchronous_composite_(false), output_surface_creation_requested_(false), + external_begin_frame_source_(external_begin_frame_source.Pass()), weak_factory_(this) { TRACE_EVENT0("cc", "SingleThreadProxy::SingleThreadProxy"); DCHECK(Proxy::IsMainThread()); @@ -84,11 +90,13 @@ if (layer_tree_host_->settings().single_thread_proxy_scheduler && !scheduler_on_impl_thread_) { SchedulerSettings scheduler_settings(layer_tree_host_->settings()); - scheduler_on_impl_thread_ = Scheduler::Create(this, - scheduler_settings, - layer_tree_host_->id(), - MainThreadTaskRunner(), - base::PowerMonitor::Get()); + scheduler_on_impl_thread_ = + Scheduler::Create(this, + scheduler_settings, + layer_tree_host_->id(), + MainThreadTaskRunner(), + base::PowerMonitor::Get(), + external_begin_frame_source_.Pass()); scheduler_on_impl_thread_->SetCanStart(); scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); } @@ -101,7 +109,6 @@ if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); // Changing visibility could change ShouldComposite(). - UpdateBackgroundAnimateTicking(); } void SingleThreadProxy::RequestNewOutputSurface() { @@ -161,6 +168,24 @@ SetNeedsCommit(); } +void SingleThreadProxy::DoAnimate() { + // Don't animate if there is no root layer. + // TODO(mithro): Both Animate and UpdateAnimationState already have a + // "!active_tree_->root_layer()" check? + if (!layer_tree_host_impl_->active_tree()->root_layer()) { + return; + } + + layer_tree_host_impl_->Animate( + layer_tree_host_impl_->CurrentBeginFrameArgs().frame_time); + + // If animations are not visible, update the animation state now as it + // won't happen in DoComposite. + if (!layer_tree_host_impl_->AnimationsAreVisible()) { + layer_tree_host_impl_->UpdateAnimationState(true); + } +} + void SingleThreadProxy::DoCommit() { TRACE_EVENT0("cc", "SingleThreadProxy::DoCommit"); DCHECK(Proxy::IsMainThread()); @@ -202,8 +227,6 @@ layer_tree_host_impl_->CommitComplete(); - UpdateBackgroundAnimateTicking(); - #if DCHECK_IS_ON // In the single-threaded case, the scale and scroll deltas should never be // touched on the impl layer tree. @@ -212,12 +235,6 @@ DCHECK(!scroll_info->scrolls.size()); DCHECK_EQ(1.f, scroll_info->page_scale_delta); #endif - - RenderingStatsInstrumentation* stats_instrumentation = - layer_tree_host_->rendering_stats_instrumentation(); - benchmark_instrumentation::IssueMainThreadRenderingStatsEvent( - stats_instrumentation->main_thread_rendering_stats()); - stats_instrumentation->AccumulateAndClearMainThreadStats(); } if (layer_tree_host_->settings().impl_side_painting) { @@ -328,7 +345,6 @@ TRACE_EVENT1( "cc", "SingleThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); DCHECK(Proxy::IsImplThread()); - UpdateBackgroundAnimateTicking(); if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->SetCanDraw(can_draw); } @@ -340,6 +356,9 @@ scheduler_on_impl_thread_->NotifyReadyToActivate(); } +void SingleThreadProxy::NotifyReadyToDraw() { +} + void SingleThreadProxy::SetNeedsRedrawOnImplThread() { client_->ScheduleComposite(); if (scheduler_on_impl_thread_) @@ -347,7 +366,9 @@ } void SingleThreadProxy::SetNeedsAnimateOnImplThread() { - SetNeedsRedrawOnImplThread(); + client_->ScheduleComposite(); + if (scheduler_on_impl_thread_) + scheduler_on_impl_thread_->SetNeedsAnimate(); } void SingleThreadProxy::SetNeedsManageTilesOnImplThread() { @@ -420,7 +441,6 @@ weak_factory_.GetWeakPtr())); } - UpdateBackgroundAnimateTicking(); timing_history_.DidActivateSyncTree(); } @@ -498,6 +518,8 @@ layer_tree_host_impl_->SynchronouslyInitializeAllTiles(); } + DoAnimate(); + LayerTreeHostImpl::FrameData frame; DoComposite(frame_begin_time, &frame); @@ -539,12 +561,6 @@ layer_tree_host_impl_->CanDraw(); } -void SingleThreadProxy::UpdateBackgroundAnimateTicking() { - DCHECK(Proxy::IsImplThread()); - layer_tree_host_impl_->UpdateBackgroundAnimateTicking( - !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer()); -} - void SingleThreadProxy::ScheduleRequestNewOutputSurface() { if (output_surface_creation_callback_.IsCancelled() && !output_surface_creation_requested_) { @@ -572,16 +588,11 @@ // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on // CanDraw() as well. if (!ShouldComposite()) { - UpdateBackgroundAnimateTicking(); return DRAW_ABORTED_CANT_DRAW; } timing_history_.DidStartDrawing(); - layer_tree_host_impl_->Animate( - layer_tree_host_impl_->CurrentBeginFrameArgs().frame_time); - UpdateBackgroundAnimateTicking(); - draw_result = layer_tree_host_impl_->PrepareToDraw(frame); draw_frame = draw_result == DRAW_SUCCESS; if (draw_frame) @@ -629,10 +640,6 @@ return false; } -BeginFrameSource* SingleThreadProxy::ExternalBeginFrameSource() { - return layer_tree_host_impl_.get(); -} - void SingleThreadProxy::WillBeginImplFrame(const BeginFrameArgs& args) { layer_tree_host_impl_->WillBeginImplFrame(args); } @@ -742,8 +749,8 @@ void SingleThreadProxy::ScheduledActionAnimate() { TRACE_EVENT0("cc", "ScheduledActionAnimate"); - layer_tree_host_impl_->Animate( - layer_tree_host_impl_->CurrentBeginFrameArgs().frame_time); + DebugScopedSetImplThread impl(this); + DoAnimate(); } void SingleThreadProxy::ScheduledActionUpdateVisibleTiles() {
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index 5b3766b..fa300de 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h
@@ -19,6 +19,7 @@ namespace cc { +class BeginFrameSource; class ContextProvider; class LayerTreeHost; class LayerTreeHostSingleThreadClient; @@ -30,7 +31,8 @@ static scoped_ptr<Proxy> Create( LayerTreeHost* layer_tree_host, LayerTreeHostSingleThreadClient* client, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); ~SingleThreadProxy() override; // Proxy implementation @@ -59,7 +61,6 @@ bool MainFrameWillHappenForTesting() override; // SchedulerClient implementation - BeginFrameSource* ExternalBeginFrameSource() override; void WillBeginImplFrame(const BeginFrameArgs& args) override; void ScheduledActionSendBeginMainFrame() override; DrawResult ScheduledActionDrawAndSwapIfPossible() override; @@ -87,6 +88,7 @@ void DidSwapBuffersCompleteOnImplThread() override; void OnCanDrawStateChanged(bool can_draw) override; void NotifyReadyToActivate() override; + void NotifyReadyToDraw() override; void SetNeedsRedrawOnImplThread() override; void SetNeedsRedrawRectOnImplThread(const gfx::Rect& dirty_rect) override; void SetNeedsAnimateOnImplThread() override; @@ -114,10 +116,12 @@ SingleThreadProxy( LayerTreeHost* layer_tree_host, LayerTreeHostSingleThreadClient* client, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); void BeginMainFrame(); void BeginMainFrameAbortedOnImplThread(); + void DoAnimate(); void DoBeginMainFrame(const BeginFrameArgs& begin_frame_args); void DoCommit(); DrawResult DoComposite(base::TimeTicks frame_begin_time, @@ -127,7 +131,6 @@ void CommitComplete(); bool ShouldComposite() const; - void UpdateBackgroundAnimateTicking(); void ScheduleRequestNewOutputSurface(); // Accessed on main thread only. @@ -160,6 +163,8 @@ // This is the callback for the scheduled RequestNewOutputSurface. base::CancelableClosure output_surface_creation_callback_; + scoped_ptr<BeginFrameSource> external_begin_frame_source_; + base::WeakPtrFactory<SingleThreadProxy> weak_factory_; DISALLOW_COPY_AND_ASSIGN(SingleThreadProxy);
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index 14e2181..0f7a2a3 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc
@@ -48,22 +48,27 @@ scoped_ptr<Proxy> ThreadProxy::Create( LayerTreeHost* layer_tree_host, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) { - return make_scoped_ptr( - new ThreadProxy(layer_tree_host, main_task_runner, impl_task_runner)); + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) { + return make_scoped_ptr(new ThreadProxy(layer_tree_host, + main_task_runner, + impl_task_runner, + external_begin_frame_source.Pass())); } ThreadProxy::ThreadProxy( LayerTreeHost* layer_tree_host, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source) : Proxy(main_task_runner, impl_task_runner), main_thread_only_vars_unsafe_(this, layer_tree_host->id()), main_thread_or_blocked_vars_unsafe_(layer_tree_host), compositor_thread_vars_unsafe_( this, layer_tree_host->id(), - layer_tree_host->rendering_stats_instrumentation()) { + layer_tree_host->rendering_stats_instrumentation(), + external_begin_frame_source.Pass()) { TRACE_EVENT0("cc", "ThreadProxy::ThreadProxy"); DCHECK(IsMainThread()); DCHECK(this->layer_tree_host()); @@ -99,7 +104,8 @@ ThreadProxy::CompositorThreadOnly::CompositorThreadOnly( ThreadProxy* proxy, int layer_tree_host_id, - RenderingStatsInstrumentation* rendering_stats_instrumentation) + RenderingStatsInstrumentation* rendering_stats_instrumentation, + scoped_ptr<BeginFrameSource> external_begin_frame_source) : layer_tree_host_id(layer_tree_host_id), contents_texture_manager(NULL), commit_completion_event(NULL), @@ -113,6 +119,7 @@ base::TimeDelta::FromMilliseconds( kSmoothnessTakesPriorityExpirationDelay * 1000)), timing_history(rendering_stats_instrumentation), + external_begin_frame_source(external_begin_frame_source.Pass()), weak_factory(proxy) { } @@ -176,18 +183,9 @@ TRACE_EVENT0("cc", "ThreadProxy::SetVisibleOnImplThread"); impl().layer_tree_host_impl->SetVisible(visible); impl().scheduler->SetVisible(visible); - UpdateBackgroundAnimateTicking(); completion->Signal(); } -void ThreadProxy::UpdateBackgroundAnimateTicking() { - bool should_background_tick = - !impl().scheduler->WillDrawIfNeeded() && - impl().layer_tree_host_impl->active_tree()->root_layer(); - impl().layer_tree_host_impl->UpdateBackgroundAnimateTicking( - should_background_tick); -} - void ThreadProxy::DidLoseOutputSurface() { TRACE_EVENT0("cc", "ThreadProxy::DidLoseOutputSurface"); DCHECK(IsMainThread()); @@ -345,10 +343,6 @@ base::Bind(&ThreadProxy::DidCompleteSwapBuffers, main_thread_weak_ptr_)); } -BeginFrameSource* ThreadProxy::ExternalBeginFrameSource() { - return impl().layer_tree_host_impl.get(); -} - void ThreadProxy::WillBeginImplFrame(const BeginFrameArgs& args) { impl().layer_tree_host_impl->WillBeginImplFrame(args); } @@ -358,7 +352,6 @@ "cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw); DCHECK(IsImplThread()); impl().scheduler->SetCanDraw(can_draw); - UpdateBackgroundAnimateTicking(); } void ThreadProxy::NotifyReadyToActivate() { @@ -366,6 +359,11 @@ impl().scheduler->NotifyReadyToActivate(); } +void ThreadProxy::NotifyReadyToDraw() { + TRACE_EVENT0("cc", "ThreadProxy::NotifyReadyToDraw"); + impl().scheduler->NotifyReadyToDraw(); +} + void ThreadProxy::SetNeedsCommitOnImplThread() { TRACE_EVENT0("cc", "ThreadProxy::SetNeedsCommitOnImplThread"); DCHECK(IsImplThread()); @@ -851,12 +849,6 @@ &completion, queue.release())); completion.Wait(); - - RenderingStatsInstrumentation* stats_instrumentation = - layer_tree_host()->rendering_stats_instrumentation(); - benchmark_instrumentation::IssueMainThreadRenderingStatsEvent( - stats_instrumentation->main_thread_rendering_stats()); - stats_instrumentation->AccumulateAndClearMainThreadStats(); } layer_tree_host()->CommitComplete(); @@ -933,9 +925,22 @@ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionAnimate"); DCHECK(IsImplThread()); + // Don't animate if there is no root layer. + // TODO(mithro): Both Animate and UpdateAnimationState already have a + // "!active_tree_->root_layer()" check? + if (!impl().layer_tree_host_impl->active_tree()->root_layer()) { + return; + } + impl().animation_time = impl().layer_tree_host_impl->CurrentBeginFrameArgs().frame_time; impl().layer_tree_host_impl->Animate(impl().animation_time); + + // If animations are not visible, update the state now as + // ScheduledActionDrawAndSwapIfPossible will never be called. + if (!impl().layer_tree_host_impl->AnimationsAreVisible()) { + impl().layer_tree_host_impl->UpdateAnimationState(true); + } } void ThreadProxy::ScheduledActionCommit() { @@ -979,8 +984,6 @@ SetInputThrottledUntilCommitOnImplThread(false); - UpdateBackgroundAnimateTicking(); - impl().next_frame_is_newly_committed_frame = true; impl().timing_history.DidCommit(); @@ -1149,13 +1152,14 @@ impl().layer_tree_host_impl = layer_tree_host()->CreateLayerTreeHostImpl(this); SchedulerSettings scheduler_settings(layer_tree_host()->settings()); - impl().scheduler = Scheduler::Create(this, - scheduler_settings, - impl().layer_tree_host_id, - ImplThreadTaskRunner(), - base::PowerMonitor::Get()); + impl().scheduler = Scheduler::Create( + this, + scheduler_settings, + impl().layer_tree_host_id, + ImplThreadTaskRunner(), + base::PowerMonitor::Get(), + impl().external_begin_frame_source.Pass()); impl().scheduler->SetVisible(impl().layer_tree_host_impl->visible()); - impl_thread_weak_ptr_ = impl().weak_factory.GetWeakPtr(); completion->Signal(); } @@ -1213,7 +1217,6 @@ layer_tree_host()->DeleteContentsTexturesOnImplThread( impl().layer_tree_host_impl->resource_provider()); impl().current_resource_update_controller = nullptr; - impl().layer_tree_host_impl->SetNeedsBeginFrames(false); impl().scheduler = nullptr; impl().layer_tree_host_impl = nullptr; impl().weak_factory.InvalidateWeakPtrs(); @@ -1358,8 +1361,6 @@ impl().completion_event_for_commit_held_on_tree_activation = NULL; } - UpdateBackgroundAnimateTicking(); - impl().timing_history.DidActivateSyncTree(); }
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h index 2f3e7c5..adccf72 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h
@@ -25,6 +25,7 @@ namespace cc { +class BeginFrameSource; class ContextProvider; class InputHandlerClient; class LayerTreeHost; @@ -40,7 +41,8 @@ static scoped_ptr<Proxy> Create( LayerTreeHost* layer_tree_host, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); ~ThreadProxy() override; @@ -96,7 +98,8 @@ CompositorThreadOnly( ThreadProxy* proxy, int layer_tree_host_id, - RenderingStatsInstrumentation* rendering_stats_instrumentation); + RenderingStatsInstrumentation* rendering_stats_instrumentation, + scoped_ptr<BeginFrameSource> external_begin_frame_source); ~CompositorThreadOnly(); const int layer_tree_host_id; @@ -137,6 +140,8 @@ ProxyTimingHistory timing_history; + scoped_ptr<BeginFrameSource> external_begin_frame_source; + scoped_ptr<LayerTreeHostImpl> layer_tree_host_impl; base::WeakPtrFactory<ThreadProxy> weak_factory; }; @@ -182,6 +187,7 @@ void DidSwapBuffersCompleteOnImplThread() override; void OnCanDrawStateChanged(bool can_draw) override; void NotifyReadyToActivate() override; + void NotifyReadyToDraw() override; // Please call these 3 functions through // LayerTreeHostImpl's SetNeedsRedraw(), SetNeedsRedrawRect() and // SetNeedsAnimate(). @@ -203,7 +209,6 @@ void DidManageTiles() override; // SchedulerClient implementation - BeginFrameSource* ExternalBeginFrameSource() override; void WillBeginImplFrame(const BeginFrameArgs& args) override; void ScheduledActionSendBeginMainFrame() override; DrawResult ScheduledActionDrawAndSwapIfPossible() override; @@ -224,9 +229,11 @@ void ReadyToFinalizeTextureUpdates() override; protected: - ThreadProxy(LayerTreeHost* layer_tree_host, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner); + ThreadProxy( + LayerTreeHost* layer_tree_host, + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner, + scoped_ptr<BeginFrameSource> external_begin_frame_source); private: // Called on main thread. @@ -253,7 +260,6 @@ void InitializeImplOnImplThread(CompletionEvent* completion); void SetLayerTreeHostClientReadyOnImplThread(); void SetVisibleOnImplThread(CompletionEvent* completion, bool visible); - void UpdateBackgroundAnimateTicking(); void HasInitializedOutputSurfaceOnImplThread( CompletionEvent* completion, bool* has_initialized_output_surface);
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn index 5991d02..f342af9 100644 --- a/crypto/BUILD.gn +++ b/crypto/BUILD.gn
@@ -124,11 +124,6 @@ deps += [ "//third_party/android_tools:cpu_features" ] } - if (is_win) { - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - cflags = [ "/wd4267" ] - } - if (use_openssl) { # Remove NSS files when using OpenSSL sources -= [ @@ -173,7 +168,9 @@ defines = [ "CRYPTO_IMPLEMENTATION" ] } -if (is_win) { +# TODO(GYP): TODO(dpranke), fix the compile errors for this stuff +# and make it work. +if (false && is_win) { # A minimal crypto subset for hmac-related stuff that small standalone # targets can use to reduce code size on Windows. This does not depend on # OpenSSL/NSS but will use Windows APIs for that functionality. @@ -203,52 +200,55 @@ } } -test("crypto_unittests") { - sources = [ - # Tests. - "curve25519_unittest.cc", - "ec_private_key_unittest.cc", - "ec_signature_creator_unittest.cc", - "encryptor_unittest.cc", - "ghash_unittest.cc", - "hkdf_unittest.cc", - "hmac_unittest.cc", - "nss_util_unittest.cc", - "openssl_bio_string_unittest.cc", - "p224_unittest.cc", - "p224_spake_unittest.cc", - "random_unittest.cc", - "rsa_private_key_unittest.cc", - "rsa_private_key_nss_unittest.cc", - "secure_hash_unittest.cc", - "sha2_unittest.cc", - "signature_creator_unittest.cc", - "signature_verifier_unittest.cc", - "symmetric_key_unittest.cc", - ] - - if (use_openssl || !is_linux) { - sources -= [ +# TODO(GYP): Make this link on win as well. +if (!is_win) { + test("crypto_unittests") { + sources = [ + # Tests. + "curve25519_unittest.cc", + "ec_private_key_unittest.cc", + "ec_signature_creator_unittest.cc", + "encryptor_unittest.cc", + "ghash_unittest.cc", + "hkdf_unittest.cc", + "hmac_unittest.cc", + "nss_util_unittest.cc", + "openssl_bio_string_unittest.cc", + "p224_unittest.cc", + "p224_spake_unittest.cc", + "random_unittest.cc", + "rsa_private_key_unittest.cc", "rsa_private_key_nss_unittest.cc", + "secure_hash_unittest.cc", + "sha2_unittest.cc", + "signature_creator_unittest.cc", + "signature_verifier_unittest.cc", + "symmetric_key_unittest.cc", + ] + + if (use_openssl || !is_linux) { + sources -= [ + "rsa_private_key_nss_unittest.cc", + ] + } + + if (use_openssl) { + sources -= [ "nss_util_unittest.cc" ] + } else { + sources -= [ "openssl_bio_string_unittest.cc" ] + } + + deps = [ + ":crypto", + ":platform", + ":test_support", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", ] } - - if (use_openssl) { - sources -= [ "nss_util_unittest.cc" ] - } else { - sources -= [ "openssl_bio_string_unittest.cc" ] - } - - deps = [ - ":crypto", - ":platform", - ":test_support", - "//base", - "//base/test:run_all_unittests", - "//base/test:test_support", - "//testing/gmock", - "//testing/gtest", - ] } source_set("test_support") {
diff --git a/crypto/ec_signature_creator_openssl.cc b/crypto/ec_signature_creator_openssl.cc index 91e8a6a..c422cef 100644 --- a/crypto/ec_signature_creator_openssl.cc +++ b/crypto/ec_signature_creator_openssl.cc
@@ -60,24 +60,13 @@ // The result is made of two 32-byte vectors. const size_t kMaxBytesPerBN = 32; - std::vector<uint8> result; - result.resize(2 * kMaxBytesPerBN); - memset(&result[0], 0, result.size()); + std::vector<uint8> result(2 * kMaxBytesPerBN); - BIGNUM* r = ecdsa_sig.get()->r; - BIGNUM* s = ecdsa_sig.get()->s; - int r_bytes = BN_num_bytes(r); - int s_bytes = BN_num_bytes(s); - // NOTE: Can't really check for equality here since sometimes the value - // returned by BN_num_bytes() will be slightly smaller than kMaxBytesPerBN. - if (r_bytes > static_cast<int>(kMaxBytesPerBN) || - s_bytes > static_cast<int>(kMaxBytesPerBN)) { - DLOG(ERROR) << "Invalid key sizes r(" << r_bytes << ") s(" << s_bytes - << ")"; + if (!BN_bn2bin_padded(&result[0], kMaxBytesPerBN, ecdsa_sig->r) || + !BN_bn2bin_padded(&result[kMaxBytesPerBN], kMaxBytesPerBN, + ecdsa_sig->s)) { return false; } - BN_bn2bin(ecdsa_sig.get()->r, &result[kMaxBytesPerBN - r_bytes]); - BN_bn2bin(ecdsa_sig.get()->s, &result[2 * kMaxBytesPerBN - s_bytes]); out_raw_sig->swap(result); return true; }
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc index 8f9865a..02c9327 100644 --- a/crypto/nss_util.cc +++ b/crypto/nss_util.cc
@@ -835,7 +835,8 @@ base::CPU cpu; if (cpu.has_avx_hardware() && !cpu.has_avx()) { - base::Environment::Create()->SetVar("NSS_DISABLE_HW_AES", "1"); + scoped_ptr<base::Environment> env(base::Environment::Create()); + env->SetVar("NSS_DISABLE_HW_AES", "1"); } } }
diff --git a/crypto/rsa_private_key_nss.cc b/crypto/rsa_private_key_nss.cc index 078544d..0065875 100644 --- a/crypto/rsa_private_key_nss.cc +++ b/crypto/rsa_private_key_nss.cc
@@ -285,6 +285,9 @@ // and signature generation. const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | KU_DIGITAL_SIGNATURE; + // TODO(davidben): PK11_ImportDERPrivateKeyInfoAndReturnKey calls NSS's + // SEC_ASN1DecodeItem which does not enforce that there is no trailing + // data. SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( slot, &der_private_key_info, NULL, NULL, permanent, sensitive, key_usage, &result->key_, NULL);
diff --git a/crypto/rsa_private_key_openssl.cc b/crypto/rsa_private_key_openssl.cc index 053c4a2..0df1730 100644 --- a/crypto/rsa_private_key_openssl.cc +++ b/crypto/rsa_private_key_openssl.cc
@@ -19,6 +19,9 @@ namespace { +typedef ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type + ScopedPKCS8_PRIV_KEY_INFO; + // Function pointer definition, for injecting the required key export function // into ExportKey, below. The supplied function should export EVP_PKEY into // the supplied BIO, returning 1 on success or 0 on failure. @@ -76,17 +79,16 @@ return NULL; OpenSSLErrStackTracer err_tracer(FROM_HERE); - // BIO_new_mem_buf is not const aware, but it does not modify the buffer. - char* data = reinterpret_cast<char*>(const_cast<uint8*>(&input[0])); - ScopedBIO bio(BIO_new_mem_buf(data, input.size())); - if (!bio.get()) - return NULL; // Importing is a little more involved than exporting, as we must first // PKCS#8 decode the input, and then import the EVP_PKEY from Private Key // Info structure returned. - ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type p8inf( - d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); + // + // TODO(davidben): This should check that |ptr| advanced to the end of |input| + // to ensure there is no trailing data. + const uint8_t* ptr = &input[0]; + ScopedPKCS8_PRIV_KEY_INFO p8inf( + d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, input.size())); if (!p8inf.get()) return NULL;
diff --git a/crypto/signature_verifier_openssl.cc b/crypto/signature_verifier_openssl.cc index a855120..93ce9ba 100644 --- a/crypto/signature_verifier_openssl.cc +++ b/crypto/signature_verifier_openssl.cc
@@ -122,8 +122,7 @@ int rv = EVP_DigestVerifyFinal(verify_context_->ctx.get(), vector_as_array(&signature_), signature_.size()); - // rv is -1 if a DER-encoded ECDSA signature cannot be decoded correctly. - DCHECK_GE(rv, -1); + DCHECK_EQ(static_cast<int>(!!rv), rv); Reset(); return rv == 1; } @@ -141,19 +140,14 @@ signature_.assign(signature, signature + signature_len); - // BIO_new_mem_buf is not const aware, but it does not modify the buffer. - char* data = reinterpret_cast<char*>(const_cast<uint8*>(public_key_info)); - ScopedBIO bio(BIO_new_mem_buf(data, public_key_info_len)); - if (!bio.get()) - return false; - - ScopedEVP_PKEY public_key(d2i_PUBKEY_bio(bio.get(), NULL)); - if (!public_key.get()) + const uint8_t* ptr = public_key_info; + ScopedEVP_PKEY public_key(d2i_PUBKEY(nullptr, &ptr, public_key_info_len)); + if (!public_key.get() || ptr != public_key_info + public_key_info_len) return false; verify_context_->ctx.reset(EVP_MD_CTX_create()); int rv = EVP_DigestVerifyInit(verify_context_->ctx.get(), pkey_ctx, - digest, NULL, public_key.get()); + digest, nullptr, public_key.get()); return rv == 1; }
diff --git a/crypto/signature_verifier_unittest.cc b/crypto/signature_verifier_unittest.cc index f6c42e0..b521bd7 100644 --- a/crypto/signature_verifier_unittest.cc +++ b/crypto/signature_verifier_unittest.cc
@@ -258,6 +258,26 @@ ok = verifier.VerifyFinal(); EXPECT_FALSE(ok); } + + // Test 5: import an invalid key. + uint8_t bad_public_key_info[sizeof(public_key_info)]; + memcpy(bad_public_key_info, public_key_info, sizeof(public_key_info)); + bad_public_key_info[0] += 1; // Corrupt part of the SPKI syntax. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + bad_public_key_info, sizeof(bad_public_key_info)); + EXPECT_FALSE(ok); + + // Test 6: import a key with extra data. + uint8_t long_public_key_info[sizeof(public_key_info) + 5]; + memset(long_public_key_info, 0, sizeof(long_public_key_info)); + memcpy(long_public_key_info, public_key_info, sizeof(public_key_info)); + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + long_public_key_info, sizeof(long_public_key_info)); + EXPECT_FALSE(ok); } //////////////////////////////////////////////////////////////////////
diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc index 334c0e0..2cdacc0 100644 --- a/gin/isolate_holder.cc +++ b/gin/isolate_holder.cc
@@ -7,6 +7,7 @@ #include <stdlib.h> #include <string.h> +#include "base/files/memory_mapped_file.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/rand_util.h" @@ -19,7 +20,6 @@ #include "gin/run_microtasks_observer.h" #ifdef V8_USE_EXTERNAL_STARTUP_DATA -#include "base/files/memory_mapped_file.h" #include "base/path_service.h" #endif // V8_USE_EXTERNAL_STARTUP_DATA @@ -34,10 +34,10 @@ return true; } -#ifdef V8_USE_EXTERNAL_STARTUP_DATA base::MemoryMappedFile* g_mapped_natives = NULL; base::MemoryMappedFile* g_mapped_snapshot = NULL; +#ifdef V8_USE_EXTERNAL_STARTUP_DATA bool MapV8Files(base::FilePath* natives_path, base::FilePath* snapshot_path, int natives_fd = -1, int snapshot_fd = -1) { int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; @@ -80,7 +80,13 @@ return true; base::FilePath data_path; - PathService::Get(base::DIR_ANDROID_APP_DATA, &data_path); + PathService::Get( +#if defined(OS_ANDROID) + base::DIR_ANDROID_APP_DATA, +#elif defined(OS_POSIX) + base::DIR_EXE, +#endif + &data_path); DCHECK(!data_path.empty()); base::FilePath natives_path = data_path.AppendASCII("natives_blob.bin"); @@ -98,6 +104,22 @@ } #endif // V8_USE_EXTERNAL_STARTUP_DATA +//static +void IsolateHolder::GetV8ExternalSnapshotData(const char** natives_data_out, + int* natives_size_out, + const char** snapshot_data_out, + int* snapshot_size_out) { + if (!g_mapped_natives || !g_mapped_snapshot) { + *natives_data_out = *snapshot_data_out = NULL; + *natives_size_out = *snapshot_size_out = 0; + return; + } + *natives_data_out = reinterpret_cast<const char*>(g_mapped_natives->data()); + *snapshot_data_out = reinterpret_cast<const char*>(g_mapped_snapshot->data()); + *natives_size_out = static_cast<int>(g_mapped_natives->length()); + *snapshot_size_out = static_cast<int>(g_mapped_snapshot->length()); +} + IsolateHolder::IsolateHolder() { CHECK(g_array_buffer_allocator) << "You need to invoke gin::IsolateHolder::Initialize first"; @@ -158,14 +180,14 @@ #ifdef V8_USE_EXTERNAL_STARTUP_DATA v8::StartupData natives; natives.data = reinterpret_cast<const char*>(g_mapped_natives->data()); - natives.raw_size = g_mapped_natives->length(); - natives.compressed_size = g_mapped_natives->length(); + natives.raw_size = static_cast<int>(g_mapped_natives->length()); + natives.compressed_size = static_cast<int>(g_mapped_natives->length()); v8::V8::SetNativesDataBlob(&natives); v8::StartupData snapshot; snapshot.data = reinterpret_cast<const char*>(g_mapped_snapshot->data()); - snapshot.raw_size = g_mapped_snapshot->length(); - snapshot.compressed_size = g_mapped_snapshot->length(); + snapshot.raw_size = static_cast<int>(g_mapped_snapshot->length()); + snapshot.compressed_size = static_cast<int>(g_mapped_snapshot->length()); v8::V8::SetSnapshotDataBlob(&snapshot); #endif // V8_USE_EXTERNAL_STARTUP_DATA v8::V8::Initialize();
diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h index 4e14ade..b12734c 100644 --- a/gin/public/isolate_holder.h +++ b/gin/public/isolate_holder.h
@@ -52,11 +52,13 @@ void RemoveRunMicrotasksObserver(); #ifdef V8_USE_EXTERNAL_STARTUP_DATA -#ifdef OS_ANDROID static bool LoadV8SnapshotFD(int natives_fd, int snapshot_fd); -#endif static bool LoadV8Snapshot(); #endif // V8_USE_EXTERNAL_STARTUP_DATA + static void GetV8ExternalSnapshotData(const char** natives_data_out, + int* natives_size_out, + const char** snapshot_data_out, + int* snapshot_size_out); private: v8::Isolate* isolate_;
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index e8bf519..990cd02 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -184,6 +184,7 @@ "command_buffer/service/gles2_cmd_decoder_unittest_programs.cc", "command_buffer/service/gles2_cmd_decoder_unittest_textures.cc", "command_buffer/service/gles2_cmd_decoder_unittest_attribs.cc", + "command_buffer/service/gles2_cmd_decoder_unittest_valuebuffer.cc", "command_buffer/service/gl_surface_mock.cc", "command_buffer/service/gl_surface_mock.h", "command_buffer/service/gpu_scheduler_unittest.cc", @@ -195,11 +196,13 @@ "command_buffer/service/mocks.cc", "command_buffer/service/mocks.h", "command_buffer/service/program_manager_unittest.cc", + "command_buffer/service/valuebuffer_manager_unittest.cc", "command_buffer/service/query_manager_unittest.cc", "command_buffer/service/renderbuffer_manager_unittest.cc", "command_buffer/service/program_cache_unittest.cc", "command_buffer/service/shader_manager_unittest.cc", "command_buffer/service/shader_translator_unittest.cc", + "command_buffer/service/shader_translator_cache_unittest.cc", "command_buffer/service/test_helper.cc", "command_buffer/service/test_helper.h", "command_buffer/service/texture_manager_unittest.cc",
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_get_multiple.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_get_multiple.txt index 0a35700..d4685d5 100644 --- a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_get_multiple.txt +++ b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_get_multiple.txt
@@ -16,8 +16,8 @@ Overview - This extension adds the ability to query multiple state and program - information in a single call. + This extension adds the ability to query multiple program information in a + single call. Issues @@ -28,27 +28,6 @@ New Procedures and Functions - void GetMultipleIntegervCHROMIUM (const GLenum* pnames, GLuint count, - GLint* results, GLsizeiptr size) - - <pnames> points to an array of state enums that would normally be queried by - GetIntegerv. <count> is the number of pnames. <results> points memory large - enough to contain all the state being queried. <size> is the size of the - buffer pointed to be <results> - - Example: <pnames> points to an array with VIEWPORT and MAX_TEXTURE_SIZE. - VIEWPORT returns 4 values, MAX_TEXTURE_SIZE returns 1 value. Therefore - results must point to a buffer of size 5 * sizeof(GLint) and size must be at - least 5 * sizeof(GLint) - - INVALID_ENUM is generated if any of the pnames are not valid for GetIntegerv - - INVALID_VALUE is generated if <size> does not equal the size needed for the - query - - INVALID_VALUE is generated if the memory pointed to be <results> has not - been zeroed out. - void GetProgrmaInfoCHROMIUM (GLuint program, GLsizei bufsize, GLsizei* size, void* info)
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_subscribe_uniform.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_subscribe_uniform.txt new file mode 100644 index 0000000..2427d32 --- /dev/null +++ b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_subscribe_uniform.txt
@@ -0,0 +1,113 @@ +Name + + CHROMIUM_subscribe_uniform + +Name Strings + + CHROMIUM_subscribe_uniform + +Version + + Last Modifed Date: October 30, 2014 + +Dependencies + + OpenGL ES 2.0 is required. + +Overview + + Allows clients to subscribe to a set of input uniforms which can + be populated within buffers and used to modify uniform variables within + their programs. + + Decreases percieved latency for operations performed against these + uniforms. + +New Tokens + + Accepted by the <target> parameter of glBindValueBufferCHROMIUM, + glSubscribeValueCHROMIUM, glPopulateSubscribedValuesCHROMIUM and + glUniformValueBufferCHROMIUM + + GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM 0x924B + + Accepted by the <subscription> parameter of glSubscribeValueCHROMIUM and + glUniformValueBufferCHROMIUM: + + GL_MOUSE_POSITION_CHROMIUM 0x924C + +New Procedures and Functions + + The command + + void glGenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) + + Generates value buffer object names. + <n> Specifies the number of value buffer object names to be generated. + <buffers> Specifies an array in which the generated value buffer object + names are stored. + + The command + + void glDeleteValuebuffersCHROMIUM(GLsizei n, const GLuint* valuebuffers) + + Deletes named value buffer objects. + <n> Specifies the number of value buffer objects to be deleted. + <buffers> Specifies an array of value buffer objects to be deleted. + + The command + + boolean glIsValuebufferCHROMIUM(GLuint buffer); + + Returns whether an object is a value buffer object. + <buffer> Specifies the name of a buffer object. + + The command + + void glBindValuebufferCHROMIUM(GLenum target, GLuint buffer); + + Lets you use a named value buffer object. + <target> Specifies the target to which the buffer object is bound. + <buffer> Specifies the name of a buffer object. + + The command + + void glSubscribeValueCHROMIUM(GLenum target, GLenum subscription) + + Subscribes the currently bound buffer object to a subscription target. + <target> Specifies the target to which the buffer object is bound. + <subscription> Specifies the subscription to which the currently bound + buffer object should be subscribed. + + The command + + void glPopulateSubscribedValuesCHROMIUM(GLenum target) + + Populates the currently bound buffer object with all subscription states + to which it is subscribed. + <target> Specifies the target to which the buffer object is bound. + + The command + + void glUniformValueBufferCHROMIUM(GLint location, GLenum target, + GLenum subscription) + + Populates the uniform specified by location within the currently bound + program with the value in the currently bound buffer for the subscription + target. + <location> Specifies the location of the uniform variable to by modified. + <target> Specifies the target to which the buffer object is bound. + <subscription> Specifies the subscription in the currently bound buffer + whose value should be used to populate the uniform. + +Errors + + None. + +New State + + None. + +Revision History + + 10/30/2014 Documented the extension
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h index c6a3919..5489d06 100644 --- a/gpu/GLES2/gl2chromium_autogen.h +++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -195,7 +195,6 @@ #define glRequestExtensionCHROMIUM GLES2_GET_FUN(RequestExtensionCHROMIUM) #define glRateLimitOffscreenContextCHROMIUM \ GLES2_GET_FUN(RateLimitOffscreenContextCHROMIUM) -#define glGetMultipleIntegervCHROMIUM GLES2_GET_FUN(GetMultipleIntegervCHROMIUM) #define glGetProgramInfoCHROMIUM GLES2_GET_FUN(GetProgramInfoCHROMIUM) #define glCreateStreamTextureCHROMIUM GLES2_GET_FUN(CreateStreamTextureCHROMIUM) #define glCreateImageCHROMIUM GLES2_GET_FUN(CreateImageCHROMIUM) @@ -218,6 +217,14 @@ #define glCreateAndConsumeTextureCHROMIUM \ GLES2_GET_FUN(CreateAndConsumeTextureCHROMIUM) #define glBindUniformLocationCHROMIUM GLES2_GET_FUN(BindUniformLocationCHROMIUM) +#define glGenValuebuffersCHROMIUM GLES2_GET_FUN(GenValuebuffersCHROMIUM) +#define glDeleteValuebuffersCHROMIUM GLES2_GET_FUN(DeleteValuebuffersCHROMIUM) +#define glIsValuebufferCHROMIUM GLES2_GET_FUN(IsValuebufferCHROMIUM) +#define glBindValuebufferCHROMIUM GLES2_GET_FUN(BindValuebufferCHROMIUM) +#define glSubscribeValueCHROMIUM GLES2_GET_FUN(SubscribeValueCHROMIUM) +#define glPopulateSubscribedValuesCHROMIUM \ + GLES2_GET_FUN(PopulateSubscribedValuesCHROMIUM) +#define glUniformValuebufferCHROMIUM GLES2_GET_FUN(UniformValuebufferCHROMIUM) #define glBindTexImage2DCHROMIUM GLES2_GET_FUN(BindTexImage2DCHROMIUM) #define glReleaseTexImage2DCHROMIUM GLES2_GET_FUN(ReleaseTexImage2DCHROMIUM) #define glTraceBeginCHROMIUM GLES2_GET_FUN(TraceBeginCHROMIUM)
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h index b524112..a4017ec 100644 --- a/gpu/GLES2/gl2extchromium.h +++ b/gpu/GLES2/gl2extchromium.h
@@ -623,13 +623,9 @@ #ifndef GL_CHROMIUM_get_multiple #define GL_CHROMIUM_get_multiple 1 #ifdef GL_GLEXT_PROTOTYPES -GL_APICALL void GL_APIENTRY glGetMultipleIntegervCHROMIUM( - const GLenum* pnames, GLuint count, GLint* results, GLsizeiptr size); GL_APICALL void GL_APIENTRY glGetProgramInfoCHROMIUM( GLuint program, GLsizei bufsize, GLsizei* size, void* info); #endif -typedef void (GL_APIENTRYP PFNGLGETMULTIPLEINTEGERVCHROMIUMPROC) ( - const GLenum* pnames, GLuint count, GLint* results, GLsizeiptr size); typedef void (GL_APIENTRYP PFNGLGETPROGRAMINFOCHROMIUMPROC) ( GLuint program, GLsizei bufsize, GLsizei* size, void* info); #endif /* GL_CHROMIUM_get_multiple */ @@ -687,6 +683,35 @@ #define GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM 0x924A #endif +/* GL_CHROMIUM_subscribe_uniform */ +#ifndef GL_CHROMIUM_subscribe_uniform +#define GL_CHROMIUM_subscribe_uniform 1 + +#ifndef GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM +#define GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM 0x924B +#endif + +#ifndef GL_MOUSE_POSITION_CHROMIUM +#define GL_MOUSE_POSITION_CHROMIUM 0x924C +#endif + +#ifdef GL_GLEXT_PROTOTYPES +GL_APICALL void GL_APIENTRY +glGenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers); +GL_APICALL void GL_APIENTRY +glDeleteValuebuffersCHROMIUM(GLsizei n, const GLuint* valuebuffers); +GL_APICALL GLboolean GL_APIENTRY glIsValuebufferCHROMIUM(GLuint valuebuffer); +GL_APICALL void GL_APIENTRY +glBindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer); +GL_APICALL void GL_APIENTRY +glSubscribeValueCHROMIUM(GLenum target, GLenum subscription); +GL_APICALL void GL_APIENTRY glPopulateSubscribedValuesCHROMIUM(GLenum target); +GL_APICALL void GL_APIENTRY glUniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription); +#endif +#endif /* GL_CHROMIUM_subscribe_uniform */ + #ifdef GL_GLEXT_PROTOTYPES GL_APICALL void GL_APIENTRY glScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 94c3b34..226cb73 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1195,6 +1195,18 @@ 'GL_SCANOUT_CHROMIUM' ], }, + 'ValueBufferTarget': { + 'type': 'GLenum', + 'valid': [ + 'GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM', + ], + }, + 'SubscriptionTarget': { + 'type': 'GLenum', + 'valid': [ + 'GL_MOUSE_POSITION_CHROMIUM', + ], + }, 'VertexAttribType': { 'type': 'GLenum', 'valid': [ @@ -1448,6 +1460,57 @@ 'extension': "CHROMIUM_texture_mailbox", 'chromium': True, }, + 'GenValuebuffersCHROMIUM': { + 'type': 'GENn', + 'gl_test_func': 'glGenValuebuffersCHROMIUM', + 'resource_type': 'Valuebuffer', + 'resource_types': 'Valuebuffers', + 'unit_test': False, + 'extension': True, + 'chromium': True, + }, + 'DeleteValuebuffersCHROMIUM': { + 'type': 'DELn', + 'gl_test_func': 'glDeleteValuebuffersCHROMIUM', + 'resource_type': 'Valuebuffer', + 'resource_types': 'Valuebuffers', + 'unit_test': False, + 'extension': True, + 'chromium': True, + }, + 'IsValuebufferCHROMIUM': { + 'type': 'Is', + 'decoder_func': 'DoIsValuebufferCHROMIUM', + 'expectation': False, + 'extension': True, + 'chromium': True, + }, + 'BindValuebufferCHROMIUM': { + 'type': 'Bind', + 'decoder_func': 'DoBindValueBufferCHROMIUM', + 'gen_func': 'GenValueBuffersCHROMIUM', + 'unit_test': False, + 'extension': True, + 'chromium': True, + }, + 'SubscribeValueCHROMIUM': { + 'decoder_func': 'DoSubscribeValueCHROMIUM', + 'unit_test': False, + 'extension': True, + 'chromium': True, + }, + 'PopulateSubscribedValuesCHROMIUM': { + 'decoder_func': 'DoPopulateSubscribedValuesCHROMIUM', + 'unit_test': False, + 'extension': True, + 'chromium': True, + }, + 'UniformValuebufferCHROMIUM': { + 'decoder_func': 'DoUniformValueBufferCHROMIUM', + 'unit_test': False, + 'extension': True, + 'chromium': True, + }, 'ClearStencil': { 'type': 'StateSet', 'state': 'ClearStencil', @@ -1807,14 +1870,6 @@ 'chromium': True, 'impl_func': False, }, - 'GetMultipleIntegervCHROMIUM': { - 'type': 'Custom', - 'data_transfer_methods': ['shm'], - 'expectation': False, - 'extension': True, - 'chromium': True, - 'client_test': False, - }, 'GetProgramiv': { 'type': 'GETn', 'decoder_func': 'DoGetProgramiv',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index 56f1880..b1181ae 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -751,13 +751,6 @@ void GLES2RateLimitOffscreenContextCHROMIUM() { gles2::GetGLContext()->RateLimitOffscreenContextCHROMIUM(); } -void GLES2GetMultipleIntegervCHROMIUM(const GLenum* pnames, - GLuint count, - GLint* results, - GLsizeiptr size) { - gles2::GetGLContext()->GetMultipleIntegervCHROMIUM(pnames, count, results, - size); -} void GLES2GetProgramInfoCHROMIUM(GLuint program, GLsizei bufsize, GLsizei* size, @@ -853,6 +846,30 @@ const char* name) { gles2::GetGLContext()->BindUniformLocationCHROMIUM(program, location, name); } +void GLES2GenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) { + gles2::GetGLContext()->GenValuebuffersCHROMIUM(n, buffers); +} +void GLES2DeleteValuebuffersCHROMIUM(GLsizei n, const GLuint* valuebuffers) { + gles2::GetGLContext()->DeleteValuebuffersCHROMIUM(n, valuebuffers); +} +GLboolean GLES2IsValuebufferCHROMIUM(GLuint valuebuffer) { + return gles2::GetGLContext()->IsValuebufferCHROMIUM(valuebuffer); +} +void GLES2BindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer) { + gles2::GetGLContext()->BindValuebufferCHROMIUM(target, valuebuffer); +} +void GLES2SubscribeValueCHROMIUM(GLenum target, GLenum subscription) { + gles2::GetGLContext()->SubscribeValueCHROMIUM(target, subscription); +} +void GLES2PopulateSubscribedValuesCHROMIUM(GLenum target) { + gles2::GetGLContext()->PopulateSubscribedValuesCHROMIUM(target); +} +void GLES2UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) { + gles2::GetGLContext()->UniformValuebufferCHROMIUM(location, target, + subscription); +} void GLES2BindTexImage2DCHROMIUM(GLenum target, GLint imageId) { gles2::GetGLContext()->BindTexImage2DCHROMIUM(target, imageId); } @@ -1654,10 +1671,6 @@ glRateLimitOffscreenContextCHROMIUM), }, { - "glGetMultipleIntegervCHROMIUM", - reinterpret_cast<GLES2FunctionPointer>(glGetMultipleIntegervCHROMIUM), - }, - { "glGetProgramInfoCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(glGetProgramInfoCHROMIUM), }, @@ -1731,6 +1744,34 @@ reinterpret_cast<GLES2FunctionPointer>(glBindUniformLocationCHROMIUM), }, { + "glGenValuebuffersCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glGenValuebuffersCHROMIUM), + }, + { + "glDeleteValuebuffersCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glDeleteValuebuffersCHROMIUM), + }, + { + "glIsValuebufferCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glIsValuebufferCHROMIUM), + }, + { + "glBindValuebufferCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glBindValuebufferCHROMIUM), + }, + { + "glSubscribeValueCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glSubscribeValueCHROMIUM), + }, + { + "glPopulateSubscribedValuesCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glPopulateSubscribedValuesCHROMIUM), + }, + { + "glUniformValuebufferCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>(glUniformValuebufferCHROMIUM), + }, + { "glBindTexImage2DCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(glBindTexImage2DCHROMIUM), },
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h index 9aeb449..75796a2 100644 --- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h +++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -1538,20 +1538,6 @@ } } -void GetMultipleIntegervCHROMIUM(uint32_t pnames_shm_id, - uint32_t pnames_shm_offset, - GLuint count, - uint32_t results_shm_id, - uint32_t results_shm_offset, - GLsizeiptr size) { - gles2::cmds::GetMultipleIntegervCHROMIUM* c = - GetCmdSpace<gles2::cmds::GetMultipleIntegervCHROMIUM>(); - if (c) { - c->Init(pnames_shm_id, pnames_shm_offset, count, results_shm_id, - results_shm_offset, size); - } -} - void GetProgramInfoCHROMIUM(GLuint program, uint32_t bucket_id) { gles2::cmds::GetProgramInfoCHROMIUM* c = GetCmdSpace<gles2::cmds::GetProgramInfoCHROMIUM>(); @@ -1677,6 +1663,73 @@ } } +void GenValuebuffersCHROMIUMImmediate(GLsizei n, GLuint* buffers) { + const uint32_t size = + gles2::cmds::GenValuebuffersCHROMIUMImmediate::ComputeSize(n); + gles2::cmds::GenValuebuffersCHROMIUMImmediate* c = + GetImmediateCmdSpaceTotalSize< + gles2::cmds::GenValuebuffersCHROMIUMImmediate>(size); + if (c) { + c->Init(n, buffers); + } +} + +void DeleteValuebuffersCHROMIUMImmediate(GLsizei n, + const GLuint* valuebuffers) { + const uint32_t size = + gles2::cmds::DeleteValuebuffersCHROMIUMImmediate::ComputeSize(n); + gles2::cmds::DeleteValuebuffersCHROMIUMImmediate* c = + GetImmediateCmdSpaceTotalSize< + gles2::cmds::DeleteValuebuffersCHROMIUMImmediate>(size); + if (c) { + c->Init(n, valuebuffers); + } +} + +void IsValuebufferCHROMIUM(GLuint valuebuffer, + uint32_t result_shm_id, + uint32_t result_shm_offset) { + gles2::cmds::IsValuebufferCHROMIUM* c = + GetCmdSpace<gles2::cmds::IsValuebufferCHROMIUM>(); + if (c) { + c->Init(valuebuffer, result_shm_id, result_shm_offset); + } +} + +void BindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer) { + gles2::cmds::BindValuebufferCHROMIUM* c = + GetCmdSpace<gles2::cmds::BindValuebufferCHROMIUM>(); + if (c) { + c->Init(target, valuebuffer); + } +} + +void SubscribeValueCHROMIUM(GLenum target, GLenum subscription) { + gles2::cmds::SubscribeValueCHROMIUM* c = + GetCmdSpace<gles2::cmds::SubscribeValueCHROMIUM>(); + if (c) { + c->Init(target, subscription); + } +} + +void PopulateSubscribedValuesCHROMIUM(GLenum target) { + gles2::cmds::PopulateSubscribedValuesCHROMIUM* c = + GetCmdSpace<gles2::cmds::PopulateSubscribedValuesCHROMIUM>(); + if (c) { + c->Init(target); + } +} + +void UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) { + gles2::cmds::UniformValuebufferCHROMIUM* c = + GetCmdSpace<gles2::cmds::UniformValuebufferCHROMIUM>(); + if (c) { + c->Init(location, target, subscription); + } +} + void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) { gles2::cmds::BindTexImage2DCHROMIUM* c = GetCmdSpace<gles2::cmds::BindTexImage2DCHROMIUM>();
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 66d8c2b..e7118e8 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -49,21 +49,6 @@ GLES2Implementation::GLStaticState::~GLStaticState() { } -GLES2Implementation::GLStaticState::IntState::IntState() - : max_combined_texture_image_units(0), - max_cube_map_texture_size(0), - max_fragment_uniform_vectors(0), - max_renderbuffer_size(0), - max_texture_image_units(0), - max_texture_size(0), - max_varying_vectors(0), - max_vertex_attribs(0), - max_vertex_texture_image_units(0), - max_vertex_uniform_vectors(0), - num_compressed_texture_formats(0), - num_shader_binary_formats(0), - bind_generates_resource_chromium(0) {} - GLES2Implementation::SingleThreadChecker::SingleThreadChecker( GLES2Implementation* gles2_implementation) : gles2_implementation_(gles2_implementation) { @@ -99,6 +84,7 @@ bound_framebuffer_(0), bound_read_framebuffer_(0), bound_renderbuffer_(0), + bound_valuebuffer_(0), current_program_(0), bound_array_buffer_id_(0), bound_pixel_pack_transfer_buffer_id_(0), @@ -173,17 +159,22 @@ } mapped_memory_->set_chunk_size_multiple(chunk_size); - if (!QueryAndCacheStaticState()) - return false; + GLStaticState::ShaderPrecisionMap* shader_precisions = + &static_state_.shader_precisions; + capabilities_.VisitPrecisions([shader_precisions]( + GLenum shader, GLenum type, Capabilities::ShaderPrecision* result) { + const GLStaticState::ShaderPrecisionKey key(shader, type); + cmds::GetShaderPrecisionFormat::Result cached_result = { + true, result->min_range, result->max_range, result->precision}; + shader_precisions->insert(std::make_pair(key, cached_result)); + }); util_.set_num_compressed_texture_formats( - static_state_.int_state.num_compressed_texture_formats); - util_.set_num_shader_binary_formats( - static_state_.int_state.num_shader_binary_formats); + capabilities_.num_compressed_texture_formats); + util_.set_num_shader_binary_formats(capabilities_.num_shader_binary_formats); texture_units_.reset( - new TextureUnit[ - static_state_.int_state.max_combined_texture_image_units]); + new TextureUnit[capabilities_.max_combined_texture_image_units]); query_tracker_.reset(new QueryTracker(mapped_memory_.get())); buffer_tracker_.reset(new BufferTracker(mapped_memory_.get())); @@ -195,14 +186,12 @@ } vertex_array_object_manager_.reset(new VertexArrayObjectManager( - static_state_.int_state.max_vertex_attribs, - reserved_ids_[0], - reserved_ids_[1], + capabilities_.max_vertex_attribs, reserved_ids_[0], reserved_ids_[1], support_client_side_arrays_)); // GL_BIND_GENERATES_RESOURCE_CHROMIUM state must be the same // on Client & Service. - if (static_state_.int_state.bind_generates_resource_chromium != + if (capabilities_.bind_generates_resource_chromium != (share_group_->bind_generates_resource() ? 1 : 0)) { SetGLError(GL_INVALID_OPERATION, "Initialize", @@ -213,80 +202,6 @@ return true; } -bool GLES2Implementation::QueryAndCacheStaticState() { - TRACE_EVENT0("gpu", "GLES2Implementation::QueryAndCacheStaticState"); - // Setup query for multiple GetIntegerv's - static const GLenum pnames[] = { - GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, - GL_MAX_CUBE_MAP_TEXTURE_SIZE, - GL_MAX_FRAGMENT_UNIFORM_VECTORS, - GL_MAX_RENDERBUFFER_SIZE, - GL_MAX_TEXTURE_IMAGE_UNITS, - GL_MAX_TEXTURE_SIZE, - GL_MAX_VARYING_VECTORS, - GL_MAX_VERTEX_ATTRIBS, - GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, - GL_MAX_VERTEX_UNIFORM_VECTORS, - GL_NUM_COMPRESSED_TEXTURE_FORMATS, - GL_NUM_SHADER_BINARY_FORMATS, - GL_BIND_GENERATES_RESOURCE_CHROMIUM, - }; - - GetMultipleIntegervState integerv_state( - pnames, arraysize(pnames), - &static_state_.int_state.max_combined_texture_image_units, - sizeof(static_state_.int_state)); - if (!GetMultipleIntegervSetup(&integerv_state)) { - return false; - } - - // Setup query for multiple GetShaderPrecisionFormat's - static const GLenum precision_params[][2] = { - { GL_VERTEX_SHADER, GL_LOW_INT }, - { GL_VERTEX_SHADER, GL_MEDIUM_INT }, - { GL_VERTEX_SHADER, GL_HIGH_INT }, - { GL_VERTEX_SHADER, GL_LOW_FLOAT }, - { GL_VERTEX_SHADER, GL_MEDIUM_FLOAT }, - { GL_VERTEX_SHADER, GL_HIGH_FLOAT }, - { GL_FRAGMENT_SHADER, GL_LOW_INT }, - { GL_FRAGMENT_SHADER, GL_MEDIUM_INT }, - { GL_FRAGMENT_SHADER, GL_HIGH_INT }, - { GL_FRAGMENT_SHADER, GL_LOW_FLOAT }, - { GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT }, - { GL_FRAGMENT_SHADER, GL_HIGH_FLOAT }, - }; - - GetAllShaderPrecisionFormatsState precision_state( - precision_params, arraysize(precision_params)); - GetAllShaderPrecisionFormatsSetup(&precision_state); - - // Allocate and partition transfer buffer for all requests - void* buffer = transfer_buffer_->Alloc( - integerv_state.transfer_buffer_size_needed + - precision_state.transfer_buffer_size_needed); - if (!buffer) { - SetGLError(GL_OUT_OF_MEMORY, "QueryAndCacheStaticState", - "Transfer buffer allocation failed."); - return false; - } - integerv_state.buffer = buffer; - precision_state.results_buffer = - static_cast<char*>(buffer) + integerv_state.transfer_buffer_size_needed; - - // Make all the requests and wait once for all the results. - GetMultipleIntegervRequest(&integerv_state); - GetAllShaderPrecisionFormatsRequest(&precision_state); - WaitForCmd(); - GetMultipleIntegervOnCompleted(&integerv_state); - GetAllShaderPrecisionFormatsOnCompleted(&precision_state); - - // TODO(gman): We should be able to free without a token. - transfer_buffer_->FreePendingToken(buffer, helper_->InsertToken()); - CheckGLError(); - - return true; -} - GLES2Implementation::~GLES2Implementation() { // Make sure the queries are finished otherwise we'll delete the // shared memory (mapped_memory_) which will free the memory used @@ -683,40 +598,40 @@ bool GLES2Implementation::GetHelper(GLenum pname, GLint* params) { switch (pname) { case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: - *params = static_state_.int_state.max_combined_texture_image_units; + *params = capabilities_.max_combined_texture_image_units; return true; case GL_MAX_CUBE_MAP_TEXTURE_SIZE: - *params = static_state_.int_state.max_cube_map_texture_size; + *params = capabilities_.max_cube_map_texture_size; return true; case GL_MAX_FRAGMENT_UNIFORM_VECTORS: - *params = static_state_.int_state.max_fragment_uniform_vectors; + *params = capabilities_.max_fragment_uniform_vectors; return true; case GL_MAX_RENDERBUFFER_SIZE: - *params = static_state_.int_state.max_renderbuffer_size; + *params = capabilities_.max_renderbuffer_size; return true; case GL_MAX_TEXTURE_IMAGE_UNITS: - *params = static_state_.int_state.max_texture_image_units; + *params = capabilities_.max_texture_image_units; return true; case GL_MAX_TEXTURE_SIZE: - *params = static_state_.int_state.max_texture_size; + *params = capabilities_.max_texture_size; return true; case GL_MAX_VARYING_VECTORS: - *params = static_state_.int_state.max_varying_vectors; + *params = capabilities_.max_varying_vectors; return true; case GL_MAX_VERTEX_ATTRIBS: - *params = static_state_.int_state.max_vertex_attribs; + *params = capabilities_.max_vertex_attribs; return true; case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: - *params = static_state_.int_state.max_vertex_texture_image_units; + *params = capabilities_.max_vertex_texture_image_units; return true; case GL_MAX_VERTEX_UNIFORM_VECTORS: - *params = static_state_.int_state.max_vertex_uniform_vectors; + *params = capabilities_.max_vertex_uniform_vectors; return true; case GL_NUM_COMPRESSED_TEXTURE_FORMATS: - *params = static_state_.int_state.num_compressed_texture_formats; + *params = capabilities_.num_compressed_texture_formats; return true; case GL_NUM_SHADER_BINARY_FORMATS: - *params = static_state_.int_state.num_shader_binary_formats; + *params = capabilities_.num_shader_binary_formats; return true; case GL_ARRAY_BUFFER_BINDING: if (share_group_->bind_generates_resource()) { @@ -2327,8 +2242,8 @@ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glActiveTexture(" << GLES2Util::GetStringEnum(texture) << ")"); GLuint texture_index = texture - GL_TEXTURE0; - if (texture_index >= static_cast<GLuint>( - static_state_.int_state.max_combined_texture_image_units)) { + if (texture_index >= + static_cast<GLuint>(capabilities_.max_combined_texture_image_units)) { SetGLErrorInvalidEnum( "glActiveTexture", texture, "texture"); return; @@ -2364,6 +2279,11 @@ GLsizei /* n */, const GLuint* /* queries */) { } +void GLES2Implementation::GenValuebuffersCHROMIUMHelper( + GLsizei /* n */, + const GLuint* /* valuebuffers */) { +} + // NOTE #1: On old versions of OpenGL, calling glBindXXX with an unused id // generates a new resource. On newer versions of OpenGL they don't. The code // related to binding below will need to change if we switch to the new OpenGL @@ -2513,6 +2433,26 @@ return changed; } +bool GLES2Implementation::BindValuebufferCHROMIUMHelper(GLenum target, + GLuint valuebuffer) { + bool changed = false; + switch (target) { + case GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM: + if (bound_valuebuffer_ != valuebuffer) { + bound_valuebuffer_ = valuebuffer; + changed = true; + } + break; + default: + changed = true; + break; + } + // TODO(gman): There's a bug here. If the target is invalid the ID will not be + // used even though it's marked it as used here. + GetIdHandler(id_namespaces::kValuebuffers)->MarkAsUsedForBind(valuebuffer); + return changed; +} + bool GLES2Implementation::UseProgramHelper(GLuint program) { bool changed = false; if (current_program_ != program) { @@ -2612,8 +2552,7 @@ return; } for (GLsizei ii = 0; ii < n; ++ii) { - for (GLint tt = 0; - tt < static_state_.int_state.max_combined_texture_image_units; + for (GLint tt = 0; tt < capabilities_.max_combined_texture_image_units; ++tt) { TextureUnit& unit = texture_units_[tt]; if (textures[ii] == unit.bound_texture_2d) { @@ -2629,6 +2568,11 @@ } } +void GLES2Implementation::DeleteTexturesStub(GLsizei n, + const GLuint* textures) { + helper_->DeleteTexturesImmediate(n, textures); +} + void GLES2Implementation::DeleteVertexArraysOESHelper( GLsizei n, const GLuint* arrays) { vertex_array_object_manager_->DeleteVertexArrays(n, arrays); @@ -2646,9 +2590,27 @@ helper_->DeleteVertexArraysOESImmediate(n, arrays); } -void GLES2Implementation::DeleteTexturesStub( - GLsizei n, const GLuint* textures) { - helper_->DeleteTexturesImmediate(n, textures); +void GLES2Implementation::DeleteValuebuffersCHROMIUMHelper( + GLsizei n, + const GLuint* valuebuffers) { + if (!GetIdHandler(id_namespaces::kValuebuffers) + ->FreeIds(this, n, valuebuffers, + &GLES2Implementation::DeleteValuebuffersCHROMIUMStub)) { + SetGLError(GL_INVALID_VALUE, "glDeleteValuebuffersCHROMIUM", + "id not created by this context."); + return; + } + for (GLsizei ii = 0; ii < n; ++ii) { + if (valuebuffers[ii] == bound_valuebuffer_) { + bound_valuebuffer_ = 0; + } + } +} + +void GLES2Implementation::DeleteValuebuffersCHROMIUMStub( + GLsizei n, + const GLuint* valuebuffers) { + helper_->DeleteValuebuffersCHROMIUMImmediate(n, valuebuffers); } void GLES2Implementation::DisableVertexAttribArray(GLuint index) { @@ -3034,134 +2996,6 @@ rate_limit_tokens_.push(helper_->InsertToken()); } -void GLES2Implementation::GetMultipleIntegervCHROMIUM( - const GLenum* pnames, GLuint count, GLint* results, GLsizeiptr size) { - GPU_CLIENT_SINGLE_THREAD_CHECK(); - GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetMultipleIntegervCHROMIUM(" - << static_cast<const void*>(pnames) << ", " - << count << ", " << results << ", " - << size << ")"); - GPU_CLIENT_LOG_CODE_BLOCK({ - for (GLuint i = 0; i < count; ++i) { - GPU_CLIENT_LOG( - " " << i << ": " << GLES2Util::GetStringGLState(pnames[i])); - } - }); - DCHECK(size >= 0 && FitInt32NonNegative<GLsizeiptr>(size)); - - GetMultipleIntegervState state(pnames, count, results, size); - if (!GetMultipleIntegervSetup(&state)) { - return; - } - state.buffer = transfer_buffer_->Alloc(state.transfer_buffer_size_needed); - if (!state.buffer) { - SetGLError(GL_OUT_OF_MEMORY, "glGetMultipleIntegervCHROMIUM", - "Transfer buffer allocation failed."); - return; - } - GetMultipleIntegervRequest(&state); - WaitForCmd(); - GetMultipleIntegervOnCompleted(&state); - - GPU_CLIENT_LOG(" returned"); - GPU_CLIENT_LOG_CODE_BLOCK({ - for (int i = 0; i < state.num_results; ++i) { - GPU_CLIENT_LOG(" " << i << ": " << (results[i])); - } - }); - - // TODO(gman): We should be able to free without a token. - transfer_buffer_->FreePendingToken(state.buffer, helper_->InsertToken()); - CheckGLError(); -} - -bool GLES2Implementation::GetMultipleIntegervSetup( - GetMultipleIntegervState* state) { - state->num_results = 0; - for (GLuint ii = 0; ii < state->pnames_count; ++ii) { - int num = util_.GLGetNumValuesReturned(state->pnames[ii]); - if (!num) { - SetGLErrorInvalidEnum( - "glGetMultipleIntegervCHROMIUM", state->pnames[ii], "pname"); - return false; - } - state->num_results += num; - } - if (static_cast<size_t>(state->results_size) != - state->num_results * sizeof(GLint)) { - SetGLError(GL_INVALID_VALUE, "glGetMultipleIntegervCHROMIUM", "bad size"); - return false; - } - for (int ii = 0; ii < state->num_results; ++ii) { - if (state->results[ii] != 0) { - SetGLError(GL_INVALID_VALUE, - "glGetMultipleIntegervCHROMIUM", "results not set to zero."); - return false; - } - } - state->transfer_buffer_size_needed = - state->pnames_count * sizeof(state->pnames[0]) + - state->num_results * sizeof(state->results[0]); - return true; -} - -void GLES2Implementation::GetMultipleIntegervRequest( - GetMultipleIntegervState* state) { - GLenum* pnames_buffer = static_cast<GLenum*>(state->buffer); - state->results_buffer = pnames_buffer + state->pnames_count; - memcpy(pnames_buffer, state->pnames, state->pnames_count * sizeof(GLenum)); - memset(state->results_buffer, 0, state->num_results * sizeof(GLint)); - helper_->GetMultipleIntegervCHROMIUM( - transfer_buffer_->GetShmId(), - transfer_buffer_->GetOffset(pnames_buffer), - state->pnames_count, - transfer_buffer_->GetShmId(), - transfer_buffer_->GetOffset(state->results_buffer), - state->results_size); -} - -void GLES2Implementation::GetMultipleIntegervOnCompleted( - GetMultipleIntegervState* state) { - memcpy(state->results, state->results_buffer, state->results_size);; -} - -void GLES2Implementation::GetAllShaderPrecisionFormatsSetup( - GetAllShaderPrecisionFormatsState* state) { - state->transfer_buffer_size_needed = - state->precision_params_count * - sizeof(cmds::GetShaderPrecisionFormat::Result); -} - -void GLES2Implementation::GetAllShaderPrecisionFormatsRequest( - GetAllShaderPrecisionFormatsState* state) { - typedef cmds::GetShaderPrecisionFormat::Result Result; - Result* result = static_cast<Result*>(state->results_buffer); - - for (int i = 0; i < state->precision_params_count; i++) { - result->success = false; - helper_->GetShaderPrecisionFormat(state->precision_params[i][0], - state->precision_params[i][1], - transfer_buffer_->GetShmId(), - transfer_buffer_->GetOffset(result)); - result++; - } -} - -void GLES2Implementation::GetAllShaderPrecisionFormatsOnCompleted( - GetAllShaderPrecisionFormatsState* state) { - typedef cmds::GetShaderPrecisionFormat::Result Result; - Result* result = static_cast<Result*>(state->results_buffer); - - for (int i = 0; i < state->precision_params_count; i++) { - if (result->success) { - const GLStaticState::ShaderPrecisionKey key( - state->precision_params[i][0], state->precision_params[i][1]); - static_state_.shader_precisions[key] = *result; - } - result++; - } -} - void GLES2Implementation::GetProgramInfoCHROMIUMHelper( GLuint program, std::vector<int8>* result) { DCHECK(result);
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h index a612ac2..cfe8ed9 100644 --- a/gpu/command_buffer/client/gles2_implementation.h +++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -137,24 +137,6 @@ GLStaticState(); ~GLStaticState(); - struct GLES2_IMPL_EXPORT IntState { - IntState(); - GLint max_combined_texture_image_units; - GLint max_cube_map_texture_size; - GLint max_fragment_uniform_vectors; - GLint max_renderbuffer_size; - GLint max_texture_image_units; - GLint max_texture_size; - GLint max_varying_vectors; - GLint max_vertex_attribs; - GLint max_vertex_texture_image_units; - GLint max_vertex_uniform_vectors; - GLint num_compressed_texture_formats; - GLint num_shader_binary_formats; - GLint bind_generates_resource_chromium; - }; - IntState int_state; - typedef std::pair<GLenum, GLenum> ShaderPrecisionKey; typedef std::map<ShaderPrecisionKey, cmds::GetShaderPrecisionFormat::Result> @@ -398,58 +380,6 @@ int32 GetResultShmId(); uint32 GetResultShmOffset(); - bool QueryAndCacheStaticState(); - - // Helpers used to batch synchronous GetIntergerv calls with other - // synchronous calls. - struct GetMultipleIntegervState { - GetMultipleIntegervState(const GLenum* pnames, GLuint pnames_count, - GLint* results, GLsizeiptr results_size) - : pnames(pnames), - pnames_count(pnames_count), - results(results), - results_size(results_size) - { } - // inputs - const GLenum* pnames; - GLuint pnames_count; - // outputs - GLint* results; - GLsizeiptr results_size; - // transfer buffer - int num_results; - int transfer_buffer_size_needed; - void* buffer; - void* results_buffer; - }; - bool GetMultipleIntegervSetup( - GetMultipleIntegervState* state); - void GetMultipleIntegervRequest( - GetMultipleIntegervState* state); - void GetMultipleIntegervOnCompleted( - GetMultipleIntegervState* state); - - // Helpers used to batch synchronous GetShaderPrecision calls with other - // synchronous calls. - struct GetAllShaderPrecisionFormatsState { - GetAllShaderPrecisionFormatsState( - const GLenum (*precision_params)[2], - int precision_params_count) - : precision_params(precision_params), - precision_params_count(precision_params_count) - { } - const GLenum (*precision_params)[2]; - int precision_params_count; - int transfer_buffer_size_needed; - void* results_buffer; - }; - void GetAllShaderPrecisionFormatsSetup( - GetAllShaderPrecisionFormatsState* state); - void GetAllShaderPrecisionFormatsRequest( - GetAllShaderPrecisionFormatsState* state); - void GetAllShaderPrecisionFormatsOnCompleted( - GetAllShaderPrecisionFormatsState* state); - // Lazily determines if GL_ANGLE_pack_reverse_row_order is available bool IsAnglePackReverseRowOrderAvailable(); bool IsChromiumFramebufferMultisampleAvailable(); @@ -495,17 +425,19 @@ // Returns true if id is reserved. bool IsBufferReservedId(GLuint id); - bool IsFramebufferReservedId(GLuint id) { return false; } + bool IsFramebufferReservedId(GLuint id) { return false; } bool IsRenderbufferReservedId(GLuint id) { return false; } bool IsTextureReservedId(GLuint id) { return false; } bool IsVertexArrayReservedId(GLuint id) { return false; } bool IsProgramReservedId(GLuint id) { return false; } + bool IsValuebufferReservedId(GLuint id) { return false; } bool BindBufferHelper(GLenum target, GLuint texture); bool BindFramebufferHelper(GLenum target, GLuint texture); bool BindRenderbufferHelper(GLenum target, GLuint texture); bool BindTextureHelper(GLenum target, GLuint texture); bool BindVertexArrayOESHelper(GLuint array); + bool BindValuebufferCHROMIUMHelper(GLenum target, GLuint valuebuffer); bool UseProgramHelper(GLuint program); void GenBuffersHelper(GLsizei n, const GLuint* buffers); @@ -514,6 +446,7 @@ void GenTexturesHelper(GLsizei n, const GLuint* textures); void GenVertexArraysOESHelper(GLsizei n, const GLuint* arrays); void GenQueriesEXTHelper(GLsizei n, const GLuint* queries); + void GenValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* valuebuffers); void DeleteBuffersHelper(GLsizei n, const GLuint* buffers); void DeleteFramebuffersHelper(GLsizei n, const GLuint* framebuffers); @@ -523,6 +456,7 @@ bool DeleteShaderHelper(GLuint shader); void DeleteQueriesEXTHelper(GLsizei n, const GLuint* queries); void DeleteVertexArraysOESHelper(GLsizei n, const GLuint* arrays); + void DeleteValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* valuebuffers); void DeleteBuffersStub(GLsizei n, const GLuint* buffers); void DeleteFramebuffersStub(GLsizei n, const GLuint* framebuffers); @@ -531,6 +465,7 @@ void DeleteProgramStub(GLsizei n, const GLuint* programs); void DeleteShaderStub(GLsizei n, const GLuint* shaders); void DeleteVertexArraysOESStub(GLsizei n, const GLuint* arrays); + void DeleteValuebuffersCHROMIUMStub(GLsizei n, const GLuint* valuebuffers); void BufferDataHelper( GLenum target, GLsizeiptr size, const void* data, GLenum usage); @@ -699,6 +634,7 @@ GLuint bound_framebuffer_; GLuint bound_read_framebuffer_; GLuint bound_renderbuffer_; + GLuint bound_valuebuffer_; // The program in use by glUseProgram GLuint current_program_;
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index 4e547d9..f58c0a8 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -552,11 +552,6 @@ void RateLimitOffscreenContextCHROMIUM() override; -void GetMultipleIntegervCHROMIUM(const GLenum* pnames, - GLuint count, - GLint* results, - GLsizeiptr size) override; - void GetProgramInfoCHROMIUM(GLuint program, GLsizei bufsize, GLsizei* size, @@ -629,6 +624,22 @@ GLint location, const char* name) override; +void GenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) override; + +void DeleteValuebuffersCHROMIUM(GLsizei n, const GLuint* valuebuffers) override; + +GLboolean IsValuebufferCHROMIUM(GLuint valuebuffer) override; + +void BindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer) override; + +void SubscribeValueCHROMIUM(GLenum target, GLenum subscription) override; + +void PopulateSubscribedValuesCHROMIUM(GLenum target) override; + +void UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) override; + void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h index e7cc074..8541e77 100644 --- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -2019,6 +2019,120 @@ CheckGLError(); } +void GLES2Implementation::GenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) { + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGenValuebuffersCHROMIUM(" << n + << ", " << static_cast<const void*>(buffers) << ")"); + if (n < 0) { + SetGLError(GL_INVALID_VALUE, "glGenValuebuffersCHROMIUM", "n < 0"); + return; + } + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GetIdHandler(id_namespaces::kValuebuffers)->MakeIds(this, 0, n, buffers); + GenValuebuffersCHROMIUMHelper(n, buffers); + helper_->GenValuebuffersCHROMIUMImmediate(n, buffers); + if (share_group_->bind_generates_resource()) + helper_->CommandBufferHelper::Flush(); + GPU_CLIENT_LOG_CODE_BLOCK({ + for (GLsizei i = 0; i < n; ++i) { + GPU_CLIENT_LOG(" " << i << ": " << buffers[i]); + } + }); + CheckGLError(); +} + +void GLES2Implementation::DeleteValuebuffersCHROMIUM( + GLsizei n, + const GLuint* valuebuffers) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glDeleteValuebuffersCHROMIUM(" << n + << ", " << static_cast<const void*>(valuebuffers) << ")"); + GPU_CLIENT_LOG_CODE_BLOCK({ + for (GLsizei i = 0; i < n; ++i) { + GPU_CLIENT_LOG(" " << i << ": " << valuebuffers[i]); + } + }); + GPU_CLIENT_DCHECK_CODE_BLOCK({ + for (GLsizei i = 0; i < n; ++i) { + DCHECK(valuebuffers[i] != 0); + } + }); + if (n < 0) { + SetGLError(GL_INVALID_VALUE, "glDeleteValuebuffersCHROMIUM", "n < 0"); + return; + } + DeleteValuebuffersCHROMIUMHelper(n, valuebuffers); + CheckGLError(); +} + +GLboolean GLES2Implementation::IsValuebufferCHROMIUM(GLuint valuebuffer) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + TRACE_EVENT0("gpu", "GLES2Implementation::IsValuebufferCHROMIUM"); + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glIsValuebufferCHROMIUM(" + << valuebuffer << ")"); + typedef cmds::IsValuebufferCHROMIUM::Result Result; + Result* result = GetResultAs<Result*>(); + if (!result) { + return GL_FALSE; + } + *result = 0; + helper_->IsValuebufferCHROMIUM(valuebuffer, GetResultShmId(), + GetResultShmOffset()); + WaitForCmd(); + GLboolean result_value = *result != 0; + GPU_CLIENT_LOG("returned " << result_value); + CheckGLError(); + return result_value; +} + +void GLES2Implementation::BindValuebufferCHROMIUM(GLenum target, + GLuint valuebuffer) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindValuebufferCHROMIUM(" + << GLES2Util::GetStringValueBufferTarget(target) << ", " + << valuebuffer << ")"); + if (IsValuebufferReservedId(valuebuffer)) { + SetGLError(GL_INVALID_OPERATION, "BindValuebufferCHROMIUM", + "valuebuffer reserved id"); + return; + } + if (BindValuebufferCHROMIUMHelper(target, valuebuffer)) { + helper_->BindValuebufferCHROMIUM(target, valuebuffer); + } + CheckGLError(); +} + +void GLES2Implementation::SubscribeValueCHROMIUM(GLenum target, + GLenum subscription) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glSubscribeValueCHROMIUM(" + << GLES2Util::GetStringValueBufferTarget(target) << ", " + << GLES2Util::GetStringSubscriptionTarget(subscription) + << ")"); + helper_->SubscribeValueCHROMIUM(target, subscription); + CheckGLError(); +} + +void GLES2Implementation::PopulateSubscribedValuesCHROMIUM(GLenum target) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() + << "] glPopulateSubscribedValuesCHROMIUM(" + << GLES2Util::GetStringValueBufferTarget(target) << ")"); + helper_->PopulateSubscribedValuesCHROMIUM(target); + CheckGLError(); +} + +void GLES2Implementation::UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG( + "[" << GetLogPrefix() << "] glUniformValuebufferCHROMIUM(" << location + << ", " << GLES2Util::GetStringValueBufferTarget(target) << ", " + << GLES2Util::GetStringSubscriptionTarget(subscription) << ")"); + helper_->UniformValuebufferCHROMIUM(location, target, subscription); + CheckGLError(); +} + void GLES2Implementation::BindTexImage2DCHROMIUM(GLenum target, GLint imageId) { GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBindTexImage2DCHROMIUM("
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc index be1f1a3..5c944a8 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest.cc +++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -383,6 +383,7 @@ static const GLuint kTexturesStartId = 1; static const GLuint kQueriesStartId = 1; static const GLuint kVertexArraysStartId = 1; + static const GLuint kValuebuffersStartId = 1; typedef MockTransferBuffer::ExpectedMemoryInfo ExpectedMemoryInfo; @@ -408,43 +409,36 @@ helper_->Initialize(kCommandBufferSizeBytes); gpu_control_.reset(new StrictMock<MockClientGpuControl>()); - EXPECT_CALL(*gpu_control_, GetCapabilities()) - .WillOnce(testing::Return(Capabilities())); - - GLES2Implementation::GLStaticState state; - GLES2Implementation::GLStaticState::IntState& int_state = state.int_state; - int_state.max_combined_texture_image_units = + Capabilities capabilities; + capabilities.VisitPrecisions( + [](GLenum shader, GLenum type, + Capabilities::ShaderPrecision* precision) { + precision->min_range = 3; + precision->max_range = 5; + precision->precision = 7; + }); + capabilities.max_combined_texture_image_units = kMaxCombinedTextureImageUnits; - int_state.max_cube_map_texture_size = kMaxCubeMapTextureSize; - int_state.max_fragment_uniform_vectors = kMaxFragmentUniformVectors; - int_state.max_renderbuffer_size = kMaxRenderbufferSize; - int_state.max_texture_image_units = kMaxTextureImageUnits; - int_state.max_texture_size = kMaxTextureSize; - int_state.max_varying_vectors = kMaxVaryingVectors; - int_state.max_vertex_attribs = kMaxVertexAttribs; - int_state.max_vertex_texture_image_units = kMaxVertexTextureImageUnits; - int_state.max_vertex_uniform_vectors = kMaxVertexUniformVectors; - int_state.num_compressed_texture_formats = kNumCompressedTextureFormats; - int_state.num_shader_binary_formats = kNumShaderBinaryFormats; - int_state.bind_generates_resource_chromium = + capabilities.max_cube_map_texture_size = kMaxCubeMapTextureSize; + capabilities.max_fragment_uniform_vectors = kMaxFragmentUniformVectors; + capabilities.max_renderbuffer_size = kMaxRenderbufferSize; + capabilities.max_texture_image_units = kMaxTextureImageUnits; + capabilities.max_texture_size = kMaxTextureSize; + capabilities.max_varying_vectors = kMaxVaryingVectors; + capabilities.max_vertex_attribs = kMaxVertexAttribs; + capabilities.max_vertex_texture_image_units = kMaxVertexTextureImageUnits; + capabilities.max_vertex_uniform_vectors = kMaxVertexUniformVectors; + capabilities.num_compressed_texture_formats = + kNumCompressedTextureFormats; + capabilities.num_shader_binary_formats = kNumShaderBinaryFormats; + capabilities.bind_generates_resource_chromium = bind_generates_resource_service ? 1 : 0; - - // This just happens to work for now because IntState has 1 GLint per - // state. - // If IntState gets more complicated this code will need to get more - // complicated. - ExpectedMemoryInfo mem1 = transfer_buffer_->GetExpectedMemory( - sizeof(GLES2Implementation::GLStaticState::IntState) * 2 + - sizeof(cmds::GetShaderPrecisionFormat::Result) * 12); + EXPECT_CALL(*gpu_control_, GetCapabilities()) + .WillOnce(testing::Return(capabilities)); { InSequence sequence; - EXPECT_CALL(*command_buffer_, OnFlush()) - .WillOnce(SetMemory(mem1.ptr + sizeof(int_state), int_state)) - .RetiresOnSaturation(); - GetNextToken(); // eat the token that starting up will use. - const bool support_client_side_arrays = true; gl_.reset(new GLES2Implementation(helper_.get(), share_group, @@ -461,7 +455,6 @@ return false; } - EXPECT_CALL(*command_buffer_, OnFlush()).Times(1).RetiresOnSaturation(); helper_->CommandBufferHelper::Finish(); ::testing::Mock::VerifyAndClearExpectations(gl_.get()); @@ -755,6 +748,7 @@ const GLuint GLES2ImplementationTest::kTexturesStartId; const GLuint GLES2ImplementationTest::kQueriesStartId; const GLuint GLES2ImplementationTest::kVertexArraysStartId; +const GLuint GLES2ImplementationTest::kValuebuffersStartId; #endif TEST_F(GLES2ImplementationTest, Basic) { @@ -817,20 +811,22 @@ cmds::GetShaderPrecisionFormat cmd; }; typedef cmds::GetShaderPrecisionFormat::Result Result; + const unsigned kDummyType1 = 3; + const unsigned kDummyType2 = 4; - // The first call for mediump should trigger a command buffer request. + // The first call for dummy type 1 should trigger a command buffer request. GLint range1[2] = {0, 0}; GLint precision1 = 0; Cmds expected1; ExpectedMemoryInfo client_result1 = GetExpectedResultMemory(4); - expected1.cmd.Init(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, - client_result1.id, client_result1.offset); + expected1.cmd.Init(GL_FRAGMENT_SHADER, kDummyType1, client_result1.id, + client_result1.offset); Result server_result1 = {true, 14, 14, 10}; EXPECT_CALL(*command_buffer(), OnFlush()) .WillOnce(SetMemory(client_result1.ptr, server_result1)) .RetiresOnSaturation(); - gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, - range1, &precision1); + gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, kDummyType1, range1, + &precision1); const void* commands2 = GetPut(); EXPECT_NE(commands_, commands2); EXPECT_EQ(0, memcmp(&expected1, commands_, sizeof(expected1))); @@ -838,39 +834,53 @@ EXPECT_EQ(range1[1], 14); EXPECT_EQ(precision1, 10); - // The second call for mediump should use the cached value and avoid + // The second call for dummy type 1 should use the cached value and avoid // triggering a command buffer request, so we do not expect a call to // OnFlush() here. We do expect the results to be correct though. GLint range2[2] = {0, 0}; GLint precision2 = 0; - gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, - range2, &precision2); + gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, kDummyType1, range2, + &precision2); const void* commands3 = GetPut(); EXPECT_EQ(commands2, commands3); EXPECT_EQ(range2[0], 14); EXPECT_EQ(range2[1], 14); EXPECT_EQ(precision2, 10); - // If we then make a request for highp, we should get another command + // If we then make a request for dummy type 2, we should get another command // buffer request since it hasn't been cached yet. GLint range3[2] = {0, 0}; GLint precision3 = 0; Cmds expected3; ExpectedMemoryInfo result3 = GetExpectedResultMemory(4); - expected3.cmd.Init(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, - result3.id, result3.offset); + expected3.cmd.Init(GL_FRAGMENT_SHADER, kDummyType2, result3.id, + result3.offset); Result result3_source = {true, 62, 62, 16}; EXPECT_CALL(*command_buffer(), OnFlush()) .WillOnce(SetMemory(result3.ptr, result3_source)) .RetiresOnSaturation(); - gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, - range3, &precision3); + gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, kDummyType2, range3, + &precision3); const void* commands4 = GetPut(); EXPECT_NE(commands3, commands4); EXPECT_EQ(0, memcmp(&expected3, commands3, sizeof(expected3))); EXPECT_EQ(range3[0], 62); EXPECT_EQ(range3[1], 62); EXPECT_EQ(precision3, 16); + + // Any call for predefined types should use the cached value from the + // Capabilities and avoid triggering a command buffer request, so we do not + // expect a call to OnFlush() here. We do expect the results to be correct + // though. + GLint range4[2] = {0, 0}; + GLint precision4 = 0; + gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, range4, + &precision4); + const void* commands5 = GetPut(); + EXPECT_EQ(commands4, commands5); + EXPECT_EQ(range4[0], 3); + EXPECT_EQ(range4[1], 5); + EXPECT_EQ(precision4, 7); } TEST_F(GLES2ImplementationTest, ShaderSource) { @@ -1947,122 +1957,6 @@ EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); } -TEST_F(GLES2ImplementationTest, GetMultipleIntegervCHROMIUMValidArgs) { - const GLenum pnames[] = { - GL_DEPTH_WRITEMASK, - GL_COLOR_WRITEMASK, - GL_STENCIL_WRITEMASK, - }; - const GLint num_results = 6; - GLint results[num_results + 1]; - struct Cmds { - cmds::GetMultipleIntegervCHROMIUM get_multiple; - cmd::SetToken set_token; - }; - const GLsizei kNumPnames = arraysize(pnames); - const GLsizeiptr kResultsSize = num_results * sizeof(results[0]); - const size_t kPNamesSize = kNumPnames * sizeof(pnames[0]); - - ExpectedMemoryInfo mem1 = GetExpectedMemory(kPNamesSize + kResultsSize); - ExpectedMemoryInfo result1 = GetExpectedResultMemory( - sizeof(cmds::GetError::Result)); - - const uint32 kPnamesOffset = mem1.offset; - const uint32 kResultsOffset = mem1.offset + kPNamesSize; - Cmds expected; - expected.get_multiple.Init( - mem1.id, kPnamesOffset, kNumPnames, - mem1.id, kResultsOffset, kResultsSize); - expected.set_token.Init(GetNextToken()); - - const GLint kSentinel = 0x12345678; - memset(results, 0, sizeof(results)); - results[num_results] = kSentinel; - const GLint returned_results[] = { - 1, 0, 1, 0, 1, -1, - }; - // One call to flush to wait for results - EXPECT_CALL(*command_buffer(), OnFlush()) - .WillOnce(SetMemoryFromArray(mem1.ptr + kPNamesSize, - returned_results, sizeof(returned_results))) - .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR))) - .RetiresOnSaturation(); - - gl_->GetMultipleIntegervCHROMIUM( - &pnames[0], kNumPnames, &results[0], kResultsSize); - EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); - EXPECT_EQ(0, memcmp(&returned_results, results, sizeof(returned_results))); - EXPECT_EQ(kSentinel, results[num_results]); - EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); -} - -TEST_F(GLES2ImplementationTest, GetMultipleIntegervCHROMIUMBadArgs) { - GLenum pnames[] = { - GL_DEPTH_WRITEMASK, - GL_COLOR_WRITEMASK, - GL_STENCIL_WRITEMASK, - }; - const GLint num_results = 6; - GLint results[num_results + 1]; - const GLsizei kNumPnames = arraysize(pnames); - const GLsizeiptr kResultsSize = num_results * sizeof(results[0]); - - ExpectedMemoryInfo result1 = - GetExpectedResultMemory(sizeof(cmds::GetError::Result)); - ExpectedMemoryInfo result2 = - GetExpectedResultMemory(sizeof(cmds::GetError::Result)); - ExpectedMemoryInfo result3 = - GetExpectedResultMemory(sizeof(cmds::GetError::Result)); - ExpectedMemoryInfo result4 = - GetExpectedResultMemory(sizeof(cmds::GetError::Result)); - - // Calls to flush to wait for GetError - EXPECT_CALL(*command_buffer(), OnFlush()) - .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR))) - .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) - .WillOnce(SetMemory(result3.ptr, GLuint(GL_NO_ERROR))) - .WillOnce(SetMemory(result4.ptr, GLuint(GL_NO_ERROR))) - .RetiresOnSaturation(); - - const GLint kSentinel = 0x12345678; - memset(results, 0, sizeof(results)); - results[num_results] = kSentinel; - // try bad size. - gl_->GetMultipleIntegervCHROMIUM( - &pnames[0], kNumPnames, &results[0], kResultsSize + 1); - EXPECT_TRUE(NoCommandsWritten()); - EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); - EXPECT_EQ(0, results[0]); - EXPECT_EQ(kSentinel, results[num_results]); - // try bad size. - ClearCommands(); - gl_->GetMultipleIntegervCHROMIUM( - &pnames[0], kNumPnames, &results[0], kResultsSize - 1); - EXPECT_TRUE(NoCommandsWritten()); - EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); - EXPECT_EQ(0, results[0]); - EXPECT_EQ(kSentinel, results[num_results]); - // try uncleared results. - ClearCommands(); - results[2] = 1; - gl_->GetMultipleIntegervCHROMIUM( - &pnames[0], kNumPnames, &results[0], kResultsSize); - EXPECT_TRUE(NoCommandsWritten()); - EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); - EXPECT_EQ(0, results[0]); - EXPECT_EQ(kSentinel, results[num_results]); - // try bad enum results. - ClearCommands(); - results[2] = 0; - pnames[1] = GL_TRUE; - gl_->GetMultipleIntegervCHROMIUM( - &pnames[0], kNumPnames, &results[0], kResultsSize); - EXPECT_TRUE(NoCommandsWritten()); - EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), gl_->GetError()); - EXPECT_EQ(0, results[0]); - EXPECT_EQ(kSentinel, results[num_results]); -} - TEST_F(GLES2ImplementationTest, GetProgramInfoCHROMIUMGoodArgs) { const uint32 kBucketId = GLES2Implementation::kResultBucketId; const GLuint kProgramId = 123;
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h index 8069b12..74a438c 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -1800,6 +1800,108 @@ // TODO: Implement unit test for GenMailboxCHROMIUM // TODO: Implement unit test for BindUniformLocationCHROMIUM +TEST_F(GLES2ImplementationTest, GenValuebuffersCHROMIUM) { + GLuint ids[2] = { + 0, + }; + struct Cmds { + cmds::GenValuebuffersCHROMIUMImmediate gen; + GLuint data[2]; + }; + Cmds expected; + expected.gen.Init(arraysize(ids), &ids[0]); + expected.data[0] = kValuebuffersStartId; + expected.data[1] = kValuebuffersStartId + 1; + gl_->GenValuebuffersCHROMIUM(arraysize(ids), &ids[0]); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); + EXPECT_EQ(kValuebuffersStartId, ids[0]); + EXPECT_EQ(kValuebuffersStartId + 1, ids[1]); +} + +TEST_F(GLES2ImplementationTest, DeleteValuebuffersCHROMIUM) { + GLuint ids[2] = {kValuebuffersStartId, kValuebuffersStartId + 1}; + struct Cmds { + cmds::DeleteValuebuffersCHROMIUMImmediate del; + GLuint data[2]; + }; + Cmds expected; + expected.del.Init(arraysize(ids), &ids[0]); + expected.data[0] = kValuebuffersStartId; + expected.data[1] = kValuebuffersStartId + 1; + gl_->DeleteValuebuffersCHROMIUM(arraysize(ids), &ids[0]); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); +} + +TEST_F(GLES2ImplementationTest, IsValuebufferCHROMIUM) { + struct Cmds { + cmds::IsValuebufferCHROMIUM cmd; + }; + + Cmds expected; + ExpectedMemoryInfo result1 = + GetExpectedResultMemory(sizeof(cmds::IsValuebufferCHROMIUM::Result)); + expected.cmd.Init(1, result1.id, result1.offset); + + EXPECT_CALL(*command_buffer(), OnFlush()) + .WillOnce(SetMemory(result1.ptr, uint32_t(1))) + .RetiresOnSaturation(); + + GLboolean result = gl_->IsValuebufferCHROMIUM(1); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); + EXPECT_TRUE(result); +} + +TEST_F(GLES2ImplementationTest, BindValuebufferCHROMIUM) { + struct Cmds { + cmds::BindValuebufferCHROMIUM cmd; + }; + Cmds expected; + expected.cmd.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, 2); + + gl_->BindValuebufferCHROMIUM(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, 2); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); + ClearCommands(); + gl_->BindValuebufferCHROMIUM(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, 2); + EXPECT_TRUE(NoCommandsWritten()); +} + +TEST_F(GLES2ImplementationTest, SubscribeValueCHROMIUM) { + struct Cmds { + cmds::SubscribeValueCHROMIUM cmd; + }; + Cmds expected; + expected.cmd.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + + gl_->SubscribeValueCHROMIUM(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); +} + +TEST_F(GLES2ImplementationTest, PopulateSubscribedValuesCHROMIUM) { + struct Cmds { + cmds::PopulateSubscribedValuesCHROMIUM cmd; + }; + Cmds expected; + expected.cmd.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM); + + gl_->PopulateSubscribedValuesCHROMIUM(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); +} + +TEST_F(GLES2ImplementationTest, UniformValuebufferCHROMIUM) { + struct Cmds { + cmds::UniformValuebufferCHROMIUM cmd; + }; + Cmds expected; + expected.cmd.Init(1, GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + + gl_->UniformValuebufferCHROMIUM(1, GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); +} + TEST_F(GLES2ImplementationTest, BindTexImage2DCHROMIUM) { struct Cmds { cmds::BindTexImage2DCHROMIUM cmd;
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h index d840804..ee4591c 100644 --- a/gpu/command_buffer/client/gles2_interface_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -392,10 +392,6 @@ virtual const GLchar* GetRequestableExtensionsCHROMIUM() = 0; virtual void RequestExtensionCHROMIUM(const char* extension) = 0; virtual void RateLimitOffscreenContextCHROMIUM() = 0; -virtual void GetMultipleIntegervCHROMIUM(const GLenum* pnames, - GLuint count, - GLint* results, - GLsizeiptr size) = 0; virtual void GetProgramInfoCHROMIUM(GLuint program, GLsizei bufsize, GLsizei* size, @@ -450,6 +446,16 @@ virtual void BindUniformLocationCHROMIUM(GLuint program, GLint location, const char* name) = 0; +virtual void GenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) = 0; +virtual void DeleteValuebuffersCHROMIUM(GLsizei n, + const GLuint* valuebuffers) = 0; +virtual GLboolean IsValuebufferCHROMIUM(GLuint valuebuffer) = 0; +virtual void BindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer) = 0; +virtual void SubscribeValueCHROMIUM(GLenum target, GLenum subscription) = 0; +virtual void PopulateSubscribedValuesCHROMIUM(GLenum target) = 0; +virtual void UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) = 0; virtual void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) = 0; virtual void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) = 0; virtual void TraceBeginCHROMIUM(const char* name) = 0;
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h index 687ff48..637c000 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -383,10 +383,6 @@ const GLchar* GetRequestableExtensionsCHROMIUM() override; void RequestExtensionCHROMIUM(const char* extension) override; void RateLimitOffscreenContextCHROMIUM() override; -void GetMultipleIntegervCHROMIUM(const GLenum* pnames, - GLuint count, - GLint* results, - GLsizeiptr size) override; void GetProgramInfoCHROMIUM(GLuint program, GLsizei bufsize, GLsizei* size, @@ -441,6 +437,15 @@ void BindUniformLocationCHROMIUM(GLuint program, GLint location, const char* name) override; +void GenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) override; +void DeleteValuebuffersCHROMIUM(GLsizei n, const GLuint* valuebuffers) override; +GLboolean IsValuebufferCHROMIUM(GLuint valuebuffer) override; +void BindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer) override; +void SubscribeValueCHROMIUM(GLenum target, GLenum subscription) override; +void PopulateSubscribedValuesCHROMIUM(GLenum target) override; +void UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) override; void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void TraceBeginCHROMIUM(const char* name) override;
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h index e78b07c..5a986e9 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -694,11 +694,6 @@ } void GLES2InterfaceStub::RateLimitOffscreenContextCHROMIUM() { } -void GLES2InterfaceStub::GetMultipleIntegervCHROMIUM(const GLenum* /* pnames */, - GLuint /* count */, - GLint* /* results */, - GLsizeiptr /* size */) { -} void GLES2InterfaceStub::GetProgramInfoCHROMIUM(GLuint /* program */, GLsizei /* bufsize */, GLsizei* /* size */, @@ -781,6 +776,28 @@ GLint /* location */, const char* /* name */) { } +void GLES2InterfaceStub::GenValuebuffersCHROMIUM(GLsizei /* n */, + GLuint* /* buffers */) { +} +void GLES2InterfaceStub::DeleteValuebuffersCHROMIUM( + GLsizei /* n */, + const GLuint* /* valuebuffers */) { +} +GLboolean GLES2InterfaceStub::IsValuebufferCHROMIUM(GLuint /* valuebuffer */) { + return 0; +} +void GLES2InterfaceStub::BindValuebufferCHROMIUM(GLenum /* target */, + GLuint /* valuebuffer */) { +} +void GLES2InterfaceStub::SubscribeValueCHROMIUM(GLenum /* target */, + GLenum /* subscription */) { +} +void GLES2InterfaceStub::PopulateSubscribedValuesCHROMIUM(GLenum /* target */) { +} +void GLES2InterfaceStub::UniformValuebufferCHROMIUM(GLint /* location */, + GLenum /* target */, + GLenum /* subscription */) { +} void GLES2InterfaceStub::BindTexImage2DCHROMIUM(GLenum /* target */, GLint /* imageId */) { }
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h index a599eb4..b4349ae 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -383,10 +383,6 @@ const GLchar* GetRequestableExtensionsCHROMIUM() override; void RequestExtensionCHROMIUM(const char* extension) override; void RateLimitOffscreenContextCHROMIUM() override; -void GetMultipleIntegervCHROMIUM(const GLenum* pnames, - GLuint count, - GLint* results, - GLsizeiptr size) override; void GetProgramInfoCHROMIUM(GLuint program, GLsizei bufsize, GLsizei* size, @@ -441,6 +437,15 @@ void BindUniformLocationCHROMIUM(GLuint program, GLint location, const char* name) override; +void GenValuebuffersCHROMIUM(GLsizei n, GLuint* buffers) override; +void DeleteValuebuffersCHROMIUM(GLsizei n, const GLuint* valuebuffers) override; +GLboolean IsValuebufferCHROMIUM(GLuint valuebuffer) override; +void BindValuebufferCHROMIUM(GLenum target, GLuint valuebuffer) override; +void SubscribeValueCHROMIUM(GLenum target, GLenum subscription) override; +void PopulateSubscribedValuesCHROMIUM(GLenum target) override; +void UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) override; void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void TraceBeginCHROMIUM(const char* name) override;
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h index cf614a9..d7b53b4 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -1207,15 +1207,6 @@ gl_->RateLimitOffscreenContextCHROMIUM(); } -void GLES2TraceImplementation::GetMultipleIntegervCHROMIUM(const GLenum* pnames, - GLuint count, - GLint* results, - GLsizeiptr size) { - TRACE_EVENT_BINARY_EFFICIENT0("gpu", - "GLES2Trace::GetMultipleIntegervCHROMIUM"); - gl_->GetMultipleIntegervCHROMIUM(pnames, count, results, size); -} - void GLES2TraceImplementation::GetProgramInfoCHROMIUM(GLuint program, GLsizei bufsize, GLsizei* size, @@ -1358,6 +1349,51 @@ gl_->BindUniformLocationCHROMIUM(program, location, name); } +void GLES2TraceImplementation::GenValuebuffersCHROMIUM(GLsizei n, + GLuint* buffers) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::GenValuebuffersCHROMIUM"); + gl_->GenValuebuffersCHROMIUM(n, buffers); +} + +void GLES2TraceImplementation::DeleteValuebuffersCHROMIUM( + GLsizei n, + const GLuint* valuebuffers) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", + "GLES2Trace::DeleteValuebuffersCHROMIUM"); + gl_->DeleteValuebuffersCHROMIUM(n, valuebuffers); +} + +GLboolean GLES2TraceImplementation::IsValuebufferCHROMIUM(GLuint valuebuffer) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::IsValuebufferCHROMIUM"); + return gl_->IsValuebufferCHROMIUM(valuebuffer); +} + +void GLES2TraceImplementation::BindValuebufferCHROMIUM(GLenum target, + GLuint valuebuffer) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::BindValuebufferCHROMIUM"); + gl_->BindValuebufferCHROMIUM(target, valuebuffer); +} + +void GLES2TraceImplementation::SubscribeValueCHROMIUM(GLenum target, + GLenum subscription) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::SubscribeValueCHROMIUM"); + gl_->SubscribeValueCHROMIUM(target, subscription); +} + +void GLES2TraceImplementation::PopulateSubscribedValuesCHROMIUM(GLenum target) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", + "GLES2Trace::PopulateSubscribedValuesCHROMIUM"); + gl_->PopulateSubscribedValuesCHROMIUM(target); +} + +void GLES2TraceImplementation::UniformValuebufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", + "GLES2Trace::UniformValuebufferCHROMIUM"); + gl_->UniformValuebufferCHROMIUM(location, target, subscription); +} + void GLES2TraceImplementation::BindTexImage2DCHROMIUM(GLenum target, GLint imageId) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::BindTexImage2DCHROMIUM");
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt index 37c33be..0f17198 100644 --- a/gpu/command_buffer/cmd_buffer_functions.txt +++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -183,7 +183,6 @@ GL_APICALL const GLchar* GL_APIENTRY glGetRequestableExtensionsCHROMIUM (void); GL_APICALL void GL_APIENTRY glRequestExtensionCHROMIUM (const char* extension); GL_APICALL void GL_APIENTRY glRateLimitOffscreenContextCHROMIUM (void); -GL_APICALL void GL_APIENTRY glGetMultipleIntegervCHROMIUM (const GLenum* pnames, GLuint count, GLint* results, GLsizeiptr size); GL_APICALL void GL_APIENTRY glGetProgramInfoCHROMIUM (GLidProgram program, GLsizeiNotNegative bufsize, GLsizei* size, void* info); GL_APICALL GLuint GL_APIENTRY glCreateStreamTextureCHROMIUM (GLuint texture); GL_APICALL GLuint GL_APIENTRY glCreateImageCHROMIUM (ClientBuffer buffer, GLsizei width, GLsizei height, GLenum internalformat); @@ -202,6 +201,13 @@ GL_APICALL void GL_APIENTRY glConsumeTextureCHROMIUM (GLenumTextureBindTarget target, const GLbyte* mailbox); GL_APICALL GLuint GL_APIENTRY glCreateAndConsumeTextureCHROMIUM (GLenumTextureBindTarget target, const GLbyte* mailbox); GL_APICALL void GL_APIENTRY glBindUniformLocationCHROMIUM (GLidProgram program, GLint location, const char* name); +GL_APICALL void GL_APIENTRY glGenValuebuffersCHROMIUM (GLsizeiNotNegative n, GLuint* buffers); +GL_APICALL void GL_APIENTRY glDeleteValuebuffersCHROMIUM (GLsizeiNotNegative n, const GLuint* valuebuffers); +GL_APICALL GLboolean GL_APIENTRY glIsValuebufferCHROMIUM (GLidBindValuebuffer valuebuffer); +GL_APICALL void GL_APIENTRY glBindValuebufferCHROMIUM (GLenumValueBufferTarget target, GLidBindValuebuffer valuebuffer); +GL_APICALL void GL_APIENTRY glSubscribeValueCHROMIUM (GLenumValueBufferTarget target, GLenumSubscriptionTarget subscription); +GL_APICALL void GL_APIENTRY glPopulateSubscribedValuesCHROMIUM (GLenumValueBufferTarget target); +GL_APICALL void GL_APIENTRY glUniformValuebufferCHROMIUM (GLintUniformLocation location, GLenumValueBufferTarget target, GLenumSubscriptionTarget subscription); GL_APICALL void GL_APIENTRY glBindTexImage2DCHROMIUM (GLenumTextureBindTarget target, GLint imageId); GL_APICALL void GL_APIENTRY glReleaseTexImage2DCHROMIUM (GLenumTextureBindTarget target, GLint imageId); GL_APICALL void GL_APIENTRY glTraceBeginCHROMIUM (const char* name);
diff --git a/gpu/command_buffer/common/capabilities.cc b/gpu/command_buffer/common/capabilities.cc index f905f4e..9d9e90d 100644 --- a/gpu/command_buffer/common/capabilities.cc +++ b/gpu/command_buffer/common/capabilities.cc
@@ -6,8 +6,24 @@ namespace gpu { +Capabilities::PerStagePrecisions::PerStagePrecisions() { +} + Capabilities::Capabilities() - : post_sub_buffer(false), + : max_combined_texture_image_units(0), + max_cube_map_texture_size(0), + max_fragment_uniform_vectors(0), + max_renderbuffer_size(0), + max_texture_image_units(0), + max_texture_size(0), + max_varying_vectors(0), + max_vertex_attribs(0), + max_vertex_texture_image_units(0), + max_vertex_uniform_vectors(0), + num_compressed_texture_formats(0), + num_shader_binary_formats(0), + bind_generates_resource_chromium(0), + post_sub_buffer(false), egl_image_external(false), texture_format_bgra8888(false), texture_format_etc1(false),
diff --git a/gpu/command_buffer/common/capabilities.h b/gpu/command_buffer/common/capabilities.h index fff1ef8..224829c 100644 --- a/gpu/command_buffer/common/capabilities.h +++ b/gpu/command_buffer/common/capabilities.h
@@ -7,9 +7,75 @@ #include "gpu/gpu_export.h" +// From gl2.h. We want to avoid including gl headers because client-side and +// service-side headers conflict. +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 + namespace gpu { struct GPU_EXPORT Capabilities { + struct ShaderPrecision { + ShaderPrecision() : min_range(0), max_range(0), precision(0) {} + int min_range; + int max_range; + int precision; + }; + + struct PerStagePrecisions { + PerStagePrecisions(); + + ShaderPrecision low_int; + ShaderPrecision medium_int; + ShaderPrecision high_int; + ShaderPrecision low_float; + ShaderPrecision medium_float; + ShaderPrecision high_float; + }; + + Capabilities(); + + template <typename T> + void VisitStagePrecisions(unsigned stage, + PerStagePrecisions* precisions, + const T& visitor) { + visitor(stage, GL_LOW_INT, &precisions->low_int); + visitor(stage, GL_MEDIUM_INT, &precisions->medium_int); + visitor(stage, GL_HIGH_INT, &precisions->high_int); + visitor(stage, GL_LOW_FLOAT, &precisions->low_float); + visitor(stage, GL_MEDIUM_FLOAT, &precisions->medium_float); + visitor(stage, GL_HIGH_FLOAT, &precisions->high_float); + } + + template <typename T> + void VisitPrecisions(const T& visitor) { + VisitStagePrecisions(GL_VERTEX_SHADER, &vertex_shader_precisions, visitor); + VisitStagePrecisions(GL_FRAGMENT_SHADER, &fragment_shader_precisions, + visitor); + } + + PerStagePrecisions vertex_shader_precisions; + PerStagePrecisions fragment_shader_precisions; + int max_combined_texture_image_units; + int max_cube_map_texture_size; + int max_fragment_uniform_vectors; + int max_renderbuffer_size; + int max_texture_image_units; + int max_texture_size; + int max_varying_vectors; + int max_vertex_attribs; + int max_vertex_texture_image_units; + int max_vertex_uniform_vectors; + int num_compressed_texture_formats; + int num_shader_binary_formats; + int bind_generates_resource_chromium; + bool post_sub_buffer; bool egl_image_external; bool texture_format_bgra8888; @@ -25,8 +91,6 @@ bool future_sync_points; bool blend_equation_advanced; bool blend_equation_advanced_coherent; - - Capabilities(); }; } // namespace gpu
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h index 393303e..c0bc663 100644 --- a/gpu/command_buffer/common/gles2_cmd_format.h +++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -59,6 +59,7 @@ kTextures, kQueries, kVertexArrays, + kValuebuffers, kNumIdNamespaces };
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h index ecef6bb..14f0728 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -7533,72 +7533,6 @@ COMPILE_ASSERT(offsetof(RequestExtensionCHROMIUM, bucket_id) == 4, OffsetOf_RequestExtensionCHROMIUM_bucket_id_not_4); -struct GetMultipleIntegervCHROMIUM { - typedef GetMultipleIntegervCHROMIUM ValueType; - static const CommandId kCmdId = kGetMultipleIntegervCHROMIUM; - static const cmd::ArgFlags kArgFlags = cmd::kFixed; - static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); - - static uint32_t ComputeSize() { - return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT - } - - void SetHeader() { header.SetCmd<ValueType>(); } - - void Init(uint32_t _pnames_shm_id, - uint32_t _pnames_shm_offset, - GLuint _count, - uint32_t _results_shm_id, - uint32_t _results_shm_offset, - GLsizeiptr _size) { - SetHeader(); - pnames_shm_id = _pnames_shm_id; - pnames_shm_offset = _pnames_shm_offset; - count = _count; - results_shm_id = _results_shm_id; - results_shm_offset = _results_shm_offset; - size = _size; - } - - void* Set(void* cmd, - uint32_t _pnames_shm_id, - uint32_t _pnames_shm_offset, - GLuint _count, - uint32_t _results_shm_id, - uint32_t _results_shm_offset, - GLsizeiptr _size) { - static_cast<ValueType*>(cmd)->Init(_pnames_shm_id, _pnames_shm_offset, - _count, _results_shm_id, - _results_shm_offset, _size); - return NextCmdAddress<ValueType>(cmd); - } - - gpu::CommandHeader header; - uint32_t pnames_shm_id; - uint32_t pnames_shm_offset; - uint32_t count; - uint32_t results_shm_id; - uint32_t results_shm_offset; - int32_t size; -}; - -COMPILE_ASSERT(sizeof(GetMultipleIntegervCHROMIUM) == 28, - Sizeof_GetMultipleIntegervCHROMIUM_is_not_28); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, header) == 0, - OffsetOf_GetMultipleIntegervCHROMIUM_header_not_0); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, pnames_shm_id) == 4, - OffsetOf_GetMultipleIntegervCHROMIUM_pnames_shm_id_not_4); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, pnames_shm_offset) == 8, - OffsetOf_GetMultipleIntegervCHROMIUM_pnames_shm_offset_not_8); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, count) == 12, - OffsetOf_GetMultipleIntegervCHROMIUM_count_not_12); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, results_shm_id) == 16, - OffsetOf_GetMultipleIntegervCHROMIUM_results_shm_id_not_16); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, results_shm_offset) == 20, - OffsetOf_GetMultipleIntegervCHROMIUM_results_shm_offset_not_20); -COMPILE_ASSERT(offsetof(GetMultipleIntegervCHROMIUM, size) == 24, - OffsetOf_GetMultipleIntegervCHROMIUM_size_not_24); - struct GetProgramInfoCHROMIUM { typedef GetProgramInfoCHROMIUM ValueType; static const CommandId kCmdId = kGetProgramInfoCHROMIUM; @@ -8171,6 +8105,287 @@ offsetof(BindUniformLocationCHROMIUMBucket, name_bucket_id) == 12, OffsetOf_BindUniformLocationCHROMIUMBucket_name_bucket_id_not_12); +struct GenValuebuffersCHROMIUMImmediate { + typedef GenValuebuffersCHROMIUMImmediate ValueType; + static const CommandId kCmdId = kGenValuebuffersCHROMIUMImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeDataSize(GLsizei n) { + return static_cast<uint32_t>(sizeof(GLuint) * n); // NOLINT + } + + static uint32_t ComputeSize(GLsizei n) { + return static_cast<uint32_t>(sizeof(ValueType) + + ComputeDataSize(n)); // NOLINT + } + + void SetHeader(GLsizei n) { + header.SetCmdByTotalSize<ValueType>(ComputeSize(n)); + } + + void Init(GLsizei _n, GLuint* _buffers) { + SetHeader(_n); + n = _n; + memcpy(ImmediateDataAddress(this), _buffers, ComputeDataSize(_n)); + } + + void* Set(void* cmd, GLsizei _n, GLuint* _buffers) { + static_cast<ValueType*>(cmd)->Init(_n, _buffers); + const uint32_t size = ComputeSize(_n); + return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size); + } + + gpu::CommandHeader header; + int32_t n; +}; + +COMPILE_ASSERT(sizeof(GenValuebuffersCHROMIUMImmediate) == 8, + Sizeof_GenValuebuffersCHROMIUMImmediate_is_not_8); +COMPILE_ASSERT(offsetof(GenValuebuffersCHROMIUMImmediate, header) == 0, + OffsetOf_GenValuebuffersCHROMIUMImmediate_header_not_0); +COMPILE_ASSERT(offsetof(GenValuebuffersCHROMIUMImmediate, n) == 4, + OffsetOf_GenValuebuffersCHROMIUMImmediate_n_not_4); + +struct DeleteValuebuffersCHROMIUMImmediate { + typedef DeleteValuebuffersCHROMIUMImmediate ValueType; + static const CommandId kCmdId = kDeleteValuebuffersCHROMIUMImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeDataSize(GLsizei n) { + return static_cast<uint32_t>(sizeof(GLuint) * n); // NOLINT + } + + static uint32_t ComputeSize(GLsizei n) { + return static_cast<uint32_t>(sizeof(ValueType) + + ComputeDataSize(n)); // NOLINT + } + + void SetHeader(GLsizei n) { + header.SetCmdByTotalSize<ValueType>(ComputeSize(n)); + } + + void Init(GLsizei _n, const GLuint* _valuebuffers) { + SetHeader(_n); + n = _n; + memcpy(ImmediateDataAddress(this), _valuebuffers, ComputeDataSize(_n)); + } + + void* Set(void* cmd, GLsizei _n, const GLuint* _valuebuffers) { + static_cast<ValueType*>(cmd)->Init(_n, _valuebuffers); + const uint32_t size = ComputeSize(_n); + return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size); + } + + gpu::CommandHeader header; + int32_t n; +}; + +COMPILE_ASSERT(sizeof(DeleteValuebuffersCHROMIUMImmediate) == 8, + Sizeof_DeleteValuebuffersCHROMIUMImmediate_is_not_8); +COMPILE_ASSERT(offsetof(DeleteValuebuffersCHROMIUMImmediate, header) == 0, + OffsetOf_DeleteValuebuffersCHROMIUMImmediate_header_not_0); +COMPILE_ASSERT(offsetof(DeleteValuebuffersCHROMIUMImmediate, n) == 4, + OffsetOf_DeleteValuebuffersCHROMIUMImmediate_n_not_4); + +struct IsValuebufferCHROMIUM { + typedef IsValuebufferCHROMIUM ValueType; + static const CommandId kCmdId = kIsValuebufferCHROMIUM; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + typedef uint32_t Result; + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLuint _valuebuffer, + uint32_t _result_shm_id, + uint32_t _result_shm_offset) { + SetHeader(); + valuebuffer = _valuebuffer; + result_shm_id = _result_shm_id; + result_shm_offset = _result_shm_offset; + } + + void* Set(void* cmd, + GLuint _valuebuffer, + uint32_t _result_shm_id, + uint32_t _result_shm_offset) { + static_cast<ValueType*>(cmd) + ->Init(_valuebuffer, _result_shm_id, _result_shm_offset); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + uint32_t valuebuffer; + uint32_t result_shm_id; + uint32_t result_shm_offset; +}; + +COMPILE_ASSERT(sizeof(IsValuebufferCHROMIUM) == 16, + Sizeof_IsValuebufferCHROMIUM_is_not_16); +COMPILE_ASSERT(offsetof(IsValuebufferCHROMIUM, header) == 0, + OffsetOf_IsValuebufferCHROMIUM_header_not_0); +COMPILE_ASSERT(offsetof(IsValuebufferCHROMIUM, valuebuffer) == 4, + OffsetOf_IsValuebufferCHROMIUM_valuebuffer_not_4); +COMPILE_ASSERT(offsetof(IsValuebufferCHROMIUM, result_shm_id) == 8, + OffsetOf_IsValuebufferCHROMIUM_result_shm_id_not_8); +COMPILE_ASSERT(offsetof(IsValuebufferCHROMIUM, result_shm_offset) == 12, + OffsetOf_IsValuebufferCHROMIUM_result_shm_offset_not_12); + +struct BindValuebufferCHROMIUM { + typedef BindValuebufferCHROMIUM ValueType; + static const CommandId kCmdId = kBindValuebufferCHROMIUM; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLenum _target, GLuint _valuebuffer) { + SetHeader(); + target = _target; + valuebuffer = _valuebuffer; + } + + void* Set(void* cmd, GLenum _target, GLuint _valuebuffer) { + static_cast<ValueType*>(cmd)->Init(_target, _valuebuffer); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + uint32_t target; + uint32_t valuebuffer; +}; + +COMPILE_ASSERT(sizeof(BindValuebufferCHROMIUM) == 12, + Sizeof_BindValuebufferCHROMIUM_is_not_12); +COMPILE_ASSERT(offsetof(BindValuebufferCHROMIUM, header) == 0, + OffsetOf_BindValuebufferCHROMIUM_header_not_0); +COMPILE_ASSERT(offsetof(BindValuebufferCHROMIUM, target) == 4, + OffsetOf_BindValuebufferCHROMIUM_target_not_4); +COMPILE_ASSERT(offsetof(BindValuebufferCHROMIUM, valuebuffer) == 8, + OffsetOf_BindValuebufferCHROMIUM_valuebuffer_not_8); + +struct SubscribeValueCHROMIUM { + typedef SubscribeValueCHROMIUM ValueType; + static const CommandId kCmdId = kSubscribeValueCHROMIUM; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLenum _target, GLenum _subscription) { + SetHeader(); + target = _target; + subscription = _subscription; + } + + void* Set(void* cmd, GLenum _target, GLenum _subscription) { + static_cast<ValueType*>(cmd)->Init(_target, _subscription); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + uint32_t target; + uint32_t subscription; +}; + +COMPILE_ASSERT(sizeof(SubscribeValueCHROMIUM) == 12, + Sizeof_SubscribeValueCHROMIUM_is_not_12); +COMPILE_ASSERT(offsetof(SubscribeValueCHROMIUM, header) == 0, + OffsetOf_SubscribeValueCHROMIUM_header_not_0); +COMPILE_ASSERT(offsetof(SubscribeValueCHROMIUM, target) == 4, + OffsetOf_SubscribeValueCHROMIUM_target_not_4); +COMPILE_ASSERT(offsetof(SubscribeValueCHROMIUM, subscription) == 8, + OffsetOf_SubscribeValueCHROMIUM_subscription_not_8); + +struct PopulateSubscribedValuesCHROMIUM { + typedef PopulateSubscribedValuesCHROMIUM ValueType; + static const CommandId kCmdId = kPopulateSubscribedValuesCHROMIUM; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLenum _target) { + SetHeader(); + target = _target; + } + + void* Set(void* cmd, GLenum _target) { + static_cast<ValueType*>(cmd)->Init(_target); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + uint32_t target; +}; + +COMPILE_ASSERT(sizeof(PopulateSubscribedValuesCHROMIUM) == 8, + Sizeof_PopulateSubscribedValuesCHROMIUM_is_not_8); +COMPILE_ASSERT(offsetof(PopulateSubscribedValuesCHROMIUM, header) == 0, + OffsetOf_PopulateSubscribedValuesCHROMIUM_header_not_0); +COMPILE_ASSERT(offsetof(PopulateSubscribedValuesCHROMIUM, target) == 4, + OffsetOf_PopulateSubscribedValuesCHROMIUM_target_not_4); + +struct UniformValuebufferCHROMIUM { + typedef UniformValuebufferCHROMIUM ValueType; + static const CommandId kCmdId = kUniformValuebufferCHROMIUM; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLint _location, GLenum _target, GLenum _subscription) { + SetHeader(); + location = _location; + target = _target; + subscription = _subscription; + } + + void* Set(void* cmd, GLint _location, GLenum _target, GLenum _subscription) { + static_cast<ValueType*>(cmd)->Init(_location, _target, _subscription); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + int32_t location; + uint32_t target; + uint32_t subscription; +}; + +COMPILE_ASSERT(sizeof(UniformValuebufferCHROMIUM) == 16, + Sizeof_UniformValuebufferCHROMIUM_is_not_16); +COMPILE_ASSERT(offsetof(UniformValuebufferCHROMIUM, header) == 0, + OffsetOf_UniformValuebufferCHROMIUM_header_not_0); +COMPILE_ASSERT(offsetof(UniformValuebufferCHROMIUM, location) == 4, + OffsetOf_UniformValuebufferCHROMIUM_location_not_4); +COMPILE_ASSERT(offsetof(UniformValuebufferCHROMIUM, target) == 8, + OffsetOf_UniformValuebufferCHROMIUM_target_not_8); +COMPILE_ASSERT(offsetof(UniformValuebufferCHROMIUM, subscription) == 12, + OffsetOf_UniformValuebufferCHROMIUM_subscription_not_12); + struct BindTexImage2DCHROMIUM { typedef BindTexImage2DCHROMIUM ValueType; static const CommandId kCmdId = kBindTexImage2DCHROMIUM;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h index 7d037a6..d1470d4 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -2533,25 +2533,6 @@ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); } -TEST_F(GLES2FormatTest, GetMultipleIntegervCHROMIUM) { - cmds::GetMultipleIntegervCHROMIUM& cmd = - *GetBufferAs<cmds::GetMultipleIntegervCHROMIUM>(); - void* next_cmd = - cmd.Set(&cmd, static_cast<uint32_t>(11), static_cast<uint32_t>(12), - static_cast<GLuint>(13), static_cast<uint32_t>(14), - static_cast<uint32_t>(15), static_cast<GLsizeiptr>(16)); - EXPECT_EQ(static_cast<uint32_t>(cmds::GetMultipleIntegervCHROMIUM::kCmdId), - cmd.header.command); - EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); - EXPECT_EQ(static_cast<uint32_t>(11), cmd.pnames_shm_id); - EXPECT_EQ(static_cast<uint32_t>(12), cmd.pnames_shm_offset); - EXPECT_EQ(static_cast<GLuint>(13), cmd.count); - EXPECT_EQ(static_cast<uint32_t>(14), cmd.results_shm_id); - EXPECT_EQ(static_cast<uint32_t>(15), cmd.results_shm_offset); - EXPECT_EQ(static_cast<GLsizeiptr>(16), cmd.size); - CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); -} - TEST_F(GLES2FormatTest, GetProgramInfoCHROMIUM) { cmds::GetProgramInfoCHROMIUM& cmd = *GetBufferAs<cmds::GetProgramInfoCHROMIUM>(); @@ -2939,6 +2920,111 @@ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); } +TEST_F(GLES2FormatTest, GenValuebuffersCHROMIUMImmediate) { + static GLuint ids[] = { + 12, 23, 34, + }; + cmds::GenValuebuffersCHROMIUMImmediate& cmd = + *GetBufferAs<cmds::GenValuebuffersCHROMIUMImmediate>(); + void* next_cmd = cmd.Set(&cmd, static_cast<GLsizei>(arraysize(ids)), ids); + EXPECT_EQ( + static_cast<uint32_t>(cmds::GenValuebuffersCHROMIUMImmediate::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(cmd.n * 4u), + cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLsizei>(arraysize(ids)), cmd.n); + CheckBytesWrittenMatchesExpectedSize( + next_cmd, + sizeof(cmd) + RoundSizeToMultipleOfEntries(arraysize(ids) * 4u)); + // TODO(gman): Check that ids were inserted; +} + +TEST_F(GLES2FormatTest, DeleteValuebuffersCHROMIUMImmediate) { + static GLuint ids[] = { + 12, 23, 34, + }; + cmds::DeleteValuebuffersCHROMIUMImmediate& cmd = + *GetBufferAs<cmds::DeleteValuebuffersCHROMIUMImmediate>(); + void* next_cmd = cmd.Set(&cmd, static_cast<GLsizei>(arraysize(ids)), ids); + EXPECT_EQ( + static_cast<uint32_t>(cmds::DeleteValuebuffersCHROMIUMImmediate::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(cmd.n * 4u), + cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLsizei>(arraysize(ids)), cmd.n); + CheckBytesWrittenMatchesExpectedSize( + next_cmd, + sizeof(cmd) + RoundSizeToMultipleOfEntries(arraysize(ids) * 4u)); + // TODO(gman): Check that ids were inserted; +} + +TEST_F(GLES2FormatTest, IsValuebufferCHROMIUM) { + cmds::IsValuebufferCHROMIUM& cmd = + *GetBufferAs<cmds::IsValuebufferCHROMIUM>(); + void* next_cmd = + cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<uint32_t>(12), + static_cast<uint32_t>(13)); + EXPECT_EQ(static_cast<uint32_t>(cmds::IsValuebufferCHROMIUM::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLuint>(11), cmd.valuebuffer); + EXPECT_EQ(static_cast<uint32_t>(12), cmd.result_shm_id); + EXPECT_EQ(static_cast<uint32_t>(13), cmd.result_shm_offset); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + +TEST_F(GLES2FormatTest, BindValuebufferCHROMIUM) { + cmds::BindValuebufferCHROMIUM& cmd = + *GetBufferAs<cmds::BindValuebufferCHROMIUM>(); + void* next_cmd = + cmd.Set(&cmd, static_cast<GLenum>(11), static_cast<GLuint>(12)); + EXPECT_EQ(static_cast<uint32_t>(cmds::BindValuebufferCHROMIUM::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLenum>(11), cmd.target); + EXPECT_EQ(static_cast<GLuint>(12), cmd.valuebuffer); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + +TEST_F(GLES2FormatTest, SubscribeValueCHROMIUM) { + cmds::SubscribeValueCHROMIUM& cmd = + *GetBufferAs<cmds::SubscribeValueCHROMIUM>(); + void* next_cmd = + cmd.Set(&cmd, static_cast<GLenum>(11), static_cast<GLenum>(12)); + EXPECT_EQ(static_cast<uint32_t>(cmds::SubscribeValueCHROMIUM::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLenum>(11), cmd.target); + EXPECT_EQ(static_cast<GLenum>(12), cmd.subscription); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + +TEST_F(GLES2FormatTest, PopulateSubscribedValuesCHROMIUM) { + cmds::PopulateSubscribedValuesCHROMIUM& cmd = + *GetBufferAs<cmds::PopulateSubscribedValuesCHROMIUM>(); + void* next_cmd = cmd.Set(&cmd, static_cast<GLenum>(11)); + EXPECT_EQ( + static_cast<uint32_t>(cmds::PopulateSubscribedValuesCHROMIUM::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLenum>(11), cmd.target); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + +TEST_F(GLES2FormatTest, UniformValuebufferCHROMIUM) { + cmds::UniformValuebufferCHROMIUM& cmd = + *GetBufferAs<cmds::UniformValuebufferCHROMIUM>(); + void* next_cmd = cmd.Set(&cmd, static_cast<GLint>(11), + static_cast<GLenum>(12), static_cast<GLenum>(13)); + EXPECT_EQ(static_cast<uint32_t>(cmds::UniformValuebufferCHROMIUM::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLint>(11), cmd.location); + EXPECT_EQ(static_cast<GLenum>(12), cmd.target); + EXPECT_EQ(static_cast<GLenum>(13), cmd.subscription); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + TEST_F(GLES2FormatTest, BindTexImage2DCHROMIUM) { cmds::BindTexImage2DCHROMIUM& cmd = *GetBufferAs<cmds::BindTexImage2DCHROMIUM>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h index 8bd5c0a..9c98f51 100644 --- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -178,39 +178,45 @@ OP(ResizeCHROMIUM) /* 419 */ \ OP(GetRequestableExtensionsCHROMIUM) /* 420 */ \ OP(RequestExtensionCHROMIUM) /* 421 */ \ - OP(GetMultipleIntegervCHROMIUM) /* 422 */ \ - OP(GetProgramInfoCHROMIUM) /* 423 */ \ - OP(GetTranslatedShaderSourceANGLE) /* 424 */ \ - OP(PostSubBufferCHROMIUM) /* 425 */ \ - OP(TexImageIOSurface2DCHROMIUM) /* 426 */ \ - OP(CopyTextureCHROMIUM) /* 427 */ \ - OP(DrawArraysInstancedANGLE) /* 428 */ \ - OP(DrawElementsInstancedANGLE) /* 429 */ \ - OP(VertexAttribDivisorANGLE) /* 430 */ \ - OP(GenMailboxCHROMIUM) /* 431 */ \ - OP(ProduceTextureCHROMIUMImmediate) /* 432 */ \ - OP(ProduceTextureDirectCHROMIUMImmediate) /* 433 */ \ - OP(ConsumeTextureCHROMIUMImmediate) /* 434 */ \ - OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 435 */ \ - OP(BindUniformLocationCHROMIUMBucket) /* 436 */ \ - OP(BindTexImage2DCHROMIUM) /* 437 */ \ - OP(ReleaseTexImage2DCHROMIUM) /* 438 */ \ - OP(TraceBeginCHROMIUM) /* 439 */ \ - OP(TraceEndCHROMIUM) /* 440 */ \ - OP(AsyncTexSubImage2DCHROMIUM) /* 441 */ \ - OP(AsyncTexImage2DCHROMIUM) /* 442 */ \ - OP(WaitAsyncTexImage2DCHROMIUM) /* 443 */ \ - OP(WaitAllAsyncTexImage2DCHROMIUM) /* 444 */ \ - OP(DiscardFramebufferEXTImmediate) /* 445 */ \ - OP(LoseContextCHROMIUM) /* 446 */ \ - OP(InsertSyncPointCHROMIUM) /* 447 */ \ - OP(WaitSyncPointCHROMIUM) /* 448 */ \ - OP(DrawBuffersEXTImmediate) /* 449 */ \ - OP(DiscardBackbufferCHROMIUM) /* 450 */ \ - OP(ScheduleOverlayPlaneCHROMIUM) /* 451 */ \ - OP(MatrixLoadfCHROMIUMImmediate) /* 452 */ \ - OP(MatrixLoadIdentityCHROMIUM) /* 453 */ \ - OP(BlendBarrierKHR) /* 454 */ + OP(GetProgramInfoCHROMIUM) /* 422 */ \ + OP(GetTranslatedShaderSourceANGLE) /* 423 */ \ + OP(PostSubBufferCHROMIUM) /* 424 */ \ + OP(TexImageIOSurface2DCHROMIUM) /* 425 */ \ + OP(CopyTextureCHROMIUM) /* 426 */ \ + OP(DrawArraysInstancedANGLE) /* 427 */ \ + OP(DrawElementsInstancedANGLE) /* 428 */ \ + OP(VertexAttribDivisorANGLE) /* 429 */ \ + OP(GenMailboxCHROMIUM) /* 430 */ \ + OP(ProduceTextureCHROMIUMImmediate) /* 431 */ \ + OP(ProduceTextureDirectCHROMIUMImmediate) /* 432 */ \ + OP(ConsumeTextureCHROMIUMImmediate) /* 433 */ \ + OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 434 */ \ + OP(BindUniformLocationCHROMIUMBucket) /* 435 */ \ + OP(GenValuebuffersCHROMIUMImmediate) /* 436 */ \ + OP(DeleteValuebuffersCHROMIUMImmediate) /* 437 */ \ + OP(IsValuebufferCHROMIUM) /* 438 */ \ + OP(BindValuebufferCHROMIUM) /* 439 */ \ + OP(SubscribeValueCHROMIUM) /* 440 */ \ + OP(PopulateSubscribedValuesCHROMIUM) /* 441 */ \ + OP(UniformValuebufferCHROMIUM) /* 442 */ \ + OP(BindTexImage2DCHROMIUM) /* 443 */ \ + OP(ReleaseTexImage2DCHROMIUM) /* 444 */ \ + OP(TraceBeginCHROMIUM) /* 445 */ \ + OP(TraceEndCHROMIUM) /* 446 */ \ + OP(AsyncTexSubImage2DCHROMIUM) /* 447 */ \ + OP(AsyncTexImage2DCHROMIUM) /* 448 */ \ + OP(WaitAsyncTexImage2DCHROMIUM) /* 449 */ \ + OP(WaitAllAsyncTexImage2DCHROMIUM) /* 450 */ \ + OP(DiscardFramebufferEXTImmediate) /* 451 */ \ + OP(LoseContextCHROMIUM) /* 452 */ \ + OP(InsertSyncPointCHROMIUM) /* 453 */ \ + OP(WaitSyncPointCHROMIUM) /* 454 */ \ + OP(DrawBuffersEXTImmediate) /* 455 */ \ + OP(DiscardBackbufferCHROMIUM) /* 456 */ \ + OP(ScheduleOverlayPlaneCHROMIUM) /* 457 */ \ + OP(MatrixLoadfCHROMIUMImmediate) /* 458 */ \ + OP(MatrixLoadIdentityCHROMIUM) /* 459 */ \ + OP(BlendBarrierKHR) /* 460 */ enum CommandId { kStartPoint = cmd::kLastCommonId, // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h index 1871201..e8631e6 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
@@ -55,6 +55,7 @@ static std::string GetStringSrcBlendFactor(uint32_t value); static std::string GetStringStencilOp(uint32_t value); static std::string GetStringStringType(uint32_t value); +static std::string GetStringSubscriptionTarget(uint32_t value); static std::string GetStringTextureBindTarget(uint32_t value); static std::string GetStringTextureFormat(uint32_t value); static std::string GetStringTextureInternalFormat(uint32_t value); @@ -66,6 +67,7 @@ static std::string GetStringTextureTarget(uint32_t value); static std::string GetStringTextureUsage(uint32_t value); static std::string GetStringTextureWrapMode(uint32_t value); +static std::string GetStringValueBufferTarget(uint32_t value); static std::string GetStringVertexAttribType(uint32_t value); static std::string GetStringVertexAttribute(uint32_t value); static std::string GetStringVertexPointer(uint32_t value);
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h index aba98aa..1a0945a 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -3065,6 +3065,14 @@ "GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT", }, { + 0x924C, + "GL_MOUSE_POSITION_CHROMIUM", + }, + { + 0x924B, + "GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM", + }, + { 0x924A, "GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM", }, @@ -4355,6 +4363,14 @@ arraysize(string_table), value); } +std::string GLES2Util::GetStringSubscriptionTarget(uint32_t value) { + static const EnumToString string_table[] = { + {GL_MOUSE_POSITION_CHROMIUM, "GL_MOUSE_POSITION_CHROMIUM"}, + }; + return GLES2Util::GetQualifiedEnumString(string_table, + arraysize(string_table), value); +} + std::string GLES2Util::GetStringTextureBindTarget(uint32_t value) { static const EnumToString string_table[] = { {GL_TEXTURE_2D, "GL_TEXTURE_2D"}, @@ -4479,6 +4495,15 @@ arraysize(string_table), value); } +std::string GLES2Util::GetStringValueBufferTarget(uint32_t value) { + static const EnumToString string_table[] = { + {GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + "GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM"}, + }; + return GLES2Util::GetQualifiedEnumString(string_table, + arraysize(string_table), value); +} + std::string GLES2Util::GetStringVertexAttribType(uint32_t value) { static const EnumToString string_table[] = { {GL_BYTE, "GL_BYTE"},
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn index 8293fbc..4013c3a 100644 --- a/gpu/command_buffer/service/BUILD.gn +++ b/gpu/command_buffer/service/BUILD.gn
@@ -107,6 +107,8 @@ "texture_manager.cc", "transfer_buffer_manager.cc", "transfer_buffer_manager.h", + "valuebuffer_manager.h", + "valuebuffer_manager.cc", "vertex_array_manager.h", "vertex_array_manager.cc", "vertex_attrib_manager.h",
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc index 00e7ec3..6ee57e4 100644 --- a/gpu/command_buffer/service/context_group.cc +++ b/gpu/command_buffer/service/context_group.cc
@@ -21,6 +21,7 @@ #include "gpu/command_buffer/service/shader_manager.h" #include "gpu/command_buffer/service/texture_manager.h" #include "gpu/command_buffer/service/transfer_buffer_manager.h" +#include "gpu/command_buffer/service/valuebuffer_manager.h" #include "ui/gl/gl_implementation.h" namespace gpu { @@ -121,6 +122,7 @@ renderbuffer_manager_.reset(new RenderbufferManager( memory_tracker_.get(), max_renderbuffer_size, max_samples, depth24_supported)); + valuebuffer_manager_.reset(new ValuebufferManager()); shader_manager_.reset(new ShaderManager()); // Lookup GL things we need to know. @@ -300,6 +302,11 @@ renderbuffer_manager_.reset(); } + if (valuebuffer_manager_ != NULL) { + valuebuffer_manager_->Destroy(); + valuebuffer_manager_.reset(); + } + if (texture_manager_ != NULL) { texture_manager_->Destroy(have_context); texture_manager_.reset();
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h index ae4550c..eb53252 100644 --- a/gpu/command_buffer/service/context_group.h +++ b/gpu/command_buffer/service/context_group.h
@@ -34,6 +34,7 @@ class ProgramManager; class ShaderManager; class TextureManager; +class ValuebufferManager; class MemoryTracker; struct DisallowedFeatures; @@ -126,6 +127,10 @@ return renderbuffer_manager_.get(); } + ValuebufferManager* valuebuffer_manager() const { + return valuebuffer_manager_.get(); + } + TextureManager* texture_manager() const { return texture_manager_.get(); } @@ -199,6 +204,8 @@ scoped_ptr<RenderbufferManager> renderbuffer_manager_; + scoped_ptr<ValuebufferManager> valuebuffer_manager_; + scoped_ptr<TextureManager> texture_manager_; scoped_ptr<ProgramManager> program_manager_;
diff --git a/gpu/command_buffer/service/context_state.h b/gpu/command_buffer/service/context_state.h index 7488f57..b4e812c 100644 --- a/gpu/command_buffer/service/context_state.h +++ b/gpu/command_buffer/service/context_state.h
@@ -13,6 +13,7 @@ #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/query_manager.h" #include "gpu/command_buffer/service/texture_manager.h" +#include "gpu/command_buffer/service/valuebuffer_manager.h" #include "gpu/command_buffer/service/vertex_attrib_manager.h" #include "gpu/command_buffer/service/vertex_array_manager.h" #include "gpu/gpu_export.h" @@ -200,6 +201,9 @@ scoped_refptr<Renderbuffer> bound_renderbuffer; bool bound_renderbuffer_valid; + // The currently bound valuebuffer + scoped_refptr<Valuebuffer> bound_valuebuffer; + // A map of of target -> Query for current queries typedef std::map<GLuint, scoped_refptr<QueryManager::Query> > QueryMap; QueryMap current_queries;
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc index 9be747d..24e1f92 100644 --- a/gpu/command_buffer/service/feature_info.cc +++ b/gpu/command_buffer/service/feature_info.cc
@@ -477,16 +477,22 @@ enable_texture_half_float_linear = true; may_enable_chromium_color_buffer_float = true; } else { - if (is_es3 || extensions.Contains("GL_OES_texture_float")) { + // GLES3 adds support for Float type by default but it doesn't support all + // formats as GL_OES_texture_float(i.e.LUMINANCE_ALPHA,LUMINANCE and Alpha) + if (extensions.Contains("GL_OES_texture_float")) { enable_texture_float = true; if (extensions.Contains("GL_OES_texture_float_linear")) { enable_texture_float_linear = true; } + // This extension allows a variety of floating point formats to be + // rendered to via framebuffer objects. Enable it's usage only if + // support for Floating textures is enabled. if ((is_es3 && extensions.Contains("GL_EXT_color_buffer_float")) || feature_flags_.is_angle) { may_enable_chromium_color_buffer_float = true; } } + // TODO(dshwang): GLES3 supports half float by default but GL_HALF_FLOAT_OES // isn't equal to GL_HALF_FLOAT. if (extensions.Contains("GL_OES_texture_half_float")) {
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc index 69bd4d3..88a0a37 100644 --- a/gpu/command_buffer/service/feature_info_unittest.cc +++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -669,6 +669,26 @@ GL_RGB32F)); } +TEST_F(FeatureInfoTest, Initialize_texture_floatGLES3) { + SetupInitExpectationsWithGLVersion("", "", "OpenGL ES 3.0"); + EXPECT_THAT(info_->extensions(), Not(HasSubstr("GL_OES_texture_float"))); + EXPECT_THAT(info_->extensions(), Not(HasSubstr("GL_OES_texture_half_float"))); + EXPECT_THAT(info_->extensions(), + Not(HasSubstr("GL_OES_texture_float_linear"))); + EXPECT_THAT(info_->extensions(), + Not(HasSubstr("GL_OES_texture_half_float_linear"))); + EXPECT_FALSE(info_->GetTextureFormatValidator(GL_RGB).IsValid( + GL_FLOAT)); + EXPECT_FALSE(info_->GetTextureFormatValidator(GL_RGBA).IsValid( + GL_FLOAT)); + EXPECT_FALSE(info_->GetTextureFormatValidator(GL_LUMINANCE).IsValid( + GL_FLOAT)); + EXPECT_FALSE(info_->GetTextureFormatValidator(GL_LUMINANCE_ALPHA).IsValid( + GL_FLOAT)); + EXPECT_FALSE(info_->GetTextureFormatValidator(GL_ALPHA).IsValid( + GL_FLOAT)); +} + TEST_F(FeatureInfoTest, InitializeOES_texture_floatGLES2) { SetupInitExpectations("GL_OES_texture_float"); EXPECT_FALSE(info_->feature_flags().enable_texture_float_linear);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 9b028fd..4d54739 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -56,6 +56,7 @@ #include "gpu/command_buffer/service/shader_translator.h" #include "gpu/command_buffer/service/shader_translator_cache.h" #include "gpu/command_buffer/service/texture_manager.h" +#include "gpu/command_buffer/service/valuebuffer_manager.h" #include "gpu/command_buffer/service/vertex_array_manager.h" #include "gpu/command_buffer/service/vertex_attrib_manager.h" #include "third_party/smhasher/src/City.h" @@ -627,7 +628,7 @@ return vertex_array_manager_.get(); } ImageManager* GetImageManager() override { return image_manager_.get(); } - bool ProcessPendingQueries() override; + bool ProcessPendingQueries(bool did_finish) override; bool HasMoreIdleWork() override; void PerformIdleWork() override; @@ -727,6 +728,8 @@ void DeleteFramebuffersHelper(GLsizei n, const GLuint* client_ids); bool GenRenderbuffersHelper(GLsizei n, const GLuint* client_ids); void DeleteRenderbuffersHelper(GLsizei n, const GLuint* client_ids); + bool GenValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* client_ids); + void DeleteValuebuffersCHROMIUMHelper(GLsizei n, const GLuint* client_ids); bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids); void DeleteQueriesEXTHelper(GLsizei n, const GLuint* client_ids); bool GenVertexArraysOESHelper(GLsizei n, const GLuint* client_ids); @@ -756,6 +759,10 @@ return group_->framebuffer_manager(); } + ValuebufferManager* valuebuffer_manager() { + return group_->valuebuffer_manager(); + } + ProgramManager* program_manager() { return group_->program_manager(); } @@ -944,6 +951,14 @@ void DoCreateAndConsumeTextureCHROMIUM(GLenum target, const GLbyte* key, GLuint client_id); + bool DoIsValuebufferCHROMIUM(GLuint client_id); + void DoBindValueBufferCHROMIUM(GLenum target, GLuint valuebuffer); + void DoSubscribeValueCHROMIUM(GLenum target, GLenum subscription); + void DoPopulateSubscribedValuesCHROMIUM(GLenum target); + void DoUniformValueBufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription); + void DoBindTexImage2DCHROMIUM( GLenum target, GLint image_id); @@ -1096,6 +1111,21 @@ renderbuffer_manager()->RemoveRenderbuffer(client_id); } + // Creates a valuebuffer info for the given valuebuffer. + void CreateValuebuffer(GLuint client_id) { + return valuebuffer_manager()->CreateValuebuffer(client_id); + } + + // Gets the valuebuffer info for a given valuebuffer. + Valuebuffer* GetValuebuffer(GLuint client_id) { + return valuebuffer_manager()->GetValuebuffer(client_id); + } + + // Removes the valuebuffer info for the given valuebuffer. + void RemoveValuebuffer(GLuint client_id) { + valuebuffer_manager()->RemoveValuebuffer(client_id); + } + // Gets the vertex attrib manager for the given vertex array. VertexAttribManager* GetVertexAttribManager(GLuint client_id) { VertexAttribManager* info = @@ -1177,6 +1207,22 @@ GLenum target, const char* func_name); + // Check if the current valuebuffer exists and is valid. If not generates + // the appropriate GL error. Returns true if the current valuebuffer is in + // a usable state. + bool CheckCurrentValuebuffer(const char* function_name); + + // Check if the current valuebuffer exists and is valiud and that the + // value buffer is actually subscribed to the given subscription + bool CheckCurrentValuebufferForSubscription(GLenum subscription, + const char* function_name); + + // Check if the location can be used for the given subscription target. If not + // generates the appropriate GL error. Returns true if the location is usable + bool CheckSubscriptionTarget(GLint location, + GLenum subscription, + const char* function_name); + // Checks if the current program exists and is valid. If not generates the // appropriate GL error. Returns true if the current program is in a usable // state. @@ -1193,6 +1239,13 @@ // of the draw operation are the same. bool CheckDrawingFeedbackLoops(); + // Checks if |api_type| is valid for the given uniform + // If the api type is not valid generates the appropriate GL + // error. Returns true if |api_type| is valid for the uniform + bool CheckUniformForApiType(const Program::UniformInfo* info, + const char* function_name, + Program::UniformApiType api_type); + // Gets the type of a uniform for a location in the current program. Sets GL // errors if the current program is not valid. Returns true if the current // program is valid and the location exists. Adjusts count so it @@ -2670,6 +2723,7 @@ DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); DoBindFramebuffer(GL_FRAMEBUFFER, 0); DoBindRenderbuffer(GL_RENDERBUFFER, 0); + DoBindValueBufferCHROMIUM(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, 0); bool call_gl_clear = !surfaceless_; #if defined(OS_ANDROID) @@ -2719,6 +2773,34 @@ DCHECK(initialized()); Capabilities caps; + caps.VisitPrecisions([](GLenum shader, GLenum type, + Capabilities::ShaderPrecision* shader_precision) { + GLint range[2] = {0, 0}; + GLint precision = 0; + GetShaderPrecisionFormatImpl(shader, type, range, &precision); + shader_precision->min_range = range[0]; + shader_precision->max_range = range[1]; + shader_precision->precision = precision; + }); + DoGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, + &caps.max_combined_texture_image_units); + DoGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &caps.max_cube_map_texture_size); + DoGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, + &caps.max_fragment_uniform_vectors); + DoGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &caps.max_renderbuffer_size); + DoGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &caps.max_texture_image_units); + DoGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.max_texture_size); + DoGetIntegerv(GL_MAX_VARYING_VECTORS, &caps.max_varying_vectors); + DoGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.max_vertex_attribs); + DoGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, + &caps.max_vertex_texture_image_units); + DoGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, + &caps.max_vertex_uniform_vectors); + DoGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, + &caps.num_compressed_texture_formats); + DoGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &caps.num_shader_binary_formats); + DoGetIntegerv(GL_BIND_GENERATES_RESOURCE_CHROMIUM, + &caps.bind_generates_resource_chromium); caps.egl_image_external = feature_info_->feature_flags().oes_egl_image_external; @@ -2907,6 +2989,19 @@ return true; } +bool GLES2DecoderImpl::GenValuebuffersCHROMIUMHelper(GLsizei n, + const GLuint* client_ids) { + for (GLsizei ii = 0; ii < n; ++ii) { + if (GetValuebuffer(client_ids[ii])) { + return false; + } + } + for (GLsizei ii = 0; ii < n; ++ii) { + CreateValuebuffer(client_ids[ii]); + } + return true; +} + bool GLES2DecoderImpl::GenTexturesHelper(GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { if (GetTexture(client_ids[ii])) { @@ -2996,6 +3091,20 @@ } } +void GLES2DecoderImpl::DeleteValuebuffersCHROMIUMHelper( + GLsizei n, + const GLuint* client_ids) { + for (GLsizei ii = 0; ii < n; ++ii) { + Valuebuffer* valuebuffer = GetValuebuffer(client_ids[ii]); + if (valuebuffer) { + if (state_.bound_valuebuffer.get() == valuebuffer) { + state_.bound_valuebuffer = NULL; + } + RemoveValuebuffer(client_ids[ii]); + } + } +} + void GLES2DecoderImpl::DeleteTexturesHelper( GLsizei n, const GLuint* client_ids) { bool supports_separate_framebuffer_binds = @@ -3425,6 +3534,7 @@ framebuffer_state_.bound_read_framebuffer = NULL; framebuffer_state_.bound_draw_framebuffer = NULL; state_.bound_renderbuffer = NULL; + state_.bound_valuebuffer = NULL; if (offscreen_saved_color_texture_info_.get()) { DCHECK(offscreen_target_color_texture_); @@ -3916,12 +4026,12 @@ void GLES2DecoderImpl::DoFinish() { glFinish(); ProcessPendingReadPixels(); - ProcessPendingQueries(); + ProcessPendingQueries(true); } void GLES2DecoderImpl::DoFlush() { glFlush(); - ProcessPendingQueries(); + ProcessPendingQueries(false); } void GLES2DecoderImpl::DoActiveTexture(GLenum texture_unit) { @@ -5722,6 +5832,55 @@ "glTexParameteriv", GetErrorState(), texture, pname, *params); } +bool GLES2DecoderImpl::CheckCurrentValuebuffer(const char* function_name) { + if (!state_.bound_valuebuffer.get()) { + // There is no valuebuffer bound + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, + "no valuebuffer in use"); + return false; + } + return true; +} + +bool GLES2DecoderImpl::CheckCurrentValuebufferForSubscription( + GLenum subscription, + const char* function_name) { + if (!CheckCurrentValuebuffer(function_name)) { + return false; + } + if (!state_.bound_valuebuffer.get()->IsSubscribed(subscription)) { + // The valuebuffer is not subscribed to the target + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, + "valuebuffer is not subscribed"); + return false; + } + return true; +} + +bool GLES2DecoderImpl::CheckSubscriptionTarget(GLint location, + GLenum subscription, + const char* function_name) { + if (!CheckCurrentProgramForUniform(location, function_name)) { + return false; + } + GLint real_location = -1; + GLint array_index = -1; + const Program::UniformInfo* info = + state_.current_program->GetUniformInfoByFakeLocation( + location, &real_location, &array_index); + if (!info) { + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "unknown location"); + return false; + } + if ((ValuebufferManager::ApiTypeForSubscriptionTarget(subscription) & + info->accepts_api_type) == 0) { + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, + "wrong type for subscription"); + return false; + } + return true; +} + bool GLES2DecoderImpl::CheckCurrentProgram(const char* function_name) { if (!state_.current_program.get()) { // The program does not exist. @@ -5777,6 +5936,19 @@ return false; } +bool GLES2DecoderImpl::CheckUniformForApiType( + const Program::UniformInfo* info, + const char* function_name, + Program::UniformApiType api_type) { + DCHECK(info); + if ((api_type & info->accepts_api_type) == 0) { + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, + "wrong uniform function for type"); + return false; + } + return true; +} + bool GLES2DecoderImpl::PrepForSetUniformByLocation( GLint fake_location, const char* function_name, @@ -5800,11 +5972,7 @@ GL_INVALID_OPERATION, function_name, "unknown location"); return false; } - - if ((api_type & info->accepts_api_type) == 0) { - LOCAL_SET_GL_ERROR( - GL_INVALID_OPERATION, function_name, - "wrong uniform function for type"); + if (!CheckUniformForApiType(info, function_name, api_type)) { return false; } if (*count > 1 && !info->is_array) { @@ -7608,7 +7776,7 @@ } } else { if (async && features().use_async_readpixels) { - GLuint buffer; + GLuint buffer = 0; glGenBuffersARB(1, &buffer); glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, buffer); glBufferData(GL_PIXEL_PACK_BUFFER_ARB, pixels_size, NULL, GL_STREAM_READ); @@ -7627,6 +7795,7 @@ } else { // On error, unbind pack buffer and fall through to sync readpixels glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); + glDeleteBuffersARB(1, &buffer); } } glReadPixels(x, y, width, height, format, type, pixels); @@ -9572,88 +9741,6 @@ return error::kNoError; } -error::Error GLES2DecoderImpl::HandleGetMultipleIntegervCHROMIUM( - uint32 immediate_data_size, - const void* cmd_data) { - const gles2::cmds::GetMultipleIntegervCHROMIUM& c = - *static_cast<const gles2::cmds::GetMultipleIntegervCHROMIUM*>(cmd_data); - GLuint count = c.count; - uint32 pnames_size; - if (!SafeMultiplyUint32(count, sizeof(GLenum), &pnames_size)) { - return error::kOutOfBounds; - } - const GLenum* pnames = GetSharedMemoryAs<const GLenum*>( - c.pnames_shm_id, c.pnames_shm_offset, pnames_size); - if (pnames == NULL) { - return error::kOutOfBounds; - } - - // We have to copy them since we use them twice so the client - // can't change them between the time we validate them and the time we use - // them. - scoped_ptr<GLenum[]> enums(new GLenum[count]); - memcpy(enums.get(), pnames, pnames_size); - - // Count up the space needed for the result. - uint32 num_results = 0; - for (GLuint ii = 0; ii < count; ++ii) { - uint32 num = util_.GLGetNumValuesReturned(enums[ii]); - if (num == 0) { - LOCAL_SET_GL_ERROR_INVALID_ENUM( - "glGetMultipleCHROMIUM", enums[ii], "pname"); - return error::kNoError; - } - // Num will never be more than 4. - DCHECK_LE(num, 4u); - if (!SafeAddUint32(num_results, num, &num_results)) { - return error::kOutOfBounds; - } - } - - uint32 result_size = 0; - if (!SafeMultiplyUint32(num_results, sizeof(GLint), &result_size)) { - return error::kOutOfBounds; - } - - if (result_size != static_cast<uint32>(c.size)) { - LOCAL_SET_GL_ERROR( - GL_INVALID_VALUE, - "glGetMultipleCHROMIUM", "bad size GL_INVALID_VALUE"); - return error::kNoError; - } - - GLint* results = GetSharedMemoryAs<GLint*>( - c.results_shm_id, c.results_shm_offset, result_size); - if (results == NULL) { - return error::kOutOfBounds; - } - - // Check the results have been cleared in case the context was lost. - for (uint32 ii = 0; ii < num_results; ++ii) { - if (results[ii]) { - return error::kInvalidArguments; - } - } - - // Get each result. - GLint* start = results; - for (GLuint ii = 0; ii < count; ++ii) { - GLsizei num_written = 0; - if (!state_.GetStateAsGLint(enums[ii], results, &num_written) && - !GetHelper(enums[ii], results, &num_written)) { - DoGetIntegerv(enums[ii], results); - } - results += num_written; - } - - // Just to verify. Should this be a DCHECK? - if (static_cast<uint32>(results - start) != num_results) { - return error::kOutOfBounds; - } - - return error::kNoError; -} - error::Error GLES2DecoderImpl::HandleGetProgramInfoCHROMIUM( uint32 immediate_data_size, const void* cmd_data) { @@ -9817,11 +9904,11 @@ } } -bool GLES2DecoderImpl::ProcessPendingQueries() { +bool GLES2DecoderImpl::ProcessPendingQueries(bool did_finish) { if (query_manager_.get() == NULL) { return false; } - if (!query_manager_->ProcessPendingQueries()) { + if (!query_manager_->ProcessPendingQueries(did_finish)) { current_decoder_error_ = error::kOutOfBounds; } return query_manager_->HavePendingQueries(); @@ -10696,6 +10783,73 @@ texture_ref = texture_manager()->Consume(client_id, texture); } +bool GLES2DecoderImpl::DoIsValuebufferCHROMIUM(GLuint client_id) { + const Valuebuffer* valuebuffer = GetValuebuffer(client_id); + return valuebuffer && valuebuffer->IsValid(); +} + +void GLES2DecoderImpl::DoBindValueBufferCHROMIUM(GLenum target, + GLuint client_id) { + Valuebuffer* valuebuffer = NULL; + if (client_id != 0) { + valuebuffer = GetValuebuffer(client_id); + if (!valuebuffer) { + if (!group_->bind_generates_resource()) { + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBindValuebufferCHROMIUM", + "id not generated by glBindValuebufferCHROMIUM"); + return; + } + + // It's a new id so make a valuebuffer for it. + CreateValuebuffer(client_id); + valuebuffer = GetValuebuffer(client_id); + } + valuebuffer->MarkAsValid(); + } + state_.bound_valuebuffer = valuebuffer; +} + +void GLES2DecoderImpl::DoSubscribeValueCHROMIUM(GLenum target, + GLenum subscription) { + if (!CheckCurrentValuebuffer("glSubscribeValueCHROMIUM")) { + return; + } + state_.bound_valuebuffer.get()->AddSubscription(subscription); +} + +void GLES2DecoderImpl::DoPopulateSubscribedValuesCHROMIUM(GLenum target) { + if (!CheckCurrentValuebuffer("glPopulateSubscribedValuesCHROMIUM")) { + return; + } + valuebuffer_manager()->UpdateValuebufferState(state_.bound_valuebuffer.get()); +} + +void GLES2DecoderImpl::DoUniformValueBufferCHROMIUM(GLint location, + GLenum target, + GLenum subscription) { + if (!CheckCurrentValuebufferForSubscription( + subscription, "glPopulateSubscribedValuesCHROMIUM")) { + return; + } + if (!CheckSubscriptionTarget(location, subscription, + "glPopulateSubscribedValuesCHROMIUM")) { + return; + } + const ValueState* state = + state_.bound_valuebuffer.get()->GetState(subscription); + if (state) { + switch (subscription) { + case GL_MOUSE_POSITION_CHROMIUM: + DoUniform2iv(location, 1, state->int_value); + break; + default: + NOTREACHED() << "Unhandled uniform subscription target " + << subscription; + break; + } + } +} + void GLES2DecoderImpl::DoInsertEventMarkerEXT( GLsizei length, const GLchar* marker) { if (!marker) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h index 8d72335..63618c2 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -171,7 +171,7 @@ virtual ImageManager* GetImageManager() = 0; // Process any pending queries. Returns false if there are no pending queries. - virtual bool ProcessPendingQueries() = 0; + virtual bool ProcessPendingQueries(bool did_finish) = 0; // Returns false if there are no idle work to be made. virtual bool HasMoreIdleWork() = 0;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index 86b36ca..ee3bba5 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -3109,6 +3109,146 @@ return error::kNoError; } +error::Error GLES2DecoderImpl::HandleGenValuebuffersCHROMIUMImmediate( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::GenValuebuffersCHROMIUMImmediate& c = + *static_cast<const gles2::cmds::GenValuebuffersCHROMIUMImmediate*>( + cmd_data); + (void)c; + GLsizei n = static_cast<GLsizei>(c.n); + uint32_t data_size; + if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { + return error::kOutOfBounds; + } + GLuint* buffers = + GetImmediateDataAs<GLuint*>(c, data_size, immediate_data_size); + if (buffers == NULL) { + return error::kOutOfBounds; + } + if (!GenValuebuffersCHROMIUMHelper(n, buffers)) { + return error::kInvalidArguments; + } + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleDeleteValuebuffersCHROMIUMImmediate( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::DeleteValuebuffersCHROMIUMImmediate& c = + *static_cast<const gles2::cmds::DeleteValuebuffersCHROMIUMImmediate*>( + cmd_data); + (void)c; + GLsizei n = static_cast<GLsizei>(c.n); + uint32_t data_size; + if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { + return error::kOutOfBounds; + } + const GLuint* valuebuffers = + GetImmediateDataAs<const GLuint*>(c, data_size, immediate_data_size); + if (valuebuffers == NULL) { + return error::kOutOfBounds; + } + DeleteValuebuffersCHROMIUMHelper(n, valuebuffers); + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleIsValuebufferCHROMIUM( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::IsValuebufferCHROMIUM& c = + *static_cast<const gles2::cmds::IsValuebufferCHROMIUM*>(cmd_data); + (void)c; + GLuint valuebuffer = c.valuebuffer; + typedef cmds::IsValuebufferCHROMIUM::Result Result; + Result* result_dst = GetSharedMemoryAs<Result*>( + c.result_shm_id, c.result_shm_offset, sizeof(*result_dst)); + if (!result_dst) { + return error::kOutOfBounds; + } + *result_dst = DoIsValuebufferCHROMIUM(valuebuffer); + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleBindValuebufferCHROMIUM( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::BindValuebufferCHROMIUM& c = + *static_cast<const gles2::cmds::BindValuebufferCHROMIUM*>(cmd_data); + (void)c; + GLenum target = static_cast<GLenum>(c.target); + GLuint valuebuffer = c.valuebuffer; + if (!validators_->value_buffer_target.IsValid(target)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM("glBindValuebufferCHROMIUM", target, + "target"); + return error::kNoError; + } + DoBindValueBufferCHROMIUM(target, valuebuffer); + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleSubscribeValueCHROMIUM( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::SubscribeValueCHROMIUM& c = + *static_cast<const gles2::cmds::SubscribeValueCHROMIUM*>(cmd_data); + (void)c; + GLenum target = static_cast<GLenum>(c.target); + GLenum subscription = static_cast<GLenum>(c.subscription); + if (!validators_->value_buffer_target.IsValid(target)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM("glSubscribeValueCHROMIUM", target, + "target"); + return error::kNoError; + } + if (!validators_->subscription_target.IsValid(subscription)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM("glSubscribeValueCHROMIUM", subscription, + "subscription"); + return error::kNoError; + } + DoSubscribeValueCHROMIUM(target, subscription); + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandlePopulateSubscribedValuesCHROMIUM( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::PopulateSubscribedValuesCHROMIUM& c = + *static_cast<const gles2::cmds::PopulateSubscribedValuesCHROMIUM*>( + cmd_data); + (void)c; + GLenum target = static_cast<GLenum>(c.target); + if (!validators_->value_buffer_target.IsValid(target)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM("glPopulateSubscribedValuesCHROMIUM", + target, "target"); + return error::kNoError; + } + DoPopulateSubscribedValuesCHROMIUM(target); + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleUniformValuebufferCHROMIUM( + uint32_t immediate_data_size, + const void* cmd_data) { + const gles2::cmds::UniformValuebufferCHROMIUM& c = + *static_cast<const gles2::cmds::UniformValuebufferCHROMIUM*>(cmd_data); + (void)c; + GLint location = static_cast<GLint>(c.location); + GLenum target = static_cast<GLenum>(c.target); + GLenum subscription = static_cast<GLenum>(c.subscription); + if (!validators_->value_buffer_target.IsValid(target)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM("glUniformValuebufferCHROMIUM", target, + "target"); + return error::kNoError; + } + if (!validators_->subscription_target.IsValid(subscription)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM("glUniformValuebufferCHROMIUM", + subscription, "subscription"); + return error::kNoError; + } + DoUniformValueBufferCHROMIUM(location, target, subscription); + return error::kNoError; +} + error::Error GLES2DecoderImpl::HandleBindTexImage2DCHROMIUM( uint32_t immediate_data_size, const void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h index 7346d8e..d855c87 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -57,7 +57,7 @@ MOCK_METHOD0(GetContextGroup, ContextGroup*()); MOCK_METHOD0(GetContextState, const ContextState*()); MOCK_METHOD0(GetCapabilities, Capabilities()); - MOCK_METHOD0(ProcessPendingQueries, bool()); + MOCK_METHOD1(ProcessPendingQueries, bool(bool)); MOCK_METHOD0(HasMoreIdleWork, bool()); MOCK_METHOD0(PerformIdleWork, void()); MOCK_METHOD1(RestoreState, void(const ContextState* prev_state));
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index e97b4c4..0ef9585 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -266,154 +266,6 @@ EXPECT_FALSE(DoIsTexture(client_texture_id_)); } -TEST_P(GLES2DecoderTest, GetMultipleIntegervCHROMIUMValidArgs) { - const GLsizei kCount = 3; - GLenum* pnames = GetSharedMemoryAs<GLenum*>(); - pnames[0] = GL_DEPTH_WRITEMASK; - pnames[1] = GL_COLOR_WRITEMASK; - pnames[2] = GL_STENCIL_WRITEMASK; - GLint* results = - GetSharedMemoryAsWithOffset<GLint*>(sizeof(*pnames) * kCount); - - GLsizei num_results = 0; - for (GLsizei ii = 0; ii < kCount; ++ii) { - num_results += decoder_->GetGLES2Util()->GLGetNumValuesReturned(pnames[ii]); - } - const GLsizei result_size = num_results * sizeof(*results); - memset(results, 0, result_size); - - const GLint kSentinel = 0x12345678; - results[num_results] = kSentinel; - - GetMultipleIntegervCHROMIUM cmd; - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset, - kCount, - kSharedMemoryId, - kSharedMemoryOffset + sizeof(*pnames) * kCount, - result_size); - - EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); - EXPECT_EQ(1, results[0]); // Depth writemask - EXPECT_EQ(1, results[1]); // color writemask red - EXPECT_EQ(1, results[2]); // color writemask green - EXPECT_EQ(1, results[3]); // color writemask blue - EXPECT_EQ(1, results[4]); // color writemask alpha - EXPECT_EQ(-1, results[5]); // stencil writemask alpha - EXPECT_EQ(kSentinel, results[num_results]); // End of results -} - -TEST_P(GLES2DecoderTest, GetMultipleIntegervCHROMIUMInvalidArgs) { - const GLsizei kCount = 3; - // Offset the pnames because GLGetError will use the first uint32. - const uint32 kPnameOffset = sizeof(uint32); - const uint32 kResultsOffset = kPnameOffset + sizeof(GLint) * kCount; - GLenum* pnames = GetSharedMemoryAsWithOffset<GLenum*>(kPnameOffset); - pnames[0] = GL_DEPTH_WRITEMASK; - pnames[1] = GL_COLOR_WRITEMASK; - pnames[2] = GL_STENCIL_WRITEMASK; - GLint* results = GetSharedMemoryAsWithOffset<GLint*>(kResultsOffset); - - GLsizei num_results = 0; - for (GLsizei ii = 0; ii < kCount; ++ii) { - num_results += decoder_->GetGLES2Util()->GLGetNumValuesReturned(pnames[ii]); - } - const GLsizei result_size = num_results * sizeof(*results); - memset(results, 0, result_size); - - const GLint kSentinel = 0x12345678; - results[num_results] = kSentinel; - - GetMultipleIntegervCHROMIUM cmd; - // Check bad pnames pointer. - cmd.Init(kInvalidSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - kCount, - kSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size); - EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); - // Check bad pnames pointer. - cmd.Init(kSharedMemoryId, - kInvalidSharedMemoryOffset, - kCount, - kSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size); - EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); - // Check bad count. - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - static_cast<GLuint>(-1), - kSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size); - EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); - // Check bad results pointer. - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - kCount, - kInvalidSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size); - EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); - // Check bad results pointer. - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - kCount, - kSharedMemoryId, - kInvalidSharedMemoryOffset, - result_size); - EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); - // Check bad size. - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - kCount, - kSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size + 1); - EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); - EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); - // Check bad size. - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - kCount, - kSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size - 1); - EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); - EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); - // Check bad enum. - cmd.Init(kSharedMemoryId, - kSharedMemoryOffset + kPnameOffset, - kCount, - kSharedMemoryId, - kSharedMemoryOffset + kResultsOffset, - result_size); - GLenum temp = pnames[2]; - pnames[2] = GL_TRUE; - EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); - EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); - pnames[2] = temp; - // Check results area has not been cleared by client. - results[1] = 1; - EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd)); - // Check buffer is what we expect - EXPECT_EQ(0, results[0]); - EXPECT_EQ(1, results[1]); - EXPECT_EQ(0, results[2]); - EXPECT_EQ(0, results[3]); - EXPECT_EQ(0, results[4]); - EXPECT_EQ(0, results[5]); - EXPECT_EQ(kSentinel, results[num_results]); // End of results -} - TEST_P(GLES2DecoderManualInitTest, BindGeneratesResourceFalse) { InitState init; init.gl_version = "3.0"; @@ -636,7 +488,7 @@ QueryManager* query_manager = test->GetDecoder()->GetQueryManager(); ASSERT_TRUE(query_manager != NULL); - bool process_success = query_manager->ProcessPendingQueries(); + bool process_success = query_manager->ProcessPendingQueries(false); EXPECT_TRUE(error1 != error::kNoError || error2 != error::kNoError || !process_success); @@ -797,7 +649,7 @@ EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _)) .WillOnce(Return(GL_TIMEOUT_EXPIRED)) .RetiresOnSaturation(); - bool process_success = query_manager->ProcessPendingQueries(); + bool process_success = query_manager->ProcessPendingQueries(false); EXPECT_TRUE(process_success); EXPECT_TRUE(query->pending()); @@ -810,7 +662,7 @@ EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _)) .WillOnce(Return(GL_ALREADY_SIGNALED)) .RetiresOnSaturation(); - process_success = query_manager->ProcessPendingQueries(); + process_success = query_manager->ProcessPendingQueries(false); EXPECT_TRUE(process_success); EXPECT_FALSE(query->pending());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h index b29b243..9397f45 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
@@ -664,8 +664,6 @@ // TODO(gman): RequestExtensionCHROMIUM -// TODO(gman): GetMultipleIntegervCHROMIUM - // TODO(gman): GetProgramInfoCHROMIUM // TODO(gman): GetTranslatedShaderSourceANGLE @@ -682,6 +680,31 @@ // TODO(gman): ConsumeTextureCHROMIUMImmediate // TODO(gman): CreateAndConsumeTextureCHROMIUMImmediate // TODO(gman): BindUniformLocationCHROMIUMBucket +// TODO(gman): GenValuebuffersCHROMIUMImmediate +// TODO(gman): DeleteValuebuffersCHROMIUMImmediate + +TEST_P(GLES2DecoderTest2, IsValuebufferCHROMIUMValidArgs) { + SpecializedSetup<cmds::IsValuebufferCHROMIUM, 0>(true); + cmds::IsValuebufferCHROMIUM cmd; + cmd.Init(client_valuebuffer_id_, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_P(GLES2DecoderTest2, IsValuebufferCHROMIUMInvalidArgsBadSharedMemoryId) { + SpecializedSetup<cmds::IsValuebufferCHROMIUM, 0>(false); + cmds::IsValuebufferCHROMIUM cmd; + cmd.Init(client_valuebuffer_id_, kInvalidSharedMemoryId, + shared_memory_offset_); + EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); + cmd.Init(client_valuebuffer_id_, shared_memory_id_, + kInvalidSharedMemoryOffset); + EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); +} +// TODO(gman): BindValuebufferCHROMIUM +// TODO(gman): SubscribeValueCHROMIUM +// TODO(gman): PopulateSubscribedValuesCHROMIUM +// TODO(gman): UniformValuebufferCHROMIUM // TODO(gman): BindTexImage2DCHROMIUM // TODO(gman): ReleaseTexImage2DCHROMIUM // TODO(gman): TraceBeginCHROMIUM @@ -695,13 +718,4 @@ // TODO(gman): WaitAllAsyncTexImage2DCHROMIUM -// TODO(gman): LoseContextCHROMIUM -// TODO(gman): InsertSyncPointCHROMIUM - -// TODO(gman): WaitSyncPointCHROMIUM - -// TODO(gman): DrawBuffersEXTImmediate -// TODO(gman): DiscardBackbufferCHROMIUM - -// TODO(gman): ScheduleOverlayPlaneCHROMIUM #endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h index 7e93f36..1c98b68 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
@@ -12,4 +12,13 @@ #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_ #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_ +// TODO(gman): LoseContextCHROMIUM +// TODO(gman): InsertSyncPointCHROMIUM + +// TODO(gman): WaitSyncPointCHROMIUM + +// TODO(gman): DrawBuffersEXTImmediate +// TODO(gman): DiscardBackbufferCHROMIUM + +// TODO(gman): ScheduleOverlayPlaneCHROMIUM #endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index 136834d..36afe38 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -96,6 +96,7 @@ client_fragment_shader_id_(122), client_query_id_(123), client_vertexarray_id_(124), + client_valuebuffer_id_(125), service_renderbuffer_id_(0), service_renderbuffer_valid_(false), ignore_cached_state_for_test_(GetParam()),
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h index 1507440..1a2b54a 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -19,6 +19,7 @@ #include "gpu/command_buffer/service/shader_manager.h" #include "gpu/command_buffer/service/test_helper.h" #include "gpu/command_buffer/service/texture_manager.h" +#include "gpu/command_buffer/service/valuebuffer_manager.h" #include "gpu/command_buffer/service/vertex_array_manager.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_context_stub_with_extensions.h" @@ -122,6 +123,10 @@ return group_->program_manager()->GetProgram(client_id); } + Valuebuffer* GetValuebuffer(GLuint client_id) { + return group_->valuebuffer_manager()->GetValuebuffer(client_id); + } + QueryManager::Query* GetQueryInfo(GLuint client_id) { return decoder_->GetQueryManager()->GetQuery(client_id); } @@ -136,6 +141,10 @@ return group_->program_manager(); } + ValuebufferManager* valuebuffer_manager() { + return group_->valuebuffer_manager(); + } + ImageManager* GetImageManager() { return decoder_->GetImageManager(); } void DoCreateProgram(GLuint client_id, GLuint service_id); @@ -514,6 +523,7 @@ GLuint client_fragment_shader_id_; GLuint client_query_id_; GLuint client_vertexarray_id_; + GLuint client_valuebuffer_id_; uint32 shared_memory_id_; uint32 shared_memory_offset_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc index d555f28..789b48a 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -772,6 +772,7 @@ ReadPixels(0, 0, kWidth, kHeight, GL_RGB, GL_UNSIGNED_BYTE, _)) .Times(1); EXPECT_CALL(*gl_, GenBuffersARB(1, _)).Times(1); + EXPECT_CALL(*gl_, DeleteBuffersARB(1, _)).Times(1); EXPECT_CALL(*gl_, BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, _)).Times(2); EXPECT_CALL(*gl_, BufferData(GL_PIXEL_PACK_BUFFER_ARB, _, NULL, GL_STREAM_READ))
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_valuebuffer.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_valuebuffer.cc new file mode 100644 index 0000000..012d504 --- /dev/null +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_valuebuffer.cc
@@ -0,0 +1,130 @@ +// 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 "gpu/command_buffer/service/gles2_cmd_decoder.h" + +#include "base/command_line.h" +#include "gpu/command_buffer/common/gles2_cmd_format.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h" + +#include "gpu/command_buffer/service/test_helper.h" +#include "gpu/command_buffer/service/valuebuffer_manager.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_mock.h" +#include "ui/gl/gl_surface_stub.h" + +using ::gfx::MockGLInterface; +using ::testing::_; + +namespace gpu { +namespace gles2 { + +using namespace cmds; + +TEST_P(GLES2DecoderWithShaderTest, ValuebufferBasic) { + const uint32 kBufferId = 123; + ValueState valuestate; + valuestate.int_value[0] = 111; + valuestate.int_value[1] = 222; + valuebuffer_manager()->CreateValuebuffer(kBufferId); + valuebuffer_manager()->UpdateValueState( + GL_MOUSE_POSITION_CHROMIUM, valuestate); + BindValuebufferCHROMIUM cmd1; + cmd1.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, kBufferId); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); + SubscribeValueCHROMIUM cmd2; + cmd2.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); + PopulateSubscribedValuesCHROMIUM cmd3; + cmd3.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd3)); + EXPECT_CALL(*gl_, Uniform2iv(kUniform2RealLocation, 1, _)).Times(1); + UniformValuebufferCHROMIUM cmd4; + cmd4.Init(kUniform2FakeLocation, GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd4)); +} + +TEST_P(GLES2DecoderWithShaderTest, SubscribeValuebufferNotBound) { + const uint32 kBufferId = 123; + ValueState valuestate; + valuestate.int_value[0] = 111; + valuestate.int_value[1] = 222; + valuebuffer_manager()->CreateValuebuffer(kBufferId); + valuebuffer_manager()->UpdateValueState( + GL_MOUSE_POSITION_CHROMIUM, valuestate); + SubscribeValueCHROMIUM cmd1; + cmd1.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_P(GLES2DecoderWithShaderTest, PopulateValuebufferNoSubscription) { + const uint32 kBufferId = 123; + ValueState valuestate; + valuestate.int_value[0] = 111; + valuestate.int_value[1] = 222; + valuebuffer_manager()->CreateValuebuffer(kBufferId); + valuebuffer_manager()->UpdateValueState( + GL_MOUSE_POSITION_CHROMIUM, valuestate); + BindValuebufferCHROMIUM cmd1; + cmd1.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, kBufferId); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); + PopulateSubscribedValuesCHROMIUM cmd2; + cmd2.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); + EXPECT_EQ(GL_NONE, GetGLError()); +} + +TEST_P(GLES2DecoderWithShaderTest, UniformValuebufferNoState) { + const uint32 kBufferId = 123; + ValueState valuestate; + valuestate.int_value[0] = 111; + valuestate.int_value[1] = 222; + valuebuffer_manager()->CreateValuebuffer(kBufferId); + valuebuffer_manager()->UpdateValueState( + GL_MOUSE_POSITION_CHROMIUM, valuestate); + BindValuebufferCHROMIUM cmd1; + cmd1.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, kBufferId); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); + SubscribeValueCHROMIUM cmd2; + cmd2.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); + EXPECT_CALL(*gl_, Uniform2iv(kUniform2RealLocation, 1, _)).Times(0); + UniformValuebufferCHROMIUM cmd3; + cmd3.Init(kUniform2FakeLocation, GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd3)); + EXPECT_EQ(GL_NONE, GetGLError()); +} + +TEST_P(GLES2DecoderWithShaderTest, UniformValuebufferInvalidLocation) { + const uint32 kBufferId = 123; + ValueState valuestate; + valuestate.int_value[0] = 111; + valuestate.int_value[1] = 222; + valuebuffer_manager()->CreateValuebuffer(kBufferId); + valuebuffer_manager()->UpdateValueState( + GL_MOUSE_POSITION_CHROMIUM, valuestate); + BindValuebufferCHROMIUM cmd1; + cmd1.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, kBufferId); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); + SubscribeValueCHROMIUM cmd2; + cmd2.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); + PopulateSubscribedValuesCHROMIUM cmd3; + cmd3.Init(GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd3)); + EXPECT_CALL(*gl_, Uniform2iv(kUniform2RealLocation, 1, _)).Times(0); + UniformValuebufferCHROMIUM cmd4; + cmd4.Init(kUniform1FakeLocation, GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, + GL_MOUSE_POSITION_CHROMIUM); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd4)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +} // namespace gles2 +} // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h index de84037..c72bcf8 100644 --- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
@@ -56,6 +56,7 @@ ValueValidator<GLenum> src_blend_factor; ValueValidator<GLenum> stencil_op; ValueValidator<GLenum> string_type; +ValueValidator<GLenum> subscription_target; ValueValidator<GLenum> texture_bind_target; ValueValidator<GLenum> texture_format; ValueValidator<GLenum> texture_internal_format; @@ -67,6 +68,7 @@ ValueValidator<GLenum> texture_target; ValueValidator<GLenum> texture_usage; ValueValidator<GLenum> texture_wrap_mode; +ValueValidator<GLenum> value_buffer_target; ValueValidator<GLint> vertex_attrib_size; ValueValidator<GLenum> vertex_attrib_type; ValueValidator<GLenum> vertex_attribute;
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h index 790b9b3..6a646ac 100644 --- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -413,6 +413,10 @@ GL_EXTENSIONS, }; +static const GLenum valid_subscription_target_table[] = { + GL_MOUSE_POSITION_CHROMIUM, +}; + static const GLenum valid_texture_bind_target_table[] = { GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP, @@ -493,6 +497,10 @@ GL_REPEAT, }; +static const GLenum valid_value_buffer_target_table[] = { + GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM, +}; + static const GLint valid_vertex_attrib_size_table[] = { 1, 2, @@ -593,6 +601,8 @@ arraysize(valid_src_blend_factor_table)), stencil_op(valid_stencil_op_table, arraysize(valid_stencil_op_table)), string_type(valid_string_type_table, arraysize(valid_string_type_table)), + subscription_target(valid_subscription_target_table, + arraysize(valid_subscription_target_table)), texture_bind_target(valid_texture_bind_target_table, arraysize(valid_texture_bind_target_table)), texture_format(valid_texture_format_table, @@ -616,6 +626,8 @@ arraysize(valid_texture_usage_table)), texture_wrap_mode(valid_texture_wrap_mode_table, arraysize(valid_texture_wrap_mode_table)), + value_buffer_target(valid_value_buffer_target_table, + arraysize(valid_value_buffer_target_table)), vertex_attrib_size(valid_vertex_attrib_size_table, arraysize(valid_vertex_attrib_size_table)), vertex_attrib_type(valid_vertex_attrib_type_table,
diff --git a/gpu/command_buffer/service/gpu_scheduler.cc b/gpu/command_buffer/service/gpu_scheduler.cc index 058a546..48df8dd 100644 --- a/gpu/command_buffer/service/gpu_scheduler.cc +++ b/gpu/command_buffer/service/gpu_scheduler.cc
@@ -170,7 +170,7 @@ bool GpuScheduler::HasMoreWork() { return !unschedule_fences_.empty() || - (decoder_ && decoder_->ProcessPendingQueries()) || + (decoder_ && decoder_->ProcessPendingQueries(false)) || HasMoreIdleWork(); }
diff --git a/gpu/command_buffer/service/gpu_tracer_unittest.cc b/gpu/command_buffer/service/gpu_tracer_unittest.cc index 59ea63e..2a7a94c 100644 --- a/gpu/command_buffer/service/gpu_tracer_unittest.cc +++ b/gpu/command_buffer/service/gpu_tracer_unittest.cc
@@ -13,18 +13,9 @@ namespace gpu { namespace gles2 { -using ::testing::InvokeWithoutArgs; using ::testing::Return; -using ::testing::ReturnRef; -using ::testing::ReturnPointee; using ::testing::NotNull; -using ::testing::ElementsAreArray; -using ::testing::ElementsAre; -using ::testing::SetArrayArgument; using ::testing::AtLeast; -using ::testing::SetArgPointee; -using ::testing::Pointee; -using ::testing::Unused; using ::testing::Invoke; using ::testing::_;
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc index d3e954e..9134c78 100644 --- a/gpu/command_buffer/service/memory_program_cache_unittest.cc +++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -16,10 +16,8 @@ #include "ui/gl/gl_mock.h" using ::testing::_; -using ::testing::ElementsAreArray; using ::testing::Invoke; using ::testing::SetArgPointee; -using ::testing::SetArrayArgument; namespace gpu { namespace gles2 {
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc index 7bc72de..f3c447c 100644 --- a/gpu/command_buffer/service/program_manager.cc +++ b/gpu/command_buffer/service/program_manager.cc
@@ -193,8 +193,7 @@ memcmp(name, kInvalidPrefix, sizeof(kInvalidPrefix)) == 0); } -Program::Program( - ProgramManager* manager, GLuint service_id) +Program::Program(ProgramManager* manager, GLuint service_id) : manager_(manager), use_count_(0), max_attrib_name_length_(0),
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h index dbe9c14..2f5deae 100644 --- a/gpu/command_buffer/service/program_manager.h +++ b/gpu/command_buffer/service/program_manager.h
@@ -39,6 +39,7 @@ }; enum UniformApiType { + kUniformNone = 0, kUniform1i = 1 << 0, kUniform2i = 1 << 1, kUniform3i = 1 << 2,
diff --git a/gpu/command_buffer/service/query_manager.cc b/gpu/command_buffer/service/query_manager.cc index 5f518ec..fdb5fa8 100644 --- a/gpu/command_buffer/service/query_manager.cc +++ b/gpu/command_buffer/service/query_manager.cc
@@ -64,7 +64,7 @@ bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -105,7 +105,7 @@ return AddToPendingTransferQueue(submit_count); } -bool AsyncPixelTransfersCompletedQuery::Process() { +bool AsyncPixelTransfersCompletedQuery::Process(bool did_finish) { QuerySync* sync = manager()->decoder()->GetSharedMemoryAs<QuerySync*>( shm_id(), shm_offset(), sizeof(*sync)); if (!sync) @@ -141,7 +141,7 @@ GLuint service_id); bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -169,7 +169,7 @@ return AddToPendingQueue(submit_count); } -bool AllSamplesPassedQuery::Process() { +bool AllSamplesPassedQuery::Process(bool did_finish) { GLuint available = 0; glGetQueryObjectuivARB( service_id_, GL_QUERY_RESULT_AVAILABLE_EXT, &available); @@ -200,7 +200,7 @@ bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -226,7 +226,7 @@ return MarkAsCompleted(elapsed.InMicroseconds()); } -bool CommandsIssuedQuery::Process() { +bool CommandsIssuedQuery::Process(bool did_finish) { NOTREACHED(); return true; } @@ -247,7 +247,7 @@ bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -269,7 +269,7 @@ return MarkAsCompleted(now.InMicroseconds()); } -bool CommandLatencyQuery::Process() { +bool CommandLatencyQuery::Process(bool did_finish) { NOTREACHED(); return true; } @@ -293,7 +293,7 @@ bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -324,7 +324,7 @@ base::Bind(&AsyncReadPixelsCompletedQuery::Complete, AsWeakPtr())); - return Process(); + return Process(false); } void AsyncReadPixelsCompletedQuery::Complete() { @@ -332,7 +332,7 @@ complete_result_ = MarkAsCompleted(1); } -bool AsyncReadPixelsCompletedQuery::Process() { +bool AsyncReadPixelsCompletedQuery::Process(bool did_finish) { return !completed_ || complete_result_; } @@ -353,7 +353,7 @@ bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -376,7 +376,7 @@ return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError()); } -bool GetErrorQuery::Process() { +bool GetErrorQuery::Process(bool did_finish) { NOTREACHED(); return true; } @@ -400,7 +400,7 @@ // Overridden from QueryManager::Query: bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; - bool Process() override; + bool Process(bool did_finish) override; void Destroy(bool have_context) override; protected: @@ -424,9 +424,16 @@ return AddToPendingQueue(submit_count); } -bool CommandsCompletedQuery::Process() { - if (fence_ && !fence_->HasCompleted()) +bool CommandsCompletedQuery::Process(bool did_finish) { + // Note: |did_finish| guarantees that the GPU has passed the fence but + // we cannot assume that GLFence::HasCompleted() will return true yet as + // that's not guaranteed by all GLFence implementations. + // + // TODO(reveman): Add UMA stats to determine how common it is that glFinish() + // needs to be called for these queries to complete. crbug.com/431845 + if (!did_finish && fence_ && !fence_->HasCompleted()) return true; + return MarkAsCompleted(0); } @@ -635,10 +642,10 @@ return true; } -bool QueryManager::ProcessPendingQueries() { +bool QueryManager::ProcessPendingQueries(bool did_finish) { while (!pending_queries_.empty()) { Query* query = pending_queries_.front().get(); - if (!query->Process()) { + if (!query->Process(did_finish)) { return false; } if (query->pending()) { @@ -658,7 +665,7 @@ bool QueryManager::ProcessPendingTransferQueries() { while (!pending_transfer_queries_.empty()) { Query* query = pending_transfer_queries_.front().get(); - if (!query->Process()) { + if (!query->Process(false)) { return false; } if (query->pending()) {
diff --git a/gpu/command_buffer/service/query_manager.h b/gpu/command_buffer/service/query_manager.h index 62da3b8..5f14929 100644 --- a/gpu/command_buffer/service/query_manager.h +++ b/gpu/command_buffer/service/query_manager.h
@@ -64,7 +64,7 @@ virtual bool End(base::subtle::Atomic32 submit_count) = 0; // Returns false if shared memory for sync is invalid. - virtual bool Process() = 0; + virtual bool Process(bool did_finish) = 0; virtual void Destroy(bool have_context) = 0; @@ -170,8 +170,9 @@ bool EndQuery(Query* query, base::subtle::Atomic32 submit_count); // Processes pending queries. Returns false if any queries are pointing - // to invalid shared memory. - bool ProcessPendingQueries(); + // to invalid shared memory. |did_finish| is true if this is called as + // a result of calling glFinish(). + bool ProcessPendingQueries(bool did_finish); // True if there are pending queries. bool HavePendingQueries();
diff --git a/gpu/command_buffer/service/query_manager_unittest.cc b/gpu/command_buffer/service/query_manager_unittest.cc index 80efd69..7c8e93c 100644 --- a/gpu/command_buffer/service/query_manager_unittest.cc +++ b/gpu/command_buffer/service/query_manager_unittest.cc
@@ -214,7 +214,7 @@ const GLuint kResult = 1; // Check nothing happens if there are no pending queries. - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); // Create Query. scoped_refptr<QueryManager::Query> query( @@ -239,7 +239,7 @@ GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) .WillOnce(SetArgumentPointee<2>(0)) .RetiresOnSaturation(); - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); EXPECT_TRUE(query->pending()); EXPECT_EQ(0, sync->process_count); EXPECT_EQ(0u, sync->result); @@ -254,7 +254,7 @@ GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) .WillOnce(SetArgumentPointee<2>(kResult)) .RetiresOnSaturation(); - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); EXPECT_FALSE(query->pending()); EXPECT_EQ(kSubmitCount, sync->process_count); EXPECT_EQ(kResult, sync->result); @@ -262,7 +262,7 @@ // Process with no queries. // Expect no GL commands/ - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); } TEST_F(QueryManagerTest, ProcessPendingQueries) { @@ -342,7 +342,7 @@ GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) .WillOnce(SetArgumentPointee<2>(0)) .RetiresOnSaturation(); - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); } EXPECT_FALSE(query1->pending()); EXPECT_FALSE(query2->pending()); @@ -361,7 +361,7 @@ GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) .WillOnce(SetArgumentPointee<2>(0)) .RetiresOnSaturation(); - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); EXPECT_TRUE(query3->pending()); EXPECT_EQ(0, sync3->process_count); EXPECT_EQ(0u, sync3->result); @@ -377,7 +377,7 @@ GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_EXT, _)) .WillOnce(SetArgumentPointee<2>(kResult3)) .RetiresOnSaturation(); - EXPECT_TRUE(manager_->ProcessPendingQueries()); + EXPECT_TRUE(manager_->ProcessPendingQueries(false)); EXPECT_FALSE(query3->pending()); EXPECT_EQ(kSubmitCount3, sync3->process_count); EXPECT_EQ(kResult3, sync3->result); @@ -410,7 +410,7 @@ GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) .WillOnce(SetArgumentPointee<2>(kResult)) .RetiresOnSaturation(); - EXPECT_FALSE(manager_->ProcessPendingQueries()); + EXPECT_FALSE(manager_->ProcessPendingQueries(false)); } TEST_F(QueryManagerTest, ProcessPendingBadSharedMemoryOffset) { @@ -439,7 +439,7 @@ GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) .WillOnce(SetArgumentPointee<2>(kResult)) .RetiresOnSaturation(); - EXPECT_FALSE(manager_->ProcessPendingQueries()); + EXPECT_FALSE(manager_->ProcessPendingQueries(false)); } TEST_F(QueryManagerTest, ExitWithPendingQuery) {
diff --git a/gpu/command_buffer/service/shader_translator_cache.h b/gpu/command_buffer/service/shader_translator_cache.h index e804ef2..6b0b1a5 100644 --- a/gpu/command_buffer/service/shader_translator_cache.h +++ b/gpu/command_buffer/service/shader_translator_cache.h
@@ -41,6 +41,7 @@ private: friend class base::RefCounted<ShaderTranslatorCache>; + friend class ShaderTranslatorCacheTest_InitParamComparable_Test; ~ShaderTranslatorCache() override; // Parameters passed into ShaderTranslator::Init @@ -52,18 +53,18 @@ glsl_implementation_type; ShCompileOptions driver_bug_workarounds; - ShaderTranslatorInitParams( - sh::GLenum shader_type, - ShShaderSpec shader_spec, - const ShBuiltInResources& resources, - ShaderTranslatorInterface::GlslImplementationType - glsl_implementation_type, - ShCompileOptions driver_bug_workarounds) - : shader_type(shader_type), - shader_spec(shader_spec), - resources(resources), - glsl_implementation_type(glsl_implementation_type), - driver_bug_workarounds(driver_bug_workarounds) { + ShaderTranslatorInitParams(sh::GLenum shader_type, + ShShaderSpec shader_spec, + const ShBuiltInResources& resources, + ShaderTranslatorInterface::GlslImplementationType + glsl_implementation_type, + ShCompileOptions driver_bug_workarounds) { + memset(this, 0, sizeof(*this)); + this->shader_type = shader_type; + this->shader_spec = shader_spec; + this->resources = resources; + this->glsl_implementation_type = glsl_implementation_type; + this->driver_bug_workarounds = driver_bug_workarounds; } ShaderTranslatorInitParams(const ShaderTranslatorInitParams& params) { @@ -77,6 +78,10 @@ bool operator< (const ShaderTranslatorInitParams& params) const { return memcmp(¶ms, this, sizeof(*this)) < 0; } + + private: + ShaderTranslatorInitParams(); + ShaderTranslatorInitParams& operator=(const ShaderTranslatorInitParams&); }; typedef std::map<ShaderTranslatorInitParams, ShaderTranslator* > Cache;
diff --git a/gpu/command_buffer/service/shader_translator_cache_unittest.cc b/gpu/command_buffer/service/shader_translator_cache_unittest.cc new file mode 100644 index 0000000..c5233e5 --- /dev/null +++ b/gpu/command_buffer/service/shader_translator_cache_unittest.cc
@@ -0,0 +1,56 @@ +// Copyright (c) 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 <GLES2/gl2.h> + +#include "gpu/command_buffer/service/shader_translator_cache.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gpu { +namespace gles2 { + +TEST(ShaderTranslatorCacheTest, InitParamComparable) { + // Tests that ShaderTranslatorInitParams padding or padding of its + // members does not affect the object equality or ordering. + + ShBuiltInResources a_resources; + memset(&a_resources, 88, sizeof(a_resources)); + ShInitBuiltInResources(&a_resources); + + ShBuiltInResources b_resources; + memset(&b_resources, 77, sizeof(b_resources)); + ShInitBuiltInResources(&b_resources); + + EXPECT_TRUE(memcmp(&a_resources, &b_resources, sizeof(a_resources)) == 0); + + ShCompileOptions driver_bug_workarounds = SH_VALIDATE; + + char a_storage[sizeof(ShaderTranslatorCache::ShaderTranslatorInitParams)]; + memset(a_storage, 55, sizeof(a_storage)); + ShaderTranslatorCache::ShaderTranslatorInitParams* a = + new (&a_storage) ShaderTranslatorCache::ShaderTranslatorInitParams( + GL_VERTEX_SHADER, + SH_GLES2_SPEC, + a_resources, + ShaderTranslatorInterface::kGlslES, + driver_bug_workarounds); + + ShaderTranslatorCache::ShaderTranslatorInitParams b( + GL_VERTEX_SHADER, + SH_GLES2_SPEC, + b_resources, + ShaderTranslatorInterface::kGlslES, + driver_bug_workarounds); + + EXPECT_TRUE(*a == b); + EXPECT_FALSE(*a < b || b < *a); + + memset(a_storage, 55, sizeof(a_storage)); + a = new (&a_storage) ShaderTranslatorCache::ShaderTranslatorInitParams(b); + + EXPECT_TRUE(*a == b); + EXPECT_FALSE(*a < b || b < *a); +} +} // namespace gles2 +} // namespace gpu
diff --git a/gpu/command_buffer/service/valuebuffer_manager.cc b/gpu/command_buffer/service/valuebuffer_manager.cc new file mode 100644 index 0000000..eb4db09 --- /dev/null +++ b/gpu/command_buffer/service/valuebuffer_manager.cc
@@ -0,0 +1,117 @@ +// Copyright (c) 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 "gpu/command_buffer/service/valuebuffer_manager.h" + +#include "gpu/command_buffer/service/program_manager.h" + +namespace gpu { +namespace gles2 { + +Valuebuffer::Valuebuffer(ValuebufferManager* manager, GLuint client_id) + : manager_(manager), client_id_(client_id), has_been_bound_(false) { + manager_->StartTracking(this); +} + +Valuebuffer::~Valuebuffer() { + if (manager_) { + manager_->StopTracking(this); + manager_ = NULL; + } +} + +void Valuebuffer::AddSubscription(GLenum subscription) { + subscriptions_.insert(subscription); +} + +void Valuebuffer::RemoveSubscription(GLenum subscription) { + subscriptions_.erase(subscription); +} + +bool Valuebuffer::IsSubscribed(GLenum subscription) { + return subscriptions_.find(subscription) != subscriptions_.end(); +} + +const ValueState *Valuebuffer::GetState(GLenum target) const { + StateMap::const_iterator it = active_state_map_.find(target); + return it != active_state_map_.end() ? &it->second : NULL; +} + +void Valuebuffer::UpdateState(const StateMap& pending_state) { + for (SubscriptionSet::const_iterator it = subscriptions_.begin(); + it != subscriptions_.end(); ++it) { + StateMap::const_iterator pending_state_it = pending_state.find((*it)); + if (pending_state_it != pending_state.end()) { + active_state_map_[pending_state_it->first] = pending_state_it->second; + } + } +} + +ValuebufferManager::ValuebufferManager() + : valuebuffer_count_(0) { +} + +ValuebufferManager::~ValuebufferManager() { + DCHECK(valuebuffer_map_.empty()); + DCHECK(pending_state_map_.empty()); + // If this triggers, that means something is keeping a reference to + // a Valuebuffer belonging to this. + CHECK_EQ(valuebuffer_count_, 0u); +} + +void ValuebufferManager::Destroy() { + valuebuffer_map_.clear(); + pending_state_map_.clear(); +} + +void ValuebufferManager::StartTracking(Valuebuffer* /* valuebuffer */) { + ++valuebuffer_count_; +} + +void ValuebufferManager::StopTracking(Valuebuffer* /* valuebuffer */) { + --valuebuffer_count_; +} + +void ValuebufferManager::CreateValuebuffer(GLuint client_id) { + scoped_refptr<Valuebuffer> valuebuffer(new Valuebuffer(this, client_id)); + std::pair<ValuebufferMap::iterator, bool> result = + valuebuffer_map_.insert(std::make_pair(client_id, valuebuffer)); + DCHECK(result.second); +} + +Valuebuffer* ValuebufferManager::GetValuebuffer(GLuint client_id) { + ValuebufferMap::iterator it = valuebuffer_map_.find(client_id); + return it != valuebuffer_map_.end() ? it->second.get() : NULL; +} + +void ValuebufferManager::RemoveValuebuffer(GLuint client_id) { + ValuebufferMap::iterator it = valuebuffer_map_.find(client_id); + if (it != valuebuffer_map_.end()) { + Valuebuffer* valuebuffer = it->second.get(); + valuebuffer->MarkAsDeleted(); + valuebuffer_map_.erase(it); + } +} + +void ValuebufferManager::UpdateValuebufferState(Valuebuffer* valuebuffer) { + DCHECK(valuebuffer); + valuebuffer->UpdateState(pending_state_map_); +} + +void ValuebufferManager::UpdateValueState( + GLenum target, const ValueState& state) { + pending_state_map_[target] = state; +} + +uint32 ValuebufferManager::ApiTypeForSubscriptionTarget(GLenum target) { + switch (target) { + case GL_MOUSE_POSITION_CHROMIUM: + return Program::kUniform2i; + } + NOTREACHED() << "Unhandled uniform subscription target " << target; + return Program::kUniformNone; +} + +} // namespace gles2 +} // namespace gpu
diff --git a/gpu/command_buffer/service/valuebuffer_manager.h b/gpu/command_buffer/service/valuebuffer_manager.h new file mode 100644 index 0000000..3cc4ac1 --- /dev/null +++ b/gpu/command_buffer/service/valuebuffer_manager.h
@@ -0,0 +1,123 @@ +// Copyright (c) 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 GPU_COMMAND_BUFFER_SERVICE_VALUEBUFFER_MANAGER_H_ +#define GPU_COMMAND_BUFFER_SERVICE_VALUEBUFFER_MANAGER_H_ + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/gpu_export.h" + +namespace gpu { +namespace gles2 { + +class ValuebufferManager; + +union ValueState { + float float_value[4]; + int int_value[4]; +}; + +class GPU_EXPORT Valuebuffer : public base::RefCounted<Valuebuffer> { + public: + Valuebuffer(ValuebufferManager* manager, GLuint client_id); + + GLuint client_id() const { return client_id_; } + + bool IsDeleted() const { return client_id_ == 0; } + + void MarkAsValid() { has_been_bound_ = true; } + + bool IsValid() const { return has_been_bound_ && !IsDeleted(); } + + void AddSubscription(GLenum subscription); + void RemoveSubscription(GLenum subscription); + + // Returns true if this Valuebuffer is subscribed to subscription + bool IsSubscribed(GLenum subscription); + + // Returns the active state for a given target in this Valuebuffer + // returns NULL if target state doesn't exist + const ValueState* GetState(GLenum target) const; + + private: + friend class ValuebufferManager; + friend class base::RefCounted<Valuebuffer>; + + typedef base::hash_map<GLenum, ValueState> StateMap; + typedef base::hash_set<GLenum> SubscriptionSet; + + ~Valuebuffer(); + + void UpdateState(const StateMap& pending_state); + + void MarkAsDeleted() { client_id_ = 0; } + + // ValuebufferManager that owns this Valuebuffer. + ValuebufferManager* manager_; + + // Client side Valuebuffer id. + GLuint client_id_; + + // Whether this Valuebuffer has ever been bound. + bool has_been_bound_; + + SubscriptionSet subscriptions_; + + StateMap active_state_map_; +}; + +class GPU_EXPORT ValuebufferManager { + public: + ValuebufferManager(); + ~ValuebufferManager(); + + // Must call before destruction. + void Destroy(); + + // Creates a Valuebuffer for the given Valuebuffer ids. + void CreateValuebuffer(GLuint client_id); + + // Gets the Valuebuffer for the given Valuebuffer id. + Valuebuffer* GetValuebuffer(GLuint client_id); + + // Removes a Valuebuffer for the given Valuebuffer id. + void RemoveValuebuffer(GLuint client_id); + + // Updates the value state for the given Valuebuffer + void UpdateValuebufferState(Valuebuffer* valuebuffer); + + // Gets the state for the given subscription target + void UpdateValueState(GLenum target, const ValueState& state); + + static uint32 ApiTypeForSubscriptionTarget(GLenum target); + + private: + friend class Valuebuffer; + + typedef base::hash_map<GLuint, scoped_refptr<Valuebuffer>> ValuebufferMap; + + void StartTracking(Valuebuffer* valuebuffer); + void StopTracking(Valuebuffer* valuebuffer); + + // Counts the number of Valuebuffer allocated with 'this' as its manager. + // Allows to check no Valuebuffer will outlive this. + unsigned valuebuffer_count_; + + // Info for each Valuebuffer in the system. + ValuebufferMap valuebuffer_map_; + + // Current value state in the system + Valuebuffer::StateMap pending_state_map_; + + DISALLOW_COPY_AND_ASSIGN(ValuebufferManager); +}; + +} // namespace gles2 +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_SERVICE_VALUEBUFFER_MANAGER_H_
diff --git a/gpu/command_buffer/service/valuebuffer_manager_unittest.cc b/gpu/command_buffer/service/valuebuffer_manager_unittest.cc new file mode 100644 index 0000000..ead5df5 --- /dev/null +++ b/gpu/command_buffer/service/valuebuffer_manager_unittest.cc
@@ -0,0 +1,100 @@ +// Copyright (c) 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 "gpu/command_buffer/service/valuebuffer_manager.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "gpu/command_buffer/common/gles2_cmd_format.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" +#include "gpu/command_buffer/service/common_decoder.h" +#include "gpu/command_buffer/service/feature_info.h" +#include "gpu/command_buffer/service/gpu_service_test.h" +#include "gpu/command_buffer/service/mocks.h" +#include "gpu/command_buffer/service/test_helper.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_mock.h" + +namespace gpu { +namespace gles2 { + +class ValuebufferManagerTest : public GpuServiceTest { + public: + ValuebufferManagerTest() : manager_() {} + ~ValuebufferManagerTest() override { manager_.Destroy(); } + + protected: + ValuebufferManager manager_; +}; + +TEST_F(ValuebufferManagerTest, Basic) { + const GLuint kClient1Id = 1; + const GLuint kClient2Id = 2; + // Check we can create a Valuebuffer + manager_.CreateValuebuffer(kClient1Id); + Valuebuffer* valuebuffer0 = manager_.GetValuebuffer(kClient1Id); + ASSERT_TRUE(valuebuffer0 != NULL); + EXPECT_EQ(kClient1Id, valuebuffer0->client_id()); + // Check we get nothing for a non-existent Valuebuffer. + // Check trying to a remove non-existent Valuebuffer does not crash + manager_.RemoveValuebuffer(kClient2Id); + // Check we can't get the renderbuffer after we remove it. + manager_.RemoveValuebuffer(kClient1Id); + EXPECT_TRUE(manager_.GetValuebuffer(kClient1Id) == NULL); +} + +TEST_F(ValuebufferManagerTest, Destroy) { + const GLuint kClient1Id = 1; + // Check we can create Valuebuffer. + manager_.CreateValuebuffer(kClient1Id); + Valuebuffer* valuebuffer0 = manager_.GetValuebuffer(kClient1Id); + ASSERT_TRUE(valuebuffer0 != NULL); + EXPECT_EQ(kClient1Id, valuebuffer0->client_id()); + manager_.Destroy(); + // Check the resources were released. + Valuebuffer* valuebuffer1 = manager_.GetValuebuffer(kClient1Id); + ASSERT_TRUE(valuebuffer1 == NULL); +} + +TEST_F(ValuebufferManagerTest, ValueBuffer) { + const GLuint kClient1Id = 1; + // Check we can create a Valuebuffer + manager_.CreateValuebuffer(kClient1Id); + Valuebuffer* valuebuffer0 = manager_.GetValuebuffer(kClient1Id); + ASSERT_TRUE(valuebuffer0 != NULL); + EXPECT_EQ(kClient1Id, valuebuffer0->client_id()); + EXPECT_FALSE(valuebuffer0->IsValid()); +} + +TEST_F(ValuebufferManagerTest, UpdateState) { + const GLuint kClient1Id = 1; + ValueState valuestate1; + valuestate1.int_value[0] = 111; + ValueState valuestate2; + valuestate2.int_value[0] = 222; + manager_.CreateValuebuffer(kClient1Id); + Valuebuffer* valuebuffer0 = manager_.GetValuebuffer(kClient1Id); + ASSERT_TRUE(valuebuffer0 != NULL); + EXPECT_EQ(kClient1Id, valuebuffer0->client_id()); + valuebuffer0->AddSubscription(GL_MOUSE_POSITION_CHROMIUM); + ASSERT_TRUE(valuebuffer0->GetState(GL_MOUSE_POSITION_CHROMIUM) == NULL); + manager_.UpdateValueState(GL_MOUSE_POSITION_CHROMIUM, valuestate1); + manager_.UpdateValuebufferState(valuebuffer0); + const ValueState* new_state1 = + valuebuffer0->GetState(GL_MOUSE_POSITION_CHROMIUM); + ASSERT_TRUE(new_state1 != NULL); + ASSERT_TRUE(new_state1->int_value[0] == 111); + // Ensure state changes + manager_.UpdateValueState(GL_MOUSE_POSITION_CHROMIUM, valuestate2); + manager_.UpdateValuebufferState(valuebuffer0); + const ValueState* new_state2 = + valuebuffer0->GetState(GL_MOUSE_POSITION_CHROMIUM); + ASSERT_TRUE(new_state2 != NULL); + ASSERT_TRUE(new_state2->int_value[0] == 222); +} + +} // namespace gles2 +} // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc index a8a7425..ebd821c 100644 --- a/gpu/command_buffer/tests/gl_manager.cc +++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -323,7 +323,11 @@ } void GLManager::PumpCommands() { - decoder_->MakeCurrent(); + if (!decoder_->MakeCurrent()) { + command_buffer_->SetContextLostReason(decoder_->GetContextLostReason()); + command_buffer_->SetParseError(::gpu::error::kLostContext); + return; + } gpu_scheduler_->PutChanged(); ::gpu::CommandBuffer::State state = command_buffer_->GetLastState(); if (!context_lost_allowed_) {
diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi index f1ed489..13ffe94 100644 --- a/gpu/command_buffer_service.gypi +++ b/gpu/command_buffer_service.gypi
@@ -125,6 +125,8 @@ 'command_buffer/service/texture_manager.cc', 'command_buffer/service/transfer_buffer_manager.cc', 'command_buffer/service/transfer_buffer_manager.h', + 'command_buffer/service/valuebuffer_manager.h', + 'command_buffer/service/valuebuffer_manager.cc', 'command_buffer/service/vertex_array_manager.h', 'command_buffer/service/vertex_array_manager.cc', 'command_buffer/service/vertex_attrib_manager.h',
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc index 53343f6..eabf9b2 100644 --- a/gpu/config/gpu_info_collector_win.cc +++ b/gpu/config/gpu_info_collector_win.cc
@@ -665,6 +665,45 @@ return; } + // Track D3D Shader Model (if available) + const std::string& shader_version = + context_gpu_info.vertex_shader_version; + + // Only gather if this is the first time we're seeing + // a non-empty shader version string. + if (!shader_version.empty() && + basic_gpu_info->vertex_shader_version.empty()) { + + // Note: do not reorder, used by UMA_HISTOGRAM below + enum ShaderModel { + SHADER_MODEL_UNKNOWN, + SHADER_MODEL_2_0, + SHADER_MODEL_3_0, + SHADER_MODEL_4_0, + SHADER_MODEL_4_1, + SHADER_MODEL_5_0, + NUM_SHADER_MODELS + }; + + ShaderModel shader_model = SHADER_MODEL_UNKNOWN; + + if (shader_version == "5.0") { + shader_model = SHADER_MODEL_5_0; + } else if (shader_version == "4.1") { + shader_model = SHADER_MODEL_4_1; + } else if (shader_version == "4.0") { + shader_model = SHADER_MODEL_4_0; + } else if (shader_version == "3.0") { + shader_model = SHADER_MODEL_3_0; + } else if (shader_version == "2.0") { + shader_model = SHADER_MODEL_2_0; + } + + UMA_HISTOGRAM_ENUMERATION("GPU.D3DShaderModel", + shader_model, + NUM_SHADER_MODELS); + } + MergeGPUInfoGL(basic_gpu_info, context_gpu_info); basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index 0878a1d..ddecf45 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp
@@ -232,6 +232,7 @@ 'command_buffer/service/gles2_cmd_decoder_unittest_programs.cc', 'command_buffer/service/gles2_cmd_decoder_unittest_textures.cc', 'command_buffer/service/gles2_cmd_decoder_unittest_attribs.cc', + 'command_buffer/service/gles2_cmd_decoder_unittest_valuebuffer.cc', 'command_buffer/service/gl_surface_mock.cc', 'command_buffer/service/gl_surface_mock.h', 'command_buffer/service/gpu_scheduler_unittest.cc', @@ -248,10 +249,12 @@ 'command_buffer/service/program_cache_unittest.cc', 'command_buffer/service/shader_manager_unittest.cc', 'command_buffer/service/shader_translator_unittest.cc', + 'command_buffer/service/shader_translator_cache_unittest.cc', 'command_buffer/service/test_helper.cc', 'command_buffer/service/test_helper.h', 'command_buffer/service/texture_manager_unittest.cc', 'command_buffer/service/transfer_buffer_manager_unittest.cc', + 'command_buffer/service/valuebuffer_manager_unittest.cc', 'command_buffer/service/vertex_attrib_manager_unittest.cc', 'command_buffer/service/vertex_array_manager_unittest.cc', 'command_buffer/service/gpu_tracer_unittest.cc',
diff --git a/gpu/ipc/gpu_command_buffer_traits.cc b/gpu/ipc/gpu_command_buffer_traits.cc index 46395fb..1b30034 100644 --- a/gpu/ipc/gpu_command_buffer_traits.cc +++ b/gpu/ipc/gpu_command_buffer_traits.cc
@@ -3,8 +3,27 @@ // found in the LICENSE file. #include "gpu/ipc/gpu_command_buffer_traits.h" + #include "gpu/command_buffer/common/mailbox_holder.h" +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "gpu/ipc/gpu_command_buffer_traits_multi.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "gpu/ipc/gpu_command_buffer_traits_multi.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "gpu/ipc/gpu_command_buffer_traits_multi.h" +} // namespace IPC + namespace IPC { void ParamTraits<gpu::CommandBuffer::State> ::Write(Message* m,
diff --git a/gpu/ipc/gpu_command_buffer_traits.h b/gpu/ipc/gpu_command_buffer_traits.h index ce854d2..3451639 100644 --- a/gpu/ipc/gpu_command_buffer_traits.h +++ b/gpu/ipc/gpu_command_buffer_traits.h
@@ -5,9 +5,10 @@ #ifndef GPU_IPC_GPU_PARAM_TRAITS_H_ #define GPU_IPC_GPU_PARAM_TRAITS_H_ -#include "ipc/ipc_message_utils.h" #include "gpu/command_buffer/common/command_buffer.h" #include "gpu/gpu_export.h" +#include "gpu/ipc/gpu_command_buffer_traits_multi.h" +#include "ipc/ipc_message_utils.h" namespace gpu { struct Mailbox;
diff --git a/gpu/ipc/gpu_command_buffer_traits_multi.h b/gpu/ipc/gpu_command_buffer_traits_multi.h new file mode 100644 index 0000000..0978f79 --- /dev/null +++ b/gpu/ipc/gpu_command_buffer_traits_multi.h
@@ -0,0 +1,59 @@ +// 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. + +// Multiply-included message file, hence no include guard here. +#include "gpu/command_buffer/common/capabilities.h" +#include "gpu/gpu_export.h" +#include "ipc/ipc_message_utils.h" +#include "ipc/param_traits_macros.h" + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT GPU_EXPORT + +IPC_STRUCT_TRAITS_BEGIN(gpu::Capabilities::ShaderPrecision) + IPC_STRUCT_TRAITS_MEMBER(min_range) + IPC_STRUCT_TRAITS_MEMBER(max_range) + IPC_STRUCT_TRAITS_MEMBER(precision) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(gpu::Capabilities::PerStagePrecisions) + IPC_STRUCT_TRAITS_MEMBER(low_int) + IPC_STRUCT_TRAITS_MEMBER(medium_int) + IPC_STRUCT_TRAITS_MEMBER(high_int) + IPC_STRUCT_TRAITS_MEMBER(low_float) + IPC_STRUCT_TRAITS_MEMBER(medium_float) + IPC_STRUCT_TRAITS_MEMBER(high_float) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(gpu::Capabilities) + IPC_STRUCT_TRAITS_MEMBER(vertex_shader_precisions) + IPC_STRUCT_TRAITS_MEMBER(fragment_shader_precisions) + IPC_STRUCT_TRAITS_MEMBER(max_combined_texture_image_units) + IPC_STRUCT_TRAITS_MEMBER(max_cube_map_texture_size) + IPC_STRUCT_TRAITS_MEMBER(max_fragment_uniform_vectors) + IPC_STRUCT_TRAITS_MEMBER(max_renderbuffer_size) + IPC_STRUCT_TRAITS_MEMBER(max_texture_image_units) + IPC_STRUCT_TRAITS_MEMBER(max_texture_size) + IPC_STRUCT_TRAITS_MEMBER(max_varying_vectors) + IPC_STRUCT_TRAITS_MEMBER(max_vertex_attribs) + IPC_STRUCT_TRAITS_MEMBER(max_vertex_texture_image_units) + IPC_STRUCT_TRAITS_MEMBER(max_vertex_uniform_vectors) + IPC_STRUCT_TRAITS_MEMBER(num_compressed_texture_formats) + IPC_STRUCT_TRAITS_MEMBER(num_shader_binary_formats) + IPC_STRUCT_TRAITS_MEMBER(bind_generates_resource_chromium) + IPC_STRUCT_TRAITS_MEMBER(post_sub_buffer) + IPC_STRUCT_TRAITS_MEMBER(egl_image_external) + IPC_STRUCT_TRAITS_MEMBER(texture_format_bgra8888) + IPC_STRUCT_TRAITS_MEMBER(texture_format_etc1) + IPC_STRUCT_TRAITS_MEMBER(texture_format_etc1_npot) + IPC_STRUCT_TRAITS_MEMBER(texture_rectangle) + IPC_STRUCT_TRAITS_MEMBER(iosurface) + IPC_STRUCT_TRAITS_MEMBER(texture_usage) + IPC_STRUCT_TRAITS_MEMBER(texture_storage) + IPC_STRUCT_TRAITS_MEMBER(discard_framebuffer) + IPC_STRUCT_TRAITS_MEMBER(sync_query) + IPC_STRUCT_TRAITS_MEMBER(image) + IPC_STRUCT_TRAITS_MEMBER(blend_equation_advanced) + IPC_STRUCT_TRAITS_MEMBER(blend_equation_advanced_coherent) +IPC_STRUCT_TRAITS_END()
diff --git a/mojo/gles2/gles2_context.cc b/mojo/gles2/gles2_context.cc index f5e8471..51e7114 100644 --- a/mojo/gles2/gles2_context.cc +++ b/mojo/gles2/gles2_context.cc
@@ -38,7 +38,7 @@ return false; gles2_helper_->SetAutomaticFlushes(false); transfer_buffer_.reset(new gpu::TransferBuffer(gles2_helper_.get())); - bool bind_generates_resource = true; + bool bind_generates_resource = false; // TODO(piman): Some contexts (such as compositor) want this to be true, so // this needs to be a public parameter. bool lose_context_when_out_of_memory = false;
diff --git a/net/BUILD.gn b/net/BUILD.gn index a184a4c..d95aa5e 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -381,16 +381,6 @@ if (!enable_websockets) { sources -= [ - "socket_stream/socket_stream.cc", - "socket_stream/socket_stream.h", - "socket_stream/socket_stream_job.cc", - "socket_stream/socket_stream_job.h", - "socket_stream/socket_stream_job_manager.cc", - "socket_stream/socket_stream_job_manager.h", - "socket_stream/socket_stream_metrics.cc", - "socket_stream/socket_stream_metrics.h", - "spdy/spdy_websocket_stream.cc", - "spdy/spdy_websocket_stream.h", "websockets/websocket_basic_handshake_stream.cc", "websockets/websocket_basic_handshake_stream.h", "websockets/websocket_basic_stream.cc", @@ -427,15 +417,9 @@ "websockets/websocket_handshake_stream_create_helper.h", "websockets/websocket_inflater.cc", "websockets/websocket_inflater.h", - "websockets/websocket_job.cc", - "websockets/websocket_job.h", "websockets/websocket_mux.h", - "websockets/websocket_net_log_params.cc", - "websockets/websocket_net_log_params.h", "websockets/websocket_stream.cc", "websockets/websocket_stream.h", - "websockets/websocket_throttle.cc", - "websockets/websocket_throttle.h", ] } @@ -588,11 +572,6 @@ ":net", "//base", ] - if (is_win) { - cflags = [ - "/wd4267", - ] - } } executable("dump_cache") { @@ -689,6 +668,10 @@ "test/spawned_test_server/spawned_test_server.h", "test/spawned_test_server/spawner_communicator.cc", "test/spawned_test_server/spawner_communicator.h", + "test/url_request/url_request_failed_job.cc", + "test/url_request/url_request_failed_job.h", + "test/url_request/url_request_mock_http_job.cc", + "test/url_request/url_request_mock_http_job.h", "url_request/test_url_fetcher_factory.cc", "url_request/test_url_fetcher_factory.h", "url_request/test_url_request_interceptor.cc", @@ -1086,7 +1069,8 @@ # TODO(GYP) make this compile on Android, we need some native test deps done. # TODO(GYP) Also doesn't work on Windows; dependency on boringssl is wrong. -if (!is_android && !is_win) { +# TODO(GYP) Also doesn't work on Mac, need to figure out why not. +if (!is_android && !is_win && !is_mac) { source_set("quic_tools") { sources = [ @@ -1238,9 +1222,6 @@ if (!enable_websockets) { sources -= [ - "socket_stream/socket_stream_metrics_unittest.cc", - "socket_stream/socket_stream_unittest.cc", - "spdy/spdy_websocket_stream_unittest.cc", "websockets/websocket_basic_stream_test.cc", "websockets/websocket_channel_test.cc", "websockets/websocket_deflate_predictor_impl_test.cc", @@ -1254,12 +1235,9 @@ "websockets/websocket_handshake_handler_test.cc", "websockets/websocket_handshake_stream_create_helper_test.cc", "websockets/websocket_inflater_test.cc", - "websockets/websocket_job_test.cc", - "websockets/websocket_net_log_params_test.cc", "websockets/websocket_stream_test.cc", "websockets/websocket_test_util.cc", "websockets/websocket_test_util.h", - "websockets/websocket_throttle_test.cc", ] } @@ -1397,4 +1375,4 @@ ] } -} # !is_android +} # !is_android && !is_win && !is_mac
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 89fbfff..87063b7 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h
@@ -271,9 +271,7 @@ // due to a malformed frame or other protocol violation. NET_ERROR(WS_PROTOCOL_ERROR, -145) -// Connection was aborted for switching to another ptotocol. -// WebSocket abort SocketStream connection when alternate protocol is found. -NET_ERROR(PROTOCOL_SWITCHED, -146) +// Error -146 was removed (PROTOCOL_SWITCHED) // Returned when attempting to bind an address that is already in use. NET_ERROR(ADDRESS_IN_USE, -147) @@ -305,9 +303,7 @@ // pushed to the queue. NET_ERROR(WS_THROTTLE_QUEUE_TOO_LARGE, -154) -// There are too many active SocketStream instances, so the new connect request -// was rejected. -NET_ERROR(TOO_MANY_SOCKET_STREAMS, -155) +// Error -155 was removed (TOO_MANY_SOCKET_STREAMS) // The SSL server certificate changed in a renegotiation. NET_ERROR(SSL_SERVER_CERT_CHANGED, -156)
diff --git a/net/base/net_errors.cc b/net/base/net_errors.cc index 7c219af..55cdebb 100644 --- a/net/base/net_errors.cc +++ b/net/base/net_errors.cc
@@ -56,6 +56,18 @@ (error == ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN); } +bool IsClientCertificateError(int error) { + switch (error) { + case ERR_BAD_SSL_CLIENT_AUTH_CERT: + case ERR_SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED: + case ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY: + case ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED: + return true; + default: + return false; + } +} + std::vector<int> GetAllErrorCodesForUma() { return base::CustomHistogram::ArrayToCustomRanges( kAllErrorCodes, arraysize(kAllErrorCodes));
diff --git a/net/base/net_errors.h b/net/base/net_errors.h index 4dc6042..29b7742 100644 --- a/net/base/net_errors.h +++ b/net/base/net_errors.h
@@ -39,6 +39,11 @@ // Returns true if |error| is a certificate error code. NET_EXPORT bool IsCertificateError(int error); +// Returns true if |error| is a client certificate authentication error. This +// does not include ERR_SSL_PROTOCOL_ERROR which may also signal a bad client +// certificate. +NET_EXPORT bool IsClientCertificateError(int error); + // Map system error code to Error. NET_EXPORT Error MapSystemError(int os_error);
diff --git a/net/base/net_log.h b/net/base/net_log.h index 1b598ed..5fb2791 100644 --- a/net/base/net_log.h +++ b/net/base/net_log.h
@@ -27,7 +27,7 @@ // NetLog is the destination for log messages generated by the network stack. // Each log message has a "source" field which identifies the specific entity // that generated the message (for example, which URLRequest or which -// SocketStream). +// SpdySession). // // To avoid needing to pass in the "source ID" to the logging functions, NetLog // is usually accessed through a BoundNetLog, which will always pass in a
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index 823cde1..6dfe8c6 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h
@@ -26,22 +26,13 @@ // } EVENT_TYPE(FAILED) -// Marks the creation/destruction of a request (net::URLRequest or -// SocketStream). +// Marks the creation/destruction of a request (net::URLRequest). EVENT_TYPE(REQUEST_ALIVE) // ------------------------------------------------------------------------ // HostResolverImpl // ------------------------------------------------------------------------ -// The start/end of waiting on a host resolve (DNS) request. -// The BEGIN phase contains the following parameters: -// -// { -// "source_dependency": <Source id of the request being waited on>, -// } -EVENT_TYPE(HOST_RESOLVER_IMPL) - // The start/end of a host resolve (DNS) request. Note that these events are // logged for all DNS requests, though not all requests result in the creation // of a HostResolvedImpl::Request object. @@ -50,12 +41,11 @@ // // { // "host": <Hostname associated with the request>, -// "address_family": <The address family to restrict results to> +// "address_family": <The address family to restrict results to>, // "allow_cached_response": <Whether it is ok to return a result from -// the host cache> +// the host cache>, // "is_speculative": <Whether this request was started by the DNS // prefetcher> -// "source_dependency": <Source id, if any, of what created the request>, // } // // If an error occurred, the END phase will contain these parameters: @@ -1694,49 +1684,6 @@ EVENT_TYPE(HTTP_STREAM_PARSER_READ_HEADERS) // ------------------------------------------------------------------------ -// SocketStream -// ------------------------------------------------------------------------ - -// Measures the time between SocketStream::Connect() and -// SocketStream::DidEstablishConnection() -// -// For the BEGIN phase, the following parameters are attached: -// { -// "url": <String of URL being loaded>, -// } -// -// For the END phase, if there was an error, the following parameters are -// attached: -// { -// "net_error": <Net error code of the failure>, -// } -EVENT_TYPE(SOCKET_STREAM_CONNECT) - -// A message sent on the SocketStream. -EVENT_TYPE(SOCKET_STREAM_SENT) - -// A message received on the SocketStream. -EVENT_TYPE(SOCKET_STREAM_RECEIVED) - -// ------------------------------------------------------------------------ -// WebSocketJob -// ------------------------------------------------------------------------ - -// This event is sent for a WebSocket handshake request. -// The following parameters are attached: -// { -// "headers": <handshake request message>, -// } -EVENT_TYPE(WEB_SOCKET_SEND_REQUEST_HEADERS) - -// This event is sent on receipt of the WebSocket handshake response headers. -// The following parameters are attached: -// { -// "headers": <handshake response message>, -// } -EVENT_TYPE(WEB_SOCKET_READ_RESPONSE_HEADERS) - -// ------------------------------------------------------------------------ // SOCKS5ClientSocket // ------------------------------------------------------------------------
diff --git a/net/base/net_log_source_type_list.h b/net/base/net_log_source_type_list.h index 9d66c17..dc553a4 100644 --- a/net/base/net_log_source_type_list.h +++ b/net/base/net_log_source_type_list.h
@@ -9,13 +9,11 @@ SOURCE_TYPE(NONE) SOURCE_TYPE(URL_REQUEST) -SOURCE_TYPE(SOCKET_STREAM) SOURCE_TYPE(PROXY_SCRIPT_DECIDER) SOURCE_TYPE(CONNECT_JOB) SOURCE_TYPE(SOCKET) SOURCE_TYPE(SPDY_SESSION) SOURCE_TYPE(QUIC_SESSION) -SOURCE_TYPE(HOST_RESOLVER_IMPL_REQUEST) SOURCE_TYPE(HOST_RESOLVER_IMPL_JOB) SOURCE_TYPE(DISK_CACHE_ENTRY) SOURCE_TYPE(MEMORY_CACHE_ENTRY)
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc index 9cad50a..5b69cfb 100644 --- a/net/base/network_delegate.cc +++ b/net/base/network_delegate.cc
@@ -176,19 +176,6 @@ return OnAuthRequired(request, auth_info, callback, credentials); } -int NetworkDelegate::NotifyBeforeSocketStreamConnect( - SocketStream* socket, - const CompletionCallback& callback) { - DCHECK(CalledOnValidThread()); - DCHECK(socket); - DCHECK(!callback.is_null()); - // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION( - "423948 NetworkDelegate::OnBeforeSocketStreamConnect")); - return OnBeforeSocketStreamConnect(socket, callback); -} - bool NetworkDelegate::CanGetCookies(const URLRequest& request, const CookieList& cookie_list) { DCHECK(CalledOnValidThread());
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h index 299989c..03dee0b 100644 --- a/net/base/network_delegate.h +++ b/net/base/network_delegate.h
@@ -105,9 +105,6 @@ bool CanEnablePrivacyMode(const GURL& url, const GURL& first_party_for_cookies) const; - int NotifyBeforeSocketStreamConnect(SocketStream* socket, - const CompletionCallback& callback); - bool CancelURLRequestWithPolicyViolatingReferrerHeader( const URLRequest& request, const GURL& target_url,
diff --git a/net/base/registry_controlled_domains/effective_tld_names.dat b/net/base/registry_controlled_domains/effective_tld_names.dat index ab13045..111ee06 100644 --- a/net/base/registry_controlled_domains/effective_tld_names.dat +++ b/net/base/registry_controlled_domains/effective_tld_names.dat
@@ -1601,7 +1601,7 @@ // jp : http://en.wikipedia.org/wiki/.jp // http://jprs.co.jp/en/jpdomain.html -// Submitted by registry <info@jprs.jp> 2014-02-28 +// Submitted by registry <info@jprs.jp> 2014-10-30 jp // jp organizational type names ac.jp @@ -1613,7 +1613,7 @@ lg.jp ne.jp or.jp -// jp preficture type names +// jp prefecture type names aichi.jp akita.jp aomori.jp @@ -1661,6 +1661,53 @@ yamagata.jp yamaguchi.jp yamanashi.jp +栃木.jp +愛知.jp +愛媛.jp +兵庫.jp +熊本.jp +茨城.jp +北海道.jp +千葉.jp +和歌山.jp +長崎.jp +長野.jp +新潟.jp +青森.jp +静岡.jp +東京.jp +石川.jp +埼玉.jp +三重.jp +京都.jp +佐賀.jp +大分.jp +大阪.jp +奈良.jp +宮城.jp +宮崎.jp +富山.jp +山口.jp +山形.jp +山梨.jp +岩手.jp +岐阜.jp +岡山.jp +島根.jp +広島.jp +徳島.jp +沖縄.jp +滋賀.jp +神奈川.jp +福井.jp +福岡.jp +福島.jp +秋田.jp +群馬.jp +香川.jp +高知.jp +鳥取.jp +鹿児島.jp // jp geographic type names // http://jprs.jp/doc/rule/saisoku-1.html *.kawasaki.jp @@ -5259,27 +5306,30 @@ gos.pk info.pk -// pl : http://www.dns.pl/english/ +// pl http://www.dns.pl/english/index.html +// confirmed on 26.09.2014 from Bogna Tchórzewska <partner@dns.pl> pl -// NASK functional domains (nask.pl / dns.pl) : http://www.dns.pl/english/dns-funk.html +com.pl +net.pl +org.pl +info.pl +waw.pl +gov.pl +// pl functional domains (http://www.dns.pl/english/index.html) aid.pl agro.pl atm.pl auto.pl biz.pl -com.pl edu.pl gmina.pl gsm.pl -info.pl mail.pl miasta.pl media.pl mil.pl -net.pl nieruchomosci.pl nom.pl -org.pl pc.pl powiat.pl priv.pl @@ -5295,12 +5345,7 @@ tourism.pl travel.pl turystyka.pl -// ICM functional domains (icm.edu.pl) -6bone.pl -art.pl -mbone.pl // Government domains (administred by ippt.gov.pl) -gov.pl uw.gov.pl um.gov.pl ug.gov.pl @@ -5310,11 +5355,7 @@ sr.gov.pl po.gov.pl pa.gov.pl -// other functional domains -ngo.pl -irc.pl -usenet.pl -// NASK geographical domains : http://www.dns.pl/english/dns-regiony.html +// pl regional domains (http://www.dns.pl/english/index.html) augustow.pl babia-gora.pl bedzin.pl @@ -5400,7 +5441,6 @@ rzeszow.pl sanok.pl sejny.pl -siedlce.pl slask.pl slupsk.pl sosnowiec.pl @@ -5422,7 +5462,6 @@ walbrzych.pl warmia.pl warszawa.pl -waw.pl wegrow.pl wielun.pl wlocl.pl @@ -5435,18 +5474,6 @@ zarow.pl zgora.pl zgorzelec.pl -// TASK geographical domains (www.task.gda.pl/uslugi/dns) -gda.pl -gdansk.pl -gdynia.pl -med.pl -sopot.pl -// other geographical domains -gliwice.pl -krakow.pl -poznan.pl -wroc.pl -zakopane.pl // pm : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf pm @@ -5631,7 +5658,7 @@ mari-el.ru marine.ru mordovia.ru -mosreg.ru +// mosreg.ru Bug 1090800 - removed at request of Aleksey Konstantinov <konstantinovav@mosreg.ru> msk.ru murmansk.ru nalchik.ru @@ -6756,7 +6783,10 @@ *.zw -// List of new gTLDs imported from https://newgtlds.icann.org/newgtlds.csv on 2014-09-02T12:02:06Z +// List of new gTLDs imported from https://newgtlds.icann.org/newgtlds.csv on 2014-11-03T18:02:06Z + +// abb : 2014-10-24 ABB Ltd +abb // abbott : 2014-07-24 Abbott Laboratories, Inc. abbott @@ -6779,6 +6809,12 @@ // actor : 2013-12-12 United TLD Holdco Ltd. actor +// adult : 2014-10-16 ICM Registry AD LLC +adult + +// afl : 2014-10-02 Australian Football League +afl + // africa : 2014-03-24 ZA Central Registry NPC trading as Registry.Africa africa @@ -6788,6 +6824,9 @@ // airforce : 2014-03-06 United TLD Holdco Ltd. airforce +// airtel : 2014-10-24 Bharti Airtel Limited +airtel + // allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft allfinanz @@ -6812,10 +6851,10 @@ // associates : 2014-03-06 Baxter Hill, LLC associates -// attorney : 2014-03-20 undefined +// attorney : 2014-03-20 attorney -// auction : 2014-03-20 undefined +// auction : 2014-03-20 auction // audio : 2014-03-20 Uniregistry, Corp. @@ -6827,9 +6866,12 @@ // axa : 2013-12-19 AXA SA axa -// band : 2014-06-12 Auburn Hollow, LLC +// band : 2014-06-12 band +// bank : 2014-09-25 fTLD Registry Services LLC +bank + // bar : 2013-12-12 Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable bar @@ -6845,6 +6887,9 @@ // bayern : 2014-01-23 Bayern Connect GmbH bayern +// bbva : 2014-10-02 BANCO BILBAO VIZCAYA ARGENTARIA, S.A. +bbva + // bcn : 2014-07-24 Municipi de Barcelona bcn @@ -6884,6 +6929,9 @@ // blue : 2013-11-07 Afilias Limited blue +// bms : 2014-10-30 Bristol-Myers Squibb Company +bms + // bmw : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft bmw @@ -6893,6 +6941,9 @@ // bnpparibas : 2014-05-29 BNP Paribas bnpparibas +// bom : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br +bom + // bond : 2014-06-05 Bond University Limited bond @@ -6938,6 +6989,9 @@ // cancerresearch : 2014-05-15 Australian Cancer Research Foundation cancerresearch +// canon : 2014-09-12 Canon Inc. +canon + // capetown : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry capetown @@ -6995,6 +7049,9 @@ // cheap : 2013-11-14 Sand Cover, LLC cheap +// chloe : 2014-10-16 Richemont DNS Inc. +chloe + // christmas : 2013-11-21 Uniregistry, Corp. christmas @@ -7028,6 +7085,9 @@ // club : 2013-11-08 .CLUB DOMAINS, LLC club +// coach : 2014-10-09 Koko Island, LLC +coach + // codes : 2013-10-31 Puff Willow, LLC codes @@ -7058,7 +7118,7 @@ // construction : 2013-09-16 Fox Dynamite, LLC construction -// consulting : 2013-12-05 undefined +// consulting : 2013-12-05 consulting // contractors : 2013-09-10 Magic Woods, LLC @@ -7070,6 +7130,9 @@ // cool : 2013-11-14 Koko Lake, LLC cool +// corsica : 2014-09-25 Collectivité Territoriale de Corse +corsica + // country : 2013-12-19 Top Level Domain Holdings Limited country @@ -7079,12 +7142,21 @@ // creditcard : 2014-03-20 Binky Frostbite, LLC creditcard +// cricket : 2014-10-09 dot Cricket Limited +cricket + +// crown : 2014-10-24 Crown Equipment Corporation +crown + // crs : 2014-04-03 Federated Co-operatives Limited crs // cruises : 2013-12-05 Spring Way, LLC cruises +// csc : 2014-09-25 Alliance-One Services, Inc. +csc + // cuisinella : 2014-04-03 SALM S.A.S. cuisinella @@ -7112,21 +7184,30 @@ // deals : 2014-05-22 Sand Sunset, LLC deals -// degree : 2014-03-06 undefined +// degree : 2014-03-06 degree +// delivery : 2014-09-11 Steel Station, LLC +delivery + +// dell : 2014-10-24 Dell Inc. +dell + // democrat : 2013-10-24 United TLD Holdco Ltd. democrat // dental : 2014-03-20 Tin Birch, LLC dental -// dentist : 2014-03-20 undefined +// dentist : 2014-03-20 dentist // desi : 2013-11-14 Desi Networks LLC desi +// dev : 2014-10-16 Charleston Road Registry Inc. +dev + // diamonds : 2013-09-22 John Edge, LLC diamonds @@ -7148,6 +7229,12 @@ // dnp : 2013-12-13 Dai Nippon Printing Co., Ltd. dnp +// docs : 2014-10-16 Charleston Road Registry Inc. +docs + +// doha : 2014-09-18 Communications Regulatory Authority (CRA) +doha + // domains : 2013-10-17 Sugar Cross, LLC domains @@ -7172,6 +7259,9 @@ // emerck : 2014-04-03 Merck KGaA emerck +// energy : 2014-09-11 Binky Birch, LLC +energy + // engineer : 2014-03-06 United TLD Holdco Ltd. engineer @@ -7217,7 +7307,7 @@ // fail : 2014-03-06 Atomic Pipe, LLC fail -// fan : 2014-03-06 undefined +// fan : 2014-03-06 fan // farm : 2013-11-07 Just Maple, LLC @@ -7229,6 +7319,9 @@ // feedback : 2013-12-19 Top Level Spectrum, Inc. feedback +// final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br +final + // finance : 2014-03-20 Cotton Cypress, LLC finance @@ -7253,6 +7346,9 @@ // florist : 2013-11-07 Half Cypress, LLC florist +// flowers : 2014-10-09 Uniregistry, Corp. +flowers + // flsmidth : 2014-07-24 FLSmidth A/S flsmidth @@ -7262,7 +7358,7 @@ // foo : 2014-01-23 Charleston Road Registry Inc. foo -// forsale : 2014-05-22 undefined +// forsale : 2014-05-22 forsale // foundation : 2013-12-05 John Dale, LLC @@ -7280,7 +7376,7 @@ // furniture : 2014-03-20 Lone Fields, LLC furniture -// futbol : 2013-09-20 undefined +// futbol : 2013-09-20 futbol // gal : 2013-11-07 Asociación puntoGAL @@ -7370,7 +7466,7 @@ // hamburg : 2014-02-20 Hamburg Top-Level-Domain GmbH hamburg -// haus : 2013-12-05 undefined +// haus : 2013-12-05 haus // healthcare : 2014-06-12 Silver Glen, LLC @@ -7388,6 +7484,9 @@ // hiphop : 2014-03-06 Uniregistry, Corp. hiphop +// hitachi : 2014-10-31 Hitachi, Ltd. +hitachi + // hiv : 2014-03-13 dotHIV gemeinnuetziger e.V. hiv @@ -7415,9 +7514,15 @@ // how : 2014-01-23 Charleston Road Registry Inc. how +// hsbc : 2014-10-24 HSBC Holdings PLC +hsbc + // ibm : 2014-07-31 International Business Machines Corporation ibm +// ice : 2014-10-30 IntercontinentalExchange, Inc. +ice + // ifm : 2014-01-30 ifm electronic gmbh ifm @@ -7466,6 +7571,9 @@ // istanbul : 2014-08-28 Istanbul Metropolitan Municipality istanbul +// itau : 2014-10-02 Itau Unibanco Holding S.A. +itau + // iwc : 2014-06-23 Richemont DNS Inc. iwc @@ -7478,12 +7586,18 @@ // joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry joburg +// jprs : 2014-09-18 Japan Registry Services Co., Ltd. +jprs + // juegos : 2014-03-20 Uniregistry, Corp. juegos // kaufen : 2013-11-07 United TLD Holdco Ltd. kaufen +// kddi : 2014-09-12 KDDI CORPORATION +kddi + // kim : 2013-09-23 Afilias Limited kim @@ -7508,10 +7622,13 @@ // land : 2013-09-10 Pine Moon, LLC land +// lat : 2014-10-16 ECOM-LAC Federaciòn de Latinoamèrica y el Caribe para Internet y el Comercio Electrònico +lat + // latrobe : 2014-06-16 La Trobe University latrobe -// lawyer : 2014-03-20 undefined +// lawyer : 2014-03-20 lawyer // lds : 2014-03-20 IRI Domain Management, LLC (\ @@ -7523,9 +7640,18 @@ // leclerc : 2014-08-07 A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc leclerc +// legal : 2014-10-16 Blue Falls, LLC +legal + // lgbt : 2014-05-08 Afilias Limited lgbt +// liaison : 2014-10-02 Liaison Technologies, Incorporated +liaison + +// lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG +lidl + // life : 2014-02-06 Trixy Oaks, LLC life @@ -7550,6 +7676,9 @@ // lotto : 2014-04-10 Afilias Limited lotto +// ltd : 2014-09-25 Over Corner, LLC +ltd + // ltda : 2014-04-17 DOMAIN ROBOT SERVICOS DE HOSPEDAGEM NA INTERNET LTDA ltda @@ -7562,6 +7691,9 @@ // madrid : 2014-05-01 Comunidad de Madrid madrid +// maif : 2014-10-02 Mutuelle Assurance Instituteur France (MAIF) +maif + // maison : 2013-12-05 Victor Frostbite, LLC maison @@ -7571,12 +7703,15 @@ // mango : 2013-10-24 PUNTO FA S.L. mango -// market : 2014-03-06 undefined +// market : 2014-03-06 market // marketing : 2013-11-07 Fern Pass, LLC marketing +// marriott : 2014-10-09 Marriott Worldwide Corporation +marriott + // media : 2014-03-06 Grand Glen, LLC media @@ -7589,6 +7724,9 @@ // meme : 2014-01-30 Charleston Road Registry Inc. meme +// memorial : 2014-10-16 Dog Beach, LLC +memorial + // menu : 2013-09-11 Wedding TLD2, LLC menu @@ -7607,13 +7745,16 @@ // monash : 2013-09-30 Monash University monash +// money : 2014-10-16 Outer McCook, LLC +money + // montblanc : 2014-06-23 Richemont DNS Inc. montblanc // mormon : 2013-12-05 IRI Domain Management, LLC (\ mormon -// mortgage : 2014-03-20 undefined +// mortgage : 2014-03-20 mortgage // moscow : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) @@ -7625,6 +7766,9 @@ // mov : 2014-01-30 Charleston Road Registry Inc. mov +// movistar : 2014-10-16 Telefónica S.A. +movistar + // nagoya : 2013-10-24 GMO Registry, Inc. nagoya @@ -7658,16 +7802,25 @@ // nissan : 2014-03-27 NISSAN MOTOR CO., LTD. nissan +// nowruz : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +nowruz + // nra : 2014-05-22 NRA Holdings Company, INC. nra // nrw : 2013-11-21 Minds + Machines GmbH nrw +// ntt : 2014-10-31 NIPPON TELEGRAPH AND TELEPHONE CORPORATION +ntt + // nyc : 2014-01-23 The City of New York by and through the New York City Department of Information Technology & Telecommunications nyc -// okinawa : 2013-12-05 BusinessRalliart inc. +// obi : 2014-09-25 OBI Group Holding SE & Co. KGaA +obi + +// okinawa : 2013-12-05 BusinessRalliart Inc. okinawa // ong : 2014-03-06 Public Interest Registry @@ -7685,6 +7838,9 @@ // organic : 2014-03-27 Afilias Limited organic +// osaka : 2014-09-04 Interlink Co., Ltd. +osaka + // otsuka : 2013-10-11 Otsuka Holdings Co., Ltd. otsuka @@ -7694,12 +7850,18 @@ // paris : 2014-01-30 City of Paris paris +// pars : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +pars + // partners : 2013-12-05 Magic Glen, LLC partners // parts : 2013-12-05 Sea Goodbye, LLC parts +// party : 2014-09-11 Blue Sky Registry Limited +party + // pharmacy : 2014-06-19 National Association of Boards of Pharmacy pharmacy @@ -7715,6 +7877,9 @@ // physio : 2014-05-01 PhysBiz Pty Ltd physio +// piaget : 2014-10-16 Richemont DNS Inc. +piaget + // pics : 2013-11-14 Uniregistry, Corp. pics @@ -7742,6 +7907,9 @@ // poker : 2014-07-03 Afilias Domains No. 5 Limited poker +// porn : 2014-10-16 ICM Registry PN LLC +porn + // praxi : 2013-12-05 Praxi S.p.A. praxi @@ -7781,6 +7949,9 @@ // red : 2013-11-07 Afilias Limited red +// redstone : 2014-10-31 Redstone Haute Couture Co., Ltd. +redstone + // rehab : 2014-03-06 United TLD Holdco Ltd. rehab @@ -7790,6 +7961,9 @@ // reisen : 2014-03-06 New Cypress, LLC reisen +// reit : 2014-09-04 National Association of Real Estate Investment Trusts, Inc. +reit + // ren : 2013-12-12 Beijing Qianxiang Wangjing Technology Development Co., Ltd. ren @@ -7811,7 +7985,7 @@ // restaurant : 2014-07-03 Snow Avenue, LLC restaurant -// reviews : 2013-09-13 undefined +// reviews : 2013-09-13 reviews // rich : 2013-11-21 I-Registry Ltd. @@ -7823,7 +7997,7 @@ // rip : 2014-07-10 United TLD Holdco Ltd. rip -// rocks : 2013-11-14 undefined +// rocks : 2013-11-14 rocks // rodeo : 2013-12-19 Top Level Domain Holdings Limited @@ -7835,21 +8009,30 @@ // ruhr : 2013-10-02 regiodot GmbH & Co. KG ruhr -// ryukyu : 2014-01-09 BusinessRalliart inc. +// ryukyu : 2014-01-09 BusinessRalliart Inc. ryukyu // saarland : 2013-12-12 dotSaarland GmbH saarland +// sale : 2014-10-16 Half Bloom, LLC +sale + // samsung : 2014-04-03 SAMSUNG SDS CO., LTD samsung +// sanofi : 2014-10-09 Sanofi +sanofi + // sap : 2014-03-27 SAP AG sap // sarl : 2014-07-03 Delta Orchard, LLC sarl +// saxo : 2014-10-31 Saxo Bank A/S +saxo + // sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) sca @@ -7865,12 +8048,24 @@ // schule : 2014-03-06 Outer Moon, LLC schule +// schwarz : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG +schwarz + +// science : 2014-09-11 dot Science Limited +science + +// scor : 2014-10-31 SCOR SE +scor + // scot : 2014-01-23 Dot Scot Registry Limited scot // seat : 2014-05-22 SEAT, S.A. (Sociedad Unipersonal) seat +// sener : 2014-10-24 Sener Ingeniería y Sistemas, S.A. +sener + // services : 2014-02-27 Fox Castle, LLC services @@ -7883,6 +8078,9 @@ // sharp : 2014-05-01 Sharp Corporation sharp +// shia : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +shia + // shiksha : 2013-11-14 Afilias Limited shiksha @@ -7901,7 +8099,7 @@ // social : 2013-11-07 United TLD Holdco Ltd. social -// software : 2014-03-20 undefined +// software : 2014-03-20 software // sohu : 2013-12-19 Sohu.com Limited @@ -7922,6 +8120,12 @@ // spiegel : 2014-02-05 SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG spiegel +// stc : 2014-10-09 Saudi Telecom Company +stc + +// stcgroup : 2014-10-09 Saudi Telecom Company +stcgroup + // supplies : 2013-12-19 Atomic Fields, LLC supplies @@ -7940,6 +8144,12 @@ // suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION suzuki +// swiss : 2014-10-16 Swiss Confederation +swiss + +// sydney : 2014-09-18 State of New South Wales, Department of Premier and Cabinet +sydney + // systems : 2013-11-07 Dash Cypress, LLC systems @@ -7955,9 +8165,15 @@ // tax : 2014-03-20 Storm Orchard, LLC tax +// tci : 2014-09-12 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +tci + // technology : 2013-09-13 Auburn Falls technology +// telefonica : 2014-10-16 Telefónica S.A. +telefonica + // temasek : 2014-08-07 Temasek Holdings (Private) Limited temasek @@ -7997,6 +8213,9 @@ // training : 2013-11-07 Wild Willow, LLC training +// trust : 2014-10-16 +trust + // tui : 2014-07-03 TUI AG tui @@ -8021,18 +8240,30 @@ // versicherung : 2014-03-20 dotversicherung-registry GmbH versicherung -// vet : 2014-03-06 undefined +// vet : 2014-03-06 vet // viajes : 2013-10-17 Black Madison, LLC viajes +// video : 2014-10-16 Lone Tigers, LLC +video + // villas : 2013-12-05 New Sky, LLC villas +// virgin : 2014-09-25 Virgin Enterprises Limited +virgin + // vision : 2013-12-05 Koko Station, LLC vision +// vista : 2014-09-18 Vistaprint Limited +vista + +// vistaprint : 2014-09-18 Vistaprint Limited +vistaprint + // vlaanderen : 2014-02-06 DNS.be vzw vlaanderen @@ -8102,6 +8333,9 @@ // wtf : 2014-03-06 Hidden Way, LLC wtf +// xerox : 2014-10-24 Xerox DNHC LLC +xerox + // xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd. 佛山 @@ -8195,6 +8429,9 @@ // xn--mgbab2bd : 2013-10-31 CORE Association بازار +// xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. +همراه + // xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd. 政府 @@ -8234,6 +8471,9 @@ // xn--vhquv : 2013-08-27 Dash McCook, LLC 企业 +// xn--vuq861b : 2014-10-16 Beijing Tele-info Network Technology Co., Ltd. +信息 + // xn--xhq521b : 2013-11-14 Guangzhou YU Wei Information Technology Co., Ltd. 广东 @@ -8706,6 +8946,7 @@ // Google, Inc. // Submitted by Eduardo Vela <evn@google.com> 2012-10-24 appspot.com +blogspot.ae blogspot.be blogspot.bj blogspot.ca @@ -8720,6 +8961,7 @@ blogspot.com.au blogspot.com.br blogspot.com.es +blogspot.com.tr blogspot.cv blogspot.cz blogspot.de @@ -8741,6 +8983,7 @@ blogspot.pt blogspot.re blogspot.ro +blogspot.ru blogspot.se blogspot.sg blogspot.sk @@ -8793,6 +9036,14 @@ // Submitted by Duarte Santos <domain-admin@outsystemscloud.com> 2014-03-11 outsystemscloud.com +// .pl domains (grandfathered) +art.pl +gliwice.pl +krakow.pl +poznan.pl +wroc.pl +zakopane.pl + // Red Hat, Inc. OpenShift : https://openshift.redhat.com/ // Submitted by Tim Kramer <tkramer@rhcloud.com> 2012-10-24 rhcloud.com @@ -8805,6 +9056,13 @@ // Submitted by registry <lendl@nic.at> 2008-06-09 priv.at +// TASK geographical domains (www.task.gda.pl/uslugi/dns) +gda.pl +gdansk.pl +gdynia.pl +med.pl +sopot.pl + // Yola : https://www.yola.com/ // Submitted by Stefano Rivera <stefano@yola.com> 2014-07-09 yolasite.com
diff --git a/net/base/registry_controlled_domains/effective_tld_names.gperf b/net/base/registry_controlled_domains/effective_tld_names.gperf index 1ef69e4..b3b6afd 100644 --- a/net/base/registry_controlled_domains/effective_tld_names.gperf +++ b/net/base/registry_controlled_domains/effective_tld_names.gperf
@@ -19,7 +19,6 @@ 4.bg, 0 5.bg, 0 6.bg, 0 -6bone.pl, 0 7.bg, 0 8.bg, 0 9.bg, 0 @@ -31,6 +30,7 @@ aarborte.no, 0 ab.ca, 0 abashiri.hokkaido.jp, 0 +abb, 0 abbott, 0 abeno.osaka.jp, 0 abiko.chiba.jp, 0 @@ -88,6 +88,7 @@ ad.jp, 0 adachi.tokyo.jp, 0 adm.br, 0 +adult, 0 adult.ht, 0 adv.br, 0 adygeya.ru, 0 @@ -103,6 +104,7 @@ aeroport.fr, 0 af, 0 afjord.no, 0 +afl, 0 africa, 0 africa.com, 4 ag, 0 @@ -138,6 +140,7 @@ airguard.museum, 0 airline.aero, 0 airport.aero, 0 +airtel, 0 airtraffic.aero, 0 aisai.aichi.jp, 0 aisho.shiga.jp, 0 @@ -276,7 +279,7 @@ art.dz, 0 art.ht, 0 art.museum, 0 -art.pl, 0 +art.pl, 4 art.sn, 0 artanddesign.museum, 0 artcenter.museum, 0 @@ -421,6 +424,7 @@ band, 0 bandai.fukushima.jp, 0 bando.ibaraki.jp, 0 +bank, 0 bar, 0 bar.pro, 0 barcelona, 0 @@ -447,6 +451,7 @@ bayern, 0 bb, 0 bbs.tr, 0 +bbva, 0 bc.ca, 0 bcn, 0 bd, 2 @@ -541,6 +546,7 @@ blogdns.net, 4 blogdns.org, 4 blogsite.org, 4 +blogspot.ae, 4 blogspot.be, 4 blogspot.bj, 4 blogspot.ca, 4 @@ -555,6 +561,7 @@ blogspot.com.au, 4 blogspot.com.br, 4 blogspot.com.es, 4 +blogspot.com.tr, 4 blogspot.cv, 4 blogspot.cz, 4 blogspot.de, 4 @@ -576,6 +583,7 @@ blogspot.pt, 4 blogspot.re, 4 blogspot.ro, 4 +blogspot.ru, 4 blogspot.se, 4 blogspot.sg, 4 blogspot.sk, 4 @@ -585,6 +593,7 @@ blue, 0 bm, 0 bmd.br, 0 +bms, 0 bmw, 0 bn, 2 bn.it, 0 @@ -601,6 +610,7 @@ bologna.it, 0 bolt.hu, 0 bolzano.it, 0 +bom, 0 bomlo.no, 0 bond, 0 bonn.museum, 0 @@ -698,6 +708,7 @@ can.museum, 0 canada.museum, 0 cancerresearch, 0 +canon, 0 capebreton.museum, 0 capetown, 0 capital, 0 @@ -849,6 +860,7 @@ chiyoda.gunma.jp, 0 chiyoda.tokyo.jp, 0 chizu.tottori.jp, 0 +chloe, 0 chocolate.museum, 0 chofu.tokyo.jp, 0 chonan.chiba.jp, 0 @@ -973,6 +985,7 @@ co.uz, 0 co.ve, 0 co.vi, 0 +coach, 0 coal.museum, 0 coastaldefence.museum, 0 codes, 0 @@ -1155,6 +1168,7 @@ coop.tt, 0 copenhagen.museum, 0 corporation.museum, 0 +corsica, 0 corvette.museum, 0 cosenza.it, 0 costume.museum, 0 @@ -1175,11 +1189,14 @@ cremona.it, 0 crew.aero, 0 cri.nz, 0 +cricket, 0 crimea.ua, 0 crotone.it, 0 +crown, 0 crs, 0 cruises, 0 cs.it, 0 +csc, 0 ct.it, 0 ct.us, 0 cu, 0 @@ -1235,6 +1252,8 @@ defense.tn, 0 degree, 0 delaware.museum, 0 +delivery, 0 +dell, 0 dell-ogliastra.it, 0 dellogliastra.it, 0 delmenhorst.museum, 0 @@ -1249,6 +1268,7 @@ design.aero, 0 design.museum, 0 detroit.museum, 0 +dev, 0 dgca.aero, 0 diamonds, 0 dielddanuorri.no, 0 @@ -1277,9 +1297,11 @@ dnsdojo.net, 4 dnsdojo.org, 4 do, 0 +docs, 0 does-it.net, 4 doesntexist.com, 4 doesntexist.org, 4 +doha, 0 dolls.museum, 0 domains, 0 dominic.ua, 0 @@ -1509,6 +1531,7 @@ endofinternet.org, 4 endoftheinternet.org, 4 enebakk.no, 0 +energy, 0 eng.br, 0 eng.pro, 0 engerdal.no, 0 @@ -1616,6 +1639,7 @@ film.museum, 0 fin.ec, 0 fin.tn, 0 +final, 0 finance, 0 financial, 0 fineart.museum, 0 @@ -1655,6 +1679,7 @@ florida.museum, 0 florist, 0 floro.no, 0 +flowers, 0 flsmidth, 0 fly, 0 flynnhub.com, 4 @@ -1867,10 +1892,10 @@ gc.ca, 0 gd, 0 gd.cn, 0 -gda.pl, 0 -gdansk.pl, 0 +gda.pl, 4 +gdansk.pl, 4 gdn, 0 -gdynia.pl, 0 +gdynia.pl, 4 ge, 0 ge.it, 0 geek.nz, 0 @@ -1920,7 +1945,7 @@ glass.museum, 0 gle, 0 gliding.aero, 0 -gliwice.pl, 0 +gliwice.pl, 4 global, 0 global.prod.fastly.net, 4 global.ssl.fastly.net, 4 @@ -2360,6 +2385,7 @@ history.museum, 0 historyofscience.museum, 0 hita.oita.jp, 0 +hitachi, 0 hitachi.ibaraki.jp, 0 hitachinaka.ibaraki.jp, 0 hitachiomiya.ibaraki.jp, 0 @@ -2432,6 +2458,7 @@ hoylandet.no, 0 hr, 0 hs.kr, 0 +hsbc, 0 ht, 0 hu, 0 hu.com, 4 @@ -2456,6 +2483,7 @@ ibestad.no, 0 ibigawa.gifu.jp, 0 ibm, 0 +ice, 0 ichiba.tokushima.jp, 0 ichihara.chiba.jp, 0 ichikai.tochigi.jp, 0 @@ -2620,7 +2648,6 @@ iq, 0 ir, 0 iraq.museum, 0 -irc.pl, 0 iris.arpa, 0 irish, 0 irkutsk.ru, 0 @@ -2737,6 +2764,7 @@ itakura.gunma.jp, 0 itami.hyogo.jp, 0 itano.tokushima.jp, 0 +itau, 0 itayanagi.aomori.jp, 0 ito.shizuoka.jp, 0 itoigawa.niigata.jp, 0 @@ -2821,6 +2849,7 @@ jp, 0 jp.net, 4 jpn.com, 4 +jprs, 0 js.cn, 0 judaica.museum, 0 judygarland.museum, 0 @@ -3042,6 +3071,7 @@ kazo.saitama.jp, 0 kazuno.akita.jp, 0 kchr.ru, 0 +kddi, 0 ke, 2 keisen.fukuoka.jp, 0 kembuchi.hokkaido.jp, 0 @@ -3203,7 +3233,7 @@ kr.ua, 0 kraanghke.no, 0 kragero.no, 0 -krakow.pl, 0 +krakow.pl, 4 krasnoyarsk.ru, 0 krd, 0 kred, 0 @@ -3315,6 +3345,7 @@ larsson.museum, 0 larvik.no, 0 laspezia.it, 0 +lat, 0 latina.it, 0 latrobe, 0 lavagis.no, 0 @@ -3338,6 +3369,7 @@ lecco.it, 0 leclerc, 0 leg.br, 0 +legal, 0 legnica.pl, 0 leikanger.no, 0 leirfjord.no, 0 @@ -3357,6 +3389,7 @@ lgbt, 0 li, 0 li.it, 0 +liaison, 0 lib.ak.us, 0 lib.al.us, 0 lib.ar.us, 0 @@ -3412,6 +3445,7 @@ lib.wa.us, 0 lib.wi.us, 0 lib.wy.us, 0 +lidl, 0 lier.no, 0 lierne.no, 0 life, 0 @@ -3463,6 +3497,7 @@ lt, 0 lt.it, 0 lt.ua, 0 +ltd, 0 ltd.co.im, 0 ltd.gi, 0 ltd.lk, 0 @@ -3505,6 +3540,7 @@ magazine.aero, 0 magnitka.ru, 0 maibara.shiga.jp, 0 +maif, 0 mail.pl, 0 maintenance.aero, 0 maison, 0 @@ -3542,6 +3578,7 @@ marketing, 0 marketplace.aero, 0 marnardal.no, 0 +marriott, 0 marugame.kagawa.jp, 0 marumori.miyagi.jp, 0 maryland.museum, 0 @@ -3578,7 +3615,6 @@ mazury.pl, 0 mb.ca, 0 mb.it, 0 -mbone.pl, 0 mc, 0 mc.it, 0 md, 0 @@ -3596,7 +3632,7 @@ med.ly, 0 med.om, 0 med.pa, 0 -med.pl, 0 +med.pl, 4 med.pro, 0 med.sa, 0 med.sd, 0 @@ -3622,6 +3658,7 @@ melhus.no, 0 meloy.no, 0 meme, 0 +memorial, 0 memorial.museum, 0 menu, 0 meraker.no, 0 @@ -3840,6 +3877,7 @@ moma.museum, 0 mombetsu.hokkaido.jp, 0 monash, 0 +money, 0 money.museum, 0 monmouth.museum, 0 montblanc, 0 @@ -3867,7 +3905,6 @@ moseushi.hokkaido.jp, 0 mosjoen.no, 0 moskenes.no, 0 -mosreg.ru, 0 moss.no, 0 mosvik.no, 0 motegi.tochigi.jp, 0 @@ -3877,6 +3914,7 @@ motosu.gifu.jp, 0 motoyama.kochi.jp, 0 mov, 0 +movistar, 0 mp, 0 mp.br, 0 mq, 0 @@ -4228,7 +4266,6 @@ ngo, 0 ngo.lk, 0 ngo.ph, 0 -ngo.pl, 0 nh.us, 0 nhk, 0 nhs.uk, 0 @@ -4340,6 +4377,7 @@ novara.it, 0 novosibirsk.ru, 0 nowaruda.pl, 0 +nowruz, 0 nozawaonsen.nagano.jp, 0 np, 2 nr, 0 @@ -4357,6 +4395,7 @@ nt.no, 0 nt.ro, 0 ntr.br, 0 +ntt, 0 nu, 0 nu.ca, 0 nu.it, 0 @@ -4383,6 +4422,7 @@ obama.fukui.jp, 0 obama.nagasaki.jp, 0 obanazawa.yamagata.jp, 0 +obi, 0 obihiro.hokkaido.jp, 0 obira.hokkaido.jp, 0 obu.aichi.jp, 0 @@ -4672,6 +4712,7 @@ oryol.ru, 0 os.hedmark.no, 0 os.hordaland.no, 0 +osaka, 0 osaka.jp, 0 osakasayama.osaka.jp, 0 osaki.miyagi.jp, 0 @@ -4757,9 +4798,11 @@ parliament.nz, 0 parma.it, 0 paroch.k12.ma.us, 0 +pars, 0 parti.se, 0 partners, 0 parts, 0 +party, 0 pasadena.museum, 0 passenger-association.aero, 0 pavia.it, 0 @@ -4802,6 +4845,7 @@ physio, 0 pi.it, 0 piacenza.it, 0 +piaget, 0 pics, 0 pictet, 0 pictures, 0 @@ -4851,6 +4895,7 @@ pomorskie.pl, 0 pomorze.pl, 0 pordenone.it, 0 +porn, 0 porsanger.no, 0 porsangu.no, 0 porsgrunn.no, 0 @@ -4862,7 +4907,7 @@ posts-and-telecommunications.museum, 0 potenza.it, 0 powiat.pl, 0 -poznan.pl, 0 +poznan.pl, 4 pp.az, 0 pp.ru, 0 pp.se, 0 @@ -4989,6 +5034,7 @@ recreation.aero, 0 red, 0 red.sv, 0 +redstone, 0 reggio-calabria.it, 0 reggio-emilia.it, 0 reggiocalabria.it, 0 @@ -4996,6 +5042,7 @@ rehab, 0 reise, 0 reisen, 0 +reit, 0 reklam.hu, 0 rel.ht, 0 rel.pl, 0 @@ -5158,6 +5205,7 @@ sakyo.kyoto.jp, 0 salangen.no, 0 salat.no, 0 +sale, 0 salem.museum, 0 salerno.it, 0 saltdal.no, 0 @@ -5184,6 +5232,7 @@ sannan.hyogo.jp, 0 sannohe.aomori.jp, 0 sano.tochigi.jp, 0 +sanofi, 0 sanok.pl, 0 santabarbara.museum, 0 santacruz.museum, 0 @@ -5214,6 +5263,7 @@ savannahga.museum, 0 saves-the-whales.com, 4 savona.it, 0 +saxo, 0 sayama.osaka.jp, 0 sayama.saitama.jp, 0 sayo.hyogo.jp, 0 @@ -5246,8 +5296,10 @@ school.na, 0 school.nz, 0 schule, 0 +schwarz, 0 schweiz.museum, 0 sci.eg, 0 +science, 0 science-fiction.museum, 0 science.museum, 0 scienceandhistory.museum, 0 @@ -5258,6 +5310,7 @@ sciences.museum, 0 sciencesnaturelles.museum, 0 scientist.aero, 0 +scor, 0 scot, 0 scotland.museum, 0 scrapper-site.net, 4 @@ -5297,6 +5350,7 @@ semboku.akita.jp, 0 semine.miyagi.jp, 0 sendai.jp, 2 +sener, 0 sennan.osaka.jp, 0 seoul.kr, 0 sera.hiroshima.jp, 0 @@ -5331,6 +5385,7 @@ sharp, 0 shell.museum, 0 sherbrooke.museum, 0 +shia, 0 shibata.miyagi.jp, 0 shibata.niigata.jp, 0 shibecha.hokkaido.jp, 0 @@ -5435,7 +5490,6 @@ sic.it, 0 sicilia.it, 0 sicily.it, 0 -siedlce.pl, 0 siellak.no, 0 siena.it, 0 sigdal.no, 0 @@ -5514,7 +5568,7 @@ songdalen.no, 0 soni.nara.jp, 0 soo.kagoshima.jp, 0 -sopot.pl, 0 +sopot.pl, 4 sor-aurdal.no, 0 sor-fron.no, 0 sor-odal.no, 0 @@ -5565,6 +5619,8 @@ stavanger.no, 0 stavern.no, 0 stavropol.ru, 0 +stc, 0 +stcgroup, 0 steam.museum, 0 steiermark.museum, 0 steigen.no, 0 @@ -5640,9 +5696,11 @@ swidnica.pl, 0 swiebodzin.pl, 0 swinoujscie.pl, 0 +swiss, 0 sx, 0 sx.cn, 0 sy, 0 +sydney, 0 sydney.museum, 0 sykkylven.no, 0 systems, 0 @@ -5758,6 +5816,7 @@ taxi.aero, 0 taxi.br, 0 tc, 0 +tci, 0 tcm.museum, 0 td, 0 te.it, 0 @@ -5769,6 +5828,7 @@ tel, 0 tel.tr, 0 teledata.mz, 1 +telefonica, 0 telekommunikation.museum, 0 television.museum, 0 temasek, 0 @@ -5977,6 +6037,7 @@ tromsa.no, 0 tromso.no, 0 trondheim.no, 0 +trust, 0 trust.museum, 0 trustee.museum, 0 trysil.no, 0 @@ -6127,7 +6188,6 @@ uscountryestate.museum, 0 usculture.museum, 0 usdecorativearts.museum, 0 -usenet.pl, 0 usgarden.museum, 0 ushiku.ibaraki.jp, 0 ushistory.museum, 0 @@ -6235,6 +6295,7 @@ vic.edu.au, 0 vic.gov.au, 0 vicenza.it, 0 +video, 0 video.hu, 0 vik.no, 0 viking.museum, 0 @@ -6244,10 +6305,13 @@ vindafjord.no, 0 vinnica.ua, 0 vinnytsia.ua, 0 +virgin, 0 virginia.museum, 0 virtual.museum, 0 virtuel.museum, 0 vision, 0 +vista, 0 +vistaprint, 0 viterbo.it, 0 vlaanderen, 0 vlaanderen.museum, 0 @@ -6360,7 +6424,7 @@ world, 0 worse-than.tv, 4 writesthisblog.com, 4 -wroc.pl, 0 +wroc.pl, 4 wroclaw.pl, 0 ws, 0 ws.na, 0 @@ -6372,27 +6436,45 @@ wy.us, 0 x.bg, 0 x.se, 0 +xerox, 0 xj.cn, 0 +xn--0trq7p7nn.jp, 0 +xn--1ctwo.jp, 0 +xn--1lqs03n.jp, 0 +xn--1lqs71d.jp, 0 xn--1qqw23a, 0 +xn--2m4a15e.jp, 0 xn--30rr7y, 0 +xn--32vp30h.jp, 0 xn--3bst00m, 0 xn--3ds443g, 0 xn--3e0b707e, 0 xn--45brj9c, 0 xn--45q11c, 0 xn--4gbrim, 0 +xn--4it168d.jp, 0 +xn--4it797k.jp, 0 +xn--4pvxs.jp, 0 xn--54b7fta0cc, 0 xn--55qw42g, 0 xn--55qx5d, 0 xn--55qx5d.cn, 0 xn--55qx5d.hk, 0 +xn--5js045d.jp, 0 +xn--5rtp49c.jp, 0 +xn--5rtq34k.jp, 0 +xn--6btw5a.jp, 0 xn--6frz82g, 0 +xn--6orx2r.jp, 0 xn--6qq986b3xl, 0 +xn--7t0a264c.jp, 0 xn--80adxhks, 0 xn--80ao21a, 0 xn--80asehdb, 0 xn--80aswg, 0 xn--80au.xn--90a3ac, 0 +xn--8ltr62k.jp, 0 +xn--8pvr4u.jp, 0 xn--90a3ac, 0 xn--90azh.xn--90a3ac, 0 xn--9dbhblg6di.museum, 0 @@ -6423,6 +6505,7 @@ xn--btsfjord-9za.no, 0 xn--c1avg, 0 xn--c1avg.xn--90a3ac, 0 +xn--c3s14m.jp, 0 xn--cg4bki, 0 xn--ciqpn.hk, 0 xn--clchc0ea0b2g2a9gcd, 0 @@ -6434,12 +6517,19 @@ xn--czrw28b.tw, 0 xn--d1acj3b, 0 xn--d1at.xn--90a3ac, 0 +xn--d5qv7z876c.jp, 0 xn--davvenjrga-y4a.no, 0 +xn--djrs72d6uy.jp, 0 +xn--djty4k.jp, 0 xn--dnna-gra.no, 0 xn--drbak-wua.no, 0 xn--dyry-ira.no, 0 +xn--efvn9s.jp, 0 xn--efvy88h, 0 +xn--ehqz56n.jp, 0 +xn--elqq16h.jp, 0 xn--eveni-0qa01ga.no, 0 +xn--f6qx53a.jp, 0 xn--finny-yua.no, 0 xn--fiq228c5hs, 0 xn--fiq64b, 0 @@ -6488,9 +6578,15 @@ xn--j6w193g, 0 xn--jlster-bya.no, 0 xn--jrpeland-54a.no, 0 +xn--k7yn95e.jp, 0 xn--karmy-yua.no, 0 +xn--kbrq7o.jp, 0 xn--kfjord-iua.no, 0 xn--klbu-woa.no, 0 +xn--klt787d.jp, 0 +xn--kltp7d.jp, 0 +xn--kltx9a.jp, 0 +xn--klty5x.jp, 0 xn--koluokta-7ya57h.no, 0 xn--kprw13d, 0 xn--kpry57d, 0 @@ -6540,10 +6636,12 @@ xn--mgberp4a5d4ar, 0 xn--mgbqly7c0a67fbc, 0 xn--mgbqly7cvafr, 0 +xn--mgbt3dhd, 0 xn--mgbtf8fl, 0 xn--mgbx4cd0ab, 0 xn--mjndalen-64a.no, 0 xn--mk0axi.hk, 0 +xn--mkru45i.jp, 0 xn--mlatvuopmi-s4a.no, 0 xn--mli-tla.no, 0 xn--mlselv-iua.no, 0 @@ -6557,12 +6655,15 @@ xn--mxtq1m, 0 xn--mxtq1m.hk, 0 xn--ngbc5azd, 0 +xn--nit225k.jp, 0 xn--nmesjevuemie-tcba.no, 0 xn--nnx388a, 0 xn--node, 0 xn--nqv7f, 0 xn--nqv7fs00ema, 0 xn--nry-yla5g.no, 0 +xn--ntso0iqx3a.jp, 0 +xn--ntsq17g.jp, 0 xn--nttery-byae.no, 0 xn--nvuotna-hwa.no, 0 xn--o1ac.xn--90a3ac, 0 @@ -6579,8 +6680,10 @@ xn--p1ai, 0 xn--pgbs0dh, 0 xn--porsgu-sta26f.no, 0 +xn--pssu33l.jp, 0 xn--q9jyb4c, 0 xn--qcka1pmc, 0 +xn--qqqt11m.jp, 0 xn--rady-ira.no, 0 xn--rdal-poa.no, 0 xn--rde-ula.no, 0 @@ -6589,11 +6692,15 @@ xn--rhkkervju-01af.no, 0 xn--rholt-mra.no, 0 xn--rhqv96g, 0 +xn--rht27z.jp, 0 +xn--rht3d.jp, 0 +xn--rht61e.jp, 0 xn--risa-5na.no, 0 xn--risr-ira.no, 0 xn--rland-uua.no, 0 xn--rlingen-mxa.no, 0 xn--rmskog-bya.no, 0 +xn--rny31h.jp, 0 xn--rros-gra.no, 0 xn--rskog-uua.no, 0 xn--rst-0na.no, 0 @@ -6633,6 +6740,7 @@ xn--tjme-hra.no, 0 xn--tn0ag.hk, 0 xn--tnsberg-q1a.no, 0 +xn--tor131o.jp, 0 xn--trany-yua.no, 0 xn--trgstad-r1a.no, 0 xn--trna-woa.no, 0 @@ -6641,8 +6749,11 @@ xn--uc0atv.hk, 0 xn--uc0atv.tw, 0 xn--uc0ay4a.hk, 0 +xn--uist22h.jp, 0 +xn--uisz3g.jp, 0 xn--unjrga-rta.no, 0 xn--unup4y, 0 +xn--uuwu58a.jp, 0 xn--vads-jra.no, 0 xn--vard-jra.no, 0 xn--vegrshei-c0a.no, 0 @@ -6652,12 +6763,14 @@ xn--vg-yiab.no, 0 xn--vgan-qoa.no, 0 xn--vgsy-qoa0j.no, 0 +xn--vgu402c.jp, 0 xn--vhquv, 0 xn--vler-qoa.hedmark.no, 0 xn--vler-qoa.xn--stfold-9xa.no, 0 xn--vre-eiker-k8a.no, 0 xn--vrggt-xqad.no, 0 xn--vry-yla5g.no, 0 +xn--vuq861b, 0 xn--wcvs22d.hk, 0 xn--wgbh1c, 0 xn--wgbl6a, 0 @@ -6669,6 +6782,7 @@ xn--ygarden-p1a.no, 0 xn--ygbi2ammx, 0 xn--ystre-slidre-ujb.no, 0 +xn--zbx025d.jp, 0 xn--zf0ao64a.tw, 0 xn--zf0avx.hk, 0 xn--zfr164b, 0 @@ -6805,7 +6919,7 @@ za.org, 4 zachpomor.pl, 0 zagan.pl, 0 -zakopane.pl, 0 +zakopane.pl, 4 zama.kanagawa.jp, 0 zamami.okinawa.jp, 0 zao.miyagi.jp, 0
diff --git a/net/base/request_priority.h b/net/base/request_priority.h index e663610..dcf7793 100644 --- a/net/base/request_priority.h +++ b/net/base/request_priority.h
@@ -11,6 +11,10 @@ // Prioritization used in various parts of the networking code such // as connection prioritization and resource loading prioritization. +// A Java counterpart will be generated for this enum. +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net +// GENERATED_JAVA_CLASS_NAME_OVERRIDE: RequestPriority +// GENERATED_JAVA_PREFIX_TO_STRIP: enum RequestPriority { IDLE = 0, MINIMUM_PRIORITY = IDLE,
diff --git a/net/cert/ct_known_logs_static.h b/net/cert/ct_known_logs_static.h index 7171271..a99944d 100644 --- a/net/cert/ct_known_logs_static.h +++ b/net/cert/ct_known_logs_static.h
@@ -27,9 +27,17 @@ "\x6b\xbd\x27\xbc\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85" "\x3b\x0d\xf7\x1f\x3f\xe9", "Google 'Aviator' log" + }, + {"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86" + "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa2\xf7\xed\x13\xe1\xd3\x5c" + "\x02\x08\xc4\x8e\x8b\x9b\x8b\x3b\x39\x68\xc7\x92\x6a\x38\xa1\x4f\x23" + "\xc5\xa5\x6f\x6f\xd7\x65\x81\xf8\xc1\x9b\xf4\x9f\xa9\x8b\x45\xf4\xb9" + "\x4e\x1b\xc9\xa2\x69\x17\xa5\x78\x87\xd9\xce\x88\x6f\x41\x03\xbb\xa3" + "\x2a\xe3\x77\x97\x8d\x78", + "SSLWatcher.com CT log 'alpha'" } }; -const size_t kNumKnownCTLogs = 2; +const size_t kNumKnownCTLogs = 3; #endif // NET_CERT_CT_KNOWN_LOGS_STATIC_H_
diff --git a/net/cert/ct_log_verifier_openssl.cc b/net/cert/ct_log_verifier_openssl.cc index 884ae8a..55bc3e1 100644 --- a/net/cert/ct_log_verifier_openssl.cc +++ b/net/cert/ct_log_verifier_openssl.cc
@@ -56,13 +56,10 @@ const base::StringPiece& description) { crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - crypto::ScopedBIO bio( - BIO_new_mem_buf(const_cast<char*>(public_key.data()), public_key.size())); - if (!bio.get()) - return false; - - public_key_ = d2i_PUBKEY_bio(bio.get(), NULL); - if (!public_key_) + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(public_key.data()); + const uint8_t* end = ptr + public_key.size(); + public_key_ = d2i_PUBKEY(nullptr, &ptr, public_key.size()); + if (!public_key_ || ptr != end) return false; key_id_ = crypto::SHA256HashString(public_key);
diff --git a/net/cert/ct_log_verifier_unittest.cc b/net/cert/ct_log_verifier_unittest.cc index 373f69e..e8d1bc4 100644 --- a/net/cert/ct_log_verifier_unittest.cc +++ b/net/cert/ct_log_verifier_unittest.cc
@@ -89,4 +89,13 @@ ASSERT_FALSE(log_->SetSignedTreeHead(sth.Pass())); } +// Test that excess data after the public key is rejected. +TEST_F(CTLogVerifierTest, ExcessDataInPublicKey) { + std::string key = ct::GetTestPublicKey(); + key += "extra"; + + scoped_ptr<CTLogVerifier> log = CTLogVerifier::Create(key, "testlog"); + EXPECT_FALSE(log); +} + } // namespace net
diff --git a/net/data/url_request_unittest/simple.html b/net/data/url_request_unittest/simple.html new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/net/data/url_request_unittest/simple.html
@@ -0,0 +1 @@ +hello
diff --git a/net/data/url_request_unittest/simple.html.mock-http-headers b/net/data/url_request_unittest/simple.html.mock-http-headers new file mode 100644 index 0000000..be8f7b5 --- /dev/null +++ b/net/data/url_request_unittest/simple.html.mock-http-headers
@@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 5
diff --git a/net/data/url_request_unittest/two-content-lengths.html b/net/data/url_request_unittest/two-content-lengths.html new file mode 100644 index 0000000..364322d --- /dev/null +++ b/net/data/url_request_unittest/two-content-lengths.html
@@ -0,0 +1 @@ +This file is boring; all the action's in the .mock-http-headers.
diff --git a/net/data/url_request_unittest/two-content-lengths.html.mock-http-headers b/net/data/url_request_unittest/two-content-lengths.html.mock-http-headers new file mode 100644 index 0000000..707c0b0 --- /dev/null +++ b/net/data/url_request_unittest/two-content-lengths.html.mock-http-headers
@@ -0,0 +1,4 @@ +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 42 +Content-Length: 41
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index 8913f17..fe63820 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc
@@ -329,11 +329,9 @@ // Creates NetLog parameters containing the information in a RequestInfo object, // along with the associated NetLog::Source. -base::Value* NetLogRequestInfoCallback(const NetLog::Source& source, - const HostResolver::RequestInfo* info, +base::Value* NetLogRequestInfoCallback(const HostResolver::RequestInfo* info, NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); - source.AddToEventParameters(dict); dict->SetString("host", info->host_port_pair().ToString()); dict->SetInteger("address_family", @@ -374,34 +372,25 @@ // Logs when a request has just been started. void LogStartRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, const HostResolver::RequestInfo& info) { source_net_log.BeginEvent( - NetLog::TYPE_HOST_RESOLVER_IMPL, - request_net_log.source().ToEventParametersCallback()); - - request_net_log.BeginEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, - base::Bind(&NetLogRequestInfoCallback, source_net_log.source(), &info)); + base::Bind(&NetLogRequestInfoCallback, &info)); } // Logs when a request has just completed (before its callback is run). void LogFinishRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, const HostResolver::RequestInfo& info, int net_error) { - request_net_log.EndEventWithNetErrorCode( + source_net_log.EndEventWithNetErrorCode( NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, net_error); - source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL); } // Logs when a request has been cancelled. void LogCancelRequest(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, const HostResolverImpl::RequestInfo& info) { - request_net_log.AddEvent(NetLog::TYPE_CANCELLED); - request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST); - source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL); + source_net_log.AddEvent(NetLog::TYPE_CANCELLED); + source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST); } //----------------------------------------------------------------------------- @@ -461,13 +450,11 @@ class HostResolverImpl::Request { public: Request(const BoundNetLog& source_net_log, - const BoundNetLog& request_net_log, const RequestInfo& info, RequestPriority priority, const CompletionCallback& callback, AddressList* addresses) : source_net_log_(source_net_log), - request_net_log_(request_net_log), info_(info), priority_(priority), job_(NULL), @@ -511,11 +498,6 @@ return source_net_log_; } - // NetLog for this request. - const BoundNetLog& request_net_log() { - return request_net_log_; - } - const RequestInfo& info() const { return info_; } @@ -525,8 +507,7 @@ base::TimeTicks request_time() const { return request_time_; } private: - BoundNetLog source_net_log_; - BoundNetLog request_net_log_; + const BoundNetLog source_net_log_; // The request info that started the request. const RequestInfo info_; @@ -1200,7 +1181,7 @@ Job(const base::WeakPtr<HostResolverImpl>& resolver, const Key& key, RequestPriority priority, - const BoundNetLog& request_net_log) + const BoundNetLog& source_net_log) : resolver_(resolver), key_(key), priority_tracker_(priority), @@ -1210,14 +1191,14 @@ dns_task_error_(OK), creation_time_(base::TimeTicks::Now()), priority_change_time_(creation_time_), - net_log_(BoundNetLog::Make(request_net_log.net_log(), + net_log_(BoundNetLog::Make(source_net_log.net_log(), NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) { - request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB); + source_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB); net_log_.BeginEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, base::Bind(&NetLogJobCreationCallback, - request_net_log.source(), + source_net_log.source(), &key_.hostname)); } @@ -1248,8 +1229,7 @@ if (req->was_canceled()) continue; DCHECK_EQ(this, req->job()); - LogCancelRequest(req->source_net_log(), req->request_net_log(), - req->info()); + LogCancelRequest(req->source_net_log(), req->info()); } } @@ -1278,14 +1258,14 @@ req->set_job(this); priority_tracker_.Add(req->priority()); - req->request_net_log().AddEvent( + req->source_net_log().AddEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, net_log_.source().ToEventParametersCallback()); net_log_.AddEvent( NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_ATTACH, base::Bind(&NetLogJobAttachCallback, - req->request_net_log().source(), + req->source_net_log().source(), priority())); // TODO(szym): Check if this is still needed. @@ -1308,13 +1288,12 @@ // Don't remove it from |requests_| just mark it canceled. req->MarkAsCanceled(); - LogCancelRequest(req->source_net_log(), req->request_net_log(), - req->info()); + LogCancelRequest(req->source_net_log(), req->info()); priority_tracker_.Remove(req->priority()); net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH, base::Bind(&NetLogJobAttachCallback, - req->request_net_log().source(), + req->source_net_log().source(), priority())); if (num_active_requests() > 0) { @@ -1700,8 +1679,7 @@ DCHECK_EQ(this, req->job()); // Update the net log and notify registered observers. - LogFinishRequest(req->source_net_log(), req->request_net_log(), - req->info(), entry.error); + LogFinishRequest(req->source_net_log(), req->info(), entry.error); if (did_complete) { // Record effective total time from creation to completion. RecordTotalTime(had_dns_config_, req->info().is_speculative(), @@ -1871,19 +1849,15 @@ if (!DNSDomainFromDot(info.hostname(), &labeled_hostname)) return ERR_NAME_NOT_RESOLVED; - // Make a log item for the request. - BoundNetLog request_net_log = BoundNetLog::Make(net_log_, - NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST); - - LogStartRequest(source_net_log, request_net_log, info); + LogStartRequest(source_net_log, info); // Build a key that identifies the request in the cache and in the // outstanding jobs map. - Key key = GetEffectiveKeyForRequest(info, request_net_log); + Key key = GetEffectiveKeyForRequest(info, source_net_log); - int rv = ResolveHelper(key, info, addresses, request_net_log); + int rv = ResolveHelper(key, info, addresses, source_net_log); if (rv != ERR_DNS_CACHE_MISS) { - LogFinishRequest(source_net_log, request_net_log, info, rv); + LogFinishRequest(source_net_log, info, rv); RecordTotalTime(HaveDnsConfig(), info.is_speculative(), base::TimeDelta()); return rv; } @@ -1895,7 +1869,7 @@ Job* job; if (jobit == jobs_.end()) { job = - new Job(weak_ptr_factory_.GetWeakPtr(), key, priority, request_net_log); + new Job(weak_ptr_factory_.GetWeakPtr(), key, priority, source_net_log); job->Schedule(false); // Check for queue overflow. @@ -1905,7 +1879,7 @@ evicted->OnEvicted(); // Deletes |evicted|. if (evicted == job) { rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; - LogFinishRequest(source_net_log, request_net_log, info, rv); + LogFinishRequest(source_net_log, info, rv); return rv; } } @@ -1916,7 +1890,7 @@ // Can't complete synchronously. Create and attach request. scoped_ptr<Request> req(new Request( - source_net_log, request_net_log, info, priority, callback, addresses)); + source_net_log, info, priority, callback, addresses)); if (out_req) *out_req = reinterpret_cast<RequestHandle>(req.get()); @@ -1928,7 +1902,7 @@ int HostResolverImpl::ResolveHelper(const Key& key, const RequestInfo& info, AddressList* addresses, - const BoundNetLog& request_net_log) { + const BoundNetLog& source_net_log) { // The result of |getaddrinfo| for empty hosts is inconsistent across systems. // On Windows it gives the default interface's address, whereas on Linux it // gives an error. We will make it fail on all platforms for consistency. @@ -1939,13 +1913,13 @@ if (ResolveAsIP(key, info, &net_error, addresses)) return net_error; if (ServeFromCache(key, info, &net_error, addresses)) { - request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CACHE_HIT); + source_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CACHE_HIT); return net_error; } // TODO(szym): Do not do this if nsswitch.conf instructs not to. // http://crbug.com/117655 if (ServeFromHosts(key, info, addresses)) { - request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_HOSTS_HIT); + source_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_HOSTS_HIT); return OK; } return ERR_DNS_CACHE_MISS; @@ -1957,17 +1931,13 @@ DCHECK(CalledOnValidThread()); DCHECK(addresses); - // Make a log item for the request. - BoundNetLog request_net_log = BoundNetLog::Make(net_log_, - NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST); - // Update the net log and notify registered observers. - LogStartRequest(source_net_log, request_net_log, info); + LogStartRequest(source_net_log, info); - Key key = GetEffectiveKeyForRequest(info, request_net_log); + Key key = GetEffectiveKeyForRequest(info, source_net_log); - int rv = ResolveHelper(key, info, addresses, request_net_log); - LogFinishRequest(source_net_log, request_net_log, info, rv); + int rv = ResolveHelper(key, info, addresses, source_net_log); + LogFinishRequest(source_net_log, info, rv); return rv; }
diff --git a/net/http/disk_cache_based_quic_server_info.cc b/net/http/disk_cache_based_quic_server_info.cc index 2e116da..148df3f 100644 --- a/net/http/disk_cache_based_quic_server_info.cc +++ b/net/http/disk_cache_based_quic_server_info.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/callback_helpers.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "net/base/completion_callback.h" @@ -17,44 +18,6 @@ namespace net { -// Histogram that tracks number of times data read/parse/write API calls of -// QuicServerInfo to and from disk cache is called. -enum QuicServerInfoAPICall { - QUIC_SERVER_INFO_START = 0, - QUIC_SERVER_INFO_WAIT_FOR_DATA_READY = 1, - QUIC_SERVER_INFO_PARSE = 2, - QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL = 3, - QUIC_SERVER_INFO_READY_TO_PERSIST = 4, - QUIC_SERVER_INFO_PERSIST = 5, - QUIC_SERVER_INFO_NUM_OF_API_CALLS = 6, -}; - -// Histogram that tracks failure reasons to read/load/write of QuicServerInfo to -// and from disk cache. -enum FailureReason { - WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE = 0, - GET_BACKEND_FAILURE = 1, - OPEN_FAILURE = 2, - CREATE_OR_OPEN_FAILURE = 3, - PARSE_NO_DATA_FAILURE = 4, - PARSE_FAILURE = 5, - READ_FAILURE = 6, - READY_TO_PERSIST_FAILURE = 7, - PERSIST_NO_BACKEND_FAILURE = 8, - WRITE_FAILURE = 9, - NUM_OF_FAILURES = 10, -}; - -void RecordQuicServerInfoStatus(QuicServerInfoAPICall call) { - UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall", call, - QUIC_SERVER_INFO_NUM_OF_API_CALLS); -} - -void RecordQuicServerInfoFailure(FailureReason failure) { - UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason", failure, - NUM_OF_FAILURES); -} - // Some APIs inside disk_cache take a handle that the caller must keep alive // until the API has finished its asynchronous execution. // @@ -94,6 +57,7 @@ http_cache_(http_cache), backend_(NULL), entry_(NULL), + last_failure_(NO_FAILURE), weak_factory_(this) { io_callback_ = base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, @@ -104,6 +68,7 @@ void DiskCacheBasedQuicServerInfo::Start() { DCHECK(CalledOnValidThread()); DCHECK_EQ(GET_BACKEND, state_); + DCHECK_EQ(last_failure_, NO_FAILURE); RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START); load_start_time_ = base::TimeTicks::Now(); DoLoop(OK); @@ -115,17 +80,19 @@ DCHECK_NE(GET_BACKEND, state_); RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY); - if (ready_) + if (ready_) { + RecordLastFailure(); return OK; + } if (!callback.is_null()) { // Prevent a new callback for WaitForDataReady overwriting an existing - // pending callback (|user_callback_|). - if (!user_callback_.is_null()) { + // pending callback (|wait_for_ready_callback_|). + if (!wait_for_ready_callback_.is_null()) { RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE); return ERR_INVALID_ARGUMENT; } - user_callback_ = callback; + wait_for_ready_callback_ = callback; } return ERR_IO_PENDING; @@ -135,8 +102,10 @@ DCHECK(CalledOnValidThread()); RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL); - if (!user_callback_.is_null()) - user_callback_.Reset(); + if (!wait_for_ready_callback_.is_null()) { + RecordLastFailure(); + wait_for_ready_callback_.Reset(); + } } bool DiskCacheBasedQuicServerInfo::IsDataReady() { @@ -144,10 +113,6 @@ } bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() { - // TODO(rtenneti): Handle updates while a write is pending. Change - // Persist() to save the data to be written into a temporary buffer - // and then persist that data when we are ready to persist. - // // The data can be persisted if it has been loaded from the disk cache // and there are no pending writes. RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST); @@ -159,12 +124,29 @@ void DiskCacheBasedQuicServerInfo::Persist() { DCHECK(CalledOnValidThread()); - DCHECK_NE(GET_BACKEND, state_); + if (!IsReadyToPersist()) { + // Handle updates while a write is pending or if we haven't loaded from disk + // cache. Save the data to be written into a temporary buffer and then + // persist that data when we are ready to persist. + pending_write_data_ = Serialize(); + return; + } + PersistInternal(); +} +void DiskCacheBasedQuicServerInfo::PersistInternal() { + DCHECK(CalledOnValidThread()); + DCHECK_NE(GET_BACKEND, state_); DCHECK(new_data_.empty()); CHECK(ready_); - DCHECK(user_callback_.is_null()); - new_data_ = Serialize(); + DCHECK(wait_for_ready_callback_.is_null()); + + if (pending_write_data_.empty()) { + new_data_ = Serialize(); + } else { + new_data_ = pending_write_data_; + pending_write_data_.clear(); + } RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST); if (!backend_) { @@ -176,8 +158,21 @@ DoLoop(OK); } +void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() { + DCHECK(CalledOnValidThread()); + DCHECK_NE(GET_BACKEND, state_); + + RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT); + if (!backend_) { + RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); + return; + } + + backend_->OnExternalCacheHit(key()); +} + DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { - DCHECK(user_callback_.is_null()); + DCHECK(wait_for_ready_callback_.is_null()); if (entry_) entry_->Close(); } @@ -190,10 +185,15 @@ int rv) { DCHECK_NE(NONE, state_); rv = DoLoop(rv); - if (rv != ERR_IO_PENDING && !user_callback_.is_null()) { - CompletionCallback callback = user_callback_; - user_callback_.Reset(); - callback.Run(rv); + if (rv == ERR_IO_PENDING) + return; + if (!wait_for_ready_callback_.is_null()) { + RecordLastFailure(); + base::ResetAndReturn(&wait_for_ready_callback_).Run(rv); + } + if (ready_ && !pending_write_data_.empty()) { + DCHECK_EQ(NONE, state_); + PersistInternal(); } } @@ -381,4 +381,43 @@ return OK; } +void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus( + QuicServerInfoAPICall call) { + if (!backend_) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call, + QUIC_SERVER_INFO_NUM_OF_API_CALLS); + } else if (backend_->GetCacheType() == net::MEMORY_CACHE) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call, + QUIC_SERVER_INFO_NUM_OF_API_CALLS); + } else { + UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call, + QUIC_SERVER_INFO_NUM_OF_API_CALLS); + } +} + +void DiskCacheBasedQuicServerInfo::RecordLastFailure() { + if (last_failure_ != NO_FAILURE) { + UMA_HISTOGRAM_ENUMERATION( + "Net.QuicDiskCache.FailureReason.WaitForDataReady", + last_failure_, NUM_OF_FAILURES); + } + last_failure_ = NO_FAILURE; +} + +void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure( + FailureReason failure) { + last_failure_ = failure; + + if (!backend_) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend", + failure, NUM_OF_FAILURES); + } else if (backend_->GetCacheType() == net::MEMORY_CACHE) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache", + failure, NUM_OF_FAILURES); + } else { + UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache", + failure, NUM_OF_FAILURES); + } +} + } // namespace net
diff --git a/net/http/disk_cache_based_quic_server_info.h b/net/http/disk_cache_based_quic_server_info.h index 97fe95e..e95d535 100644 --- a/net/http/disk_cache_based_quic_server_info.h +++ b/net/http/disk_cache_based_quic_server_info.h
@@ -38,9 +38,11 @@ bool IsDataReady() override; bool IsReadyToPersist() override; void Persist() override; + void OnExternalCacheHit() override; private: struct CacheOperationDataShim; + enum State { GET_BACKEND, GET_BACKEND_COMPLETE, @@ -57,8 +59,42 @@ NONE, }; + // Enum to track number of times data read/parse/write API calls of + // QuicServerInfo to and from disk cache is called. + enum QuicServerInfoAPICall { + QUIC_SERVER_INFO_START = 0, + QUIC_SERVER_INFO_WAIT_FOR_DATA_READY = 1, + QUIC_SERVER_INFO_PARSE = 2, + QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL = 3, + QUIC_SERVER_INFO_READY_TO_PERSIST = 4, + QUIC_SERVER_INFO_PERSIST = 5, + QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT = 6, + QUIC_SERVER_INFO_NUM_OF_API_CALLS = 7, + }; + + // Enum to track failure reasons to read/load/write of QuicServerInfo to + // and from disk cache. + enum FailureReason { + WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE = 0, + GET_BACKEND_FAILURE = 1, + OPEN_FAILURE = 2, + CREATE_OR_OPEN_FAILURE = 3, + PARSE_NO_DATA_FAILURE = 4, + PARSE_FAILURE = 5, + READ_FAILURE = 6, + READY_TO_PERSIST_FAILURE = 7, + PERSIST_NO_BACKEND_FAILURE = 8, + WRITE_FAILURE = 9, + NO_FAILURE = 10, + NUM_OF_FAILURES = 11, + }; + ~DiskCacheBasedQuicServerInfo() override; + // Persists |pending_write_data_| if it is not empty, otherwise serializes the + // data and pesists it. + void PersistInternal(); + std::string key() const; // The |unused| parameter is a small hack so that we can have the @@ -86,23 +122,39 @@ // DoSetDone is the terminal state of the write operation. int DoSetDone(); + // Tracks in a histogram the number of times data read/parse/write API calls + // of QuicServerInfo to and from disk cache is called. + void RecordQuicServerInfoStatus(QuicServerInfoAPICall call); + + // Tracks in a histogram the failure reasons to read/load/write of + // QuicServerInfo to and from disk cache. It also saves the |failure| in + // |last_failure_|. + void RecordQuicServerInfoFailure(FailureReason failure); + + // Tracks in a histogram if |last_failure_| is not NO_FAILURE. + void RecordLastFailure(); + CacheOperationDataShim* data_shim_; // Owned by |io_callback_|. CompletionCallback io_callback_; State state_; bool ready_; bool found_entry_; // Controls the behavior of DoCreateOrOpen. std::string new_data_; + std::string pending_write_data_; const QuicServerId server_id_; HttpCache* const http_cache_; disk_cache::Backend* backend_; disk_cache::Entry* entry_; - CompletionCallback user_callback_; + CompletionCallback wait_for_ready_callback_; scoped_refptr<IOBuffer> read_buffer_; scoped_refptr<IOBuffer> write_buffer_; std::string data_; base::TimeTicks load_start_time_; + FailureReason last_failure_; base::WeakPtrFactory<DiskCacheBasedQuicServerInfo> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DiskCacheBasedQuicServerInfo); }; } // namespace net
diff --git a/net/http/disk_cache_based_quic_server_info_unittest.cc b/net/http/disk_cache_based_quic_server_info_unittest.cc index 845fdc9..847b3d8 100644 --- a/net/http/disk_cache_based_quic_server_info_unittest.cc +++ b/net/http/disk_cache_based_quic_server_info_unittest.cc
@@ -395,4 +395,191 @@ RemoveMockTransaction(&kHostInfoTransaction1); } +// Test Start() followed by Persist() without calling WaitForDataReady. +TEST(DiskCacheBasedQuicServerInfo, StartAndPersist) { + MockHttpCache cache; + AddMockTransaction(&kHostInfoTransaction1); + + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + EXPECT_FALSE(quic_server_info->IsDataReady()); + quic_server_info->Start(); + // Wait until Start() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + EXPECT_TRUE(quic_server_info->IsDataReady()); + + QuicServerInfo::State* state = quic_server_info->mutable_state(); + EXPECT_TRUE(state->certs.empty()); + const string server_config_a = "server_config_a"; + const string source_address_token_a = "source_address_token_a"; + const string server_config_sig_a = "server_config_sig_a"; + const string cert_a = "cert_a"; + + state->server_config = server_config_a; + state->source_address_token = source_address_token_a; + state->server_config_sig = server_config_sig_a; + state->certs.push_back(cert_a); + EXPECT_TRUE(quic_server_info->IsReadyToPersist()); + quic_server_info->Persist(); + quic_server_info->OnExternalCacheHit(); + + // Once we call Persist, IsReadyToPersist should return false until Persist + // has completed. + EXPECT_FALSE(quic_server_info->IsReadyToPersist()); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + EXPECT_TRUE(quic_server_info->IsReadyToPersist()); + + // Verify that the state was updated. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + TestCompletionCallback callback; + int rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + const QuicServerInfo::State& state1 = quic_server_info->state(); + EXPECT_EQ(server_config_a, state1.server_config); + EXPECT_EQ(source_address_token_a, state1.source_address_token); + EXPECT_EQ(server_config_sig_a, state1.server_config_sig); + EXPECT_EQ(1U, state1.certs.size()); + EXPECT_EQ(cert_a, state1.certs[0]); + + RemoveMockTransaction(&kHostInfoTransaction1); +} + +// Test Persisting data when we are not ready to persist and then verify it +// persists the data when Start() finishes. +TEST(DiskCacheBasedQuicServerInfo, PersistWhenNotReadyToPersist) { + MockBlockingBackendFactory* factory = new MockBlockingBackendFactory(); + MockHttpCache cache(factory); + AddMockTransaction(&kHostInfoTransaction1); + TestCompletionCallback callback; + + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + EXPECT_FALSE(quic_server_info->IsDataReady()); + // We do a Start(), but don't call WaitForDataReady(). Because we haven't + // created the backend, we will wait and data wouldn't be ready. + quic_server_info->Start(); + EXPECT_FALSE(quic_server_info->IsDataReady()); + + // Persist data once, even though the backend is not ready. + QuicServerInfo::State* state = quic_server_info->mutable_state(); + EXPECT_TRUE(state->certs.empty()); + const string server_config_init = "server_config_init"; + const string source_address_token_init = "source_address_token_init"; + const string server_config_sig_init = "server_config_sig_init"; + const string cert_init = "cert_init"; + + state->server_config = server_config_init; + state->source_address_token = source_address_token_init; + state->server_config_sig = server_config_sig_init; + state->certs.push_back(cert_init); + EXPECT_FALSE(quic_server_info->IsReadyToPersist()); + quic_server_info->Persist(); + EXPECT_FALSE(quic_server_info->IsReadyToPersist()); + + // Now complete the backend creation and let the callback run. + factory->FinishCreation(); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + // Verify that the state was updated. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + int rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + const QuicServerInfo::State& state1 = quic_server_info->state(); + EXPECT_EQ(server_config_init, state1.server_config); + EXPECT_EQ(source_address_token_init, state1.source_address_token); + EXPECT_EQ(server_config_sig_init, state1.server_config_sig); + EXPECT_EQ(1U, state1.certs.size()); + EXPECT_EQ(cert_init, state1.certs[0]); + RemoveMockTransaction(&kHostInfoTransaction1); +} + +// Test multiple calls to Persist without waiting for the data to be written. +TEST(DiskCacheBasedQuicServerInfo, MultiplePersistsWithoutWaiting) { + MockHttpCache cache; + AddMockTransaction(&kHostInfoTransaction1); + TestCompletionCallback callback; + + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + EXPECT_FALSE(quic_server_info->IsDataReady()); + quic_server_info->Start(); + int rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + // Persist data once. + QuicServerInfo::State* state = quic_server_info->mutable_state(); + EXPECT_TRUE(state->certs.empty()); + const string server_config_init = "server_config_init"; + const string source_address_token_init = "source_address_token_init"; + const string server_config_sig_init = "server_config_sig_init"; + const string cert_init = "cert_init"; + + state->server_config = server_config_init; + state->source_address_token = source_address_token_init; + state->server_config_sig = server_config_sig_init; + state->certs.push_back(cert_init); + EXPECT_TRUE(quic_server_info->IsReadyToPersist()); + quic_server_info->Persist(); + + // Once we call Persist, IsReadyToPersist should return false until Persist + // has completed. + EXPECT_FALSE(quic_server_info->IsReadyToPersist()); + + // Persist one more time using the same |quic_server_info| object and without + // doing another Start() and WaitForDataReady. + const string server_config_a = "server_config_a"; + const string source_address_token_a = "source_address_token_a"; + const string server_config_sig_a = "server_config_sig_a"; + const string cert_a = "cert_a"; + + state->server_config = server_config_a; + state->source_address_token = source_address_token_a; + state->server_config_sig = server_config_sig_a; + state->certs.push_back(cert_a); + EXPECT_FALSE(quic_server_info->IsReadyToPersist()); + quic_server_info->Persist(); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + EXPECT_TRUE(quic_server_info->IsReadyToPersist()); + + // Verify that the state was updated. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + // Verify the second time persisted data is persisted. + const QuicServerInfo::State& state1 = quic_server_info->state(); + EXPECT_EQ(server_config_a, state1.server_config); + EXPECT_EQ(source_address_token_a, state1.source_address_token); + EXPECT_EQ(server_config_sig_a, state1.server_config_sig); + EXPECT_EQ(1U, state1.certs.size()); + EXPECT_EQ(cert_a, state1.certs[0]); + + RemoveMockTransaction(&kHostInfoTransaction1); +} + } // namespace net
diff --git a/net/http/http_auth_handler.cc b/net/http/http_auth_handler.cc index 7369ea5..4de2226 100644 --- a/net/http/http_auth_handler.cc +++ b/net/http/http_auth_handler.cc
@@ -64,7 +64,7 @@ int HttpAuthHandler::GenerateAuthToken( const AuthCredentials* credentials, const HttpRequestInfo* request, const CompletionCallback& callback, std::string* auth_token) { - // TODO(cbentzel): Enforce non-NULL callback after cleaning up SocketStream. + DCHECK(!callback.is_null()); DCHECK(request); DCHECK(credentials != NULL || AllowsDefaultCredentials()); DCHECK(auth_token != NULL); @@ -96,8 +96,8 @@ void HttpAuthHandler::OnGenerateAuthTokenComplete(int rv) { CompletionCallback callback = callback_; FinishGenerateAuthToken(); - if (!callback.is_null()) - callback.Run(rv); + DCHECK(!callback.is_null()); + callback.Run(rv); } void HttpAuthHandler::FinishGenerateAuthToken() {
diff --git a/net/http/http_auth_handler_basic_unittest.cc b/net/http/http_auth_handler_basic_unittest.cc index a369df3..60e5882 100644 --- a/net/http/http_auth_handler_basic_unittest.cc +++ b/net/http/http_auth_handler_basic_unittest.cc
@@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" #include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler_basic.h" #include "net/http/http_request_info.h" @@ -41,8 +42,9 @@ base::ASCIIToUTF16(tests[i].password)); HttpRequestInfo request_info; std::string auth_token; + TestCompletionCallback callback; int rv = basic->GenerateAuthToken(&credentials, &request_info, - CompletionCallback(), &auth_token); + callback.callback(), &auth_token); EXPECT_EQ(OK, rv); EXPECT_STREQ(tests[i].expected_credentials, auth_token.c_str()); }
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc index 3ec607e..28b3b22 100644 --- a/net/http/http_network_session.cc +++ b/net/http/http_network_session.cc
@@ -89,7 +89,6 @@ force_spdy_always(false), use_alternate_protocols(false), alternate_protocol_probability_threshold(1), - enable_websocket_over_spdy(false), enable_quic(false), enable_quic_port_selection(true), quic_always_require_handshake_confirmation(false),
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index a3f70aa..557b84e 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h
@@ -109,7 +109,6 @@ // trying SSL and then falling back to http. bool use_alternate_protocols; double alternate_protocol_probability_threshold; - bool enable_websocket_over_spdy; bool enable_quic; bool enable_quic_port_selection;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index eea7294..20ffdd1 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc
@@ -90,19 +90,6 @@ *session); } -// Returns true if |error| is a client certificate authentication error. -bool IsClientCertificateError(int error) { - switch (error) { - case ERR_BAD_SSL_CLIENT_AUTH_CERT: - case ERR_SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED: - case ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY: - case ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED: - return true; - default: - return false; - } -} - base::Value* NetLogSSLVersionFallbackCallback( const GURL* url, int net_error,
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 938f1b0..d08d162 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc
@@ -408,7 +408,7 @@ NextProto, HttpNetworkTransactionTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); namespace {
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc index 19df6a0..694d394 100644 --- a/net/http/http_proxy_client_socket_pool_unittest.cc +++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -351,9 +351,12 @@ HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY31), HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY31), HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY31), - HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4), - HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4), - HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4))); + HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4_14), + HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4_14), + HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4_14), + HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4_15), + HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4_15), + HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4_15))); TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) { Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index 2ec0fb5..6967d79 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc
@@ -369,8 +369,10 @@ case kProtoSPDY3: case kProtoSPDY31: return CONNECTION_INFO_SPDY3; - case kProtoSPDY4: - return CONNECTION_INFO_SPDY4; + case kProtoSPDY4_14: + return CONNECTION_INFO_HTTP2_14; + case kProtoSPDY4_15: + return CONNECTION_INFO_HTTP2_15; case kProtoQUIC1SPDY3: return CONNECTION_INFO_QUIC1_SPDY3; @@ -395,9 +397,12 @@ return "spdy/2"; case CONNECTION_INFO_SPDY3: return "spdy/3"; - case CONNECTION_INFO_SPDY4: - // This is the HTTP/2 draft-15 identifier. For internal - // consistency, HTTP/2 is named SPDY4 within Chromium. + case CONNECTION_INFO_HTTP2_14: + // For internal consistency, HTTP/2 is named SPDY4 within Chromium. + // This is the HTTP/2 draft-14 identifier. + return "h2-14"; + case CONNECTION_INFO_HTTP2_15: + // This is the HTTP/2 draft-15 identifier. return "h2-15"; case CONNECTION_INFO_QUIC1_SPDY3: return "quic/1+spdy/3";
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h index efd76fe..df3e300 100644 --- a/net/http/http_response_info.h +++ b/net/http/http_response_info.h
@@ -37,8 +37,10 @@ CONNECTION_INFO_HTTP1 = 1, CONNECTION_INFO_DEPRECATED_SPDY2 = 2, CONNECTION_INFO_SPDY3 = 3, - CONNECTION_INFO_SPDY4 = 4, + // CONNECTION_INFO_HTTP2 = 4, // TODO(bnc): This will be HTTP/2. CONNECTION_INFO_QUIC1_SPDY3 = 5, + CONNECTION_INFO_HTTP2_14 = 6, // HTTP/2 draft-14. + CONNECTION_INFO_HTTP2_15 = 7, // HTTP/2 draft-15. NUM_OF_CONNECTION_INFOS, };
diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc index cc41ea8..25725a8 100644 --- a/net/http/http_server_properties.cc +++ b/net/http/http_server_properties.cc
@@ -20,6 +20,7 @@ "npn-spdy/2", "npn-spdy/3", "npn-spdy/3.1", + "npn-h2-14", // HTTP/2 draft-14. Called SPDY4 internally. "npn-h2-15", // HTTP/2 draft-15. Called SPDY4 internally. "quic" }; @@ -51,7 +52,8 @@ case DEPRECATED_NPN_SPDY_2: case NPN_SPDY_3: case NPN_SPDY_3_1: - case NPN_SPDY_4: + case NPN_SPDY_4_14: + case NPN_SPDY_4_15: case QUIC: DCHECK(IsAlternateProtocolValid(protocol)); return kAlternateProtocolStrings[ @@ -81,8 +83,10 @@ return NPN_SPDY_3; case kProtoSPDY31: return NPN_SPDY_3_1; - case kProtoSPDY4: - return NPN_SPDY_4; + case kProtoSPDY4_14: + return NPN_SPDY_4_14; + case kProtoSPDY4_15: + return NPN_SPDY_4_15; case kProtoQUIC1SPDY3: return QUIC;
diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h index 337f22d..a63dc6a 100644 --- a/net/http/http_server_properties.h +++ b/net/http/http_server_properties.h
@@ -57,8 +57,9 @@ NPN_SPDY_MINIMUM_VERSION = DEPRECATED_NPN_SPDY_2, NPN_SPDY_3, NPN_SPDY_3_1, - NPN_SPDY_4, // SPDY4 is HTTP/2. - NPN_SPDY_MAXIMUM_VERSION = NPN_SPDY_4, + NPN_SPDY_4_14, // HTTP/2 draft-14 + NPN_SPDY_4_15, // HTTP/2 draft-15 + NPN_SPDY_MAXIMUM_VERSION = NPN_SPDY_4_15, QUIC, ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION = QUIC, UNINITIALIZED_ALTERNATE_PROTOCOL,
diff --git a/net/http/http_stream_factory_impl_request_unittest.cc b/net/http/http_stream_factory_impl_request_unittest.cc index 4695067..587358d 100644 --- a/net/http/http_stream_factory_impl_request_unittest.cc +++ b/net/http/http_stream_factory_impl_request_unittest.cc
@@ -20,7 +20,7 @@ NextProto, HttpStreamFactoryImplRequestTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); namespace {
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc index 4bf1659..68e9114 100644 --- a/net/http/http_stream_factory_impl_unittest.cc +++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -425,7 +425,7 @@ NextProto, HttpStreamFactoryTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); TEST_P(HttpStreamFactoryTest, PreconnectDirect) { for (size_t i = 0; i < arraysize(kTests); ++i) {
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc index 907e6d8..baf38bf 100644 --- a/net/http/transport_security_state.cc +++ b/net/http/transport_security_state.cc
@@ -415,7 +415,8 @@ // hostname_offset contains the number of bytes from the start of the given // hostname where the name of the matching entry starts. size_t hostname_offset; - bool include_subdomains; + bool sts_include_subdomains; + bool pkp_include_subdomains; bool force_https; bool has_pins; }; @@ -509,15 +510,19 @@ if (c == kEndOfString) { PreloadResult tmp; - if (!reader.Next(&tmp.include_subdomains) || + if (!reader.Next(&tmp.sts_include_subdomains) || !reader.Next(&tmp.force_https) || !reader.Next(&tmp.has_pins)) { return false; } + tmp.pkp_include_subdomains = tmp.sts_include_subdomains; + if (tmp.has_pins) { if (!reader.Read(4, &tmp.pinset_id) || - !reader.Read(9, &tmp.domain_id)) { + !reader.Read(9, &tmp.domain_id) || + (!tmp.sts_include_subdomains && + !reader.Next(&tmp.pkp_include_subdomains))) { return false; } } @@ -525,13 +530,16 @@ tmp.hostname_offset = hostname_offset; if (hostname_offset == 0 || hostname[hostname_offset - 1] == '.') { - *out_found = tmp.include_subdomains; + *out_found = + tmp.sts_include_subdomains || tmp.pkp_include_subdomains; *out = tmp; - } - if (hostname_offset == 0) { - *out_found = true; - return true; + if (hostname_offset > 0) { + out->force_https &= tmp.sts_include_subdomains; + } else { + *out_found = true; + return true; + } } continue; @@ -768,7 +776,7 @@ return false; out->domain = host.substr(result.hostname_offset); - out->sts.include_subdomains = result.include_subdomains; + out->sts.include_subdomains = result.sts_include_subdomains; out->sts.last_observed = base::GetBuildTime(); out->sts.upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; @@ -778,7 +786,7 @@ } if (enable_static_pins_ && result.has_pins) { - out->pkp.include_subdomains = result.include_subdomains; + out->pkp.include_subdomains = result.pkp_include_subdomains; out->pkp.last_observed = base::GetBuildTime(); if (result.pinset_id >= arraysize(kPinsets))
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h index e757950..867cafd 100644 --- a/net/http/transport_security_state_static.h +++ b/net/http/transport_security_state_static.h
@@ -698,10 +698,10 @@ 0xf6, 0x07, 0xeb, 0x08, 0xed, 0x09, 0xae, 0xe3, 0x0a, 0x0b, 0x03, 0x0c, 0x02, 0x0d, 0xe2, 0xe8, 0x0f, 0xf3, 0xee, 0xf4, 0x10, 0x11, 0xff, 0x12, - 0xb8, 0xb9, 0xb6, 0x14, 0xb7, 0x15, 0xb5, 0x16, - 0xb0, 0x17, 0xb2, 0x18, 0x19, 0xea, 0xf8, 0x1a, - 0x1b, 0xe6, 0xf5, 0x1c, 0xad, 0xf9, 0xe4, 0x1e, - 0x1d, 0x1f, 0xe5, 0x20, 0xe9, 0xf2, 0xef, 0x22, + 0xad, 0xe6, 0x14, 0xf5, 0xb8, 0xb9, 0xb6, 0x16, + 0xb7, 0x17, 0xb5, 0x18, 0xb0, 0x19, 0xb2, 0x1a, + 0x1b, 0xea, 0xf8, 0x1c, 0x1d, 0xf9, 0xe4, 0x1e, + 0x15, 0x1f, 0xe5, 0x20, 0xe9, 0xf2, 0xef, 0x22, 0x21, 0x23, 0x13, 0x24, 0x0e, 0x25, }; @@ -711,11 +711,11 @@ 0x4a, 0x54, 0x80, 0x26, 0x94, 0x02, 0xd3, 0xfa, 0xf8, 0x20, 0x67, 0xaa, 0x53, 0x8d, 0x34, 0xfc, 0xfb, 0x79, 0xb6, 0x34, 0x74, 0x29, 0xf9, 0x72, - 0x14, 0x9d, 0x89, 0x86, 0xea, 0x43, 0xb6, 0x7f, + 0x14, 0x9d, 0x89, 0x86, 0xee, 0x43, 0xb6, 0x7f, 0x3b, 0x05, 0x3b, 0x90, 0xd1, 0x51, 0x4f, 0xff, - 0x5b, 0xd6, 0x76, 0xbc, 0xca, 0x36, 0x5c, 0xd8, + 0x5b, 0xd6, 0x76, 0x9c, 0xca, 0x36, 0x5c, 0xd8, 0xe9, 0xf0, 0xa7, 0x72, 0x1a, 0x2b, 0xd9, 0xf7, - 0x9b, 0xd3, 0x4a, 0x0e, 0x93, 0xb0, 0xf7, 0x04, + 0x9b, 0xd3, 0x5a, 0x0e, 0x93, 0xb0, 0xf7, 0x04, 0xc2, 0x7f, 0xe7, 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x91, 0xa7, 0xff, 0x39, 0x9c, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x27, 0x89, 0xff, 0xce, 0x67, @@ -725,18 +725,18 @@ 0xc8, 0x68, 0xa2, 0x27, 0xfd, 0xcf, 0x76, 0x0a, 0x77, 0x21, 0xa2, 0x91, 0x9f, 0xff, 0xb3, 0x6b, 0x2e, 0xf7, 0x5f, 0xfc, 0x0a, 0xd5, 0x40, 0x54, - 0x9c, 0xc4, 0x51, 0xea, 0x23, 0x4f, 0xfc, 0xee, + 0x9c, 0xc4, 0x51, 0xee, 0x23, 0x4f, 0xfc, 0xee, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, 0x12, 0x84, 0x50, - 0xba, 0x70, 0xf8, 0xd2, 0x10, 0xc7, 0x56, 0x76, + 0xba, 0x70, 0xf8, 0xd2, 0x10, 0xc7, 0x76, 0x76, 0x30, 0xaf, 0x61, 0x35, 0x93, 0x00, 0xa4, 0xdd, - 0xa9, 0x55, 0x4b, 0x48, 0x7a, 0xb7, 0x96, 0x4f, + 0xa9, 0x55, 0x4b, 0x58, 0x7a, 0xb7, 0x96, 0x4f, 0xfe, 0x73, 0x39, 0xee, 0xc1, 0x4e, 0xe4, 0x34, 0x4e, 0x73, 0xff, 0x9c, 0xce, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, 0x14, 0x4c, 0xff, 0xc2, 0xf7, 0x5b, 0xd5, 0x69, 0xfe, 0x01, 0xd0, 0x28, 0xea, 0xaa, - 0x96, 0x8a, 0x53, 0xf9, 0xd8, 0x29, 0xdc, 0x86, + 0x96, 0xaa, 0x53, 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x88, 0x72, 0x7c, 0x29, 0xdc, 0x86, 0x88, 0xbe, - 0x7d, 0xd7, 0x7a, 0xeb, 0x2a, 0x4e, 0xc3, 0xd8, + 0x7d, 0xd7, 0x7a, 0xe9, 0x2a, 0x4e, 0xc3, 0xd8, 0xf9, 0x84, 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0xc9, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0xb7, 0xa7, 0xff, 0x39, 0x9c, 0xf7, @@ -745,1449 +745,1501 @@ 0xfc, 0xec, 0x14, 0xee, 0x43, 0x44, 0x3b, 0x3f, 0xf9, 0xcc, 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x2c, 0x4f, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x23, - 0x09, 0xff, 0x9d, 0xcf, 0x76, 0x0a, 0x77, 0x21, - 0xa2, 0x3d, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, + 0x09, 0xf0, 0xa7, 0x72, 0x1a, 0x23, 0xd9, 0xea, + 0xf5, 0x2d, 0x8e, 0x9e, 0x77, 0x3d, 0xd8, 0x7a, + 0x97, 0x30, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x58, 0x73, 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x8b, - 0x9e, 0x15, 0x38, 0xaa, 0x0e, 0xde, 0xa5, 0xe3, - 0xb6, 0x26, 0x5c, 0xee, 0x7f, 0x3b, 0x05, 0x3b, - 0x90, 0xd1, 0x0f, 0x4f, 0x85, 0x3b, 0x90, 0xd1, - 0x13, 0x4d, 0x76, 0x1d, 0x3f, 0xfb, 0x2d, 0xbe, - 0x8a, 0xaf, 0xa9, 0xba, 0xb0, 0xe8, 0xa4, 0xf8, - 0xec, 0x2d, 0x3d, 0x7d, 0x3b, 0x79, 0xa2, 0x17, - 0x93, 0xb1, 0x1c, 0xf5, 0x84, 0x9e, 0x84, 0x73, - 0xd7, 0xb7, 0xae, 0x74, 0xff, 0xef, 0x34, 0xfc, - 0xa5, 0xfb, 0x99, 0xdd, 0xf3, 0xa6, 0xe6, 0xf3, - 0xa5, 0xaa, 0x62, 0x21, 0xec, 0x45, 0xc9, 0xb3, - 0xeb, 0x75, 0xf9, 0xe7, 0x4f, 0x7a, 0xbe, 0x01, - 0xd3, 0x82, 0x10, 0x95, 0x3e, 0xef, 0xde, 0xd8, - 0x53, 0x8b, 0xc9, 0xf7, 0x5d, 0xac, 0xb9, 0xd1, - 0xb2, 0x26, 0x80, 0x7d, 0xf3, 0x59, 0xff, 0x81, - 0xe6, 0xb7, 0x59, 0x7c, 0x3a, 0x6a, 0xce, 0x9f, - 0xf7, 0xb5, 0xaf, 0xd6, 0xab, 0xe6, 0x1d, 0x3f, - 0x63, 0x6d, 0x1f, 0x95, 0x3a, 0x7b, 0x36, 0xca, - 0x0e, 0x8c, 0x4f, 0x37, 0xd0, 0xc0, 0x68, 0xc6, - 0xc9, 0x9f, 0x3f, 0xb9, 0x7c, 0xfd, 0x81, 0x5c, - 0xdb, 0xc7, 0x4f, 0xff, 0xfe, 0xff, 0xe9, 0x5d, - 0x7f, 0xcd, 0xad, 0xe6, 0xb7, 0x57, 0x29, 0x78, - 0xe0, 0x4e, 0x9f, 0x7a, 0xfa, 0x2d, 0x07, 0x4f, - 0xea, 0x5f, 0x5d, 0x7a, 0x2d, 0x27, 0x4f, 0xee, - 0x0e, 0x8d, 0x2d, 0xf8, 0xe8, 0xf1, 0xf5, 0xfc, - 0xe2, 0x15, 0x33, 0x5c, 0x7f, 0x18, 0x4a, 0x4d, - 0xa3, 0xce, 0x9d, 0xd7, 0xa9, 0xd3, 0x69, 0x41, - 0xd3, 0xcb, 0xfb, 0xb4, 0xf3, 0x66, 0x23, 0x73, - 0xff, 0xed, 0x16, 0xfb, 0xbf, 0xa3, 0x7e, 0xec, - 0xbe, 0xa6, 0x5c, 0xe9, 0xff, 0xac, 0xba, 0xf7, - 0x5f, 0xfd, 0x75, 0xb9, 0xd3, 0xfb, 0x75, 0x2f, - 0xf5, 0xb4, 0x61, 0xd0, 0x03, 0xff, 0xfa, 0x3c, - 0xff, 0x85, 0xad, 0xd5, 0x5f, 0x51, 0xd7, 0x3a, - 0x14, 0xf8, 0xf4, 0x22, 0x9f, 0xff, 0xfe, 0xd3, - 0xf5, 0x1f, 0xab, 0x5b, 0xb7, 0xdf, 0xcf, 0xca, - 0x6f, 0x8d, 0xbf, 0x95, 0x3a, 0x4a, 0x74, 0xfe, - 0x7f, 0x0f, 0xaa, 0xb4, 0x9d, 0x3f, 0xff, 0xff, - 0xbc, 0x0a, 0xff, 0x3a, 0x9d, 0xdb, 0x5b, 0x4d, - 0xdf, 0xca, 0x5f, 0x9a, 0x51, 0xfc, 0xea, 0x4e, - 0x9d, 0xdc, 0x86, 0x8a, 0x62, 0x31, 0x17, 0xe9, - 0x09, 0x99, 0xff, 0x67, 0xe9, 0x7d, 0x6e, 0xa2, - 0xd1, 0xd3, 0xea, 0x7f, 0x8d, 0xb0, 0xe8, 0xf1, - 0xf4, 0x71, 0xfc, 0xff, 0x73, 0x36, 0xb2, 0xd1, - 0x81, 0x3a, 0x7f, 0xff, 0x7f, 0x12, 0xf7, 0x5b, - 0xd5, 0xaf, 0xe0, 0x77, 0x2b, 0x75, 0x3a, 0x7f, - 0xc8, 0xac, 0xb6, 0x57, 0x3d, 0xf3, 0xa1, 0x51, - 0x49, 0x66, 0x89, 0xf0, 0xab, 0x65, 0x13, 0xa7, - 0xee, 0xfe, 0xed, 0x97, 0xe7, 0x46, 0x1f, 0xbf, - 0x88, 0xb4, 0x26, 0x8a, 0x17, 0x67, 0xf6, 0x4a, - 0xd6, 0x70, 0x91, 0xa1, 0xd2, 0x45, 0xe8, 0x43, - 0x34, 0x1e, 0x30, 0xf1, 0x64, 0x23, 0xbe, 0x43, - 0x78, 0xd5, 0x27, 0xfd, 0xac, 0x78, 0x3b, 0xbb, - 0x6e, 0xa0, 0xe9, 0xec, 0xfe, 0xbd, 0x59, 0xd3, - 0xff, 0xff, 0xeb, 0x7a, 0xf6, 0xc1, 0x56, 0xdb, - 0x99, 0xa3, 0x7e, 0xec, 0xa5, 0xf9, 0xa7, 0xc1, - 0x53, 0xa5, 0x7f, 0x22, 0xda, 0xa4, 0xf3, 0xff, - 0xec, 0xcb, 0x79, 0x2d, 0x9b, 0xab, 0xa5, 0x97, - 0x56, 0x74, 0xff, 0xf5, 0xed, 0x80, 0xdc, 0xc5, - 0xcb, 0xed, 0x9f, 0x3a, 0x7f, 0xa9, 0xdc, 0xc5, - 0xcf, 0xdf, 0x50, 0xe9, 0x57, 0x11, 0x1f, 0xca, - 0x32, 0xf7, 0xd3, 0x10, 0xbc, 0x3a, 0x67, 0xcf, - 0x5a, 0x5f, 0x53, 0xa7, 0xff, 0xff, 0xfd, 0x9f, - 0xd7, 0xbb, 0xd6, 0xd1, 0xcd, 0x7a, 0xea, 0xcf, - 0xf6, 0xbb, 0x79, 0xd9, 0x6a, 0x40, 0xbb, 0x15, - 0x3f, 0xff, 0xf2, 0x78, 0x3b, 0x68, 0x3b, 0xb4, - 0x5a, 0x2a, 0x96, 0xd2, 0xd4, 0xf0, 0x4e, 0x9b, - 0x4a, 0x29, 0x4d, 0x1b, 0xc5, 0x15, 0x85, 0x2c, - 0x2a, 0xaf, 0x3b, 0x46, 0x73, 0xf8, 0xdd, 0x67, - 0xff, 0x31, 0x37, 0x5b, 0x9f, 0x8c, 0xef, 0x80, - 0xe9, 0xff, 0xf7, 0xbf, 0x7b, 0x63, 0x7d, 0xda, - 0xcd, 0x5f, 0x7c, 0x07, 0x4f, 0xfc, 0xbf, 0xa3, - 0xf8, 0xda, 0xba, 0x2d, 0x27, 0x45, 0x51, 0x4b, - 0xa2, 0xec, 0xf6, 0x5f, 0x1b, 0xb3, 0xa7, 0x6a, - 0x65, 0x8e, 0x9b, 0x36, 0x3a, 0x28, 0x4d, 0xb1, - 0xf0, 0xef, 0xa9, 0x25, 0xc9, 0x5b, 0x0f, 0x4f, - 0xdb, 0x2f, 0xed, 0xcf, 0x3a, 0x72, 0xec, 0xa7, - 0x4f, 0xe6, 0x9f, 0x97, 0xaf, 0x35, 0xb8, 0xf2, - 0x18, 0x5b, 0x3f, 0x37, 0xdb, 0xb5, 0x6a, 0x03, - 0xa7, 0xf0, 0xb5, 0xfc, 0xb7, 0x5c, 0xe8, 0xc3, - 0xe5, 0xf1, 0x9c, 0xfc, 0xa0, 0xcb, 0xef, 0xa0, - 0xe8, 0x79, 0xe8, 0x7c, 0x86, 0x7f, 0xff, 0xd7, - 0xff, 0x02, 0xb4, 0x67, 0xbf, 0x7f, 0xe3, 0x6d, - 0xd5, 0x0f, 0xce, 0x9f, 0xf7, 0xb1, 0xad, 0xd5, - 0x5b, 0x76, 0xac, 0xe9, 0xfd, 0xd4, 0xd6, 0xd9, - 0xc2, 0x74, 0x6c, 0x7e, 0xb7, 0x44, 0x93, 0x0e, - 0x9d, 0xfc, 0xd8, 0xe8, 0x53, 0x59, 0x48, 0x84, - 0xfd, 0x4d, 0x73, 0x3f, 0xac, 0xe9, 0xe6, 0x6f, - 0x50, 0x1d, 0x18, 0x7a, 0x5e, 0x2f, 0x85, 0x4f, - 0x2b, 0x21, 0xcf, 0xe4, 0xf1, 0x73, 0x9f, 0xd7, - 0xf5, 0x19, 0x6d, 0x2e, 0x74, 0xf6, 0x52, 0xad, - 0x1d, 0x3f, 0x5b, 0x9e, 0xc1, 0xf1, 0xd3, 0xef, - 0x00, 0x72, 0x93, 0xa3, 0x5a, 0x2a, 0xbc, 0x6a, - 0x02, 0x2d, 0x0b, 0x27, 0xff, 0xe1, 0x7d, 0xb1, - 0xab, 0x2f, 0xf5, 0xff, 0x2f, 0x6d, 0x0e, 0x9f, - 0xff, 0xde, 0xca, 0x5f, 0x5b, 0xd9, 0x58, 0xbe, - 0xdb, 0x72, 0xb7, 0x53, 0xa7, 0xff, 0xff, 0xda, - 0x7f, 0x70, 0x7a, 0x97, 0x8f, 0x03, 0x75, 0x74, - 0x5a, 0x6d, 0x9e, 0xfe, 0x6b, 0x3a, 0x7f, 0xfd, - 0xa0, 0x8e, 0x5e, 0xb6, 0xf6, 0x6e, 0xb0, 0x14, - 0xe8, 0xc4, 0x71, 0x5a, 0x11, 0x93, 0xff, 0xcf, - 0x6f, 0xb6, 0x2f, 0xda, 0xfe, 0x5e, 0xbc, 0x74, - 0xff, 0xfb, 0x6c, 0x66, 0xe6, 0xfb, 0xad, 0x5a, - 0x7f, 0x0d, 0x07, 0x4f, 0xf6, 0x7e, 0x8d, 0xc0, - 0xee, 0x68, 0xe9, 0xff, 0x57, 0x4f, 0xee, 0xae, - 0x9a, 0x9a, 0x09, 0xd3, 0xff, 0xa8, 0xdd, 0x5b, - 0x69, 0xba, 0xf5, 0xaa, 0x89, 0xd3, 0xff, 0xfe, - 0xca, 0x6b, 0xec, 0x0e, 0xeb, 0x65, 0xeb, 0x94, - 0x6e, 0xcf, 0x6c, 0x74, 0x62, 0x30, 0xbc, 0xa1, - 0x14, 0x2b, 0x87, 0x48, 0xd3, 0x29, 0x27, 0xf2, - 0x93, 0x17, 0x2a, 0x77, 0x78, 0xc2, 0x67, 0xfd, - 0xc3, 0x6c, 0xba, 0xff, 0xc0, 0x3a, 0x7f, 0xb0, - 0x34, 0xbe, 0xbb, 0xaf, 0xe3, 0xa7, 0xff, 0xfc, - 0xa8, 0xac, 0xdc, 0xd5, 0xbd, 0xfd, 0xb7, 0x6f, - 0x4f, 0xe5, 0xb0, 0xe8, 0x44, 0x75, 0x7c, 0xf1, - 0xbc, 0xf2, 0x7b, 0xd7, 0xf2, 0x9d, 0x3f, 0xff, - 0xff, 0x5d, 0x59, 0xff, 0x6b, 0xb7, 0x35, 0xbb, - 0xfa, 0x37, 0xee, 0xca, 0x5f, 0x9a, 0x7c, 0x15, - 0x3a, 0x1e, 0x8b, 0x8f, 0x10, 0xc2, 0xaf, 0x27, - 0xe4, 0xbe, 0x5f, 0x46, 0xbb, 0xd0, 0xea, 0x9e, - 0xbd, 0x78, 0x07, 0x4f, 0xfd, 0x6f, 0x6e, 0xf0, - 0xe9, 0x47, 0x0a, 0x1d, 0x3f, 0x5f, 0x87, 0xf4, - 0xf8, 0xe8, 0xc4, 0x4a, 0x6b, 0x21, 0xba, 0x34, - 0xf6, 0xfd, 0x3a, 0xa7, 0x4f, 0xff, 0xde, 0x1d, - 0xd9, 0xec, 0xfe, 0xbb, 0x7b, 0x19, 0x88, 0x74, - 0xff, 0xff, 0x7a, 0x81, 0x56, 0x57, 0x45, 0xdc, - 0xa2, 0xbb, 0x59, 0x58, 0x74, 0x62, 0x31, 0x05, - 0x72, 0x7f, 0xff, 0xf0, 0x8f, 0xa8, 0xdd, 0x6c, - 0xbf, 0xd5, 0xab, 0x65, 0x0b, 0x4d, 0xf4, 0x43, - 0xa7, 0xff, 0xfe, 0x5d, 0x76, 0x5d, 0xdf, 0xcd, - 0x35, 0xee, 0xa5, 0xf5, 0xf7, 0xf7, 0xad, 0x07, - 0x4f, 0xa9, 0xaf, 0xb3, 0xe7, 0x46, 0x22, 0x9f, - 0x47, 0xf8, 0xf2, 0x68, 0xd6, 0x8c, 0x8e, 0x7f, - 0xbf, 0xa3, 0x5f, 0xcb, 0x75, 0xce, 0x9f, 0xed, - 0xac, 0xa1, 0xda, 0xdc, 0xc3, 0xa7, 0xff, 0xfb, - 0x86, 0xd9, 0x7f, 0x67, 0xe8, 0xaa, 0xe7, 0xf6, - 0xb2, 0x9d, 0x3d, 0x7d, 0xd4, 0x09, 0xd1, 0x4a, - 0x22, 0x79, 0x96, 0x7e, 0x6a, 0x97, 0xe8, 0xb7, - 0x3a, 0x7f, 0xad, 0xcc, 0xfe, 0x5b, 0xae, 0x74, - 0x29, 0xf3, 0xd4, 0xc2, 0x7e, 0xcb, 0x88, 0xe3, - 0xce, 0x9f, 0xbf, 0x96, 0xfe, 0x21, 0xd3, 0xdf, - 0xca, 0xee, 0x68, 0xf5, 0x6e, 0x55, 0x1b, 0x2b, - 0xca, 0x48, 0xd9, 0xfc, 0x54, 0x27, 0x56, 0x86, - 0x1d, 0xe1, 0x14, 0xd9, 0xda, 0x7f, 0xef, 0xff, - 0x37, 0x0e, 0x5b, 0xf8, 0xd1, 0xd0, 0xab, 0xad, - 0xd9, 0x39, 0x65, 0xce, 0xf3, 0xef, 0xab, 0x3b, - 0x63, 0xa7, 0xf6, 0x35, 0x6c, 0xba, 0xec, 0x74, - 0xff, 0xff, 0xf5, 0xb2, 0xf5, 0xe6, 0xb7, 0x7f, - 0x29, 0x7e, 0x69, 0x47, 0xf3, 0x6b, 0x79, 0xa3, - 0xa6, 0xd3, 0x59, 0xd0, 0x28, 0x9e, 0xbc, 0x21, - 0x67, 0xec, 0xa3, 0xf9, 0x4d, 0x4e, 0x9f, 0xfc, - 0x34, 0xbe, 0xb8, 0xca, 0x56, 0xd6, 0x53, 0xa5, - 0x53, 0xa3, 0x43, 0xda, 0xde, 0x95, 0x3f, 0x95, - 0x9f, 0xcb, 0x75, 0xce, 0x9f, 0xff, 0xf5, 0xfe, - 0xa0, 0xa5, 0xf5, 0xd1, 0x6f, 0xfc, 0xdb, 0xd8, - 0x2d, 0x1d, 0x1e, 0x54, 0xd8, 0xd1, 0x3f, 0x43, - 0x47, 0xe4, 0xf5, 0x84, 0x46, 0x84, 0xbb, 0xcc, - 0x67, 0xcf, 0xf0, 0xe5, 0x27, 0x4f, 0xb2, 0xb4, - 0x60, 0x4e, 0x8a, 0x4f, 0x3a, 0xc4, 0xf3, 0xff, - 0xef, 0x51, 0x89, 0xb5, 0xbd, 0x9a, 0xf7, 0x6b, - 0xd1, 0x4e, 0x9e, 0x6f, 0xbe, 0x58, 0xe9, 0xff, - 0xff, 0x9b, 0xe9, 0x7f, 0x53, 0xbb, 0xfa, 0x37, - 0xee, 0xca, 0x5f, 0x9a, 0x7c, 0x15, 0x3a, 0x28, - 0x45, 0x36, 0x12, 0x4f, 0xff, 0xff, 0x96, 0x9a, - 0xe8, 0xb4, 0xee, 0xb6, 0x6d, 0xbb, 0xf9, 0xa6, - 0xbd, 0xd5, 0x00, 0x3d, 0x73, 0xa7, 0xcb, 0xfa, - 0x7a, 0x83, 0xa7, 0xff, 0xff, 0xff, 0x63, 0x31, - 0x16, 0xca, 0xcb, 0xe5, 0x6a, 0xa2, 0x2b, 0xfc, - 0xa3, 0x34, 0xae, 0x8d, 0x7d, 0x58, 0x74, 0xff, - 0x03, 0x36, 0xfe, 0x5b, 0xae, 0x74, 0xfe, 0xa3, - 0x45, 0xbb, 0x3d, 0x73, 0xa7, 0xfc, 0xba, 0xdb, - 0xf4, 0xcf, 0xb5, 0xbd, 0x87, 0x46, 0x1f, 0xe5, - 0x26, 0xb3, 0xff, 0xd9, 0x7d, 0xb3, 0x01, 0x5d, - 0xc3, 0x5d, 0xaa, 0x74, 0xf5, 0x1e, 0xfd, 0xce, - 0x85, 0x57, 0xe1, 0x84, 0x7e, 0x8c, 0x0c, 0x48, - 0xd9, 0x09, 0x1b, 0x14, 0x74, 0x28, 0xff, 0x0b, - 0x4a, 0x91, 0x68, 0xa5, 0x3b, 0x6d, 0xcd, 0x1d, - 0x3f, 0xfd, 0x4b, 0xfe, 0xbb, 0x99, 0xf5, 0xd7, - 0x7f, 0x50, 0x74, 0x61, 0xfb, 0x78, 0x82, 0x7e, - 0xa1, 0xa7, 0xb7, 0x06, 0x83, 0xa7, 0xcc, 0xf6, - 0xd9, 0xb1, 0xd2, 0x68, 0xe9, 0x95, 0x87, 0x4a, - 0xe7, 0x40, 0x9a, 0x5e, 0x15, 0x8d, 0x8f, 0x56, - 0xc6, 0xd3, 0xd7, 0xaf, 0x52, 0x74, 0xcd, 0x71, - 0xd3, 0xfa, 0xde, 0x1a, 0x5f, 0xf5, 0x3a, 0x35, - 0x52, 0x68, 0x0a, 0x69, 0x8f, 0xde, 0x23, 0x61, - 0x16, 0x82, 0xd3, 0xfa, 0xbe, 0xa4, 0x3d, 0xf0, - 0x1d, 0x3f, 0xfe, 0x51, 0x54, 0xc6, 0x2a, 0x3f, - 0xd5, 0xd1, 0x87, 0x43, 0x48, 0x86, 0xd4, 0x34, - 0x9d, 0xfe, 0x61, 0xd3, 0x01, 0x4e, 0x8f, 0x1b, - 0x0f, 0x8d, 0xc9, 0xcd, 0xc7, 0x59, 0x1e, 0xb2, - 0x81, 0xa8, 0x5d, 0xda, 0x36, 0x1c, 0x9d, 0xd2, - 0xd7, 0x0b, 0xa4, 0x97, 0xd7, 0x4c, 0x75, 0x3e, - 0x84, 0xdb, 0x51, 0x81, 0x8c, 0x7d, 0x8c, 0x87, - 0x55, 0xa7, 0xa3, 0xfa, 0x1c, 0x7f, 0x9d, 0x9f, - 0xac, 0xaa, 0x4b, 0xce, 0x58, 0xb6, 0x8c, 0x57, - 0x52, 0x36, 0x1d, 0xf0, 0xe3, 0x6f, 0x57, 0x9f, - 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x53, 0xb3, 0xe1, - 0x4e, 0xe4, 0x34, 0x54, 0x73, 0xfe, 0xe7, 0xbb, - 0x05, 0x3b, 0x90, 0xd1, 0x34, 0x49, 0xd8, 0x7e, - 0xcc, 0x30, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, - 0x55, 0xf3, 0xe1, 0x4e, 0xe4, 0x34, 0x56, 0xd3, - 0xfc, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x24, 0x19, - 0x3b, 0x0f, 0xc7, 0x8c, 0x27, 0xfe, 0x77, 0x3d, - 0xd8, 0x29, 0xdc, 0x86, 0x89, 0x0e, 0x7c, 0x29, - 0xdc, 0x86, 0x8b, 0x12, 0x7d, 0x82, 0xbf, 0xd6, - 0x74, 0xff, 0xcb, 0x95, 0xcf, 0xed, 0xe0, 0x2f, - 0xce, 0x9d, 0xf5, 0xa0, 0xe9, 0x3f, 0xe7, 0xbf, - 0xa2, 0x1c, 0xed, 0x4f, 0x00, 0xe9, 0xc2, 0xaf, - 0x3a, 0x7d, 0xcf, 0xdf, 0x65, 0x3a, 0x7e, 0xad, - 0x47, 0xc1, 0xd5, 0x9d, 0x1e, 0x3d, 0x9e, 0x28, - 0x9f, 0xeb, 0x27, 0xf1, 0xb7, 0xf2, 0xa7, 0x4f, - 0xe0, 0x2e, 0x5f, 0xdf, 0x09, 0x51, 0x89, 0xfe, - 0xeb, 0x30, 0xa6, 0x10, 0xde, 0x29, 0x11, 0xfb, - 0x39, 0xf1, 0x08, 0x0e, 0x67, 0xfe, 0xb7, 0x96, - 0xf6, 0xed, 0x79, 0xf0, 0x1d, 0x3f, 0xfd, 0x94, - 0xbe, 0xfa, 0x50, 0xeb, 0x73, 0x3d, 0x53, 0xa6, - 0xb3, 0xb1, 0x13, 0x1f, 0x45, 0x87, 0x26, 0x7a, - 0xb1, 0x87, 0xcf, 0xe7, 0x60, 0xa7, 0x72, 0x1a, - 0x2c, 0xd9, 0xff, 0xce, 0x67, 0x3d, 0xd8, 0x29, - 0xdc, 0x86, 0x8a, 0x02, 0x7f, 0x7f, 0x2e, 0x0d, - 0xff, 0x68, 0xe9, 0xb7, 0x54, 0xe9, 0xe4, 0xee, - 0x43, 0x45, 0xbf, 0x3f, 0x6f, 0xbf, 0x27, 0x09, - 0xd0, 0x27, 0xae, 0x02, 0xb9, 0xf5, 0xdf, 0x5d, - 0xf5, 0x3a, 0x7f, 0x2b, 0x6d, 0xf7, 0xfa, 0x80, - 0xe9, 0xee, 0xf7, 0xee, 0x74, 0xfe, 0xd2, 0x9a, - 0xaa, 0x63, 0x0e, 0x9d, 0xfc, 0x79, 0xd2, 0x72, - 0xa7, 0x2a, 0xf3, 0x6c, 0x6f, 0x12, 0x1b, 0x15, - 0x00, 0xdb, 0xe4, 0x37, 0x33, 0x9f, 0xf7, 0x3d, - 0xd8, 0x29, 0xdc, 0x86, 0x8a, 0x52, 0x7f, 0x9e, - 0xec, 0x14, 0xee, 0x43, 0x44, 0x9d, 0x27, 0x31, - 0x10, 0xfc, 0x8d, 0x0a, 0xee, 0x2e, 0x68, 0x8c, - 0x8b, 0x6b, 0x5a, 0xa3, 0x91, 0xd3, 0xa4, 0x2c, - 0x7c, 0x4f, 0xab, 0x85, 0x63, 0x44, 0xcc, 0x97, - 0x0d, 0x62, 0x90, 0x1d, 0xd6, 0x3e, 0x76, 0xd0, - 0xfc, 0x9f, 0x0a, 0x77, 0x21, 0xa2, 0x1f, 0x9f, - 0xf7, 0x3d, 0xd8, 0x29, 0xdc, 0x86, 0x89, 0x4e, - 0x4e, 0xc3, 0xf6, 0x61, 0x84, 0xfe, 0x76, 0x0a, - 0x77, 0x21, 0xa2, 0x27, 0x9f, 0xce, 0xc1, 0x4e, - 0xe4, 0x34, 0x46, 0x33, 0xff, 0x9c, 0xce, 0x7b, - 0xb0, 0x53, 0xb9, 0x0d, 0x13, 0xcc, 0x2a, 0x3b, - 0xe8, 0x27, 0x79, 0xdd, 0x8e, 0xe7, 0xc2, 0x9d, - 0xc8, 0x68, 0x88, 0x27, 0xfd, 0xcf, 0x76, 0x0a, - 0x77, 0x21, 0xa2, 0x54, 0x93, 0xb0, 0xfd, 0x98, - 0x61, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0x8a, - 0x27, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x11, 0x94, - 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, 0xee, 0x43, - 0x44, 0xcb, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, - 0xa9, 0x27, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x15, - 0xd4, 0xfe, 0x76, 0x0a, 0x77, 0x21, 0xa2, 0xc5, - 0x9f, 0xf9, 0x9c, 0xf7, 0x60, 0xa7, 0x72, 0x1a, - 0x27, 0xa9, 0xfe, 0x0b, 0xb9, 0x7d, 0xa9, 0xe6, - 0xf3, 0xa1, 0xc8, 0x8a, 0x54, 0xc9, 0xfb, 0x55, - 0xd9, 0x7f, 0x69, 0x53, 0xa7, 0x85, 0x97, 0xa4, - 0xe9, 0xc9, 0x96, 0x2a, 0x7f, 0xfe, 0xb7, 0xfb, - 0x5f, 0x85, 0x5e, 0xfc, 0xa8, 0x00, 0xa7, 0x4f, - 0xff, 0xc3, 0xe6, 0xfe, 0x63, 0x94, 0x47, 0xcd, - 0xe9, 0xa5, 0x07, 0x4f, 0x93, 0xd6, 0xde, 0xc3, - 0xa7, 0xff, 0x75, 0x7f, 0x80, 0x75, 0xbc, 0x9c, - 0x27, 0x4f, 0xaf, 0x65, 0x62, 0x1d, 0x3f, 0xfb, - 0xfb, 0x95, 0x7e, 0xac, 0xdd, 0xba, 0xda, 0x87, - 0x4d, 0xd4, 0x9d, 0x3f, 0xba, 0xbd, 0x6d, 0x3f, - 0x41, 0xd1, 0xf3, 0xca, 0xd0, 0x5a, 0x3c, 0xa8, - 0x40, 0x56, 0xd8, 0xc1, 0x62, 0x9f, 0xa3, 0xdc, - 0x99, 0xbe, 0x13, 0x93, 0xbf, 0xd7, 0x35, 0x42, - 0xd3, 0xbf, 0xea, 0x9d, 0x20, 0xea, 0x87, 0x88, - 0xa5, 0x13, 0xc2, 0xdb, 0x98, 0x74, 0x61, 0xe7, - 0x78, 0xb2, 0x7f, 0xde, 0xb7, 0x5d, 0x5a, 0xaf, - 0x98, 0x74, 0xfd, 0xef, 0xeb, 0xf6, 0x54, 0xe9, - 0xef, 0xed, 0xbb, 0x59, 0xd3, 0xf6, 0x54, 0x3d, - 0xfa, 0x0e, 0x8e, 0x3d, 0x4f, 0x93, 0xcf, 0xb4, - 0xd7, 0x5d, 0x94, 0xe8, 0xc4, 0x67, 0xfa, 0x10, - 0x76, 0x21, 0x9f, 0xff, 0xfd, 0x65, 0xf0, 0x8a, - 0xef, 0xbf, 0xbf, 0xa3, 0xf2, 0xbb, 0x69, 0xc2, - 0xf3, 0xa7, 0xd4, 0x6c, 0x3e, 0x09, 0xd3, 0xf6, - 0xdc, 0x20, 0xd4, 0xc3, 0xa7, 0xfd, 0xff, 0x3e, - 0xf6, 0xc1, 0xf5, 0x4e, 0x9f, 0xf7, 0x60, 0x17, - 0xfd, 0xb6, 0x6b, 0x3a, 0x1e, 0x7f, 0x80, 0x3e, - 0x9f, 0xfc, 0xb9, 0x4d, 0xec, 0xb7, 0xfa, 0x82, - 0xa7, 0x4e, 0xad, 0x98, 0x74, 0x5c, 0xf9, 0x74, - 0x49, 0x9f, 0xb4, 0xad, 0xeb, 0x97, 0x3a, 0x70, - 0x42, 0x12, 0xa7, 0xff, 0x5f, 0xd7, 0xad, 0xbd, - 0xa9, 0xbc, 0x55, 0xe5, 0x38, 0xbc, 0x8f, 0x22, - 0xa3, 0x44, 0xa8, 0x7a, 0xac, 0x4c, 0x7a, 0xf1, - 0x48, 0xc2, 0xaf, 0xf0, 0x87, 0xbc, 0x32, 0xe6, - 0xd7, 0x87, 0x4f, 0x83, 0x75, 0xa4, 0x27, 0x4f, - 0xe5, 0xe1, 0xb0, 0x7b, 0x62, 0xa6, 0x08, 0x4a, - 0x8d, 0x8f, 0x1c, 0x26, 0x33, 0xd5, 0xbf, 0xb5, - 0x65, 0x38, 0xd1, 0xcf, 0xfe, 0xd2, 0xb5, 0xdb, - 0xeb, 0xb5, 0x96, 0xf5, 0x3a, 0x7f, 0xd7, 0xf0, - 0xf0, 0x1b, 0xef, 0x96, 0x3a, 0x1e, 0x88, 0xdf, - 0x26, 0xcf, 0xb2, 0x8c, 0xf0, 0x9d, 0x3f, 0x5d, - 0x47, 0x6c, 0xf9, 0xd3, 0xfb, 0x6b, 0x2f, 0xc3, - 0xd4, 0x9d, 0x3f, 0xff, 0xef, 0x33, 0xbf, 0x4b, - 0xdc, 0x1c, 0xf3, 0x6c, 0xfe, 0x6d, 0xcf, 0x3c, - 0x5e, 0xb0, 0xa8, 0xdc, 0xe2, 0xcd, 0x0d, 0x27, - 0xff, 0xb2, 0xf5, 0xc6, 0xac, 0xad, 0xb2, 0xd6, - 0x53, 0xa7, 0xff, 0xff, 0xde, 0xca, 0x6b, 0xec, - 0x0e, 0xec, 0xa5, 0xf5, 0xf5, 0xeb, 0xea, 0x76, - 0xdb, 0x4a, 0x0e, 0x8f, 0xa3, 0x7a, 0xea, 0x13, - 0x78, 0x27, 0x4d, 0xbc, 0x07, 0x43, 0x46, 0xb8, - 0x02, 0xd3, 0x9f, 0xdb, 0x1d, 0x30, 0x42, 0x74, - 0x78, 0xf5, 0x2a, 0x44, 0x11, 0xc9, 0xd7, 0xde, - 0x02, 0x9c, 0x6b, 0xe7, 0xff, 0xff, 0x5d, 0x76, - 0xb6, 0x9d, 0xad, 0x9d, 0xb6, 0x67, 0xf5, 0xd6, - 0xb9, 0xf0, 0x1d, 0x14, 0xa2, 0xaf, 0xc5, 0xd3, - 0xfd, 0x97, 0xb2, 0x8d, 0xf1, 0x0e, 0x9c, 0xa2, - 0xd1, 0xd1, 0x89, 0xf9, 0xf4, 0x6f, 0xff, 0x24, - 0xd4, 0x35, 0x9f, 0x7f, 0x2f, 0xcd, 0xe7, 0x4f, - 0xfd, 0x82, 0x38, 0xd5, 0xb2, 0xbd, 0xf3, 0xa7, - 0xf7, 0xd4, 0x74, 0x4b, 0x29, 0xd3, 0xef, 0x6b, - 0xb7, 0x3c, 0xe9, 0xfb, 0xca, 0xca, 0x30, 0x27, - 0x4c, 0x10, 0x9d, 0x08, 0x7d, 0x82, 0x52, 0x12, - 0xd9, 0xfe, 0xc6, 0xb9, 0xae, 0xa5, 0xf5, 0x29, - 0xc6, 0xb6, 0x7f, 0xf6, 0x7c, 0x19, 0x6f, 0x53, - 0x5b, 0x69, 0x53, 0xa7, 0xef, 0xf0, 0x36, 0xcf, - 0x9d, 0x3f, 0x33, 0xda, 0x98, 0x3a, 0xce, 0x9c, - 0x10, 0x84, 0xa9, 0xff, 0x94, 0x7f, 0xa7, 0x6f, - 0xc6, 0x95, 0x87, 0x38, 0xbc, 0x8d, 0x5a, 0xa2, - 0xe6, 0xa1, 0x95, 0x54, 0xab, 0xa6, 0x68, 0x5a, - 0x14, 0xd9, 0xdc, 0x34, 0x1e, 0x41, 0x29, 0xe7, - 0xe5, 0x2f, 0x3c, 0x82, 0x53, 0xab, 0xed, 0x8f, - 0x20, 0x94, 0xc1, 0x09, 0xe4, 0x12, 0x84, 0x45, - 0x23, 0x45, 0x17, 0x2f, 0x09, 0x54, 0xdd, 0x72, - 0xc8, 0x24, 0xe3, 0x7b, 0x3f, 0x65, 0xb3, 0x3f, - 0xac, 0xe9, 0xdd, 0xf0, 0x7c, 0xf7, 0xee, 0x65, - 0x3d, 0xa9, 0x9d, 0x63, 0xa5, 0x9a, 0xb3, 0xd6, - 0x01, 0x9c, 0xfe, 0x69, 0xfa, 0x51, 0x7f, 0x50, - 0x54, 0xfe, 0xf3, 0xf4, 0xea, 0xdb, 0xc7, 0x49, - 0xe5, 0x4e, 0xe1, 0xa0, 0xa8, 0x2a, 0x14, 0xda, - 0xa0, 0x83, 0x06, 0xe7, 0xbe, 0xa0, 0x42, 0x9c, - 0x6b, 0x21, 0x51, 0x8b, 0x90, 0x95, 0x9e, 0xd3, - 0x85, 0xe7, 0x49, 0x87, 0x4d, 0x8f, 0xd8, 0xd8, - 0xb4, 0x43, 0x3e, 0x0e, 0x35, 0xe6, 0x1d, 0x3f, - 0x22, 0xb4, 0xf5, 0x01, 0x52, 0xf1, 0xd3, 0xfd, - 0x46, 0x8f, 0xda, 0xda, 0x77, 0xce, 0x9f, 0xbc, - 0xac, 0xa3, 0x02, 0x74, 0xfb, 0x28, 0x66, 0x30, - 0xe9, 0xd5, 0xf0, 0x9d, 0x1f, 0x3c, 0x2b, 0x93, - 0xcf, 0xdf, 0xe0, 0x03, 0x4a, 0x9d, 0x3f, 0x7b, - 0x5e, 0xf5, 0x63, 0xb6, 0x4c, 0x33, 0x04, 0x04, - 0xf6, 0xad, 0xf7, 0x22, 0x8c, 0x4f, 0x47, 0xc5, - 0x1f, 0x8c, 0xe2, 0x7b, 0xf9, 0x46, 0x87, 0x43, - 0xd5, 0xeb, 0xe1, 0x5d, 0xa1, 0xfb, 0xf5, 0x7a, - 0xc7, 0xff, 0x73, 0x69, 0xff, 0xf5, 0x02, 0x39, - 0xb7, 0x32, 0xdd, 0x7f, 0xab, 0x47, 0x4e, 0x08, - 0x42, 0x54, 0xcf, 0x52, 0x9c, 0x5e, 0x42, 0xa2, - 0x54, 0x5c, 0x67, 0xea, 0x1a, 0x7b, 0x70, 0x68, - 0x3a, 0x7a, 0xdd, 0x48, 0x4e, 0x9f, 0x50, 0x3e, - 0x56, 0x1d, 0x3f, 0xfc, 0xbb, 0xef, 0xf5, 0x07, - 0x5c, 0x1b, 0xfe, 0xf3, 0xa3, 0x55, 0x23, 0x43, - 0xc6, 0x9c, 0x45, 0x72, 0x79, 0xcf, 0xf7, 0xce, - 0x9f, 0xae, 0xad, 0x7f, 0x4a, 0x9d, 0x0f, 0x3c, - 0xbb, 0x8e, 0x4e, 0xd3, 0x46, 0xec, 0xe9, 0xb9, - 0xcd, 0xd1, 0xb9, 0x08, 0xd5, 0x63, 0x6d, 0x57, - 0xe4, 0x4b, 0x28, 0xaa, 0x88, 0x7f, 0xbc, 0x87, - 0x68, 0xc3, 0xb2, 0x57, 0xc6, 0xb6, 0x64, 0x17, - 0xf4, 0x24, 0x9a, 0x86, 0x18, 0x91, 0xb2, 0x30, - 0x2b, 0x4b, 0x00, 0xe4, 0x5f, 0x94, 0xd6, 0x53, - 0x7d, 0xe7, 0x74, 0x74, 0x87, 0xc6, 0xa4, 0x3c, - 0x77, 0xc2, 0x35, 0xbc, 0x8a, 0x7f, 0xf3, 0x99, - 0xcf, 0x76, 0x0a, 0x77, 0x21, 0xa2, 0x8a, 0x9f, - 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x5d, 0x13, 0xff, - 0x3b, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0x97, - 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0xbc, 0xa1, - 0x5b, 0xe1, 0xaa, 0x09, 0xde, 0x77, 0x87, 0x68, - 0xa4, 0xd1, 0xdb, 0x0e, 0xed, 0x0c, 0x5f, 0xd7, - 0x25, 0x55, 0x8e, 0xca, 0xea, 0x5a, 0x1d, 0xb6, - 0x4c, 0x9f, 0xfc, 0xe6, 0x73, 0xdd, 0x82, 0x9d, - 0xc8, 0x68, 0x96, 0x67, 0x93, 0xb9, 0x0d, 0x11, - 0x9c, 0xfd, 0xe5, 0x65, 0x18, 0x13, 0xa6, 0xfb, - 0xce, 0x93, 0x0e, 0x9f, 0x78, 0x6d, 0xce, 0x13, - 0xd3, 0x61, 0x6e, 0xf1, 0x69, 0xfb, 0x9f, 0xe1, - 0xe1, 0x3a, 0x75, 0x56, 0x93, 0xa0, 0x53, 0x10, - 0x63, 0xf7, 0xd3, 0x2e, 0x55, 0x3f, 0xf5, 0xfc, - 0x3c, 0x0d, 0xd4, 0xbd, 0x58, 0x74, 0xfb, 0xea, - 0xfa, 0xf1, 0xd1, 0xe3, 0xec, 0x6c, 0x8d, 0x3f, - 0xff, 0x7b, 0x6d, 0x2f, 0x5a, 0x5e, 0xbf, 0x05, - 0x77, 0x69, 0xf3, 0xa7, 0xfe, 0xc6, 0x5f, 0xc1, - 0xaa, 0xdb, 0xb5, 0x67, 0x4f, 0xfa, 0xcb, 0xeb, - 0x79, 0x7e, 0xd6, 0x87, 0x49, 0xd8, 0x9d, 0xbb, - 0x50, 0xa9, 0xe2, 0x4f, 0xb1, 0xd5, 0x1e, 0x7f, - 0xf3, 0x99, 0xcf, 0x76, 0x0a, 0x77, 0x21, 0xa2, - 0x69, 0x9f, 0xfc, 0xe6, 0x73, 0xdd, 0x82, 0x9d, - 0xc8, 0x68, 0x9c, 0x67, 0xff, 0x39, 0x9c, 0xf7, - 0x60, 0xa7, 0x72, 0x1a, 0x28, 0x19, 0xf0, 0xa7, - 0x72, 0x1a, 0x2e, 0x09, 0x97, 0xc7, 0x4f, 0xef, - 0xbd, 0x7d, 0xb6, 0x30, 0xe9, 0x3b, 0x0f, 0xe2, - 0xc6, 0x1a, 0x0a, 0xcf, 0xef, 0x3b, 0x7a, 0x83, - 0x02, 0x74, 0xff, 0x3d, 0xd8, 0x29, 0xdc, 0x86, - 0x89, 0x26, 0x4e, 0xb1, 0xfa, 0xf1, 0xa4, 0x50, - 0xbb, 0x5a, 0xf9, 0x63, 0x28, 0xaa, 0xd2, 0x90, - 0x14, 0xab, 0x0c, 0xab, 0xc2, 0xaa, 0x7f, 0xda, - 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x8b, 0x52, 0x7f, - 0xdc, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x25, 0x58, - 0x3a, 0x4e, 0xd6, 0x89, 0x86, 0x24, 0x37, 0xa3, - 0x4f, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x22, 0x99, - 0xfc, 0xec, 0x14, 0xee, 0x43, 0x44, 0x69, 0x3f, - 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0xa7, 0xe7, 0xff, - 0x39, 0x9c, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x26, - 0xa9, 0xfc, 0xec, 0x14, 0xee, 0x43, 0x45, 0x77, - 0x3c, 0x9d, 0xc8, 0x68, 0xaf, 0xa7, 0x04, 0x21, - 0x2a, 0x5f, 0x29, 0xc5, 0xe4, 0x09, 0xf3, 0x31, - 0x1e, 0x73, 0xd6, 0x93, 0xa7, 0xfd, 0x96, 0x0f, - 0x5e, 0xab, 0x94, 0x1d, 0x3f, 0xee, 0xf0, 0xe5, - 0x1e, 0x7e, 0x6c, 0x74, 0x9d, 0x88, 0xb8, 0x68, - 0x87, 0xe3, 0x95, 0x3d, 0x9f, 0xfc, 0xe6, 0x73, - 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x9e, 0xe7, 0xf3, - 0xb0, 0x53, 0xb9, 0x0d, 0x17, 0x0c, 0xff, 0xce, - 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x27, 0xc2, - 0xab, 0xb3, 0xa0, 0xed, 0xe7, 0x7a, 0xce, 0xd0, - 0xed, 0xa5, 0x21, 0x8d, 0x66, 0xc8, 0x75, 0x52, - 0x6c, 0x77, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, - 0x8a, 0xa7, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x14, - 0xcc, 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, 0xee, - 0x43, 0x44, 0xcf, 0x3f, 0xf9, 0xcc, 0xe7, 0xbb, - 0x05, 0x3b, 0x90, 0xd1, 0x46, 0xc5, 0x09, 0x8d, - 0x6c, 0x77, 0xac, 0xee, 0xea, 0x53, 0xff, 0x3b, - 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0x75, 0x3f, - 0x7b, 0x3f, 0xb6, 0x50, 0x74, 0xf8, 0x53, 0xb9, - 0x0d, 0x14, 0xd4, 0xfd, 0xe5, 0x65, 0x18, 0x13, - 0xa7, 0xff, 0xb2, 0xf6, 0xc7, 0x8f, 0x7e, 0xf6, - 0xe7, 0x9d, 0x32, 0x80, 0xe9, 0xfe, 0xaf, 0x52, - 0xa9, 0xe4, 0x70, 0xa2, 0x3f, 0xe5, 0xbb, 0xd3, - 0x64, 0xed, 0x93, 0x4b, 0xc2, 0xeb, 0xc2, 0xfe, - 0x7c, 0x29, 0xdc, 0x86, 0x8a, 0xaa, 0x7f, 0xdc, - 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x26, 0xd9, 0x3b, - 0x0f, 0xd9, 0x86, 0x13, 0xf9, 0xd8, 0x29, 0xdc, - 0x86, 0x8a, 0xfe, 0x7f, 0x3b, 0x05, 0x3b, 0x90, - 0xd1, 0x63, 0x4f, 0x85, 0x3b, 0x90, 0xd1, 0x6a, - 0xcf, 0xfb, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x45, - 0x05, 0x27, 0x61, 0xfb, 0x30, 0xc2, 0x7f, 0x3b, - 0x05, 0x3b, 0x90, 0xd1, 0x71, 0x4f, 0xe7, 0x60, - 0xa7, 0x72, 0x1a, 0x2e, 0x99, 0xff, 0xfc, 0xb6, - 0xfe, 0x9d, 0xab, 0xc7, 0xdb, 0x35, 0xe6, 0xcd, - 0x68, 0x74, 0xff, 0x9f, 0x82, 0x3c, 0xfa, 0x30, - 0x27, 0x4e, 0xff, 0x38, 0x51, 0x4b, 0x76, 0x99, - 0xff, 0x0a, 0xd3, 0x6e, 0xad, 0xd4, 0x27, 0x4f, - 0xda, 0xa7, 0xf2, 0xdd, 0x73, 0xa7, 0xe0, 0x73, - 0x4f, 0xf5, 0xce, 0x9f, 0xff, 0xf6, 0xb5, 0xda, - 0xd9, 0x4d, 0xff, 0xd6, 0x07, 0x56, 0xbc, 0x28, - 0x74, 0xf2, 0x77, 0x21, 0xa2, 0x4c, 0x9f, 0xe1, - 0xca, 0x6f, 0x7e, 0x7d, 0x4e, 0x81, 0x3e, 0x36, - 0x15, 0xcf, 0xdb, 0x03, 0x54, 0xa8, 0x35, 0x0e, - 0x9f, 0x65, 0xfc, 0xce, 0x3a, 0x7f, 0xfd, 0xc8, - 0x2b, 0x6f, 0xe5, 0xf1, 0xc1, 0x08, 0x4a, 0x87, - 0x9f, 0xc5, 0xc9, 0xa7, 0xff, 0x69, 0xfe, 0xba, - 0x8d, 0x6c, 0xbf, 0xb9, 0xd3, 0x82, 0x10, 0x95, - 0x3f, 0x0f, 0x50, 0xcf, 0x54, 0xa7, 0x17, 0x93, - 0xec, 0x60, 0xe6, 0xac, 0xe9, 0xff, 0x31, 0x52, - 0xdf, 0xd2, 0xeb, 0x53, 0xa7, 0xfb, 0x38, 0x1b, - 0xb3, 0x6e, 0x79, 0xd3, 0xff, 0xba, 0x8b, 0x79, - 0x7f, 0x8d, 0x78, 0x7c, 0x74, 0x62, 0x30, 0x6e, - 0x7c, 0x13, 0x99, 0xf7, 0xf2, 0xdd, 0x73, 0xa7, - 0xfc, 0xf5, 0xfd, 0x1b, 0xc7, 0xfa, 0x3c, 0xe8, - 0xd5, 0x9f, 0x43, 0x44, 0xd3, 0xd7, 0xa3, 0x36, - 0x3a, 0x7f, 0xbd, 0xaf, 0x78, 0x81, 0x9e, 0xa9, - 0xd0, 0xd1, 0xf0, 0x6a, 0x11, 0xce, 0x08, 0x42, - 0x74, 0xff, 0xfb, 0x19, 0x7f, 0xa8, 0x33, 0x6a, - 0xe3, 0x15, 0x0a, 0x71, 0x79, 0x18, 0x99, 0x5f, - 0xa1, 0x0f, 0x64, 0x29, 0xfa, 0xcb, 0xb9, 0x3d, - 0x41, 0xd3, 0xf0, 0xf0, 0xfd, 0x5e, 0x74, 0x6c, - 0x7b, 0x22, 0x5f, 0x33, 0xdc, 0xdc, 0x5e, 0x1e, - 0x53, 0x17, 0x97, 0x64, 0x34, 0x35, 0x90, 0xd3, - 0x0a, 0xf6, 0x88, 0xb9, 0x97, 0xe7, 0x95, 0x87, - 0x55, 0xe3, 0xc2, 0xdf, 0x08, 0xe9, 0xf6, 0x30, - 0x73, 0x56, 0x74, 0xff, 0xd9, 0x6f, 0xad, 0x1b, - 0xed, 0x6e, 0xd5, 0x9d, 0x3f, 0x72, 0x38, 0x21, - 0x09, 0xd2, 0x72, 0x32, 0x00, 0x84, 0xcf, 0xa7, - 0x96, 0xff, 0x0d, 0x5a, 0x94, 0xb6, 0x4a, 0x87, - 0xb3, 0x43, 0x76, 0x8e, 0x5e, 0x98, 0x59, 0x09, - 0x3b, 0x0e, 0xc1, 0x0b, 0x1a, 0x93, 0xdc, 0xef, - 0x4a, 0x47, 0x14, 0xfe, 0x76, 0x0a, 0x77, 0x21, - 0xa2, 0x35, 0x9f, 0x0a, 0x77, 0x21, 0xa2, 0xa5, - 0x9b, 0x90, 0xd1, 0x0d, 0x49, 0xd8, 0x7a, 0x3c, - 0x61, 0x3f, 0xf3, 0xb9, 0xee, 0xc1, 0x4e, 0xe4, - 0x34, 0x47, 0xd3, 0xf9, 0xd8, 0x29, 0xdc, 0x86, - 0x8b, 0x1e, 0x7e, 0xd5, 0x3f, 0x96, 0xeb, 0x9d, - 0x3f, 0xdf, 0xe0, 0x57, 0xaf, 0x7e, 0x3a, 0x78, - 0x0c, 0xf0, 0x9d, 0x3f, 0xff, 0xca, 0x3f, 0xcd, - 0xb2, 0xd6, 0xf2, 0x5b, 0xd7, 0xaf, 0x52, 0x74, - 0x79, 0x10, 0xf6, 0x21, 0x9d, 0xdc, 0x86, 0x8b, - 0x42, 0x7f, 0xdc, 0x16, 0xf4, 0xe1, 0xa3, 0x02, - 0x74, 0x85, 0x0f, 0x94, 0x49, 0xa7, 0xe6, 0xdb, - 0xef, 0xf5, 0x01, 0xd3, 0xe5, 0xbe, 0x59, 0x4e, - 0x9f, 0xfe, 0xcb, 0xd7, 0x1a, 0xb2, 0xb6, 0xcb, - 0x59, 0x4e, 0x8a, 0x0f, 0xd7, 0xe4, 0xb0, 0xa8, - 0xc9, 0xc8, 0x53, 0x4f, 0xfd, 0x95, 0xf6, 0x9f, - 0x78, 0xae, 0x6c, 0x74, 0xfa, 0xf6, 0xef, 0xeb, - 0x3a, 0x7f, 0xbf, 0xa5, 0x1b, 0x5b, 0x3e, 0xa7, - 0x4d, 0xfc, 0xc3, 0xe4, 0x42, 0x99, 0xfd, 0x96, - 0x6b, 0xd6, 0xf3, 0x0e, 0x9f, 0xcf, 0xc1, 0xaf, - 0xd6, 0x83, 0xa7, 0xb6, 0xcb, 0x71, 0xd3, 0xfd, - 0x98, 0x1c, 0x44, 0xc0, 0x9d, 0x18, 0x8b, 0xba, - 0x4d, 0x38, 0xca, 0xa4, 0x33, 0xd9, 0x6e, 0xb9, - 0xd3, 0xfb, 0x5f, 0xbf, 0xa3, 0xf2, 0xa7, 0x49, - 0xcd, 0xc5, 0xca, 0xb5, 0x31, 0xda, 0x17, 0xd9, - 0x08, 0x8b, 0x43, 0xbb, 0xe4, 0xd5, 0x85, 0x45, - 0xe1, 0xcd, 0xa8, 0x79, 0xbc, 0x82, 0x7e, 0x6f, - 0xf6, 0xdf, 0x56, 0x8e, 0x9e, 0xcb, 0x75, 0xce, - 0x96, 0xa9, 0x87, 0xa5, 0xf3, 0x29, 0xf0, 0xa7, - 0x72, 0x1a, 0x2d, 0x69, 0xff, 0x73, 0xdd, 0x82, - 0x9d, 0xc8, 0x68, 0xa0, 0xe4, 0xe6, 0xe2, 0x28, - 0xb0, 0xb1, 0x86, 0x13, 0xff, 0x9c, 0xce, 0x7b, - 0xb0, 0x53, 0xb9, 0x0d, 0x14, 0x5c, 0xfe, 0x76, - 0x0a, 0x77, 0x21, 0xa2, 0xea, 0x87, 0xb2, 0x14, - 0xd2, 0x11, 0x9e, 0x26, 0x62, 0x65, 0xa7, 0x39, - 0x81, 0x1d, 0x4d, 0x4e, 0x6e, 0xa5, 0x3e, 0x14, - 0xee, 0x43, 0x44, 0x43, 0x3a, 0xd9, 0xb1, 0xd2, - 0x76, 0x1e, 0x65, 0x26, 0x13, 0xf9, 0xd8, 0x29, - 0xdc, 0x86, 0x88, 0xda, 0x7f, 0x3b, 0x05, 0x3b, - 0x90, 0xd1, 0x4d, 0xcf, 0xe7, 0x60, 0xa7, 0x72, - 0x1a, 0x2a, 0x09, 0xfc, 0xec, 0x14, 0xee, 0x43, - 0x45, 0x4d, 0x3e, 0x14, 0xee, 0x43, 0x45, 0x61, - 0x3e, 0xf0, 0x76, 0xd0, 0x4e, 0x9f, 0xe7, 0xbb, - 0x05, 0x3b, 0x90, 0xd1, 0x1f, 0xce, 0xc5, 0xa0, - 0xe9, 0x3b, 0x11, 0x6a, 0x86, 0x1c, 0x53, 0xf4, - 0x19, 0xff, 0xce, 0x67, 0x3d, 0xd8, 0x29, 0xdc, - 0x86, 0x89, 0xbe, 0x7f, 0xe6, 0x73, 0xdd, 0x82, - 0x9d, 0xc8, 0x68, 0x9f, 0xa7, 0xe6, 0xeb, 0xaa, - 0xb5, 0x4d, 0x4c, 0xa4, 0xe9, 0xff, 0xe5, 0x55, - 0x55, 0x55, 0x55, 0x5a, 0x6a, 0x74, 0xf8, 0x7d, - 0x47, 0x30, 0xa9, 0x82, 0x12, 0xa3, 0x0d, 0xe8, - 0x49, 0xe5, 0xa1, 0x4e, 0x34, 0x10, 0xa8, 0xc7, - 0xac, 0x29, 0xa7, 0xe1, 0xe4, 0xb7, 0x84, 0xe9, - 0xea, 0x19, 0xe7, 0x9d, 0x3e, 0xaf, 0xf4, 0xe7, - 0x9d, 0x3f, 0xac, 0xac, 0x70, 0x00, 0xa7, 0x48, - 0x54, 0xff, 0x70, 0x8f, 0xc5, 0x13, 0x81, 0x5c, - 0x3a, 0x7f, 0xed, 0x96, 0xfd, 0x5c, 0xb5, 0xbc, - 0xc3, 0xa7, 0x5f, 0xcd, 0x1d, 0x0a, 0x7c, 0x35, - 0x44, 0x85, 0x4e, 0x77, 0xc4, 0xe3, 0x0a, 0x7f, - 0x99, 0x5d, 0xf2, 0x7b, 0x9a, 0xde, 0xc3, 0xa6, - 0xef, 0x9d, 0x02, 0x6e, 0x58, 0x49, 0x38, 0x21, - 0x09, 0xd3, 0xd4, 0x7f, 0xca, 0x53, 0x8b, 0xc9, - 0xea, 0x6f, 0xe4, 0x3a, 0x15, 0x11, 0xf6, 0x3c, - 0xd0, 0xc6, 0x7f, 0xea, 0x34, 0x06, 0x7f, 0x46, - 0x96, 0xfc, 0x74, 0xf2, 0xde, 0xa8, 0x68, 0x83, - 0xa7, 0xee, 0xb2, 0xea, 0xf4, 0x79, 0xd0, 0x28, - 0xa5, 0xba, 0x3b, 0x62, 0xd9, 0x87, 0x63, 0xa6, - 0x08, 0x4e, 0x87, 0x9a, 0xc0, 0x8b, 0x4f, 0x55, - 0x7c, 0xd8, 0xa7, 0x1a, 0x19, 0xf5, 0x77, 0xec, - 0x0a, 0x9d, 0x1e, 0x3d, 0xed, 0xe6, 0x73, 0x82, - 0x10, 0x95, 0x05, 0x38, 0xbc, 0x9e, 0xf0, 0x7c, - 0xd1, 0x50, 0x86, 0xf3, 0xc3, 0x31, 0xe4, 0xde, - 0x2d, 0x0d, 0xcf, 0xbe, 0x4e, 0xff, 0xaa, 0x74, - 0xf6, 0xdd, 0xf0, 0x1d, 0x3f, 0xf2, 0xfc, 0x1f, - 0x5a, 0x5a, 0x5f, 0x80, 0xe8, 0xf2, 0x20, 0xc0, - 0x39, 0x52, 0x29, 0xf9, 0xa5, 0xc1, 0xb7, 0x8e, - 0x9f, 0xaf, 0x8d, 0x7d, 0x58, 0x78, 0x80, 0xa7, - 0xdd, 0xfd, 0xac, 0xa6, 0x88, 0x09, 0xc6, 0xea, - 0x7c, 0xa0, 0xf5, 0x35, 0x3a, 0x7f, 0x0b, 0x55, - 0xf7, 0xef, 0xe3, 0xa7, 0xa9, 0x00, 0xa9, 0x53, - 0x04, 0x25, 0x42, 0x9b, 0x70, 0x90, 0xcf, 0xdc, - 0x2f, 0x7f, 0xfc, 0x53, 0x8d, 0x04, 0x2a, 0x79, - 0x38, 0x61, 0xe6, 0x6f, 0xa2, 0x5c, 0xa3, 0x48, - 0x45, 0xcf, 0xb4, 0xbe, 0x9d, 0xbc, 0xd1, 0x03, - 0xcf, 0xfa, 0xda, 0x50, 0xbb, 0x96, 0xeb, 0xb1, - 0xd3, 0xb8, 0x68, 0x3a, 0x60, 0x84, 0xe9, 0xfc, - 0x3e, 0xc6, 0xdb, 0xde, 0xe4, 0x36, 0x21, 0x1b, - 0x8d, 0x91, 0x7e, 0x2e, 0x73, 0xff, 0x7f, 0x35, - 0xdb, 0x9f, 0xbf, 0xa9, 0x53, 0xa1, 0x4f, 0xab, - 0x08, 0xe7, 0xff, 0x63, 0x19, 0xcf, 0xc1, 0x4e, - 0xe4, 0x34, 0x43, 0x11, 0x63, 0xf1, 0xf9, 0x04, - 0xfd, 0x82, 0x9d, 0xc8, 0x68, 0x82, 0xa7, 0xaf, - 0x55, 0x01, 0x53, 0xb8, 0x68, 0x2a, 0x7b, 0x4f, - 0xf6, 0xb2, 0xa7, 0xf7, 0xa8, 0xcb, 0xd5, 0x40, - 0x54, 0x15, 0x3f, 0x62, 0x2d, 0x95, 0x85, 0x4c, - 0x10, 0x95, 0x3f, 0x7d, 0x7f, 0x47, 0x84, 0xa8, - 0xc4, 0xc2, 0x90, 0x85, 0x83, 0x76, 0x24, 0x01, - 0x9f, 0xc2, 0x82, 0x55, 0xbc, 0x5a, 0x6f, 0x09, - 0x4e, 0x3f, 0x29, 0x73, 0xd3, 0xd7, 0xb4, 0x76, - 0x33, 0xfe, 0x57, 0xf7, 0xf2, 0xa2, 0xb5, 0x3a, - 0x7f, 0xaf, 0x65, 0x6d, 0xb2, 0x81, 0x4e, 0x9f, - 0xe5, 0xa5, 0xfa, 0x8a, 0x98, 0xc3, 0xa1, 0x4f, - 0xd2, 0xc7, 0x53, 0xff, 0xf3, 0x5d, 0xcf, 0xeb, - 0x6e, 0xca, 0x34, 0xbe, 0x9d, 0xbc, 0xd1, 0x7d, - 0xcf, 0xbd, 0x7f, 0xe3, 0xce, 0x9f, 0xf7, 0xfc, - 0xfb, 0xdb, 0x07, 0xd5, 0x3a, 0x7f, 0x5c, 0x1b, - 0xfe, 0xfb, 0x61, 0xe2, 0x01, 0x9d, 0xc2, 0xf3, - 0xc4, 0x03, 0x18, 0x7d, 0x3a, 0x21, 0x4d, 0xcf, - 0x3c, 0x40, 0x33, 0xdd, 0xfa, 0x5e, 0x78, 0x80, - 0x67, 0xf7, 0x92, 0xd8, 0x00, 0x29, 0xe2, 0x01, - 0x9d, 0xef, 0xec, 0x78, 0x80, 0x63, 0x64, 0x5c, - 0xb0, 0x8a, 0xc5, 0xed, 0x8f, 0xa7, 0x0a, 0xdc, - 0xf1, 0x00, 0xc1, 0xe2, 0x01, 0x99, 0x58, 0x78, - 0x80, 0x63, 0x63, 0x73, 0xe1, 0x79, 0xef, 0x33, - 0x65, 0x3c, 0x40, 0x33, 0xaf, 0xc8, 0x78, 0x80, - 0x67, 0xfd, 0xfe, 0x7b, 0xad, 0xe4, 0xe1, 0x3c, - 0x40, 0x33, 0x76, 0xc7, 0x88, 0x06, 0x7f, 0x7f, - 0x83, 0x5a, 0xa8, 0x0f, 0x10, 0x0c, 0xfb, 0xda, - 0xfb, 0xe0, 0x3c, 0x40, 0x33, 0x7a, 0xa7, 0x88, - 0x06, 0x04, 0xf6, 0x6e, 0x6d, 0x3e, 0xbf, 0xd6, - 0x97, 0x9a, 0x20, 0x19, 0x80, 0xa7, 0x88, 0x05, - 0xc6, 0xd6, 0x7d, 0xe5, 0x67, 0x6c, 0x78, 0x80, - 0x67, 0xb4, 0xef, 0xa1, 0xe2, 0x01, 0x9c, 0xa2, - 0x87, 0x88, 0x06, 0x7f, 0xd9, 0x4d, 0x76, 0x5c, - 0xf8, 0x2a, 0x78, 0x80, 0x67, 0xda, 0x73, 0xde, - 0xa7, 0x88, 0x06, 0x31, 0x10, 0x16, 0x4c, 0x98, - 0x40, 0x78, 0x80, 0x61, 0xea, 0xa3, 0xf6, 0x23, - 0xc8, 0x4c, 0x79, 0x5a, 0xc6, 0x40, 0x34, 0xa9, - 0x75, 0xe1, 0x4d, 0xa1, 0x14, 0xfb, 0x2f, 0x5e, - 0xa4, 0xf1, 0x00, 0xcf, 0xed, 0x95, 0x1a, 0x15, - 0xb9, 0xe2, 0x01, 0xd8, 0xda, 0x4e, 0x15, 0x09, - 0xe2, 0x01, 0x84, 0x3f, 0x71, 0x50, 0x9e, 0xf7, - 0xdf, 0x53, 0xc4, 0x03, 0x3f, 0x73, 0x4f, 0xca, - 0x5e, 0x78, 0x80, 0x63, 0x11, 0x14, 0x02, 0x0d, - 0x0b, 0xe7, 0xfa, 0xcb, 0x57, 0x57, 0x41, 0x01, - 0xe2, 0x01, 0x97, 0x8f, 0x10, 0x0c, 0xdd, 0x46, - 0xc7, 0xc7, 0x64, 0x69, 0x84, 0x07, 0x88, 0x06, - 0x7d, 0xd7, 0xaf, 0xa9, 0x3c, 0x40, 0x33, 0xf7, - 0xbf, 0xa3, 0xf2, 0xa7, 0x88, 0x06, 0x15, 0x12, - 0x5f, 0x22, 0xb9, 0xac, 0x6c, 0xc8, 0x0e, 0xc8, - 0x6d, 0xb4, 0x80, 0x2c, 0x16, 0x22, 0xe2, 0xf0, - 0x13, 0xd6, 0x56, 0xc5, 0xe3, 0xc0, 0xd4, 0x84, - 0x9e, 0xf8, 0x60, 0xce, 0xee, 0x43, 0x44, 0x02, - 0xe4, 0x5e, 0x4f, 0x6b, 0xd5, 0x6d, 0x98, 0x74, - 0xc0, 0x52, 0xa5, 0xac, 0xa9, 0xd6, 0x5a, 0x0e, - 0x98, 0x21, 0x2a, 0x3c, 0x7b, 0x3a, 0xb1, 0x56, - 0x84, 0x82, 0x39, 0x39, 0x9e, 0xa9, 0x4e, 0x3c, - 0x19, 0xeb, 0xd5, 0xbf, 0x43, 0xa2, 0x86, 0x57, - 0x4b, 0xc9, 0xd2, 0x92, 0xe3, 0xe3, 0x80, 0x43, - 0x1b, 0x79, 0x6c, 0xfc, 0x2d, 0x3d, 0x46, 0x83, - 0xa7, 0xfc, 0xfa, 0xee, 0xfe, 0x31, 0x47, 0x59, - 0xd3, 0xff, 0x0d, 0xbb, 0x1e, 0xa2, 0x0d, 0xec, - 0x3a, 0x7e, 0xde, 0x11, 0xf6, 0x58, 0xa9, 0xe6, - 0xad, 0xcd, 0x1d, 0x3e, 0xc6, 0x9e, 0xb4, 0x9d, - 0x3d, 0x9f, 0x57, 0x95, 0x02, 0x7d, 0x7f, 0x23, - 0x6f, 0x28, 0x85, 0x4d, 0xef, 0x0b, 0x7c, 0x83, - 0x64, 0x3e, 0x84, 0xcc, 0xf7, 0xbf, 0x7a, 0x9d, - 0x3b, 0x46, 0xee, 0xa7, 0x4f, 0xfc, 0x06, 0x62, - 0x5b, 0xdb, 0x5b, 0x4d, 0x67, 0x4f, 0xfd, 0x7a, - 0xe6, 0xcb, 0x4d, 0x72, 0x9b, 0x9d, 0x3f, 0x9f, - 0x95, 0xdf, 0x5b, 0xe1, 0xd0, 0xa8, 0xe4, 0xf1, - 0x17, 0x24, 0x68, 0x8d, 0x3e, 0xe6, 0xdb, 0x6c, - 0xa7, 0x4d, 0x5a, 0x9d, 0x38, 0x21, 0x09, 0xd3, - 0x03, 0x8a, 0x71, 0x79, 0x02, 0x7a, 0xd5, 0x32, - 0x9d, 0xc0, 0xc2, 0xa1, 0xe8, 0xbb, 0x64, 0x20, - 0x35, 0x08, 0x67, 0xf5, 0xff, 0x94, 0x83, 0x7b, - 0xce, 0x9f, 0xef, 0xe3, 0x6c, 0x70, 0x42, 0x12, - 0xa7, 0x33, 0xae, 0x74, 0x58, 0xf5, 0x5b, 0x1d, - 0x42, 0xa3, 0x9f, 0xc7, 0x1d, 0x08, 0xb9, 0xad, - 0xc7, 0x4c, 0xd5, 0x4e, 0x9f, 0x9d, 0x8d, 0xbd, - 0xfd, 0xb0, 0xd6, 0x6f, 0x15, 0x9e, 0xdb, 0x2d, - 0xc7, 0x4f, 0xfc, 0xb9, 0xf7, 0xdb, 0x3f, 0xf5, - 0x09, 0xd2, 0xf0, 0xa2, 0xaf, 0xe9, 0x17, 0x21, - 0x8a, 0x1b, 0x67, 0x77, 0xc7, 0xa5, 0xb4, 0x7f, - 0x3a, 0xcb, 0xd2, 0x15, 0xfe, 0x21, 0x1a, 0x5a, - 0xbd, 0xa3, 0xe6, 0x02, 0x6f, 0xe3, 0x06, 0xbc, - 0x7b, 0xcd, 0xa3, 0x0a, 0x9f, 0xec, 0x1f, 0xad, - 0xdf, 0x96, 0x3a, 0x7e, 0xf8, 0x33, 0x6e, 0x79, - 0xd3, 0xed, 0xed, 0x3d, 0x75, 0x65, 0x42, 0xa2, - 0x47, 0x0d, 0xb7, 0x96, 0xcf, 0xfe, 0x1d, 0x7f, - 0xd3, 0xb7, 0xe5, 0x7c, 0xbf, 0x3a, 0x5b, 0xcd, - 0x10, 0x2c, 0xae, 0x6a, 0x05, 0x25, 0xeb, 0x9b, - 0xc0, 0x8f, 0xcf, 0xfb, 0xda, 0x96, 0xf6, 0xf7, - 0x00, 0x0a, 0x54, 0xff, 0xeb, 0xd7, 0xa9, 0xdc, - 0xc5, 0xcf, 0xdf, 0x50, 0xe8, 0x54, 0x49, 0xf9, - 0x12, 0x75, 0x1f, 0x79, 0xd0, 0xa9, 0xc1, 0x32, - 0x11, 0x36, 0x85, 0xd6, 0x84, 0x53, 0xf9, 0xed, - 0x67, 0xfa, 0xd8, 0x74, 0xff, 0xe1, 0x4f, 0xe3, - 0x6f, 0xe2, 0x8a, 0xd4, 0xe9, 0xfd, 0xcd, 0x5b, - 0x19, 0x95, 0x3a, 0x30, 0xfe, 0x5b, 0x23, 0xcf, - 0xde, 0xd7, 0x7c, 0xc6, 0x1d, 0x3e, 0xcd, 0xbc, - 0x35, 0x3a, 0x7f, 0xfa, 0xb7, 0xa5, 0x3d, 0x6d, - 0xcd, 0xf9, 0x6f, 0x30, 0xe9, 0x2d, 0x07, 0xfa, - 0x12, 0x78, 0xf2, 0x3d, 0xac, 0x47, 0x78, 0x55, - 0xce, 0xdb, 0x84, 0xe9, 0xf0, 0x3b, 0xf8, 0xde, - 0x74, 0x29, 0xe2, 0x68, 0x37, 0x3c, 0xac, 0xa5, - 0xa3, 0xa1, 0x55, 0x2f, 0xbe, 0x3a, 0x1a, 0xbc, - 0xdc, 0x8a, 0x78, 0x1d, 0x4d, 0x4e, 0x98, 0x21, - 0x3a, 0x2a, 0x6e, 0x02, 0x45, 0x3f, 0x57, 0xd6, - 0xec, 0xb1, 0x4e, 0x34, 0x33, 0x82, 0x10, 0x95, - 0x3c, 0xfb, 0xe2, 0x14, 0xe2, 0xf2, 0x7d, 0x9b, - 0x78, 0x15, 0x3a, 0x5c, 0x27, 0xaf, 0xf2, 0xf9, - 0xfb, 0xf4, 0xd5, 0xac, 0xb9, 0xd3, 0xea, 0x35, - 0x7e, 0xca, 0x4e, 0x9f, 0x7a, 0xab, 0x43, 0x0f, - 0x97, 0xec, 0xf9, 0x78, 0x41, 0xa1, 0xf2, 0xfd, - 0x9b, 0x9e, 0x7c, 0xbf, 0x67, 0xb4, 0x7e, 0x54, - 0xf9, 0x7e, 0xc6, 0xc7, 0xa3, 0xf2, 0x29, 0xf2, - 0xe5, 0x73, 0xe7, 0xcb, 0xf6, 0x0f, 0x97, 0xec, - 0xdd, 0x73, 0xe5, 0xfa, 0xc2, 0xde, 0x4f, 0xf9, - 0xfd, 0x68, 0x93, 0x3d, 0x9a, 0x9e, 0x01, 0xf2, - 0xfd, 0x83, 0xe5, 0xfb, 0x30, 0x14, 0xf9, 0x7e, - 0xcf, 0xf6, 0x03, 0x87, 0x1b, 0x66, 0xc7, 0xcb, - 0xf6, 0x7e, 0xcb, 0x7a, 0xba, 0x50, 0x7c, 0xbf, - 0x60, 0x08, 0xa3, 0xf9, 0x15, 0x51, 0x67, 0x86, - 0x85, 0xb9, 0xf2, 0xfd, 0x83, 0xe5, 0xfb, 0x86, - 0xba, 0x60, 0x84, 0xf9, 0x7e, 0xc3, 0xd5, 0x88, - 0xec, 0x6b, 0x90, 0x84, 0xa6, 0x13, 0xa2, 0x52, - 0xc3, 0x1a, 0xc2, 0xea, 0xeb, 0xc1, 0x26, 0x9e, - 0xc7, 0xae, 0xb2, 0xe5, 0xfa, 0xe4, 0x47, 0xcf, - 0xfb, 0x13, 0x6c, 0x10, 0xf5, 0x9a, 0x3a, 0x67, - 0xd0, 0x54, 0x50, 0x89, 0x5a, 0x50, 0x7e, 0x7b, - 0x02, 0xb9, 0x29, 0xd3, 0x8c, 0x53, 0xff, 0xf5, - 0x2f, 0x15, 0x6d, 0xcd, 0x2e, 0xd6, 0x56, 0x73, - 0x47, 0x42, 0xae, 0xb9, 0xe1, 0x37, 0x4e, 0x61, - 0xe8, 0x59, 0x3f, 0xed, 0x6b, 0x4f, 0xd6, 0xd6, - 0xd1, 0x0e, 0x9f, 0xbf, 0xda, 0xed, 0xcf, 0x3a, - 0x70, 0x42, 0x12, 0xa7, 0x6f, 0x50, 0x14, 0xe2, - 0xf2, 0x7f, 0xdf, 0xe7, 0xef, 0x1c, 0x6d, 0xc2, - 0x74, 0xff, 0x7f, 0x81, 0xb9, 0xea, 0x0a, 0x4e, - 0x8d, 0x93, 0x31, 0x62, 0x08, 0x12, 0xbe, 0x59, - 0x73, 0xf9, 0xff, 0x7f, 0xfe, 0xd1, 0xbb, 0xae, - 0xa2, 0xdc, 0xe9, 0xc1, 0x08, 0x4b, 0x10, 0x84, - 0xf8, 0x53, 0xb9, 0x0b, 0x10, 0x83, 0x8d, 0x4c, - 0xe0, 0x84, 0x25, 0x88, 0x3e, 0x0b, 0x10, 0x7b, - 0x8d, 0x4c, 0xca, 0xcc, 0x44, 0x82, 0x34, 0xcf, - 0xae, 0xb7, 0x56, 0x1d, 0x3d, 0xff, 0x2e, 0xb3, - 0xa7, 0x6f, 0x50, 0x1d, 0x14, 0x1e, 0x03, 0x08, - 0xe7, 0xc8, 0xb6, 0x56, 0x15, 0x3e, 0xff, 0x7f, - 0xf8, 0x54, 0xd8, 0x85, 0x4c, 0x10, 0x95, 0x18, - 0x7e, 0xb5, 0x25, 0xb9, 0x30, 0x45, 0x27, 0xf7, - 0xf7, 0xa8, 0x2d, 0x8d, 0xe5, 0x38, 0xdd, 0xc2, - 0xa7, 0x01, 0xe6, 0x6e, 0x86, 0x9c, 0xff, 0xeb, - 0x28, 0x2b, 0x99, 0xb7, 0x7f, 0xb5, 0x9d, 0x3f, - 0xab, 0xbd, 0x95, 0x0f, 0x52, 0x74, 0x2a, 0xba, - 0xdc, 0x4c, 0xf4, 0x60, 0x23, 0x1d, 0x67, 0x1a, - 0x55, 0x2a, 0x70, 0x42, 0x12, 0xa7, 0xcf, 0x07, - 0x7f, 0x62, 0x9c, 0x5e, 0x4f, 0xff, 0x7e, 0x9d, - 0xd4, 0x81, 0x7e, 0x9d, 0x47, 0xf8, 0xe9, 0xff, - 0xec, 0x57, 0x6d, 0xf5, 0xd1, 0x33, 0x5f, 0x84, - 0xe9, 0xe6, 0xfb, 0x01, 0xb1, 0xd0, 0xf3, 0xf7, - 0xe5, 0x09, 0xff, 0xcf, 0xc1, 0x03, 0x3d, 0x5d, - 0xf5, 0x5c, 0x3a, 0x7b, 0xdb, 0x63, 0x0e, 0x85, - 0x4e, 0x39, 0xe6, 0xfd, 0x0c, 0xaf, 0x90, 0xe8, - 0x95, 0x3f, 0x6a, 0xeb, 0xef, 0xd2, 0xf3, 0xa7, - 0xfd, 0xc3, 0xa9, 0xeb, 0x67, 0x52, 0x03, 0xa7, - 0xfd, 0x5a, 0xa8, 0xdd, 0x5d, 0xb7, 0x8e, 0x9f, - 0xf7, 0xf9, 0xab, 0x70, 0x8f, 0xb6, 0x3a, 0x31, - 0x1d, 0xe8, 0x67, 0xe4, 0x06, 0x1f, 0x4f, 0x3f, - 0x7e, 0x34, 0x74, 0xf8, 0x76, 0xcc, 0xf9, 0xd3, - 0xff, 0x6a, 0xfd, 0x96, 0x56, 0xd5, 0xb2, 0xd2, - 0x74, 0x71, 0xf8, 0x54, 0x9a, 0x7f, 0xfb, 0x2f, - 0x5c, 0x6a, 0xca, 0xdb, 0x2d, 0x65, 0x3a, 0x7f, - 0x5d, 0xba, 0xb5, 0xf6, 0xe9, 0x46, 0x87, 0x46, - 0xc8, 0xb4, 0xf9, 0x0d, 0xd4, 0x27, 0xff, 0xbd, - 0x5c, 0xda, 0xf5, 0xf6, 0xbb, 0x77, 0xfc, 0x74, - 0xff, 0xfe, 0xfd, 0xf2, 0xde, 0x5b, 0xf8, 0x0a, - 0xf7, 0x04, 0x21, 0x2a, 0x7b, 0x6c, 0xcd, 0x65, - 0x4e, 0x7f, 0xf4, 0x34, 0x43, 0x33, 0x82, 0x10, - 0x95, 0x3b, 0x3e, 0x85, 0x38, 0xbc, 0x9f, 0xf6, - 0x51, 0x9b, 0x73, 0xfe, 0xb4, 0x1d, 0x00, 0x3e, - 0x8f, 0x94, 0xcf, 0xe7, 0xff, 0x2f, 0x7d, 0x28, - 0x3a, 0x15, 0x38, 0x9a, 0x18, 0x69, 0x23, 0x18, - 0x57, 0x71, 0x14, 0xf7, 0xaf, 0xe5, 0x3a, 0x7f, - 0x69, 0x82, 0x00, 0x7b, 0xe7, 0x4f, 0xff, 0x95, - 0xfe, 0xda, 0xdb, 0xeb, 0x82, 0x9d, 0xc8, 0x68, - 0x83, 0x22, 0xc8, 0x94, 0xb9, 0x9c, 0xfe, 0xd4, - 0xca, 0xea, 0x60, 0xa9, 0xd0, 0xf4, 0xc3, 0xbd, - 0x0b, 0x5b, 0x91, 0xcf, 0xff, 0x2f, 0xe8, 0xdc, - 0xd7, 0xbf, 0x9b, 0x67, 0xf5, 0x9d, 0x39, 0x45, - 0xa3, 0xa1, 0x57, 0x65, 0x36, 0x3b, 0xc8, 0xd2, - 0x10, 0xc7, 0xd1, 0xee, 0x74, 0x6b, 0xba, 0x1b, - 0x6a, 0x2b, 0x4f, 0xef, 0xfd, 0x5e, 0xc5, 0xf1, - 0xd3, 0xfa, 0xfe, 0x6d, 0xeb, 0xbd, 0x4a, 0x93, - 0x0e, 0x9f, 0xb3, 0xfa, 0xc5, 0x5d, 0xe3, 0xc5, - 0xde, 0x6b, 0x1e, 0x45, 0xf5, 0x5d, 0xe7, 0x87, - 0x4a, 0x5e, 0x74, 0xfe, 0xfb, 0xff, 0x8c, 0x54, - 0x3a, 0x73, 0x36, 0xc3, 0xa1, 0x4f, 0xc3, 0x08, - 0xf8, 0xc6, 0x75, 0xfd, 0x41, 0xd3, 0xfe, 0xd2, - 0xf5, 0xde, 0x20, 0x67, 0xaa, 0x74, 0xff, 0xcb, - 0xfe, 0xb2, 0x81, 0xc3, 0x5a, 0x95, 0x1b, 0x22, - 0x11, 0x88, 0x53, 0xeb, 0x7b, 0xd4, 0xd4, 0xe8, - 0x2a, 0x7e, 0xae, 0xf5, 0x15, 0x61, 0x50, 0x54, - 0x15, 0x05, 0x41, 0x50, 0xf3, 0xdf, 0xf0, 0x50, - 0x0b, 0x74, 0x0a, 0xd4, 0x0a, 0x6f, 0x0a, 0x9a, - 0xd8, 0x54, 0xfd, 0xdd, 0x76, 0x95, 0x85, 0x6e, - 0x2d, 0x64, 0xdd, 0x95, 0x05, 0x41, 0x50, 0xf2, - 0xd3, 0xc1, 0x50, 0x54, 0x15, 0x05, 0x41, 0x50, - 0x54, 0x15, 0x14, 0x1b, 0xcd, 0x82, 0xbc, 0x14, - 0x00, 0xaa, 0x85, 0x36, 0x0a, 0x82, 0xa0, 0xa8, - 0x79, 0x69, 0x50, 0xa8, 0x2a, 0x0a, 0x82, 0xa0, - 0xa8, 0x79, 0xa8, 0x00, 0x55, 0xc2, 0x9b, 0xc2, - 0xa0, 0xa8, 0x2a, 0x0a, 0x82, 0xa2, 0x83, 0x51, - 0xac, 0x28, 0x42, 0xac, 0x15, 0x2d, 0x65, 0x41, - 0x50, 0x54, 0x15, 0x05, 0x46, 0xc6, 0xa2, 0x90, - 0xa0, 0x05, 0x68, 0x15, 0x05, 0x41, 0x50, 0x54, - 0xfa, 0xca, 0x0a, 0xe1, 0x50, 0x54, 0x3c, 0xf3, - 0x90, 0x2a, 0xc1, 0x5c, 0x14, 0x02, 0x69, 0x21, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x50, 0xf3, 0x51, - 0x48, 0x57, 0x82, 0x9b, 0x05, 0x41, 0x50, 0x54, - 0x15, 0x05, 0x43, 0xcd, 0x46, 0xc1, 0x56, 0x0a, - 0xf8, 0x54, 0xac, 0x54, 0x15, 0x05, 0x49, 0xe5, - 0x41, 0x54, 0x96, 0x10, 0x54, 0x15, 0x05, 0x41, - 0x51, 0x41, 0xf3, 0x3c, 0x2b, 0x58, 0xd2, 0x0d, - 0x34, 0x14, 0x00, 0xab, 0x85, 0x4b, 0x0a, 0x82, - 0xa0, 0xa9, 0x3c, 0xa8, 0x2a, 0x92, 0xc2, 0x0a, - 0x82, 0xa1, 0x4f, 0x49, 0xe1, 0x5e, 0x1a, 0x11, - 0xa6, 0x05, 0x41, 0x50, 0x54, 0x15, 0x05, 0x41, - 0x50, 0xa6, 0xca, 0x90, 0xa1, 0x0a, 0x60, 0x57, - 0xc2, 0xa0, 0xa8, 0x2a, 0x0a, 0x81, 0x2f, 0xaa, - 0x15, 0x70, 0xa8, 0x2a, 0x0a, 0x82, 0xa1, 0x85, - 0xf7, 0xc2, 0xae, 0x15, 0x26, 0x15, 0x05, 0x41, - 0x50, 0x02, 0xd3, 0x40, 0xa8, 0x2a, 0x0a, 0x82, - 0xa0, 0xa8, 0x53, 0x50, 0xd0, 0x55, 0x82, 0xb4, - 0x0a, 0x85, 0x5f, 0xad, 0xa1, 0xc9, 0xe7, 0xbb, - 0x14, 0xe2, 0xe2, 0x36, 0xd2, 0x91, 0xe6, 0x6d, - 0x59, 0xeb, 0x47, 0x43, 0x08, 0xc6, 0x23, 0xd8, - 0xdf, 0x99, 0x80, 0x79, 0xf7, 0x6a, 0xb3, 0x5d, - 0x87, 0x47, 0x96, 0xcc, 0xa1, 0x4a, 0xd4, 0x2d, - 0xde, 0x48, 0xde, 0x93, 0x3e, 0x7f, 0xd7, 0x2a, - 0x53, 0x93, 0x57, 0x9d, 0xe5, 0x61, 0x53, 0xde, - 0x4e, 0x13, 0xa7, 0x7b, 0x6c, 0x3a, 0x72, 0xef, - 0x75, 0x28, 0x8f, 0x13, 0x9b, 0x0d, 0xdc, 0x7e, - 0x7f, 0x5b, 0xc1, 0xd8, 0x56, 0x83, 0xa2, 0x94, - 0x42, 0x34, 0xa1, 0x30, 0x14, 0xe9, 0xf8, 0x7b, - 0xfb, 0xd4, 0x07, 0x4d, 0xc2, 0x74, 0xbc, 0x72, - 0x16, 0x92, 0x43, 0xa4, 0xc3, 0xa7, 0x66, 0xa3, - 0xbc, 0x89, 0x31, 0x15, 0xb2, 0x0f, 0x0f, 0x6f, - 0x0f, 0x9f, 0xff, 0x68, 0xbb, 0xb8, 0x47, 0x80, - 0xae, 0x08, 0x42, 0x74, 0x3d, 0x9b, 0x1d, 0xb4, - 0x24, 0xd0, 0xb3, 0xd0, 0xa1, 0x68, 0x93, 0xa9, - 0x53, 0xff, 0x85, 0xc5, 0xd7, 0xa7, 0xf2, 0xfe, - 0xfd, 0xfd, 0x30, 0xe9, 0xf9, 0xff, 0xf3, 0xed, - 0x87, 0x4f, 0xa8, 0xf0, 0xab, 0xce, 0x81, 0x3d, - 0x4f, 0x96, 0xcf, 0xfa, 0xca, 0x20, 0xfe, 0x07, - 0x80, 0x74, 0xec, 0xc6, 0x8e, 0x96, 0x58, 0xf5, - 0xc0, 0x79, 0x3f, 0x57, 0x1b, 0x7d, 0x69, 0x3a, - 0x79, 0x7f, 0x74, 0x3a, 0x4b, 0x88, 0xee, 0xfb, - 0xc5, 0xc9, 0xf5, 0x0b, 0xe7, 0xb4, 0xe1, 0x79, - 0xd3, 0xcb, 0xa3, 0x77, 0x52, 0xa7, 0xcf, 0x70, - 0x42, 0x13, 0xa3, 0xe7, 0x9f, 0xa1, 0x3c, 0x6c, - 0x89, 0x3c, 0x70, 0x86, 0x2a, 0x6e, 0x04, 0x6e, - 0x9a, 0x43, 0x5e, 0x7e, 0x1d, 0x01, 0x9f, 0x43, - 0xa7, 0xfa, 0x8f, 0xe3, 0xed, 0x80, 0xa9, 0xd3, - 0xdb, 0x6c, 0xad, 0x8e, 0x9f, 0xff, 0x2d, 0x95, - 0x88, 0xb7, 0xc1, 0x4e, 0xe4, 0x34, 0x5f, 0x13, - 0xf8, 0x3d, 0x7a, 0xae, 0x50, 0x74, 0xff, 0xfd, - 0x97, 0xde, 0xf5, 0x6d, 0xef, 0x80, 0x7c, 0xac, - 0xc2, 0xa6, 0xbe, 0x1d, 0x3e, 0x1f, 0x51, 0xcc, - 0x34, 0xc2, 0x73, 0xd7, 0xd1, 0x78, 0xd3, 0x09, - 0xcc, 0x05, 0x35, 0x02, 0x73, 0xfb, 0xfd, 0xae, - 0xea, 0x20, 0x35, 0x02, 0x73, 0xfa, 0xb9, 0x6f, - 0x57, 0x4a, 0x0d, 0x30, 0x9c, 0xd9, 0xb1, 0xa6, - 0x13, 0x98, 0x21, 0x3c, 0xc2, 0x71, 0x89, 0xa6, - 0x52, 0x69, 0xe2, 0xe6, 0x11, 0x55, 0x01, 0xb2, - 0x10, 0x48, 0xe5, 0x72, 0xcc, 0x26, 0xe3, 0xe7, - 0x97, 0xa9, 0x4f, 0xd8, 0x31, 0xe4, 0x45, 0x55, - 0x42, 0x5e, 0x52, 0x9c, 0x2a, 0xb9, 0xbd, 0x8e, - 0x50, 0x96, 0xcb, 0x77, 0x95, 0x6d, 0x3e, 0xdb, - 0x07, 0xda, 0xce, 0x9f, 0x7f, 0x37, 0xe3, 0x47, - 0x4f, 0xf5, 0xb9, 0x96, 0xf2, 0xd2, 0xf3, 0xa7, - 0xed, 0xff, 0x7e, 0xa7, 0x50, 0x74, 0x78, 0xfb, - 0x00, 0x73, 0x1f, 0x45, 0xbd, 0xe1, 0x2b, 0x0a, - 0xba, 0x43, 0x92, 0xf1, 0xfc, 0xa5, 0xd0, 0xe0, - 0x9f, 0xfc, 0xcb, 0xd7, 0xdf, 0xa5, 0xf7, 0xf5, - 0x7c, 0x74, 0xff, 0xfe, 0xfe, 0x53, 0x95, 0xef, - 0xdb, 0xcb, 0x7b, 0x69, 0xcc, 0x3a, 0x7e, 0xfe, - 0x8c, 0xf5, 0x94, 0xe9, 0xff, 0xf7, 0x7e, 0xfd, - 0xd6, 0xfa, 0xde, 0xa1, 0xcb, 0x1d, 0x38, 0x6b, - 0x53, 0xc4, 0x07, 0x3f, 0xfd, 0xbc, 0x56, 0xcb, - 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x01, 0xb8, 0xd4, - 0x40, 0x11, 0xcb, 0x77, 0xb8, 0xc4, 0xcd, 0xbd, - 0x18, 0x9c, 0xff, 0xfe, 0xe7, 0xe5, 0x7c, 0x23, - 0xe6, 0x6f, 0xbd, 0xb0, 0x37, 0x3a, 0x7f, 0xff, - 0xb9, 0xf9, 0xf6, 0x9f, 0x94, 0xbf, 0x7a, 0x81, - 0xc1, 0x08, 0x4a, 0x8b, 0x23, 0x2f, 0xec, 0x33, - 0xf9, 0x70, 0x53, 0xb9, 0x0d, 0x10, 0x4c, 0xff, - 0x2d, 0xf0, 0x53, 0xb9, 0x0d, 0x17, 0xcc, 0xfb, - 0xcd, 0xbf, 0x95, 0xd8, 0xfe, 0x90, 0xea, 0x7f, - 0xd4, 0x75, 0x3a, 0x99, 0x7f, 0xf3, 0x63, 0xa7, - 0x04, 0x21, 0x2a, 0x7e, 0xbd, 0xb1, 0x38, 0x4a, - 0x71, 0x79, 0x14, 0x22, 0x67, 0xec, 0x13, 0xff, - 0x50, 0xc5, 0xa6, 0xbf, 0xca, 0x37, 0xbc, 0xe9, - 0xff, 0xed, 0xf4, 0xff, 0x72, 0x5b, 0xc0, 0x02, - 0x8b, 0xce, 0x9c, 0x10, 0x84, 0xa9, 0xfd, 0xbc, - 0x0b, 0xfc, 0xa6, 0xe5, 0x38, 0xbc, 0x9f, 0xff, - 0x9b, 0xbd, 0xc9, 0x6f, 0x6d, 0xb6, 0x07, 0x75, - 0xeb, 0x94, 0x1d, 0x2e, 0xb2, 0x2a, 0xf4, 0x44, - 0x87, 0xaa, 0x8b, 0xf4, 0x36, 0x84, 0x91, 0x89, - 0x37, 0x8c, 0x6e, 0x7f, 0xd9, 0x75, 0x1a, 0xd9, - 0x7f, 0x73, 0xc4, 0x11, 0x3f, 0x97, 0x05, 0x3b, - 0x90, 0xd1, 0x04, 0x38, 0xf2, 0x67, 0xee, 0xb0, - 0x8f, 0x82, 0x74, 0xfd, 0xab, 0xd8, 0x3d, 0xf7, - 0x9d, 0x1f, 0x3d, 0xdd, 0x0a, 0xe0, 0x08, 0xcb, - 0xfc, 0x2a, 0x27, 0xf9, 0x7f, 0x7d, 0xf9, 0x6e, - 0xa4, 0xe9, 0xff, 0xee, 0x0d, 0xf2, 0xcb, 0xeb, - 0xff, 0x2d, 0xe3, 0xa1, 0xe8, 0x88, 0xf9, 0xd4, - 0xfe, 0xbe, 0xf6, 0x54, 0x3d, 0x49, 0xd3, 0xde, - 0x6b, 0xc1, 0x3a, 0x7f, 0xfd, 0xa5, 0x1f, 0x5c, - 0xb3, 0x96, 0xf9, 0x6d, 0x04, 0xe9, 0xf6, 0x5e, - 0xbb, 0xeb, 0xb1, 0xfc, 0xef, 0x23, 0x9f, 0x92, - 0xde, 0xd4, 0xf6, 0xb3, 0xa7, 0xe6, 0x2e, 0xae, - 0xd9, 0x41, 0xd3, 0xff, 0xff, 0xde, 0xbf, 0x33, - 0xbe, 0x07, 0xfa, 0xba, 0x32, 0xde, 0x5b, 0xdb, - 0x4e, 0x61, 0xd1, 0xb2, 0x38, 0xfc, 0x65, 0xc6, - 0x33, 0x87, 0x7d, 0x4e, 0x9f, 0xf7, 0x7d, 0x30, - 0x53, 0xb9, 0x0d, 0x10, 0x8c, 0x29, 0xf1, 0x68, - 0x3b, 0x3f, 0xf9, 0x72, 0x9d, 0xf7, 0xf7, 0xf4, - 0x7e, 0x54, 0xe9, 0xff, 0xee, 0xea, 0x37, 0xe5, - 0xb9, 0x8e, 0x08, 0x42, 0x74, 0xb3, 0x64, 0x4f, - 0x34, 0x9b, 0x38, 0x21, 0x09, 0x53, 0xfd, 0x80, - 0xe1, 0xc6, 0xd9, 0xb1, 0x4e, 0x2f, 0x26, 0x08, - 0x4a, 0x9c, 0x10, 0x84, 0xa9, 0xfb, 0xa8, 0xda, - 0xcb, 0x52, 0x9c, 0x5e, 0x47, 0xd1, 0x6c, 0x14, - 0x7d, 0x43, 0x29, 0xf2, 0x7b, 0x6d, 0xec, 0x29, - 0xc6, 0xce, 0x70, 0x42, 0x12, 0xa7, 0x55, 0x44, - 0xa7, 0x17, 0x92, 0x07, 0x8f, 0xfe, 0xea, 0xd3, - 0xf6, 0xdf, 0x57, 0xf2, 0x1d, 0x3f, 0xb7, 0x8e, - 0xde, 0x6c, 0xbb, 0x1d, 0x3f, 0xaf, 0x6c, 0x6d, - 0x97, 0xa9, 0xd0, 0x28, 0x98, 0xb1, 0x67, 0xce, - 0x21, 0x51, 0xdf, 0x90, 0xc0, 0x9d, 0x4e, 0xab, - 0x9b, 0xa9, 0xd3, 0xff, 0xb3, 0x57, 0xdf, 0x06, - 0x22, 0xd9, 0x58, 0x74, 0xfc, 0x9e, 0xad, 0xbc, - 0xd1, 0x53, 0xfb, 0xf9, 0x4b, 0xeb, 0xed, 0x67, - 0x4f, 0x77, 0x0e, 0xb3, 0xa3, 0x71, 0xeb, 0xd0, - 0x6d, 0x3c, 0xbc, 0xc6, 0xc5, 0x4e, 0x08, 0x42, - 0x54, 0xff, 0xf6, 0xf5, 0x06, 0x67, 0xef, 0x6f, - 0x2f, 0xe8, 0x29, 0xc5, 0xe4, 0xb1, 0x11, 0x3c, - 0xc3, 0xe8, 0x54, 0xf9, 0x1e, 0x57, 0x4a, 0x57, - 0xa1, 0x0b, 0x68, 0x61, 0xcf, 0xff, 0x99, 0x88, - 0xbf, 0xeb, 0xd7, 0x6d, 0x38, 0x5e, 0x74, 0xf9, - 0x6f, 0x56, 0xd8, 0x74, 0x29, 0xfe, 0x5d, 0x52, - 0x7f, 0xfb, 0xcd, 0xb3, 0xfb, 0x7f, 0x83, 0x9f, - 0xed, 0x67, 0x4f, 0xff, 0xf6, 0xd6, 0xf0, 0x71, - 0xbf, 0xcc, 0xeb, 0xd7, 0x47, 0xe5, 0x37, 0x3a, - 0x31, 0x18, 0x1c, 0xa7, 0x0a, 0xd8, 0xef, 0xd0, - 0x9a, 0xf8, 0xe5, 0xf6, 0x87, 0xa6, 0x4b, 0x28, - 0xf4, 0x6d, 0x4d, 0x42, 0xc0, 0x48, 0xd9, 0x0a, - 0xfb, 0x46, 0x4e, 0x08, 0x49, 0x7e, 0x18, 0x15, - 0x95, 0x3b, 0x79, 0x41, 0x5a, 0x43, 0x44, 0x30, - 0xe1, 0x9c, 0xd9, 0x7e, 0x74, 0xff, 0xf6, 0x7d, - 0xd7, 0xd7, 0xea, 0x6b, 0x6c, 0x07, 0x1d, 0x2a, - 0x5e, 0x7d, 0xfb, 0x0e, 0xcf, 0xd6, 0x6b, 0xd6, - 0xf3, 0x0e, 0x9f, 0xf7, 0xd6, 0xf6, 0xe0, 0x75, - 0x35, 0x3a, 0x73, 0x5a, 0x09, 0xd3, 0xfe, 0xef, - 0x0e, 0x52, 0xe0, 0x84, 0x27, 0x47, 0x1e, 0xdd, - 0x47, 0x67, 0xff, 0xbe, 0xaf, 0xdd, 0xbe, 0xfe, - 0xfe, 0x8f, 0xca, 0x9d, 0x18, 0x99, 0xf2, 0x17, - 0xda, 0x13, 0x20, 0x21, 0x9f, 0xcb, 0xf7, 0xff, - 0x30, 0x07, 0x4f, 0xe7, 0xe0, 0xd7, 0xeb, 0x41, - 0xd3, 0xff, 0xca, 0xd9, 0x45, 0xdb, 0xef, 0xfc, - 0xa3, 0xc2, 0x7b, 0xbd, 0xe7, 0xff, 0xb2, 0xeb, - 0xf4, 0xcb, 0xdb, 0xc2, 0x04, 0x3a, 0x7d, 0xd5, - 0xcf, 0xdc, 0xe9, 0xd7, 0xed, 0x59, 0xd3, 0xfa, - 0xf6, 0xf3, 0x81, 0xde, 0x3a, 0x28, 0x4c, 0x8f, - 0x65, 0xff, 0xa6, 0x5c, 0x9b, 0x41, 0xf9, 0xfd, - 0xa0, 0x16, 0xde, 0xe7, 0x9d, 0x3f, 0xff, 0x7f, - 0x2b, 0xa6, 0xec, 0xb7, 0x7f, 0x1a, 0x7a, 0xd2, - 0x74, 0xff, 0xb2, 0xba, 0x60, 0xa7, 0x72, 0x1a, - 0x20, 0x69, 0xf6, 0x5e, 0xdc, 0xfd, 0xc8, 0xa5, - 0xfa, 0xf4, 0xff, 0xff, 0xfd, 0x97, 0xb7, 0x7f, - 0x5e, 0xea, 0x8f, 0x75, 0xdb, 0x66, 0xdb, 0xb1, - 0x9d, 0xfa, 0x5e, 0x78, 0x82, 0xe7, 0xfd, 0xdd, - 0xad, 0x3b, 0x6d, 0xda, 0xc2, 0x78, 0x82, 0xe7, - 0xfe, 0xb7, 0xad, 0xe5, 0xfd, 0xf7, 0x6b, 0x09, - 0xe2, 0x0b, 0x9f, 0xcb, 0xef, 0xdf, 0x76, 0xb0, - 0x9e, 0x20, 0xb9, 0xf9, 0x99, 0xb6, 0xed, 0x61, - 0x3c, 0x41, 0x73, 0xff, 0xfd, 0xdf, 0xff, 0x99, - 0xba, 0xa9, 0x6f, 0x0f, 0xb5, 0xd1, 0x81, 0x3c, - 0x41, 0x73, 0x53, 0xbb, 0x64, 0xe8, 0x51, 0x40, - 0x55, 0xb9, 0x13, 0xe7, 0xf1, 0x65, 0x52, 0xdf, - 0x94, 0x7d, 0x3d, 0xc2, 0x0e, 0x3a, 0x7f, 0xeb, - 0x7a, 0xde, 0x5f, 0xdf, 0x76, 0xb0, 0x9e, 0x20, - 0xb9, 0xfe, 0x6a, 0xa9, 0xea, 0x37, 0x6b, 0x09, - 0xe2, 0x0b, 0x9f, 0x5e, 0xaa, 0xcd, 0xc8, 0x8a, - 0x2d, 0xea, 0xd3, 0xff, 0xb7, 0x25, 0xbc, 0x8b, - 0x7a, 0xee, 0xd6, 0x13, 0xc4, 0x17, 0x3f, 0xff, - 0xf7, 0xff, 0xe6, 0x6e, 0xd3, 0x37, 0x55, 0x2d, - 0xe1, 0xf6, 0xba, 0x30, 0x27, 0x88, 0x2e, 0x31, - 0x32, 0x6a, 0x50, 0xf9, 0x72, 0x7f, 0xad, 0xe1, - 0xf6, 0xba, 0x30, 0x27, 0x88, 0x2e, 0x7f, 0xfb, - 0xba, 0x97, 0xd6, 0xde, 0xdb, 0x65, 0x15, 0x2a, - 0x7f, 0xd8, 0xf7, 0xe9, 0x51, 0xfd, 0x1a, 0x87, - 0x88, 0x2e, 0x11, 0x1d, 0x02, 0x91, 0x55, 0x09, - 0xff, 0x27, 0x86, 0xfc, 0x0a, 0xee, 0x09, 0xe2, - 0x0b, 0x9f, 0xad, 0xeb, 0x5b, 0xc0, 0x34, 0x01, - 0x73, 0xec, 0x06, 0xed, 0x61, 0x3c, 0x41, 0x73, - 0x65, 0xd0, 0xfc, 0xec, 0x77, 0x14, 0xa3, 0xae, - 0xb0, 0xbf, 0x9f, 0x99, 0x9b, 0x6e, 0xd6, 0x13, - 0xc4, 0x17, 0x3f, 0xe4, 0xb7, 0x87, 0xda, 0xe8, - 0xc0, 0x9e, 0x20, 0xb9, 0xb3, 0x77, 0x22, 0x32, - 0xa7, 0xf3, 0xfb, 0x4f, 0x33, 0xbf, 0x4b, 0xcf, - 0x10, 0x5c, 0xff, 0xb3, 0xcd, 0xb3, 0xf9, 0xb7, - 0x3c, 0xf1, 0x05, 0xb0, 0xf0, 0xa3, 0x65, 0xdc, - 0x70, 0x16, 0x7e, 0x3e, 0x7a, 0xc6, 0x31, 0x78, - 0xc6, 0x74, 0x85, 0xa8, 0x5c, 0x67, 0xc0, 0xa8, - 0x00, 0xa6, 0x88, 0x2d, 0xc8, 0x80, 0x9f, 0xf6, - 0x3e, 0xdc, 0xf6, 0xf6, 0xfd, 0x28, 0x3a, 0x7f, - 0x0f, 0xf3, 0x6b, 0x68, 0x13, 0xa7, 0xd4, 0xdf, - 0x84, 0x07, 0x4f, 0xd9, 0x47, 0x59, 0x75, 0x67, - 0x47, 0x91, 0x17, 0xc6, 0x9f, 0x27, 0x9d, 0xc3, - 0x41, 0xd3, 0x01, 0x4e, 0x9e, 0xf2, 0xb3, 0x0e, - 0x83, 0xa7, 0xee, 0xd7, 0x75, 0x10, 0x1d, 0x1b, - 0x1b, 0x7f, 0x85, 0x4f, 0xff, 0xe5, 0xf6, 0xdf, - 0x5d, 0x12, 0xf8, 0x9b, 0x2a, 0x6f, 0x61, 0xd3, - 0x01, 0x4e, 0x99, 0x75, 0x9d, 0x3f, 0xd9, 0x7a, - 0xab, 0x37, 0xe3, 0x47, 0x4f, 0xea, 0xe5, 0xbd, - 0x5d, 0x28, 0x3a, 0x60, 0x84, 0xa9, 0xfe, 0xfe, - 0x36, 0xe7, 0xd7, 0xdb, 0x1d, 0x08, 0x9f, 0xbf, - 0x86, 0xc4, 0x55, 0x8a, 0xf6, 0x22, 0x03, 0x13, - 0x76, 0x2b, 0xf1, 0x7a, 0x9d, 0x04, 0xd7, 0x78, - 0xb4, 0xe0, 0x84, 0x25, 0x49, 0x85, 0x38, 0xbc, - 0x9f, 0x73, 0x5d, 0xc2, 0x53, 0x91, 0xb3, 0xbe, - 0x17, 0x53, 0xfa, 0x9b, 0xae, 0x5b, 0x28, 0x3a, - 0x15, 0xb2, 0x07, 0xc8, 0xda, 0x35, 0xa0, 0xd2, - 0x63, 0xe8, 0xd5, 0x45, 0x3a, 0xd0, 0xd8, 0xea, - 0x48, 0xf7, 0xd1, 0xab, 0x0d, 0x1d, 0x25, 0x7f, - 0xb6, 0x4b, 0x9e, 0xc4, 0x5b, 0x9d, 0x3d, 0x8c, - 0xcb, 0x9d, 0x3d, 0xd4, 0x6f, 0x79, 0xd0, 0xa7, - 0xc7, 0x48, 0xfb, 0x79, 0x04, 0xfd, 0xfd, 0x36, - 0x67, 0x3c, 0xe9, 0xff, 0xea, 0x5f, 0xf5, 0xdc, - 0xcf, 0xae, 0xbb, 0xfa, 0x83, 0xa7, 0xf5, 0x6e, - 0xdb, 0x3f, 0x9b, 0x1d, 0x0a, 0x8b, 0xaf, 0x17, - 0xf2, 0xb4, 0xee, 0xdd, 0xbc, 0xe9, 0xff, 0xef, - 0xbf, 0x76, 0x6d, 0xea, 0xd3, 0x7d, 0x3a, 0xa7, - 0x46, 0xe3, 0xf4, 0xc1, 0xf9, 0xfa, 0x86, 0x9e, - 0xdc, 0x1a, 0x0e, 0x99, 0xf4, 0x1d, 0x3e, 0xb6, - 0x79, 0xf5, 0x3a, 0x7f, 0xfa, 0xcb, 0xbd, 0xd7, - 0xff, 0x02, 0xb5, 0x50, 0x15, 0x3f, 0x81, 0x82, - 0x9d, 0xc8, 0x78, 0x81, 0x21, 0xe8, 0xb3, 0xd8, - 0x9c, 0x54, 0x66, 0x6f, 0x79, 0xd3, 0xd7, 0xa3, - 0xbe, 0x74, 0xf5, 0x35, 0xeb, 0x9d, 0x14, 0x1e, - 0xee, 0x0c, 0xd8, 0x8a, 0x7f, 0x7f, 0x83, 0x5a, - 0xa8, 0x0e, 0x9c, 0x10, 0x84, 0xf8, 0x7d, 0x4e, - 0xef, 0xec, 0x5c, 0x3e, 0x9c, 0x6a, 0x63, 0x64, - 0x4a, 0x01, 0x6e, 0x7f, 0xfd, 0x9f, 0xf6, 0xb7, - 0x56, 0xf6, 0xd3, 0x9e, 0xf5, 0x3a, 0x28, 0x3f, - 0xcd, 0x64, 0x91, 0xaa, 0x95, 0x77, 0xbc, 0xd3, - 0x21, 0x95, 0xe8, 0x46, 0xf4, 0x67, 0xf3, 0xef, - 0x5d, 0x6f, 0x52, 0xa7, 0x91, 0x6f, 0x52, 0xa6, - 0x08, 0x4a, 0x87, 0x9e, 0xee, 0x13, 0x84, 0x82, - 0x6c, 0x09, 0x4e, 0x35, 0xd3, 0xff, 0xec, 0xba, - 0xa6, 0x67, 0xef, 0x6f, 0x2f, 0xe8, 0x3a, 0x00, - 0x7f, 0x01, 0x25, 0x9f, 0xff, 0xca, 0xdb, 0xdf, - 0x06, 0xeb, 0xd7, 0x12, 0xdd, 0xfb, 0xd4, 0xe9, - 0xfc, 0xd5, 0x2f, 0xb6, 0x8a, 0x27, 0x4e, 0x7f, - 0x09, 0xd3, 0xe7, 0xe5, 0xf3, 0x62, 0xa5, 0xe6, - 0x8f, 0x06, 0xe3, 0x53, 0x01, 0x4e, 0x98, 0x0a, - 0x74, 0xfd, 0xfd, 0x1f, 0x9f, 0x77, 0x8d, 0x50, - 0x05, 0x67, 0xfe, 0xbd, 0xb1, 0x9b, 0xd4, 0x19, - 0x4d, 0xce, 0x9f, 0xd4, 0xa7, 0x87, 0x7a, 0xec, - 0x74, 0xf0, 0x19, 0xcf, 0xa9, 0xfd, 0xdd, 0x1a, - 0x76, 0xa6, 0x52, 0x74, 0x29, 0xec, 0x71, 0xcc, - 0xe0, 0x84, 0x25, 0x4f, 0xb3, 0x5f, 0xf4, 0xa9, - 0x4e, 0x2f, 0x27, 0xd8, 0xe0, 0x84, 0x27, 0x42, - 0x9f, 0x05, 0xce, 0x67, 0x83, 0xdf, 0x79, 0xd3, - 0x25, 0x8e, 0x9c, 0x10, 0x84, 0xa9, 0xfb, 0xdd, - 0xae, 0xde, 0xb9, 0x4e, 0x2f, 0x27, 0xd8, 0x0c, - 0xc6, 0x8e, 0x95, 0x77, 0x22, 0x54, 0x4c, 0x7e, - 0x7d, 0x3d, 0xef, 0xe8, 0xfd, 0x91, 0xdb, 0x90, - 0xb6, 0x85, 0x5c, 0x52, 0x79, 0x16, 0x32, 0x6b, - 0x76, 0x15, 0x3b, 0x46, 0x31, 0x58, 0x4e, 0x5e, - 0x30, 0xe9, 0xf9, 0x58, 0x23, 0xd7, 0x3a, 0x7f, - 0xfb, 0xcc, 0xa5, 0xf5, 0xdd, 0x9f, 0xfe, 0x78, - 0x07, 0x45, 0x8f, 0xf6, 0xe5, 0x52, 0x73, 0x74, - 0x84, 0x1c, 0x4a, 0xca, 0xa6, 0xa2, 0x31, 0x67, - 0xca, 0x4d, 0xda, 0x54, 0x16, 0x56, 0xda, 0x7a, - 0xe3, 0x71, 0x48, 0xec, 0xa9, 0x95, 0x4f, 0xe8, - 0x4e, 0xb5, 0x3c, 0x44, 0x33, 0xa4, 0x8c, 0x94, - 0x17, 0x69, 0xed, 0x2e, 0xa7, 0x69, 0x82, 0x57, - 0xdf, 0xe7, 0x57, 0xeb, 0x59, 0x14, 0x5e, 0xb3, - 0x9f, 0xd2, 0x33, 0x56, 0xd0, 0xec, 0x0c, 0x28, - 0xb5, 0x25, 0x89, 0x6f, 0x9d, 0xb5, 0x6f, 0x8c, - 0x06, 0x1d, 0x08, 0x5f, 0x07, 0xe2, 0x0f, 0x56, - 0x7f, 0x3b, 0x05, 0x3b, 0x90, 0xd1, 0x71, 0xcf, - 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x2e, 0xb9, 0xff, - 0x9d, 0xcf, 0x76, 0x0a, 0x77, 0x21, 0xa2, 0x51, - 0x85, 0x84, 0x4a, 0x2b, 0xca, 0x36, 0x3b, 0xd6, - 0x76, 0x87, 0x7e, 0x87, 0x86, 0xad, 0x03, 0xb1, - 0x0c, 0x85, 0x53, 0x8b, 0x9d, 0xb7, 0x9d, 0xcf, - 0xfe, 0x73, 0x39, 0xee, 0xc1, 0x4e, 0xe4, 0x34, - 0x4b, 0x53, 0xe1, 0x4e, 0xe4, 0x34, 0x46, 0xf3, - 0xfe, 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x2f, - 0xc9, 0xd8, 0x7e, 0xcc, 0x30, 0x9f, 0xce, 0xc1, - 0x4e, 0xe4, 0x34, 0x55, 0x73, 0xfd, 0xaf, 0x3f, - 0x94, 0xdf, 0x98, 0x74, 0xdb, 0x78, 0xe9, 0xfb, - 0x05, 0x3b, 0x90, 0xd1, 0x20, 0x46, 0xe3, 0xcc, - 0x70, 0xbc, 0xfa, 0xbd, 0xfe, 0xa4, 0xe8, 0x79, - 0xe5, 0xd2, 0x49, 0x1a, 0xd1, 0xe9, 0xd0, 0xd1, - 0x9f, 0xfb, 0xf9, 0x43, 0xb5, 0x17, 0xff, 0xcb, - 0x9d, 0x0e, 0x3f, 0x01, 0x29, 0x9f, 0xce, 0xc1, - 0x4e, 0xe4, 0x34, 0x59, 0x13, 0xf9, 0xd8, 0x29, - 0xdc, 0x86, 0x8b, 0x5e, 0x7f, 0x3b, 0x05, 0x3b, - 0x90, 0xd1, 0x72, 0x4f, 0x85, 0x3b, 0x90, 0xd1, - 0x76, 0x4f, 0xfb, 0x9e, 0xec, 0x14, 0xee, 0x43, - 0x45, 0x1d, 0x27, 0x61, 0xfb, 0x30, 0xc2, 0x7c, - 0x29, 0xdc, 0x86, 0x8a, 0x56, 0x7f, 0xff, 0xfa, - 0xcb, 0x42, 0xdb, 0xcd, 0xbb, 0x6b, 0x79, 0xc9, - 0x6f, 0x35, 0x65, 0xa3, 0x0e, 0x9f, 0x39, 0x9c, - 0xf7, 0x62, 0x2c, 0x9a, 0x30, 0x8a, 0x17, 0x3b, - 0xdf, 0x0c, 0x8a, 0x49, 0xfd, 0x1c, 0xfb, 0x08, - 0x40, 0x77, 0x53, 0xbb, 0xc2, 0xc5, 0xb4, 0x36, - 0x67, 0xf9, 0xee, 0xc1, 0x4e, 0xe4, 0x34, 0x47, - 0x13, 0xfd, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x2b, - 0x59, 0x3b, 0x91, 0x05, 0x74, 0x19, 0xff, 0xce, - 0x67, 0x3d, 0xd8, 0x29, 0xdc, 0x86, 0x89, 0x6e, - 0x6c, 0xa4, 0xe9, 0xfd, 0xb6, 0x31, 0x8b, 0xea, - 0x9d, 0x14, 0x9e, 0x4f, 0x85, 0xa7, 0x5b, 0x67, - 0x9d, 0x39, 0xec, 0x43, 0xa0, 0xd1, 0x0d, 0xcf, - 0xfb, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0xc1, - 0x38, 0x74, 0x01, 0x53, 0xfb, 0xf9, 0x7b, 0xa8, - 0xd4, 0xe9, 0x39, 0x53, 0x05, 0xa0, 0x8f, 0x61, - 0xd4, 0x1b, 0x60, 0xbd, 0xd1, 0xb7, 0x8e, 0x4e, - 0x66, 0x82, 0x54, 0xff, 0xb9, 0xee, 0xc1, 0x4e, - 0xe4, 0x34, 0x4c, 0x72, 0x77, 0x8f, 0x89, 0x83, - 0x93, 0xf2, 0xd7, 0x75, 0xf2, 0xc7, 0x4f, 0xff, - 0xff, 0x3e, 0xb6, 0xcd, 0x03, 0x5b, 0x62, 0x65, - 0x77, 0x65, 0x2f, 0xaf, 0xbf, 0xe3, 0xa7, 0x93, - 0xb9, 0x0d, 0x15, 0x8c, 0xff, 0xb5, 0x32, 0xff, - 0xcd, 0x17, 0xf7, 0x3a, 0x35, 0xa6, 0x37, 0x49, - 0x70, 0xc2, 0x06, 0xe5, 0x73, 0xff, 0x97, 0xf7, - 0xd7, 0xa2, 0xff, 0x11, 0x44, 0xe9, 0xff, 0x66, - 0xd6, 0xca, 0xd2, 0xf5, 0xb1, 0xd3, 0xff, 0xff, - 0x7f, 0x4a, 0x31, 0x37, 0x7f, 0x46, 0xfd, 0xd9, - 0x4b, 0xf3, 0x4f, 0x82, 0xa7, 0x4f, 0xed, 0x55, - 0x43, 0x4f, 0x6e, 0x0d, 0x07, 0x4e, 0xb7, 0x9d, - 0x89, 0xc5, 0xa2, 0x2d, 0x92, 0x6e, 0x7f, 0xa8, - 0xfd, 0x36, 0x8d, 0x1d, 0x3e, 0xef, 0xd1, 0xa5, - 0x4e, 0x8f, 0x9e, 0x0d, 0x46, 0x27, 0x94, 0x7b, - 0xe7, 0x4f, 0x56, 0xaa, 0x03, 0xa2, 0xc7, 0x80, - 0x11, 0xf9, 0xf2, 0xbc, 0x1a, 0x3c, 0xe9, 0xcb, - 0xf7, 0x9d, 0x0d, 0x1e, 0x1d, 0xca, 0x27, 0xe5, - 0x66, 0x7f, 0xda, 0xce, 0x95, 0x4e, 0x8f, 0x1b, - 0xee, 0x2e, 0x98, 0x0a, 0x54, 0xc1, 0x09, 0x51, - 0xe3, 0x56, 0x11, 0x59, 0xfd, 0xcf, 0xfe, 0x5e, - 0xde, 0x29, 0xc6, 0x86, 0x7b, 0x6e, 0xa6, 0xa7, - 0x4e, 0x5f, 0xb4, 0x74, 0xdf, 0x53, 0xa1, 0xa3, - 0x62, 0x23, 0x93, 0xc2, 0x0c, 0x6f, 0x3a, 0x76, - 0xf1, 0xd6, 0x74, 0x2a, 0x2d, 0xf1, 0x53, 0xc4, - 0x36, 0x23, 0x9a, 0xd7, 0x3a, 0x70, 0x42, 0x12, - 0xa7, 0xfe, 0xc4, 0xd9, 0x68, 0xdf, 0x96, 0xea, - 0x4a, 0x71, 0x79, 0x3d, 0xe1, 0x6f, 0xd8, 0xe9, - 0x09, 0xd3, 0xe6, 0x7a, 0xe0, 0xe3, 0xa2, 0x83, - 0xdb, 0xd5, 0x92, 0xe8, 0x1f, 0x3f, 0x93, 0xd5, - 0xfe, 0x36, 0xe3, 0xa6, 0xcb, 0x9d, 0x14, 0x9e, - 0x45, 0x8c, 0xe7, 0xff, 0xaf, 0xea, 0xdb, 0x36, - 0xfe, 0x36, 0xfe, 0x54, 0xe9, 0x39, 0x57, 0x27, - 0x36, 0x62, 0xc6, 0x51, 0x61, 0x63, 0x65, 0xa1, - 0xf3, 0xc7, 0x7f, 0x32, 0xac, 0x2c, 0x2e, 0xfb, - 0xbc, 0x8e, 0x7c, 0x29, 0xdc, 0x86, 0x8a, 0xde, - 0x7f, 0xdc, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x27, - 0x09, 0x3b, 0x0f, 0xd9, 0x86, 0x13, 0xe1, 0x4e, - 0xe4, 0x34, 0x5a, 0x33, 0xd8, 0xd7, 0xb5, 0x9d, - 0x27, 0x61, 0xea, 0x58, 0xc2, 0x79, 0x3b, 0x90, - 0xd1, 0x6d, 0x4f, 0xd6, 0x56, 0x2f, 0xb6, 0x3a, - 0x67, 0x60, 0x9e, 0xc5, 0xca, 0xe7, 0xf3, 0xb0, - 0x53, 0xb9, 0x0d, 0x17, 0x2c, 0xfe, 0x76, 0x0a, - 0x77, 0x21, 0xa2, 0xed, 0x85, 0x66, 0xae, 0x50, - 0x50, 0xf8, 0xe8, 0x36, 0x86, 0x7e, 0xb9, 0x59, - 0xde, 0x9d, 0x3b, 0xd5, 0xc6, 0x41, 0x67, 0xbf, - 0xbf, 0xd4, 0xb2, 0xe7, 0x73, 0xf9, 0xd8, 0x29, - 0xdc, 0x86, 0x8a, 0x9e, 0x79, 0x3b, 0x90, 0xd1, - 0x5c, 0xcf, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x2c, - 0xe8, 0x13, 0xe6, 0xb1, 0x5c, 0xf8, 0x53, 0xb9, - 0x0d, 0x12, 0x14, 0xff, 0x7a, 0x9a, 0xdf, 0xea, - 0xca, 0x9d, 0x33, 0xdd, 0x87, 0xd4, 0x03, 0x09, - 0xe6, 0xe9, 0x65, 0x61, 0xd3, 0xfb, 0x2d, 0x5a, - 0xe0, 0xd0, 0x74, 0x9d, 0x89, 0x88, 0xf4, 0x22, - 0xea, 0x5b, 0x72, 0x79, 0xff, 0xce, 0x67, 0x3d, - 0xd8, 0x29, 0xdc, 0x86, 0x89, 0xf2, 0x11, 0x52, - 0x5e, 0xae, 0x3b, 0x2b, 0x23, 0xcf, 0x85, 0x3b, - 0x90, 0xd1, 0x59, 0x4f, 0xfb, 0x9e, 0xec, 0x14, - 0xee, 0x43, 0x44, 0xdd, 0x37, 0x9d, 0x87, 0xec, - 0xc3, 0x09, 0xf0, 0xa7, 0x72, 0x1a, 0x25, 0x69, - 0xfe, 0xd7, 0x6f, 0x6e, 0xd1, 0x32, 0xa7, 0x4f, - 0x9c, 0xce, 0x7b, 0xb0, 0xfb, 0x78, 0xc2, 0x7c, - 0x29, 0xdc, 0x86, 0x89, 0x72, 0x7f, 0xbd, 0xcf, - 0xbf, 0xd5, 0x95, 0x3a, 0x79, 0xb7, 0xbe, 0xa7, - 0x4f, 0x9c, 0xce, 0x7b, 0xb1, 0x11, 0x76, 0x30, - 0xe3, 0x89, 0xff, 0x9d, 0xcf, 0x76, 0x0a, 0x77, - 0x21, 0xa2, 0x3b, 0x9f, 0xeb, 0xdb, 0x1e, 0xee, - 0xfd, 0x4e, 0x9f, 0x9b, 0x9f, 0x54, 0xcf, 0x9d, - 0x3f, 0x6a, 0x9f, 0xcb, 0x75, 0xce, 0x9f, 0x0a, - 0x77, 0x21, 0xa2, 0xa1, 0x9f, 0x65, 0xaf, 0x94, - 0x1d, 0x27, 0x6a, 0x88, 0xb7, 0x6e, 0x17, 0xe1, - 0x8d, 0xcc, 0x27, 0xc2, 0x9d, 0xc8, 0x68, 0xaa, - 0x27, 0xfd, 0xcf, 0x76, 0x0a, 0x77, 0x21, 0xa2, - 0x6b, 0x93, 0xb0, 0xfd, 0x98, 0x61, 0x3f, 0x9d, - 0x82, 0x9d, 0xc8, 0x68, 0xab, 0x27, 0xfe, 0x77, - 0x3d, 0xd8, 0x29, 0xdc, 0x86, 0x89, 0x12, 0x7c, - 0x29, 0xdc, 0x86, 0x8b, 0x4a, 0x7f, 0xdc, 0xf7, - 0x60, 0xa7, 0x72, 0x1a, 0x27, 0xd9, 0x3b, 0x0f, - 0xd9, 0x86, 0x13, 0xff, 0x9c, 0xce, 0x7b, 0xb0, - 0x53, 0xb9, 0x0d, 0x14, 0x24, 0xfa, 0xde, 0xa1, - 0x44, 0xe9, 0xf0, 0xa7, 0x72, 0x1a, 0x28, 0xf9, - 0xff, 0xfb, 0x36, 0xb2, 0xef, 0x75, 0xff, 0xc0, - 0xad, 0x54, 0x05, 0x4f, 0x9c, 0xce, 0x7b, 0x95, - 0x16, 0x98, 0x4f, 0xa8, 0x61, 0x0a, 0xbb, 0x5d, - 0x44, 0x32, 0x1e, 0x97, 0x89, 0x9a, 0xe1, 0xde, - 0x90, 0xc1, 0xa4, 0x9d, 0xa3, 0xbb, 0x43, 0x08, - 0x04, 0xf7, 0x8c, 0x1a, 0x7f, 0x3b, 0x05, 0x3b, - 0x90, 0xd1, 0x11, 0x4f, 0xd8, 0x29, 0xdc, 0x86, - 0x88, 0xae, 0x7f, 0xb5, 0x6e, 0xc1, 0x4e, 0xe4, - 0x34, 0x57, 0x10, 0xe3, 0xfa, 0xe3, 0x59, 0xec, - 0xb7, 0x5c, 0xe9, 0xfd, 0x9e, 0x10, 0x03, 0x4a, - 0x9d, 0x2d, 0x53, 0xe7, 0xa7, 0x52, 0x09, 0xff, - 0xbb, 0xcf, 0xf7, 0xef, 0xa5, 0x87, 0x0e, 0x9f, - 0x67, 0xb6, 0x1c, 0x3a, 0x75, 0xeb, 0xeb, 0x1f, - 0x55, 0xd1, 0x27, 0xc2, 0x9d, 0xc8, 0x68, 0xa7, - 0xa1, 0xb8, 0x8f, 0xb5, 0x84, 0xbe, 0x1a, 0x4f, - 0xfe, 0xe7, 0xbb, 0x6f, 0xf0, 0x6b, 0x55, 0x01, - 0xd0, 0xe4, 0x40, 0xec, 0x6f, 0x39, 0xdb, 0x61, - 0xd3, 0xe5, 0x78, 0x34, 0x79, 0xd3, 0xc9, 0xdc, - 0x86, 0x8a, 0xce, 0x1a, 0x3d, 0x31, 0x28, 0x9f, - 0xa8, 0x62, 0xfd, 0xf5, 0x3a, 0x72, 0xd1, 0xe3, - 0xa7, 0xd7, 0x06, 0xff, 0xbc, 0xe9, 0xf6, 0x35, - 0x46, 0x80, 0x3a, 0x4e, 0xc4, 0x60, 0x09, 0x15, - 0x8b, 0xb8, 0x73, 0x79, 0x54, 0xff, 0xce, 0xe7, - 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x22, 0xcf, 0xe7, - 0x60, 0xa7, 0x72, 0x1a, 0x2c, 0x99, 0xfc, 0xec, - 0x14, 0xee, 0x43, 0x45, 0xb1, 0x3b, 0x2b, 0x87, - 0x4f, 0x85, 0x3b, 0x90, 0xd1, 0x6d, 0xc9, 0xcf, - 0x3c, 0xac, 0x1a, 0x9f, 0xf3, 0x71, 0xb9, 0xaa, - 0xd7, 0xb6, 0x7d, 0x40, 0x74, 0xfc, 0xbf, 0x7e, - 0xda, 0x30, 0xe9, 0xf0, 0xa7, 0x72, 0x1a, 0x2f, - 0x09, 0xeb, 0xea, 0x65, 0x8e, 0x9f, 0xf9, 0x77, - 0xb2, 0x97, 0xdf, 0x3f, 0x7a, 0x9d, 0x3e, 0xcf, - 0x81, 0xbd, 0x4e, 0x9f, 0x2e, 0xfd, 0x3a, 0xa7, - 0x4e, 0xba, 0x80, 0xe9, 0x3b, 0x55, 0xe4, 0xe1, - 0x28, 0x4d, 0xc2, 0xfa, 0x4c, 0x2c, 0x48, 0x04, - 0x7f, 0x94, 0xdc, 0xa6, 0x77, 0xb2, 0x93, 0xa7, - 0xc2, 0x9d, 0xc8, 0x68, 0xbd, 0x27, 0xfd, 0xfe, - 0xbb, 0xd7, 0x5d, 0xfd, 0x41, 0xd3, 0xec, 0x65, - 0xbb, 0x63, 0xa4, 0xed, 0x91, 0x69, 0x83, 0x9b, - 0xcc, 0x1b, 0xd0, 0x61, 0x59, 0x0f, 0x94, 0x42, - 0xd7, 0x23, 0xf0, 0x42, 0x4f, 0x46, 0x5e, 0x29, - 0xec, 0x4c, 0x01, 0xdf, 0xe1, 0x0b, 0xa4, 0x75, - 0x61, 0x8c, 0xda, 0x7b, 0x2d, 0xd7, 0x3a, 0x7f, - 0x67, 0x84, 0x00, 0xd2, 0xa7, 0x4b, 0x54, 0xf9, - 0xe9, 0xd4, 0x82, 0x7c, 0x29, 0xdc, 0x86, 0x88, - 0x8e, 0x7f, 0xfa, 0xda, 0x56, 0xb8, 0xcf, 0xe3, - 0x6d, 0xca, 0xdd, 0x4e, 0x9f, 0xf3, 0xd6, 0x81, - 0xcb, 0xdf, 0xd7, 0x3a, 0x7f, 0xff, 0x96, 0xdd, - 0xeb, 0xad, 0xbf, 0x97, 0xb7, 0xad, 0x9e, 0x09, - 0xd3, 0x50, 0xc2, 0xa6, 0x08, 0x4a, 0x9f, 0xf9, - 0xee, 0x4b, 0x79, 0xab, 0x2d, 0x0e, 0x01, 0xad, - 0x08, 0xbc, 0xfe, 0xcd, 0x5f, 0xf2, 0xdd, 0x73, - 0xa7, 0xff, 0xe4, 0x70, 0xf7, 0xfd, 0x77, 0x0a, - 0xdf, 0xfc, 0x03, 0xa1, 0xb8, 0xa9, 0xc7, 0x0b, - 0x50, 0xc2, 0x95, 0xa1, 0x3e, 0xe8, 0x49, 0x5d, - 0x75, 0xb1, 0xac, 0xf8, 0x53, 0xb9, 0x0d, 0x11, - 0x74, 0xfa, 0xe0, 0xdf, 0xf7, 0x96, 0xcf, 0x69, - 0x3b, 0x0f, 0x9f, 0x8c, 0x21, 0xc9, 0x84, 0x3e, - 0x1c, 0x53, 0xff, 0x33, 0x9e, 0xec, 0x14, 0xee, - 0x43, 0x44, 0xcd, 0x3a, 0x95, 0xa9, 0xd3, 0x93, - 0xcc, 0x3a, 0x76, 0xaa, 0xd4, 0xd5, 0x47, 0x4f, - 0x93, 0xad, 0xe6, 0x8e, 0x9f, 0xf9, 0xbf, 0xd7, - 0xf2, 0x7b, 0x6b, 0xe2, 0x1d, 0x3f, 0xff, 0xda, - 0x77, 0xd3, 0xf9, 0xb5, 0xb4, 0xdd, 0x65, 0x78, - 0xad, 0x07, 0x46, 0x22, 0xc6, 0xc8, 0xf3, 0xff, - 0xed, 0xbd, 0xfa, 0x32, 0xbe, 0xeb, 0x38, 0x21, - 0x09, 0x53, 0xc9, 0xdc, 0x86, 0x8b, 0x3e, 0x7f, - 0xfb, 0xc3, 0x6e, 0xdd, 0x8d, 0xbf, 0x95, 0xcf, - 0x9d, 0x38, 0x21, 0x09, 0x53, 0xfe, 0xe7, 0xd7, - 0xf8, 0x9b, 0x60, 0x94, 0xe2, 0xf2, 0x7f, 0x96, - 0xde, 0xdb, 0xfd, 0xcd, 0x1d, 0x3f, 0xde, 0xfe, - 0xdb, 0xf6, 0xd2, 0xf5, 0x3a, 0x11, 0x3b, 0x31, - 0x58, 0xb1, 0x58, 0x1b, 0x6e, 0x96, 0xd8, 0xe6, - 0x7f, 0xff, 0xfe, 0xcc, 0x05, 0x73, 0x3f, 0xaf, - 0x78, 0xfd, 0x72, 0xce, 0xaf, 0xf2, 0x86, 0x63, - 0x0e, 0x9d, 0x55, 0xa0, 0xe9, 0xda, 0x99, 0x63, - 0xa1, 0xe8, 0xc4, 0xac, 0x22, 0xae, 0x39, 0x3d, - 0x48, 0x34, 0x43, 0xa7, 0xd6, 0xfa, 0xe0, 0x99, - 0x3f, 0xef, 0x0b, 0xf0, 0x68, 0xd3, 0xb6, 0x34, - 0x41, 0xae, 0x34, 0xb3, 0xee, 0x4f, 0x31, 0x4e, - 0x9f, 0x9f, 0x6f, 0xf5, 0x37, 0x3a, 0x58, 0x87, - 0xa7, 0xf2, 0x59, 0xff, 0xff, 0x70, 0x6f, 0x96, - 0xba, 0xe7, 0xe9, 0xbe, 0x0f, 0xa8, 0xe6, 0x1d, - 0x0f, 0x4d, 0x03, 0x21, 0x5f, 0xf2, 0x69, 0xf8, - 0x73, 0x6a, 0xd9, 0x4e, 0x9f, 0xff, 0xda, 0x7f, - 0xb5, 0xee, 0xdf, 0xfe, 0xbd, 0x7d, 0x4d, 0xbc, - 0x13, 0xa7, 0xff, 0xfa, 0xb5, 0x51, 0xba, 0xb9, - 0x99, 0x7a, 0xef, 0xaf, 0x3f, 0x0e, 0x9e, 0xb6, - 0x79, 0xb1, 0xd1, 0xe4, 0x44, 0xd9, 0x9a, 0x66, - 0x6a, 0x86, 0x8b, 0xf2, 0x7f, 0xe7, 0xdf, 0x47, - 0xbd, 0x97, 0xba, 0xb0, 0xe9, 0xff, 0xbd, 0x9f, - 0xcc, 0xae, 0xe7, 0xd5, 0x87, 0x45, 0x28, 0x8b, - 0xaa, 0x34, 0xf7, 0xfe, 0xa8, 0x74, 0x2a, 0x63, - 0x5b, 0x11, 0xe4, 0x2c, 0xf8, 0x92, 0x7f, 0xff, - 0xe6, 0xf5, 0xb7, 0x98, 0xe6, 0x9f, 0xc3, 0xbe, - 0xde, 0xa6, 0xa9, 0xea, 0x0e, 0x9f, 0x69, 0x95, - 0xf7, 0xce, 0x9e, 0xdf, 0xa7, 0x54, 0xe9, 0xff, - 0x5e, 0xa0, 0xf5, 0x34, 0xbf, 0x2e, 0x74, 0x29, - 0xf2, 0xe1, 0x24, 0xcd, 0x68, 0x74, 0x98, 0x74, - 0x52, 0x6a, 0x1b, 0xb1, 0x89, 0xff, 0xff, 0x5b, - 0xd4, 0xdf, 0x29, 0xb8, 0x8a, 0xdc, 0x1b, 0xfe, - 0xfb, 0x61, 0xd3, 0x82, 0x10, 0x95, 0x3d, 0xb6, - 0xdd, 0xf2, 0x9c, 0x5e, 0x4f, 0xfb, 0xac, 0x9e, - 0xed, 0xd5, 0xeb, 0x1d, 0x0a, 0x99, 0x12, 0x13, - 0xfa, 0x11, 0x76, 0x32, 0x9f, 0xfb, 0x3f, 0x4d, - 0xf0, 0x7d, 0x47, 0x30, 0xe9, 0xf6, 0x5d, 0xec, - 0xb1, 0xd0, 0xa7, 0xd7, 0x54, 0x49, 0x81, 0x63, - 0xa7, 0xea, 0x1a, 0x7b, 0x70, 0x68, 0x3a, 0x7f, - 0x6b, 0xb6, 0x5e, 0xde, 0xb9, 0xd3, 0x78, 0x4e, - 0x8d, 0x54, 0x7f, 0xde, 0x34, 0xb1, 0xac, 0xff, - 0xed, 0xb6, 0x5d, 0x85, 0x6f, 0xe4, 0xf6, 0xc7, - 0x49, 0xcd, 0xd1, 0x99, 0xd5, 0xaa, 0xc3, 0x8a, - 0x37, 0x41, 0x5e, 0xd0, 0xd8, 0xc8, 0xf5, 0x69, - 0x8c, 0x73, 0xc6, 0xad, 0x47, 0x10, 0x26, 0xac, - 0x2c, 0xb4, 0xa0, 0xde, 0x41, 0x03, 0xd7, 0xe1, - 0x1b, 0x58, 0xda, 0xaf, 0x0a, 0xc0, 0x90, 0xea, - 0x42, 0x53, 0x79, 0xac, 0x39, 0x9d, 0x25, 0x6a, - 0x69, 0xb4, 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, - 0xee, 0x43, 0x44, 0xd9, 0x3f, 0x9d, 0x82, 0x9d, - 0xc8, 0x68, 0xab, 0x67, 0xf3, 0xdf, 0xa5, 0xfe, - 0xaf, 0x3a, 0x7b, 0x2d, 0xd7, 0x3a, 0x5a, 0xa6, - 0x1e, 0xa7, 0xcd, 0x27, 0xc2, 0x9d, 0xc8, 0x68, - 0xad, 0x27, 0xff, 0x91, 0x47, 0x01, 0xcc, 0xe7, - 0xaf, 0x90, 0xe9, 0xff, 0xe7, 0xd7, 0x3e, 0xac, - 0xec, 0xbd, 0xd5, 0x87, 0x4d, 0x7b, 0xaa, 0x25, - 0xf9, 0x2a, 0x7f, 0x2b, 0x6d, 0xf7, 0xfa, 0x80, - 0xe9, 0xfa, 0x8c, 0xb7, 0xb1, 0x87, 0x4f, 0xf6, - 0x37, 0xfd, 0x43, 0x4b, 0xea, 0x74, 0xfe, 0x7e, - 0x5f, 0xbe, 0x0d, 0x0e, 0x93, 0x9b, 0x89, 0xf9, - 0x61, 0x67, 0xa1, 0x8f, 0x62, 0xde, 0x36, 0xf9, - 0x6d, 0xcf, 0x27, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, - 0x16, 0x04, 0xf8, 0x53, 0xb9, 0x0d, 0x13, 0xac, - 0xff, 0xfe, 0xb2, 0xd1, 0xb6, 0x31, 0xd5, 0xae, - 0x5b, 0xd5, 0xd2, 0x83, 0xa7, 0xce, 0x67, 0x3d, - 0xd8, 0x89, 0x56, 0x8c, 0x27, 0xc2, 0x9d, 0xc8, - 0x68, 0xb6, 0x67, 0xfc, 0x05, 0xab, 0xad, 0xe4, - 0xe1, 0x3a, 0x4e, 0xc3, 0xec, 0xe3, 0x09, 0xe4, - 0xee, 0x43, 0x45, 0xcd, 0x26, 0x1d, 0x33, 0xb0, - 0x4d, 0xd8, 0x4a, 0xe7, 0xf3, 0xb0, 0x53, 0xb9, - 0x0d, 0x17, 0x7c, 0xf3, 0xb5, 0xec, 0x03, 0xa1, - 0x5b, 0x49, 0x8c, 0xa7, 0x5d, 0xa1, 0x85, 0x2a, - 0x5e, 0x95, 0x02, 0x2e, 0x4c, 0x86, 0x80, 0x21, - 0x59, 0x56, 0x1b, 0x95, 0xef, 0x3b, 0x9f, 0xf7, - 0xf9, 0xcb, 0x5a, 0xb3, 0x9e, 0x74, 0xfe, 0xfd, - 0x18, 0xdf, 0x75, 0xd6, 0x74, 0xf8, 0x53, 0xb9, - 0x0d, 0x12, 0xbc, 0xf6, 0x9c, 0x2f, 0x3a, 0x7f, - 0xff, 0x7a, 0xfc, 0xce, 0xf8, 0x2c, 0x1e, 0xfd, - 0x09, 0xea, 0x0e, 0x95, 0xb6, 0x44, 0x1f, 0x10, - 0xce, 0xe7, 0xb9, 0x53, 0x14, 0xc3, 0x8a, 0xc2, - 0xce, 0x75, 0xd7, 0x62, 0xa7, 0xff, 0x5b, 0xbd, - 0x75, 0x7f, 0x86, 0x8d, 0x00, 0x54, 0xfe, 0xe4, - 0x76, 0xc2, 0xdd, 0xb9, 0x4f, 0x9e, 0xa3, 0x92, - 0x72, 0x2a, 0x4b, 0x64, 0x6a, 0x2d, 0xa1, 0x59, - 0x3f, 0xf9, 0xcc, 0xe7, 0xbb, 0x05, 0x3b, 0x90, - 0xd1, 0x30, 0xcf, 0xff, 0xd8, 0xed, 0xe3, 0xc0, - 0x76, 0xbb, 0xbc, 0x2b, 0x57, 0x9d, 0x3f, 0xf6, - 0xa9, 0x94, 0xbf, 0x4e, 0xbf, 0xd5, 0xe7, 0x4f, - 0xf7, 0xd7, 0xa8, 0xbe, 0xbe, 0x13, 0xa7, 0xe6, - 0x2e, 0x7e, 0xfa, 0x87, 0x4f, 0x97, 0x3f, 0x7d, - 0x43, 0xa7, 0xeb, 0x7a, 0xf5, 0xea, 0x77, 0x1e, - 0xc3, 0x0b, 0xe7, 0xff, 0xfd, 0xfc, 0x0b, 0x7d, - 0xf2, 0xdb, 0xbf, 0x94, 0x7b, 0x3f, 0x4b, 0xea, - 0x74, 0xfc, 0xdb, 0x7d, 0xfe, 0xa0, 0x3a, 0x7f, - 0xf2, 0xdc, 0x0a, 0xfc, 0xa6, 0xf4, 0x68, 0x13, - 0xa1, 0x4f, 0xf7, 0xc6, 0x33, 0xf5, 0x7d, 0xad, - 0x9d, 0x73, 0xa7, 0xff, 0xfc, 0x38, 0xd9, 0x7f, - 0x7d, 0xdb, 0xfe, 0xb6, 0xef, 0x5d, 0x6f, 0x53, - 0xa5, 0x94, 0xa2, 0x7c, 0x4b, 0xe7, 0xbe, 0xa0, - 0xde, 0x74, 0xf7, 0x6f, 0xdb, 0x0e, 0x93, 0x9b, - 0x8a, 0xd0, 0x76, 0x4a, 0x48, 0x48, 0xb4, 0x91, - 0x68, 0x78, 0x7e, 0x18, 0x17, 0x28, 0xd0, 0x8e, - 0x7c, 0x29, 0xdc, 0x86, 0x8a, 0xba, 0x7f, 0x9e, - 0xec, 0x14, 0xee, 0x43, 0x44, 0x79, 0x27, 0x61, - 0xf8, 0xf1, 0x84, 0xfe, 0x76, 0x0a, 0x77, 0x21, - 0xa2, 0xc1, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, - 0x59, 0x53, 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x8b, - 0x4e, 0x79, 0x3b, 0x90, 0xd1, 0x6e, 0x4f, 0xfc, - 0xb8, 0x39, 0xfd, 0xd6, 0xca, 0x42, 0x74, 0x09, - 0xf7, 0xd4, 0xae, 0x7f, 0xdc, 0xf7, 0x60, 0xa7, - 0x72, 0x1a, 0x28, 0x79, 0xfb, 0xed, 0xcc, 0x15, - 0xa9, 0xd3, 0xff, 0xf6, 0x6d, 0x65, 0xde, 0xeb, - 0xff, 0x81, 0x5a, 0xa8, 0x0a, 0x93, 0xb1, 0x1e, - 0x6c, 0x21, 0xd1, 0x1b, 0x50, 0xbe, 0x7f, 0x3b, - 0x05, 0x3b, 0x90, 0xd1, 0x79, 0xc2, 0xaf, 0xf7, - 0x3d, 0x13, 0x65, 0x2c, 0x97, 0x3f, 0x4c, 0x3f, - 0xc4, 0x99, 0x87, 0x76, 0x3b, 0xfc, 0x73, 0xad, - 0x91, 0xa7, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x14, - 0xb4, 0xfe, 0x76, 0x0a, 0x77, 0x21, 0xa2, 0xc2, - 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x59, 0x73, - 0xce, 0xd7, 0xaa, 0xf9, 0xba, 0x9d, 0x39, 0xdb, - 0xd8, 0x74, 0xf2, 0x39, 0x6a, 0x87, 0xa5, 0xa1, - 0xa4, 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, 0xee, - 0x43, 0x45, 0x19, 0x3f, 0x9c, 0xad, 0x94, 0x54, - 0x07, 0x43, 0xd3, 0xbf, 0x13, 0xb6, 0x1d, 0xf4, - 0x23, 0x6a, 0x71, 0x75, 0x29, 0xff, 0x9d, 0xcf, - 0x76, 0x0a, 0x77, 0x21, 0xa2, 0x39, 0x9f, 0xfc, - 0xe6, 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x9c, - 0xa7, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x16, 0x64, - 0xfe, 0x76, 0x0a, 0x77, 0x21, 0xa2, 0xdd, 0x9f, - 0xfc, 0xe6, 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, - 0xa4, 0x27, 0xfe, 0x77, 0x3d, 0xd8, 0x29, 0xdc, - 0x86, 0x89, 0x4a, 0x28, 0x4e, 0xc9, 0xa4, 0xc6, - 0x29, 0x7c, 0xee, 0xe7, 0x6d, 0xea, 0x53, 0xfe, - 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x3b, 0x4f, - 0xff, 0xbd, 0xb6, 0x8c, 0x56, 0xfe, 0xdb, 0xeb, - 0xfc, 0x43, 0xa4, 0xe6, 0x22, 0x7f, 0x91, 0xa7, - 0xfe, 0xe6, 0x5b, 0x13, 0xac, 0xbb, 0xde, 0x74, - 0xff, 0xdf, 0xcb, 0x75, 0x19, 0x5f, 0xf5, 0x27, - 0x4d, 0xa8, 0xed, 0x91, 0x0f, 0x54, 0x38, 0x62, - 0x38, 0xb5, 0x21, 0x5f, 0x3e, 0x14, 0xee, 0x43, - 0x44, 0x59, 0x3f, 0xee, 0x7b, 0xb0, 0x53, 0xb9, - 0x0d, 0x12, 0xec, 0xff, 0xfd, 0x9b, 0x59, 0x77, - 0xba, 0xff, 0xe0, 0x56, 0xaa, 0x02, 0xa4, 0xec, - 0x46, 0x9b, 0x0c, 0x35, 0x11, 0xa7, 0xff, 0x39, - 0x9c, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x26, 0x29, - 0xfc, 0xec, 0x14, 0xee, 0x43, 0x45, 0x53, 0x3f, - 0xf9, 0xcc, 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, - 0x3a, 0x4f, 0xef, 0x70, 0xab, 0xf9, 0xb1, 0xd3, - 0xe6, 0x95, 0xa5, 0xa9, 0xd3, 0xf0, 0x73, 0xcd, - 0xb3, 0xe7, 0x4f, 0x7a, 0xca, 0xef, 0x1e, 0xb5, - 0x4a, 0x27, 0xfe, 0xd2, 0xb7, 0x5a, 0x5d, 0xa3, - 0x5e, 0x61, 0xd0, 0xc4, 0x41, 0x58, 0xe6, 0x7f, - 0xdc, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x27, 0x79, - 0xf5, 0xf4, 0xe6, 0x78, 0xa9, 0x3b, 0x64, 0xe6, - 0xf2, 0x30, 0xa6, 0x11, 0xf2, 0x34, 0xff, 0xe7, - 0x33, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x45, 0x0b, - 0x3f, 0xf9, 0xcc, 0xe7, 0xbb, 0x05, 0x3b, 0x90, - 0xd1, 0x49, 0x4f, 0xff, 0xec, 0xab, 0xb7, 0xe3, - 0x56, 0xe6, 0x5e, 0xcb, 0x7d, 0x18, 0x74, 0x50, - 0xb8, 0x06, 0xf3, 0x84, 0x52, 0x13, 0xb6, 0x47, - 0xda, 0x04, 0x9d, 0x14, 0xb5, 0x14, 0xa7, 0xf3, - 0xb0, 0x53, 0xb9, 0x0d, 0x11, 0x24, 0xff, 0xe7, - 0x33, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0xbd, - 0x3e, 0x14, 0xee, 0x43, 0x45, 0x2f, 0x3f, 0x97, - 0x66, 0xff, 0x31, 0x6a, 0x74, 0x9d, 0x87, 0xcd, - 0x73, 0x09, 0xfc, 0xec, 0x14, 0xee, 0x43, 0x45, - 0x39, 0x3f, 0xef, 0x0f, 0x56, 0xd9, 0x9f, 0x43, - 0xa7, 0xfd, 0x96, 0x51, 0xc7, 0x04, 0x21, 0x2a, - 0x6f, 0x04, 0xe9, 0xa8, 0x76, 0xc8, 0x8d, 0xd4, - 0x3b, 0x6f, 0x3c, 0x9f, 0x0a, 0x77, 0x21, 0xa2, - 0xbc, 0x9f, 0xff, 0xb3, 0x6b, 0x2e, 0xf7, 0x5f, - 0xfc, 0x0a, 0xd5, 0x40, 0x54, 0x9d, 0x88, 0x8d, - 0xd4, 0x30, 0x9f, 0xf9, 0xdc, 0xf7, 0x60, 0xa7, - 0x72, 0x1a, 0x24, 0x79, 0xde, 0x02, 0x9d, 0x39, - 0x31, 0x85, 0x38, 0xbb, 0x9f, 0x0a, 0x77, 0x21, - 0xa2, 0x48, 0x9e, 0x77, 0x3d, 0xca, 0x7b, 0x38, - 0x53, 0x3f, 0xf3, 0xb9, 0xee, 0xc1, 0x4e, 0xe4, - 0x34, 0x49, 0x53, 0xe1, 0x4e, 0xe4, 0x34, 0x5e, - 0x33, 0xf6, 0x9f, 0xa7, 0xa9, 0xa9, 0xd3, 0xea, - 0xd6, 0xf8, 0x87, 0x4f, 0xf3, 0xdd, 0x82, 0x9d, - 0xc8, 0x68, 0x93, 0x64, 0xec, 0x46, 0x3d, 0x26, - 0x02, 0x61, 0xc4, 0xd0, 0xab, 0xa5, 0x34, 0x1d, - 0xbe, 0x17, 0x1b, 0x13, 0xfa, 0x19, 0x0d, 0x43, - 0x45, 0x85, 0x76, 0x86, 0x05, 0xcd, 0xf4, 0x8c, - 0x4e, 0x16, 0x36, 0x9d, 0x1a, 0x23, 0xae, 0x7c, - 0x79, 0x3b, 0x47, 0xb5, 0x96, 0xe4, 0x47, 0x5c, - 0xa5, 0x64, 0xaf, 0xb3, 0x69, 0x9e, 0x7b, 0xf4, - 0xe6, 0x26, 0xae, 0x3b, 0x06, 0xa9, 0xa9, 0xe3, - 0x48, 0xf0, 0x66, 0x22, 0x86, 0x2d, 0x3b, 0x51, - 0xd4, 0xe7, 0x80, 0x4b, 0x0e, 0x6e, 0xe1, 0x63, - 0xf9, 0xe4, 0x8a, 0xd2, 0x4c, 0xef, 0x5c, 0x90, - 0x69, 0x4a, 0xef, 0x6d, 0x29, 0x80, 0x32, 0x83, - 0x75, 0x23, 0xde, 0xdf, 0x3a, 0x02, 0xdf, 0x3b, - 0x4d, 0x00, + 0x9e, 0x15, 0x3c, 0xaa, 0x0e, 0xde, 0xa5, 0xe8, + 0x47, 0xb0, 0xe2, 0xe7, 0x73, 0xf9, 0xd8, 0x29, + 0xdc, 0x86, 0x88, 0x7a, 0x7c, 0x29, 0xdc, 0x86, + 0x88, 0x9a, 0x6b, 0xb0, 0xe9, 0xff, 0xd9, 0x6d, + 0xf4, 0x55, 0x7d, 0x4d, 0xd5, 0x87, 0x45, 0x27, + 0xc7, 0x61, 0x69, 0xeb, 0xeb, 0xdb, 0xcd, 0x10, + 0xbc, 0x9d, 0x88, 0xe7, 0xac, 0x24, 0xf5, 0x23, + 0x9e, 0xbd, 0xbd, 0x73, 0xa7, 0xff, 0x79, 0xa7, + 0xe5, 0x2f, 0xd0, 0xce, 0xef, 0x9d, 0x37, 0x37, + 0x9d, 0x2d, 0xd3, 0x11, 0x0f, 0x62, 0x2e, 0x4d, + 0x9f, 0x5b, 0xaf, 0xcf, 0x3a, 0x7b, 0xd5, 0xf0, + 0x0e, 0x9c, 0x10, 0x84, 0xa9, 0xf7, 0x7e, 0xf6, + 0xc2, 0x9c, 0x5e, 0x4f, 0xba, 0xed, 0x65, 0xce, + 0x8d, 0x91, 0x34, 0x03, 0xef, 0x9a, 0xcf, 0xfc, + 0x0f, 0x35, 0xa2, 0xcb, 0xe1, 0xd7, 0x76, 0x74, + 0xff, 0xbd, 0xa5, 0x7e, 0xb5, 0x5f, 0x30, 0xe9, + 0xfb, 0x1b, 0x6a, 0xfc, 0xa9, 0xd3, 0xd9, 0xb6, + 0x50, 0x74, 0x62, 0x79, 0xbe, 0x86, 0x03, 0x46, + 0x36, 0x4c, 0xf9, 0xfd, 0xcb, 0xe7, 0xec, 0x0a, + 0xe6, 0xde, 0x3a, 0x7f, 0xff, 0xf7, 0xff, 0x4a, + 0xe9, 0xfe, 0x6d, 0x6f, 0x35, 0xa2, 0xb9, 0x4b, + 0xc7, 0x02, 0x74, 0xfb, 0xd7, 0xd5, 0x68, 0x3a, + 0x7f, 0x52, 0xfa, 0xe9, 0xd5, 0x69, 0x3a, 0x7f, + 0x70, 0x75, 0x69, 0x6f, 0xc7, 0x47, 0x8f, 0xaf, + 0xe7, 0x10, 0xa9, 0x9a, 0xe3, 0xf8, 0xc2, 0x52, + 0x6d, 0x5e, 0x74, 0xee, 0xbd, 0x4e, 0x9b, 0x5a, + 0x0e, 0x9e, 0x5f, 0xdd, 0xa7, 0x9b, 0x31, 0x1b, + 0x9f, 0xff, 0x6a, 0xb7, 0xd1, 0xfd, 0x5b, 0xf4, + 0x65, 0xf7, 0x32, 0xe7, 0x4f, 0xfd, 0x65, 0xd3, + 0xa2, 0xff, 0xeb, 0xad, 0xce, 0x9f, 0xda, 0x29, + 0x7f, 0xad, 0xab, 0x0e, 0x80, 0x1f, 0xff, 0xd1, + 0xe7, 0xfc, 0x2d, 0x68, 0xaa, 0xfa, 0x8e, 0xb9, + 0xd0, 0xa7, 0xc7, 0xa9, 0x14, 0xff, 0xff, 0xf6, + 0xbf, 0xa8, 0xfd, 0x5a, 0xd1, 0xbe, 0xfe, 0x7e, + 0x53, 0x7c, 0x6d, 0xfc, 0xa9, 0xd2, 0x53, 0xa7, + 0xf3, 0xf8, 0x7d, 0x55, 0xa4, 0xe9, 0xff, 0xff, + 0xfd, 0xe0, 0x57, 0xf9, 0xd4, 0xe8, 0xda, 0xda, + 0xe8, 0xfe, 0x52, 0xfc, 0xd6, 0x8f, 0xe7, 0x52, + 0x74, 0xee, 0xe4, 0x34, 0x53, 0x11, 0x88, 0xbf, + 0x48, 0x4c, 0xcf, 0xfb, 0x3f, 0x4b, 0xeb, 0x75, + 0x16, 0x8e, 0x9f, 0x53, 0xfc, 0x6d, 0x87, 0x47, + 0x8f, 0xa3, 0x8f, 0xe7, 0xfb, 0x99, 0xb5, 0x96, + 0x8c, 0x09, 0xd3, 0xff, 0xfb, 0xf8, 0x97, 0xba, + 0xde, 0xad, 0x7f, 0x03, 0xa1, 0x5b, 0xa9, 0xd3, + 0xfe, 0x45, 0x65, 0xb2, 0xb9, 0xef, 0x9d, 0x0a, + 0x8a, 0x4b, 0x34, 0x4f, 0x85, 0x5b, 0x28, 0x9d, + 0x3f, 0x77, 0xf4, 0x6c, 0xbf, 0x3a, 0x30, 0xfd, + 0xfc, 0x45, 0xa9, 0x34, 0x50, 0xbb, 0x3f, 0xb2, + 0x56, 0x93, 0x84, 0x8d, 0x0e, 0x92, 0x2f, 0x42, + 0x19, 0xa0, 0xf1, 0x87, 0x8b, 0x21, 0x1d, 0xf2, + 0x1b, 0xc6, 0xa9, 0x3f, 0xed, 0x23, 0xc1, 0xd1, + 0xdb, 0x75, 0x07, 0x4f, 0x67, 0xf4, 0xee, 0xce, + 0x9f, 0xff, 0xff, 0x5b, 0xd7, 0xb6, 0x0a, 0xb6, + 0xd0, 0xcd, 0x5b, 0xf4, 0x65, 0x2f, 0xcd, 0x7e, + 0x0a, 0x9d, 0x2b, 0xf9, 0x16, 0xd5, 0x27, 0x9f, + 0xff, 0x66, 0x5b, 0xc9, 0x6c, 0xd1, 0x5d, 0x6c, + 0xbb, 0xb3, 0xa7, 0xff, 0xaf, 0x6c, 0x06, 0x86, + 0x2e, 0x5f, 0x6c, 0xf9, 0xd3, 0xfd, 0x4e, 0x86, + 0x2e, 0x7e, 0xfb, 0x87, 0x4a, 0xb8, 0x88, 0xfe, + 0x51, 0x97, 0xbe, 0x98, 0x85, 0xe1, 0xd3, 0x3e, + 0x7a, 0xd2, 0xfa, 0x9d, 0x3f, 0xff, 0xff, 0xec, + 0xfe, 0x9d, 0x1e, 0xb6, 0xae, 0x6b, 0xd7, 0x56, + 0x7f, 0xb4, 0xdb, 0xce, 0xcb, 0x52, 0x05, 0xd8, + 0xa9, 0xff, 0xff, 0x93, 0xc1, 0xdb, 0x51, 0xd1, + 0xaa, 0xd1, 0x54, 0xb6, 0xb6, 0xa7, 0x82, 0x74, + 0xda, 0xd1, 0x4a, 0x68, 0xde, 0x28, 0xac, 0x29, + 0x61, 0x55, 0x79, 0xda, 0x33, 0x9f, 0xc6, 0xeb, + 0x3f, 0xf9, 0x89, 0xa2, 0xdc, 0xfc, 0x67, 0x7c, + 0x07, 0x4f, 0xff, 0xbd, 0xfb, 0xdb, 0x1b, 0xee, + 0xd6, 0x6e, 0xfb, 0xe0, 0x3a, 0x7f, 0xe5, 0xfd, + 0x1f, 0xc6, 0xd5, 0xd5, 0x69, 0x3a, 0x2a, 0x8a, + 0x5d, 0x57, 0x67, 0xb2, 0xf8, 0xdd, 0x9d, 0x3b, + 0x73, 0x2c, 0x74, 0xd9, 0xb1, 0xd1, 0x42, 0x6d, + 0x8f, 0x87, 0x7d, 0x49, 0x2e, 0x4a, 0xd8, 0x7a, + 0x7e, 0xd9, 0x7f, 0x6e, 0x79, 0xd3, 0x97, 0x65, + 0x3a, 0x7f, 0x34, 0xfc, 0xbd, 0x79, 0xad, 0x07, + 0x90, 0xc2, 0xd9, 0xf9, 0xbe, 0xdd, 0xbb, 0x50, + 0x1d, 0x3f, 0x85, 0xaf, 0xe5, 0xba, 0xe7, 0x46, + 0x1f, 0x2f, 0x8c, 0xe7, 0xe5, 0x06, 0x5f, 0x7d, + 0x07, 0x43, 0xcf, 0x43, 0xe4, 0x33, 0xff, 0xfe, + 0xbf, 0xf8, 0x15, 0xa3, 0x3d, 0xfb, 0xff, 0x1b, + 0x68, 0xa8, 0x7e, 0x74, 0xff, 0xbd, 0x8d, 0x68, + 0xaa, 0xdb, 0xb7, 0x67, 0x4f, 0xee, 0xa6, 0xb6, + 0xce, 0x13, 0xa3, 0x63, 0xf5, 0xba, 0x24, 0x98, + 0x74, 0xef, 0xe6, 0xc7, 0x42, 0x9a, 0xca, 0x44, + 0x27, 0xea, 0x6b, 0x99, 0xfd, 0x27, 0x4f, 0x33, + 0x7a, 0x80, 0xe8, 0xc3, 0xd2, 0xf1, 0x7c, 0x2a, + 0x79, 0x59, 0x0e, 0x7f, 0x27, 0x8b, 0x9c, 0xfe, + 0xbf, 0xa8, 0xcb, 0x6b, 0x73, 0xa7, 0xb2, 0x95, + 0x68, 0xe9, 0xfa, 0xdc, 0xf6, 0x0f, 0x8e, 0x9f, + 0x78, 0x03, 0x94, 0x9d, 0x1a, 0x51, 0x55, 0xe3, + 0x50, 0x11, 0x6a, 0x59, 0x3f, 0xff, 0x0b, 0xed, + 0x8d, 0x59, 0x7f, 0xa7, 0xf9, 0x7b, 0x6a, 0x74, + 0xff, 0xfe, 0xf6, 0x52, 0xfa, 0xde, 0xca, 0xc5, + 0xf6, 0xda, 0x15, 0xba, 0x9d, 0x3f, 0xff, 0xfe, + 0xd7, 0xfa, 0x03, 0xd4, 0xbc, 0x78, 0x1a, 0x2b, + 0xaa, 0xd3, 0x6c, 0xf7, 0xf3, 0x49, 0xd3, 0xff, + 0xed, 0x44, 0x72, 0xf5, 0xb7, 0xb3, 0x45, 0x80, + 0xa7, 0x46, 0x23, 0x8a, 0xd0, 0x8c, 0x9f, 0xfe, + 0x7b, 0x7d, 0xb1, 0x7e, 0xd7, 0xf2, 0xf5, 0xe3, + 0xa7, 0xff, 0xdb, 0x63, 0x34, 0x37, 0xdd, 0x6a, + 0xd3, 0xf8, 0x68, 0x3a, 0x7f, 0xb3, 0xf4, 0x68, + 0x07, 0x73, 0x47, 0x4f, 0xfa, 0xba, 0xff, 0x45, + 0x75, 0xdc, 0xd4, 0x4e, 0x9f, 0xfd, 0x46, 0x8a, + 0xdb, 0x5d, 0x17, 0xad, 0x54, 0x4e, 0x9f, 0xff, + 0xf6, 0x53, 0x5f, 0x60, 0x74, 0x5b, 0x2f, 0x5c, + 0xa3, 0x46, 0x7b, 0x63, 0xa3, 0x11, 0x85, 0xe5, + 0x08, 0xa1, 0x5c, 0x3a, 0x46, 0x99, 0x49, 0x3f, + 0x94, 0x98, 0xb9, 0x53, 0xbb, 0xc6, 0x13, 0x3f, + 0xee, 0x1b, 0x65, 0xd7, 0xfe, 0x01, 0xd3, 0xfd, + 0x81, 0xa5, 0xf5, 0xd1, 0x7f, 0x1d, 0x3f, 0xff, + 0xe5, 0x45, 0x66, 0x86, 0xad, 0xef, 0xed, 0xa3, + 0x7a, 0x7f, 0x2d, 0x87, 0x42, 0x23, 0xab, 0xe7, + 0x8d, 0xe7, 0x93, 0xde, 0xbf, 0x94, 0xe9, 0xff, + 0xff, 0xfa, 0xea, 0xcf, 0xfb, 0x4d, 0xb9, 0xad, + 0x1f, 0xd5, 0xbf, 0x46, 0x52, 0xfc, 0xd7, 0xe0, + 0xa9, 0xd0, 0xf4, 0x5c, 0x78, 0x86, 0x15, 0x79, + 0x3f, 0x25, 0xf2, 0xfa, 0x35, 0xde, 0x87, 0x54, + 0xf5, 0xeb, 0xc0, 0x3a, 0x7f, 0xeb, 0x7b, 0x47, + 0x87, 0x5a, 0x38, 0x50, 0xe9, 0xef, 0x5b, 0xa8, + 0x3a, 0x7e, 0xbf, 0x0f, 0xe9, 0xf1, 0xd1, 0x88, + 0xb6, 0xd2, 0x42, 0x88, 0xd7, 0x22, 0x9e, 0xdf, + 0xaf, 0x54, 0xe9, 0xff, 0xfb, 0xc3, 0xa3, 0x3d, + 0x9f, 0xd3, 0x6f, 0x63, 0x31, 0x0e, 0x9f, 0xff, + 0xef, 0x50, 0x2a, 0xca, 0xea, 0xba, 0x14, 0x57, + 0x6b, 0x2b, 0x0e, 0x8c, 0x46, 0x20, 0xae, 0x4f, + 0xff, 0xfe, 0x11, 0xf5, 0x1a, 0x2d, 0x97, 0xfa, + 0xb5, 0x6c, 0xa1, 0x69, 0xbe, 0xa8, 0x74, 0xff, + 0xff, 0xcb, 0xa6, 0xcb, 0xa3, 0xf9, 0xae, 0x9d, + 0x14, 0xbe, 0xbe, 0xfe, 0xf5, 0xa0, 0xe9, 0xf5, + 0x35, 0xf6, 0x7c, 0xe8, 0xc4, 0x53, 0xea, 0xff, + 0x1e, 0x4d, 0x1a, 0xd1, 0x91, 0xcf, 0xfc, 0xfa, + 0xfb, 0x4d, 0x97, 0xd4, 0x2e, 0x93, 0xa7, 0xfb, + 0xfa, 0xb5, 0xfc, 0xb7, 0x5c, 0xe9, 0xfe, 0xda, + 0xca, 0x1d, 0xad, 0xcc, 0x3a, 0x7f, 0xff, 0xb8, + 0x6d, 0x97, 0xf6, 0x7e, 0x8a, 0xae, 0x7f, 0x6b, + 0x29, 0xd3, 0xd7, 0xd1, 0x40, 0x9d, 0x14, 0xa2, + 0x27, 0x99, 0x67, 0xe6, 0xa9, 0x7e, 0xab, 0x73, + 0xa7, 0xfa, 0xdc, 0xcf, 0xe5, 0xba, 0xe7, 0x42, + 0x9f, 0x3d, 0x4c, 0x27, 0xec, 0xb8, 0x8e, 0x3c, + 0xe9, 0xfb, 0xf9, 0x6f, 0xe2, 0x1d, 0x3d, 0xfc, + 0xae, 0x86, 0x8f, 0x56, 0xe5, 0x51, 0xb2, 0xe0, + 0x9a, 0x46, 0xcf, 0x49, 0x57, 0x92, 0x44, 0xea, + 0xd0, 0xc3, 0xbc, 0x22, 0x9b, 0x3b, 0x4f, 0xfd, + 0xff, 0xe6, 0x81, 0xcb, 0x7f, 0x1a, 0x3a, 0x15, + 0x77, 0x1b, 0x27, 0x3e, 0xfa, 0x10, 0xd3, 0xef, + 0xab, 0x3b, 0x63, 0xa7, 0xf6, 0x35, 0x6c, 0xba, + 0xec, 0x74, 0xff, 0xff, 0xf5, 0xb2, 0xf5, 0xe6, + 0xb4, 0x7f, 0x29, 0x7e, 0x6b, 0x47, 0xf3, 0x6b, + 0x79, 0xa3, 0xa6, 0xd7, 0x49, 0xd0, 0x28, 0x9e, + 0xbc, 0x21, 0x67, 0xec, 0xa3, 0xf9, 0x4d, 0x4e, + 0x9f, 0xfc, 0x34, 0xbe, 0xb8, 0xca, 0x56, 0xd6, + 0x53, 0xa5, 0x53, 0xa3, 0x53, 0xda, 0xde, 0x95, + 0x3f, 0x95, 0x9f, 0xcb, 0x75, 0xce, 0x9f, 0xff, + 0xf5, 0xfe, 0xa0, 0xa5, 0xf5, 0xd5, 0x6f, 0xfc, + 0xdb, 0xd8, 0x2d, 0x1d, 0x1e, 0x54, 0xd8, 0xd1, + 0x3f, 0x43, 0x47, 0xe4, 0xf5, 0x84, 0x46, 0xa4, + 0xbb, 0xcc, 0x67, 0xcf, 0xf0, 0xe5, 0x27, 0x4f, + 0xb2, 0xb4, 0x60, 0x4e, 0x8a, 0x4f, 0x3a, 0xc4, + 0xf3, 0xff, 0xef, 0x51, 0x89, 0xb5, 0xbd, 0x9a, + 0x74, 0x69, 0xd5, 0x4e, 0x9e, 0x6f, 0xbe, 0x58, + 0xe9, 0xff, 0xff, 0x9b, 0xe9, 0x7f, 0x53, 0xa3, + 0xfa, 0xb7, 0xe8, 0xca, 0x5f, 0x9a, 0xfc, 0x15, + 0x3a, 0x28, 0x45, 0x36, 0x12, 0x4f, 0xff, 0xff, + 0x96, 0x9a, 0xea, 0xb4, 0xe8, 0xb6, 0x6d, 0xa3, + 0xf9, 0xae, 0x9d, 0x15, 0x00, 0x3d, 0x73, 0xa7, + 0xcb, 0xfa, 0x7a, 0x83, 0xa7, 0xff, 0xff, 0xff, + 0x63, 0x31, 0x16, 0xca, 0xcb, 0xe5, 0x6a, 0xa2, + 0x2b, 0xfc, 0xa3, 0x35, 0xae, 0xad, 0x7d, 0x58, + 0x74, 0xff, 0x03, 0x36, 0xfe, 0x5b, 0xae, 0x74, + 0xfe, 0xa3, 0x55, 0xbb, 0x3d, 0x73, 0xa7, 0xfc, + 0xba, 0x5b, 0xf5, 0xcf, 0xb5, 0xbd, 0x87, 0x46, + 0x1f, 0xe5, 0x26, 0xb3, 0xff, 0xd9, 0x7d, 0xb3, + 0x01, 0x5d, 0x03, 0x5d, 0xaa, 0x74, 0xf5, 0x1e, + 0xfd, 0xce, 0x85, 0x57, 0xe1, 0x84, 0x7e, 0x8c, + 0x0c, 0x48, 0xd9, 0x09, 0x1b, 0x14, 0x74, 0x28, + 0xff, 0x0b, 0x4a, 0x91, 0x6a, 0xa5, 0x3b, 0x6d, + 0x0d, 0x1d, 0x3f, 0xfd, 0x4b, 0xfe, 0xba, 0x19, + 0xf5, 0xd3, 0x7f, 0x50, 0x74, 0x61, 0xfb, 0x78, + 0x82, 0x7e, 0xa1, 0xa7, 0xb7, 0x06, 0x83, 0xa7, + 0xcc, 0xf6, 0xd9, 0xb1, 0xd2, 0x68, 0xe9, 0x95, + 0x87, 0x4a, 0xe7, 0x40, 0x9a, 0x5e, 0x15, 0x8d, + 0x8f, 0x56, 0xc6, 0xd3, 0xd7, 0xaf, 0x52, 0x74, + 0xcd, 0x71, 0xd3, 0xfa, 0xde, 0x1a, 0x5f, 0xf5, + 0x3a, 0x37, 0x52, 0x68, 0x0a, 0x69, 0x8f, 0xde, + 0x23, 0x61, 0x16, 0xa2, 0xd3, 0xfa, 0xbe, 0xa4, + 0x3d, 0xf0, 0x1d, 0x3f, 0xfe, 0x51, 0x54, 0xc6, + 0x2a, 0x3f, 0xd5, 0xd5, 0x87, 0x43, 0x48, 0x86, + 0xdc, 0x34, 0x9d, 0xfe, 0x61, 0xd3, 0x01, 0x4e, + 0x8f, 0x1b, 0x0f, 0x8d, 0xc9, 0xcd, 0xc7, 0x5a, + 0x34, 0xb2, 0x81, 0xa8, 0x5d, 0xda, 0x36, 0x1c, + 0x9d, 0xd2, 0xd3, 0x0b, 0xa4, 0x97, 0xd7, 0x4c, + 0x75, 0x3e, 0x84, 0xdb, 0x51, 0x81, 0x8c, 0x7d, + 0x8c, 0x87, 0x55, 0xa7, 0xa3, 0xfa, 0x30, 0x5f, + 0xcf, 0x01, 0xd6, 0x55, 0x4d, 0xe7, 0x2c, 0x5b, + 0x46, 0x2b, 0xb9, 0x1b, 0x0e, 0xf8, 0x71, 0xb7, + 0xab, 0xcf, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x29, + 0xd9, 0xf0, 0xa7, 0x72, 0x1a, 0x2a, 0x39, 0xff, + 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x9a, 0x24, + 0xec, 0x3f, 0x66, 0x18, 0x4f, 0xe7, 0x60, 0xa7, + 0x72, 0x1a, 0x2a, 0xf9, 0xf0, 0xa7, 0x72, 0x1a, + 0x2b, 0x69, 0xfe, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, + 0x12, 0x0c, 0x9d, 0x87, 0xe3, 0xc6, 0x13, 0xff, + 0x3b, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0x87, + 0x3e, 0x14, 0xee, 0x43, 0x45, 0x89, 0x3e, 0xc1, + 0x5f, 0xe9, 0x3a, 0x7f, 0x59, 0x77, 0xee, 0x60, + 0xa9, 0xd3, 0xff, 0x2e, 0x57, 0x3f, 0xb7, 0x80, + 0xbf, 0x3a, 0x77, 0xd6, 0x83, 0xa4, 0xff, 0x9e, + 0xfe, 0xa8, 0x73, 0xb7, 0x3c, 0x03, 0xa7, 0x0a, + 0xbc, 0xe9, 0xfd, 0x82, 0xf0, 0x66, 0x34, 0x74, + 0xfb, 0x9f, 0xbe, 0xca, 0x74, 0xfd, 0x5a, 0x8f, + 0x83, 0xbb, 0x3a, 0x15, 0x11, 0x5e, 0x31, 0xe2, + 0x89, 0xff, 0xe7, 0xab, 0x15, 0xfb, 0x7d, 0x6d, + 0xd8, 0x27, 0x4f, 0xe4, 0xfe, 0x36, 0xfe, 0x54, + 0xe8, 0xa5, 0x10, 0x16, 0x4d, 0x9f, 0xc0, 0x5c, + 0xbf, 0xbe, 0x12, 0xa3, 0x15, 0x6d, 0xe9, 0x30, + 0x42, 0x7a, 0x61, 0x29, 0xe2, 0x91, 0x1f, 0xb4, + 0x29, 0x3a, 0x15, 0xc0, 0x24, 0x9f, 0xfa, 0xde, + 0x5b, 0xdb, 0xb4, 0xe7, 0xc0, 0x74, 0xff, 0xf6, + 0x52, 0xfb, 0xeb, 0x43, 0xad, 0xcc, 0xf5, 0x4e, + 0x9a, 0xce, 0xc4, 0x4c, 0x7d, 0x16, 0x1c, 0x9a, + 0xa2, 0xc6, 0x4d, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, + 0x68, 0xb3, 0x67, 0xff, 0x39, 0x9c, 0xf7, 0x60, + 0xa7, 0x72, 0x1a, 0x28, 0x09, 0xfd, 0xfc, 0xb8, + 0x37, 0xfd, 0xa3, 0xa6, 0xd1, 0x53, 0xa7, 0x93, + 0xb9, 0x0d, 0x16, 0xfc, 0xfd, 0xbe, 0xfc, 0x9c, + 0x27, 0x40, 0x9e, 0xb8, 0x0a, 0xe7, 0xd7, 0x7d, + 0x77, 0xd4, 0xe9, 0xfc, 0xad, 0xb7, 0xdf, 0xea, + 0x03, 0xa7, 0xbb, 0xdf, 0xb9, 0xd3, 0xfb, 0x5a, + 0x6a, 0xa9, 0x8c, 0x3a, 0x7a, 0xda, 0x76, 0x61, + 0xd3, 0x63, 0xce, 0x8c, 0x37, 0x5f, 0x25, 0x93, + 0x95, 0x3d, 0x47, 0x9b, 0x63, 0x78, 0x90, 0xd8, + 0xa8, 0x06, 0xdf, 0x21, 0xbb, 0x6c, 0xff, 0xb9, + 0xee, 0xc1, 0x4e, 0xe4, 0x34, 0x52, 0x93, 0xfc, + 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x24, 0xe9, 0x39, + 0x88, 0x87, 0xe4, 0x68, 0x57, 0x74, 0xf3, 0x44, + 0x64, 0x5b, 0x5a, 0xdd, 0xcc, 0x8e, 0xa1, 0x21, + 0x63, 0xe2, 0x7d, 0xdc, 0x2b, 0x1a, 0x26, 0x64, + 0xe3, 0xdd, 0x8a, 0x40, 0x77, 0x59, 0x44, 0xcd, + 0xa1, 0xf9, 0x3e, 0x14, 0xee, 0x43, 0x44, 0x3f, + 0x3f, 0xee, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, 0x12, + 0x9c, 0x9d, 0x87, 0xec, 0xc3, 0x09, 0xfc, 0xec, + 0x14, 0xee, 0x43, 0x44, 0x4f, 0x3f, 0x9d, 0x82, + 0x9d, 0xc8, 0x68, 0x8c, 0x67, 0xff, 0x39, 0x9c, + 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x27, 0x98, 0x54, + 0x77, 0xd0, 0x4e, 0xf3, 0xbb, 0x1d, 0xcf, 0x85, + 0x3b, 0x90, 0xd1, 0x10, 0x4f, 0xfb, 0x9e, 0xec, + 0x14, 0xee, 0x43, 0x44, 0xa9, 0x27, 0x61, 0xfb, + 0x30, 0xc2, 0x7f, 0x3b, 0x05, 0x3b, 0x90, 0xd1, + 0x14, 0x4f, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x23, + 0x29, 0xff, 0xce, 0x67, 0x3d, 0xd8, 0x29, 0xdc, + 0x86, 0x89, 0x96, 0x7f, 0x3b, 0x05, 0x3b, 0x90, + 0xd1, 0x52, 0x4f, 0xe7, 0x60, 0xa7, 0x72, 0x1a, + 0x2b, 0xa9, 0xfc, 0xec, 0x14, 0xee, 0x43, 0x45, + 0x8b, 0x3f, 0xf3, 0x39, 0xee, 0xc1, 0x4e, 0xe4, + 0x34, 0x4f, 0x53, 0xfc, 0x17, 0x72, 0xfb, 0x73, + 0xcd, 0xe7, 0x43, 0x91, 0x14, 0xa9, 0x93, 0xf6, + 0xeb, 0xb2, 0xfe, 0xd6, 0xa7, 0x4f, 0x0b, 0x2f, + 0x49, 0xd3, 0x93, 0x2c, 0x54, 0xff, 0xfd, 0x6f, + 0xf6, 0x9f, 0x0a, 0xbd, 0xf9, 0x50, 0x01, 0x4e, + 0x9f, 0xff, 0x87, 0xcd, 0xfc, 0xc7, 0x28, 0x8f, + 0x9b, 0xd3, 0x5a, 0x0e, 0x9f, 0x27, 0xad, 0xbd, + 0x87, 0x4f, 0xfe, 0xea, 0xff, 0x00, 0xeb, 0x79, + 0x38, 0x4e, 0x9f, 0x5e, 0xca, 0xc4, 0x3a, 0x7f, + 0xf7, 0xf4, 0x2a, 0xfd, 0x59, 0xa3, 0x45, 0xb7, + 0x0e, 0x9b, 0xa9, 0x3a, 0x7f, 0x75, 0x7a, 0xda, + 0xfe, 0x83, 0xa3, 0xe7, 0x95, 0xa8, 0xb4, 0x79, + 0x50, 0x80, 0xad, 0xb1, 0x82, 0xc5, 0x3f, 0x47, + 0xb9, 0x33, 0x7c, 0x27, 0x27, 0x7f, 0xae, 0x6a, + 0x85, 0xa7, 0x7f, 0xd5, 0x3a, 0x41, 0xdd, 0x0f, + 0x11, 0x4a, 0x27, 0x85, 0xb7, 0x30, 0xe8, 0xc3, + 0xce, 0xf1, 0x64, 0xff, 0xbd, 0x6e, 0xba, 0xb5, + 0x5f, 0x30, 0xe9, 0xf7, 0xf4, 0xfb, 0x2a, 0x74, + 0xff, 0xd8, 0x1e, 0x06, 0x25, 0xb8, 0x15, 0x3a, + 0x3c, 0x7d, 0x5f, 0x29, 0x9e, 0xfe, 0xda, 0x34, + 0x9d, 0x3f, 0x65, 0x43, 0xdf, 0xa0, 0xe8, 0xe3, + 0xd4, 0xf9, 0x3c, 0xfb, 0x5d, 0x35, 0xd9, 0x4e, + 0x8c, 0x46, 0x17, 0x9d, 0xac, 0x43, 0x3f, 0xff, + 0xfa, 0xcb, 0xe1, 0x15, 0xdf, 0x7f, 0x7f, 0x57, + 0xe5, 0x76, 0xd7, 0x85, 0xe7, 0x4f, 0xa8, 0xd8, + 0x7c, 0x13, 0xa7, 0xed, 0xb8, 0x41, 0xb9, 0x87, + 0x4f, 0xfb, 0xfe, 0x7d, 0xed, 0x83, 0xea, 0x9d, + 0x3f, 0xee, 0xc0, 0x2f, 0xfb, 0x6c, 0xd2, 0x74, + 0x3c, 0xff, 0x00, 0x7d, 0x3f, 0xf9, 0x72, 0x9b, + 0xd9, 0x6f, 0xf5, 0x05, 0x4e, 0x9d, 0x5b, 0x30, + 0xe8, 0xb9, 0xf2, 0xea, 0x93, 0x3f, 0x6b, 0x5b, + 0xd7, 0x2e, 0x74, 0xe0, 0x84, 0x25, 0x4f, 0xfe, + 0xbf, 0xaf, 0x5b, 0x7b, 0x73, 0x78, 0xab, 0xca, + 0x71, 0x79, 0x1e, 0x45, 0x46, 0xa9, 0x50, 0xf5, + 0x58, 0x98, 0xf5, 0xe2, 0x91, 0x85, 0x5f, 0xe1, + 0x0f, 0x78, 0x65, 0xcd, 0xa7, 0x0e, 0x9f, 0x06, + 0xeb, 0x48, 0x4e, 0x9f, 0xcb, 0xc3, 0x60, 0xf6, + 0xc5, 0x4c, 0x10, 0x95, 0x1b, 0x1e, 0x38, 0x4c, + 0x67, 0xab, 0x7f, 0x6e, 0xca, 0x71, 0xa3, 0x98, + 0x35, 0x3a, 0x58, 0x74, 0xa9, 0xc3, 0x4b, 0x71, + 0x69, 0xff, 0xab, 0x5d, 0xbe, 0xbb, 0x59, 0x6f, + 0x53, 0xa1, 0x4f, 0xb7, 0x52, 0x79, 0xff, 0x5f, + 0xc3, 0xc0, 0x6f, 0xbe, 0x58, 0xe8, 0x79, 0xf1, + 0x78, 0x8a, 0x7d, 0x94, 0x67, 0x84, 0xe9, 0xfa, + 0xea, 0x3b, 0x67, 0xce, 0x9f, 0xdb, 0x59, 0x7e, + 0x1e, 0xa4, 0xe9, 0xff, 0xbd, 0xb5, 0x95, 0x14, + 0x1d, 0xf0, 0x1d, 0x3f, 0xff, 0xef, 0x33, 0xbf, + 0x4b, 0xdc, 0x1c, 0xf3, 0x6c, 0xfe, 0x6d, 0xcf, + 0x3c, 0x5e, 0xb0, 0xa9, 0x8f, 0xf1, 0x65, 0x4d, + 0x35, 0x43, 0x9f, 0xfe, 0xcb, 0xd7, 0x1a, 0xb2, + 0xb6, 0xcb, 0x59, 0x4e, 0x9f, 0xff, 0xff, 0x7b, + 0x29, 0xaf, 0xb0, 0x3a, 0x32, 0x97, 0xd7, 0xd7, + 0xaf, 0xa9, 0xdb, 0x6d, 0x68, 0x3a, 0x3e, 0x8d, + 0xeb, 0xa8, 0x4d, 0xe0, 0x9d, 0x36, 0xf0, 0x1d, + 0x0d, 0x1a, 0xe0, 0x0b, 0x4e, 0x7f, 0x6c, 0x74, + 0xc1, 0x09, 0xd1, 0xe3, 0xd4, 0xa9, 0x10, 0x47, + 0x27, 0x5f, 0x78, 0x0a, 0x71, 0xaf, 0x9f, 0xff, + 0xfd, 0x75, 0xda, 0xda, 0xf6, 0x96, 0x76, 0xd9, + 0x9f, 0xd3, 0x5a, 0xe7, 0xc0, 0x74, 0x52, 0x8a, + 0xbf, 0x17, 0x4f, 0xf6, 0x5e, 0xca, 0x37, 0xc4, + 0x3a, 0x72, 0x8b, 0x47, 0x46, 0x27, 0xe7, 0xd1, + 0xbf, 0xfc, 0x93, 0x70, 0xd6, 0x7d, 0xfc, 0xbf, + 0x37, 0x9d, 0x3f, 0xf6, 0x08, 0xe3, 0x56, 0xca, + 0xf7, 0xce, 0x9f, 0xdf, 0x51, 0xd5, 0x2c, 0xa7, + 0x4f, 0xbd, 0xa6, 0xdc, 0xf3, 0xa7, 0xef, 0x2b, + 0x28, 0xc0, 0x9d, 0x30, 0x42, 0x74, 0x21, 0xf6, + 0x09, 0x48, 0x4b, 0x67, 0xfb, 0x1a, 0xe6, 0xba, + 0x97, 0xd4, 0xa7, 0x1a, 0xd9, 0xff, 0xd9, 0xf0, + 0x65, 0xbd, 0x4d, 0x6d, 0xad, 0x4e, 0x9f, 0xbf, + 0xc0, 0xdb, 0x3e, 0x74, 0xfc, 0xcf, 0x6e, 0x60, + 0xe9, 0x3a, 0x70, 0x42, 0x12, 0xa7, 0xfe, 0x51, + 0xfe, 0xbd, 0xbf, 0x1a, 0x56, 0x1c, 0xe2, 0xf2, + 0x37, 0x6a, 0x8b, 0x9a, 0x86, 0x55, 0x52, 0xae, + 0x99, 0xa9, 0x68, 0x53, 0x67, 0x70, 0xd0, 0x79, + 0x04, 0xa7, 0x9f, 0x94, 0xbc, 0xf2, 0x09, 0x4e, + 0xaf, 0xb6, 0x3c, 0x82, 0x53, 0x04, 0x27, 0x90, + 0x4a, 0x11, 0x14, 0x8d, 0x14, 0x5c, 0xbc, 0x25, + 0x53, 0x75, 0xcb, 0x20, 0x91, 0xc6, 0xf6, 0x7e, + 0xcb, 0x66, 0x7f, 0x49, 0xd3, 0xbb, 0xe0, 0xf9, + 0xf0, 0x5c, 0xce, 0x7b, 0x73, 0x3a, 0xc7, 0x4b, + 0x37, 0x67, 0xac, 0x03, 0x39, 0xfc, 0xd3, 0xf5, + 0xa2, 0xfe, 0xa0, 0xa9, 0xfd, 0xe7, 0xeb, 0xd5, + 0xb7, 0x8e, 0x93, 0xca, 0x9d, 0xc3, 0x41, 0x50, + 0x54, 0x29, 0xb5, 0x41, 0x06, 0x0d, 0xcf, 0x7d, + 0x40, 0x85, 0x38, 0xd6, 0x42, 0xa3, 0x17, 0x21, + 0x2b, 0x3d, 0xaf, 0x0b, 0xce, 0x93, 0x0e, 0x9b, + 0x1f, 0xb1, 0xb1, 0x68, 0x86, 0x7c, 0x1c, 0x6b, + 0xcc, 0x3a, 0x7e, 0x45, 0x69, 0xea, 0x02, 0xa5, + 0xe3, 0xa7, 0xfa, 0x8d, 0x5f, 0xb5, 0xb5, 0xef, + 0x9d, 0x3f, 0x79, 0x59, 0x46, 0x04, 0xe9, 0xf6, + 0x50, 0xcc, 0x61, 0xd3, 0xab, 0xe1, 0x3a, 0x3e, + 0x78, 0x57, 0x27, 0x9f, 0xbf, 0xc0, 0x06, 0xb5, + 0x3a, 0x7e, 0xf6, 0x9d, 0xea, 0xc7, 0x6c, 0x98, + 0x66, 0x08, 0x09, 0xed, 0x5b, 0xee, 0x45, 0x18, + 0x9e, 0x8f, 0x8a, 0x3f, 0x19, 0xc4, 0xf7, 0xf2, + 0x8d, 0x4e, 0x87, 0xab, 0xd7, 0xc2, 0xbb, 0x43, + 0xf7, 0xea, 0xf5, 0x8f, 0xfe, 0xe6, 0xd3, 0xff, + 0xea, 0x04, 0x73, 0x6e, 0x65, 0xba, 0xff, 0x56, + 0x8e, 0x9c, 0x10, 0x84, 0xa9, 0x9e, 0xa5, 0x38, + 0xbc, 0x85, 0x44, 0xa8, 0xb8, 0xcf, 0xd4, 0x34, + 0xf6, 0xe0, 0xd0, 0x74, 0xf5, 0xba, 0x90, 0x9d, + 0x3e, 0xa0, 0x7c, 0xac, 0x3a, 0x7f, 0xf9, 0x77, + 0xdf, 0xea, 0x0e, 0xb8, 0x37, 0xfd, 0xe7, 0x46, + 0xea, 0x46, 0x87, 0x8d, 0x38, 0x8a, 0xe4, 0xf3, + 0x9f, 0xef, 0x9d, 0x3f, 0x5d, 0x5a, 0xfe, 0xb5, + 0x3a, 0x1e, 0x79, 0x77, 0x1c, 0x9d, 0xae, 0xad, + 0xd9, 0xd3, 0x73, 0x9b, 0xa3, 0x74, 0x31, 0xba, + 0xc6, 0xdb, 0xaf, 0xc8, 0x96, 0x51, 0x55, 0x10, + 0xff, 0x79, 0x0e, 0xd1, 0xb5, 0xe4, 0xaf, 0x3d, + 0x2c, 0xc8, 0x2f, 0xe8, 0x49, 0x35, 0x19, 0x50, + 0x91, 0x32, 0x34, 0x5b, 0x4b, 0x0e, 0xe4, 0x5f, + 0x94, 0xd6, 0x53, 0x7d, 0xe7, 0x74, 0xb5, 0x87, + 0xc6, 0xe4, 0x3c, 0x77, 0xc2, 0x35, 0xbc, 0x8a, + 0x7f, 0xf3, 0x99, 0xcf, 0x76, 0x0a, 0x77, 0x21, + 0xa2, 0x8a, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, + 0x5d, 0x13, 0xff, 0x3b, 0x9e, 0xec, 0x14, 0xee, + 0x43, 0x44, 0x97, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, + 0x68, 0xbc, 0xa1, 0x5b, 0xf2, 0xba, 0x09, 0xde, + 0x77, 0x87, 0x68, 0xa4, 0xd1, 0xdb, 0x0e, 0xed, + 0x0c, 0x5f, 0xd7, 0x47, 0x55, 0x8e, 0xce, 0xea, + 0x5a, 0x9d, 0xb6, 0x4c, 0x9f, 0xfc, 0xe6, 0x73, + 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x96, 0x67, 0x93, + 0xb9, 0x0d, 0x11, 0x9c, 0xfd, 0xe5, 0x65, 0x18, + 0x13, 0xa6, 0xfb, 0xce, 0x93, 0x0e, 0x9f, 0x78, + 0x6d, 0xce, 0x13, 0xd3, 0x61, 0x6e, 0xf1, 0x69, + 0xfb, 0x9f, 0xe1, 0xe1, 0x3a, 0x75, 0x56, 0x93, + 0xa0, 0x53, 0x10, 0x63, 0xf7, 0xd3, 0x2e, 0x55, + 0x3f, 0xf5, 0xfc, 0x3c, 0x0d, 0x14, 0xbd, 0x58, + 0x74, 0xfb, 0xea, 0xfa, 0xf1, 0xd1, 0xe3, 0xec, + 0x6c, 0x8d, 0x3f, 0xff, 0x7b, 0x6d, 0x6f, 0x5a, + 0x5e, 0xbf, 0x05, 0x74, 0x6b, 0xf3, 0xa7, 0xfe, + 0xc6, 0x5f, 0xc1, 0xaa, 0xdb, 0xb7, 0x67, 0x4f, + 0xfa, 0xcb, 0xeb, 0x79, 0x7e, 0xd6, 0xa7, 0x49, + 0xd8, 0x9d, 0xbb, 0x50, 0xa9, 0xe2, 0x4f, 0xb1, + 0xd5, 0x1e, 0x7f, 0xf3, 0x99, 0xcf, 0x76, 0x0a, + 0x77, 0x21, 0xa2, 0x69, 0x9f, 0xfc, 0xe6, 0x73, + 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x9c, 0x67, 0xff, + 0x39, 0x9c, 0xf7, 0x60, 0xa7, 0x72, 0x1a, 0x28, + 0x19, 0xf0, 0xa7, 0x72, 0x1a, 0x2e, 0x09, 0x97, + 0xc7, 0x4f, 0xef, 0xbd, 0x7d, 0xb6, 0x30, 0xe9, + 0x3b, 0x0f, 0xe2, 0xc6, 0x1a, 0x8a, 0xcf, 0xef, + 0x3b, 0x7a, 0x83, 0x02, 0x74, 0xff, 0x3d, 0xd8, + 0x29, 0xdc, 0x86, 0x89, 0x26, 0x4e, 0xb1, 0xfa, + 0xf1, 0xa4, 0x50, 0xbb, 0x5a, 0xf9, 0x63, 0x28, + 0xaa, 0xd2, 0x90, 0x14, 0xab, 0x0c, 0xab, 0xc2, + 0xaa, 0x7f, 0xda, 0x79, 0xd8, 0x29, 0xdc, 0x86, + 0x8b, 0x52, 0x7f, 0xdc, 0xf7, 0x60, 0xa7, 0x72, + 0x1a, 0x25, 0x58, 0x3a, 0x4e, 0xd2, 0x89, 0x86, + 0x24, 0x37, 0xa3, 0x4f, 0xe7, 0x60, 0xa7, 0x72, + 0x1a, 0x22, 0x99, 0xfc, 0xec, 0x14, 0xee, 0x43, + 0x44, 0x69, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, + 0xa7, 0xe7, 0xff, 0x39, 0x9c, 0xf7, 0x60, 0xa7, + 0x72, 0x1a, 0x26, 0xa9, 0xfc, 0xec, 0x14, 0xee, + 0x43, 0x45, 0x77, 0x3c, 0x9d, 0xc8, 0x68, 0xaf, + 0xa7, 0x04, 0x21, 0x2a, 0x5f, 0x29, 0xc5, 0xe4, + 0x09, 0xf3, 0x31, 0x1e, 0x73, 0xd6, 0x93, 0xa7, + 0xfd, 0x96, 0x0f, 0x5e, 0xab, 0x94, 0x1d, 0x2b, + 0x9d, 0x3f, 0xde, 0x1c, 0xa3, 0xcf, 0xcd, 0x8e, + 0x81, 0x3c, 0x9e, 0x10, 0x93, 0xb1, 0x1c, 0x6d, + 0x10, 0xfc, 0x72, 0xb0, 0x86, 0x9f, 0xfc, 0xe6, + 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x9e, 0xe7, + 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x17, 0x0c, 0xff, + 0xce, 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x27, + 0xc2, 0xab, 0xe3, 0xa0, 0xed, 0xe7, 0x7a, 0x4e, + 0xd0, 0xed, 0xa5, 0x21, 0x8e, 0x12, 0xc9, 0x15, + 0x52, 0x6c, 0x77, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, + 0x68, 0x8a, 0xa7, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, + 0x14, 0xcc, 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, + 0xee, 0x43, 0x44, 0xcf, 0x3f, 0xf9, 0xcc, 0xe7, + 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x46, 0xc5, 0x09, + 0x8d, 0x6c, 0x77, 0xa4, 0xee, 0xea, 0x53, 0xff, + 0x3b, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0x75, + 0x3f, 0x7b, 0x3f, 0xb6, 0x50, 0x74, 0xf8, 0x53, + 0xb9, 0x0d, 0x14, 0xd4, 0xff, 0xcf, 0xb7, 0xbf, + 0x45, 0x75, 0x51, 0x68, 0xe9, 0xfb, 0xca, 0xca, + 0x30, 0x27, 0x4f, 0xff, 0x65, 0xed, 0x8f, 0x1e, + 0xfd, 0xed, 0xcf, 0x3a, 0x65, 0x01, 0xd3, 0xfd, + 0x5e, 0xa5, 0x53, 0xc8, 0xe1, 0x44, 0x7f, 0xcb, + 0x77, 0xa6, 0xc9, 0xdb, 0x27, 0x25, 0x85, 0xd4, + 0x98, 0x5e, 0x1a, 0xd3, 0xe1, 0x4e, 0xe4, 0x34, + 0x55, 0x53, 0xfe, 0xe7, 0xbb, 0x05, 0x3b, 0x90, + 0xd1, 0x36, 0xc9, 0xd8, 0x7e, 0xcc, 0x30, 0x9f, + 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x57, 0xf3, 0xf9, + 0xd8, 0x29, 0xdc, 0x86, 0x8b, 0x1a, 0x7c, 0x29, + 0xdc, 0x86, 0x8b, 0x56, 0x7f, 0xdc, 0xf7, 0x60, + 0xa7, 0x72, 0x1a, 0x28, 0x29, 0x3b, 0x0f, 0xd9, + 0x86, 0x13, 0xe1, 0x4e, 0xe4, 0x34, 0x5c, 0x53, + 0xcd, 0xed, 0xfe, 0x43, 0xa7, 0xf2, 0xb3, 0x9b, + 0x28, 0xb7, 0x9d, 0x27, 0x62, 0x23, 0x38, 0xc2, + 0xe4, 0xf3, 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x8b, + 0xa6, 0x7f, 0xff, 0x2d, 0xbf, 0xaf, 0x6e, 0xf1, + 0xf6, 0xcd, 0x39, 0xb3, 0x5a, 0x9d, 0x3f, 0xe7, + 0xe0, 0x8f, 0x3e, 0x8c, 0x09, 0xd3, 0xbf, 0xce, + 0x14, 0x52, 0xdd, 0xa6, 0x7f, 0xc2, 0xb4, 0xdb, + 0xab, 0x75, 0x09, 0xd3, 0xf6, 0xe9, 0xfc, 0xb7, + 0x5c, 0xe9, 0xf8, 0x1c, 0xd3, 0xfd, 0x73, 0xa7, + 0xff, 0xfd, 0xa5, 0x76, 0xb6, 0x53, 0x7f, 0xf5, + 0x81, 0xd5, 0xaf, 0x0a, 0x1d, 0x3c, 0x9d, 0xc8, + 0x68, 0x93, 0x27, 0xf8, 0x72, 0x9b, 0xdf, 0x9f, + 0x53, 0xa0, 0x4f, 0x8d, 0x85, 0x73, 0xf6, 0xc0, + 0xdd, 0x2a, 0x0d, 0xc3, 0xa7, 0xff, 0x5b, 0xd7, + 0xa5, 0x3c, 0x35, 0x69, 0x78, 0xe9, 0xf6, 0x5f, + 0xcc, 0xe3, 0xa7, 0xff, 0xdc, 0x82, 0xb6, 0xfe, + 0x5f, 0x1c, 0x10, 0x84, 0xa8, 0x79, 0xfc, 0x5c, + 0x9a, 0x7f, 0xf6, 0xbf, 0xeb, 0xa8, 0xd6, 0xcb, + 0xfb, 0x9d, 0x38, 0x21, 0x09, 0x53, 0xf0, 0xf5, + 0x0c, 0xf5, 0x4a, 0x71, 0x79, 0x3e, 0xc6, 0x0e, + 0x6e, 0xce, 0x9f, 0x7a, 0xf4, 0x60, 0x4e, 0x9f, + 0xe5, 0x4b, 0x7f, 0x5b, 0xad, 0x4e, 0x81, 0x3e, + 0x06, 0x14, 0x4f, 0xf6, 0x70, 0x34, 0x66, 0xdc, + 0xf3, 0xa7, 0xff, 0x75, 0x16, 0xf2, 0xff, 0x1a, + 0xf0, 0xf8, 0xe8, 0xc4, 0x52, 0x5c, 0x84, 0x27, + 0x33, 0xef, 0xe5, 0xba, 0xe7, 0x4f, 0xf9, 0xeb, + 0xfa, 0x37, 0x8f, 0xf5, 0x79, 0xd1, 0xbb, 0x3e, + 0x86, 0x89, 0xa7, 0xaf, 0x46, 0x6c, 0x74, 0xff, + 0x7b, 0x4e, 0xf1, 0x03, 0x3d, 0x53, 0xa1, 0xa3, + 0xe0, 0xdc, 0x23, 0x9c, 0x10, 0x84, 0xe9, 0xff, + 0xf6, 0x32, 0xff, 0x50, 0x66, 0xd5, 0xc6, 0x2a, + 0x14, 0xe2, 0xf2, 0x31, 0x32, 0xbf, 0x42, 0x1e, + 0xc8, 0x53, 0xf5, 0x97, 0x42, 0x7a, 0x83, 0xa7, + 0xe1, 0xe1, 0xfa, 0xbc, 0xe8, 0xd8, 0xf6, 0x44, + 0xbe, 0x67, 0xb9, 0xb8, 0xbd, 0x56, 0xa6, 0x2f, + 0x2e, 0xc8, 0x68, 0x69, 0x21, 0x43, 0x7a, 0x61, + 0x84, 0xd1, 0x17, 0x32, 0xfc, 0xf2, 0xb1, 0x96, + 0xde, 0x3c, 0x2d, 0xf0, 0x8e, 0x9f, 0x63, 0x07, + 0x37, 0x67, 0x4f, 0xfd, 0x96, 0xfa, 0xd1, 0xbe, + 0xd6, 0xed, 0xd9, 0xd3, 0xf7, 0x23, 0x82, 0x10, + 0x9d, 0x27, 0x23, 0x22, 0x70, 0x4c, 0xfa, 0x7b, + 0xa3, 0xf0, 0xe2, 0xa9, 0x4b, 0x64, 0xa8, 0x7b, + 0x3a, 0x07, 0x68, 0xf7, 0xa9, 0x86, 0x00, 0x93, + 0xb0, 0xec, 0x10, 0xb1, 0xac, 0x2c, 0xee, 0x69, + 0xad, 0x25, 0xf2, 0x7f, 0x3b, 0x05, 0x3b, 0x90, + 0xd1, 0x1a, 0xcf, 0x85, 0x3b, 0x90, 0xd1, 0x52, + 0xcd, 0xc8, 0x68, 0x86, 0xa4, 0xec, 0x3d, 0x1e, + 0x30, 0x9f, 0xf9, 0xdc, 0xf7, 0x60, 0xa7, 0x72, + 0x1a, 0x23, 0xe9, 0xfc, 0xec, 0x14, 0xee, 0x43, + 0x45, 0x8f, 0x3f, 0x6e, 0x9f, 0xcb, 0x75, 0xce, + 0x9f, 0xef, 0xf0, 0x2b, 0xd7, 0xbf, 0x1d, 0x3c, + 0x06, 0x78, 0x4e, 0x9f, 0xff, 0xe5, 0x1f, 0xe6, + 0xd9, 0x6b, 0x79, 0x2d, 0xeb, 0xd7, 0xa9, 0x3a, + 0x3c, 0x88, 0x7b, 0x10, 0xce, 0xee, 0x43, 0x45, + 0xa1, 0x3f, 0xee, 0x0b, 0x7a, 0x70, 0xd1, 0x81, + 0x3a, 0x42, 0x87, 0xca, 0x24, 0xd3, 0xf3, 0x6d, + 0xf7, 0xfa, 0x80, 0xe9, 0xf2, 0xdf, 0x2c, 0xa7, + 0x4f, 0xff, 0x65, 0xeb, 0x8d, 0x59, 0x5b, 0x65, + 0xac, 0xa7, 0x45, 0x07, 0xeb, 0xf2, 0x58, 0x54, + 0x64, 0xe4, 0x29, 0xa7, 0xfe, 0xca, 0xfb, 0x5f, + 0xbc, 0x57, 0x36, 0x3a, 0x7d, 0x7b, 0x77, 0xf4, + 0x9d, 0x3f, 0xdf, 0xd6, 0x8d, 0xad, 0x9f, 0x53, + 0xa6, 0xfe, 0x61, 0xf2, 0x21, 0x4c, 0xfe, 0xcb, + 0x35, 0xeb, 0x79, 0x87, 0x4f, 0xe7, 0xe0, 0xd7, + 0xeb, 0x41, 0xd3, 0xdb, 0x65, 0xb8, 0xe9, 0xfe, + 0xcc, 0x0e, 0x22, 0x60, 0x4e, 0x8c, 0x45, 0xdd, + 0x26, 0x9c, 0x65, 0x52, 0x19, 0xec, 0xb7, 0x5c, + 0xe9, 0xfd, 0xa7, 0xdf, 0xd5, 0xf9, 0x53, 0xa4, + 0xe6, 0xe2, 0xe5, 0x5a, 0x98, 0xed, 0x0b, 0xec, + 0x84, 0x45, 0xa1, 0xdd, 0xf2, 0x6a, 0xc2, 0xa2, + 0xf0, 0xe6, 0xdc, 0x3c, 0xde, 0x41, 0x3f, 0x37, + 0xfb, 0x6f, 0xab, 0x47, 0x4f, 0x65, 0xba, 0xe7, + 0x4b, 0x74, 0xc3, 0xd2, 0xf9, 0x94, 0xf8, 0x53, + 0xb9, 0x0d, 0x16, 0xb4, 0xff, 0xb9, 0xee, 0xc1, + 0x4e, 0xe4, 0x34, 0x50, 0x72, 0x73, 0x71, 0x14, + 0x58, 0x58, 0xc3, 0x09, 0xff, 0xce, 0x67, 0x3d, + 0xd8, 0x29, 0xdc, 0x86, 0x8a, 0x2e, 0x7f, 0x3b, + 0x05, 0x3b, 0x90, 0xd1, 0x75, 0x43, 0xd9, 0x0a, + 0x69, 0x08, 0xcf, 0x13, 0x31, 0x32, 0xd3, 0x9c, + 0xc0, 0x8e, 0xa6, 0xa7, 0x37, 0x52, 0x9f, 0x0a, + 0x77, 0x21, 0xa2, 0x21, 0x9d, 0x6c, 0xd8, 0xe9, + 0x3b, 0x0f, 0x32, 0x93, 0x09, 0xfc, 0xec, 0x14, + 0xee, 0x43, 0x44, 0x6d, 0x3f, 0x9d, 0x82, 0x9d, + 0xc8, 0x68, 0xa6, 0xe7, 0xf3, 0xb0, 0x53, 0xb9, + 0x0d, 0x15, 0x04, 0xfe, 0x76, 0x0a, 0x77, 0x21, + 0xa2, 0xa6, 0x9f, 0x0a, 0x77, 0x21, 0xa2, 0xb0, + 0x9f, 0x78, 0x3b, 0x6a, 0x27, 0x4f, 0xf3, 0xdd, + 0x82, 0x9d, 0xc8, 0x68, 0x8f, 0xe7, 0x62, 0xd0, + 0x74, 0x9d, 0x88, 0xb5, 0x43, 0x0e, 0x29, 0xfa, + 0x0c, 0xff, 0xe7, 0x33, 0x9e, 0xec, 0x14, 0xee, + 0x43, 0x44, 0xdf, 0x3f, 0xf3, 0x39, 0xee, 0xc1, + 0x4e, 0xe4, 0x34, 0x4f, 0xd3, 0xf3, 0x75, 0xdd, + 0x5b, 0xa6, 0xe6, 0x52, 0x74, 0xff, 0xf2, 0xaa, + 0xaa, 0xaa, 0xaa, 0xad, 0x35, 0x3a, 0x7c, 0x3e, + 0xa3, 0x98, 0x54, 0xc1, 0x09, 0x51, 0x86, 0xf4, + 0x24, 0xf2, 0xd4, 0xa7, 0x1a, 0x08, 0x54, 0x63, + 0xd6, 0x14, 0xd3, 0xf0, 0xf2, 0x5b, 0xc2, 0x74, + 0xf5, 0x0c, 0xf3, 0xce, 0x9f, 0x57, 0xfa, 0xf3, + 0xce, 0x9f, 0xd6, 0x56, 0x38, 0x00, 0x53, 0xa4, + 0x2a, 0x7f, 0xb8, 0x47, 0xe2, 0x89, 0xff, 0x2d, + 0x3b, 0x6f, 0x5d, 0xdd, 0xb5, 0xa9, 0xd3, 0x81, + 0x5c, 0x3a, 0x7f, 0xed, 0x96, 0xfd, 0x5c, 0xb5, + 0xbc, 0xc3, 0xa7, 0x5f, 0xcd, 0x1d, 0x0a, 0x7c, + 0x35, 0x44, 0x85, 0x4f, 0x93, 0xc4, 0xe3, 0x0a, + 0x76, 0x19, 0x7d, 0x1e, 0xef, 0x93, 0xdc, 0xd6, + 0xf6, 0x1d, 0x37, 0x7c, 0xe8, 0x13, 0x72, 0xc2, + 0x49, 0xc1, 0x08, 0x4e, 0x9e, 0xa3, 0xfe, 0x52, + 0x9c, 0x5e, 0x4f, 0x53, 0x7f, 0x21, 0xd0, 0xa8, + 0x8f, 0xb1, 0xe6, 0xa6, 0x33, 0xff, 0x51, 0xa8, + 0x33, 0xfa, 0xb4, 0xb7, 0xe3, 0xa7, 0x96, 0xf5, + 0x43, 0x44, 0x1d, 0x3f, 0x75, 0x97, 0x77, 0xab, + 0xce, 0x81, 0x45, 0x2d, 0xd1, 0xdb, 0x16, 0xcc, + 0x3b, 0x1d, 0x30, 0x42, 0x74, 0x3c, 0xd6, 0x04, + 0x5a, 0x7a, 0xab, 0xe6, 0xc5, 0x38, 0xd0, 0xcf, + 0xab, 0xbf, 0x60, 0x54, 0xe8, 0xf1, 0xef, 0x6f, + 0x33, 0x9c, 0x10, 0x84, 0xa8, 0x29, 0xc5, 0xe4, + 0xf7, 0x83, 0xe6, 0x8a, 0x84, 0x37, 0x9e, 0x19, + 0x8f, 0x26, 0xf1, 0x68, 0x6e, 0x7d, 0xf2, 0x77, + 0xfd, 0x53, 0xa7, 0xb6, 0xef, 0x80, 0xe9, 0xff, + 0x97, 0xe0, 0xfa, 0xd2, 0xd2, 0xfc, 0x07, 0x47, + 0x91, 0x06, 0x01, 0xca, 0x91, 0x4f, 0x97, 0x06, + 0xde, 0x3a, 0x7b, 0x6c, 0x66, 0xec, 0xe8, 0x68, + 0xf2, 0xdb, 0x12, 0xcf, 0xd7, 0xc6, 0xbe, 0xac, + 0x3c, 0x40, 0x53, 0xee, 0xfe, 0xd6, 0x53, 0x44, + 0x04, 0xe3, 0x75, 0x3f, 0xca, 0x34, 0x68, 0xc6, + 0xbc, 0xc3, 0xa7, 0xca, 0x0f, 0x53, 0x53, 0xa7, + 0xf0, 0xb5, 0x5f, 0x7e, 0xfe, 0x3a, 0x7a, 0x90, + 0x0a, 0x95, 0x30, 0x42, 0x54, 0x29, 0xb7, 0x09, + 0x0c, 0xfd, 0xc2, 0xf7, 0xff, 0xc5, 0x38, 0xd0, + 0x42, 0xaa, 0x48, 0xc7, 0xbf, 0x2f, 0xd9, 0x13, + 0xe7, 0x77, 0x28, 0xd6, 0x11, 0x73, 0xed, 0x6f, + 0xaf, 0x6f, 0x34, 0x40, 0xf3, 0xfe, 0xb6, 0xb4, + 0x2e, 0x85, 0xba, 0xec, 0x74, 0xee, 0x1a, 0x0e, + 0x98, 0x21, 0x3a, 0x7f, 0x0f, 0xb1, 0xb6, 0xf7, + 0xb9, 0x0d, 0x88, 0x46, 0xe3, 0x64, 0x5f, 0x8b, + 0x9c, 0xff, 0xdf, 0xcd, 0x36, 0xe7, 0xef, 0xea, + 0x54, 0xe8, 0x53, 0xea, 0xc2, 0x39, 0xff, 0xd8, + 0xc6, 0x73, 0xf0, 0x53, 0xb9, 0x0d, 0x10, 0xc4, + 0x58, 0xfc, 0x7e, 0x41, 0x3f, 0x60, 0xa7, 0x72, + 0x1a, 0x20, 0xa9, 0xeb, 0xd5, 0x40, 0x54, 0xee, + 0x1a, 0x0a, 0x9e, 0xd7, 0xfd, 0xa4, 0xa9, 0xfd, + 0xea, 0x32, 0xf5, 0x50, 0x15, 0x05, 0x4f, 0xd8, + 0x8b, 0x65, 0x61, 0x53, 0x04, 0x25, 0x4f, 0xdf, + 0x5f, 0xd1, 0xe1, 0x2a, 0x31, 0x30, 0xa4, 0x21, + 0x60, 0xdd, 0x89, 0x00, 0x67, 0xf0, 0xa0, 0x95, + 0x6f, 0x16, 0x9b, 0xc2, 0x53, 0x8f, 0xca, 0x5c, + 0xf4, 0xf5, 0xed, 0x1d, 0x8c, 0xff, 0x95, 0xfd, + 0xfc, 0xa8, 0xad, 0x4e, 0x9f, 0xeb, 0xd9, 0x5b, + 0x6c, 0xa0, 0x53, 0xa7, 0xf9, 0x69, 0x7e, 0xe2, + 0xa6, 0x30, 0xe8, 0x53, 0xf4, 0xb1, 0xd4, 0xff, + 0xfc, 0xd7, 0x73, 0xfa, 0xda, 0x32, 0x8d, 0x6f, + 0xaf, 0x6f, 0x34, 0x5f, 0x73, 0xef, 0x5f, 0xf8, + 0xf3, 0xa7, 0xfd, 0xff, 0x3e, 0xf6, 0xc1, 0xf5, + 0x4e, 0x9f, 0xd7, 0x06, 0xff, 0xbe, 0xd8, 0x78, + 0x80, 0x67, 0x70, 0xbc, 0xf1, 0x00, 0xc6, 0x1f, + 0x4e, 0xa8, 0x53, 0x73, 0xcf, 0x10, 0x0c, 0xf7, + 0x7e, 0x97, 0x9e, 0x20, 0x19, 0xfd, 0xe4, 0xb6, + 0x00, 0x0a, 0x78, 0x80, 0x67, 0x7b, 0xfb, 0x1e, + 0x20, 0x18, 0xd9, 0x17, 0x2c, 0x22, 0xb1, 0x7b, + 0x63, 0xe9, 0xc2, 0xb7, 0x3c, 0x40, 0x30, 0x78, + 0x80, 0x66, 0x56, 0x1e, 0x20, 0x18, 0xd8, 0xdc, + 0xf8, 0x5e, 0x7b, 0xcc, 0xd9, 0x4f, 0x10, 0x0c, + 0xeb, 0xf2, 0x1e, 0x20, 0x19, 0xff, 0x7f, 0x9e, + 0xeb, 0x79, 0x38, 0x4f, 0x10, 0x0c, 0xdd, 0xb1, + 0xe2, 0x01, 0x9f, 0xdf, 0xe0, 0xd6, 0xaa, 0x03, + 0xc4, 0x03, 0x3e, 0xf6, 0x9e, 0xf8, 0x0f, 0x10, + 0x0c, 0xde, 0xa9, 0xe2, 0x01, 0x81, 0x3d, 0x9b, + 0x9b, 0x4f, 0xaf, 0xf5, 0xa5, 0xe6, 0x88, 0x06, + 0x60, 0x29, 0xe2, 0x01, 0x71, 0xb5, 0x9f, 0x79, + 0x59, 0xdb, 0x1e, 0x20, 0x19, 0xed, 0x7b, 0xe8, + 0x78, 0x80, 0x67, 0x28, 0xa1, 0xe2, 0x01, 0x9f, + 0xf6, 0x53, 0x5d, 0x97, 0x3e, 0x0a, 0x9e, 0x20, + 0x19, 0xf6, 0xbc, 0xf7, 0xa9, 0xe2, 0x01, 0x8c, + 0x44, 0x05, 0x93, 0x26, 0x10, 0x1e, 0x20, 0x18, + 0x7a, 0xa8, 0xfd, 0x88, 0xf2, 0x13, 0x1e, 0x56, + 0xb1, 0x90, 0x0d, 0x2a, 0x5d, 0x78, 0x53, 0x6a, + 0x45, 0x3e, 0xcb, 0xd7, 0xa9, 0x3c, 0x40, 0x33, + 0xfb, 0x65, 0x46, 0x85, 0x6e, 0x78, 0x80, 0x76, + 0x36, 0x93, 0x85, 0x42, 0x78, 0x80, 0x61, 0x0f, + 0xdc, 0x54, 0x27, 0xbd, 0xf7, 0xd4, 0xf1, 0x00, + 0xcf, 0xdc, 0xd3, 0xf2, 0x97, 0x9e, 0x20, 0x18, + 0xc4, 0x45, 0x00, 0x83, 0x52, 0xf9, 0xfe, 0xb2, + 0xd5, 0xd5, 0xd4, 0x40, 0x78, 0x80, 0x65, 0xe3, + 0xc4, 0x03, 0x37, 0x51, 0xb1, 0xf1, 0xd9, 0x1a, + 0x61, 0x01, 0xe2, 0x01, 0x9f, 0x75, 0xeb, 0xea, + 0x4f, 0x10, 0x0c, 0xfd, 0xef, 0xea, 0xfc, 0xa9, + 0xe2, 0x01, 0x85, 0x44, 0x97, 0xc8, 0xae, 0x6b, + 0x1b, 0x32, 0x03, 0xb2, 0x1b, 0x6d, 0x20, 0x0b, + 0x05, 0x88, 0xb8, 0xbc, 0x04, 0xf5, 0x95, 0xb1, + 0x78, 0xf0, 0x37, 0x21, 0x27, 0xbe, 0x18, 0x33, + 0xbb, 0x90, 0xd1, 0x00, 0xb9, 0x17, 0x93, 0xda, + 0x77, 0x5b, 0x66, 0x1d, 0x30, 0x14, 0xa9, 0x69, + 0x2a, 0x75, 0x96, 0x83, 0xa6, 0x08, 0x4a, 0x8f, + 0x1e, 0xce, 0xec, 0x55, 0xa1, 0x20, 0x8e, 0x4e, + 0x67, 0xaa, 0x53, 0x8f, 0x06, 0x7a, 0xf5, 0x6f, + 0xd4, 0xe8, 0xa1, 0x95, 0xd2, 0xf2, 0x74, 0xa4, + 0xb8, 0xf8, 0xe0, 0x10, 0xc6, 0xde, 0x5b, 0x3f, + 0x0b, 0x4f, 0x51, 0xa0, 0xe9, 0xff, 0x3e, 0xba, + 0x3f, 0x8c, 0x51, 0xd2, 0x74, 0xff, 0xc3, 0x6e, + 0xc7, 0xa8, 0x83, 0x7b, 0x0e, 0x9f, 0xb7, 0x84, + 0x7d, 0x96, 0x2a, 0x79, 0xab, 0x73, 0x47, 0x4f, + 0xb1, 0xa7, 0xad, 0x27, 0x4f, 0x67, 0xd5, 0xe5, + 0x40, 0x9f, 0x5f, 0xc8, 0xdb, 0xca, 0x21, 0x53, + 0x7b, 0xc2, 0xdf, 0x20, 0xd9, 0x0f, 0xa1, 0x33, + 0x3d, 0xef, 0xde, 0xa7, 0x4e, 0xd5, 0xbb, 0xa9, + 0xd3, 0xff, 0x01, 0x98, 0x96, 0xf6, 0xd6, 0xd7, + 0x49, 0xd3, 0xff, 0x5e, 0xb9, 0xb2, 0xd3, 0x5c, + 0xa6, 0xe7, 0x4f, 0xe7, 0xe5, 0x77, 0xd6, 0xf8, + 0x74, 0x2a, 0x39, 0x3c, 0x45, 0xc9, 0x1a, 0xa3, + 0x4f, 0xb9, 0xb6, 0xdb, 0x29, 0xd3, 0x56, 0xa7, + 0x4e, 0x08, 0x42, 0x74, 0xc0, 0xe2, 0x9c, 0x5e, + 0x40, 0x9e, 0xb5, 0x4c, 0xa7, 0x70, 0x30, 0xa8, + 0x7a, 0x2e, 0xd9, 0x08, 0x0d, 0xc2, 0x19, 0xfd, + 0x7f, 0xe5, 0x20, 0xde, 0xf3, 0xa7, 0xfb, 0xf8, + 0xdb, 0x1c, 0x10, 0x84, 0xa9, 0xcc, 0xeb, 0x9d, + 0x16, 0x3d, 0x56, 0xc7, 0x50, 0xa8, 0xe7, 0xf1, + 0xc7, 0x42, 0x2e, 0x7f, 0xf6, 0x6d, 0xcc, 0xca, + 0x5f, 0x80, 0xcd, 0x8e, 0x9a, 0xdc, 0x74, 0xcd, + 0x54, 0xe9, 0xf6, 0x36, 0xf7, 0xf6, 0xc3, 0x59, + 0xbc, 0x56, 0x34, 0x22, 0xe1, 0xce, 0xd3, 0xdb, + 0x65, 0xb8, 0xe9, 0xff, 0x97, 0x3e, 0xfb, 0x67, + 0xfe, 0xa1, 0x3a, 0x5e, 0x14, 0x44, 0x7c, 0x96, + 0xe4, 0x31, 0x43, 0x6d, 0xc0, 0xf8, 0xf4, 0xb6, + 0x8f, 0xe7, 0x49, 0x7a, 0x42, 0xbf, 0xc4, 0x23, + 0x4b, 0x57, 0xb4, 0x7c, 0xc0, 0x4d, 0xfc, 0x60, + 0xd7, 0x8f, 0x79, 0xb4, 0x6e, 0x73, 0xfd, 0x83, + 0xf5, 0xbb, 0xf2, 0xc7, 0x4f, 0xdf, 0x06, 0x6d, + 0xcf, 0x3a, 0x7d, 0xbd, 0xa7, 0xae, 0xec, 0xa8, + 0x54, 0x48, 0xe1, 0xb6, 0xf2, 0xd9, 0xff, 0xc3, + 0xa7, 0xfa, 0xf6, 0xfc, 0xaf, 0x97, 0xe7, 0x4b, + 0x79, 0xa2, 0x05, 0x95, 0xcd, 0x40, 0xa4, 0xbd, + 0x73, 0x78, 0x11, 0xf9, 0xff, 0x7b, 0x72, 0xde, + 0xde, 0xe0, 0x01, 0x4a, 0x9f, 0xfd, 0x7a, 0xf5, + 0x3a, 0x18, 0xb9, 0xfb, 0xee, 0x1d, 0x0a, 0x89, + 0x3f, 0x22, 0x4e, 0xa3, 0xef, 0x3a, 0x15, 0x38, + 0x26, 0x42, 0x26, 0xd0, 0xba, 0xd4, 0x8a, 0x7f, + 0x3d, 0xac, 0xff, 0x5b, 0x0e, 0x9f, 0xfc, 0x29, + 0xfc, 0x6d, 0xfc, 0x51, 0x5a, 0x9d, 0x3f, 0xb9, + 0xab, 0x63, 0x32, 0xa7, 0x46, 0x1f, 0xcb, 0x64, + 0x79, 0xfb, 0xda, 0x6f, 0x98, 0xc3, 0xa7, 0xd9, + 0xb7, 0x86, 0xa7, 0x4f, 0xff, 0x56, 0xf4, 0xa7, + 0xad, 0xa1, 0xbf, 0x2d, 0xe6, 0x1d, 0x25, 0xa0, + 0xff, 0x42, 0x4f, 0x1e, 0x47, 0xb5, 0x88, 0xef, + 0x0a, 0xb9, 0xdb, 0x70, 0x9d, 0x3e, 0x07, 0x7f, + 0x1b, 0xce, 0x85, 0x3c, 0x4d, 0x46, 0xe7, 0x95, + 0x94, 0xb4, 0x74, 0x2a, 0xa5, 0xf7, 0xc7, 0x43, + 0x57, 0x9b, 0x91, 0x4f, 0x03, 0xa9, 0xa9, 0xd3, + 0x04, 0x27, 0x45, 0x4d, 0xc0, 0x48, 0xa7, 0xea, + 0xfa, 0xdd, 0x96, 0x29, 0xc6, 0x86, 0x70, 0x42, + 0x12, 0xa7, 0x9f, 0x7c, 0x42, 0x9c, 0x5e, 0x4f, + 0xb3, 0x6f, 0x02, 0xa7, 0x4b, 0x84, 0xf5, 0xfe, + 0x5f, 0x3f, 0x7e, 0x9a, 0xb5, 0x97, 0x3a, 0x7f, + 0xfb, 0xf7, 0x6b, 0x07, 0x39, 0x8b, 0x6f, 0x6c, + 0x74, 0xfa, 0x8d, 0xdf, 0xb2, 0x93, 0xa7, 0xde, + 0xaa, 0xd0, 0xc3, 0xe5, 0xfb, 0x3e, 0x5e, 0x10, + 0x6a, 0x7c, 0xbf, 0x66, 0xe7, 0x9f, 0x2f, 0xd9, + 0xed, 0x5f, 0x95, 0x3e, 0x5f, 0xb1, 0xb1, 0xe8, + 0xfc, 0x8a, 0x7c, 0xb9, 0x5c, 0xf9, 0xf2, 0xfd, + 0x83, 0xe5, 0xfb, 0x37, 0x5c, 0xf9, 0x7e, 0xb0, + 0xb7, 0x93, 0xfe, 0x7f, 0x5a, 0xa4, 0xcf, 0x66, + 0xe7, 0x80, 0x7c, 0xbf, 0x60, 0xf9, 0x7e, 0xcc, + 0x05, 0x3e, 0x5f, 0xb3, 0xfd, 0x80, 0xe1, 0xc6, + 0xd9, 0xb1, 0xf2, 0xfd, 0x9f, 0xb2, 0xde, 0xae, + 0xb4, 0x1f, 0x2f, 0xd8, 0x02, 0x28, 0xfe, 0x45, + 0x54, 0x59, 0xe1, 0xa1, 0x6e, 0x7c, 0xbf, 0x60, + 0xf9, 0x7e, 0xe1, 0xae, 0x98, 0x21, 0x3e, 0x5f, + 0xb0, 0xf5, 0x62, 0x3b, 0x1a, 0xe4, 0x21, 0x29, + 0x84, 0xe8, 0x94, 0xb0, 0xc6, 0xb0, 0xba, 0xba, + 0xf0, 0x49, 0xa7, 0xb1, 0xeb, 0xa4, 0xb9, 0x7e, + 0xdc, 0x88, 0xf9, 0xff, 0x62, 0x6d, 0x82, 0x1e, + 0xb3, 0x47, 0x4c, 0xfa, 0x0a, 0x8a, 0x11, 0x2d, + 0x4a, 0x17, 0xcf, 0x60, 0x57, 0x25, 0x7a, 0x71, + 0x8e, 0x7f, 0xfe, 0xa5, 0xe2, 0xad, 0xb9, 0xa5, + 0xda, 0xca, 0xce, 0x68, 0xe8, 0x55, 0xdc, 0x3c, + 0x26, 0xf1, 0x7f, 0x4e, 0x72, 0x6a, 0x59, 0x3f, + 0xed, 0x2b, 0x4f, 0xd6, 0xd6, 0xd5, 0x0e, 0x9f, + 0xbf, 0xda, 0x6d, 0xcf, 0x3a, 0x70, 0x42, 0x12, + 0xa7, 0x6f, 0x50, 0x14, 0xe2, 0xf2, 0x7f, 0xdf, + 0xe7, 0xef, 0x1c, 0x6d, 0xc2, 0x74, 0xff, 0x7f, + 0x81, 0xa1, 0xea, 0x0a, 0x4e, 0x8d, 0x93, 0x31, + 0x62, 0x08, 0x12, 0xbe, 0x59, 0x73, 0xf9, 0xff, + 0x7f, 0xfe, 0xd5, 0xbb, 0xae, 0xe2, 0xdc, 0xe9, + 0xc1, 0x08, 0x4b, 0x10, 0x82, 0x7c, 0x29, 0xdc, + 0x85, 0x88, 0x40, 0xe3, 0x55, 0x38, 0x21, 0x09, + 0x62, 0x0f, 0x41, 0x62, 0x0f, 0x38, 0xd5, 0x4c, + 0xac, 0xc4, 0x49, 0xa3, 0x5c, 0xfa, 0xeb, 0x75, + 0x61, 0xd3, 0xdf, 0xf2, 0xe9, 0x3a, 0x76, 0xf5, + 0x01, 0xd1, 0x41, 0xe0, 0x30, 0x8e, 0x7c, 0x8b, + 0x65, 0x61, 0x53, 0xef, 0xf7, 0xff, 0x85, 0x4d, + 0x88, 0x54, 0xc1, 0x09, 0x51, 0x87, 0xeb, 0x52, + 0x5b, 0x93, 0x04, 0x52, 0x7f, 0x7f, 0x7a, 0x82, + 0xd8, 0xde, 0x53, 0x8d, 0xdc, 0x2a, 0x70, 0x1e, + 0x66, 0xe8, 0x69, 0xcf, 0xfe, 0xb2, 0x82, 0xb9, + 0x9b, 0x77, 0xfb, 0x49, 0xd3, 0xfa, 0xbb, 0xd9, + 0x50, 0xf5, 0x27, 0x42, 0xab, 0xb1, 0xc4, 0xcf, + 0x46, 0x0a, 0x31, 0xd6, 0x71, 0xa5, 0x52, 0xa7, + 0x04, 0x21, 0x2a, 0x7c, 0xf0, 0x77, 0xf6, 0x29, + 0xc5, 0xe4, 0xff, 0xf7, 0xe9, 0xd1, 0x48, 0x17, + 0xe9, 0xd4, 0x7f, 0x8e, 0x9f, 0xfe, 0xc5, 0x76, + 0xdf, 0x5d, 0x53, 0x34, 0xf8, 0x4e, 0x9e, 0x6f, + 0xb0, 0x1b, 0x1d, 0x0f, 0x3f, 0x7e, 0x50, 0x9f, + 0xfc, 0xfc, 0x10, 0x33, 0xd5, 0xdf, 0x55, 0xc3, + 0xa7, 0xbd, 0xb6, 0x30, 0xe8, 0x54, 0xe3, 0x9e, + 0x6f, 0xd0, 0xca, 0xf9, 0x0e, 0xa9, 0x53, 0xf6, + 0xee, 0xbe, 0xfd, 0x2f, 0x3a, 0x7f, 0xdc, 0x3b, + 0x9e, 0xb6, 0x75, 0x20, 0x3a, 0x7f, 0xd5, 0xaa, + 0x8d, 0xd5, 0xdb, 0x78, 0xe9, 0xff, 0x7f, 0x9a, + 0xb7, 0x08, 0xfb, 0x63, 0xa3, 0x11, 0xde, 0x86, + 0x7e, 0x40, 0x61, 0xf4, 0xf3, 0xf7, 0xe3, 0x47, + 0x4f, 0x87, 0x6c, 0xcf, 0x9d, 0x3f, 0xf6, 0xef, + 0xd9, 0x65, 0x6d, 0x5b, 0x2d, 0x27, 0x47, 0x1f, + 0x85, 0x49, 0xa7, 0xff, 0xb2, 0xf5, 0xc6, 0xac, + 0xad, 0xb2, 0xd6, 0x53, 0xa7, 0xf5, 0xdb, 0xab, + 0x5f, 0x6e, 0x94, 0x6a, 0x74, 0x6c, 0x8b, 0x4f, + 0x90, 0xdd, 0x42, 0x7f, 0xfb, 0xd5, 0xcd, 0xaf, + 0x5f, 0x69, 0xb7, 0x7f, 0xc7, 0x4f, 0xff, 0xef, + 0xdf, 0x2d, 0xe5, 0xbf, 0x80, 0xaf, 0x70, 0x42, + 0x12, 0xa7, 0xb6, 0xcc, 0xd2, 0x54, 0xe7, 0xff, + 0x53, 0x44, 0x33, 0x38, 0x21, 0x09, 0x53, 0xb3, + 0xe8, 0x53, 0x8b, 0xc9, 0xff, 0x65, 0x19, 0xb7, + 0x3f, 0xeb, 0x41, 0xd0, 0x03, 0xe8, 0xf9, 0x4c, + 0xfe, 0x7f, 0xf2, 0xf7, 0xd6, 0x83, 0xa1, 0x53, + 0x89, 0xa1, 0x86, 0x92, 0x31, 0x85, 0x77, 0x11, + 0x4f, 0x7a, 0xfe, 0x53, 0xa7, 0xf6, 0xb8, 0x20, + 0x07, 0xbe, 0x74, 0xff, 0xf9, 0x5f, 0xed, 0xad, + 0xbe, 0xb8, 0x29, 0xdc, 0x86, 0x88, 0x32, 0x2c, + 0x89, 0x4b, 0x99, 0xcf, 0xaa, 0x3e, 0x0e, 0xec, + 0xe9, 0xfd, 0xb9, 0x95, 0xdc, 0xc1, 0x53, 0xa1, + 0xe9, 0x9c, 0xfa, 0x16, 0xb5, 0x23, 0xb9, 0x5c, + 0xff, 0xf2, 0xfe, 0x8d, 0x0d, 0x7b, 0xf9, 0xb6, + 0x7f, 0x49, 0xd3, 0x94, 0x5a, 0x3a, 0x15, 0x77, + 0x33, 0x63, 0xbc, 0x8d, 0x21, 0x0c, 0x7d, 0x1e, + 0xe7, 0x47, 0x11, 0xaa, 0x1e, 0xe2, 0xb4, 0xff, + 0xf2, 0x2d, 0x6a, 0xac, 0xcb, 0xfa, 0xb6, 0xe3, + 0xa7, 0xf7, 0xfe, 0xaf, 0x62, 0xf8, 0xe9, 0xfd, + 0x7f, 0x36, 0xf5, 0xde, 0xa5, 0x49, 0x87, 0x4f, + 0xd9, 0xfd, 0x22, 0xae, 0xf1, 0xe2, 0xef, 0x35, + 0x8c, 0x4c, 0x2f, 0xc9, 0xb5, 0x77, 0x9e, 0x1d, + 0x69, 0x79, 0xd3, 0xfb, 0xef, 0xfe, 0x31, 0x50, + 0xe9, 0xcc, 0xdb, 0x0e, 0x85, 0x3f, 0x0c, 0x23, + 0xe3, 0x19, 0xd7, 0xf5, 0x07, 0x4f, 0xfb, 0x5b, + 0xd7, 0x78, 0x81, 0x9e, 0xa9, 0xd3, 0xff, 0x2f, + 0xfa, 0xca, 0x07, 0x0d, 0x6a, 0x54, 0x6c, 0x88, + 0x46, 0x21, 0x4f, 0xad, 0xef, 0x53, 0x53, 0xa0, + 0xa9, 0xfa, 0xbb, 0xd4, 0x55, 0x85, 0x41, 0x50, + 0x54, 0x15, 0x05, 0x43, 0xcf, 0x7f, 0xc1, 0x40, + 0x2d, 0xd4, 0x2b, 0x70, 0x29, 0xbc, 0x2a, 0x6b, + 0x61, 0x53, 0xf7, 0x75, 0xda, 0x56, 0x15, 0xa0, + 0xb5, 0x93, 0x76, 0x54, 0x15, 0x05, 0x43, 0xcb, + 0x4f, 0x05, 0x41, 0x50, 0x54, 0x15, 0x05, 0x41, + 0x50, 0x54, 0x50, 0x6f, 0x36, 0x0a, 0xf0, 0x50, + 0x02, 0xaa, 0x14, 0xd8, 0x2a, 0x0a, 0x82, 0xa1, + 0xe5, 0xa5, 0x42, 0xa0, 0xa8, 0x2a, 0x0a, 0x82, + 0xa1, 0xe6, 0xa0, 0x01, 0x57, 0x0a, 0x6f, 0x0a, + 0x82, 0xa0, 0xa8, 0x2a, 0x0a, 0x8a, 0x0d, 0x46, + 0x90, 0xa1, 0x0a, 0xb0, 0x54, 0xb4, 0x95, 0x05, + 0x41, 0x50, 0x54, 0x15, 0x1b, 0x1a, 0x8a, 0x42, + 0x80, 0x15, 0xa8, 0x54, 0x15, 0x05, 0x41, 0x53, + 0xeb, 0x28, 0x2b, 0x85, 0x41, 0x50, 0xf3, 0xce, + 0x40, 0xab, 0x05, 0x70, 0x50, 0x09, 0xa4, 0x85, + 0x41, 0x50, 0x54, 0x15, 0x05, 0x43, 0xcd, 0x45, + 0x21, 0x5e, 0x0a, 0x6c, 0x15, 0x05, 0x41, 0x50, + 0x54, 0x15, 0x0f, 0x35, 0x1b, 0x05, 0x58, 0x2b, + 0xe1, 0x52, 0xb1, 0x50, 0x54, 0x15, 0x27, 0x95, + 0x05, 0x52, 0x58, 0x41, 0x50, 0x54, 0x15, 0x05, + 0x45, 0x07, 0xcc, 0xf0, 0xad, 0x23, 0x48, 0x34, + 0xd0, 0x50, 0x02, 0xae, 0x15, 0x2c, 0x2a, 0x0a, + 0x82, 0xa4, 0xf2, 0xa0, 0xaa, 0x4b, 0x08, 0x2a, + 0x0a, 0x85, 0x3d, 0x27, 0x85, 0x78, 0x68, 0x46, + 0x98, 0x15, 0x05, 0x41, 0x50, 0x54, 0x15, 0x05, + 0x42, 0x9b, 0x2a, 0x42, 0x84, 0x29, 0x81, 0x5f, + 0x0a, 0x82, 0xa0, 0xa8, 0x2a, 0x04, 0xbe, 0xa8, + 0x55, 0xc2, 0xa0, 0xa8, 0x2a, 0x0a, 0x86, 0x17, + 0xdf, 0x0a, 0xb8, 0x54, 0x98, 0x54, 0x15, 0x05, + 0x40, 0x0b, 0x4d, 0x42, 0xa0, 0xa8, 0x2a, 0x0a, + 0x82, 0xa1, 0x4d, 0x43, 0x41, 0x56, 0x0a, 0xd4, + 0x2a, 0x15, 0x7e, 0xb6, 0x87, 0x27, 0x9e, 0xec, + 0x53, 0x8b, 0x88, 0xdb, 0x4a, 0x47, 0x99, 0xb7, + 0x67, 0xad, 0x1d, 0x0c, 0x23, 0x18, 0x8f, 0x63, + 0x7e, 0x66, 0x01, 0xe7, 0xdd, 0xaa, 0xcd, 0x76, + 0x1d, 0x5e, 0x5b, 0x32, 0x85, 0x2b, 0x70, 0xb7, + 0x79, 0x23, 0x7a, 0x4c, 0xf9, 0xff, 0x5c, 0xa9, + 0x4e, 0x4d, 0x5e, 0x77, 0x95, 0x85, 0x4f, 0x79, + 0x38, 0x4e, 0x9d, 0xed, 0xb0, 0xe9, 0xcb, 0xbd, + 0xd4, 0xa2, 0x3c, 0x4e, 0x6c, 0x37, 0x71, 0xf9, + 0xfd, 0x6f, 0x07, 0x61, 0x5a, 0x0e, 0x8a, 0x51, + 0x08, 0xd2, 0x84, 0xc0, 0x53, 0xa7, 0xe1, 0xef, + 0xef, 0x50, 0x1d, 0x37, 0x09, 0xd2, 0xf1, 0xc8, + 0x5a, 0x49, 0x0e, 0x93, 0x0e, 0x9d, 0x9b, 0x8e, + 0xf2, 0x24, 0xc4, 0x56, 0xc8, 0x3c, 0x3d, 0xbc, + 0x3e, 0x7f, 0xfd, 0xaa, 0xe8, 0xe1, 0x1e, 0x02, + 0xb8, 0x21, 0x09, 0xd0, 0xf6, 0x6c, 0xde, 0xd0, + 0xa0, 0x42, 0xcf, 0x42, 0x85, 0xa2, 0x4e, 0xa5, + 0x4f, 0xfe, 0x17, 0x17, 0x5e, 0x9f, 0xcb, 0xfb, + 0xf7, 0xf5, 0xc3, 0xa7, 0xe7, 0xff, 0xcf, 0xb6, + 0x1d, 0x3e, 0xa3, 0xc2, 0xaf, 0x3a, 0x04, 0xf5, + 0x3e, 0x5b, 0x3f, 0xeb, 0x28, 0x83, 0xf8, 0x1e, + 0x01, 0xd3, 0xb3, 0x1a, 0x3a, 0x59, 0x63, 0xd7, + 0x01, 0xe4, 0xf3, 0x17, 0x28, 0x3a, 0x7d, 0x8d, + 0xbe, 0xb4, 0x9d, 0x1e, 0x3c, 0xaa, 0x90, 0xcf, + 0x2f, 0xee, 0x87, 0x49, 0x71, 0x32, 0xbf, 0xbc, + 0x5d, 0xc7, 0x70, 0x8a, 0x7b, 0x5e, 0x17, 0x9d, + 0x3c, 0xba, 0xb7, 0x75, 0x2a, 0x7c, 0xf7, 0x04, + 0x21, 0x3a, 0x3e, 0x79, 0xfa, 0x93, 0xc6, 0xc8, + 0x93, 0xc7, 0x08, 0x62, 0xaa, 0xc0, 0x47, 0x63, + 0xac, 0x36, 0x27, 0xe1, 0xd4, 0x19, 0xf4, 0x3a, + 0x7f, 0xa8, 0xfe, 0x3e, 0xd8, 0x0a, 0x9d, 0x3d, + 0xb6, 0xca, 0xd8, 0xe9, 0xff, 0xf2, 0xd9, 0x58, + 0x8b, 0x7c, 0x14, 0xee, 0x43, 0x45, 0xf1, 0x3f, + 0x83, 0xd7, 0xaa, 0xe5, 0x07, 0x4f, 0xff, 0xd9, + 0x7d, 0xef, 0x56, 0xde, 0xf8, 0x07, 0xca, 0xcc, + 0x2a, 0x6b, 0xe1, 0xd3, 0xe1, 0xf5, 0x1c, 0xc3, + 0x4c, 0x27, 0x3d, 0x7d, 0x57, 0x8d, 0x30, 0x9c, + 0xc0, 0x53, 0x50, 0x27, 0x3f, 0xbf, 0xda, 0x6e, + 0xa2, 0x03, 0x50, 0x27, 0x3f, 0xab, 0x96, 0xf5, + 0x75, 0xa0, 0xd3, 0x09, 0xcd, 0x9b, 0x1a, 0x61, + 0x39, 0x82, 0x13, 0xcc, 0x27, 0x18, 0x9a, 0x65, + 0x26, 0x9e, 0x2e, 0x61, 0x15, 0x50, 0x1b, 0x21, + 0x04, 0x8e, 0x57, 0x2c, 0xc2, 0x67, 0x1f, 0x3c, + 0xbd, 0x4a, 0x7e, 0xe1, 0x8f, 0x22, 0x2a, 0xaa, + 0x16, 0xf2, 0x95, 0x21, 0x55, 0xce, 0x6c, 0x72, + 0x84, 0xb6, 0x5b, 0xbc, 0xab, 0x79, 0xf6, 0xd8, + 0x3e, 0xd2, 0x74, 0xfb, 0xf9, 0xbf, 0x1a, 0x3a, + 0x7f, 0xad, 0xcc, 0xb7, 0x96, 0x97, 0x9d, 0x3f, + 0x6f, 0xfb, 0xf7, 0x3a, 0x83, 0xa3, 0xc7, 0xd8, + 0x03, 0x98, 0xfa, 0x2d, 0xef, 0x09, 0x58, 0x55, + 0xd2, 0x2c, 0x97, 0x91, 0xe5, 0x2e, 0x87, 0x04, + 0xff, 0xe6, 0x5e, 0xbe, 0xfd, 0x2f, 0xbf, 0xab, + 0xe3, 0xa7, 0xff, 0xf7, 0xf2, 0x9c, 0xaf, 0x7e, + 0xde, 0x5b, 0xdb, 0x5e, 0x61, 0xd3, 0xf7, 0xf5, + 0x67, 0xac, 0xa7, 0x4f, 0xff, 0xbb, 0xf7, 0xee, + 0xb7, 0xd6, 0xf5, 0x0e, 0x58, 0xe9, 0xc3, 0x5a, + 0x9e, 0x20, 0x39, 0xff, 0xed, 0xe2, 0xb6, 0x5d, + 0x18, 0x29, 0xdc, 0x86, 0x88, 0x0d, 0xc6, 0xa2, + 0x00, 0x8e, 0x5b, 0xbd, 0xc6, 0x26, 0x6d, 0xe8, + 0xc4, 0xe7, 0xff, 0xf7, 0x3f, 0x2b, 0xe1, 0x1f, + 0x33, 0x7d, 0xed, 0x81, 0xb9, 0xd3, 0xff, 0xfd, + 0xcf, 0xcf, 0xb4, 0xfc, 0xa5, 0xfb, 0xd4, 0x0e, + 0x08, 0x42, 0x54, 0x59, 0x19, 0x7f, 0x61, 0x9f, + 0xcb, 0x82, 0x9d, 0xc8, 0x68, 0x82, 0x67, 0xf9, + 0x6f, 0x82, 0x9d, 0xc8, 0x68, 0xbe, 0x67, 0xde, + 0x6d, 0xfc, 0xae, 0xc7, 0xf4, 0x87, 0x53, 0xfe, + 0xa3, 0xa9, 0xdc, 0xcb, 0xff, 0x9b, 0x1d, 0x38, + 0x21, 0x09, 0x53, 0xf5, 0xed, 0x89, 0xc2, 0x53, + 0x8b, 0xc8, 0xa1, 0x13, 0x3f, 0x60, 0x9f, 0xfa, + 0x86, 0x2d, 0x35, 0xfe, 0x51, 0xbd, 0xe7, 0x4f, + 0xff, 0x6f, 0xa7, 0xfa, 0x12, 0xde, 0x00, 0x14, + 0x5e, 0x74, 0xe0, 0x84, 0x25, 0x4f, 0xed, 0xe0, + 0x5f, 0xe5, 0x37, 0x29, 0xc5, 0xe4, 0xff, 0xfc, + 0xdd, 0xe8, 0x4b, 0x7b, 0x6d, 0xb0, 0x3a, 0x2f, + 0x5c, 0xa0, 0xe9, 0x75, 0x91, 0x57, 0xaa, 0x24, + 0x3d, 0x54, 0x5f, 0xa1, 0xb4, 0x24, 0x8c, 0x49, + 0xbc, 0x63, 0x73, 0xeb, 0x65, 0xda, 0x79, 0xd3, + 0xfe, 0xcb, 0xa8, 0xd6, 0xcb, 0xfb, 0x9e, 0x20, + 0x89, 0xfc, 0xb8, 0x29, 0xdc, 0x86, 0x88, 0x21, + 0xc7, 0x93, 0x3f, 0x75, 0x84, 0x7c, 0x13, 0xa7, + 0xed, 0xde, 0xc1, 0xef, 0xbc, 0xe8, 0xf9, 0xee, + 0xea, 0x57, 0x16, 0x4c, 0xf0, 0x0e, 0x3f, 0x85, + 0x44, 0xff, 0x2f, 0xef, 0xbf, 0x2d, 0xd4, 0x9d, + 0x3f, 0xfd, 0xc1, 0xbe, 0x59, 0x7d, 0x7f, 0xe5, + 0xbc, 0x74, 0x3d, 0x11, 0x1f, 0x3a, 0x9f, 0xd7, + 0xde, 0xca, 0x87, 0xa9, 0x3a, 0x7b, 0xcd, 0x78, + 0x27, 0x4f, 0xff, 0xb5, 0xa3, 0xeb, 0x96, 0x72, + 0xdf, 0x2d, 0xa8, 0x9d, 0x3e, 0xcb, 0xd7, 0x7d, + 0x76, 0x3f, 0x9d, 0xe4, 0x73, 0xf2, 0x5b, 0xdb, + 0x9e, 0xd2, 0x74, 0xfc, 0xc5, 0xdd, 0xdb, 0x28, + 0x3a, 0x7f, 0xff, 0xfb, 0xd7, 0xe6, 0x77, 0xc0, + 0xff, 0x57, 0x56, 0x5b, 0xcb, 0x7b, 0x6b, 0xcc, + 0x3a, 0x36, 0x47, 0x1f, 0x8c, 0xb8, 0xc6, 0x70, + 0xef, 0xa9, 0xd3, 0xfe, 0xef, 0xa6, 0x0a, 0x77, + 0x21, 0xa2, 0x11, 0x85, 0x3e, 0x2d, 0x47, 0x67, + 0xff, 0x2e, 0x53, 0xbe, 0xfe, 0xfe, 0xaf, 0xca, + 0x9d, 0x3f, 0xfd, 0xdd, 0x46, 0xfc, 0xb7, 0x31, + 0xc1, 0x08, 0x4e, 0x96, 0x6c, 0x89, 0xe6, 0x93, + 0x67, 0x04, 0x21, 0x2a, 0x7f, 0xb0, 0x1c, 0x38, + 0xdb, 0x36, 0x29, 0xc5, 0xe4, 0xc1, 0x09, 0x53, + 0x82, 0x10, 0x95, 0x3f, 0x75, 0x1b, 0x59, 0x6a, + 0x53, 0x8b, 0xc8, 0xfa, 0x2d, 0x82, 0x8f, 0xb8, + 0x65, 0x3e, 0x4f, 0x6d, 0xbd, 0x85, 0x38, 0xd9, + 0xce, 0x08, 0x42, 0x54, 0xea, 0xa8, 0x94, 0xe2, + 0xf2, 0x40, 0xf1, 0xff, 0xdd, 0x5a, 0x7e, 0xdb, + 0xea, 0xfe, 0x43, 0xa7, 0xf6, 0xf1, 0xdb, 0xcd, + 0x97, 0x63, 0xa7, 0xf5, 0xed, 0x8d, 0xb2, 0xf5, + 0x3a, 0x05, 0x13, 0x16, 0x2c, 0xf9, 0xc4, 0x2a, + 0x3b, 0xf2, 0x18, 0x13, 0xa9, 0xdd, 0x73, 0x75, + 0x3a, 0x7f, 0xf6, 0x6e, 0xfb, 0xe0, 0xc4, 0x5b, + 0x2b, 0x0e, 0x9f, 0x93, 0xd5, 0xb7, 0x9a, 0x2a, + 0x7f, 0x7f, 0x29, 0x7d, 0x7d, 0xa4, 0xe9, 0xee, + 0xe1, 0xd2, 0x74, 0x68, 0x3d, 0x7a, 0x0d, 0xa7, + 0x97, 0x98, 0xd8, 0xa9, 0xc1, 0x08, 0x4a, 0x9f, + 0xfe, 0xde, 0xa0, 0xcc, 0xfd, 0xed, 0xe5, 0xfd, + 0x05, 0x38, 0xbc, 0x96, 0x22, 0x27, 0x98, 0x7d, + 0x0a, 0x9f, 0x23, 0xca, 0xe9, 0x4a, 0xf4, 0x21, + 0x6d, 0x0c, 0x39, 0xff, 0xf3, 0x31, 0x17, 0xfd, + 0x7a, 0xed, 0xaf, 0x0b, 0xce, 0x9f, 0x2d, 0xea, + 0xdb, 0x0e, 0x85, 0x3f, 0xcb, 0xaa, 0x4f, 0xff, + 0x79, 0xb6, 0x7f, 0x6f, 0xf0, 0x73, 0xfd, 0xa4, + 0xe9, 0xff, 0xfe, 0xda, 0xde, 0x0e, 0x37, 0xf9, + 0x9d, 0x7a, 0xea, 0xfc, 0xa6, 0xe7, 0x46, 0x23, + 0x03, 0x94, 0xe1, 0x5b, 0x21, 0x5a, 0x13, 0x5f, + 0x1c, 0xbe, 0xd0, 0xf4, 0xc9, 0x65, 0x1e, 0x8e, + 0x49, 0xa8, 0x5f, 0x09, 0x1b, 0x21, 0x5f, 0x68, + 0xc9, 0xc1, 0x09, 0x2f, 0xc3, 0x02, 0xb2, 0xa7, + 0x6f, 0x28, 0x2b, 0x58, 0x68, 0x86, 0x1c, 0x33, + 0x9b, 0x2f, 0xce, 0x9f, 0xfe, 0xcf, 0xba, 0xfa, + 0x7d, 0x4d, 0x6d, 0x80, 0xe3, 0xa5, 0x4b, 0xcf, + 0xbf, 0x61, 0xd9, 0xfa, 0xcd, 0x7a, 0xde, 0x61, + 0xd3, 0xfe, 0xfa, 0xde, 0xdc, 0x0e, 0xa6, 0xa7, + 0x4e, 0x6b, 0x51, 0x3a, 0x7f, 0xdd, 0xe1, 0xca, + 0x5c, 0x10, 0x84, 0xe8, 0xe3, 0xdb, 0xa8, 0xec, + 0xff, 0xf7, 0xd5, 0xfa, 0x37, 0xdf, 0xdf, 0xd5, + 0xf9, 0x53, 0xa3, 0x13, 0x3e, 0x42, 0xfb, 0x42, + 0x64, 0x04, 0x33, 0xf9, 0x7e, 0xff, 0xe6, 0x00, + 0xe9, 0xfc, 0xfc, 0x1a, 0xfd, 0x68, 0x3a, 0x7f, + 0xf9, 0x5b, 0x28, 0xbb, 0x7d, 0xff, 0x94, 0x78, + 0x4f, 0x77, 0xbc, 0xff, 0xf6, 0x5d, 0x7e, 0x99, + 0x7b, 0x78, 0x40, 0x87, 0x4f, 0xba, 0xb9, 0xfb, + 0x9d, 0x3a, 0xfd, 0xbb, 0x3a, 0x7f, 0x5e, 0xde, + 0x70, 0x3b, 0xc7, 0x45, 0x09, 0x91, 0xec, 0xbf, + 0xf4, 0xcb, 0x93, 0x6a, 0x3f, 0x3f, 0xb5, 0x02, + 0xdb, 0xdc, 0xf3, 0xa7, 0xff, 0xef, 0xe5, 0x75, + 0xd1, 0x96, 0xef, 0xe3, 0x4f, 0x5a, 0x4e, 0x9f, + 0xf6, 0x57, 0x5c, 0x14, 0xee, 0x43, 0x44, 0x0d, + 0x3e, 0xcb, 0xdb, 0x9f, 0xa1, 0x14, 0xbf, 0x5e, + 0x9f, 0xff, 0xff, 0xb2, 0xf6, 0xef, 0xe9, 0xd1, + 0x51, 0xee, 0xbb, 0x6c, 0xdb, 0x46, 0x33, 0xbf, + 0x4b, 0xcf, 0x10, 0x5c, 0xff, 0xbb, 0xb4, 0xa7, + 0x6d, 0xa3, 0x48, 0x4f, 0x10, 0x5c, 0xff, 0xd6, + 0xf5, 0xbc, 0xbf, 0xbe, 0x8d, 0x21, 0x3c, 0x41, + 0x73, 0xf9, 0x7d, 0xfb, 0xe8, 0xd2, 0x13, 0xc4, + 0x17, 0x3f, 0x33, 0x36, 0xd1, 0xa4, 0x27, 0x88, + 0x2e, 0x7f, 0xff, 0xbb, 0xff, 0xf3, 0x34, 0x55, + 0x2d, 0xe1, 0xf6, 0x9a, 0x30, 0x27, 0x88, 0x2e, + 0x6a, 0x74, 0x6c, 0x9d, 0x0a, 0x28, 0x0a, 0xb7, + 0x22, 0x7c, 0xfe, 0x2c, 0xaa, 0x5b, 0xf2, 0x8f, + 0xa7, 0xb8, 0x41, 0xc7, 0x4f, 0xfd, 0x6f, 0x5b, + 0xcb, 0xfb, 0xe8, 0xd2, 0x13, 0xc4, 0x17, 0x3f, + 0xcd, 0x55, 0x3d, 0x46, 0x8d, 0x21, 0x3c, 0x41, + 0x73, 0xeb, 0xd5, 0x59, 0xa1, 0x11, 0x45, 0xbd, + 0x5a, 0x7f, 0xf6, 0x84, 0xb7, 0x91, 0x6f, 0x5d, + 0x1a, 0x42, 0x78, 0x82, 0xe7, 0xff, 0xfe, 0xff, + 0xfc, 0xcd, 0x1a, 0xe6, 0x8a, 0xa5, 0xbc, 0x3e, + 0xd3, 0x46, 0x04, 0xf1, 0x05, 0xc6, 0x26, 0x4d, + 0x4a, 0x1f, 0x2e, 0x4f, 0xf5, 0xbc, 0x3e, 0xd3, + 0x46, 0x04, 0xf1, 0x05, 0xcf, 0xff, 0x77, 0x52, + 0xfa, 0xdb, 0xdb, 0x6c, 0xa2, 0xa5, 0x4f, 0xfb, + 0x1e, 0xfd, 0x6a, 0x3f, 0xa3, 0x70, 0xf1, 0x05, + 0xc2, 0x23, 0xa0, 0x52, 0x2a, 0xa1, 0x3f, 0xe4, + 0xf0, 0xdf, 0x81, 0x5d, 0x01, 0x3c, 0x41, 0x73, + 0xf5, 0xbd, 0x6b, 0x78, 0x06, 0x80, 0x2e, 0x7d, + 0x80, 0xd1, 0xa4, 0x27, 0x88, 0x2e, 0x6c, 0xba, + 0x1f, 0x9d, 0x8e, 0xe2, 0x94, 0x75, 0xd6, 0x17, + 0xf3, 0xf3, 0x33, 0x6d, 0x1a, 0x42, 0x78, 0x82, + 0xe7, 0xfc, 0x96, 0xf0, 0xfb, 0x4d, 0x18, 0x13, + 0xc4, 0x17, 0x36, 0x68, 0xe4, 0x46, 0x54, 0xfe, + 0x7f, 0x6b, 0xe6, 0x77, 0xe9, 0x79, 0xe2, 0x0b, + 0x9f, 0xf6, 0x79, 0xb6, 0x7f, 0x36, 0xe7, 0x9e, + 0x20, 0xb6, 0x1e, 0x14, 0x6c, 0xbb, 0x8e, 0x02, + 0xcf, 0xc7, 0xcf, 0x58, 0xc6, 0x2f, 0x18, 0xce, + 0xb0, 0xb5, 0x0b, 0x8c, 0xf8, 0x15, 0x00, 0x14, + 0xd1, 0x05, 0xb9, 0x10, 0x13, 0xfe, 0xc7, 0xdb, + 0x9e, 0xde, 0xdf, 0xad, 0x07, 0x4f, 0xe1, 0xfe, + 0x6d, 0x6d, 0x42, 0x74, 0xfa, 0x9b, 0xf0, 0x80, + 0xe9, 0xfb, 0x28, 0xeb, 0x2e, 0xec, 0xe8, 0xf2, + 0x22, 0xf8, 0xd3, 0xe4, 0xf3, 0xb8, 0x68, 0x3a, + 0x60, 0x29, 0xd3, 0xde, 0x56, 0x61, 0xd0, 0x74, + 0xfd, 0xda, 0x6e, 0xa2, 0x03, 0xa3, 0x63, 0x6f, + 0xf0, 0xa9, 0xff, 0xfc, 0xbe, 0xdb, 0xeb, 0xaa, + 0x5f, 0x13, 0x65, 0x4d, 0xec, 0x3a, 0x60, 0x29, + 0xd3, 0x2e, 0x93, 0xa7, 0xfb, 0x2f, 0x55, 0x66, + 0xfc, 0x68, 0xe9, 0xfd, 0x5c, 0xb7, 0xab, 0xad, + 0x07, 0x4c, 0x10, 0x95, 0x3f, 0xdf, 0xc6, 0xdc, + 0xfa, 0xfb, 0x63, 0xa1, 0x13, 0xf7, 0xf0, 0xd8, + 0x8a, 0xb1, 0x5e, 0xc4, 0x40, 0x62, 0x6e, 0xc5, + 0x7e, 0x2f, 0x53, 0xa0, 0x9a, 0xef, 0x16, 0x9c, + 0x10, 0x84, 0xa9, 0x30, 0xa7, 0x17, 0x93, 0xee, + 0x6b, 0xb8, 0x4a, 0x72, 0x36, 0x77, 0xc2, 0xea, + 0x7f, 0x53, 0x75, 0xcb, 0x65, 0x07, 0x42, 0xb6, + 0x40, 0xf9, 0x1b, 0x46, 0x94, 0x1a, 0x4c, 0x7d, + 0x1a, 0xa8, 0xa7, 0x5a, 0x1b, 0x1d, 0x49, 0x1e, + 0xfa, 0x35, 0x61, 0xa3, 0xac, 0xaf, 0xf6, 0xc9, + 0x73, 0xd8, 0x8b, 0x73, 0xa7, 0xb1, 0x99, 0x73, + 0xa7, 0xba, 0x8d, 0xef, 0x3a, 0x14, 0xf8, 0xe9, + 0x1f, 0x6f, 0x20, 0x9f, 0xbf, 0xae, 0xcc, 0xe7, + 0x9d, 0x3f, 0xfd, 0x4b, 0xfe, 0xba, 0x19, 0xf5, + 0xd3, 0x7f, 0x50, 0x74, 0xfe, 0xad, 0xdb, 0x67, + 0xf3, 0x63, 0xa1, 0x51, 0x75, 0xe2, 0xfe, 0x56, + 0x9d, 0xda, 0x37, 0x9d, 0x3f, 0xfd, 0xf7, 0xe8, + 0xcd, 0xbd, 0x5a, 0x6f, 0xaf, 0x54, 0xe8, 0xd0, + 0x7e, 0x98, 0x3f, 0x3f, 0x50, 0xd3, 0xdb, 0x83, + 0x41, 0xd3, 0x3e, 0x83, 0xa7, 0xd6, 0xcf, 0x3e, + 0xa7, 0x4f, 0xff, 0x59, 0x77, 0xba, 0xff, 0xe0, + 0x56, 0xaa, 0x02, 0xa7, 0xf0, 0x30, 0x53, 0xb9, + 0x0f, 0x10, 0x24, 0x3d, 0x16, 0x7b, 0x13, 0x8a, + 0x8c, 0xcd, 0xef, 0x3a, 0x7a, 0xf4, 0x77, 0xce, + 0x9e, 0xa6, 0xbd, 0x73, 0xa2, 0x83, 0xdd, 0xc1, + 0x9b, 0x11, 0x4f, 0xef, 0xf0, 0x6b, 0x55, 0x01, + 0xd3, 0x82, 0x10, 0x9f, 0x0f, 0xa9, 0xdd, 0xfd, + 0x8b, 0x87, 0xd1, 0xc6, 0xa6, 0x36, 0x44, 0xa8, + 0x16, 0xe7, 0xff, 0xd9, 0xff, 0x69, 0x75, 0x6f, + 0x6d, 0x79, 0xef, 0x53, 0xa2, 0x83, 0xfc, 0xd2, + 0x49, 0x1b, 0xa9, 0x57, 0x83, 0xcd, 0x32, 0x19, + 0x5e, 0x84, 0x6f, 0x46, 0x81, 0x3e, 0xf5, 0xd6, + 0xf5, 0x2a, 0x79, 0x16, 0xf5, 0x2a, 0x60, 0x84, + 0xa8, 0x79, 0xee, 0xe1, 0x38, 0x48, 0x26, 0xc0, + 0x94, 0xe3, 0x5d, 0x3f, 0xfe, 0xcb, 0xaa, 0x66, + 0x7e, 0xf6, 0xf2, 0xfe, 0x83, 0xa0, 0x07, 0xf0, + 0x12, 0x59, 0xff, 0xfc, 0xad, 0xbd, 0xf0, 0x68, + 0xbd, 0x71, 0x2d, 0xdf, 0xbd, 0x4e, 0x9f, 0xcd, + 0x52, 0xfb, 0x6a, 0xa2, 0x74, 0xe7, 0xf0, 0x9d, + 0x3e, 0x7e, 0x5f, 0x36, 0x2a, 0x5e, 0x68, 0xf0, + 0x6e, 0x35, 0x30, 0x14, 0xe9, 0x80, 0xa7, 0x4f, + 0xdf, 0xd5, 0xf9, 0xf7, 0x78, 0xd5, 0x00, 0x56, + 0x7f, 0xeb, 0xdb, 0x19, 0xbd, 0x41, 0x94, 0xdc, + 0xe9, 0xfd, 0x4a, 0x78, 0x77, 0xae, 0xc7, 0x4f, + 0x01, 0x9c, 0xfa, 0x9f, 0xdd, 0xd1, 0xa7, 0x6e, + 0x65, 0x27, 0x42, 0x9e, 0xc7, 0x1c, 0xcf, 0x57, + 0x5a, 0x6e, 0x74, 0xe0, 0x84, 0x25, 0x4f, 0xb3, + 0x4f, 0xf5, 0xa9, 0x4e, 0x2f, 0x27, 0xd8, 0xe0, + 0x84, 0x27, 0x42, 0x9f, 0x05, 0xce, 0x67, 0x83, + 0xdf, 0x79, 0xd3, 0x25, 0x8e, 0x9c, 0x10, 0x84, + 0xa9, 0xfb, 0xdd, 0xa6, 0xde, 0xb9, 0x4e, 0x2f, + 0x27, 0xd8, 0x0c, 0xc6, 0x8e, 0x95, 0x74, 0x22, + 0x54, 0x4c, 0x7e, 0x7d, 0x3d, 0xef, 0xea, 0xfd, + 0x91, 0xdb, 0x90, 0xb6, 0x85, 0x5c, 0x76, 0x79, + 0x16, 0x32, 0x69, 0x76, 0x15, 0x3b, 0x46, 0x31, + 0xc4, 0x15, 0x85, 0x05, 0xe3, 0x0e, 0x9f, 0x95, + 0x82, 0x3d, 0x73, 0xa7, 0xff, 0xbc, 0xca, 0x5f, + 0x5d, 0x19, 0xff, 0xe7, 0x80, 0x74, 0x58, 0xff, + 0x6e, 0x55, 0x27, 0x37, 0x48, 0x43, 0x36, 0x2c, + 0xae, 0xda, 0x23, 0x23, 0x7c, 0xa4, 0xdd, 0xa5, + 0x80, 0xe5, 0x6e, 0x95, 0xa6, 0x37, 0x14, 0x8e, + 0xca, 0x99, 0x54, 0xfe, 0x84, 0xeb, 0x53, 0xce, + 0x43, 0x3a, 0x6a, 0xc9, 0x41, 0x76, 0x9f, 0x0a, + 0xea, 0x79, 0x68, 0x25, 0x9c, 0xfe, 0x75, 0x82, + 0xb5, 0x92, 0xf5, 0xeb, 0x39, 0xfd, 0x63, 0x35, + 0x6d, 0x0e, 0xc0, 0xc2, 0x8b, 0x72, 0x58, 0x9e, + 0xf9, 0xdd, 0x96, 0xf8, 0xc3, 0x61, 0xd0, 0x88, + 0xd3, 0x7e, 0x21, 0xae, 0x27, 0xf3, 0xb0, 0x53, + 0xb9, 0x0d, 0x17, 0x1c, 0xfe, 0x76, 0x0a, 0x77, + 0x21, 0xa2, 0xeb, 0x9f, 0xf9, 0xdc, 0xf7, 0x60, + 0xa7, 0x72, 0x1a, 0x25, 0x18, 0x58, 0x46, 0x14, + 0x3c, 0xa3, 0x63, 0xbd, 0x27, 0x68, 0x77, 0xe8, + 0x78, 0x6e, 0xd0, 0x3b, 0x11, 0x24, 0xb5, 0x38, + 0xb9, 0xdb, 0x79, 0xdc, 0xff, 0xe7, 0x33, 0x9e, + 0xec, 0x14, 0xee, 0x43, 0x44, 0xb5, 0x3e, 0x14, + 0xee, 0x43, 0x44, 0x6f, 0x3f, 0xee, 0x7b, 0xb0, + 0x53, 0xb9, 0x0d, 0x12, 0xfc, 0x9d, 0x87, 0xec, + 0xc3, 0x09, 0xfc, 0xec, 0x14, 0xee, 0x43, 0x45, + 0x57, 0x3f, 0xda, 0x73, 0xf9, 0x4d, 0xf9, 0x87, + 0x4d, 0xb7, 0x8e, 0x9f, 0xb0, 0x53, 0xb9, 0x0d, + 0x12, 0x04, 0x68, 0x3c, 0xc7, 0x0b, 0xcf, 0xab, + 0xdf, 0xea, 0x4e, 0x87, 0x9e, 0x5d, 0x24, 0x91, + 0xa5, 0x1e, 0x9d, 0x0d, 0x19, 0xff, 0xbf, 0x94, + 0x3b, 0x71, 0x7f, 0xfc, 0xb9, 0xd0, 0xe3, 0xf0, + 0x12, 0x99, 0xfc, 0xec, 0x14, 0xee, 0x43, 0x45, + 0x91, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0xb5, + 0xe7, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x17, 0x24, + 0xf8, 0x53, 0xb9, 0x0d, 0x17, 0x64, 0xff, 0xb9, + 0xee, 0xc1, 0x4e, 0xe4, 0x34, 0x51, 0xd2, 0x76, + 0x1f, 0xb3, 0x0c, 0x27, 0xc2, 0x9d, 0xc8, 0x68, + 0xa5, 0x67, 0xff, 0xff, 0xac, 0xb4, 0x2d, 0xbc, + 0xdb, 0xb6, 0xb7, 0x9c, 0x96, 0xf3, 0x56, 0x5a, + 0x30, 0xe9, 0xf3, 0x99, 0xcf, 0x76, 0x22, 0xc9, + 0xa3, 0x08, 0xa1, 0x73, 0xbd, 0xf0, 0xc8, 0xa4, + 0x9f, 0xd1, 0xcf, 0xb0, 0x84, 0x07, 0x75, 0x3b, + 0xbc, 0x2c, 0x5b, 0x43, 0x66, 0x7f, 0x9e, 0xec, + 0x14, 0xee, 0x43, 0x44, 0x71, 0x3f, 0xde, 0x76, + 0x0a, 0x77, 0x21, 0xa2, 0xb5, 0x93, 0xb9, 0x10, + 0x57, 0x41, 0x9f, 0xfc, 0xe6, 0x73, 0xdd, 0x82, + 0x9d, 0xc8, 0x68, 0x96, 0xe6, 0xca, 0x4e, 0x9f, + 0xdb, 0x63, 0x18, 0xbe, 0xa9, 0xd1, 0x49, 0xe4, + 0xf8, 0x5a, 0x75, 0xb6, 0x79, 0xd3, 0x9e, 0xc4, + 0x3a, 0x7f, 0xfe, 0xb7, 0x52, 0x0d, 0xad, 0xcf, + 0xca, 0xff, 0x5e, 0xde, 0x74, 0x1a, 0x21, 0xb9, + 0xff, 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x98, + 0x27, 0x0e, 0xa0, 0x2a, 0x7f, 0x7f, 0x2f, 0x75, + 0x1a, 0x9d, 0x27, 0x2a, 0x6c, 0x94, 0x11, 0xec, + 0x3b, 0x83, 0x68, 0xbe, 0xc1, 0x7b, 0xa3, 0x6f, + 0x1c, 0x9c, 0xcd, 0x44, 0xa9, 0xff, 0x73, 0xdd, + 0x82, 0x9d, 0xc8, 0x68, 0x98, 0xe4, 0xef, 0x1f, + 0x13, 0x07, 0x27, 0xe5, 0xae, 0x8b, 0xe5, 0x8e, + 0x9f, 0xff, 0xfe, 0x7d, 0x6d, 0x9a, 0x86, 0xb6, + 0xc4, 0xca, 0xe8, 0xca, 0x5f, 0x5f, 0x7f, 0xc7, + 0x4f, 0x27, 0x72, 0x1a, 0x2b, 0x19, 0xff, 0x6e, + 0x65, 0xff, 0x9a, 0xaf, 0xee, 0x74, 0x69, 0x4c, + 0x6e, 0x92, 0xe1, 0x84, 0x0d, 0xca, 0xe7, 0xff, + 0x2f, 0xef, 0xa7, 0x55, 0xfe, 0x22, 0x89, 0xd3, + 0xfe, 0xcd, 0xad, 0x95, 0xa5, 0xeb, 0x63, 0xa7, + 0xfd, 0x9b, 0x0f, 0xb5, 0xa0, 0x15, 0x79, 0xd3, + 0xff, 0xff, 0x7f, 0x5a, 0x31, 0x34, 0x7f, 0x56, + 0xfd, 0x19, 0x4b, 0xf3, 0x5f, 0x82, 0xa7, 0x4f, + 0xed, 0xd5, 0x43, 0x4f, 0x6e, 0x0d, 0x07, 0x4e, + 0xb7, 0x9d, 0x89, 0xe6, 0x22, 0x2d, 0x92, 0x7e, + 0x7f, 0x74, 0x1d, 0xc7, 0xe9, 0xb5, 0x68, 0xe9, + 0xf7, 0x7e, 0x8d, 0x6a, 0x74, 0x7c, 0xf0, 0x6a, + 0x31, 0x3c, 0xa3, 0xdf, 0x3a, 0x7a, 0xb5, 0x50, + 0x1d, 0x16, 0x3c, 0x00, 0x8f, 0xcf, 0x95, 0xe0, + 0xd5, 0xe7, 0x4e, 0x5f, 0xbc, 0xe8, 0x68, 0xf0, + 0xee, 0x51, 0x3f, 0x2b, 0x33, 0xfe, 0xd2, 0x74, + 0xaa, 0x74, 0x78, 0xdf, 0x71, 0x74, 0xc0, 0x52, + 0xa6, 0x08, 0x4a, 0x8f, 0x1a, 0xb0, 0x8a, 0xcf, + 0xee, 0x7f, 0xf2, 0xf6, 0xf1, 0x4e, 0x34, 0x33, + 0xdb, 0x75, 0x35, 0x3a, 0x72, 0xfd, 0xa3, 0xa6, + 0xfa, 0x9d, 0x0d, 0x1b, 0x11, 0x1c, 0x9e, 0x10, + 0x63, 0x79, 0xd3, 0xb7, 0x8e, 0x93, 0xa1, 0x51, + 0x6f, 0x8a, 0x9e, 0x21, 0xb1, 0x1c, 0xd6, 0xb9, + 0xd3, 0x82, 0x10, 0x95, 0x3f, 0xf6, 0x26, 0xcb, + 0x46, 0xfc, 0xb7, 0x52, 0x53, 0x8b, 0xc9, 0xef, + 0x0b, 0x7e, 0xc7, 0x48, 0x4e, 0x9f, 0x33, 0xd7, + 0x07, 0x1d, 0x14, 0x1e, 0xde, 0xec, 0x97, 0x50, + 0xf9, 0xfc, 0x9e, 0xaf, 0xf1, 0xb7, 0x1d, 0x36, + 0x5c, 0xe8, 0xa4, 0xf2, 0x2c, 0x67, 0x3d, 0x7f, + 0xe3, 0xce, 0x9f, 0xfd, 0xea, 0xdb, 0x36, 0xfe, + 0x36, 0xfe, 0x54, 0xe8, 0x13, 0xed, 0xb9, 0x0c, + 0x9c, 0xab, 0x9b, 0x1b, 0x31, 0x63, 0x28, 0xb0, + 0xb1, 0xb2, 0xd0, 0xf9, 0xe3, 0xbf, 0x99, 0x56, + 0x16, 0x17, 0x7d, 0xdf, 0x09, 0x09, 0xf0, 0xa7, + 0x72, 0x1a, 0x2b, 0x79, 0xff, 0x73, 0xdd, 0x82, + 0x9d, 0xc8, 0x68, 0x9c, 0x24, 0xec, 0x3f, 0x66, + 0x18, 0x4f, 0x85, 0x3b, 0x90, 0xd1, 0x68, 0xcf, + 0x63, 0x5e, 0xd2, 0x74, 0x9d, 0x87, 0xa9, 0x63, + 0x09, 0xe4, 0xee, 0x43, 0x45, 0xb5, 0x3f, 0x59, + 0x58, 0xbe, 0xd8, 0xe9, 0x9d, 0x82, 0x7b, 0x17, + 0x2b, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x5c, + 0xb3, 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x8b, 0xb6, + 0x15, 0x9c, 0xad, 0x41, 0x43, 0xe3, 0xff, 0xda, + 0x1b, 0x7a, 0x65, 0x87, 0xfa, 0x76, 0x13, 0x77, + 0x19, 0x2d, 0x9e, 0xfe, 0xff, 0x52, 0xcb, 0x9d, + 0xcf, 0xe7, 0x60, 0xa7, 0x72, 0x1a, 0x2a, 0x79, + 0xe4, 0xee, 0x43, 0x45, 0x73, 0x3f, 0x9d, 0x82, + 0x9d, 0xc8, 0x68, 0xb3, 0xa0, 0x4f, 0x9a, 0xc5, + 0x73, 0xe1, 0x4e, 0xe4, 0x34, 0x48, 0x53, 0xfd, + 0xea, 0x6b, 0x7f, 0xab, 0x2a, 0x74, 0xcf, 0x76, + 0x1f, 0x50, 0x0c, 0x27, 0x9b, 0xa5, 0x95, 0x87, + 0x4f, 0xec, 0xb5, 0x6b, 0x83, 0x41, 0xd2, 0x76, + 0x26, 0x23, 0xd0, 0x8b, 0xa9, 0x6d, 0xc9, 0xe7, + 0xff, 0x39, 0x9c, 0xf7, 0x60, 0xa7, 0x72, 0x1a, + 0x27, 0xc8, 0x45, 0x49, 0x7b, 0xb8, 0xec, 0xac, + 0x8f, 0x3e, 0x14, 0xee, 0x43, 0x45, 0x65, 0x3f, + 0xee, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, 0x13, 0x74, + 0xde, 0x76, 0x1f, 0xb3, 0x0c, 0x27, 0xc2, 0x9d, + 0xc8, 0x68, 0x95, 0xa7, 0xfb, 0x4d, 0xbd, 0xa3, + 0x54, 0xca, 0x9d, 0x3e, 0x73, 0x39, 0xee, 0xc3, + 0xed, 0xe3, 0x09, 0xf0, 0xa7, 0x72, 0x1a, 0x25, + 0xc9, 0xfe, 0xf7, 0x3e, 0xff, 0x56, 0x54, 0xe9, + 0xe6, 0xde, 0xfa, 0x9d, 0x3e, 0x73, 0x39, 0xee, + 0xc4, 0x45, 0xd8, 0xc3, 0x8e, 0x27, 0xfe, 0x77, + 0x3d, 0xd8, 0x29, 0xdc, 0x86, 0x88, 0xee, 0x7f, + 0xaf, 0x6c, 0x7b, 0xbb, 0xf5, 0x3a, 0x7e, 0x6e, + 0x7d, 0x53, 0x3e, 0x74, 0xfd, 0xba, 0x7f, 0x2d, + 0xd7, 0x3a, 0x7c, 0x29, 0xdc, 0x86, 0x8a, 0x86, + 0x7d, 0x96, 0xbe, 0x50, 0x74, 0xff, 0xff, 0x6b, + 0xdb, 0xbf, 0x51, 0x6e, 0xf5, 0xd6, 0xde, 0xbd, + 0x73, 0x63, 0xa4, 0xed, 0xd1, 0x32, 0x46, 0xe1, + 0x7e, 0x18, 0xdc, 0xc3, 0x70, 0x9e, 0x7c, 0x29, + 0xdc, 0x86, 0x8a, 0xa2, 0x7f, 0xdc, 0xf7, 0x60, + 0xa7, 0x72, 0x1a, 0x26, 0xb9, 0x3b, 0x0f, 0xd9, + 0x86, 0x13, 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x8a, + 0xb2, 0x7f, 0xe7, 0x73, 0xdd, 0x82, 0x9d, 0xc8, + 0x68, 0x91, 0x27, 0xc2, 0x9d, 0xc8, 0x68, 0xb4, + 0xa7, 0xfd, 0xcf, 0x76, 0x0a, 0x77, 0x21, 0xa2, + 0x7d, 0x93, 0xb0, 0xfd, 0x98, 0x61, 0x3f, 0xf9, + 0xcc, 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x42, + 0x4f, 0xad, 0xea, 0x14, 0x4e, 0x9f, 0x0a, 0x77, + 0x21, 0xa2, 0x8f, 0x9f, 0xff, 0xb3, 0x6b, 0x2e, + 0xf7, 0x5f, 0xfc, 0x0a, 0xd5, 0x40, 0x54, 0xf9, + 0xcc, 0xe7, 0xb9, 0x51, 0x69, 0x84, 0xfb, 0x86, + 0x10, 0xab, 0xc5, 0x94, 0x43, 0x21, 0xe9, 0x78, + 0x99, 0xa6, 0x35, 0xc4, 0x86, 0x95, 0x24, 0xed, + 0x1d, 0xda, 0x18, 0x40, 0x27, 0xbc, 0x60, 0xd3, + 0xf9, 0xd8, 0x29, 0xdc, 0x86, 0x88, 0x8a, 0x7e, + 0xc1, 0x4e, 0xe4, 0x34, 0x45, 0x73, 0xfd, 0xbb, + 0x76, 0x0a, 0x77, 0x21, 0xa2, 0xb8, 0x87, 0x1f, + 0xd7, 0x1a, 0xcf, 0x65, 0xba, 0xe7, 0x4f, 0xec, + 0xf0, 0x80, 0x1a, 0xd4, 0xe9, 0x6e, 0x9f, 0x3d, + 0x3a, 0x90, 0x4f, 0xfd, 0xde, 0x7f, 0xbf, 0x7d, + 0x6c, 0x38, 0x74, 0xfb, 0x3d, 0xb0, 0xe1, 0xd3, + 0xaf, 0x5f, 0x58, 0xfa, 0xae, 0x89, 0x3e, 0x14, + 0xee, 0x43, 0x45, 0x3d, 0x0d, 0xc4, 0x7d, 0xac, + 0x25, 0xf0, 0xd2, 0x7f, 0xf7, 0x3d, 0xdb, 0x7f, + 0x83, 0x5a, 0xa8, 0x0e, 0x87, 0x22, 0x07, 0x63, + 0x79, 0xce, 0xdb, 0x0e, 0x9f, 0x2b, 0xc1, 0xab, + 0xce, 0x9e, 0x4e, 0xe4, 0x34, 0x56, 0x70, 0xd1, + 0xe9, 0x89, 0x44, 0xfd, 0x43, 0x17, 0xef, 0xa9, + 0xd3, 0x96, 0x8f, 0x1d, 0x3e, 0xb8, 0x37, 0xfd, + 0xe7, 0x4e, 0xd7, 0xaa, 0x74, 0xfb, 0x1a, 0xa3, + 0x50, 0x1d, 0x27, 0x62, 0x37, 0x04, 0x8a, 0xc5, + 0xdc, 0x39, 0xf2, 0xad, 0xe3, 0x93, 0xff, 0x3b, + 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0x8b, 0x3f, + 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0xb2, 0x67, 0xf3, + 0xb0, 0x53, 0xb9, 0x0d, 0x16, 0xc4, 0xec, 0xae, + 0x1d, 0x3e, 0x14, 0xee, 0x43, 0x45, 0xb7, 0x27, + 0x3c, 0xf2, 0xb0, 0x6a, 0x7f, 0xcd, 0xc6, 0xe6, + 0xeb, 0x5e, 0xd9, 0xf5, 0x01, 0xd3, 0xf2, 0xfd, + 0xfb, 0x6a, 0xc3, 0xa7, 0xc2, 0x9d, 0xc8, 0x68, + 0xbc, 0x27, 0xaf, 0xb9, 0x96, 0x3a, 0x7f, 0xe5, + 0xde, 0xca, 0x5f, 0x7c, 0xfd, 0xea, 0x74, 0xfb, + 0x3e, 0x06, 0xf5, 0x3a, 0x7c, 0xbb, 0xf5, 0xea, + 0x9d, 0x3a, 0xea, 0x03, 0xa4, 0xed, 0xd7, 0x93, + 0x84, 0xa1, 0x37, 0x0b, 0xe9, 0x30, 0xb1, 0x20, + 0x11, 0xfe, 0x53, 0x72, 0x99, 0xde, 0xca, 0x4e, + 0x9f, 0x0a, 0x77, 0x21, 0xa2, 0xf4, 0x9f, 0xf7, + 0xfa, 0xef, 0x5d, 0x37, 0xf5, 0x07, 0x4f, 0xb1, + 0x96, 0xed, 0x8e, 0x93, 0xb6, 0x45, 0xa6, 0x0e, + 0x6f, 0x30, 0x6f, 0x41, 0x85, 0x64, 0x52, 0xd1, + 0x0b, 0x5c, 0x8f, 0xc1, 0x09, 0x3d, 0x1a, 0x58, + 0xae, 0x31, 0x30, 0x07, 0x7f, 0x84, 0x2e, 0xb1, + 0xd5, 0x86, 0x33, 0x69, 0xec, 0xb7, 0x5c, 0xe9, + 0xfd, 0x9e, 0x10, 0x03, 0x5a, 0x9d, 0x2d, 0xd3, + 0xe7, 0xa7, 0x52, 0x09, 0xf0, 0xa7, 0x72, 0x1a, + 0x22, 0x39, 0xff, 0xeb, 0x6b, 0x5a, 0xe3, 0x3f, + 0x8d, 0xb4, 0x2b, 0x75, 0x3a, 0x7f, 0xcf, 0x5a, + 0x07, 0x2f, 0x7f, 0x5c, 0xe9, 0xff, 0xfe, 0x5b, + 0x77, 0xae, 0xb6, 0xfe, 0x5e, 0xde, 0xb6, 0x78, + 0x27, 0x4d, 0x43, 0x0a, 0x98, 0x21, 0x2a, 0x7f, + 0xe7, 0xb9, 0x2d, 0xe6, 0xac, 0xb4, 0x38, 0x06, + 0xb4, 0x22, 0xf3, 0xfb, 0x37, 0x7f, 0xcb, 0x75, + 0xce, 0x9f, 0xff, 0x91, 0xc3, 0xdf, 0xf5, 0xdc, + 0x2b, 0x7f, 0xf0, 0x0e, 0x86, 0xe2, 0xa7, 0x1c, + 0x2d, 0x43, 0x0a, 0x56, 0x84, 0xfb, 0xa1, 0x25, + 0x75, 0xd6, 0xc6, 0xb3, 0xe1, 0x4e, 0xe4, 0x34, + 0x45, 0xd3, 0xeb, 0x83, 0x7f, 0xde, 0x5b, 0x3d, + 0x92, 0x76, 0x1f, 0x47, 0x18, 0x43, 0x93, 0x09, + 0x7c, 0x38, 0xe7, 0xfe, 0x67, 0x3d, 0xd8, 0x29, + 0xdc, 0x86, 0x89, 0x9a, 0x75, 0x2b, 0x53, 0xa7, + 0x27, 0x98, 0x74, 0xed, 0xd5, 0xb9, 0xba, 0x8e, + 0x9f, 0x27, 0x5b, 0xcd, 0x1d, 0x3f, 0xf3, 0x7f, + 0xaf, 0xe4, 0xf6, 0xd7, 0xc4, 0x3a, 0x7f, 0xff, + 0xb5, 0xef, 0xa7, 0xf3, 0x6b, 0x6b, 0xa2, 0xca, + 0xf1, 0x5a, 0x0e, 0x8c, 0x45, 0x8d, 0x91, 0xe7, + 0xff, 0xdb, 0x7b, 0xf4, 0x65, 0x7d, 0xd6, 0x70, + 0x42, 0x12, 0xa7, 0x93, 0xb9, 0x0d, 0x16, 0x7c, + 0xff, 0xbd, 0x65, 0x7f, 0xae, 0xb7, 0xa9, 0xd3, + 0xff, 0xde, 0x1b, 0x76, 0x8c, 0x6d, 0xfc, 0xae, + 0x7c, 0xe9, 0xc1, 0x08, 0x4a, 0x9f, 0xf7, 0x3e, + 0xbf, 0xc4, 0xdb, 0x04, 0xa7, 0x17, 0x93, 0xfc, + 0xb6, 0xf6, 0xdf, 0xee, 0x68, 0xe9, 0xfe, 0xf7, + 0xf6, 0xdf, 0xb6, 0xb7, 0xa9, 0xd0, 0x89, 0xf8, + 0x8a, 0xc3, 0x0a, 0xec, 0x7c, 0x06, 0xdb, 0xa5, + 0xb6, 0x39, 0x9f, 0xff, 0xff, 0xb3, 0x01, 0x5c, + 0xcf, 0xe9, 0xde, 0x3f, 0x5c, 0xb3, 0xab, 0xfc, + 0xa1, 0x98, 0xc3, 0xa7, 0x55, 0x68, 0x3a, 0x76, + 0xe6, 0x58, 0xe8, 0x7a, 0x31, 0x2b, 0x08, 0xab, + 0x8e, 0x4f, 0x52, 0x0d, 0x50, 0xe9, 0xf5, 0xbe, + 0xb8, 0x26, 0x4f, 0xfb, 0xc2, 0xfc, 0x1a, 0x35, + 0xed, 0x8d, 0x10, 0x6b, 0x8d, 0x2c, 0xfb, 0x93, + 0xcc, 0x53, 0xa7, 0xe7, 0xdb, 0xfd, 0x4d, 0xce, + 0x96, 0x21, 0xe9, 0xfc, 0x96, 0x7f, 0xff, 0xdc, + 0x1b, 0xe5, 0xae, 0xb9, 0xfa, 0x6f, 0x83, 0xea, + 0x39, 0x87, 0x43, 0xd3, 0x40, 0xc8, 0x57, 0xfc, + 0x9a, 0x7e, 0x1c, 0xda, 0xb6, 0x53, 0xa7, 0xff, + 0xf6, 0xbf, 0xed, 0x3a, 0x37, 0xff, 0xaf, 0x5f, + 0x53, 0x6f, 0x04, 0xe9, 0xff, 0xfe, 0xad, 0x54, + 0x6e, 0xae, 0x66, 0x5e, 0xbb, 0xeb, 0xcf, 0xc3, + 0xa7, 0xad, 0x9e, 0x6c, 0x74, 0x79, 0x11, 0x36, + 0x66, 0x99, 0x9b, 0xa1, 0xa2, 0xfc, 0x9f, 0xf9, + 0xf7, 0xd5, 0xef, 0x65, 0xee, 0xac, 0x3a, 0x7f, + 0xef, 0x67, 0xf3, 0x2b, 0xa1, 0xf5, 0x61, 0xd1, + 0x4a, 0x22, 0xea, 0x8d, 0x3d, 0xff, 0xaa, 0x1d, + 0x0a, 0x98, 0xd6, 0xc4, 0x79, 0x0b, 0x3e, 0x24, + 0x9f, 0xff, 0xf9, 0xbd, 0x6d, 0xe6, 0x39, 0xa7, + 0xf0, 0xef, 0xb7, 0xa9, 0xaa, 0x7a, 0x83, 0xa7, + 0xfd, 0xcf, 0x67, 0x3e, 0xf5, 0x5d, 0x27, 0x4f, + 0x65, 0x7d, 0xf3, 0xa3, 0xe7, 0xc1, 0xa9, 0xfc, + 0xf6, 0xfd, 0x7a, 0xa7, 0x4f, 0xfa, 0xf5, 0x07, + 0xa9, 0xa5, 0xf9, 0x73, 0xa1, 0x4f, 0x97, 0x09, + 0x26, 0x6b, 0x53, 0xa4, 0xc3, 0xa2, 0x93, 0x50, + 0xdd, 0x8c, 0x4f, 0xff, 0xfa, 0xde, 0xa6, 0xf9, + 0x4d, 0xc4, 0x56, 0xe0, 0xdf, 0xf7, 0xdb, 0x0e, + 0x9c, 0x10, 0x84, 0xa9, 0xed, 0xb6, 0xef, 0x94, + 0xe2, 0xf2, 0x7f, 0xdd, 0x64, 0xf7, 0x68, 0xaf, + 0x58, 0xe8, 0x54, 0xc8, 0x90, 0x9f, 0xd0, 0x8b, + 0xb1, 0x94, 0xff, 0xd9, 0xfa, 0x6f, 0x83, 0xea, + 0x39, 0x87, 0x4f, 0xb2, 0xef, 0x65, 0x8e, 0x85, + 0x3e, 0xba, 0xa2, 0x4c, 0x0b, 0x1d, 0x3f, 0x50, + 0xd3, 0xdb, 0x83, 0x41, 0xd3, 0xfb, 0x4d, 0xb2, + 0xf6, 0xf5, 0xce, 0x9b, 0xc2, 0x74, 0x6e, 0xa3, + 0xfe, 0xf1, 0xa5, 0x8d, 0x67, 0xfe, 0xd9, 0x76, + 0x15, 0xbf, 0x93, 0xdb, 0x1d, 0x3f, 0x5b, 0x5a, + 0x1a, 0xb7, 0x8e, 0x8d, 0x8f, 0xda, 0xc8, 0xb2, + 0x73, 0x74, 0x66, 0xee, 0xee, 0xb0, 0xe2, 0x8d, + 0xd0, 0x57, 0xb4, 0x36, 0x32, 0x50, 0x9d, 0x31, + 0x94, 0xf8, 0xd5, 0xa8, 0xe2, 0x04, 0xd5, 0x85, + 0x96, 0x94, 0x1b, 0xc8, 0x20, 0x86, 0xd7, 0xe1, + 0x11, 0x58, 0xda, 0xaf, 0x0a, 0xc0, 0x90, 0xee, + 0x42, 0x53, 0x7c, 0x2a, 0xe1, 0xcc, 0xf9, 0x0b, + 0x53, 0x8a, 0x67, 0xff, 0x39, 0x9c, 0xf7, 0x60, + 0xa7, 0x72, 0x1a, 0x26, 0xc9, 0xfc, 0xec, 0x14, + 0xee, 0x43, 0x45, 0x5b, 0x3f, 0x9e, 0xfd, 0x6f, + 0xf5, 0x79, 0xd3, 0xd9, 0x6e, 0xb9, 0xd2, 0xdd, + 0x30, 0xf5, 0x3e, 0x69, 0x3e, 0x14, 0xee, 0x43, + 0x45, 0x69, 0x3f, 0xfc, 0x8a, 0x38, 0x0e, 0x67, + 0x3d, 0x7c, 0x87, 0x4f, 0xff, 0x3e, 0xb9, 0xf5, + 0x67, 0x65, 0xee, 0xac, 0x3a, 0x6b, 0xdd, 0x51, + 0x2f, 0xc9, 0x53, 0xf9, 0x5b, 0x6f, 0xbf, 0xd4, + 0x07, 0x4f, 0xd4, 0x65, 0xbd, 0x8c, 0x3a, 0x7f, + 0xb1, 0xbf, 0xea, 0x1a, 0x5f, 0x53, 0xa7, 0xf3, + 0xf2, 0xfd, 0xf0, 0x6a, 0x74, 0x9c, 0xdc, 0x4f, + 0xcb, 0x0b, 0x3d, 0x0c, 0x7b, 0x16, 0xf1, 0xb7, + 0xcb, 0x6e, 0x79, 0x3f, 0x9d, 0x82, 0x9d, 0xc8, + 0x68, 0xb0, 0x27, 0xc2, 0x9d, 0xc8, 0x68, 0x9d, + 0x67, 0xff, 0xf5, 0x96, 0x8d, 0xb1, 0x8e, 0xad, + 0x72, 0xde, 0xae, 0xb4, 0x1d, 0x3e, 0x73, 0x39, + 0xee, 0xc4, 0x4a, 0xb4, 0x61, 0x3e, 0x14, 0xee, + 0x43, 0x45, 0xb3, 0x3f, 0xe0, 0x2d, 0x5d, 0x6f, + 0x27, 0x09, 0xd2, 0x76, 0x1f, 0x67, 0x18, 0x4f, + 0x27, 0x72, 0x1a, 0x2e, 0x69, 0x30, 0xe9, 0x9d, + 0x82, 0x6e, 0xc2, 0x57, 0x3f, 0x9d, 0x82, 0x9d, + 0xc8, 0x68, 0xbb, 0xe7, 0x9d, 0xa7, 0x60, 0x1d, + 0x0a, 0xda, 0xcb, 0x65, 0x3e, 0xe5, 0x0c, 0x29, + 0x52, 0xf4, 0xa8, 0x11, 0x72, 0x64, 0x34, 0x01, + 0x0a, 0xca, 0xb0, 0xdc, 0xaf, 0x79, 0xdc, 0xff, + 0xbf, 0xce, 0x5a, 0xd5, 0x9c, 0xf3, 0xa7, 0xf7, + 0xe8, 0xc6, 0xfb, 0xae, 0x93, 0xa7, 0xc2, 0x9d, + 0xc8, 0x68, 0x95, 0xe7, 0xfd, 0xfe, 0x0e, 0xd6, + 0x5a, 0x30, 0x27, 0x4f, 0x6b, 0xc2, 0xf3, 0xa7, + 0xff, 0xf7, 0xaf, 0xcc, 0xef, 0x82, 0xc1, 0xef, + 0xd0, 0x9e, 0xa0, 0xe8, 0xd9, 0x10, 0x7c, 0x43, + 0x1b, 0x23, 0x96, 0xd0, 0xc2, 0x9d, 0xcf, 0x72, + 0xa6, 0xed, 0x87, 0x15, 0x8c, 0x56, 0x75, 0xd7, + 0x62, 0xa7, 0xff, 0x5b, 0xbd, 0x75, 0x7f, 0x86, + 0x8d, 0x40, 0x54, 0xfe, 0xe4, 0x76, 0xc2, 0xdd, + 0xb9, 0x4f, 0x9e, 0xa3, 0x92, 0x72, 0x2a, 0xae, + 0x64, 0x76, 0x6d, 0xa1, 0x5b, 0x3f, 0xf9, 0xcc, + 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, 0x30, 0xcf, + 0xff, 0xd8, 0xed, 0xe3, 0xc0, 0x76, 0x9b, 0xbc, + 0x2b, 0x57, 0x9d, 0x3f, 0xf6, 0xe9, 0x94, 0xbf, + 0x5e, 0xbf, 0xd5, 0xe7, 0x4f, 0xe5, 0xea, 0x2f, + 0xa7, 0x84, 0xe9, 0xf7, 0x0b, 0xf9, 0x87, 0x47, + 0xcf, 0x63, 0x53, 0x39, 0xf9, 0x8b, 0x9f, 0xbe, + 0xe1, 0xd3, 0xe5, 0xcf, 0xdf, 0x70, 0xe9, 0xfa, + 0xde, 0xbd, 0x7a, 0x9d, 0x07, 0xb0, 0xc2, 0xf9, + 0xff, 0xff, 0x7f, 0x02, 0xdf, 0x7c, 0xb6, 0x8f, + 0xe5, 0x1e, 0xcf, 0xd2, 0xfa, 0x9d, 0x3f, 0x36, + 0xdf, 0x7f, 0xa8, 0x0e, 0x9f, 0xfc, 0xb7, 0x02, + 0xbf, 0x29, 0xbd, 0x1a, 0x84, 0xe8, 0x53, 0xfd, + 0xf1, 0x8c, 0xfd, 0x5f, 0x69, 0x67, 0x5c, 0xe9, + 0xff, 0xff, 0x0e, 0x36, 0x5f, 0xdf, 0x46, 0xff, + 0xad, 0xbb, 0xd7, 0x5b, 0xd4, 0xe9, 0x65, 0x28, + 0x9f, 0x12, 0xf9, 0xef, 0xa8, 0x37, 0x9d, 0x3d, + 0xdb, 0xf6, 0xc3, 0xa4, 0xe6, 0xe2, 0xb8, 0x3d, + 0xa1, 0x44, 0x8f, 0x6d, 0x24, 0x5a, 0x1e, 0x1f, + 0x86, 0x05, 0xca, 0x35, 0x23, 0x9f, 0x0a, 0x77, + 0x21, 0xa2, 0xae, 0x9f, 0xe7, 0xbb, 0x05, 0x3b, + 0x90, 0xd1, 0x1e, 0x49, 0xd8, 0x7e, 0x3c, 0x61, + 0x3f, 0x9d, 0x82, 0x9d, 0xc8, 0x68, 0xb0, 0x67, + 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x16, 0x54, 0xfe, + 0x76, 0x0a, 0x77, 0x21, 0xa2, 0xd3, 0x9e, 0x4e, + 0xe4, 0x34, 0x5b, 0x93, 0xff, 0x2e, 0x0e, 0x7f, + 0x45, 0xb2, 0x90, 0x9d, 0x02, 0x7d, 0xf5, 0x2b, + 0x9f, 0xe5, 0xfe, 0xac, 0x5a, 0xdf, 0xc7, 0x4f, + 0xfb, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x45, 0x0f, + 0x3f, 0x57, 0xa9, 0xad, 0xfc, 0x74, 0xfd, 0xf6, + 0xe6, 0x0a, 0xd4, 0xe9, 0xff, 0xfb, 0x36, 0xb2, + 0xef, 0x75, 0xff, 0xc0, 0xad, 0x54, 0x05, 0x49, + 0xd8, 0x9a, 0xd7, 0x88, 0x58, 0x73, 0x74, 0x6d, + 0x4b, 0xb7, 0x0b, 0xe7, 0xf3, 0xb0, 0x53, 0xb9, + 0x0d, 0x17, 0x9c, 0x2b, 0x22, 0xc9, 0xe8, 0x9b, + 0x29, 0x64, 0xbe, 0x1a, 0x61, 0xfe, 0x24, 0xcc, + 0x3b, 0xb1, 0xdf, 0xe5, 0x02, 0x36, 0x61, 0x9f, + 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x52, 0xd3, 0xf9, + 0xd8, 0x29, 0xdc, 0x86, 0x8b, 0x0a, 0x7f, 0x3b, + 0x05, 0x3b, 0x90, 0xd1, 0x65, 0xcf, 0x3b, 0x4e, + 0xeb, 0xe6, 0xea, 0x74, 0xe7, 0x6f, 0x61, 0xd3, + 0xc8, 0xe5, 0xaa, 0x1e, 0x96, 0xa6, 0x93, 0xff, + 0x9c, 0xce, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, 0x14, + 0x64, 0xfe, 0x72, 0xb6, 0x51, 0x50, 0x1d, 0x0f, + 0x4e, 0xfc, 0x4e, 0xd8, 0x77, 0xd0, 0x8d, 0xa9, + 0xc5, 0xd4, 0xa7, 0xfe, 0x77, 0x3d, 0xd8, 0x29, + 0xdc, 0x86, 0x88, 0xe6, 0x7f, 0xf3, 0x99, 0xcf, + 0x76, 0x0a, 0x77, 0x21, 0xa2, 0x72, 0x9f, 0xce, + 0xc1, 0x4e, 0xe4, 0x34, 0x59, 0x93, 0xff, 0x39, + 0x40, 0x2b, 0x7b, 0x75, 0x2f, 0x3a, 0x7f, 0x3b, + 0x05, 0x3b, 0x90, 0xd1, 0x6e, 0xcf, 0xfe, 0x73, + 0x39, 0xee, 0xc1, 0x4e, 0xe4, 0x34, 0x52, 0x13, + 0xff, 0x3b, 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, + 0xa5, 0x14, 0x27, 0xea, 0xd2, 0x63, 0x14, 0x80, + 0x77, 0xf4, 0x3b, 0x9d, 0xb7, 0xa9, 0x4f, 0xfb, + 0x9e, 0xec, 0x14, 0xee, 0x43, 0x44, 0xed, 0x3f, + 0xfe, 0xf6, 0xda, 0xb1, 0x5b, 0xfb, 0x6f, 0xaf, + 0xf1, 0x0e, 0x93, 0x98, 0x89, 0xfe, 0x46, 0x9f, + 0xfb, 0x99, 0x6c, 0x4e, 0xb2, 0xef, 0x79, 0xd3, + 0xff, 0x7f, 0x2d, 0xd4, 0x65, 0x7f, 0xd4, 0x9d, + 0x36, 0xe3, 0xb6, 0x44, 0x3d, 0x50, 0xe1, 0x88, + 0xe2, 0xdc, 0x85, 0x7c, 0xf8, 0x53, 0xb9, 0x0d, + 0x11, 0x64, 0xff, 0xb9, 0xee, 0xc1, 0x4e, 0xe4, + 0x34, 0x4b, 0xb3, 0xff, 0xf6, 0x6d, 0x65, 0xde, + 0xeb, 0xff, 0x81, 0x5a, 0xa8, 0x0a, 0x93, 0xb1, + 0x1a, 0x6c, 0x30, 0xdc, 0x46, 0x9f, 0xfc, 0xe6, + 0x73, 0xdd, 0x82, 0x9d, 0xc8, 0x68, 0x98, 0xa7, + 0xec, 0x14, 0xee, 0x43, 0x45, 0x53, 0x3f, 0xff, + 0xf8, 0x7a, 0xd4, 0xbf, 0x2e, 0xeb, 0x75, 0x47, + 0xc1, 0xbd, 0xbc, 0x2f, 0x3a, 0x1c, 0x8a, 0xce, + 0x35, 0x9f, 0xfc, 0xe6, 0x73, 0xdd, 0x82, 0x9d, + 0xc8, 0x68, 0x9d, 0x27, 0xf7, 0xb8, 0x55, 0xfc, + 0xd8, 0xe9, 0xf3, 0x4a, 0xd2, 0xd4, 0xe9, 0xf8, + 0x39, 0xe6, 0xd9, 0xf3, 0xa7, 0xbd, 0x65, 0x77, + 0x8f, 0x5a, 0xa5, 0x13, 0xff, 0x6b, 0x5b, 0xad, + 0x2e, 0xd5, 0xaf, 0x30, 0xe8, 0x62, 0x20, 0xac, + 0x73, 0x3f, 0xee, 0x7b, 0xb0, 0x53, 0xb9, 0x0d, + 0x13, 0xbc, 0xfa, 0xfa, 0xf3, 0x3c, 0x54, 0x9d, + 0xb2, 0x73, 0x79, 0x18, 0x53, 0x08, 0xf9, 0x1a, + 0x7f, 0xf3, 0x99, 0xcf, 0x76, 0x0a, 0x77, 0x21, + 0xa2, 0x85, 0x9f, 0xfc, 0xe6, 0x73, 0xdd, 0x82, + 0x9d, 0xc8, 0x68, 0xa4, 0xa7, 0xff, 0xf6, 0x55, + 0xdb, 0xf1, 0xab, 0x73, 0x2f, 0x65, 0xbe, 0xac, + 0x3a, 0x28, 0x5c, 0x96, 0x79, 0xc2, 0x43, 0xf8, + 0x48, 0x99, 0x1f, 0x68, 0x12, 0x75, 0x52, 0xdc, + 0x52, 0x9f, 0xce, 0xc1, 0x4e, 0xe4, 0x34, 0x44, + 0x93, 0xff, 0x9c, 0xce, 0x7b, 0xb0, 0x53, 0xb9, + 0x0d, 0x12, 0xf4, 0xf8, 0x53, 0xb9, 0x0d, 0x14, + 0xbc, 0xff, 0xec, 0x7a, 0x82, 0xa2, 0xb4, 0xbf, + 0xcc, 0x3a, 0x7c, 0xdf, 0xe6, 0x2d, 0x4e, 0x9f, + 0x73, 0x6c, 0x1a, 0x9d, 0x25, 0xd8, 0xf4, 0x58, + 0x55, 0x27, 0x62, 0x3c, 0x9a, 0x30, 0xbc, 0x26, + 0xa7, 0xf3, 0xb0, 0x53, 0xb9, 0x0d, 0x14, 0xe4, + 0xff, 0xbc, 0x3d, 0x5b, 0x66, 0x7d, 0x0e, 0x9f, + 0xf6, 0x59, 0x47, 0x1c, 0x10, 0x84, 0xa9, 0xbc, + 0x13, 0xa6, 0xa1, 0xdb, 0x22, 0x37, 0x70, 0xed, + 0xbc, 0xf2, 0x7c, 0x29, 0xdc, 0x86, 0x8a, 0xf2, + 0x7f, 0xfe, 0xcd, 0xac, 0xbb, 0xdd, 0x7f, 0xf0, + 0x2b, 0x55, 0x01, 0x52, 0x76, 0x22, 0x37, 0x70, + 0xc2, 0x7f, 0xe7, 0x73, 0xdd, 0x82, 0x9d, 0xc8, + 0x68, 0x91, 0xe7, 0x78, 0x0a, 0x74, 0xe4, 0xc6, + 0x14, 0xe2, 0xee, 0x7c, 0x29, 0xdc, 0x86, 0x89, + 0x22, 0x79, 0xdc, 0xf7, 0x29, 0xec, 0xe1, 0x4c, + 0xff, 0xce, 0xe7, 0xbb, 0x05, 0x3b, 0x90, 0xd1, + 0x25, 0x4f, 0x85, 0x3b, 0x90, 0xd1, 0x78, 0xcf, + 0xda, 0xfe, 0x9e, 0xa6, 0xa7, 0x4f, 0xab, 0x5b, + 0xe2, 0x1d, 0x3f, 0xcf, 0x76, 0x0a, 0x77, 0x21, + 0xa2, 0x4d, 0x93, 0xb1, 0x18, 0xf4, 0x98, 0x09, + 0x87, 0x13, 0x42, 0xae, 0xe4, 0xd0, 0x76, 0xf8, + 0xd1, 0x36, 0x3b, 0xf4, 0x32, 0x1a, 0x86, 0x8b, + 0x0a, 0xed, 0x0c, 0x0b, 0x9b, 0xeb, 0x18, 0x9c, + 0x2c, 0x6e, 0xd9, 0x14, 0x47, 0x5c, 0xf8, 0xf2, + 0x76, 0x94, 0x35, 0x96, 0xeb, 0x5b, 0x4c, 0xa5, + 0x84, 0xaf, 0xf7, 0xa9, 0x9e, 0x7b, 0xf4, 0xe7, + 0xa6, 0xee, 0x3b, 0x06, 0xa9, 0xd8, 0x63, 0x48, + 0xf5, 0x66, 0x23, 0x3e, 0xed, 0x3b, 0x51, 0xd4, + 0xf7, 0x20, 0x4b, 0x0e, 0x6e, 0xe1, 0x63, 0xf9, + 0xec, 0x6a, 0xd2, 0x57, 0x6f, 0x5d, 0x0f, 0xeb, + 0x4c, 0x16, 0x6d, 0x29, 0x88, 0x32, 0x94, 0x37, + 0x23, 0xeb, 0xdf, 0x3b, 0x28, 0xdf, 0x3c, 0x97, + 0x00, }; -static const unsigned kPreloadedHSTSBits = 94538; +static const unsigned kPreloadedHSTSBits = 97858; -static const unsigned kHSTSRootPosition = 93961; +static const unsigned kHSTSRootPosition = 97280; #endif // NET_HTTP_TRANSPORT_SECURITY_STATE_STATIC_H_
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index 2517d20..39ca582 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json
@@ -1395,7 +1395,7 @@ // Facebook would like to have pinning enforced on (*.)facebook.com and // HSTS enforced on specific names. We can't (yet) represent that in JSON // So we're currently only applying pinning on the specific names. - { "name": "facebook.com", "mode": "force-https", "pins": "facebook" }, + { "name": "facebook.com", "mode": "force-https", "pins": "facebook", "include_subdomains_for_pinning": true }, { "name": "www.facebook.com", "include_subdomains": true, "mode": "force-https", "pins": "facebook" }, { "name": "m.facebook.com", "include_subdomains": true, "mode": "force-https", "pins": "facebook" }, { "name": "tablet.facebook.com", "include_subdomains": true, "mode": "force-https", "pins": "facebook" }, @@ -1472,7 +1472,50 @@ { "name": "www.etsy.com", "include_subdomains": true, "mode": "force-https" }, { "name": "etsysecure.com", "include_subdomains": true, "mode": "force-https" }, { "name": "18f.gsa.gov", "include_subdomains": true, "mode": "force-https" }, - { "name": "my.usa.gov", "include_subdomains": true, "mode": "force-https" } + { "name": "my.usa.gov", "include_subdomains": true, "mode": "force-https" }, + { "name": "alexyang.me", "include_subdomains": true, "mode": "force-https" }, + { "name": "beamitapp.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "bonigo.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "certly.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "chontalpa.pw", "include_subdomains": true, "mode": "force-https" }, + { "name": "cktennis.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "clintwilson.technology", "include_subdomains": true, "mode": "force-https" }, + { "name": "cspbuilder.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "depechemode-live.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "destinationbijoux.fr", "include_subdomains": true, "mode": "force-https" }, + { "name": "dinamoelektrik.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ethack.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "fabianfischer.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "fastcomcorp.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "gizzo.sk", "include_subdomains": true, "mode": "force-https" }, + { "name": "inkbunny.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "itsamurai.ru", "include_subdomains": true, "mode": "force-https" }, + { "name": "itshost.ru", "include_subdomains": true, "mode": "force-https" }, + { "name": "jmedved.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "jwilsson.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "keepclean.me", "include_subdomains": true, "mode": "force-https" }, + { "name": "klausbrinch.dk", "include_subdomains": true, "mode": "force-https" }, + { "name": "leonardcamacho.me", "include_subdomains": true, "mode": "force-https" }, + { "name": "mdfnet.se", "include_subdomains": true, "mode": "force-https" }, + { "name": "michalspacek.cz", "include_subdomains": true, "mode": "force-https" }, + { "name": "mike-bland.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "mocloud.eu", "include_subdomains": true, "mode": "force-https" }, + { "name": "oakslighting.co.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "onsitemassageco.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "propagandism.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "slevomat.cz", "include_subdomains": true, "mode": "force-https" }, + { "name": "sour.is", "include_subdomains": true, "mode": "force-https" }, + { "name": "spongepowered.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "staticanime.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "sunjaydhama.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "swehack.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "thusoy.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "tls.li", "include_subdomains": true, "mode": "force-https" }, + { "name": "vhost.co.id", "include_subdomains": true, "mode": "force-https" }, + { "name": "webandwords.com.au", "include_subdomains": true, "mode": "force-https" }, + { "name": "webtiles.co.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "yoursecondphone.co", "include_subdomains": true, "mode": "force-https" }, + { "name": "zlavomat.sk", "include_subdomains": true, "mode": "force-https" } ], // |ReportUMAOnPinFailure| uses these to report which domain was associated
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc index eb72050..cf5f243 100644 --- a/net/http/transport_security_state_unittest.cc +++ b/net/http/transport_security_state_unittest.cc
@@ -299,7 +299,6 @@ EXPECT_TRUE(HasStaticState("paypal.com")); EXPECT_FALSE(HasStaticState("www2.paypal.com")); - EXPECT_FALSE(HasStaticState("www2.paypal.com")); // Google hosts: @@ -535,6 +534,24 @@ EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); + + // Check that Facebook subdomains have pinning but not HSTS. + EXPECT_TRUE(state.GetStaticDomainState("facebook.com", &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE(StaticShouldRedirect("facebook.com")); + + EXPECT_TRUE(state.GetStaticDomainState("foo.facebook.com", &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_FALSE(StaticShouldRedirect("foo.facebook.com")); + + EXPECT_TRUE(state.GetStaticDomainState("www.facebook.com", &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE(StaticShouldRedirect("www.facebook.com")); + + EXPECT_TRUE( + state.GetStaticDomainState("foo.www.facebook.com", &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE(StaticShouldRedirect("foo.www.facebook.com")); } TEST_F(TransportSecurityStateTest, LongNames) {
diff --git a/net/http/url_security_manager_win.cc b/net/http/url_security_manager_win.cc index db5620f..5576366 100644 --- a/net/http/url_security_manager_win.cc +++ b/net/http/url_security_manager_win.cc
@@ -111,11 +111,11 @@ } bool URLSecurityManagerWin::EnsureSystemSecurityManager() { - if (!security_manager_) { + if (!security_manager_.get()) { HRESULT hr = CoInternetCreateSecurityManager(NULL, security_manager_.Receive(), NULL); - if (FAILED(hr) || !security_manager_) { + if (FAILED(hr) || !security_manager_.get()) { LOG(ERROR) << "Unable to create the Windows Security Manager instance"; return false; }
diff --git a/net/net.gyp b/net/net.gyp index 7b4b5d6..e9379e0 100644 --- a/net/net.gyp +++ b/net/net.gyp
@@ -376,13 +376,8 @@ }], [ 'enable_websockets != 1', { 'sources/': [ - ['exclude', '^socket_stream/'], ['exclude', '^websockets/'], ], - 'sources!': [ - 'spdy/spdy_websocket_stream.cc', - 'spdy/spdy_websocket_stream.h', - ], }], [ 'enable_mdns != 1', { 'sources!' : [ @@ -654,9 +649,7 @@ }], [ 'enable_websockets != 1', { 'sources/': [ - ['exclude', '^socket_stream/'], ['exclude', '^websockets/'], - ['exclude', '^spdy/spdy_websocket_stream_unittest\\.cc$'], ], }], ['disable_file_support==1', { @@ -1654,23 +1647,35 @@ 'net_javatests', 'net_unittests', ], + 'conditions': [ + ['v8_use_external_startup_data==1', { + 'dependencies': [ + '../v8/tools/gyp/v8.gyp:v8_external_snapshot', + ], + 'copies': [ + { + 'destination': '<(asset_location)', + 'files': [ + '<(PRODUCT_DIR)/natives_blob.bin', + '<(PRODUCT_DIR)/snapshot_blob.bin', + ], + }, + ], + }], + ], 'variables': { 'test_suite_name': 'net_unittests', 'conditions': [ ['v8_use_external_startup_data==1', { + 'asset_location': '<(PRODUCT_DIR)/net_unittests_apk/assets', 'additional_input_paths': [ + '<(PRODUCT_DIR)/net_unittests_apk/assets/natives_blob.bin', + '<(PRODUCT_DIR)/net_unittests_apk/assets/snapshot_blob.bin', + ], + 'inputs': [ '<(PRODUCT_DIR)/natives_blob.bin', '<(PRODUCT_DIR)/snapshot_blob.bin', ], - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)/net_unittests_apk/assets', - 'files': [ - '<(PRODUCT_DIR)/natives_blob.bin', - '<(PRODUCT_DIR)/snapshot_blob.bin', - ], - }, - ], }], ], },
diff --git a/net/net.gypi b/net/net.gypi index f43c67f..fef6136 100644 --- a/net/net.gypi +++ b/net/net.gypi
@@ -1016,14 +1016,6 @@ 'socket/websocket_transport_client_socket_pool.h', 'socket/websocket_transport_connect_sub_job.cc', 'socket/websocket_transport_connect_sub_job.h', - 'socket_stream/socket_stream.cc', - 'socket_stream/socket_stream.h', - 'socket_stream/socket_stream_job.cc', - 'socket_stream/socket_stream_job.h', - 'socket_stream/socket_stream_job_manager.cc', - 'socket_stream/socket_stream_job_manager.h', - 'socket_stream/socket_stream_metrics.cc', - 'socket_stream/socket_stream_metrics.h', 'spdy/buffered_spdy_framer.cc', 'spdy/buffered_spdy_framer.h', 'spdy/fuzzing/hpack_fuzz_util.cc', @@ -1089,8 +1081,6 @@ 'spdy/spdy_session_pool.h', 'spdy/spdy_stream.cc', 'spdy/spdy_stream.h', - 'spdy/spdy_websocket_stream.cc', - 'spdy/spdy_websocket_stream.h', 'spdy/spdy_write_queue.cc', 'spdy/spdy_write_queue.h', 'spdy/write_blocked_list.h', @@ -1249,15 +1239,9 @@ 'websockets/websocket_handshake_stream_create_helper.h', 'websockets/websocket_inflater.cc', 'websockets/websocket_inflater.h', - 'websockets/websocket_job.cc', - 'websockets/websocket_job.h', 'websockets/websocket_mux.h', - 'websockets/websocket_net_log_params.cc', - 'websockets/websocket_net_log_params.h', 'websockets/websocket_stream.cc', 'websockets/websocket_stream.h', - 'websockets/websocket_throttle.cc', - 'websockets/websocket_throttle.h', ], 'net_extras_sources': [ 'extras/sqlite/sqlite_channel_id_store.cc', @@ -1613,8 +1597,6 @@ 'socket/unix_domain_server_socket_posix_unittest.cc', 'socket/websocket_endpoint_lock_manager_unittest.cc', 'socket/websocket_transport_client_socket_pool_unittest.cc', - 'socket_stream/socket_stream_metrics_unittest.cc', - 'socket_stream/socket_stream_unittest.cc', 'spdy/buffered_spdy_framer_unittest.cc', 'spdy/fuzzing/hpack_fuzz_util_test.cc', 'spdy/hpack_decoder_test.cc', @@ -1657,9 +1639,6 @@ 'spdy/spdy_test_util_common.h', 'spdy/spdy_test_utils.cc', 'spdy/spdy_test_utils.h', - 'spdy/spdy_websocket_stream_unittest.cc', - 'spdy/spdy_websocket_test_util.cc', - 'spdy/spdy_websocket_test_util.h', 'spdy/spdy_write_queue_unittest.cc', 'spdy/write_blocked_list_test.cc', 'ssl/channel_id_service_unittest.cc', @@ -1722,16 +1701,12 @@ 'websockets/websocket_extension_parser_test.cc', 'websockets/websocket_frame_parser_test.cc', 'websockets/websocket_frame_test.cc', - 'websockets/websocket_handshake_handler_spdy_test.cc', 'websockets/websocket_handshake_handler_test.cc', 'websockets/websocket_handshake_stream_create_helper_test.cc', 'websockets/websocket_inflater_test.cc', - 'websockets/websocket_job_test.cc', - 'websockets/websocket_net_log_params_test.cc', 'websockets/websocket_stream_test.cc', 'websockets/websocket_test_util.cc', 'websockets/websocket_test_util.h', - 'websockets/websocket_throttle_test.cc', ], 'net_linux_test_sources': [ 'quic/quic_end_to_end_unittest.cc',
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc b/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc index 8e19001..dc0d1b4 100644 --- a/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc +++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc
@@ -86,7 +86,7 @@ dhcp_query_ = new DelayingDhcpQuery(); dhcp_query_->dhcp_delay_ = dhcp_delay_; dhcp_query_->configured_url_ = configured_url_; - return dhcp_query_; + return dhcp_query_.get(); } // Use a shorter timeout so tests can finish more quickly. @@ -116,7 +116,7 @@ } void FinishTest() { - DCHECK(dhcp_query_); + DCHECK(dhcp_query_.get()); dhcp_query_->test_finished_event_.Signal(); }
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc b/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc index eb98330..c3368ac 100644 --- a/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc +++ b/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc
@@ -330,7 +330,7 @@ } virtual AdapterQuery* ImplCreateAdapterQuery() override { - DCHECK(adapter_query_); + DCHECK(adapter_query_.get()); return adapter_query_.get(); }
diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc index 8b6dfb7..4828c3a 100644 --- a/net/quic/crypto/quic_crypto_client_config.cc +++ b/net/quic/crypto/quic_crypto_client_config.cc
@@ -32,25 +32,21 @@ namespace { -enum ServerConfigState { - // WARNING: Do not change the numerical values of any of server config state. - // Do not remove deprecated server config states - just comment them as - // deprecated. - SERVER_CONFIG_EMPTY = 0, - SERVER_CONFIG_INVALID = 1, - SERVER_CONFIG_CORRUPTED = 2, - SERVER_CONFIG_EXPIRED = 3, - SERVER_CONFIG_INVALID_EXPIRY = 4, +// Tracks the reason (the state of the server config) for sending inchoate +// ClientHello to the server. +void RecordInchoateClientHelloReason( + QuicCryptoClientConfig::CachedState::ServerConfigState state) { + UMA_HISTOGRAM_ENUMERATION( + "Net.QuicInchoateClientHelloReason", state, + QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT); +} - // NOTE: Add new server config states only immediately above this line. Make - // sure to update the QuicServerConfigState enum in - // tools/metrics/histograms/histograms.xml accordingly. - SERVER_CONFIG_COUNT -}; - -void RecordServerConfigState(ServerConfigState server_config_state) { - UMA_HISTOGRAM_ENUMERATION("Net.QuicClientHelloServerConfigState", - server_config_state, SERVER_CONFIG_COUNT); +// Tracks the state of the QUIC server information loaded from the disk cache. +void RecordDiskCacheServerConfigState( + QuicCryptoClientConfig::CachedState::ServerConfigState state) { + UMA_HISTOGRAM_ENUMERATION( + "Net.QuicServerInfo.DiskCacheState", state, + QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT); } } // namespace @@ -72,12 +68,12 @@ bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { if (server_config_.empty()) { - RecordServerConfigState(SERVER_CONFIG_EMPTY); + RecordInchoateClientHelloReason(SERVER_CONFIG_EMPTY); return false; } if (!server_config_valid_) { - RecordServerConfigState(SERVER_CONFIG_INVALID); + RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID); return false; } @@ -85,13 +81,13 @@ if (!scfg) { // Should be impossible short of cache corruption. DCHECK(false); - RecordServerConfigState(SERVER_CONFIG_CORRUPTED); + RecordInchoateClientHelloReason(SERVER_CONFIG_CORRUPTED); return false; } uint64 expiry_seconds; if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { - RecordServerConfigState(SERVER_CONFIG_INVALID_EXPIRY); + RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID_EXPIRY); return false; } if (now.ToUNIXSeconds() >= expiry_seconds) { @@ -99,7 +95,7 @@ "Net.QuicClientHelloServerConfig.InvalidDuration", base::TimeDelta::FromSeconds(now.ToUNIXSeconds() - expiry_seconds), base::TimeDelta::FromMinutes(1), base::TimeDelta::FromDays(20), 50); - RecordServerConfigState(SERVER_CONFIG_EXPIRED); + RecordInchoateClientHelloReason(SERVER_CONFIG_EXPIRED); return false; } @@ -123,7 +119,8 @@ return scfg_.get(); } -QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig( +QuicCryptoClientConfig::CachedState::ServerConfigState +QuicCryptoClientConfig::CachedState::SetServerConfig( StringPiece server_config, QuicWallTime now, string* error_details) { const bool matches_existing = server_config == server_config_; @@ -141,18 +138,18 @@ if (!new_scfg) { *error_details = "SCFG invalid"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + return SERVER_CONFIG_INVALID; } uint64 expiry_seconds; if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { *error_details = "SCFG missing EXPY"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + return SERVER_CONFIG_INVALID_EXPIRY; } if (now.ToUNIXSeconds() >= expiry_seconds) { *error_details = "SCFG has expired"; - return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; + return SERVER_CONFIG_EXPIRED; } if (!matches_existing) { @@ -160,7 +157,7 @@ SetProofInvalid(); scfg_.reset(new_scfg_storage.release()); } - return QUIC_NO_ERROR; + return SERVER_CONFIG_VALID; } void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { @@ -228,13 +225,15 @@ DCHECK(server_config_.empty()); if (server_config.empty()) { + RecordDiskCacheServerConfigState(SERVER_CONFIG_EMPTY); return false; } string error_details; - QuicErrorCode error = SetServerConfig(server_config, now, - &error_details); - if (error != QUIC_NO_ERROR) { + ServerConfigState state = SetServerConfig(server_config, now, + &error_details); + RecordDiskCacheServerConfigState(state); + if (state != SERVER_CONFIG_VALID) { DVLOG(1) << "SetServerConfig failed with " << error_details; return false; } @@ -325,7 +324,10 @@ CachedState* cached = new CachedState; cached_states_.insert(make_pair(server_id, cached)); - PopulateFromCanonicalConfig(server_id, cached); + bool cache_populated = PopulateFromCanonicalConfig(server_id, cached); + UMA_HISTOGRAM_BOOLEAN( + "Net.QuicCryptoClientConfig.PopulatedFromCanonicalConfig", + cache_populated); return cached; } @@ -591,9 +593,15 @@ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; } - QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details); - if (error != QUIC_NO_ERROR) { - return error; + CachedState::ServerConfigState state = cached->SetServerConfig( + scfg, now, error_details); + if (state == CachedState::SERVER_CONFIG_EXPIRED) { + return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; + } + // TODO(rtenneti): Return more specific error code than returning + // QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER. + if (state != CachedState::SERVER_CONFIG_VALID) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } StringPiece token; @@ -829,7 +837,7 @@ disable_ecdsa_ = true; } -void QuicCryptoClientConfig::PopulateFromCanonicalConfig( +bool QuicCryptoClientConfig::PopulateFromCanonicalConfig( const QuicServerId& server_id, CachedState* server_state) { DCHECK(server_state->IsEmpty()); @@ -840,7 +848,7 @@ } } if (i == canonical_suffixes_.size()) - return; + return false; QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(), server_id.is_https(), @@ -849,20 +857,21 @@ // This is the first host we've seen which matches the suffix, so make it // canonical. canonical_server_map_[suffix_server_id] = server_id; - return; + return false; } const QuicServerId& canonical_server_id = canonical_server_map_[suffix_server_id]; CachedState* canonical_state = cached_states_[canonical_server_id]; if (!canonical_state->proof_valid()) { - return; + return false; } // Update canonical version to point at the "most recent" entry. canonical_server_map_[suffix_server_id] = server_id; server_state->InitializeFrom(*canonical_state); + return true; } } // namespace net
diff --git a/net/quic/crypto/quic_crypto_client_config.h b/net/quic/crypto/quic_crypto_client_config.h index 26fde24..4bfed1b 100644 --- a/net/quic/crypto/quic_crypto_client_config.h +++ b/net/quic/crypto/quic_crypto_client_config.h
@@ -35,6 +35,24 @@ // over several connections to the same server. class NET_EXPORT_PRIVATE CachedState { public: + // Enum to track if the server config is valid or not. If it is not valid, + // it specifies why it is invalid. + enum ServerConfigState { + // WARNING: Do not change the numerical values of any of server config + // state. Do not remove deprecated server config states - just comment + // them as deprecated. + SERVER_CONFIG_EMPTY = 0, + SERVER_CONFIG_INVALID = 1, + SERVER_CONFIG_CORRUPTED = 2, + SERVER_CONFIG_EXPIRED = 3, + SERVER_CONFIG_INVALID_EXPIRY = 4, + SERVER_CONFIG_VALID = 5, + // NOTE: Add new server config states only immediately above this line. + // Make sure to update the QuicServerConfigState enum in + // tools/metrics/histograms/histograms.xml accordingly. + SERVER_CONFIG_COUNT + }; + CachedState(); ~CachedState(); @@ -54,9 +72,9 @@ // SetServerConfig checks that |server_config| parses correctly and stores // it in |server_config_|. |now| is used to judge whether |server_config| // has expired. - QuicErrorCode SetServerConfig(base::StringPiece server_config, - QuicWallTime now, - std::string* error_details); + ServerConfigState SetServerConfig(base::StringPiece server_config, + QuicWallTime now, + std::string* error_details); // InvalidateServerConfig clears the cached server config (if any). void InvalidateServerConfig(); @@ -282,8 +300,9 @@ // If the suffix of the hostname in |server_id| is in |canonical_suffixes_|, // then populate |cached| with the canonical cached state from - // |canonical_server_map_| for that suffix. - void PopulateFromCanonicalConfig(const QuicServerId& server_id, + // |canonical_server_map_| for that suffix. Returns true if |cached| is + // initialized with canonical cached state. + bool PopulateFromCanonicalConfig(const QuicServerId& server_id, CachedState* cached); // cached_states_ maps from the server_id to the cached information about
diff --git a/net/quic/crypto/quic_server_info.h b/net/quic/crypto/quic_server_info.h index 2a5c6fc..c6525c8 100644 --- a/net/quic/crypto/quic_server_info.h +++ b/net/quic/crypto/quic_server_info.h
@@ -64,6 +64,9 @@ // callback. virtual void Persist() = 0; + // Called whenever an external cache reuses quic server config. + virtual void OnExternalCacheHit() = 0; + struct State { State(); ~State();
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index 7c220ad..73a5aec 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc
@@ -590,6 +590,8 @@ ++it; observer->OnCryptoHandshakeConfirmed(); } + if (server_info_) + server_info_->OnExternalCacheHit(); } QuicSession::OnCryptoHandshakeEvent(event); } @@ -682,7 +684,7 @@ const QuicCryptoClientConfig::CachedState& cached) { DCHECK(cached.proof_valid()); - if (!server_info_ || !server_info_->IsReadyToPersist()) { + if (!server_info_) { return; }
diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc index b99e9d2..10a02a7 100644 --- a/net/quic/quic_headers_stream.cc +++ b/net/quic/quic_headers_stream.cc
@@ -115,7 +115,11 @@ CloseConnection("SPDY GOAWAY frame received."); } - void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) override { + void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, + bool fin, + bool end) override { CloseConnection("SPDY HEADERS frame received."); }
diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc index 32d22fe..8649a3e 100644 --- a/net/quic/quic_headers_stream_test.cc +++ b/net/quic/quic_headers_stream_test.cc
@@ -52,7 +52,8 @@ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status)); - MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end)); + MOCK_METHOD5(OnHeaders, void(SpdyStreamId stream_id, bool has_priority, + SpdyPriority priority, bool fin, bool end)); MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, uint32 delta_window_size)); MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data,
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index 62fe895..ab926d5 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc
@@ -214,7 +214,7 @@ if (use_next_protos) { params_.use_alternate_protocols = true; - params_.next_protos = NextProtosSpdy3(); + params_.next_protos = NextProtosWithSpdyAndQuic(true, true); } session_ = new HttpNetworkSession(params_);
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index afd5ed4..ad920d0 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h
@@ -76,6 +76,9 @@ // Smaller values are ignored. const QuicByteCount kMinSocketReceiveBuffer = 16 * 1024; +// Don't allow a client to suggest an RTT shorter than 10ms. +const uint32 kMinInitialRoundTripTimeUs = 10 * kNumMicrosPerMilli; + // Don't allow a client to suggest an RTT longer than 15 seconds. const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc index bdf8690..9e2d04f 100644 --- a/net/quic/quic_sent_packet_manager.cc +++ b/net/quic/quic_sent_packet_manager.cc
@@ -97,12 +97,16 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { if (config.HasReceivedInitialRoundTripTimeUs() && config.ReceivedInitialRoundTripTimeUs() > 0) { - rtt_stats_.set_initial_rtt_us(min(kMaxInitialRoundTripTimeUs, - config.ReceivedInitialRoundTripTimeUs())); - } else if (config.HasInitialRoundTripTimeUsToSend()) { rtt_stats_.set_initial_rtt_us( - min(kMaxInitialRoundTripTimeUs, - config.GetInitialRoundTripTimeUsToSend())); + max(kMinInitialRoundTripTimeUs, + min(kMaxInitialRoundTripTimeUs, + config.ReceivedInitialRoundTripTimeUs()))); + } else if (config.HasInitialRoundTripTimeUsToSend() && + config.GetInitialRoundTripTimeUsToSend() > 0) { + rtt_stats_.set_initial_rtt_us( + max(kMinInitialRoundTripTimeUs, + min(kMaxInitialRoundTripTimeUs, + config.GetInitialRoundTripTimeUsToSend()))); } // TODO(ianswett): BBR is currently a server only feature. if (FLAGS_quic_allow_bbr &&
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index cf70608..076797a 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc
@@ -944,6 +944,14 @@ } } + if (quic_server_info_factory_ && !server_info) { + // Start the disk cache loading so that we can persist the newer QUIC server + // information and/or inform the disk cache that we have reused + // |server_info|. + server_info.reset(quic_server_info_factory_->GetForServer(server_id)); + server_info->Start(); + } + *session = new QuicClientSession( connection, socket.Pass(), this, transport_security_state_, server_info.Pass(), config, server_id.is_https(), @@ -985,6 +993,8 @@ void QuicStreamFactory::InitializeCachedStateInCryptoConfig( const QuicServerId& server_id, const scoped_ptr<QuicServerInfo>& server_info) { + // |server_info| will be NULL, if a non-empty server config already exists in + // the memory cache. This is a minor optimization to avoid LookupOrCreate. if (!server_info) return; @@ -993,6 +1003,27 @@ if (!cached->IsEmpty()) return; + if (http_server_properties_) { + if (quic_supported_servers_at_startup_.empty()) { + for (const std::pair<net::HostPortPair, net::AlternateProtocolInfo>& + key_value : http_server_properties_->alternate_protocol_map()) { + if (key_value.second.protocol == QUIC) { + quic_supported_servers_at_startup_.insert(key_value.first); + } + } + } + + // TODO(rtenneti): Delete the following histogram after collecting stats. + // If the AlternateProtocolMap contained an entry for this host, check if + // the disk cache contained an entry for it. + if (ContainsKey(quic_supported_servers_at_startup_, + server_id.host_port_pair())) { + UMA_HISTOGRAM_BOOLEAN( + "Net.QuicServerInfo.ExpectConfigMissingFromDiskCache", + server_info->state().server_config.empty()); + } + } + if (!cached->Initialize(server_info->state().server_config, server_info->state().source_address_token, server_info->state().certs,
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h index c892cb2..2fad1fc 100644 --- a/net/quic/quic_stream_factory.h +++ b/net/quic/quic_stream_factory.h
@@ -299,6 +299,7 @@ // Local address of socket that was created in CreateSession. IPEndPoint local_address_; bool check_persisted_supports_quic_; + std::set<HostPortPair> quic_supported_servers_at_startup_; base::TaskRunner* task_runner_;
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index 1165b9c..05234c0 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc
@@ -121,6 +121,8 @@ virtual bool IsReadyToPersist() override { return false; } virtual void Persist() override {}; + + virtual void OnExternalCacheHit() override {}; }; class MockQuicServerInfoFactory : public QuicServerInfoFactory {
diff --git a/net/quic/quic_time.h b/net/quic/quic_time.h index 5fbe30c..53e5ebe 100644 --- a/net/quic/quic_time.h +++ b/net/quic/quic_time.h
@@ -18,6 +18,8 @@ namespace net { static const uint64 kNumMicrosPerSecond = base::Time::kMicrosecondsPerSecond; +static const uint64 kNumMicrosPerMilli = + base::Time::kMicrosecondsPerMillisecond; // A QuicTime is a purely relative time. QuicTime values from different clocks // cannot be compared to each other. If you need an absolute time, see
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index d786a95..f144651 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc
@@ -7,12 +7,15 @@ #include <ctype.h> #include <algorithm> +#include <vector> #include "base/basictypes.h" +#include "base/containers/adapters.h" #include "base/logging.h" #include "base/port.h" #include "base/strings/stringprintf.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" #include "net/quic/quic_write_blocked_list.h" using base::StringPiece; @@ -280,6 +283,25 @@ } // static +QuicTagVector QuicUtils::ParseQuicConnectionOptions( + const std::string& connection_options) { + QuicTagVector options; + std::vector<std::string> tokens; + base::SplitString(connection_options, ',', &tokens); + // Tokens are expected to be no more than 4 characters long, but we + // handle overflow gracefully. + for (const std::string& token : tokens) { + uint32 option = 0; + for (char token_char : base::Reversed(token)) { + option <<= 8; + option |= static_cast<unsigned char>(token_char); + } + options.push_back(option); + } + return options; +} + +// static string QuicUtils::StringToHexASCIIDump(StringPiece in_buffer) { int offset = 0; const int kBytesPerLine = 16; // Max bytes dumped per line
diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h index 1ecb37d..2c34ec4 100644 --- a/net/quic/quic_utils.h +++ b/net/quic/quic_utils.h
@@ -7,6 +7,8 @@ #ifndef NET_QUIC_QUIC_UTILS_H_ #define NET_QUIC_QUIC_UTILS_H_ +#include <string> + #include "net/base/int128.h" #include "net/base/net_export.h" #include "net/quic/quic_protocol.h" @@ -68,6 +70,11 @@ // if not. static std::string TagToString(QuicTag tag); + // Returns the list of QUIC tags represented by the comma separated + // string in |connection_options|. + static QuicTagVector ParseQuicConnectionOptions( + const std::string& connection_options); + // Given a binary buffer, return a hex+ASCII dump in the style of // tcpdump's -X and -XX options: // "0x0000: 0090 69bd 5400 000d 610f 0189 0800 4500 ..i.T...a.....E.\n"
diff --git a/net/quic/quic_utils_test.cc b/net/quic/quic_utils_test.cc index b7d23ed..d2f2631 100644 --- a/net/quic/quic_utils_test.cc +++ b/net/quic/quic_utils_test.cc
@@ -84,6 +84,20 @@ QuicUtils::TagToString(MakeQuicTag('C', 'H', 'L', '\x1f'))); } +TEST(QuicUtilsTest, ParseQuicConnectionOptions) { + QuicTagVector empty_options = QuicUtils::ParseQuicConnectionOptions(""); + EXPECT_EQ(0ul, empty_options.size()); + + QuicTagVector parsed_options = QuicUtils::ParseQuicConnectionOptions( + "PACE,TIMER,TBBR,REJ"); + QuicTagVector expected_options; + expected_options.push_back(net::kPACE); + expected_options.push_back(net::kTIME); + expected_options.push_back(net::kTBBR); + expected_options.push_back(net::kREJ); + EXPECT_EQ(expected_options, parsed_options); +} + } // namespace } // namespace test } // namespace net
diff --git a/net/quic/test_tools/crypto_test_utils_openssl.cc b/net/quic/test_tools/crypto_test_utils_openssl.cc index 39b48a5..5bcbccb 100644 --- a/net/quic/test_tools/crypto_test_utils_openssl.cc +++ b/net/quic/test_tools/crypto_test_utils_openssl.cc
@@ -64,9 +64,10 @@ // The signature consists of a pair of 32-byte numbers. static const size_t kSignatureLength = 32 * 2; scoped_ptr<uint8[]> signature(new uint8[kSignatureLength]); - memset(signature.get(), 0, kSignatureLength); - BN_bn2bin(sig.get()->r, signature.get() + 32 - BN_num_bytes(sig.get()->r)); - BN_bn2bin(sig.get()->s, signature.get() + 64 - BN_num_bytes(sig.get()->s)); + if (!BN_bn2bin_padded(&signature[0], 32, sig->r) || + !BN_bn2bin_padded(&signature[32], 32, sig->s)) { + return false; + } *out_signature = string(reinterpret_cast<char*>(signature.get()), kSignatureLength);
diff --git a/net/socket/next_proto.cc b/net/socket/next_proto.cc index 4ae4511..0e59ce7 100644 --- a/net/socket/next_proto.cc +++ b/net/socket/next_proto.cc
@@ -31,13 +31,6 @@ return next_protos; } -NextProtoVector NextProtosSpdy3() { - NextProtoVector next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - return next_protos; -} - NextProtoVector NextProtosSpdy31() { NextProtoVector next_protos; next_protos.push_back(kProtoHTTP11); @@ -46,21 +39,13 @@ return next_protos; } -NextProtoVector NextProtosSpdy31WithSpdy2() { - NextProtoVector next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - next_protos.push_back(kProtoDeprecatedSPDY2); - next_protos.push_back(kProtoSPDY31); - return next_protos; -} - NextProtoVector NextProtosSpdy4Http2() { NextProtoVector next_protos; next_protos.push_back(kProtoHTTP11); next_protos.push_back(kProtoQUIC1SPDY3); next_protos.push_back(kProtoSPDY31); - next_protos.push_back(kProtoSPDY4); + next_protos.push_back(kProtoSPDY4_14); + next_protos.push_back(kProtoSPDY4_15); return next_protos; }
diff --git a/net/socket/next_proto.h b/net/socket/next_proto.h index 465001d..c12a45b 100644 --- a/net/socket/next_proto.h +++ b/net/socket/next_proto.h
@@ -25,11 +25,14 @@ kProtoDeprecatedSPDY2 = 100, kProtoSPDYMinimumVersion = kProtoDeprecatedSPDY2, + kProtoSPDYHistogramOffset = kProtoDeprecatedSPDY2, kProtoSPDY3 = 101, kProtoSPDY31 = 102, - // HTTP/2 draft-14 was 103, - kProtoSPDY4 = 104, // SPDY4 is HTTP/2 draft-15. - kProtoSPDYMaximumVersion = kProtoSPDY4, + kProtoSPDY4_14 = 103, // HTTP/2 draft-14 + kProtoSPDY4MinimumVersion = kProtoSPDY4_14, + kProtoSPDY4_15 = 104, // HTTP/2 draft-15 + kProtoSPDY4MaximumVersion = kProtoSPDY4_15, + kProtoSPDYMaximumVersion = kProtoSPDY4MaximumVersion, kProtoQUIC1SPDY3 = 200, @@ -51,9 +54,7 @@ bool quic_enabled); // All of these also enable QUIC. -NET_EXPORT NextProtoVector NextProtosSpdy3(); NET_EXPORT NextProtoVector NextProtosSpdy31(); -NET_EXPORT NextProtoVector NextProtosSpdy31WithSpdy2(); NET_EXPORT NextProtoVector NextProtosSpdy4Http2(); } // namespace net
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc index 269ca7e..a52e6a3 100644 --- a/net/socket/ssl_client_socket.cc +++ b/net/socket/ssl_client_socket.cc
@@ -37,10 +37,13 @@ return kProtoSPDY3; } else if (proto_string == "spdy/3.1") { return kProtoSPDY31; + } else if (proto_string == "h2-14") { + // For internal consistency, HTTP/2 is named SPDY4 within Chromium. + // This is the HTTP/2 draft-14 identifier. + return kProtoSPDY4_14; } else if (proto_string == "h2-15") { - // This is the HTTP/2 draft-15 identifier. For internal - // consistency, HTTP/2 is named SPDY4 within Chromium. - return kProtoSPDY4; + // This is the HTTP/2 draft-15 identifier. + return kProtoSPDY4_15; } else if (proto_string == "quic/1+spdy/3") { return kProtoQUIC1SPDY3; } else { @@ -59,9 +62,12 @@ return "spdy/3"; case kProtoSPDY31: return "spdy/3.1"; - case kProtoSPDY4: - // This is the HTTP/2 draft-15 identifier. For internal - // consistency, HTTP/2 is named SPDY4 within Chromium. + case kProtoSPDY4_14: + // For internal consistency, HTTP/2 is named SPDY4 within Chromium. + // This is the HTTP/2 draft-14 identifier. + return "h2-14"; + case kProtoSPDY4_15: + // This is the HTTP/2 draft-15 identifier. return "h2-15"; case kProtoQUIC1SPDY3: return "quic/1+spdy/3";
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 31a9b8d..3e5bde0 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc
@@ -218,8 +218,6 @@ return GetCacheOCSPResponseFromSideChannelFunction() != NULL; } #else -// TODO(agl): Figure out if we can plumb the OCSP response into Mac's system -// certificate validation functions. bool IsOCSPStaplingSupported() { return false; } @@ -1292,7 +1290,7 @@ core->client_auth_cert_needed_ = !core->ssl_config_.send_client_cert; #if defined(OS_WIN) if (core->ssl_config_.send_client_cert) { - if (core->ssl_config_.client_cert) { + if (core->ssl_config_.client_cert.get()) { PCCERT_CONTEXT cert_context = core->ssl_config_.client_cert->os_cert_handle(); @@ -1630,8 +1628,8 @@ } void SSLClientSocketNSS::Core::HandshakeSucceeded() { - // TODO(vadimt): Remove ScopedProfile below once crbug.com/424386 is fixed. - tracked_objects::ScopedProfile tracking_profile( + // TODO(vadimt): Remove ScopedTracker below once crbug.com/424386 is fixed. + tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "424386 SSLClientSocketNSS::Core::HandshakeSucceeded")); @@ -1661,8 +1659,8 @@ } int SSLClientSocketNSS::Core::HandleNSSError(PRErrorCode nss_error) { - // TODO(vadimt): Remove ScopedProfile below once crbug.com/424386 is fixed. - tracked_objects::ScopedProfile tracking_profile( + // TODO(vadimt): Remove ScopedTracker below once crbug.com/424386 is fixed. + tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "424386 SSLClientSocketNSS::Core::HandleNSSError")); @@ -1687,7 +1685,7 @@ // re-insert the smart card if not. if ((net_error == ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY || net_error == ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED) && - ssl_config_.send_client_cert && ssl_config_.client_cert) { + ssl_config_.send_client_cert && ssl_config_.client_cert.get()) { CertSetCertificateContextProperty( ssl_config_.client_cert->os_cert_handle(), CERT_KEY_PROV_HANDLE_PROP_ID, 0, NULL); @@ -1813,8 +1811,8 @@ int net_error = OK; SECStatus rv = SSL_ForceHandshake(nss_fd_); - // TODO(vadimt): Remove ScopedProfile below once crbug.com/424386 is fixed. - tracked_objects::ScopedProfile tracking_profile1( + // TODO(vadimt): Remove ScopedTracker below once crbug.com/424386 is fixed. + tracked_objects::ScopedTracker tracking_profile1( FROM_HERE_WITH_EXPLICIT_FUNCTION( "424386 SSLClientSocketNSS::Core::DoHandshake 1")); @@ -2436,11 +2434,9 @@ reinterpret_cast<char*>(ocsp_responses->items[0].data), ocsp_responses->items[0].len); - // TODO(agl): figure out how to plumb an OCSP response into the Mac - // system library and update IsOCSPStaplingSupported for Mac. if (IsOCSPStaplingSupported()) { #if defined(OS_WIN) - if (nss_handshake_state_.server_cert) { + if (nss_handshake_state_.server_cert.get()) { CRYPT_DATA_BLOB ocsp_response_blob; ocsp_response_blob.cbData = ocsp_responses->items[0].len; ocsp_response_blob.pbData = ocsp_responses->items[0].data;
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index 9fdfe38..57aa619 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc
@@ -140,6 +140,19 @@ return 1; } +bool IsOCSPStaplingSupported() { +#if defined(OS_WIN) + // CERT_OCSP_RESPONSE_PROP_ID is only implemented on Vista+, but it can be + // set on Windows XP without error. There is some overhead from the server + // sending the OCSP response if it supports the extension, for the subset of + // XP clients who will request it but be unable to use it, but this is an + // acceptable trade-off for simplicity of implementation. + return true; +#else + return false; +#endif +} + } // namespace class SSLClientSocketOpenSSL::SSLContext { @@ -829,8 +842,8 @@ SSL_enable_ocsp_stapling(ssl_); } - // TODO(davidben): Enable OCSP stapling on platforms which support it and pass - // into the certificate verifier. https://crbug.com/398677 + if (IsOCSPStaplingSupported()) + SSL_enable_ocsp_stapling(ssl_); return OK; } @@ -933,10 +946,16 @@ ssl_config_.channel_id_enabled, crypto::ECPrivateKey::IsSupported()); - uint8_t* ocsp_response; - size_t ocsp_response_len; - SSL_get0_ocsp_response(ssl_, &ocsp_response, &ocsp_response_len); - set_stapled_ocsp_response_received(ocsp_response_len != 0); + // Only record OCSP histograms if OCSP was requested. + if (ssl_config_.signed_cert_timestamps_enabled || + IsOCSPStaplingSupported()) { + uint8_t* ocsp_response; + size_t ocsp_response_len; + SSL_get0_ocsp_response(ssl_, &ocsp_response, &ocsp_response_len); + + set_stapled_ocsp_response_received(ocsp_response_len != 0); + UMA_HISTOGRAM_BOOLEAN("Net.OCSPResponseStapled", ocsp_response_len != 0); + } uint8_t* sct_list; size_t sct_list_len; @@ -1166,6 +1185,31 @@ NetLog::TYPE_SSL_CERTIFICATES_RECEIVED, base::Bind(&NetLogX509CertificateCallback, base::Unretained(server_cert_.get()))); + + // TODO(rsleevi): Plumb an OCSP response into the Mac system library and + // update IsOCSPStaplingSupported for Mac. https://crbug.com/430714 + if (IsOCSPStaplingSupported()) { +#if defined(OS_WIN) + uint8_t* ocsp_response_raw; + size_t ocsp_response_len; + SSL_get0_ocsp_response(ssl_, &ocsp_response_raw, &ocsp_response_len); + + CRYPT_DATA_BLOB ocsp_response_blob; + ocsp_response_blob.cbData = ocsp_response_len; + ocsp_response_blob.pbData = ocsp_response_raw; + BOOL ok = CertSetCertificateContextProperty( + server_cert_->os_cert_handle(), + CERT_OCSP_RESPONSE_PROP_ID, + CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, + &ocsp_response_blob); + if (!ok) { + VLOG(1) << "Failed to set OCSP response property: " + << GetLastError(); + } +#else + NOTREACHED(); +#endif + } } } @@ -1567,6 +1611,8 @@ DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; DCHECK(ssl == ssl_); + net_log_.AddEvent(NetLog::TYPE_SSL_CLIENT_CERT_REQUESTED); + // Clear any currently configured certificates. SSL_certs_clear(ssl_); @@ -1644,11 +1690,17 @@ LOG(WARNING) << "Failed to set client certificate"; return -1; } + + int cert_count = 1 + sk_X509_num(chain.get()); + net_log_.AddEvent(NetLog::TYPE_SSL_CLIENT_CERT_PROVIDED, + NetLog::IntegerCallback("cert_count", cert_count)); return 1; } #endif // defined(OS_IOS) // Send no client certificate. + net_log_.AddEvent(NetLog::TYPE_SSL_CLIENT_CERT_PROVIDED, + NetLog::IntegerCallback("cert_count", 0)); return 1; }
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc index 202cd88..8ac29ef 100644 --- a/net/socket/ssl_client_socket_pool_unittest.cc +++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -240,7 +240,7 @@ NextProto, SSLClientSocketPoolTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); // Tests that the final socket will connect even if all sockets // prior to it fail.
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc index d5565ad..fd3c8b7 100644 --- a/net/socket/tcp_socket_win.cc +++ b/net/socket/tcp_socket_win.cc
@@ -324,7 +324,7 @@ const IPEndPoint& peer_address) { DCHECK(CalledOnValidThread()); DCHECK_EQ(socket_, INVALID_SOCKET); - DCHECK(!core_); + DCHECK(!core_.get()); socket_ = socket; @@ -436,7 +436,7 @@ // again after a connection attempt failed on Windows, it results in // unspecified behavior according to POSIX. Therefore, we make it behave in // the same way as TCPSocketLibevent. - DCHECK(!peer_address_ && !core_); + DCHECK(!peer_address_ && !core_.get()); if (!logging_multiple_connect_attempts_) LogConnectBegin(AddressList(address)); @@ -504,7 +504,7 @@ DCHECK_NE(socket_, INVALID_SOCKET); DCHECK(!waiting_read_); CHECK(read_callback_.is_null()); - DCHECK(!core_->read_iobuffer_); + DCHECK(!core_->read_iobuffer_.get()); return DoRead(buf, buf_len, callback); } @@ -517,7 +517,7 @@ DCHECK(!waiting_write_); CHECK(write_callback_.is_null()); DCHECK_GT(buf_len, 0); - DCHECK(!core_->write_iobuffer_); + DCHECK(!core_->write_iobuffer_.get()); base::StatsCounter writes("tcp.writes"); writes.Increment(); @@ -689,7 +689,7 @@ accept_event_ = WSA_INVALID_EVENT; } - if (core_) { + if (core_.get()) { if (waiting_connect_) { // We closed the socket, so this notification will never come. // From MSDN' WSAEventSelect documentation: @@ -796,7 +796,7 @@ int TCPSocketWin::DoConnect() { DCHECK_EQ(connect_os_error_, 0); - DCHECK(!core_); + DCHECK(!core_.get()); net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, CreateNetLogIPEndPointCallback(peer_address_.get())); @@ -1018,7 +1018,7 @@ // DoRead() because recv() reports a more accurate error code // (WSAECONNRESET vs. WSAECONNABORTED) when the connection was // reset. - rv = DoRead(core_->read_iobuffer_, core_->read_buffer_length_, + rv = DoRead(core_->read_iobuffer_.get(), core_->read_buffer_length_, read_callback_); if (rv == ERR_IO_PENDING) return;
diff --git a/net/socket_stream/OWNERS b/net/socket_stream/OWNERS deleted file mode 100644 index a2bac60..0000000 --- a/net/socket_stream/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -tyoshino@chromium.org
diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc deleted file mode 100644 index 422d61d..0000000 --- a/net/socket_stream/socket_stream.cc +++ /dev/null
@@ -1,1353 +0,0 @@ -// Copyright (c) 2012 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. -// -// TODO(ukai): code is similar with http_network_transaction.cc. We should -// think about ways to share code, if possible. - -#include "net/socket_stream/socket_stream.h" - -#include <set> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/auth.h" -#include "net/base/io_buffer.h" -#include "net/base/load_flags.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/dns/host_resolver.h" -#include "net/http/http_auth_controller.h" -#include "net/http/http_network_session.h" -#include "net/http/http_request_headers.h" -#include "net/http/http_request_info.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_stream_factory.h" -#include "net/http/http_transaction_factory.h" -#include "net/http/http_util.h" -#include "net/socket/client_socket_factory.h" -#include "net/socket/client_socket_handle.h" -#include "net/socket/socks5_client_socket.h" -#include "net/socket/socks_client_socket.h" -#include "net/socket/ssl_client_socket.h" -#include "net/socket/tcp_client_socket.h" -#include "net/socket_stream/socket_stream_metrics.h" -#include "net/ssl/ssl_cert_request_info.h" -#include "net/ssl/ssl_info.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_context.h" - -static const int kMaxPendingSendAllowed = 32768; // 32 kilobytes. -static const int kReadBufferSize = 4096; - -namespace net { - -int SocketStream::Delegate::OnStartOpenConnection( - SocketStream* socket, const CompletionCallback& callback) { - return OK; -} - -void SocketStream::Delegate::OnAuthRequired(SocketStream* socket, - AuthChallengeInfo* auth_info) { - // By default, no credential is available and close the connection. - socket->Close(); -} - -void SocketStream::Delegate::OnSSLCertificateError( - SocketStream* socket, - const SSLInfo& ssl_info, - bool fatal) { - socket->CancelWithSSLError(ssl_info); -} - -bool SocketStream::Delegate::CanGetCookies(SocketStream* socket, - const GURL& url) { - return true; -} - -bool SocketStream::Delegate::CanSetCookie(SocketStream* request, - const GURL& url, - const std::string& cookie_line, - CookieOptions* options) { - return true; -} - -SocketStream::ResponseHeaders::ResponseHeaders() : IOBuffer() {} - -void SocketStream::ResponseHeaders::Realloc(size_t new_size) { - headers_.reset(static_cast<char*>(realloc(headers_.release(), new_size))); -} - -SocketStream::ResponseHeaders::~ResponseHeaders() { data_ = NULL; } - -SocketStream::SocketStream(const GURL& url, Delegate* delegate, - URLRequestContext* context, - CookieStore* cookie_store) - : delegate_(delegate), - url_(url), - max_pending_send_allowed_(kMaxPendingSendAllowed), - context_(context), - next_state_(STATE_NONE), - factory_(ClientSocketFactory::GetDefaultFactory()), - proxy_mode_(kDirectConnection), - proxy_url_(url), - pac_request_(NULL), - connection_(new ClientSocketHandle), - privacy_mode_(PRIVACY_MODE_DISABLED), - // Unretained() is required; without it, Bind() creates a circular - // dependency and the SocketStream object will not be freed. - io_callback_(base::Bind(&SocketStream::OnIOCompleted, - base::Unretained(this))), - read_buf_(NULL), - current_write_buf_(NULL), - waiting_for_write_completion_(false), - closing_(false), - server_closed_(false), - metrics_(new SocketStreamMetrics(url)), - cookie_store_(cookie_store) { - DCHECK(base::MessageLoop::current()) - << "The current base::MessageLoop must exist"; - DCHECK(base::MessageLoopForIO::IsCurrent()) - << "The current base::MessageLoop must be TYPE_IO"; - DCHECK(delegate_); - - if (context_) { - if (!cookie_store_.get()) - cookie_store_ = context_->cookie_store(); - - net_log_ = BoundNetLog::Make( - context->net_log(), - NetLog::SOURCE_SOCKET_STREAM); - - net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE); - } -} - -SocketStream::UserData* SocketStream::GetUserData( - const void* key) const { - UserDataMap::const_iterator found = user_data_.find(key); - if (found != user_data_.end()) - return found->second.get(); - return NULL; -} - -void SocketStream::SetUserData(const void* key, UserData* data) { - user_data_[key] = linked_ptr<UserData>(data); -} - -bool SocketStream::is_secure() const { - return url_.SchemeIs("wss"); -} - -void SocketStream::DetachContext() { - if (!context_) - return; - - if (pac_request_) { - context_->proxy_service()->CancelPacRequest(pac_request_); - pac_request_ = NULL; - } - - net_log_.EndEvent(NetLog::TYPE_REQUEST_ALIVE); - net_log_ = BoundNetLog(); - - context_ = NULL; - cookie_store_ = NULL; -} - -void SocketStream::CheckPrivacyMode() { - if (context_ && context_->network_delegate()) { - bool enable = context_->network_delegate()->CanEnablePrivacyMode(url_, - url_); - privacy_mode_ = enable ? PRIVACY_MODE_ENABLED : PRIVACY_MODE_DISABLED; - // Disable Channel ID if privacy mode is enabled. - if (enable) - server_ssl_config_.channel_id_enabled = false; - } -} - -void SocketStream::Connect() { - DCHECK(base::MessageLoop::current()) - << "The current base::MessageLoop must exist"; - DCHECK(base::MessageLoopForIO::IsCurrent()) - << "The current base::MessageLoop must be TYPE_IO"; - if (context_) { - context_->ssl_config_service()->GetSSLConfig(&server_ssl_config_); - proxy_ssl_config_ = server_ssl_config_; - } - CheckPrivacyMode(); - - DCHECK_EQ(next_state_, STATE_NONE); - - AddRef(); // Released in Finish() - // Open a connection asynchronously, so that delegate won't be called - // back before returning Connect(). - next_state_ = STATE_BEFORE_CONNECT; - net_log_.BeginEvent( - NetLog::TYPE_SOCKET_STREAM_CONNECT, - NetLog::StringCallback("url", &url_.possibly_invalid_spec())); - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoLoop, this, OK)); -} - -size_t SocketStream::GetTotalSizeOfPendingWriteBufs() const { - size_t total_size = 0; - for (PendingDataQueue::const_iterator iter = pending_write_bufs_.begin(); - iter != pending_write_bufs_.end(); - ++iter) - total_size += (*iter)->size(); - return total_size; -} - -bool SocketStream::SendData(const char* data, int len) { - DCHECK(base::MessageLoop::current()) - << "The current base::MessageLoop must exist"; - DCHECK(base::MessageLoopForIO::IsCurrent()) - << "The current base::MessageLoop must be TYPE_IO"; - DCHECK_GT(len, 0); - - if (!connection_->socket() || - !connection_->socket()->IsConnected() || next_state_ == STATE_NONE) { - return false; - } - - int total_buffered_bytes = len; - if (current_write_buf_.get()) { - // Since - // - the purpose of this check is to limit the amount of buffer used by - // this instance. - // - the DrainableIOBuffer doesn't release consumed memory. - // we need to use not BytesRemaining() but size() here. - total_buffered_bytes += current_write_buf_->size(); - } - total_buffered_bytes += GetTotalSizeOfPendingWriteBufs(); - if (total_buffered_bytes > max_pending_send_allowed_) - return false; - - // TODO(tyoshino): Split data into smaller chunks e.g. 8KiB to free consumed - // buffer progressively - pending_write_bufs_.push_back(make_scoped_refptr( - new IOBufferWithSize(len))); - memcpy(pending_write_bufs_.back()->data(), data, len); - - // If current_write_buf_ is not NULL, it means that a) there's ongoing write - // operation or b) the connection is being closed. If a), the buffer we just - // pushed will be automatically handled when the completion callback runs - // the loop, and therefore we don't need to enqueue DoLoop(). If b), it's ok - // to do nothing. If current_write_buf_ is NULL, to make sure DoLoop() is - // ran soon, enequeue it. - if (!current_write_buf_.get()) { - // Send pending data asynchronously, so that delegate won't be called - // back before returning from SendData(). - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoLoop, this, OK)); - } - - return true; -} - -void SocketStream::Close() { - DCHECK(base::MessageLoop::current()) - << "The current base::MessageLoop must exist"; - DCHECK(base::MessageLoopForIO::IsCurrent()) - << "The current base::MessageLoop must be TYPE_IO"; - // If next_state_ is STATE_NONE, the socket was not opened, or already - // closed. So, return immediately. - // Otherwise, it might call Finish() more than once, so breaks balance - // of AddRef() and Release() in Connect() and Finish(), respectively. - if (next_state_ == STATE_NONE) - return; - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoClose, this)); -} - -void SocketStream::RestartWithAuth(const AuthCredentials& credentials) { - DCHECK(base::MessageLoop::current()) - << "The current base::MessageLoop must exist"; - DCHECK(base::MessageLoopForIO::IsCurrent()) - << "The current base::MessageLoop must be TYPE_IO"; - DCHECK(proxy_auth_controller_.get()); - if (!connection_->socket()) { - DVLOG(1) << "Socket is closed before restarting with auth."; - return; - } - - proxy_auth_controller_->ResetAuth(credentials); - - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoRestartWithAuth, this)); -} - -void SocketStream::DetachDelegate() { - if (!delegate_) - return; - delegate_ = NULL; - // Prevent the rest of the function from executing if we are being called from - // within Finish(). - if (next_state_ == STATE_NONE) - return; - net_log_.AddEvent(NetLog::TYPE_CANCELLED); - // We don't need to send pending data when client detach the delegate. - pending_write_bufs_.clear(); - Close(); -} - -const ProxyServer& SocketStream::proxy_server() const { - return proxy_info_.proxy_server(); -} - -void SocketStream::SetClientSocketFactory( - ClientSocketFactory* factory) { - DCHECK(factory); - factory_ = factory; -} - -void SocketStream::CancelWithError(int error) { - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoLoop, this, error)); -} - -void SocketStream::CancelWithSSLError(const SSLInfo& ssl_info) { - CancelWithError(MapCertStatusToNetError(ssl_info.cert_status)); -} - -void SocketStream::ContinueDespiteError() { - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoLoop, this, OK)); -} - -SocketStream::~SocketStream() { - DetachContext(); - DCHECK(!delegate_); - DCHECK(!pac_request_); -} - -SocketStream::RequestHeaders::~RequestHeaders() { data_ = NULL; } - -void SocketStream::set_addresses(const AddressList& addresses) { - addresses_ = addresses; -} - -void SocketStream::DoClose() { - closing_ = true; - // If next_state_ is: - // - STATE_TCP_CONNECT_COMPLETE, it's waiting other socket establishing - // connection. - // - STATE_AUTH_REQUIRED, it's waiting for restarting. - // - STATE_RESOLVE_PROTOCOL_COMPLETE, it's waiting for delegate_ to finish - // OnStartOpenConnection method call - // In these states, we'll close the SocketStream now. - if (next_state_ == STATE_TCP_CONNECT_COMPLETE || - next_state_ == STATE_AUTH_REQUIRED || - next_state_ == STATE_RESOLVE_PROTOCOL_COMPLETE) { - DoLoop(ERR_ABORTED); - return; - } - // If next_state_ is STATE_READ_WRITE, we'll run DoLoop and close - // the SocketStream. - // If it's writing now, we should defer the closing after the current - // writing is completed. - if (next_state_ == STATE_READ_WRITE && !current_write_buf_.get()) - DoLoop(ERR_ABORTED); - - // In other next_state_, we'll wait for callback of other APIs, such as - // ResolveProxy(). -} - -void SocketStream::Finish(int result) { - DCHECK(base::MessageLoop::current()) - << "The current base::MessageLoop must exist"; - DCHECK(base::MessageLoopForIO::IsCurrent()) - << "The current base::MessageLoop must be TYPE_IO"; - DCHECK_LE(result, OK); - if (result == OK) - result = ERR_CONNECTION_CLOSED; - DCHECK_EQ(next_state_, STATE_NONE); - DVLOG(1) << "Finish result=" << ErrorToString(result); - - metrics_->OnClose(); - - if (result != ERR_CONNECTION_CLOSED && delegate_) - delegate_->OnError(this, result); - if (result != ERR_PROTOCOL_SWITCHED && delegate_) - delegate_->OnClose(this); - delegate_ = NULL; - - Release(); -} - -int SocketStream::DidEstablishConnection() { - if (!connection_->socket() || !connection_->socket()->IsConnected()) { - next_state_ = STATE_CLOSE; - return ERR_CONNECTION_FAILED; - } - next_state_ = STATE_READ_WRITE; - metrics_->OnConnected(); - - net_log_.EndEvent(NetLog::TYPE_SOCKET_STREAM_CONNECT); - if (delegate_) - delegate_->OnConnected(this, max_pending_send_allowed_); - - return OK; -} - -int SocketStream::DidReceiveData(int result) { - DCHECK(read_buf_.get()); - DCHECK_GT(result, 0); - net_log_.AddEvent(NetLog::TYPE_SOCKET_STREAM_RECEIVED); - int len = result; - metrics_->OnRead(len); - if (delegate_) { - // Notify recevied data to delegate. - delegate_->OnReceivedData(this, read_buf_->data(), len); - } - read_buf_ = NULL; - return OK; -} - -void SocketStream::DidSendData(int result) { - DCHECK_GT(result, 0); - DCHECK(current_write_buf_.get()); - net_log_.AddEvent(NetLog::TYPE_SOCKET_STREAM_SENT); - - int bytes_sent = result; - - metrics_->OnWrite(bytes_sent); - - current_write_buf_->DidConsume(result); - - if (current_write_buf_->BytesRemaining()) - return; - - size_t bytes_freed = current_write_buf_->size(); - - current_write_buf_ = NULL; - - // We freed current_write_buf_ and this instance is now able to accept more - // data via SendData() (note that DidConsume() doesn't free consumed memory). - // We can tell that to delegate_ by calling OnSentData(). - if (delegate_) - delegate_->OnSentData(this, bytes_freed); -} - -void SocketStream::OnIOCompleted(int result) { - DoLoop(result); -} - -void SocketStream::OnReadCompleted(int result) { - if (result == 0) { - // 0 indicates end-of-file, so socket was closed. - // Don't close the socket if it's still writing. - server_closed_ = true; - } else if (result > 0 && read_buf_.get()) { - result = DidReceiveData(result); - } - DoLoop(result); -} - -void SocketStream::OnWriteCompleted(int result) { - waiting_for_write_completion_ = false; - if (result > 0) { - DidSendData(result); - result = OK; - } - DoLoop(result); -} - -void SocketStream::DoLoop(int result) { - if (next_state_ == STATE_NONE) - return; - - // If context was not set, close immediately. - if (!context_) - next_state_ = STATE_CLOSE; - - do { - State state = next_state_; - next_state_ = STATE_NONE; - switch (state) { - case STATE_BEFORE_CONNECT: - DCHECK_EQ(OK, result); - result = DoBeforeConnect(); - break; - case STATE_BEFORE_CONNECT_COMPLETE: - result = DoBeforeConnectComplete(result); - break; - case STATE_RESOLVE_PROXY: - DCHECK_EQ(OK, result); - result = DoResolveProxy(); - break; - case STATE_RESOLVE_PROXY_COMPLETE: - result = DoResolveProxyComplete(result); - break; - case STATE_RESOLVE_HOST: - DCHECK_EQ(OK, result); - result = DoResolveHost(); - break; - case STATE_RESOLVE_HOST_COMPLETE: - result = DoResolveHostComplete(result); - break; - case STATE_RESOLVE_PROTOCOL: - result = DoResolveProtocol(result); - break; - case STATE_RESOLVE_PROTOCOL_COMPLETE: - result = DoResolveProtocolComplete(result); - break; - case STATE_TCP_CONNECT: - result = DoTcpConnect(result); - break; - case STATE_TCP_CONNECT_COMPLETE: - result = DoTcpConnectComplete(result); - break; - case STATE_GENERATE_PROXY_AUTH_TOKEN: - result = DoGenerateProxyAuthToken(); - break; - case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE: - result = DoGenerateProxyAuthTokenComplete(result); - break; - case STATE_WRITE_TUNNEL_HEADERS: - DCHECK_EQ(OK, result); - result = DoWriteTunnelHeaders(); - break; - case STATE_WRITE_TUNNEL_HEADERS_COMPLETE: - result = DoWriteTunnelHeadersComplete(result); - break; - case STATE_READ_TUNNEL_HEADERS: - DCHECK_EQ(OK, result); - result = DoReadTunnelHeaders(); - break; - case STATE_READ_TUNNEL_HEADERS_COMPLETE: - result = DoReadTunnelHeadersComplete(result); - break; - case STATE_SOCKS_CONNECT: - DCHECK_EQ(OK, result); - result = DoSOCKSConnect(); - break; - case STATE_SOCKS_CONNECT_COMPLETE: - result = DoSOCKSConnectComplete(result); - break; - case STATE_SECURE_PROXY_CONNECT: - DCHECK_EQ(OK, result); - result = DoSecureProxyConnect(); - break; - case STATE_SECURE_PROXY_CONNECT_COMPLETE: - result = DoSecureProxyConnectComplete(result); - break; - case STATE_SECURE_PROXY_HANDLE_CERT_ERROR: - result = DoSecureProxyHandleCertError(result); - break; - case STATE_SECURE_PROXY_HANDLE_CERT_ERROR_COMPLETE: - result = DoSecureProxyHandleCertErrorComplete(result); - break; - case STATE_SSL_CONNECT: - DCHECK_EQ(OK, result); - result = DoSSLConnect(); - break; - case STATE_SSL_CONNECT_COMPLETE: - result = DoSSLConnectComplete(result); - break; - case STATE_SSL_HANDLE_CERT_ERROR: - result = DoSSLHandleCertError(result); - break; - case STATE_SSL_HANDLE_CERT_ERROR_COMPLETE: - result = DoSSLHandleCertErrorComplete(result); - break; - case STATE_READ_WRITE: - result = DoReadWrite(result); - break; - case STATE_AUTH_REQUIRED: - // It might be called when DoClose is called while waiting in - // STATE_AUTH_REQUIRED. - Finish(result); - return; - case STATE_CLOSE: - DCHECK_LE(result, OK); - Finish(result); - return; - default: - NOTREACHED() << "bad state " << state; - Finish(result); - return; - } - if (state == STATE_RESOLVE_PROTOCOL && result == ERR_PROTOCOL_SWITCHED) - continue; - // If the connection is not established yet and had actual errors, - // record the error. In next iteration, it will close the connection. - if (state != STATE_READ_WRITE && result < ERR_IO_PENDING) { - net_log_.EndEventWithNetErrorCode( - NetLog::TYPE_SOCKET_STREAM_CONNECT, result); - } - } while (result != ERR_IO_PENDING); -} - -int SocketStream::DoBeforeConnect() { - next_state_ = STATE_BEFORE_CONNECT_COMPLETE; - if (!context_ || !context_->network_delegate()) - return OK; - - int result = context_->network_delegate()->NotifyBeforeSocketStreamConnect( - this, io_callback_); - if (result != OK && result != ERR_IO_PENDING) - next_state_ = STATE_CLOSE; - - return result; -} - -int SocketStream::DoBeforeConnectComplete(int result) { - DCHECK_NE(ERR_IO_PENDING, result); - - if (result == OK) - next_state_ = STATE_RESOLVE_PROXY; - else - next_state_ = STATE_CLOSE; - - return result; -} - -int SocketStream::DoResolveProxy() { - DCHECK(context_); - DCHECK(!pac_request_); - next_state_ = STATE_RESOLVE_PROXY_COMPLETE; - - if (!proxy_url_.is_valid()) { - next_state_ = STATE_CLOSE; - return ERR_INVALID_ARGUMENT; - } - - // TODO(toyoshim): Check server advertisement of SPDY through the HTTP - // Alternate-Protocol header, then switch to SPDY if SPDY is available. - // Usually we already have a session to the SPDY server because JavaScript - // running WebSocket itself would be served by SPDY. But, in some situation - // (E.g. Used by Chrome Extensions or used for cross origin connection), this - // connection might be the first one. At that time, we should check - // Alternate-Protocol header here for ws:// or TLS NPN extension for wss:// . - - return context_->proxy_service()->ResolveProxy( - proxy_url_, net::LOAD_NORMAL, &proxy_info_, io_callback_, &pac_request_, - NULL, net_log_); -} - -int SocketStream::DoResolveProxyComplete(int result) { - pac_request_ = NULL; - if (result != OK) { - DVLOG(1) << "Failed to resolve proxy: " << result; - if (delegate_) - delegate_->OnError(this, result); - proxy_info_.UseDirect(); - } - if (proxy_info_.is_direct()) { - // If proxy was not found for original URL (i.e. websocket URL), - // try again with https URL, like Safari implementation. - // Note that we don't want to use http proxy, because we'll use tunnel - // proxy using CONNECT method, which is used by https proxy. - if (!proxy_url_.SchemeIs("https")) { - const std::string scheme = "https"; - GURL::Replacements repl; - repl.SetSchemeStr(scheme); - proxy_url_ = url_.ReplaceComponents(repl); - DVLOG(1) << "Try https proxy: " << proxy_url_; - next_state_ = STATE_RESOLVE_PROXY; - return OK; - } - } - - if (proxy_info_.is_empty()) { - // No proxies/direct to choose from. This happens when we don't support any - // of the proxies in the returned list. - return ERR_NO_SUPPORTED_PROXIES; - } - - next_state_ = STATE_RESOLVE_HOST; - return OK; -} - -int SocketStream::DoResolveHost() { - next_state_ = STATE_RESOLVE_HOST_COMPLETE; - - DCHECK(!proxy_info_.is_empty()); - if (proxy_info_.is_direct()) - proxy_mode_ = kDirectConnection; - else if (proxy_info_.proxy_server().is_socks()) - proxy_mode_ = kSOCKSProxy; - else - proxy_mode_ = kTunnelProxy; - - // Determine the host and port to connect to. - HostPortPair host_port_pair; - if (proxy_mode_ != kDirectConnection) { - host_port_pair = proxy_info_.proxy_server().host_port_pair(); - } else { - host_port_pair = HostPortPair::FromURL(url_); - } - - HostResolver::RequestInfo resolve_info(host_port_pair); - - DCHECK(context_->host_resolver()); - resolver_.reset(new SingleRequestHostResolver(context_->host_resolver())); - return resolver_->Resolve(resolve_info, - DEFAULT_PRIORITY, - &addresses_, - base::Bind(&SocketStream::OnIOCompleted, this), - net_log_); -} - -int SocketStream::DoResolveHostComplete(int result) { - if (result == OK) - next_state_ = STATE_RESOLVE_PROTOCOL; - else - next_state_ = STATE_CLOSE; - // TODO(ukai): if error occured, reconsider proxy after error. - return result; -} - -int SocketStream::DoResolveProtocol(int result) { - DCHECK_EQ(OK, result); - - if (!delegate_) { - next_state_ = STATE_CLOSE; - return result; - } - - next_state_ = STATE_RESOLVE_PROTOCOL_COMPLETE; - result = delegate_->OnStartOpenConnection(this, io_callback_); - if (result == ERR_IO_PENDING) - metrics_->OnWaitConnection(); - else if (result != OK && result != ERR_PROTOCOL_SWITCHED) - next_state_ = STATE_CLOSE; - return result; -} - -int SocketStream::DoResolveProtocolComplete(int result) { - DCHECK_NE(ERR_IO_PENDING, result); - - if (result == ERR_PROTOCOL_SWITCHED) { - next_state_ = STATE_CLOSE; - metrics_->OnCountWireProtocolType( - SocketStreamMetrics::WIRE_PROTOCOL_SPDY); - } else if (result == OK) { - next_state_ = STATE_TCP_CONNECT; - metrics_->OnCountWireProtocolType( - SocketStreamMetrics::WIRE_PROTOCOL_WEBSOCKET); - } else { - next_state_ = STATE_CLOSE; - } - return result; -} - -int SocketStream::DoTcpConnect(int result) { - if (result != OK) { - next_state_ = STATE_CLOSE; - return result; - } - next_state_ = STATE_TCP_CONNECT_COMPLETE; - DCHECK(factory_); - connection_->SetSocket( - factory_->CreateTransportClientSocket(addresses_, - net_log_.net_log(), - net_log_.source())); - metrics_->OnStartConnection(); - return connection_->socket()->Connect(io_callback_); -} - -int SocketStream::DoTcpConnectComplete(int result) { - // TODO(ukai): if error occured, reconsider proxy after error. - if (result != OK) { - next_state_ = STATE_CLOSE; - return result; - } - - if (proxy_mode_ == kTunnelProxy) { - if (proxy_info_.is_https()) - next_state_ = STATE_SECURE_PROXY_CONNECT; - else - next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; - } else if (proxy_mode_ == kSOCKSProxy) { - next_state_ = STATE_SOCKS_CONNECT; - } else if (is_secure()) { - next_state_ = STATE_SSL_CONNECT; - } else { - result = DidEstablishConnection(); - } - return result; -} - -int SocketStream::DoGenerateProxyAuthToken() { - next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE; - if (!proxy_auth_controller_.get()) { - DCHECK(context_); - DCHECK(context_->http_transaction_factory()); - DCHECK(context_->http_transaction_factory()->GetSession()); - HttpNetworkSession* session = - context_->http_transaction_factory()->GetSession(); - const char* scheme = proxy_info_.is_https() ? "https://" : "http://"; - GURL auth_url(scheme + - proxy_info_.proxy_server().host_port_pair().ToString()); - proxy_auth_controller_ = - new HttpAuthController(HttpAuth::AUTH_PROXY, - auth_url, - session->http_auth_cache(), - session->http_auth_handler_factory()); - } - HttpRequestInfo request_info; - request_info.url = url_; - request_info.method = "CONNECT"; - return proxy_auth_controller_->MaybeGenerateAuthToken( - &request_info, io_callback_, net_log_); -} - -int SocketStream::DoGenerateProxyAuthTokenComplete(int result) { - if (result != OK) { - next_state_ = STATE_CLOSE; - return result; - } - - next_state_ = STATE_WRITE_TUNNEL_HEADERS; - return result; -} - -int SocketStream::DoWriteTunnelHeaders() { - DCHECK_EQ(kTunnelProxy, proxy_mode_); - - next_state_ = STATE_WRITE_TUNNEL_HEADERS_COMPLETE; - - if (!tunnel_request_headers_.get()) { - metrics_->OnCountConnectionType(SocketStreamMetrics::TUNNEL_CONNECTION); - tunnel_request_headers_ = new RequestHeaders(); - tunnel_request_headers_bytes_sent_ = 0; - } - if (tunnel_request_headers_->headers_.empty()) { - HttpRequestHeaders request_headers; - request_headers.SetHeader("Host", GetHostAndOptionalPort(url_)); - request_headers.SetHeader("Proxy-Connection", "keep-alive"); - if (proxy_auth_controller_.get() && proxy_auth_controller_->HaveAuth()) - proxy_auth_controller_->AddAuthorizationHeader(&request_headers); - tunnel_request_headers_->headers_ = base::StringPrintf( - "CONNECT %s HTTP/1.1\r\n" - "%s", - GetHostAndPort(url_).c_str(), - request_headers.ToString().c_str()); - } - tunnel_request_headers_->SetDataOffset(tunnel_request_headers_bytes_sent_); - int buf_len = static_cast<int>(tunnel_request_headers_->headers_.size() - - tunnel_request_headers_bytes_sent_); - DCHECK_GT(buf_len, 0); - return connection_->socket()->Write( - tunnel_request_headers_.get(), buf_len, io_callback_); -} - -int SocketStream::DoWriteTunnelHeadersComplete(int result) { - DCHECK_EQ(kTunnelProxy, proxy_mode_); - - if (result < 0) { - next_state_ = STATE_CLOSE; - return result; - } - - tunnel_request_headers_bytes_sent_ += result; - if (tunnel_request_headers_bytes_sent_ < - tunnel_request_headers_->headers_.size()) { - next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; - } else { - // Handling a cert error or a client cert request requires reconnection. - // DoWriteTunnelHeaders() will be called again. - // Thus |tunnel_request_headers_bytes_sent_| should be reset to 0 for - // sending |tunnel_request_headers_| correctly. - tunnel_request_headers_bytes_sent_ = 0; - next_state_ = STATE_READ_TUNNEL_HEADERS; - } - return OK; -} - -int SocketStream::DoReadTunnelHeaders() { - DCHECK_EQ(kTunnelProxy, proxy_mode_); - - next_state_ = STATE_READ_TUNNEL_HEADERS_COMPLETE; - - if (!tunnel_response_headers_.get()) { - tunnel_response_headers_ = new ResponseHeaders(); - tunnel_response_headers_capacity_ = kMaxTunnelResponseHeadersSize; - tunnel_response_headers_->Realloc(tunnel_response_headers_capacity_); - tunnel_response_headers_len_ = 0; - } - - int buf_len = tunnel_response_headers_capacity_ - - tunnel_response_headers_len_; - tunnel_response_headers_->SetDataOffset(tunnel_response_headers_len_); - CHECK(tunnel_response_headers_->data()); - - return connection_->socket()->Read( - tunnel_response_headers_.get(), buf_len, io_callback_); -} - -int SocketStream::DoReadTunnelHeadersComplete(int result) { - DCHECK_EQ(kTunnelProxy, proxy_mode_); - - if (result < 0) { - next_state_ = STATE_CLOSE; - return result; - } - - if (result == 0) { - // 0 indicates end-of-file, so socket was closed. - next_state_ = STATE_CLOSE; - return ERR_CONNECTION_CLOSED; - } - - tunnel_response_headers_len_ += result; - DCHECK(tunnel_response_headers_len_ <= tunnel_response_headers_capacity_); - - int eoh = HttpUtil::LocateEndOfHeaders( - tunnel_response_headers_->headers(), tunnel_response_headers_len_, 0); - if (eoh == -1) { - if (tunnel_response_headers_len_ >= kMaxTunnelResponseHeadersSize) { - next_state_ = STATE_CLOSE; - return ERR_RESPONSE_HEADERS_TOO_BIG; - } - - next_state_ = STATE_READ_TUNNEL_HEADERS; - return OK; - } - // DidReadResponseHeaders - scoped_refptr<HttpResponseHeaders> headers; - headers = new HttpResponseHeaders( - HttpUtil::AssembleRawHeaders(tunnel_response_headers_->headers(), eoh)); - if (headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { - // Require the "HTTP/1.x" status line. - next_state_ = STATE_CLOSE; - return ERR_TUNNEL_CONNECTION_FAILED; - } - switch (headers->response_code()) { - case 200: // OK - if (is_secure()) { - DCHECK_EQ(eoh, tunnel_response_headers_len_); - next_state_ = STATE_SSL_CONNECT; - } else { - result = DidEstablishConnection(); - if (result < 0) { - next_state_ = STATE_CLOSE; - return result; - } - if ((eoh < tunnel_response_headers_len_) && delegate_) - delegate_->OnReceivedData( - this, tunnel_response_headers_->headers() + eoh, - tunnel_response_headers_len_ - eoh); - } - return OK; - case 407: // Proxy Authentication Required. - if (proxy_mode_ != kTunnelProxy) - return ERR_UNEXPECTED_PROXY_AUTH; - - result = proxy_auth_controller_->HandleAuthChallenge( - headers, false, true, net_log_); - if (result != OK) - return result; - DCHECK(!proxy_info_.is_empty()); - next_state_ = STATE_AUTH_REQUIRED; - if (proxy_auth_controller_->HaveAuth()) { - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoRestartWithAuth, this)); - return ERR_IO_PENDING; - } - if (delegate_) { - // Wait until RestartWithAuth or Close is called. - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&SocketStream::DoAuthRequired, this)); - return ERR_IO_PENDING; - } - break; - default: - break; - } - next_state_ = STATE_CLOSE; - return ERR_TUNNEL_CONNECTION_FAILED; -} - -int SocketStream::DoSOCKSConnect() { - DCHECK_EQ(kSOCKSProxy, proxy_mode_); - - next_state_ = STATE_SOCKS_CONNECT_COMPLETE; - - HostResolver::RequestInfo req_info(HostPortPair::FromURL(url_)); - - DCHECK(!proxy_info_.is_empty()); - scoped_ptr<StreamSocket> s; - if (proxy_info_.proxy_server().scheme() == ProxyServer::SCHEME_SOCKS5) { - s.reset(new SOCKS5ClientSocket(connection_.Pass(), req_info)); - } else { - s.reset(new SOCKSClientSocket(connection_.Pass(), - req_info, - DEFAULT_PRIORITY, - context_->host_resolver())); - } - connection_.reset(new ClientSocketHandle); - connection_->SetSocket(s.Pass()); - metrics_->OnCountConnectionType(SocketStreamMetrics::SOCKS_CONNECTION); - return connection_->socket()->Connect(io_callback_); -} - -int SocketStream::DoSOCKSConnectComplete(int result) { - DCHECK_EQ(kSOCKSProxy, proxy_mode_); - - if (result == OK) { - if (is_secure()) - next_state_ = STATE_SSL_CONNECT; - else - result = DidEstablishConnection(); - } else { - next_state_ = STATE_CLOSE; - } - return result; -} - -int SocketStream::DoSecureProxyConnect() { - DCHECK(factory_); - SSLClientSocketContext ssl_context; - ssl_context.cert_verifier = context_->cert_verifier(); - ssl_context.transport_security_state = context_->transport_security_state(); - ssl_context.channel_id_service = context_->channel_id_service(); - scoped_ptr<StreamSocket> socket(factory_->CreateSSLClientSocket( - connection_.Pass(), - proxy_info_.proxy_server().host_port_pair(), - proxy_ssl_config_, - ssl_context)); - connection_.reset(new ClientSocketHandle); - connection_->SetSocket(socket.Pass()); - next_state_ = STATE_SECURE_PROXY_CONNECT_COMPLETE; - metrics_->OnCountConnectionType(SocketStreamMetrics::SECURE_PROXY_CONNECTION); - return connection_->socket()->Connect(io_callback_); -} - -int SocketStream::DoSecureProxyConnectComplete(int result) { - DCHECK_EQ(STATE_NONE, next_state_); - // Reconnect with client authentication. - if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) - return HandleCertificateRequest(result, &proxy_ssl_config_); - - if (IsCertificateError(result)) - next_state_ = STATE_SECURE_PROXY_HANDLE_CERT_ERROR; - else if (result == OK) - next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; - else - next_state_ = STATE_CLOSE; - return result; -} - -int SocketStream::DoSecureProxyHandleCertError(int result) { - DCHECK_EQ(STATE_NONE, next_state_); - DCHECK(IsCertificateError(result)); - result = HandleCertificateError(result); - if (result == ERR_IO_PENDING) - next_state_ = STATE_SECURE_PROXY_HANDLE_CERT_ERROR_COMPLETE; - else - next_state_ = STATE_CLOSE; - return result; -} - -int SocketStream::DoSecureProxyHandleCertErrorComplete(int result) { - DCHECK_EQ(STATE_NONE, next_state_); - if (result == OK) { - if (!connection_->socket()->IsConnectedAndIdle()) - return AllowCertErrorForReconnection(&proxy_ssl_config_); - next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; - } else { - next_state_ = STATE_CLOSE; - } - return result; -} - -int SocketStream::DoSSLConnect() { - DCHECK(factory_); - SSLClientSocketContext ssl_context; - ssl_context.cert_verifier = context_->cert_verifier(); - ssl_context.transport_security_state = context_->transport_security_state(); - ssl_context.channel_id_service = context_->channel_id_service(); - scoped_ptr<StreamSocket> socket( - factory_->CreateSSLClientSocket(connection_.Pass(), - HostPortPair::FromURL(url_), - server_ssl_config_, - ssl_context)); - connection_.reset(new ClientSocketHandle); - connection_->SetSocket(socket.Pass()); - next_state_ = STATE_SSL_CONNECT_COMPLETE; - metrics_->OnCountConnectionType(SocketStreamMetrics::SSL_CONNECTION); - return connection_->socket()->Connect(io_callback_); -} - -int SocketStream::DoSSLConnectComplete(int result) { - DCHECK_EQ(STATE_NONE, next_state_); - // Reconnect with client authentication. - if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) - return HandleCertificateRequest(result, &server_ssl_config_); - - if (IsCertificateError(result)) - next_state_ = STATE_SSL_HANDLE_CERT_ERROR; - else if (result == OK) - result = DidEstablishConnection(); - else - next_state_ = STATE_CLOSE; - return result; -} - -int SocketStream::DoSSLHandleCertError(int result) { - DCHECK_EQ(STATE_NONE, next_state_); - DCHECK(IsCertificateError(result)); - result = HandleCertificateError(result); - if (result == OK || result == ERR_IO_PENDING) - next_state_ = STATE_SSL_HANDLE_CERT_ERROR_COMPLETE; - else - next_state_ = STATE_CLOSE; - return result; -} - -int SocketStream::DoSSLHandleCertErrorComplete(int result) { - DCHECK_EQ(STATE_NONE, next_state_); - // TODO(toyoshim): Upgrade to SPDY through TLS NPN extension if possible. - // If we use HTTPS and this is the first connection to the SPDY server, - // we should take care of TLS NPN extension here. - - if (result == OK) { - if (!connection_->socket()->IsConnectedAndIdle()) - return AllowCertErrorForReconnection(&server_ssl_config_); - result = DidEstablishConnection(); - } else { - next_state_ = STATE_CLOSE; - } - return result; -} - -int SocketStream::DoReadWrite(int result) { - if (result < OK) { - next_state_ = STATE_CLOSE; - return result; - } - if (!connection_->socket() || !connection_->socket()->IsConnected()) { - next_state_ = STATE_CLOSE; - return ERR_CONNECTION_CLOSED; - } - - // If client has requested close(), and there's nothing to write, then - // let's close the socket. - // We don't care about receiving data after the socket is closed. - if (closing_ && !current_write_buf_.get() && pending_write_bufs_.empty()) { - connection_->socket()->Disconnect(); - next_state_ = STATE_CLOSE; - return OK; - } - - next_state_ = STATE_READ_WRITE; - - // If server already closed the socket, we don't try to read. - if (!server_closed_) { - if (!read_buf_.get()) { - // No read pending and server didn't close the socket. - read_buf_ = new IOBuffer(kReadBufferSize); - result = connection_->socket()->Read( - read_buf_.get(), - kReadBufferSize, - base::Bind(&SocketStream::OnReadCompleted, base::Unretained(this))); - if (result > 0) { - return DidReceiveData(result); - } else if (result == 0) { - // 0 indicates end-of-file, so socket was closed. - next_state_ = STATE_CLOSE; - server_closed_ = true; - return ERR_CONNECTION_CLOSED; - } - // If read is pending, try write as well. - // Otherwise, return the result and do next loop (to close the - // connection). - if (result != ERR_IO_PENDING) { - next_state_ = STATE_CLOSE; - server_closed_ = true; - return result; - } - } - // Read is pending. - DCHECK(read_buf_.get()); - } - - if (waiting_for_write_completion_) - return ERR_IO_PENDING; - - if (!current_write_buf_.get()) { - if (pending_write_bufs_.empty()) { - // Nothing buffered for send. - return ERR_IO_PENDING; - } - - current_write_buf_ = new DrainableIOBuffer( - pending_write_bufs_.front().get(), pending_write_bufs_.front()->size()); - pending_write_bufs_.pop_front(); - } - - result = connection_->socket()->Write( - current_write_buf_.get(), - current_write_buf_->BytesRemaining(), - base::Bind(&SocketStream::OnWriteCompleted, base::Unretained(this))); - - if (result == ERR_IO_PENDING) { - waiting_for_write_completion_ = true; - } else if (result < 0) { - // Shortcut. Enter STATE_CLOSE now by changing next_state_ here than by - // calling DoReadWrite() again with the error code. - next_state_ = STATE_CLOSE; - } else if (result > 0) { - // Write is not pending. Return OK and do next loop. - DidSendData(result); - result = OK; - } - - return result; -} - -GURL SocketStream::ProxyAuthOrigin() const { - DCHECK(!proxy_info_.is_empty()); - return GURL("http://" + - proxy_info_.proxy_server().host_port_pair().ToString()); -} - -int SocketStream::HandleCertificateRequest(int result, SSLConfig* ssl_config) { - if (ssl_config->send_client_cert) { - // We already have performed SSL client authentication once and failed. - return result; - } - - DCHECK(connection_->socket()); - scoped_refptr<SSLCertRequestInfo> cert_request_info = new SSLCertRequestInfo; - SSLClientSocket* ssl_socket = - static_cast<SSLClientSocket*>(connection_->socket()); - ssl_socket->GetSSLCertRequestInfo(cert_request_info.get()); - - HttpTransactionFactory* factory = context_->http_transaction_factory(); - if (!factory) - return result; - scoped_refptr<HttpNetworkSession> session = factory->GetSession(); - if (!session.get()) - return result; - - // If the user selected one of the certificates in client_certs or declined - // to provide one for this server before, use the past decision - // automatically. - scoped_refptr<X509Certificate> client_cert; - if (!session->ssl_client_auth_cache()->Lookup( - cert_request_info->host_and_port, &client_cert)) { - return result; - } - - // Note: |client_cert| may be NULL, indicating that the caller - // wishes to proceed anonymously (eg: continue the handshake - // without sending a client cert) - // - // Check that the certificate selected is still a certificate the server - // is likely to accept, based on the criteria supplied in the - // CertificateRequest message. - const std::vector<std::string>& cert_authorities = - cert_request_info->cert_authorities; - if (client_cert.get() && !cert_authorities.empty() && - !client_cert->IsIssuedByEncoded(cert_authorities)) { - return result; - } - - ssl_config->send_client_cert = true; - ssl_config->client_cert = client_cert; - next_state_ = STATE_TCP_CONNECT; - return OK; -} - -int SocketStream::AllowCertErrorForReconnection(SSLConfig* ssl_config) { - DCHECK(ssl_config); - // The SSL handshake didn't finish, or the server closed the SSL connection. - // So, we should restart establishing connection with the certificate in - // allowed bad certificates in |ssl_config|. - // See also net/http/http_network_transaction.cc HandleCertificateError() and - // RestartIgnoringLastError(). - SSLClientSocket* ssl_socket = - static_cast<SSLClientSocket*>(connection_->socket()); - SSLInfo ssl_info; - ssl_socket->GetSSLInfo(&ssl_info); - if (ssl_info.cert.get() == NULL || - ssl_config->IsAllowedBadCert(ssl_info.cert.get(), NULL)) { - // If we already have the certificate in the set of allowed bad - // certificates, we did try it and failed again, so we should not - // retry again: the connection should fail at last. - next_state_ = STATE_CLOSE; - return ERR_UNEXPECTED; - } - // Add the bad certificate to the set of allowed certificates in the - // SSL config object. - SSLConfig::CertAndStatus bad_cert; - if (!X509Certificate::GetDEREncoded(ssl_info.cert->os_cert_handle(), - &bad_cert.der_cert)) { - next_state_ = STATE_CLOSE; - return ERR_UNEXPECTED; - } - bad_cert.cert_status = ssl_info.cert_status; - ssl_config->allowed_bad_certs.push_back(bad_cert); - // Restart connection ignoring the bad certificate. - connection_->socket()->Disconnect(); - connection_->SetSocket(scoped_ptr<StreamSocket>()); - next_state_ = STATE_TCP_CONNECT; - return OK; -} - -void SocketStream::DoAuthRequired() { - if (delegate_ && proxy_auth_controller_.get()) - delegate_->OnAuthRequired(this, proxy_auth_controller_->auth_info().get()); - else - DoLoop(ERR_UNEXPECTED); -} - -void SocketStream::DoRestartWithAuth() { - DCHECK_EQ(next_state_, STATE_AUTH_REQUIRED); - tunnel_request_headers_ = NULL; - tunnel_request_headers_bytes_sent_ = 0; - tunnel_response_headers_ = NULL; - tunnel_response_headers_capacity_ = 0; - tunnel_response_headers_len_ = 0; - - next_state_ = STATE_TCP_CONNECT; - DoLoop(OK); -} - -int SocketStream::HandleCertificateError(int result) { - DCHECK(IsCertificateError(result)); - SSLClientSocket* ssl_socket = - static_cast<SSLClientSocket*>(connection_->socket()); - DCHECK(ssl_socket); - - if (!context_) - return result; - - if (SSLClientSocket::IgnoreCertError(result, LOAD_IGNORE_ALL_CERT_ERRORS)) { - const HttpNetworkSession::Params* session_params = - context_->GetNetworkSessionParams(); - if (session_params && session_params->ignore_certificate_errors) - return OK; - } - - if (!delegate_) - return result; - - SSLInfo ssl_info; - ssl_socket->GetSSLInfo(&ssl_info); - - TransportSecurityState* state = context_->transport_security_state(); - const bool fatal = state && state->ShouldSSLErrorsBeFatal(url_.host()); - - delegate_->OnSSLCertificateError(this, ssl_info, fatal); - return ERR_IO_PENDING; -} - -CookieStore* SocketStream::cookie_store() const { - return cookie_store_.get(); -} - -} // namespace net
diff --git a/net/socket_stream/socket_stream.h b/net/socket_stream/socket_stream.h deleted file mode 100644 index 7387eb6..0000000 --- a/net/socket_stream/socket_stream.h +++ /dev/null
@@ -1,404 +0,0 @@ -// Copyright (c) 2012 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 NET_SOCKET_STREAM_SOCKET_STREAM_H_ -#define NET_SOCKET_STREAM_SOCKET_STREAM_H_ - -#include <deque> -#include <map> -#include <string> - -#include "base/memory/linked_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/address_list.h" -#include "net/base/completion_callback.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/net_export.h" -#include "net/base/net_log.h" -#include "net/base/privacy_mode.h" -#include "net/cookies/cookie_store.h" -#include "net/proxy/proxy_service.h" -#include "net/ssl/ssl_config_service.h" -#include "net/url_request/url_request.h" - -namespace net { - -class AuthChallengeInfo; -class CertVerifier; -class ChannelIDService; -class ClientSocketFactory; -class ClientSocketHandle; -class CookieOptions; -class HostResolver; -class HttpAuthController; -class SSLInfo; -class SingleRequestHostResolver; -class SocketStreamMetrics; -class TransportSecurityState; -class URLRequestContext; - -// SocketStream is used to implement Web Sockets. -// It provides plain full-duplex stream with proxy and SSL support. -// For proxy authentication, only basic mechanisum is supported. It will try -// authentication identity for proxy URL first. If server requires proxy -// authentication, it will try authentication identity for realm that server -// requests. -class NET_EXPORT SocketStream - : public base::RefCountedThreadSafe<SocketStream> { - public: - // Derive from this class and add your own data members to associate extra - // information with a SocketStream. Use GetUserData(key) and - // SetUserData(key, data). - class UserData { - public: - UserData() {} - virtual ~UserData() {} - }; - - class NET_EXPORT Delegate { - public: - virtual int OnStartOpenConnection(SocketStream* socket, - const CompletionCallback& callback); - - // Called when a socket stream has been connected. The socket stream is - // allowed to buffer pending send data at most |max_pending_send_allowed| - // bytes. A client of the socket stream should keep track of how much - // pending send data it has and must not call SendData() if the pending - // data goes over |max_pending_send_allowed| bytes. - virtual void OnConnected(SocketStream* socket, - int max_pending_send_allowed) = 0; - - // Called when |amount_sent| bytes of data are sent. - virtual void OnSentData(SocketStream* socket, - int amount_sent) = 0; - - // Called when |len| bytes of |data| are received. - virtual void OnReceivedData(SocketStream* socket, - const char* data, int len) = 0; - - // Called when the socket stream has been closed. - virtual void OnClose(SocketStream* socket) = 0; - - // Called when proxy authentication required. - // The delegate should call RestartWithAuth() if credential for |auth_info| - // is found in password database, or call Close() to close the connection. - virtual void OnAuthRequired(SocketStream* socket, - AuthChallengeInfo* auth_info); - - // Called when using SSL and the server responds with a certificate with an - // error. The delegate should call CancelBecauseOfCertError() or - // ContinueDespiteCertError() to resume connection handling. - virtual void OnSSLCertificateError(SocketStream* socket, - const SSLInfo& ssl_info, - bool fatal); - - // Called when an error occured. - // This is only for error reporting to the delegate. - // |error| is net::Error. - virtual void OnError(const SocketStream* socket, int error) {} - - // Called when reading cookies to allow the delegate to block access to the - // cookie. - virtual bool CanGetCookies(SocketStream* socket, const GURL& url); - - // Called when a cookie is set to allow the delegate to block access to the - // cookie. - virtual bool CanSetCookie(SocketStream* request, - const GURL& url, - const std::string& cookie_line, - CookieOptions* options); - - protected: - virtual ~Delegate() {} - }; - - SocketStream(const GURL& url, Delegate* delegate, URLRequestContext* context, - CookieStore* cookie_store); - - // The user data allows the clients to associate data with this job. - // Multiple user data values can be stored under different keys. - // This job will TAKE OWNERSHIP of the given data pointer, and will - // delete the object if it is changed or the job is destroyed. - UserData* GetUserData(const void* key) const; - void SetUserData(const void* key, UserData* data); - - const GURL& url() const { return url_; } - bool is_secure() const; - const AddressList& address_list() const { return addresses_; } - Delegate* delegate() const { return delegate_; } - int max_pending_send_allowed() const { return max_pending_send_allowed_; } - - URLRequestContext* context() { return context_; } - - const SSLConfig& server_ssl_config() const { return server_ssl_config_; } - PrivacyMode privacy_mode() const { return privacy_mode_; } - void CheckPrivacyMode(); - - BoundNetLog* net_log() { return &net_log_; } - - // Opens the connection on the IO thread. - // Once the connection is established, calls delegate's OnConnected. - virtual void Connect(); - - // Buffers |data| of |len| bytes for send and returns true if successful. - // If size of buffered data exceeds |max_pending_send_allowed_|, sends no - // data and returns false. |len| must be positive. - virtual bool SendData(const char* data, int len); - - // Requests to close the connection. - // Once the connection is closed, calls delegate's OnClose. - virtual void Close(); - - // Restarts with authentication info. - // Should be used for response of OnAuthRequired. - virtual void RestartWithAuth(const AuthCredentials& credentials); - - // Detach delegate. Call before delegate is deleted. - // Once delegate is detached, close the socket stream and never call delegate - // back. - virtual void DetachDelegate(); - - // Detach the context. - virtual void DetachContext(); - - const ProxyServer& proxy_server() const; - - // Sets an alternative ClientSocketFactory. Doesn't take ownership of - // |factory|. For testing purposes only. - void SetClientSocketFactory(ClientSocketFactory* factory); - - // Cancels the connection because of an error. - // |error| is net::Error which represents the error. - void CancelWithError(int error); - - // Cancels the connection because of receiving a certificate with an error. - void CancelWithSSLError(const SSLInfo& ssl_info); - - // Continues to establish the connection in spite of an error. Usually this - // case happens because users allow certificate with an error by manual - // actions on alert dialog or browser cached such kinds of user actions. - void ContinueDespiteError(); - - CookieStore* cookie_store() const; - - protected: - friend class base::RefCountedThreadSafe<SocketStream>; - virtual ~SocketStream(); - - Delegate* delegate_; - - private: - FRIEND_TEST_ALL_PREFIXES(SocketStreamTest, IOPending); - FRIEND_TEST_ALL_PREFIXES(SocketStreamTest, SwitchAfterPending); - FRIEND_TEST_ALL_PREFIXES(SocketStreamTest, - NullContextSocketStreamShouldNotCrash); - - friend class WebSocketThrottleTest; - - typedef std::map<const void*, linked_ptr<UserData> > UserDataMap; - typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue; - - class RequestHeaders : public IOBuffer { - public: - RequestHeaders() : IOBuffer() {} - - void SetDataOffset(size_t offset) { - data_ = const_cast<char*>(headers_.data()) + offset; - } - - std::string headers_; - - private: - ~RequestHeaders() override; - }; - - class ResponseHeaders : public IOBuffer { - public: - ResponseHeaders(); - - void SetDataOffset(size_t offset) { data_ = headers_.get() + offset; } - char* headers() const { return headers_.get(); } - void Reset() { headers_.reset(); } - void Realloc(size_t new_size); - - private: - ~ResponseHeaders() override; - - scoped_ptr<char, base::FreeDeleter> headers_; - }; - - enum State { - STATE_NONE, - STATE_BEFORE_CONNECT, - STATE_BEFORE_CONNECT_COMPLETE, - STATE_RESOLVE_PROXY, - STATE_RESOLVE_PROXY_COMPLETE, - STATE_RESOLVE_HOST, - STATE_RESOLVE_HOST_COMPLETE, - STATE_RESOLVE_PROTOCOL, - STATE_RESOLVE_PROTOCOL_COMPLETE, - STATE_TCP_CONNECT, - STATE_TCP_CONNECT_COMPLETE, - STATE_GENERATE_PROXY_AUTH_TOKEN, - STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE, - STATE_WRITE_TUNNEL_HEADERS, - STATE_WRITE_TUNNEL_HEADERS_COMPLETE, - STATE_READ_TUNNEL_HEADERS, - STATE_READ_TUNNEL_HEADERS_COMPLETE, - STATE_SOCKS_CONNECT, - STATE_SOCKS_CONNECT_COMPLETE, - STATE_SECURE_PROXY_CONNECT, - STATE_SECURE_PROXY_CONNECT_COMPLETE, - STATE_SECURE_PROXY_HANDLE_CERT_ERROR, - STATE_SECURE_PROXY_HANDLE_CERT_ERROR_COMPLETE, - STATE_SSL_CONNECT, - STATE_SSL_CONNECT_COMPLETE, - STATE_SSL_HANDLE_CERT_ERROR, - STATE_SSL_HANDLE_CERT_ERROR_COMPLETE, - STATE_READ_WRITE, - STATE_AUTH_REQUIRED, - STATE_CLOSE, - }; - - enum ProxyMode { - kDirectConnection, // If using a direct connection - kTunnelProxy, // If using a tunnel (CONNECT method as HTTPS) - kSOCKSProxy, // If using a SOCKS proxy - }; - - // Use the same number as HttpNetworkTransaction::kMaxHeaderBufSize. - enum { kMaxTunnelResponseHeadersSize = 32768 }; // 32 kilobytes. - - // Used for WebSocketThrottleTest. - void set_addresses(const AddressList& addresses); - - void DoClose(); - - // Finishes the job. - // Calls OnError and OnClose of delegate, and no more - // notifications will be sent to delegate. - void Finish(int result); - - int DidEstablishConnection(); - int DidReceiveData(int result); - // Given the number of bytes sent, - // - notifies the |delegate_| and |metrics_| of this event. - // - drains sent data from |current_write_buf_|. - // - if |current_write_buf_| has been fully sent, sets NULL to - // |current_write_buf_| to get ready for next write. - // and then, returns OK. - void DidSendData(int result); - - void OnIOCompleted(int result); - void OnReadCompleted(int result); - void OnWriteCompleted(int result); - - void DoLoop(int result); - - int DoBeforeConnect(); - int DoBeforeConnectComplete(int result); - int DoResolveProxy(); - int DoResolveProxyComplete(int result); - int DoResolveHost(); - int DoResolveHostComplete(int result); - int DoResolveProtocol(int result); - int DoResolveProtocolComplete(int result); - int DoTcpConnect(int result); - int DoTcpConnectComplete(int result); - int DoGenerateProxyAuthToken(); - int DoGenerateProxyAuthTokenComplete(int result); - int DoWriteTunnelHeaders(); - int DoWriteTunnelHeadersComplete(int result); - int DoReadTunnelHeaders(); - int DoReadTunnelHeadersComplete(int result); - int DoSOCKSConnect(); - int DoSOCKSConnectComplete(int result); - int DoSecureProxyConnect(); - int DoSecureProxyConnectComplete(int result); - int DoSecureProxyHandleCertError(int result); - int DoSecureProxyHandleCertErrorComplete(int result); - int DoSSLConnect(); - int DoSSLConnectComplete(int result); - int DoSSLHandleCertError(int result); - int DoSSLHandleCertErrorComplete(int result); - int DoReadWrite(int result); - - GURL ProxyAuthOrigin() const; - int HandleAuthChallenge(const HttpResponseHeaders* headers); - int HandleCertificateRequest(int result, SSLConfig* ssl_config); - void DoAuthRequired(); - void DoRestartWithAuth(); - - int HandleCertificateError(int result); - int AllowCertErrorForReconnection(SSLConfig* ssl_config); - - // Returns the sum of the size of buffers in |pending_write_bufs_|. - size_t GetTotalSizeOfPendingWriteBufs() const; - - BoundNetLog net_log_; - - GURL url_; - // The number of bytes allowed to be buffered in this object. If the size of - // buffered data which is - // current_write_buf_.BytesRemaining() + - // sum of the size of buffers in |pending_write_bufs_| - // exceeds this limit, SendData() fails. - int max_pending_send_allowed_; - URLRequestContext* context_; - - UserDataMap user_data_; - - State next_state_; - ClientSocketFactory* factory_; - - ProxyMode proxy_mode_; - - GURL proxy_url_; - ProxyService::PacRequest* pac_request_; - ProxyInfo proxy_info_; - - scoped_refptr<HttpAuthController> proxy_auth_controller_; - - scoped_refptr<RequestHeaders> tunnel_request_headers_; - size_t tunnel_request_headers_bytes_sent_; - scoped_refptr<ResponseHeaders> tunnel_response_headers_; - int tunnel_response_headers_capacity_; - int tunnel_response_headers_len_; - - scoped_ptr<SingleRequestHostResolver> resolver_; - AddressList addresses_; - scoped_ptr<ClientSocketHandle> connection_; - - SSLConfig server_ssl_config_; - SSLConfig proxy_ssl_config_; - PrivacyMode privacy_mode_; - - CompletionCallback io_callback_; - - scoped_refptr<IOBuffer> read_buf_; - int read_buf_size_; - - // Buffer to hold data to pass to socket_. - scoped_refptr<DrainableIOBuffer> current_write_buf_; - // True iff there's no error and this instance is waiting for completion of - // Write operation by socket_. - bool waiting_for_write_completion_; - PendingDataQueue pending_write_bufs_; - - bool closing_; - bool server_closed_; - - scoped_ptr<SocketStreamMetrics> metrics_; - - // Cookie store to use for this socket stream. - scoped_refptr<CookieStore> cookie_store_; - - DISALLOW_COPY_AND_ASSIGN(SocketStream); -}; - -} // namespace net - -#endif // NET_SOCKET_STREAM_SOCKET_STREAM_H_
diff --git a/net/socket_stream/socket_stream_job.cc b/net/socket_stream/socket_stream_job.cc deleted file mode 100644 index 66da741..0000000 --- a/net/socket_stream/socket_stream_job.cc +++ /dev/null
@@ -1,92 +0,0 @@ -// Copyright (c) 2012 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 "net/socket_stream/socket_stream_job.h" - -#include "base/memory/singleton.h" -#include "net/http/transport_security_state.h" -#include "net/socket_stream/socket_stream_job_manager.h" -#include "net/ssl/ssl_config_service.h" -#include "net/url_request/url_request_context.h" - -namespace net { - -// static -SocketStreamJob::ProtocolFactory* SocketStreamJob::RegisterProtocolFactory( - const std::string& scheme, ProtocolFactory* factory) { - return SocketStreamJobManager::GetInstance()->RegisterProtocolFactory( - scheme, factory); -} - -// static -SocketStreamJob* SocketStreamJob::CreateSocketStreamJob( - const GURL& url, - SocketStream::Delegate* delegate, - TransportSecurityState* sts, - SSLConfigService* ssl, - URLRequestContext* context, - CookieStore* cookie_store) { - GURL socket_url(url); - if (url.scheme() == "ws" && sts && - sts->ShouldUpgradeToSSL(url.host())) { - url::Replacements<char> replacements; - static const char kNewScheme[] = "wss"; - replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme))); - socket_url = url.ReplaceComponents(replacements); - } - return SocketStreamJobManager::GetInstance()->CreateJob( - socket_url, delegate, context, cookie_store); -} - -SocketStreamJob::SocketStreamJob() {} - -SocketStream::UserData* SocketStreamJob::GetUserData(const void* key) const { - return socket_->GetUserData(key); -} - -void SocketStreamJob::SetUserData(const void* key, - SocketStream::UserData* data) { - socket_->SetUserData(key, data); -} - -void SocketStreamJob::Connect() { - socket_->Connect(); -} - -bool SocketStreamJob::SendData(const char* data, int len) { - return socket_->SendData(data, len); -} - -void SocketStreamJob::Close() { - socket_->Close(); -} - -void SocketStreamJob::RestartWithAuth(const AuthCredentials& credentials) { - socket_->RestartWithAuth(credentials); -} - -void SocketStreamJob::CancelWithError(int error) { - socket_->CancelWithError(error); -} - -void SocketStreamJob::CancelWithSSLError(const net::SSLInfo& ssl_info) { - socket_->CancelWithSSLError(ssl_info); -} - -void SocketStreamJob::ContinueDespiteError() { - socket_->ContinueDespiteError(); -} - -void SocketStreamJob::DetachDelegate() { - socket_->DetachDelegate(); -} - -void SocketStreamJob::DetachContext() { - if (socket_.get()) - socket_->DetachContext(); -} - -SocketStreamJob::~SocketStreamJob() {} - -} // namespace net
diff --git a/net/socket_stream/socket_stream_job.h b/net/socket_stream/socket_stream_job.h deleted file mode 100644 index 9fc27d9..0000000 --- a/net/socket_stream/socket_stream_job.h +++ /dev/null
@@ -1,94 +0,0 @@ -// Copyright (c) 2012 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 NET_SOCKET_STREAM_SOCKET_STREAM_JOB_H_ -#define NET_SOCKET_STREAM_SOCKET_STREAM_JOB_H_ - -#include <string> - -#include "base/memory/ref_counted.h" -#include "net/base/net_export.h" -#include "net/socket_stream/socket_stream.h" - -class GURL; - -namespace net { - -class CookieStore; -class SSLConfigService; -class SSLInfo; -class TransportSecurityState; - -// SocketStreamJob represents full-duplex communication over SocketStream. -// If a protocol (e.g. WebSocket protocol) needs to inspect/modify data -// over SocketStream, you can implement protocol specific job (e.g. -// WebSocketJob) to do some work on data over SocketStream. -// Registers the protocol specific SocketStreamJob by RegisterProtocolFactory -// and call CreateSocketStreamJob to create SocketStreamJob for the URL. -class NET_EXPORT SocketStreamJob - : public base::RefCountedThreadSafe<SocketStreamJob> { - public: - // Callback function implemented by protocol handlers to create new jobs. - typedef SocketStreamJob* (ProtocolFactory)(const GURL& url, - SocketStream::Delegate* delegate, - URLRequestContext* context, - CookieStore* cookie_store); - - static ProtocolFactory* RegisterProtocolFactory(const std::string& scheme, - ProtocolFactory* factory); - - static SocketStreamJob* CreateSocketStreamJob( - const GURL& url, - SocketStream::Delegate* delegate, - TransportSecurityState* sts, - SSLConfigService* ssl, - URLRequestContext* context, - CookieStore* cookie_store); - - SocketStreamJob(); - void InitSocketStream(SocketStream* socket) { - socket_ = socket; - } - - virtual SocketStream::UserData* GetUserData(const void* key) const; - virtual void SetUserData(const void* key, SocketStream::UserData* data); - - URLRequestContext* context() const { - return socket_.get() ? socket_->context() : 0; - } - CookieStore* cookie_store() const { - return socket_.get() ? socket_->cookie_store() : 0; - } - - virtual void Connect(); - - virtual bool SendData(const char* data, int len); - - virtual void Close(); - - virtual void RestartWithAuth(const AuthCredentials& credentials); - - virtual void CancelWithError(int error); - - virtual void CancelWithSSLError(const net::SSLInfo& ssl_info); - - virtual void ContinueDespiteError(); - - virtual void DetachDelegate(); - - virtual void DetachContext(); - - protected: - friend class WebSocketJobTest; - friend class base::RefCountedThreadSafe<SocketStreamJob>; - virtual ~SocketStreamJob(); - - scoped_refptr<SocketStream> socket_; - - DISALLOW_COPY_AND_ASSIGN(SocketStreamJob); -}; - -} // namespace net - -#endif // NET_SOCKET_STREAM_SOCKET_STREAM_JOB_H_
diff --git a/net/socket_stream/socket_stream_job_manager.cc b/net/socket_stream/socket_stream_job_manager.cc deleted file mode 100644 index 6418be4..0000000 --- a/net/socket_stream/socket_stream_job_manager.cc +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright (c) 2011 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 "net/socket_stream/socket_stream_job_manager.h" - -#include "base/memory/singleton.h" - -namespace net { - -SocketStreamJobManager::SocketStreamJobManager() { -} - -SocketStreamJobManager::~SocketStreamJobManager() { -} - -// static -SocketStreamJobManager* SocketStreamJobManager::GetInstance() { - return Singleton<SocketStreamJobManager>::get(); -} - -SocketStreamJob* SocketStreamJobManager::CreateJob( - const GURL& url, SocketStream::Delegate* delegate, - URLRequestContext* context, CookieStore* cookie_store) const { - // If url is invalid, create plain SocketStreamJob, which will close - // the socket immediately. - if (!url.is_valid()) { - SocketStreamJob* job = new SocketStreamJob(); - job->InitSocketStream(new SocketStream(url, delegate, context, - cookie_store)); - return job; - } - - const std::string& scheme = url.scheme(); // already lowercase - - base::AutoLock locked(lock_); - FactoryMap::const_iterator found = factories_.find(scheme); - if (found != factories_.end()) { - SocketStreamJob* job = found->second(url, delegate, context, cookie_store); - if (job) - return job; - } - SocketStreamJob* job = new SocketStreamJob(); - job->InitSocketStream(new SocketStream(url, delegate, context, cookie_store)); - return job; -} - -SocketStreamJob::ProtocolFactory* -SocketStreamJobManager::RegisterProtocolFactory( - const std::string& scheme, SocketStreamJob::ProtocolFactory* factory) { - base::AutoLock locked(lock_); - - SocketStreamJob::ProtocolFactory* old_factory; - FactoryMap::iterator found = factories_.find(scheme); - if (found != factories_.end()) { - old_factory = found->second; - } else { - old_factory = NULL; - } - if (factory) { - factories_[scheme] = factory; - } else if (found != factories_.end()) { - factories_.erase(found); - } - return old_factory; -} - -} // namespace net
diff --git a/net/socket_stream/socket_stream_job_manager.h b/net/socket_stream/socket_stream_job_manager.h deleted file mode 100644 index 2363fb5..0000000 --- a/net/socket_stream/socket_stream_job_manager.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright (c) 2010 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 NET_SOCKET_STREAM_SOCKET_STREAM_JOB_MANAGER_H_ -#define NET_SOCKET_STREAM_SOCKET_STREAM_JOB_MANAGER_H_ - -#include <map> -#include <string> - -#include "net/socket_stream/socket_stream.h" -#include "net/socket_stream/socket_stream_job.h" - -template <typename T> struct DefaultSingletonTraits; -class GURL; - -namespace net { - -class SocketStreamJobManager { - public: - // Returns the singleton instance. - static SocketStreamJobManager* GetInstance(); - - SocketStreamJob* CreateJob( - const GURL& url, SocketStream::Delegate* delegate, - URLRequestContext* context, CookieStore* cookie_store) const; - - SocketStreamJob::ProtocolFactory* RegisterProtocolFactory( - const std::string& scheme, SocketStreamJob::ProtocolFactory* factory); - - private: - friend struct DefaultSingletonTraits<SocketStreamJobManager>; - typedef std::map<std::string, SocketStreamJob::ProtocolFactory*> FactoryMap; - - SocketStreamJobManager(); - ~SocketStreamJobManager(); - - mutable base::Lock lock_; - FactoryMap factories_; - - DISALLOW_COPY_AND_ASSIGN(SocketStreamJobManager); -}; - -} // namespace net - -#endif // NET_SOCKET_STREAM_SOCKET_STREAM_JOB_MANAGER_H_
diff --git a/net/socket_stream/socket_stream_metrics.cc b/net/socket_stream/socket_stream_metrics.cc deleted file mode 100644 index e026887..0000000 --- a/net/socket_stream/socket_stream_metrics.cc +++ /dev/null
@@ -1,86 +0,0 @@ -// Copyright (c) 2011 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 "net/socket_stream/socket_stream_metrics.h" - -#include <string.h> - -#include "base/metrics/histogram.h" -#include "base/time/time.h" -#include "url/gurl.h" - -namespace net { - -SocketStreamMetrics::SocketStreamMetrics(const GURL& url) - : received_bytes_(0), - received_counts_(0), - sent_bytes_(0), - sent_counts_(0) { - ProtocolType protocol_type = PROTOCOL_UNKNOWN; - if (url.SchemeIs("ws")) - protocol_type = PROTOCOL_WEBSOCKET; - else if (url.SchemeIs("wss")) - protocol_type = PROTOCOL_WEBSOCKET_SECURE; - - UMA_HISTOGRAM_ENUMERATION("Net.SocketStream.ProtocolType", - protocol_type, NUM_PROTOCOL_TYPES); -} - -SocketStreamMetrics::~SocketStreamMetrics() {} - -void SocketStreamMetrics::OnWaitConnection() { - wait_start_time_ = base::TimeTicks::Now(); -} - -void SocketStreamMetrics::OnStartConnection() { - connect_start_time_ = base::TimeTicks::Now(); - if (!wait_start_time_.is_null()) - UMA_HISTOGRAM_TIMES("Net.SocketStream.ConnectionLatency", - connect_start_time_ - wait_start_time_); - OnCountConnectionType(ALL_CONNECTIONS); -} - -void SocketStreamMetrics::OnConnected() { - connect_establish_time_ = base::TimeTicks::Now(); - UMA_HISTOGRAM_TIMES("Net.SocketStream.ConnectionEstablish", - connect_establish_time_ - connect_start_time_); -} - -void SocketStreamMetrics::OnRead(int len) { - received_bytes_ += len; - ++received_counts_; -} - -void SocketStreamMetrics::OnWrite(int len) { - sent_bytes_ += len; - ++sent_counts_; -} - -void SocketStreamMetrics::OnClose() { - base::TimeTicks closed_time = base::TimeTicks::Now(); - if (!connect_establish_time_.is_null()) { - UMA_HISTOGRAM_LONG_TIMES("Net.SocketStream.Duration", - closed_time - connect_establish_time_); - UMA_HISTOGRAM_COUNTS("Net.SocketStream.ReceivedBytes", - received_bytes_); - UMA_HISTOGRAM_COUNTS("Net.SocketStream.ReceivedCounts", - received_counts_); - UMA_HISTOGRAM_COUNTS("Net.SocketStream.SentBytes", - sent_bytes_); - UMA_HISTOGRAM_COUNTS("Net.SocketStream.SentCounts", - sent_counts_); - } -} - -void SocketStreamMetrics::OnCountConnectionType(ConnectionType type) { - UMA_HISTOGRAM_ENUMERATION("Net.SocketStream.ConnectionType", type, - NUM_CONNECTION_TYPES); -} - -void SocketStreamMetrics::OnCountWireProtocolType(WireProtocolType type) { - UMA_HISTOGRAM_ENUMERATION("Net.SocketStream.WireProtocolType", type, - NUM_WIRE_PROTOCOL_TYPES); -} - -} // namespace net
diff --git a/net/socket_stream/socket_stream_metrics.h b/net/socket_stream/socket_stream_metrics.h deleted file mode 100644 index 0040a9a..0000000 --- a/net/socket_stream/socket_stream_metrics.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (c) 2011 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. -// -// Collect metrics of SocketStream usage. -// TODO(ukai): collect WebSocket specific metrics (e.g. handshake time, etc). - -#ifndef NET_SOCKET_STREAM_SOCKET_STREAM_METRICS_H_ -#define NET_SOCKET_STREAM_SOCKET_STREAM_METRICS_H_ - -#include "base/basictypes.h" -#include "base/time/time.h" -#include "net/base/net_export.h" - -class GURL; - -namespace net { - -class NET_EXPORT_PRIVATE SocketStreamMetrics { - public: - enum ProtocolType { - PROTOCOL_UNKNOWN, - PROTOCOL_WEBSOCKET, - PROTOCOL_WEBSOCKET_SECURE, - NUM_PROTOCOL_TYPES, - }; - - enum ConnectionType { - CONNECTION_NONE, - ALL_CONNECTIONS, - TUNNEL_CONNECTION, - SOCKS_CONNECTION, - SSL_CONNECTION, - SECURE_PROXY_CONNECTION, - NUM_CONNECTION_TYPES, - }; - - enum WireProtocolType { - WIRE_PROTOCOL_WEBSOCKET, - WIRE_PROTOCOL_SPDY, - NUM_WIRE_PROTOCOL_TYPES, - }; - - explicit SocketStreamMetrics(const GURL& url); - ~SocketStreamMetrics(); - - void OnWaitConnection(); - void OnStartConnection(); - void OnConnected(); - void OnRead(int len); - void OnWrite(int len); - void OnClose(); - void OnCountConnectionType(ConnectionType type); - void OnCountWireProtocolType(WireProtocolType type); - - private: - base::TimeTicks wait_start_time_; - base::TimeTicks connect_start_time_; - base::TimeTicks connect_establish_time_; - int received_bytes_; - int received_counts_; - int sent_bytes_; - int sent_counts_; - - DISALLOW_COPY_AND_ASSIGN(SocketStreamMetrics); -}; - -} // namespace net - -#endif // NET_SOCKET_STREAM_SOCKET_STREAM_METRICS_H_
diff --git a/net/socket_stream/socket_stream_metrics_unittest.cc b/net/socket_stream/socket_stream_metrics_unittest.cc deleted file mode 100644 index 219e692..0000000 --- a/net/socket_stream/socket_stream_metrics_unittest.cc +++ /dev/null
@@ -1,221 +0,0 @@ -// Copyright (c) 2012 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 "net/socket_stream/socket_stream_metrics.h" - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/statistics_recorder.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#include "url/gurl.h" - -using base::Histogram; -using base::HistogramBase; -using base::HistogramSamples; -using base::StatisticsRecorder; - -namespace net { - -TEST(SocketStreamMetricsTest, ProtocolType) { - // First we'll preserve the original values. We need to do this - // as histograms can get affected by other tests. In particular, - // SocketStreamTest and WebSocketTest can affect the histograms. - scoped_ptr<HistogramSamples> original; - HistogramBase* histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ProtocolType"); - if (histogram) { - original = histogram->SnapshotSamples(); - } - - SocketStreamMetrics unknown(GURL("unknown://www.example.com/")); - SocketStreamMetrics ws1(GURL("ws://www.example.com/")); - SocketStreamMetrics ws2(GURL("ws://www.example.com/")); - SocketStreamMetrics wss1(GURL("wss://www.example.com/")); - SocketStreamMetrics wss2(GURL("wss://www.example.com/")); - SocketStreamMetrics wss3(GURL("wss://www.example.com/")); - - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ProtocolType"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - - scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples()); - if (original.get()) { - samples->Subtract(*original); // Cancel the original values. - } - EXPECT_EQ(1, samples->GetCount(SocketStreamMetrics::PROTOCOL_UNKNOWN)); - EXPECT_EQ(2, samples->GetCount(SocketStreamMetrics::PROTOCOL_WEBSOCKET)); - EXPECT_EQ(3, - samples->GetCount(SocketStreamMetrics::PROTOCOL_WEBSOCKET_SECURE)); -} - -TEST(SocketStreamMetricsTest, ConnectionType) { - // First we'll preserve the original values. - scoped_ptr<HistogramSamples> original; - HistogramBase* histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ConnectionType"); - if (histogram) { - original = histogram->SnapshotSamples(); - } - - SocketStreamMetrics metrics(GURL("ws://www.example.com/")); - for (int i = 0; i < 1; ++i) - metrics.OnStartConnection(); - for (int i = 0; i < 2; ++i) - metrics.OnCountConnectionType(SocketStreamMetrics::TUNNEL_CONNECTION); - for (int i = 0; i < 3; ++i) - metrics.OnCountConnectionType(SocketStreamMetrics::SOCKS_CONNECTION); - for (int i = 0; i < 4; ++i) - metrics.OnCountConnectionType(SocketStreamMetrics::SSL_CONNECTION); - - - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ConnectionType"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - - scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples()); - if (original.get()) { - samples->Subtract(*original); // Cancel the original values. - } - EXPECT_EQ(1, samples->GetCount(SocketStreamMetrics::ALL_CONNECTIONS)); - EXPECT_EQ(2, samples->GetCount(SocketStreamMetrics::TUNNEL_CONNECTION)); - EXPECT_EQ(3, samples->GetCount(SocketStreamMetrics::SOCKS_CONNECTION)); - EXPECT_EQ(4, samples->GetCount(SocketStreamMetrics::SSL_CONNECTION)); -} - -TEST(SocketStreamMetricsTest, WireProtocolType) { - // First we'll preserve the original values. - scoped_ptr<HistogramSamples> original; - HistogramBase* histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.WireProtocolType"); - if (histogram) { - original = histogram->SnapshotSamples(); - } - - SocketStreamMetrics metrics(GURL("ws://www.example.com/")); - for (int i = 0; i < 3; ++i) - metrics.OnCountWireProtocolType( - SocketStreamMetrics::WIRE_PROTOCOL_WEBSOCKET); - for (int i = 0; i < 7; ++i) - metrics.OnCountWireProtocolType(SocketStreamMetrics::WIRE_PROTOCOL_SPDY); - - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.WireProtocolType"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - - scoped_ptr<HistogramSamples> samples(histogram->SnapshotSamples()); - if (original.get()) { - samples->Subtract(*original); // Cancel the original values. - } - EXPECT_EQ(3, samples->GetCount(SocketStreamMetrics::WIRE_PROTOCOL_WEBSOCKET)); - EXPECT_EQ(7, samples->GetCount(SocketStreamMetrics::WIRE_PROTOCOL_SPDY)); -} - -TEST(SocketStreamMetricsTest, OtherNumbers) { - // First we'll preserve the original values. - int64 original_received_bytes = 0; - int64 original_received_counts = 0; - int64 original_sent_bytes = 0; - int64 original_sent_counts = 0; - - scoped_ptr<HistogramSamples> original; - - HistogramBase* histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedBytes"); - if (histogram) { - original = histogram->SnapshotSamples(); - original_received_bytes = original->sum(); - } - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedCounts"); - if (histogram) { - original = histogram->SnapshotSamples(); - original_received_counts = original->sum(); - } - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.SentBytes"); - if (histogram) { - original = histogram->SnapshotSamples(); - original_sent_bytes = original->sum(); - } - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.SentCounts"); - if (histogram) { - original = histogram->SnapshotSamples(); - original_sent_counts = original->sum(); - } - - SocketStreamMetrics metrics(GURL("ws://www.example.com/")); - metrics.OnWaitConnection(); - metrics.OnStartConnection(); - metrics.OnConnected(); - metrics.OnRead(1); - metrics.OnRead(10); - metrics.OnWrite(2); - metrics.OnWrite(20); - metrics.OnWrite(200); - metrics.OnClose(); - - scoped_ptr<HistogramSamples> samples; - - // ConnectionLatency. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ConnectionLatency"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - // We don't check the contents of the histogram as it's time sensitive. - - // ConnectionEstablish. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ConnectionEstablish"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - // We don't check the contents of the histogram as it's time sensitive. - - // Duration. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.Duration"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - // We don't check the contents of the histogram as it's time sensitive. - - // ReceivedBytes. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedBytes"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - samples = histogram->SnapshotSamples(); - EXPECT_EQ(11, samples->sum() - original_received_bytes); // 11 bytes read. - - // ReceivedCounts. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.ReceivedCounts"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - samples = histogram->SnapshotSamples(); - EXPECT_EQ(2, samples->sum() - original_received_counts); // 2 read requests. - - // SentBytes. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.SentBytes"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - samples = histogram->SnapshotSamples(); - EXPECT_EQ(222, samples->sum() - original_sent_bytes); // 222 bytes sent. - - // SentCounts. - histogram = - StatisticsRecorder::FindHistogram("Net.SocketStream.SentCounts"); - ASSERT_TRUE(histogram != NULL); - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histogram->flags()); - samples = histogram->SnapshotSamples(); - EXPECT_EQ(3, samples->sum() - original_sent_counts); // 3 write requests. -} - -} // namespace net
diff --git a/net/socket_stream/socket_stream_unittest.cc b/net/socket_stream/socket_stream_unittest.cc deleted file mode 100644 index b5ee002..0000000 --- a/net/socket_stream/socket_stream_unittest.cc +++ /dev/null
@@ -1,1041 +0,0 @@ -// Copyright (c) 2012 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 "net/socket_stream/socket_stream.h" - -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/auth.h" -#include "net/base/net_log.h" -#include "net/base/net_log_unittest.h" -#include "net/base/test_completion_callback.h" -#include "net/dns/mock_host_resolver.h" -#include "net/http/http_network_session.h" -#include "net/proxy/proxy_service.h" -#include "net/socket/socket_test_util.h" -#include "net/url_request/url_request_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -using base::ASCIIToUTF16; - -namespace net { - -namespace { - -struct SocketStreamEvent { - enum EventType { - EVENT_START_OPEN_CONNECTION, EVENT_CONNECTED, EVENT_SENT_DATA, - EVENT_RECEIVED_DATA, EVENT_CLOSE, EVENT_AUTH_REQUIRED, EVENT_ERROR, - }; - - SocketStreamEvent(EventType type, - SocketStream* socket_stream, - int num, - const std::string& str, - AuthChallengeInfo* auth_challenge_info, - int error) - : event_type(type), socket(socket_stream), number(num), data(str), - auth_info(auth_challenge_info), error_code(error) {} - - EventType event_type; - SocketStream* socket; - int number; - std::string data; - scoped_refptr<AuthChallengeInfo> auth_info; - int error_code; -}; - -class SocketStreamEventRecorder : public SocketStream::Delegate { - public: - // |callback| will be run when the OnClose() or OnError() method is called. - // For OnClose(), |callback| is called with OK. For OnError(), it's called - // with the error code. - explicit SocketStreamEventRecorder(const CompletionCallback& callback) - : callback_(callback) {} - ~SocketStreamEventRecorder() override {} - - void SetOnStartOpenConnection( - const base::Callback<int(SocketStreamEvent*)>& callback) { - on_start_open_connection_ = callback; - } - void SetOnConnected( - const base::Callback<void(SocketStreamEvent*)>& callback) { - on_connected_ = callback; - } - void SetOnSentData( - const base::Callback<void(SocketStreamEvent*)>& callback) { - on_sent_data_ = callback; - } - void SetOnReceivedData( - const base::Callback<void(SocketStreamEvent*)>& callback) { - on_received_data_ = callback; - } - void SetOnClose(const base::Callback<void(SocketStreamEvent*)>& callback) { - on_close_ = callback; - } - void SetOnAuthRequired( - const base::Callback<void(SocketStreamEvent*)>& callback) { - on_auth_required_ = callback; - } - void SetOnError(const base::Callback<void(SocketStreamEvent*)>& callback) { - on_error_ = callback; - } - - int OnStartOpenConnection(SocketStream* socket, - const CompletionCallback& callback) override { - connection_callback_ = callback; - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - socket, 0, std::string(), NULL, OK)); - if (!on_start_open_connection_.is_null()) - return on_start_open_connection_.Run(&events_.back()); - return OK; - } - void OnConnected(SocketStream* socket, - int num_pending_send_allowed) override { - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_CONNECTED, - socket, num_pending_send_allowed, std::string(), - NULL, OK)); - if (!on_connected_.is_null()) - on_connected_.Run(&events_.back()); - } - void OnSentData(SocketStream* socket, int amount_sent) override { - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_SENT_DATA, socket, - amount_sent, std::string(), NULL, OK)); - if (!on_sent_data_.is_null()) - on_sent_data_.Run(&events_.back()); - } - void OnReceivedData(SocketStream* socket, - const char* data, - int len) override { - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_RECEIVED_DATA, socket, len, - std::string(data, len), NULL, OK)); - if (!on_received_data_.is_null()) - on_received_data_.Run(&events_.back()); - } - void OnClose(SocketStream* socket) override { - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_CLOSE, socket, 0, - std::string(), NULL, OK)); - if (!on_close_.is_null()) - on_close_.Run(&events_.back()); - if (!callback_.is_null()) - callback_.Run(OK); - } - void OnAuthRequired(SocketStream* socket, - AuthChallengeInfo* auth_info) override { - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_AUTH_REQUIRED, socket, 0, - std::string(), auth_info, OK)); - if (!on_auth_required_.is_null()) - on_auth_required_.Run(&events_.back()); - } - void OnError(const SocketStream* socket, int error) override { - events_.push_back( - SocketStreamEvent(SocketStreamEvent::EVENT_ERROR, NULL, 0, - std::string(), NULL, error)); - if (!on_error_.is_null()) - on_error_.Run(&events_.back()); - if (!callback_.is_null()) - callback_.Run(error); - } - - void DoClose(SocketStreamEvent* event) { - event->socket->Close(); - } - void DoRestartWithAuth(SocketStreamEvent* event) { - VLOG(1) << "RestartWithAuth username=" << credentials_.username() - << " password=" << credentials_.password(); - event->socket->RestartWithAuth(credentials_); - } - void SetAuthInfo(const AuthCredentials& credentials) { - credentials_ = credentials; - } - // Wakes up the SocketStream waiting for completion of OnStartOpenConnection() - // of its delegate. - void CompleteConnection(int result) { - connection_callback_.Run(result); - } - - const std::vector<SocketStreamEvent>& GetSeenEvents() const { - return events_; - } - - private: - std::vector<SocketStreamEvent> events_; - base::Callback<int(SocketStreamEvent*)> on_start_open_connection_; - base::Callback<void(SocketStreamEvent*)> on_connected_; - base::Callback<void(SocketStreamEvent*)> on_sent_data_; - base::Callback<void(SocketStreamEvent*)> on_received_data_; - base::Callback<void(SocketStreamEvent*)> on_close_; - base::Callback<void(SocketStreamEvent*)> on_auth_required_; - base::Callback<void(SocketStreamEvent*)> on_error_; - const CompletionCallback callback_; - CompletionCallback connection_callback_; - AuthCredentials credentials_; - - DISALLOW_COPY_AND_ASSIGN(SocketStreamEventRecorder); -}; - -// This is used for the test OnErrorDetachDelegate. -class SelfDeletingDelegate : public SocketStream::Delegate { - public: - // |callback| must cause the test message loop to exit when called. - explicit SelfDeletingDelegate(const CompletionCallback& callback) - : socket_stream_(), callback_(callback) {} - - ~SelfDeletingDelegate() override {} - - // Call DetachDelegate(), delete |this|, then run the callback. - void OnError(const SocketStream* socket, int error) override { - // callback_ will be deleted when we delete |this|, so copy it to call it - // afterwards. - CompletionCallback callback = callback_; - socket_stream_->DetachDelegate(); - delete this; - callback.Run(OK); - } - - // This can't be passed in the constructor because this object needs to be - // created before SocketStream. - void set_socket_stream(const scoped_refptr<SocketStream>& socket_stream) { - socket_stream_ = socket_stream; - EXPECT_EQ(socket_stream_->delegate(), this); - } - - void OnConnected(SocketStream* socket, - int max_pending_send_allowed) override { - ADD_FAILURE() << "OnConnected() should not be called"; - } - void OnSentData(SocketStream* socket, int amount_sent) override { - ADD_FAILURE() << "OnSentData() should not be called"; - } - void OnReceivedData(SocketStream* socket, - const char* data, - int len) override { - ADD_FAILURE() << "OnReceivedData() should not be called"; - } - void OnClose(SocketStream* socket) override { - ADD_FAILURE() << "OnClose() should not be called"; - } - - private: - scoped_refptr<SocketStream> socket_stream_; - const CompletionCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(SelfDeletingDelegate); -}; - -class TestURLRequestContextWithProxy : public TestURLRequestContext { - public: - explicit TestURLRequestContextWithProxy(const std::string& proxy) - : TestURLRequestContext(true) { - context_storage_.set_proxy_service(ProxyService::CreateFixed(proxy)); - Init(); - } - ~TestURLRequestContextWithProxy() override {} -}; - -class TestSocketStreamNetworkDelegate : public TestNetworkDelegate { - public: - TestSocketStreamNetworkDelegate() - : before_connect_result_(OK) {} - ~TestSocketStreamNetworkDelegate() override {} - - int OnBeforeSocketStreamConnect(SocketStream* stream, - const CompletionCallback& callback) override { - return before_connect_result_; - } - - void SetBeforeConnectResult(int result) { - before_connect_result_ = result; - } - - private: - int before_connect_result_; -}; - -} // namespace - -class SocketStreamTest : public PlatformTest { - public: - ~SocketStreamTest() override {} - void SetUp() override { - mock_socket_factory_.reset(); - handshake_request_ = kWebSocketHandshakeRequest; - handshake_response_ = kWebSocketHandshakeResponse; - } - void TearDown() override { mock_socket_factory_.reset(); } - - virtual void SetWebSocketHandshakeMessage( - const char* request, const char* response) { - handshake_request_ = request; - handshake_response_ = response; - } - virtual void AddWebSocketMessage(const std::string& message) { - messages_.push_back(message); - } - - virtual MockClientSocketFactory* GetMockClientSocketFactory() { - mock_socket_factory_.reset(new MockClientSocketFactory); - return mock_socket_factory_.get(); - } - - // Functions for SocketStreamEventRecorder to handle calls to the - // SocketStream::Delegate methods from the SocketStream. - - virtual void DoSendWebSocketHandshake(SocketStreamEvent* event) { - event->socket->SendData( - handshake_request_.data(), handshake_request_.size()); - } - - virtual void DoCloseFlushPendingWriteTest(SocketStreamEvent* event) { - // handshake response received. - for (size_t i = 0; i < messages_.size(); i++) { - std::vector<char> frame; - frame.push_back('\0'); - frame.insert(frame.end(), messages_[i].begin(), messages_[i].end()); - frame.push_back('\xff'); - EXPECT_TRUE(event->socket->SendData(&frame[0], frame.size())); - } - // Actual StreamSocket close must happen after all frames queued by - // SendData above are sent out. - event->socket->Close(); - } - - virtual void DoCloseFlushPendingWriteTestWithSetContextNull( - SocketStreamEvent* event) { - event->socket->DetachContext(); - // handshake response received. - for (size_t i = 0; i < messages_.size(); i++) { - std::vector<char> frame; - frame.push_back('\0'); - frame.insert(frame.end(), messages_[i].begin(), messages_[i].end()); - frame.push_back('\xff'); - EXPECT_TRUE(event->socket->SendData(&frame[0], frame.size())); - } - // Actual StreamSocket close must happen after all frames queued by - // SendData above are sent out. - event->socket->Close(); - } - - virtual void DoFailByTooBigDataAndClose(SocketStreamEvent* event) { - std::string frame(event->number + 1, 0x00); - VLOG(1) << event->number; - EXPECT_FALSE(event->socket->SendData(&frame[0], frame.size())); - event->socket->Close(); - } - - virtual int DoSwitchToSpdyTest(SocketStreamEvent* event) { - return ERR_PROTOCOL_SWITCHED; - } - - // Notifies |io_test_callback_| of that this method is called, and keeps the - // SocketStream waiting. - virtual int DoIOPending(SocketStreamEvent* event) { - io_test_callback_.callback().Run(OK); - return ERR_IO_PENDING; - } - - static const char kWebSocketHandshakeRequest[]; - static const char kWebSocketHandshakeResponse[]; - - protected: - TestCompletionCallback io_test_callback_; - - private: - std::string handshake_request_; - std::string handshake_response_; - std::vector<std::string> messages_; - - scoped_ptr<MockClientSocketFactory> mock_socket_factory_; -}; - -const char SocketStreamTest::kWebSocketHandshakeRequest[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Upgrade: WebSocket\r\n" - "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" - "Origin: http://example.com\r\n" - "\r\n" - "^n:ds[4U"; - -const char SocketStreamTest::kWebSocketHandshakeResponse[] = - "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Origin: http://example.com\r\n" - "Sec-WebSocket-Location: ws://example.com/demo\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "\r\n" - "8jKS'y:G*Co,Wxa-"; - -TEST_F(SocketStreamTest, CloseFlushPendingWrite) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind( - &SocketStreamTest::DoSendWebSocketHandshake, base::Unretained(this))); - delegate->SetOnReceivedData(base::Bind( - &SocketStreamTest::DoCloseFlushPendingWriteTest, - base::Unretained(this))); - - TestURLRequestContext context; - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - MockWrite data_writes[] = { - MockWrite(SocketStreamTest::kWebSocketHandshakeRequest), - MockWrite(ASYNC, "\0message1\xff", 10), - MockWrite(ASYNC, "\0message2\xff", 10) - }; - MockRead data_reads[] = { - MockRead(SocketStreamTest::kWebSocketHandshakeResponse), - // Server doesn't close the connection after handshake. - MockRead(ASYNC, ERR_IO_PENDING) - }; - AddWebSocketMessage("message1"); - AddWebSocketMessage("message2"); - - DelayedSocketData data_provider( - 1, data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - - MockClientSocketFactory* mock_socket_factory = - GetMockClientSocketFactory(); - mock_socket_factory->AddSocketDataProvider(&data_provider); - - socket_stream->SetClientSocketFactory(mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - EXPECT_TRUE(data_provider.at_read_eof()); - EXPECT_TRUE(data_provider.at_write_eof()); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(7U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[2].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_RECEIVED_DATA, events[3].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[4].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[5].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[6].event_type); -} - -TEST_F(SocketStreamTest, ResolveFailure) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - - // Make resolver fail. - TestURLRequestContext context; - scoped_ptr<MockHostResolver> mock_host_resolver( - new MockHostResolver()); - mock_host_resolver->rules()->AddSimulatedFailure("example.com"); - context.set_host_resolver(mock_host_resolver.get()); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - // No read/write on socket is expected. - StaticSocketDataProvider data_provider(NULL, 0, NULL, 0); - MockClientSocketFactory* mock_socket_factory = - GetMockClientSocketFactory(); - mock_socket_factory->AddSocketDataProvider(&data_provider); - socket_stream->SetClientSocketFactory(mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(2U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[1].event_type); -} - -TEST_F(SocketStreamTest, ExceedMaxPendingSendAllowed) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind( - &SocketStreamTest::DoFailByTooBigDataAndClose, base::Unretained(this))); - - TestURLRequestContext context; - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - DelayedSocketData data_provider(1, NULL, 0, NULL, 0); - - MockClientSocketFactory* mock_socket_factory = - GetMockClientSocketFactory(); - mock_socket_factory->AddSocketDataProvider(&data_provider); - - socket_stream->SetClientSocketFactory(mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(4U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[2].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[3].event_type); -} - -TEST_F(SocketStreamTest, BasicAuthProxy) { - MockClientSocketFactory mock_socket_factory; - MockWrite data_writes1[] = { - MockWrite("CONNECT example.com:80 HTTP/1.1\r\n" - "Host: example.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - }; - MockRead data_reads1[] = { - MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"), - MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), - MockRead("\r\n"), - }; - StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), - data_writes1, arraysize(data_writes1)); - mock_socket_factory.AddSocketDataProvider(&data1); - - MockWrite data_writes2[] = { - MockWrite("CONNECT example.com:80 HTTP/1.1\r\n" - "Host: example.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), - }; - MockRead data_reads2[] = { - MockRead("HTTP/1.1 200 Connection Established\r\n"), - MockRead("Proxy-agent: Apache/2.2.8\r\n"), - MockRead("\r\n"), - // SocketStream::DoClose is run asynchronously. Socket can be read after - // "\r\n". We have to give ERR_IO_PENDING to SocketStream then to indicate - // server doesn't close the connection. - MockRead(ASYNC, ERR_IO_PENDING) - }; - StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), - data_writes2, arraysize(data_writes2)); - mock_socket_factory.AddSocketDataProvider(&data2); - - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose, - base::Unretained(delegate.get()))); - delegate->SetAuthInfo(AuthCredentials(ASCIIToUTF16("foo"), - ASCIIToUTF16("bar"))); - delegate->SetOnAuthRequired(base::Bind( - &SocketStreamEventRecorder::DoRestartWithAuth, - base::Unretained(delegate.get()))); - - TestURLRequestContextWithProxy context("myproxy:70"); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->SetClientSocketFactory(&mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(5U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_AUTH_REQUIRED, events[1].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[2].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[3].event_type); - EXPECT_EQ(ERR_ABORTED, events[3].error_code); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[4].event_type); - - // TODO(eroman): Add back NetLogTest here... -} - -TEST_F(SocketStreamTest, BasicAuthProxyWithAuthCache) { - MockClientSocketFactory mock_socket_factory; - MockWrite data_writes[] = { - // WebSocket(SocketStream) always uses CONNECT when it is configured to use - // proxy so the port may not be 443. - MockWrite("CONNECT example.com:80 HTTP/1.1\r\n" - "Host: example.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), - }; - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 Connection Established\r\n"), - MockRead("Proxy-agent: Apache/2.2.8\r\n"), - MockRead("\r\n"), - MockRead(ASYNC, ERR_IO_PENDING) - }; - StaticSocketDataProvider data(data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - mock_socket_factory.AddSocketDataProvider(&data); - - TestCompletionCallback test_callback; - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose, - base::Unretained(delegate.get()))); - - TestURLRequestContextWithProxy context("myproxy:70"); - HttpAuthCache* auth_cache = - context.http_transaction_factory()->GetSession()->http_auth_cache(); - auth_cache->Add(GURL("http://myproxy:70"), - "MyRealm1", - HttpAuth::AUTH_SCHEME_BASIC, - "Basic realm=MyRealm1", - AuthCredentials(ASCIIToUTF16("foo"), - ASCIIToUTF16("bar")), - "/"); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->SetClientSocketFactory(&mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(4U, events.size()); - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(ERR_ABORTED, events[2].error_code); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[3].event_type); -} - -TEST_F(SocketStreamTest, WSSBasicAuthProxyWithAuthCache) { - MockClientSocketFactory mock_socket_factory; - MockWrite data_writes1[] = { - MockWrite("CONNECT example.com:443 HTTP/1.1\r\n" - "Host: example.com\r\n" - "Proxy-Connection: keep-alive\r\n" - "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), - }; - MockRead data_reads1[] = { - MockRead("HTTP/1.1 200 Connection Established\r\n"), - MockRead("Proxy-agent: Apache/2.2.8\r\n"), - MockRead("\r\n"), - MockRead(ASYNC, ERR_IO_PENDING) - }; - StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), - data_writes1, arraysize(data_writes1)); - mock_socket_factory.AddSocketDataProvider(&data1); - - SSLSocketDataProvider data2(ASYNC, OK); - mock_socket_factory.AddSSLSocketDataProvider(&data2); - - TestCompletionCallback test_callback; - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose, - base::Unretained(delegate.get()))); - - TestURLRequestContextWithProxy context("myproxy:70"); - HttpAuthCache* auth_cache = - context.http_transaction_factory()->GetSession()->http_auth_cache(); - auth_cache->Add(GURL("http://myproxy:70"), - "MyRealm1", - HttpAuth::AUTH_SCHEME_BASIC, - "Basic realm=MyRealm1", - AuthCredentials(ASCIIToUTF16("foo"), - ASCIIToUTF16("bar")), - "/"); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("wss://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->SetClientSocketFactory(&mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(4U, events.size()); - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(ERR_ABORTED, events[2].error_code); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[3].event_type); -} - -TEST_F(SocketStreamTest, IOPending) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnStartOpenConnection(base::Bind( - &SocketStreamTest::DoIOPending, base::Unretained(this))); - delegate->SetOnConnected(base::Bind( - &SocketStreamTest::DoSendWebSocketHandshake, base::Unretained(this))); - delegate->SetOnReceivedData(base::Bind( - &SocketStreamTest::DoCloseFlushPendingWriteTest, - base::Unretained(this))); - - TestURLRequestContext context; - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - MockWrite data_writes[] = { - MockWrite(SocketStreamTest::kWebSocketHandshakeRequest), - MockWrite(ASYNC, "\0message1\xff", 10), - MockWrite(ASYNC, "\0message2\xff", 10) - }; - MockRead data_reads[] = { - MockRead(SocketStreamTest::kWebSocketHandshakeResponse), - // Server doesn't close the connection after handshake. - MockRead(ASYNC, ERR_IO_PENDING) - }; - AddWebSocketMessage("message1"); - AddWebSocketMessage("message2"); - - DelayedSocketData data_provider( - 1, data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - - MockClientSocketFactory* mock_socket_factory = - GetMockClientSocketFactory(); - mock_socket_factory->AddSocketDataProvider(&data_provider); - - socket_stream->SetClientSocketFactory(mock_socket_factory); - - socket_stream->Connect(); - io_test_callback_.WaitForResult(); - EXPECT_EQ(SocketStream::STATE_RESOLVE_PROTOCOL_COMPLETE, - socket_stream->next_state_); - delegate->CompleteConnection(OK); - - EXPECT_EQ(OK, test_callback.WaitForResult()); - - EXPECT_TRUE(data_provider.at_read_eof()); - EXPECT_TRUE(data_provider.at_write_eof()); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(7U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[2].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_RECEIVED_DATA, events[3].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[4].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[5].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[6].event_type); -} - -TEST_F(SocketStreamTest, SwitchToSpdy) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnStartOpenConnection(base::Bind( - &SocketStreamTest::DoSwitchToSpdyTest, base::Unretained(this))); - - TestURLRequestContext context; - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->Connect(); - - EXPECT_EQ(ERR_PROTOCOL_SWITCHED, test_callback.WaitForResult()); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(2U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[1].event_type); - EXPECT_EQ(ERR_PROTOCOL_SWITCHED, events[1].error_code); -} - -TEST_F(SocketStreamTest, SwitchAfterPending) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnStartOpenConnection(base::Bind( - &SocketStreamTest::DoIOPending, base::Unretained(this))); - - TestURLRequestContext context; - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->Connect(); - io_test_callback_.WaitForResult(); - - EXPECT_EQ(SocketStream::STATE_RESOLVE_PROTOCOL_COMPLETE, - socket_stream->next_state_); - delegate->CompleteConnection(ERR_PROTOCOL_SWITCHED); - - EXPECT_EQ(ERR_PROTOCOL_SWITCHED, test_callback.WaitForResult()); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(2U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[1].event_type); - EXPECT_EQ(ERR_PROTOCOL_SWITCHED, events[1].error_code); -} - -// Test a connection though a secure proxy. -TEST_F(SocketStreamTest, SecureProxyConnectError) { - MockClientSocketFactory mock_socket_factory; - MockWrite data_writes[] = { - MockWrite("CONNECT example.com:80 HTTP/1.1\r\n" - "Host: example.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n") - }; - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 Connection Established\r\n"), - MockRead("Proxy-agent: Apache/2.2.8\r\n"), - MockRead("\r\n"), - // SocketStream::DoClose is run asynchronously. Socket can be read after - // "\r\n". We have to give ERR_IO_PENDING to SocketStream then to indicate - // server doesn't close the connection. - MockRead(ASYNC, ERR_IO_PENDING) - }; - StaticSocketDataProvider data(data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - mock_socket_factory.AddSocketDataProvider(&data); - SSLSocketDataProvider ssl(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR); - mock_socket_factory.AddSSLSocketDataProvider(&ssl); - - TestCompletionCallback test_callback; - TestURLRequestContextWithProxy context("https://myproxy:70"); - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose, - base::Unretained(delegate.get()))); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->SetClientSocketFactory(&mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(3U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[1].event_type); - EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, events[1].error_code); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[2].event_type); -} - -// Test a connection though a secure proxy. -TEST_F(SocketStreamTest, SecureProxyConnect) { - MockClientSocketFactory mock_socket_factory; - MockWrite data_writes[] = { - MockWrite("CONNECT example.com:80 HTTP/1.1\r\n" - "Host: example.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n") - }; - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 Connection Established\r\n"), - MockRead("Proxy-agent: Apache/2.2.8\r\n"), - MockRead("\r\n"), - // SocketStream::DoClose is run asynchronously. Socket can be read after - // "\r\n". We have to give ERR_IO_PENDING to SocketStream then to indicate - // server doesn't close the connection. - MockRead(ASYNC, ERR_IO_PENDING) - }; - StaticSocketDataProvider data(data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - mock_socket_factory.AddSocketDataProvider(&data); - SSLSocketDataProvider ssl(SYNCHRONOUS, OK); - mock_socket_factory.AddSSLSocketDataProvider(&ssl); - - TestCompletionCallback test_callback; - TestURLRequestContextWithProxy context("https://myproxy:70"); - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - delegate->SetOnConnected(base::Bind(&SocketStreamEventRecorder::DoClose, - base::Unretained(delegate.get()))); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->SetClientSocketFactory(&mock_socket_factory); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(4U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[2].event_type); - EXPECT_EQ(ERR_ABORTED, events[2].error_code); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[3].event_type); -} - -TEST_F(SocketStreamTest, BeforeConnectFailed) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - - TestURLRequestContext context; - TestSocketStreamNetworkDelegate network_delegate; - network_delegate.SetBeforeConnectResult(ERR_ACCESS_DENIED); - context.set_network_delegate(&network_delegate); - - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - - socket_stream->Connect(); - - test_callback.WaitForResult(); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(2U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_ERROR, events[0].event_type); - EXPECT_EQ(ERR_ACCESS_DENIED, events[0].error_code); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[1].event_type); -} - -// Check that a connect failure, followed by the delegate calling DetachDelegate -// and deleting itself in the OnError callback, is handled correctly. -TEST_F(SocketStreamTest, OnErrorDetachDelegate) { - MockClientSocketFactory mock_socket_factory; - TestCompletionCallback test_callback; - - // SelfDeletingDelegate is self-owning; we just need a pointer to it to - // connect it and the SocketStream. - SelfDeletingDelegate* delegate = - new SelfDeletingDelegate(test_callback.callback()); - MockConnect mock_connect(ASYNC, ERR_CONNECTION_REFUSED); - StaticSocketDataProvider data; - data.set_connect_data(mock_connect); - mock_socket_factory.AddSocketDataProvider(&data); - - TestURLRequestContext context; - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://localhost:9998/echo"), delegate, - &context, NULL)); - socket_stream->SetClientSocketFactory(&mock_socket_factory); - delegate->set_socket_stream(socket_stream); - // The delegate pointer will become invalid during the test. Set it to NULL to - // avoid holding a dangling pointer. - delegate = NULL; - - socket_stream->Connect(); - - EXPECT_EQ(OK, test_callback.WaitForResult()); -} - -TEST_F(SocketStreamTest, NullContextSocketStreamShouldNotCrash) { - TestCompletionCallback test_callback; - - scoped_ptr<SocketStreamEventRecorder> delegate( - new SocketStreamEventRecorder(test_callback.callback())); - TestURLRequestContext context; - scoped_refptr<SocketStream> socket_stream( - new SocketStream(GURL("ws://example.com/demo"), delegate.get(), - &context, NULL)); - delegate->SetOnStartOpenConnection(base::Bind( - &SocketStreamTest::DoIOPending, base::Unretained(this))); - delegate->SetOnConnected(base::Bind( - &SocketStreamTest::DoSendWebSocketHandshake, base::Unretained(this))); - delegate->SetOnReceivedData(base::Bind( - &SocketStreamTest::DoCloseFlushPendingWriteTestWithSetContextNull, - base::Unretained(this))); - - MockWrite data_writes[] = { - MockWrite(SocketStreamTest::kWebSocketHandshakeRequest), - }; - MockRead data_reads[] = { - MockRead(SocketStreamTest::kWebSocketHandshakeResponse), - }; - AddWebSocketMessage("message1"); - AddWebSocketMessage("message2"); - - DelayedSocketData data_provider( - 1, data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - - MockClientSocketFactory* mock_socket_factory = GetMockClientSocketFactory(); - mock_socket_factory->AddSocketDataProvider(&data_provider); - socket_stream->SetClientSocketFactory(mock_socket_factory); - - socket_stream->Connect(); - io_test_callback_.WaitForResult(); - delegate->CompleteConnection(OK); - EXPECT_EQ(OK, test_callback.WaitForResult()); - - EXPECT_TRUE(data_provider.at_read_eof()); - EXPECT_TRUE(data_provider.at_write_eof()); - - const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents(); - ASSERT_EQ(5U, events.size()); - - EXPECT_EQ(SocketStreamEvent::EVENT_START_OPEN_CONNECTION, - events[0].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[2].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_RECEIVED_DATA, events[3].event_type); - EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[4].event_type); -} - -} // namespace net
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc index fdc65a9..f3fc575 100644 --- a/net/spdy/buffered_spdy_framer.cc +++ b/net/spdy/buffered_spdy_framer.cc
@@ -15,7 +15,8 @@ case kProtoSPDY3: case kProtoSPDY31: return SPDY3; - case kProtoSPDY4: + case kProtoSPDY4_14: + case kProtoSPDY4_15: return SPDY4; case kProtoUnknown: case kProtoHTTP11: @@ -76,6 +77,8 @@ } void BufferedSpdyFramer::OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, bool end) { frames_received_++; @@ -83,6 +86,10 @@ control_frame_fields_.reset(new ControlFrameFields()); control_frame_fields_->type = HEADERS; control_frame_fields_->stream_id = stream_id; + control_frame_fields_->has_priority = has_priority; + if (control_frame_fields_->has_priority) { + control_frame_fields_->priority = priority; + } control_frame_fields_->fin = fin; InitHeaderStreaming(stream_id); @@ -136,6 +143,8 @@ break; case HEADERS: visitor_->OnHeaders(control_frame_fields_->stream_id, + control_frame_fields_->has_priority, + control_frame_fields_->priority, control_frame_fields_->fin, headers); break; @@ -346,9 +355,14 @@ SpdyFrame* BufferedSpdyFramer::CreateHeaders( SpdyStreamId stream_id, SpdyControlFlags flags, + SpdyPriority priority, const SpdyHeaderBlock* headers) { SpdyHeadersIR headers_ir(stream_id); headers_ir.set_fin((flags & CONTROL_FLAG_FIN) != 0); + if (flags & HEADERS_FLAG_PRIORITY) { + headers_ir.set_has_priority(true); + headers_ir.set_priority(priority); + } headers_ir.set_name_value_block(*headers); return spdy_framer_.SerializeHeaders(headers_ir); }
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h index 02fa583..1227a42 100644 --- a/net/spdy/buffered_spdy_framer.h +++ b/net/spdy/buffered_spdy_framer.h
@@ -49,6 +49,8 @@ // Called after all the header data for HEADERS control frame is received. virtual void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, const SpdyHeaderBlock& headers) = 0; @@ -142,7 +144,11 @@ bool fin, bool unidirectional) override; void OnSynReply(SpdyStreamId stream_id, bool fin) override; - void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) override; + void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, + bool fin, + bool end) override; bool OnControlFrameHeaderData(SpdyStreamId stream_id, const char* header_data, size_t len) override; @@ -194,6 +200,7 @@ SpdyGoAwayStatus status) const; SpdyFrame* CreateHeaders(SpdyStreamId stream_id, SpdyControlFlags flags, + SpdyPriority priority, const SpdyHeaderBlock* headers); SpdyFrame* CreateWindowUpdate( SpdyStreamId stream_id, @@ -262,6 +269,7 @@ SpdyStreamId stream_id; SpdyStreamId associated_stream_id; SpdyStreamId promised_stream_id; + bool has_priority; SpdyPriority priority; uint8 credential_slot; bool fin;
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc index 6be20dd..11f8d08 100644 --- a/net/spdy/buffered_spdy_framer_unittest.cc +++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -59,6 +59,8 @@ } void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, const SpdyHeaderBlock& headers) override { header_stream_id_ = stream_id; @@ -207,7 +209,7 @@ NextProto, BufferedSpdyFramerTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); TEST_P(BufferedSpdyFramerTest, OnSetting) { SpdyFramer framer(spdy_version()); @@ -293,6 +295,7 @@ scoped_ptr<SpdyFrame> control_frame( framer.CreateHeaders(1, // stream_id CONTROL_FLAG_NONE, + 0, // priority &headers)); EXPECT_TRUE(control_frame.get() != NULL);
diff --git a/net/spdy/mock_spdy_framer_visitor.h b/net/spdy/mock_spdy_framer_visitor.h index b8467ba..480c367 100644 --- a/net/spdy/mock_spdy_framer_visitor.h +++ b/net/spdy/mock_spdy_framer_visitor.h
@@ -42,7 +42,8 @@ MOCK_METHOD0(OnSettingsEnd, void()); MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status)); - MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end)); + MOCK_METHOD5(OnHeaders, void(SpdyStreamId stream_id, bool has_priority, + SpdyPriority priority, bool fin, bool end)); MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, uint32 delta_window_size)); MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index de7a7a3..c08b597 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc
@@ -480,6 +480,10 @@ return "CONNECT_ERROR"; case RST_STREAM_ENHANCE_YOUR_CALM: return "ENHANCE_YOUR_CALM"; + case RST_STREAM_INADEQUATE_SECURITY: + return "INADEQUATE_SECURITY"; + case RST_STREAM_HTTP_1_1_REQUIRED: + return "HTTP_1_1_REQUIRED"; } return "UNKNOWN_STATUS"; } @@ -1497,33 +1501,19 @@ } DCHECK(reader.IsDoneReading()); if (debug_visitor_) { - // SPDY 4 reports HEADERS with PRIORITY as SYN_STREAM. - SpdyFrameType reported_type = current_frame_type_; - if (protocol_version() > SPDY3 && has_priority) { - reported_type = SYN_STREAM; - } debug_visitor_->OnReceiveCompressedFrame( current_frame_stream_id_, - reported_type, + current_frame_type_, current_frame_length_); } if (current_frame_type_ == SYN_REPLY) { visitor_->OnSynReply( current_frame_stream_id_, (current_frame_flags_ & CONTROL_FLAG_FIN) != 0); - } else if (protocol_version() > SPDY3 && - current_frame_flags_ & HEADERS_FLAG_PRIORITY) { - // SPDY 4+ is missing SYN_STREAM. Simulate it so that API changes - // can be made independent of wire changes. - visitor_->OnSynStream( - current_frame_stream_id_, - 0, // associated_to_stream_id - priority, - current_frame_flags_ & CONTROL_FLAG_FIN, - false); // unidirectional } else { visitor_->OnHeaders( current_frame_stream_id_, + (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0, priority, (current_frame_flags_ & CONTROL_FLAG_FIN) != 0, expect_continuation_ == 0); }
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 29cab72..dcb2911 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h
@@ -37,8 +37,6 @@ class SpdyProxyClientSocketTest; class SpdySessionTest; class SpdyStreamTest; -class SpdyWebSocketStreamTest; -class WebSocketJobTest; class SpdyFramer; class SpdyFrameBuilder; @@ -237,7 +235,11 @@ // Called when a HEADERS frame is received. // Note that header block data is not included. See // OnControlFrameHeaderData(). - virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) = 0; + virtual void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, + bool fin, + bool end) = 0; // Called when a WINDOW_UPDATE frame has been parsed. virtual void OnWindowUpdate(SpdyStreamId stream_id, @@ -612,16 +614,14 @@ TooLargeHeadersFrameUsesContinuation); FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation); - friend class net::HttpNetworkLayer; // This is temporary for the server. - friend class net::HttpNetworkTransactionTest; - friend class net::HttpProxyClientSocketPoolTest; - friend class net::SpdyHttpStreamTest; - friend class net::SpdyNetworkTransactionTest; - friend class net::SpdyProxyClientSocketTest; - friend class net::SpdySessionTest; - friend class net::SpdyStreamTest; - friend class net::SpdyWebSocketStreamTest; - friend class net::WebSocketJobTest; + friend class HttpNetworkLayer; // This is temporary for the server. + friend class HttpNetworkTransactionTest; + friend class HttpProxyClientSocketPoolTest; + friend class SpdyHttpStreamTest; + friend class SpdyNetworkTransactionTest; + friend class SpdyProxyClientSocketTest; + friend class SpdySessionTest; + friend class SpdyStreamTest; friend class test::TestSpdyVisitor; private:
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 7a1c3df..2b7bc4b 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc
@@ -154,10 +154,15 @@ LOG(FATAL); } - void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) override { + void OnHeaders(SpdyStreamId stream_id, bool has_priority, + SpdyPriority priority, bool fin, bool end) override { SpdyFramer framer(version_); framer.set_enable_compression(false); SpdyHeadersIR headers(stream_id); + headers.set_has_priority(has_priority); + if (headers.has_priority()) { + headers.set_priority(priority); + } headers.set_fin(fin); scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers)); ResetBuffer(); @@ -389,7 +394,8 @@ ++goaway_count_; } - void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) override { + void OnHeaders(SpdyStreamId stream_id, bool has_priority, + SpdyPriority priority, bool fin, bool end) override { ++headers_frame_count_; InitHeaderStreaming(HEADERS, stream_id); if (fin) { @@ -1225,18 +1231,19 @@ visitor.SimulateInFramer(kV4Input, sizeof(kV4Input)); } - EXPECT_EQ(2, visitor.syn_frame_count_); EXPECT_EQ(0, visitor.syn_reply_frame_count_); - EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(24, visitor.data_bytes_); - EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(2, visitor.fin_frame_count_); if (IsSpdy4()) { + EXPECT_EQ(3, visitor.headers_frame_count_); + EXPECT_EQ(0, visitor.syn_frame_count_); base::StringPiece reset_stream = "RESETSTREAM"; EXPECT_EQ(reset_stream, visitor.fin_opaque_data_); } else { + EXPECT_EQ(1, visitor.headers_frame_count_); + EXPECT_EQ(2, visitor.syn_frame_count_); EXPECT_TRUE(visitor.fin_opaque_data_.empty()); } @@ -1339,11 +1346,12 @@ } EXPECT_EQ(0, visitor.error_count_); - EXPECT_EQ(1, visitor.syn_frame_count_); if (IsSpdy4()) { + EXPECT_EQ(0, visitor.syn_frame_count_); EXPECT_EQ(0, visitor.syn_reply_frame_count_); - EXPECT_EQ(1, visitor.headers_frame_count_); + EXPECT_EQ(2, visitor.headers_frame_count_); } else { + EXPECT_EQ(1, visitor.syn_frame_count_); EXPECT_EQ(1, visitor.syn_reply_frame_count_); EXPECT_EQ(0, visitor.headers_frame_count_); } @@ -1418,11 +1426,12 @@ } EXPECT_EQ(0, visitor.error_count_); - EXPECT_EQ(1, visitor.syn_frame_count_); if (IsSpdy4()) { + EXPECT_EQ(0, visitor.syn_frame_count_); EXPECT_EQ(0, visitor.syn_reply_frame_count_); - EXPECT_EQ(1, visitor.headers_frame_count_); + EXPECT_EQ(2, visitor.headers_frame_count_); } else { + EXPECT_EQ(1, visitor.syn_frame_count_); EXPECT_EQ(1, visitor.syn_reply_frame_count_); EXPECT_EQ(0, visitor.headers_frame_count_); } @@ -5210,13 +5219,14 @@ EXPECT_CALL(visitor, OnError(_)); } else { if (spdy_version_ > SPDY3 && flags & HEADERS_FLAG_PRIORITY) { - EXPECT_CALL(visitor, OnSynStream(57, // stream id - 0, // associated stream id - 3, // priority - flags & CONTROL_FLAG_FIN, - false)); // unidirectional + EXPECT_CALL(visitor, OnHeaders(57, // stream id + true, // has priority? + 3, // priority + flags & CONTROL_FLAG_FIN, // fin? + (flags & HEADERS_FLAG_END_HEADERS) || + !IsSpdy4())); // end headers? } else { - EXPECT_CALL(visitor, OnHeaders(57, + EXPECT_CALL(visitor, OnHeaders(57, false, 0, flags & CONTROL_FLAG_FIN, (flags & HEADERS_FLAG_END_HEADERS) || !IsSpdy4())); @@ -5391,7 +5401,7 @@ EXPECT_CALL(debug_visitor, OnSendCompressedFrame(42, HEADERS, _, _)); EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame(42, HEADERS, _)); - EXPECT_CALL(visitor, OnHeaders(42, 0, false)); + EXPECT_CALL(visitor, OnHeaders(42, false, 0, 0, false)); EXPECT_CALL(visitor, OnControlFrameHeaderData(42, _, _)) .WillRepeatedly(testing::Return(true));
diff --git a/net/spdy/spdy_headers_block_parser_test.cc b/net/spdy/spdy_headers_block_parser_test.cc index d8cbe31..dc2cbb5 100644 --- a/net/spdy/spdy_headers_block_parser_test.cc +++ b/net/spdy/spdy_headers_block_parser_test.cc
@@ -17,8 +17,6 @@ using base::IntToString; using base::StringPiece; using std::string; -using testing::ElementsAre; -using testing::ElementsAreArray; // A mock the handler class to check that we parse out the correct headers // and call the callback methods when we should.
diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc index 0e8fdb0..574b76a 100644 --- a/net/spdy/spdy_http_stream_unittest.cc +++ b/net/spdy/spdy_http_stream_unittest.cc
@@ -131,7 +131,7 @@ NextProto, SpdyHttpStreamTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); // SpdyHttpStream::GetUploadProgress() should still work even before the // stream is initialized.
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index a68615f..edb39e9 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -726,9 +726,12 @@ SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYNOSSL), SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYSSL), SpdyNetworkTransactionTestParams(kProtoSPDY31, SPDYNPN), - SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYNOSSL), - SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYSSL), - SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYNPN))); + SpdyNetworkTransactionTestParams(kProtoSPDY4_14, SPDYNOSSL), + SpdyNetworkTransactionTestParams(kProtoSPDY4_14, SPDYSSL), + SpdyNetworkTransactionTestParams(kProtoSPDY4_14, SPDYNPN), + SpdyNetworkTransactionTestParams(kProtoSPDY4_15, SPDYNOSSL), + SpdyNetworkTransactionTestParams(kProtoSPDY4_15, SPDYSSL), + SpdyNetworkTransactionTestParams(kProtoSPDY4_15, SPDYNPN))); // Verify HttpNetworkTransaction constructor. TEST_P(SpdyNetworkTransactionTest, Constructor) { @@ -3576,7 +3579,7 @@ } TEST_P(SpdyNetworkTransactionTest, GoAwayOnDecompressionFailure) { - if (GetParam().protocol < kProtoSPDY4) { + if (GetParam().protocol < kProtoSPDY4MinimumVersion) { // Decompression failures are a stream error in SPDY3 and above. return; } @@ -4433,7 +4436,8 @@ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); std::vector<MockWrite> writes; - if (GetParam().protocol == kProtoSPDY4) { + if ((GetParam().protocol >= kProtoSPDY4MinimumVersion) && + (GetParam().protocol <= kProtoSPDY4MaximumVersion)) { writes.push_back( MockWrite(ASYNC, kHttp2ConnectionHeaderPrefix, @@ -6599,7 +6603,9 @@ INSTANTIATE_TEST_CASE_P( Spdy, SpdyNetworkTransactionTLSUsageCheckTest, - ::testing::Values(SpdyNetworkTransactionTestParams(kProtoSPDY4, SPDYNPN))); + ::testing::Values( + SpdyNetworkTransactionTestParams(kProtoSPDY4_14, SPDYNPN), + SpdyNetworkTransactionTestParams(kProtoSPDY4_15, SPDYNPN))); TEST_P(SpdyNetworkTransactionTLSUsageCheckTest, TLSVersionTooOld) { scoped_ptr<SSLSocketDataProvider> ssl_provider(
diff --git a/net/spdy/spdy_protocol.cc b/net/spdy/spdy_protocol.cc index d300a15..bf33d43 100644 --- a/net/spdy/spdy_protocol.cc +++ b/net/spdy/spdy_protocol.cc
@@ -372,9 +372,9 @@ } */ - // ENHANCE_YOUR_CALM is the last valid status code. + // HTTP_1_1_REQUIRED is the last valid status code. if (rst_stream_status_field > - SerializeRstStreamStatus(version, RST_STREAM_ENHANCE_YOUR_CALM)) { + SerializeRstStreamStatus(version, RST_STREAM_HTTP_1_1_REQUIRED)) { return false; } @@ -436,6 +436,10 @@ return RST_STREAM_CONNECT_ERROR; case 11: return RST_STREAM_ENHANCE_YOUR_CALM; + case 12: + return RST_STREAM_INADEQUATE_SECURITY; + case 13: + return RST_STREAM_HTTP_1_1_REQUIRED; } break; } @@ -499,6 +503,10 @@ return 10; case RST_STREAM_ENHANCE_YOUR_CALM: return 11; + case RST_STREAM_INADEQUATE_SECURITY: + return 12; + case RST_STREAM_HTTP_1_1_REQUIRED: + return 13; default: LOG(DFATAL) << "Unhandled RST_STREAM status " << rst_stream_status; @@ -589,6 +597,8 @@ return GOAWAY_ENHANCE_YOUR_CALM; case 12: return GOAWAY_INADEQUATE_SECURITY; + case 13: + return GOAWAY_HTTP_1_1_REQUIRED; } break; } @@ -666,6 +676,7 @@ case GOAWAY_CONNECT_ERROR: case GOAWAY_ENHANCE_YOUR_CALM: case GOAWAY_INADEQUATE_SECURITY: + case GOAWAY_HTTP_1_1_REQUIRED: return 1; // PROTOCOL_ERROR. default: LOG(DFATAL) << "Serializing unhandled GOAWAY status " << status; @@ -700,6 +711,8 @@ return 11; case GOAWAY_INADEQUATE_SECURITY: return 12; + case GOAWAY_HTTP_1_1_REQUIRED: + return 13; default: LOG(DFATAL) << "Serializing unhandled GOAWAY status " << status; return -1;
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index b3f71d4..b02c44f 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h
@@ -397,7 +397,9 @@ RST_STREAM_SETTINGS_TIMEOUT = 12, RST_STREAM_CONNECT_ERROR = 13, RST_STREAM_ENHANCE_YOUR_CALM = 14, - RST_STREAM_NUM_STATUS_CODES = 15 + RST_STREAM_INADEQUATE_SECURITY = 15, + RST_STREAM_HTTP_1_1_REQUIRED = 16, + RST_STREAM_NUM_STATUS_CODES = 17 }; // Status codes for GOAWAY frames. @@ -415,7 +417,8 @@ GOAWAY_COMPRESSION_ERROR = 9, GOAWAY_CONNECT_ERROR = 10, GOAWAY_ENHANCE_YOUR_CALM = 11, - GOAWAY_INADEQUATE_SECURITY = 12 + GOAWAY_INADEQUATE_SECURITY = 12, + GOAWAY_HTTP_1_1_REQUIRED = 13 }; // A SPDY priority is a number between 0 and 7 (inclusive).
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc index 92e446e..ba5d867 100644 --- a/net/spdy/spdy_proxy_client_socket_unittest.cc +++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -143,7 +143,7 @@ NextProto, SpdyProxyClientSocketTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); SpdyProxyClientSocketTest::SpdyProxyClientSocketTest() : spdy_util_(GetParam()),
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index d9a5108..08d838a 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc
@@ -398,6 +398,10 @@ return STATUS_CODE_CONNECT_ERROR; case RST_STREAM_ENHANCE_YOUR_CALM: return STATUS_CODE_ENHANCE_YOUR_CALM; + case RST_STREAM_INADEQUATE_SECURITY: + return STATUS_CODE_INADEQUATE_SECURITY; + case RST_STREAM_HTTP_1_1_REQUIRED: + return STATUS_CODE_HTTP_1_1_REQUIRED; default: NOTREACHED(); return static_cast<SpdyProtocolErrorDetails>(-1); @@ -721,7 +725,8 @@ DCHECK_GE(protocol_, kProtoSPDYMinimumVersion); DCHECK_LE(protocol_, kProtoSPDYMaximumVersion); - if (protocol_ == kProtoSPDY4) + if ((protocol_ >= kProtoSPDY4MinimumVersion) && + (protocol_ <= kProtoSPDY4MaximumVersion)) send_connection_header_prefix_ = true; if (protocol_ >= kProtoSPDY31) { @@ -741,7 +746,7 @@ buffered_spdy_framer_->set_debug_visitor(this); UMA_HISTOGRAM_ENUMERATION( "Net.SpdyVersion2", - protocol_ - kProtoSPDYMinimumVersion, + protocol_ - kProtoSPDYHistogramOffset, kProtoSPDYMaximumVersion - kProtoSPDYMinimumVersion + 1); net_log_.AddEvent(NetLog::TYPE_SPDY_SESSION_INITIALIZED, @@ -2205,11 +2210,7 @@ const SpdyHeaderBlock& headers) { CHECK(in_io_loop_); - if (GetProtocolVersion() >= SPDY4) { - DCHECK_EQ(0u, associated_stream_id); - OnHeaders(stream_id, fin, headers); - return; - } + DCHECK_LE(GetProtocolVersion(), SPDY3); base::Time response_time = base::Time::Now(); base::TimeTicks recv_first_byte_time = time_func_(); @@ -2332,6 +2333,8 @@ } void SpdySession::OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, const SpdyHeaderBlock& headers) { CHECK(in_io_loop_); @@ -2459,8 +2462,8 @@ base::Bind(&NetLogSpdyPingCallback, unique_id, is_ack, "received")); // Send response to a PING from server. - if ((protocol_ >= kProtoSPDY4 && !is_ack) || - (protocol_ < kProtoSPDY4 && unique_id % 2 == 0)) { + if ((protocol_ >= kProtoSPDY4MinimumVersion && !is_ack) || + (protocol_ < kProtoSPDY4MinimumVersion && unique_id % 2 == 0)) { WritePingFrame(unique_id, true); return; } @@ -2746,7 +2749,8 @@ DCHECK(enable_sending_initial_data_); if (send_connection_header_prefix_) { - DCHECK_EQ(protocol_, kProtoSPDY4); + DCHECK_GE(protocol_, kProtoSPDY4MinimumVersion); + DCHECK_LE(protocol_, kProtoSPDY4MaximumVersion); scoped_ptr<SpdyFrame> connection_header_prefix_frame( new SpdyFrame(const_cast<char*>(kHttp2ConnectionHeaderPrefix), kHttp2ConnectionHeaderPrefixSize,
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index ef2d8fe..862918b 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h
@@ -105,6 +105,8 @@ STATUS_CODE_SETTINGS_TIMEOUT = 32, STATUS_CODE_CONNECT_ERROR = 33, STATUS_CODE_ENHANCE_YOUR_CALM = 34, + STATUS_CODE_INADEQUATE_SECURITY = 35, + STATUS_CODE_HTTP_1_1_REQUIRED = 36, // SpdySession errors PROTOCOL_ERROR_UNEXPECTED_PING = 22, @@ -116,7 +118,7 @@ PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION = 28, // Next free value. - NUM_SPDY_PROTOCOL_ERROR_DETAILS = 35, + NUM_SPDY_PROTOCOL_ERROR_DETAILS = 37, }; SpdyProtocolErrorDetails NET_EXPORT_PRIVATE MapFramerErrorToProtocolError(SpdyFramer::SpdyError error); @@ -129,7 +131,7 @@ // to be updated with new values, as do the mapping functions above. COMPILE_ASSERT(12 == SpdyFramer::LAST_ERROR, SpdyProtocolErrorDetails_SpdyErrors_mismatch); -COMPILE_ASSERT(15 == RST_STREAM_NUM_STATUS_CODES, +COMPILE_ASSERT(17 == RST_STREAM_NUM_STATUS_CODES, SpdyProtocolErrorDetails_RstStreamStatus_mismatch); // Splits pushed |headers| into request and response parts. Request headers are @@ -844,6 +846,8 @@ bool fin, const SpdyHeaderBlock& headers) override; void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, const SpdyHeaderBlock& headers) override; bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override;
diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc index fef27bf..fc38cc2 100644 --- a/net/spdy/spdy_session_pool_unittest.cc +++ b/net/spdy/spdy_session_pool_unittest.cc
@@ -52,7 +52,7 @@ NextProto, SpdySessionPoolTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); // A delegate that opens a new session when it is closed. class SessionOpeningDelegate : public SpdyStream::Delegate {
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc index c6447bf..02e0c74 100644 --- a/net/spdy/spdy_session_unittest.cc +++ b/net/spdy/spdy_session_unittest.cc
@@ -182,7 +182,7 @@ NextProto, SpdySessionTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); // Try to create a SPDY session that will fail during // initialization. Nothing should blow up. @@ -1556,7 +1556,8 @@ kSessionFlowControlStreamId, kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); std::vector<MockWrite> writes; - if (GetParam() == kProtoSPDY4) { + if ((GetParam() >= kProtoSPDY4MinimumVersion) && + (GetParam() <= kProtoSPDY4MaximumVersion)) { writes.push_back( MockWrite(ASYNC, kHttp2ConnectionHeaderPrefix, @@ -5019,6 +5020,10 @@ MapRstStreamStatusToProtocolError(RST_STREAM_FRAME_SIZE_ERROR)); CHECK_EQ(STATUS_CODE_ENHANCE_YOUR_CALM, MapRstStreamStatusToProtocolError(RST_STREAM_ENHANCE_YOUR_CALM)); + CHECK_EQ(STATUS_CODE_INADEQUATE_SECURITY, + MapRstStreamStatusToProtocolError(RST_STREAM_INADEQUATE_SECURITY)); + CHECK_EQ(STATUS_CODE_HTTP_1_1_REQUIRED, + MapRstStreamStatusToProtocolError(RST_STREAM_HTTP_1_1_REQUIRED)); } TEST(MapNetErrorToGoAwayStatus, MapsValue) {
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc index a6926ab..96153bf 100644 --- a/net/spdy/spdy_stream_unittest.cc +++ b/net/spdy/spdy_stream_unittest.cc
@@ -111,7 +111,7 @@ NextProto, SpdyStreamTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4_14, kProtoSPDY4_15)); TEST_P(SpdyStreamTest, SendDataAfterOpen) { GURL url(kStreamUrl);
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index baa3548..801d67d 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc
@@ -57,7 +57,8 @@ next_protos.push_back(kProtoDeprecatedSPDY2); next_protos.push_back(kProtoSPDY3); next_protos.push_back(kProtoSPDY31); - next_protos.push_back(kProtoSPDY4); + next_protos.push_back(kProtoSPDY4_14); + next_protos.push_back(kProtoSPDY4_15); next_protos.push_back(kProtoQUIC1SPDY3); return next_protos; } @@ -229,8 +230,14 @@ bool fin, const SpdyHeaderBlock& headers) override {} void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, - const SpdyHeaderBlock& headers) override {} + const SpdyHeaderBlock& headers) override { + if (has_priority) { + priority_ = priority; + } + } void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, bool fin) override {} @@ -367,7 +374,6 @@ force_spdy_over_ssl(false), force_spdy_always(false), use_alternate_protocols(false), - enable_websocket_over_spdy(false), net_log(NULL) { DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol; @@ -401,7 +407,6 @@ force_spdy_over_ssl(false), force_spdy_always(false), use_alternate_protocols(false), - enable_websocket_over_spdy(false), net_log(NULL) { DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol; } @@ -461,7 +466,6 @@ params.force_spdy_over_ssl = session_deps->force_spdy_over_ssl; params.force_spdy_always = session_deps->force_spdy_always; params.use_alternate_protocols = session_deps->use_alternate_protocols; - params.enable_websocket_over_spdy = session_deps->enable_websocket_over_spdy; params.net_log = session_deps->net_log; return params; } @@ -786,6 +790,7 @@ break; case HEADERS: frame = framer.CreateHeaders(header_info.id, header_info.control_flags, + header_info.priority, headers.get()); break; default: @@ -1103,7 +1108,7 @@ RequestPriority priority, bool compressed, bool fin) const { - if (protocol_ < kProtoSPDY4) { + if (protocol_ < kProtoSPDY4MinimumVersion) { SpdySynStreamIR syn_stream(stream_id); syn_stream.set_name_value_block(block); syn_stream.set_priority( @@ -1123,7 +1128,7 @@ SpdyFrame* SpdyTestUtil::ConstructSpdyReply(int stream_id, const SpdyHeaderBlock& headers) { - if (protocol_ < kProtoSPDY4) { + if (protocol_ < kProtoSPDY4MinimumVersion) { SpdySynReplyIR syn_reply(stream_id); syn_reply.set_name_value_block(headers); return CreateFramer(false)->SerializeFrame(syn_reply); @@ -1264,7 +1269,7 @@ const char* SpdyTestUtil::GetHostKey() const { if (protocol_ < kProtoSPDY3) return "host"; - if (protocol_ < kProtoSPDY4) + if (protocol_ < kProtoSPDY4MinimumVersion) return ":host"; else return ":authority";
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h index e91800d..b08657c 100644 --- a/net/spdy/spdy_test_util_common.h +++ b/net/spdy/spdy_test_util_common.h
@@ -222,7 +222,6 @@ bool force_spdy_over_ssl; bool force_spdy_always; bool use_alternate_protocols; - bool enable_websocket_over_spdy; NetLog* net_log; }; @@ -553,7 +552,9 @@ NextProto protocol() const { return protocol_; } SpdyMajorVersion spdy_version() const { return spdy_version_; } bool is_spdy2() const { return protocol_ < kProtoSPDY3; } - bool include_version_header() const { return protocol_ < kProtoSPDY4; } + bool include_version_header() const { + return protocol_ < kProtoSPDY4MinimumVersion; + } scoped_ptr<SpdyFramer> CreateFramer(bool compressed) const; const char* GetMethodKey() const;
diff --git a/net/spdy/spdy_websocket_stream.cc b/net/spdy/spdy_websocket_stream.cc deleted file mode 100644 index 54e668c..0000000 --- a/net/spdy/spdy_websocket_stream.cc +++ /dev/null
@@ -1,130 +0,0 @@ -// Copyright (c) 2012 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 "net/spdy/spdy_websocket_stream.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/compiler_specific.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/spdy/spdy_framer.h" -#include "net/spdy/spdy_protocol.h" -#include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_stream.h" -#include "url/gurl.h" - -namespace net { - -SpdyWebSocketStream::SpdyWebSocketStream( - const base::WeakPtr<SpdySession>& spdy_session, Delegate* delegate) - : spdy_session_(spdy_session), - pending_send_data_length_(0), - delegate_(delegate), - weak_ptr_factory_(this) { - DCHECK(spdy_session_.get()); - DCHECK(delegate_); -} - -SpdyWebSocketStream::~SpdyWebSocketStream() { - delegate_ = NULL; - Close(); -} - -int SpdyWebSocketStream::InitializeStream(const GURL& url, - RequestPriority request_priority, - const BoundNetLog& net_log) { - if (!spdy_session_) - return ERR_SOCKET_NOT_CONNECTED; - - int rv = stream_request_.StartRequest( - SPDY_BIDIRECTIONAL_STREAM, spdy_session_, url, request_priority, net_log, - base::Bind(&SpdyWebSocketStream::OnSpdyStreamCreated, - weak_ptr_factory_.GetWeakPtr())); - - if (rv == OK) { - stream_ = stream_request_.ReleaseStream(); - DCHECK(stream_.get()); - stream_->SetDelegate(this); - } - return rv; -} - -int SpdyWebSocketStream::SendRequest(scoped_ptr<SpdyHeaderBlock> headers) { - if (!stream_.get()) { - NOTREACHED(); - return ERR_UNEXPECTED; - } - int result = stream_->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND); - if (result < OK && result != ERR_IO_PENDING) - Close(); - return result; -} - -int SpdyWebSocketStream::SendData(const char* data, int length) { - if (!stream_.get()) { - NOTREACHED(); - return ERR_UNEXPECTED; - } - DCHECK_GE(length, 0); - pending_send_data_length_ = static_cast<size_t>(length); - scoped_refptr<IOBuffer> buf(new IOBuffer(length)); - memcpy(buf->data(), data, length); - stream_->SendData(buf.get(), length, MORE_DATA_TO_SEND); - return ERR_IO_PENDING; -} - -void SpdyWebSocketStream::Close() { - if (stream_.get()) { - stream_->Close(); - DCHECK(!stream_.get()); - } -} - -void SpdyWebSocketStream::OnRequestHeadersSent() { - DCHECK(delegate_); - delegate_->OnSentSpdyHeaders(); -} - -SpdyResponseHeadersStatus SpdyWebSocketStream::OnResponseHeadersUpdated( - const SpdyHeaderBlock& response_headers) { - DCHECK(delegate_); - delegate_->OnSpdyResponseHeadersUpdated(response_headers); - return RESPONSE_HEADERS_ARE_COMPLETE; -} - -void SpdyWebSocketStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) { - DCHECK(delegate_); - delegate_->OnReceivedSpdyData(buffer.Pass()); -} - -void SpdyWebSocketStream::OnDataSent() { - DCHECK(delegate_); - delegate_->OnSentSpdyData(pending_send_data_length_); - pending_send_data_length_ = 0; -} - -void SpdyWebSocketStream::OnClose(int status) { - stream_.reset(); - - // Destruction without Close() call OnClose() with delegate_ being NULL. - if (!delegate_) - return; - Delegate* delegate = delegate_; - delegate_ = NULL; - delegate->OnCloseSpdyStream(); -} - -void SpdyWebSocketStream::OnSpdyStreamCreated(int result) { - DCHECK_NE(ERR_IO_PENDING, result); - if (result == OK) { - stream_ = stream_request_.ReleaseStream(); - DCHECK(stream_.get()); - stream_->SetDelegate(this); - } - DCHECK(delegate_); - delegate_->OnCreatedSpdyStream(result); -} - -} // namespace net
diff --git a/net/spdy/spdy_websocket_stream.h b/net/spdy/spdy_websocket_stream.h deleted file mode 100644 index 854afbf..0000000 --- a/net/spdy/spdy_websocket_stream.h +++ /dev/null
@@ -1,104 +0,0 @@ -// Copyright (c) 2012 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 NET_SPDY_SPDY_WEBSOCKET_STREAM_H_ -#define NET_SPDY_SPDY_WEBSOCKET_STREAM_H_ - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "net/base/completion_callback.h" -#include "net/base/request_priority.h" -#include "net/spdy/spdy_framer.h" -#include "net/spdy/spdy_header_block.h" -#include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_stream.h" - -namespace net { - -// The SpdyWebSocketStream is a WebSocket-specific type of stream known to a -// SpdySession. WebSocket's opening handshake is converted to SPDY's -// SYN_STREAM/SYN_REPLY. WebSocket frames are encapsulated as SPDY data frames. -class NET_EXPORT_PRIVATE SpdyWebSocketStream - : public SpdyStream::Delegate { - public: - // Delegate handles asynchronous events. - class NET_EXPORT_PRIVATE Delegate { - public: - // Called when InitializeStream() finishes asynchronously. This delegate is - // called if InitializeStream() returns ERR_IO_PENDING. |status| indicates - // network error. - virtual void OnCreatedSpdyStream(int status) = 0; - - // Called on corresponding to OnSendHeadersComplete() or SPDY's SYN frame - // has been sent. - virtual void OnSentSpdyHeaders() = 0; - - // Called on corresponding to OnResponseHeadersUpdated() or - // SPDY's SYN_STREAM, SYN_REPLY, or HEADERS frames are - // received. This callback may be called multiple times as SPDY's - // delegate does. - virtual void OnSpdyResponseHeadersUpdated( - const SpdyHeaderBlock& response_headers) = 0; - - // Called when data is sent. - virtual void OnSentSpdyData(size_t bytes_sent) = 0; - - // Called when data is received. - virtual void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) = 0; - - // Called when SpdyStream is closed. - virtual void OnCloseSpdyStream() = 0; - - protected: - virtual ~Delegate() {} - }; - - SpdyWebSocketStream(const base::WeakPtr<SpdySession>& spdy_session, - Delegate* delegate); - ~SpdyWebSocketStream() override; - - // Initializes SPDY stream for the WebSocket. - // It might create SPDY stream asynchronously. In this case, this method - // returns ERR_IO_PENDING and call OnCreatedSpdyStream delegate with result - // after completion. In other cases, delegate does not be called. - int InitializeStream(const GURL& url, - RequestPriority request_priority, - const BoundNetLog& stream_net_log); - - int SendRequest(scoped_ptr<SpdyHeaderBlock> headers); - int SendData(const char* data, int length); - void Close(); - - // SpdyStream::Delegate - void OnRequestHeadersSent() override; - SpdyResponseHeadersStatus OnResponseHeadersUpdated( - const SpdyHeaderBlock& response_headers) override; - void OnDataReceived(scoped_ptr<SpdyBuffer> buffer) override; - void OnDataSent() override; - void OnClose(int status) override; - - private: - friend class SpdyWebSocketStreamTest; - FRIEND_TEST_ALL_PREFIXES(SpdyWebSocketStreamTest, Basic); - - void OnSpdyStreamCreated(int status); - - SpdyStreamRequest stream_request_; - base::WeakPtr<SpdyStream> stream_; - const base::WeakPtr<SpdySession> spdy_session_; - size_t pending_send_data_length_; - Delegate* delegate_; - - base::WeakPtrFactory<SpdyWebSocketStream> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStream); -}; - -} // namespace net - -#endif // NET_SPDY_SPDY_WEBSOCKET_STREAM_H_
diff --git a/net/spdy/spdy_websocket_stream_unittest.cc b/net/spdy/spdy_websocket_stream_unittest.cc deleted file mode 100644 index fc54884..0000000 --- a/net/spdy/spdy_websocket_stream_unittest.cc +++ /dev/null
@@ -1,669 +0,0 @@ -// Copyright 2013 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 "net/spdy/spdy_websocket_stream.h" - -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "net/base/completion_callback.h" -#include "net/proxy/proxy_server.h" -#include "net/socket/next_proto.h" -#include "net/socket/ssl_client_socket.h" -#include "net/spdy/spdy_http_utils.h" -#include "net/spdy/spdy_protocol.h" -#include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_websocket_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -namespace { - -struct SpdyWebSocketStreamEvent { - enum EventType { - EVENT_CREATED, - EVENT_SENT_HEADERS, - EVENT_RECEIVED_HEADER, - EVENT_SENT_DATA, - EVENT_RECEIVED_DATA, - EVENT_CLOSE, - }; - SpdyWebSocketStreamEvent(EventType type, - const SpdyHeaderBlock& headers, - int result, - const std::string& data) - : event_type(type), - headers(headers), - result(result), - data(data) {} - - EventType event_type; - SpdyHeaderBlock headers; - int result; - std::string data; -}; - -class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStream::Delegate { - public: - explicit SpdyWebSocketStreamEventRecorder(const CompletionCallback& callback) - : callback_(callback) {} - ~SpdyWebSocketStreamEventRecorder() override {} - - typedef base::Callback<void(SpdyWebSocketStreamEvent*)> StreamEventCallback; - - void SetOnCreated(const StreamEventCallback& callback) { - on_created_ = callback; - } - void SetOnSentHeaders(const StreamEventCallback& callback) { - on_sent_headers_ = callback; - } - void SetOnReceivedHeader(const StreamEventCallback& callback) { - on_received_header_ = callback; - } - void SetOnSentData(const StreamEventCallback& callback) { - on_sent_data_ = callback; - } - void SetOnReceivedData(const StreamEventCallback& callback) { - on_received_data_ = callback; - } - void SetOnClose(const StreamEventCallback& callback) { - on_close_ = callback; - } - - void OnCreatedSpdyStream(int result) override { - events_.push_back( - SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED, - SpdyHeaderBlock(), - result, - std::string())); - if (!on_created_.is_null()) - on_created_.Run(&events_.back()); - } - void OnSentSpdyHeaders() override { - events_.push_back( - SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, - SpdyHeaderBlock(), - OK, - std::string())); - if (!on_sent_data_.is_null()) - on_sent_data_.Run(&events_.back()); - } - void OnSpdyResponseHeadersUpdated( - const SpdyHeaderBlock& response_headers) override { - events_.push_back( - SpdyWebSocketStreamEvent( - SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, - response_headers, - OK, - std::string())); - if (!on_received_header_.is_null()) - on_received_header_.Run(&events_.back()); - } - void OnSentSpdyData(size_t bytes_sent) override { - events_.push_back( - SpdyWebSocketStreamEvent( - SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - SpdyHeaderBlock(), - static_cast<int>(bytes_sent), - std::string())); - if (!on_sent_data_.is_null()) - on_sent_data_.Run(&events_.back()); - } - void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) override { - std::string buffer_data; - size_t buffer_len = 0; - if (buffer) { - buffer_len = buffer->GetRemainingSize(); - buffer_data.append(buffer->GetRemainingData(), buffer_len); - } - events_.push_back( - SpdyWebSocketStreamEvent( - SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - SpdyHeaderBlock(), - buffer_len, - buffer_data)); - if (!on_received_data_.is_null()) - on_received_data_.Run(&events_.back()); - } - void OnCloseSpdyStream() override { - events_.push_back( - SpdyWebSocketStreamEvent( - SpdyWebSocketStreamEvent::EVENT_CLOSE, - SpdyHeaderBlock(), - OK, - std::string())); - if (!on_close_.is_null()) - on_close_.Run(&events_.back()); - if (!callback_.is_null()) - callback_.Run(OK); - } - - const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const { - return events_; - } - - private: - std::vector<SpdyWebSocketStreamEvent> events_; - StreamEventCallback on_created_; - StreamEventCallback on_sent_headers_; - StreamEventCallback on_received_header_; - StreamEventCallback on_sent_data_; - StreamEventCallback on_received_data_; - StreamEventCallback on_close_; - CompletionCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder); -}; - -} // namespace - -class SpdyWebSocketStreamTest - : public ::testing::Test, - public ::testing::WithParamInterface<NextProto> { - public: - OrderedSocketData* data() { return data_.get(); } - - void DoSendHelloFrame(SpdyWebSocketStreamEvent* event) { - // Record the actual stream_id. - created_stream_id_ = websocket_stream_->stream_->stream_id(); - websocket_stream_->SendData(kMessageFrame, kMessageFrameLength); - } - - void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) { - websocket_stream_->SendData(kClosingFrame, kClosingFrameLength); - } - - void DoClose(SpdyWebSocketStreamEvent* event) { - websocket_stream_->Close(); - } - - void DoSync(SpdyWebSocketStreamEvent* event) { - sync_callback_.callback().Run(OK); - } - - protected: - SpdyWebSocketStreamTest() - : spdy_util_(GetParam()), - spdy_settings_id_to_set_(SETTINGS_MAX_CONCURRENT_STREAMS), - spdy_settings_flags_to_set_(SETTINGS_FLAG_PLEASE_PERSIST), - spdy_settings_value_to_set_(1), - session_deps_(GetParam()), - stream_id_(0), - created_stream_id_(0) {} - virtual ~SpdyWebSocketStreamTest() {} - - void SetUp() override { - host_port_pair_.set_host("example.com"); - host_port_pair_.set_port(80); - spdy_session_key_ = SpdySessionKey(host_port_pair_, - ProxyServer::Direct(), - PRIVACY_MODE_DISABLED); - - spdy_settings_to_send_[spdy_settings_id_to_set_] = - SettingsFlagsAndValue( - SETTINGS_FLAG_PERSISTED, spdy_settings_value_to_set_); - } - - void TearDown() override { base::MessageLoop::current()->RunUntilIdle(); } - - void Prepare(SpdyStreamId stream_id) { - stream_id_ = stream_id; - - request_frame_.reset(spdy_util_.ConstructSpdyWebSocketSynStream( - stream_id_, - "/echo", - "example.com", - "http://example.com/wsdemo")); - - response_frame_.reset( - spdy_util_.ConstructSpdyWebSocketSynReply(stream_id_)); - - message_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame( - kMessageFrame, - kMessageFrameLength, - stream_id_, - false)); - - closing_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame( - kClosingFrame, - kClosingFrameLength, - stream_id_, - false)); - - closing_frame_fin_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame( - kClosingFrame, - kClosingFrameLength, - stream_id_, - true)); - } - - void InitSession(MockRead* reads, size_t reads_count, - MockWrite* writes, size_t writes_count) { - data_.reset(new OrderedSocketData(reads, reads_count, - writes, writes_count)); - session_deps_.socket_factory->AddSocketDataProvider(data_.get()); - http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); - session_ = CreateInsecureSpdySession( - http_session_, spdy_session_key_, BoundNetLog()); - } - - void SendRequest() { - scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); - spdy_util_.SetHeader("path", "/echo", headers.get()); - spdy_util_.SetHeader("host", "example.com", headers.get()); - spdy_util_.SetHeader("version", "WebSocket/13", headers.get()); - spdy_util_.SetHeader("scheme", "ws", headers.get()); - spdy_util_.SetHeader("origin", "http://example.com/wsdemo", headers.get()); - websocket_stream_->SendRequest(headers.Pass()); - } - - SpdyWebSocketTestUtil spdy_util_; - SpdySettingsIds spdy_settings_id_to_set_; - SpdySettingsFlags spdy_settings_flags_to_set_; - uint32 spdy_settings_value_to_set_; - SettingsMap spdy_settings_to_send_; - SpdySessionDependencies session_deps_; - scoped_ptr<OrderedSocketData> data_; - scoped_refptr<HttpNetworkSession> http_session_; - base::WeakPtr<SpdySession> session_; - scoped_ptr<SpdyWebSocketStream> websocket_stream_; - SpdyStreamId stream_id_; - SpdyStreamId created_stream_id_; - scoped_ptr<SpdyFrame> request_frame_; - scoped_ptr<SpdyFrame> response_frame_; - scoped_ptr<SpdyFrame> message_frame_; - scoped_ptr<SpdyFrame> closing_frame_; - scoped_ptr<SpdyFrame> closing_frame_fin_; - HostPortPair host_port_pair_; - SpdySessionKey spdy_session_key_; - TestCompletionCallback completion_callback_; - TestCompletionCallback sync_callback_; - - static const char kMessageFrame[]; - static const char kClosingFrame[]; - static const size_t kMessageFrameLength; - static const size_t kClosingFrameLength; -}; - -INSTANTIATE_TEST_CASE_P( - NextProto, - SpdyWebSocketStreamTest, - testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); - -// TODO(toyoshim): Replace old framing data to new one, then use HEADERS and -// data frames. -const char SpdyWebSocketStreamTest::kMessageFrame[] = "\x81\x05hello"; -const char SpdyWebSocketStreamTest::kClosingFrame[] = "\x88\0"; -const size_t SpdyWebSocketStreamTest::kMessageFrameLength = - arraysize(SpdyWebSocketStreamTest::kMessageFrame) - 1; -const size_t SpdyWebSocketStreamTest::kClosingFrameLength = - arraysize(SpdyWebSocketStreamTest::kClosingFrame) - 1; - -TEST_P(SpdyWebSocketStreamTest, Basic) { - Prepare(1); - MockWrite writes[] = { - CreateMockWrite(*request_frame_.get(), 1), - CreateMockWrite(*message_frame_.get(), 3), - CreateMockWrite(*closing_frame_.get(), 5) - }; - - MockRead reads[] = { - CreateMockRead(*response_frame_.get(), 2), - CreateMockRead(*message_frame_.get(), 4), - // Skip sequence 6 to notify closing has been sent. - CreateMockRead(*closing_frame_.get(), 7), - MockRead(SYNCHRONOUS, 0, 8) // EOF cause OnCloseSpdyStream event. - }; - - InitSession(reads, arraysize(reads), writes, arraysize(writes)); - - SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); - delegate.SetOnReceivedHeader( - base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, - base::Unretained(this))); - delegate.SetOnReceivedData( - base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, - base::Unretained(this))); - - websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); - - BoundNetLog net_log; - GURL url("ws://example.com/echo"); - ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); - - ASSERT_TRUE(websocket_stream_->stream_.get()); - - SendRequest(); - - completion_callback_.WaitForResult(); - - EXPECT_EQ(stream_id_, created_stream_id_); - - websocket_stream_.reset(); - - const std::vector<SpdyWebSocketStreamEvent>& events = - delegate.GetSeenEvents(); - ASSERT_EQ(7U, events.size()); - - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, - events[0].event_type); - EXPECT_EQ(OK, events[0].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, - events[1].event_type); - EXPECT_EQ(OK, events[1].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[2].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[3].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[4].event_type); - EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[4].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[5].event_type); - EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, - events[6].event_type); - EXPECT_EQ(OK, events[6].result); - - // EOF close SPDY session. - EXPECT_FALSE( - HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); - EXPECT_TRUE(data()->at_read_eof()); - EXPECT_TRUE(data()->at_write_eof()); -} - -// A SPDY websocket may still send it's close frame after -// recieving a close with SPDY stream FIN. -TEST_P(SpdyWebSocketStreamTest, RemoteCloseWithFin) { - Prepare(1); - MockWrite writes[] = { - CreateMockWrite(*request_frame_.get(), 1), - CreateMockWrite(*closing_frame_.get(), 4), - }; - MockRead reads[] = { - CreateMockRead(*response_frame_.get(), 2), - CreateMockRead(*closing_frame_fin_.get(), 3), - MockRead(SYNCHRONOUS, 0, 5) // EOF cause OnCloseSpdyStream event. - }; - InitSession(reads, arraysize(reads), writes, arraysize(writes)); - - SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); - delegate.SetOnReceivedData( - base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, - base::Unretained(this))); - - websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); - BoundNetLog net_log; - GURL url("ws://example.com/echo"); - ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); - - SendRequest(); - completion_callback_.WaitForResult(); - websocket_stream_.reset(); - - const std::vector<SpdyWebSocketStreamEvent>& events = - delegate.GetSeenEvents(); - EXPECT_EQ(5U, events.size()); - - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, - events[0].event_type); - EXPECT_EQ(OK, events[0].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, - events[1].event_type); - EXPECT_EQ(OK, events[1].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[2].event_type); - EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[2].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[3].event_type); - EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[3].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, - events[4].event_type); - EXPECT_EQ(OK, events[4].result); - - // EOF closes SPDY session. - EXPECT_FALSE( - HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); - EXPECT_TRUE(data()->at_read_eof()); - EXPECT_TRUE(data()->at_write_eof()); -} - -TEST_P(SpdyWebSocketStreamTest, DestructionBeforeClose) { - Prepare(1); - MockWrite writes[] = { - CreateMockWrite(*request_frame_.get(), 1), - CreateMockWrite(*message_frame_.get(), 3) - }; - - MockRead reads[] = { - CreateMockRead(*response_frame_.get(), 2), - CreateMockRead(*message_frame_.get(), 4), - MockRead(ASYNC, ERR_IO_PENDING, 5) - }; - - InitSession(reads, arraysize(reads), writes, arraysize(writes)); - - SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); - delegate.SetOnReceivedHeader( - base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, - base::Unretained(this))); - delegate.SetOnReceivedData( - base::Bind(&SpdyWebSocketStreamTest::DoSync, - base::Unretained(this))); - - websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); - - BoundNetLog net_log; - GURL url("ws://example.com/echo"); - ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); - - SendRequest(); - - sync_callback_.WaitForResult(); - - // WebSocketStream destruction remove its SPDY stream from the session. - EXPECT_TRUE(session_->IsStreamActive(stream_id_)); - websocket_stream_.reset(); - EXPECT_FALSE(session_->IsStreamActive(stream_id_)); - - const std::vector<SpdyWebSocketStreamEvent>& events = - delegate.GetSeenEvents(); - ASSERT_GE(4U, events.size()); - - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, - events[0].event_type); - EXPECT_EQ(OK, events[0].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, - events[1].event_type); - EXPECT_EQ(OK, events[1].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[2].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[3].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); - - EXPECT_TRUE( - HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); - EXPECT_TRUE(data()->at_read_eof()); - EXPECT_TRUE(data()->at_write_eof()); -} - -TEST_P(SpdyWebSocketStreamTest, DestructionAfterExplicitClose) { - Prepare(1); - MockWrite writes[] = { - CreateMockWrite(*request_frame_.get(), 1), - CreateMockWrite(*message_frame_.get(), 3), - CreateMockWrite(*closing_frame_.get(), 5) - }; - - MockRead reads[] = { - CreateMockRead(*response_frame_.get(), 2), - CreateMockRead(*message_frame_.get(), 4), - MockRead(ASYNC, ERR_IO_PENDING, 6) - }; - - InitSession(reads, arraysize(reads), writes, arraysize(writes)); - - SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); - delegate.SetOnReceivedHeader( - base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, - base::Unretained(this))); - delegate.SetOnReceivedData( - base::Bind(&SpdyWebSocketStreamTest::DoClose, - base::Unretained(this))); - - websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); - - BoundNetLog net_log; - GURL url("ws://example.com/echo"); - ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log)); - - SendRequest(); - - completion_callback_.WaitForResult(); - - // SPDY stream has already been removed from the session by Close(). - EXPECT_FALSE(session_->IsStreamActive(stream_id_)); - websocket_stream_.reset(); - - const std::vector<SpdyWebSocketStreamEvent>& events = - delegate.GetSeenEvents(); - ASSERT_EQ(5U, events.size()); - - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, - events[0].event_type); - EXPECT_EQ(OK, events[0].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, - events[1].event_type); - EXPECT_EQ(OK, events[1].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[2].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[3].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, events[4].event_type); - - EXPECT_TRUE( - HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); -} - -TEST_P(SpdyWebSocketStreamTest, IOPending) { - Prepare(1); - scoped_ptr<SpdyFrame> settings_frame( - spdy_util_.ConstructSpdySettings(spdy_settings_to_send_)); - scoped_ptr<SpdyFrame> settings_ack(spdy_util_.ConstructSpdySettingsAck()); - MockWrite writes[] = { - CreateMockWrite(*settings_ack, 1), - CreateMockWrite(*request_frame_.get(), 2), - CreateMockWrite(*message_frame_.get(), 4), - CreateMockWrite(*closing_frame_.get(), 6) - }; - - MockRead reads[] = { - CreateMockRead(*settings_frame.get(), 0), - CreateMockRead(*response_frame_.get(), 3), - CreateMockRead(*message_frame_.get(), 5), - CreateMockRead(*closing_frame_.get(), 7), - MockRead(SYNCHRONOUS, 0, 8) // EOF cause OnCloseSpdyStream event. - }; - - DeterministicSocketData data(reads, arraysize(reads), - writes, arraysize(writes)); - session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); - http_session_ = - SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_); - - session_ = CreateInsecureSpdySession( - http_session_, spdy_session_key_, BoundNetLog()); - - // Create a dummy WebSocketStream which cause ERR_IO_PENDING to another - // WebSocketStream under test. - SpdyWebSocketStreamEventRecorder block_delegate((CompletionCallback())); - - scoped_ptr<SpdyWebSocketStream> block_stream( - new SpdyWebSocketStream(session_, &block_delegate)); - BoundNetLog block_net_log; - GURL block_url("ws://example.com/block"); - ASSERT_EQ(OK, - block_stream->InitializeStream(block_url, HIGHEST, block_net_log)); - - data.RunFor(1); - - // Create a WebSocketStream under test. - SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback()); - delegate.SetOnCreated( - base::Bind(&SpdyWebSocketStreamTest::DoSync, - base::Unretained(this))); - delegate.SetOnReceivedHeader( - base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame, - base::Unretained(this))); - delegate.SetOnReceivedData( - base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame, - base::Unretained(this))); - - websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate)); - BoundNetLog net_log; - GURL url("ws://example.com/echo"); - ASSERT_EQ(ERR_IO_PENDING, websocket_stream_->InitializeStream( - url, HIGHEST, net_log)); - - // Delete the fist stream to allow create the second stream. - block_stream.reset(); - ASSERT_EQ(OK, sync_callback_.WaitForResult()); - - SendRequest(); - - data.RunFor(8); - completion_callback_.WaitForResult(); - - websocket_stream_.reset(); - - const std::vector<SpdyWebSocketStreamEvent>& block_events = - block_delegate.GetSeenEvents(); - ASSERT_EQ(0U, block_events.size()); - - const std::vector<SpdyWebSocketStreamEvent>& events = - delegate.GetSeenEvents(); - ASSERT_EQ(8U, events.size()); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED, - events[0].event_type); - EXPECT_EQ(0, events[0].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS, - events[1].event_type); - EXPECT_EQ(OK, events[1].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER, - events[2].event_type); - EXPECT_EQ(OK, events[2].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[3].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[4].event_type); - EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[4].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA, - events[5].event_type); - EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA, - events[6].event_type); - EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[6].result); - EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, - events[7].event_type); - EXPECT_EQ(OK, events[7].result); - - // EOF close SPDY session. - EXPECT_FALSE( - HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_)); - EXPECT_TRUE(data.at_read_eof()); - EXPECT_TRUE(data.at_write_eof()); -} - -} // namespace net
diff --git a/net/spdy/spdy_websocket_test_util.cc b/net/spdy/spdy_websocket_test_util.cc deleted file mode 100644 index b1ef81b..0000000 --- a/net/spdy/spdy_websocket_test_util.cc +++ /dev/null
@@ -1,111 +0,0 @@ -// Copyright (c) 2013 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 "net/spdy/spdy_websocket_test_util.h" - -#include "net/spdy/buffered_spdy_framer.h" -#include "net/spdy/spdy_http_utils.h" - -namespace net { - -const bool kDefaultCompressed = false; - -SpdyWebSocketTestUtil::SpdyWebSocketTestUtil( - NextProto protocol) : spdy_util_(protocol) {} - -std::string SpdyWebSocketTestUtil::GetHeader(const SpdyHeaderBlock& headers, - const std::string& key) const { - SpdyHeaderBlock::const_iterator it = headers.find(GetHeaderKey(key)); - return (it == headers.end()) ? "" : it->second; -} - -void SpdyWebSocketTestUtil::SetHeader( - const std::string& key, - const std::string& value, - SpdyHeaderBlock* headers) const { - (*headers)[GetHeaderKey(key)] = value; -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdyWebSocketSynStream( - int stream_id, - const char* path, - const char* host, - const char* origin) { - scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); - SetHeader("path", path, headers.get()); - SetHeader("host", host, headers.get()); - SetHeader("version", "WebSocket/13", headers.get()); - SetHeader("scheme", "ws", headers.get()); - SetHeader("origin", origin, headers.get()); - return spdy_util_.ConstructSpdySyn( - stream_id, *headers, HIGHEST, false, false); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdyWebSocketSynReply( - int stream_id) { - SpdyHeaderBlock block; - SetHeader("status", "101", &block); - return spdy_util_.ConstructSpdyReply(stream_id, block); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdyWebSocketHandshakeRequestFrame( - scoped_ptr<SpdyHeaderBlock> headers, - SpdyStreamId stream_id, - RequestPriority request_priority) { - return spdy_util_.ConstructSpdySyn( - stream_id, *headers, request_priority, kDefaultCompressed, false); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdyWebSocketHandshakeResponseFrame( - scoped_ptr<SpdyHeaderBlock> headers, - SpdyStreamId stream_id, - RequestPriority request_priority) { - return spdy_util_.ConstructSpdyReply(stream_id, *headers); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdyWebSocketHeadersFrame( - int stream_id, - const char* length, - bool fin) { - scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); - SetHeader("opcode", "1", headers.get()); // text frame - SetHeader("length", length, headers.get()); - SetHeader("fin", fin ? "1" : "0", headers.get()); - return spdy_util_.ConstructSpdySyn(stream_id, *headers, LOWEST, false, false); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdyWebSocketDataFrame( - const char* data, - int len, - SpdyStreamId stream_id, - bool fin) { - - // Construct SPDY data frame. - BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); - return framer.CreateDataFrame( - stream_id, - data, - len, - fin ? DATA_FLAG_FIN : DATA_FLAG_NONE); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdySettings( - const SettingsMap& settings) const { - return spdy_util_.ConstructSpdySettings(settings); -} - -SpdyFrame* SpdyWebSocketTestUtil::ConstructSpdySettingsAck() const { - return spdy_util_.ConstructSpdySettingsAck(); -} - -SpdyMajorVersion SpdyWebSocketTestUtil::spdy_version() const { - return spdy_util_.spdy_version(); -} - -std::string SpdyWebSocketTestUtil::GetHeaderKey( - const std::string& key) const { - return (spdy_util_.is_spdy2() ? "" : ":") + key; -} - -} // namespace net
diff --git a/net/spdy/spdy_websocket_test_util.h b/net/spdy/spdy_websocket_test_util.h deleted file mode 100644 index 14c8c02..0000000 --- a/net/spdy/spdy_websocket_test_util.h +++ /dev/null
@@ -1,78 +0,0 @@ -// Copyright (c) 2013 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 NET_SPDY_SPDY_WEBSOCKET_TEST_UTIL_H_ -#define NET_SPDY_SPDY_WEBSOCKET_TEST_UTIL_H_ - -#include "net/base/request_priority.h" -#include "net/spdy/spdy_header_block.h" -#include "net/spdy/spdy_protocol.h" -#include "net/spdy/spdy_test_util_common.h" - -namespace net { - -class SpdyWebSocketTestUtil { - public: - explicit SpdyWebSocketTestUtil(NextProto protocol); - - // Returns the value corresponding to the given key (passed through - // GetHeaderKey()), or the empty string if none exists. - std::string GetHeader(const SpdyHeaderBlock& headers, - const std::string& key) const; - - // Adds the given key/value pair to |headers|, passing the key - // through GetHeaderKey(). - void SetHeader(const std::string& key, - const std::string& value, - SpdyHeaderBlock* headers) const; - - // Constructs a standard SPDY SYN_STREAM frame for WebSocket over - // SPDY opening handshake. - SpdyFrame* ConstructSpdyWebSocketSynStream(int stream_id, - const char* path, - const char* host, - const char* origin); - - // Constructs a standard SPDY SYN_REPLY packet to match the - // WebSocket over SPDY opening handshake. - SpdyFrame* ConstructSpdyWebSocketSynReply(int stream_id); - - // Constructs a WebSocket over SPDY handshake request packet. - SpdyFrame* ConstructSpdyWebSocketHandshakeRequestFrame( - scoped_ptr<SpdyHeaderBlock> headers, - SpdyStreamId stream_id, - RequestPriority request_priority); - - // Constructs a WebSocket over SPDY handshake response packet. - SpdyFrame* ConstructSpdyWebSocketHandshakeResponseFrame( - scoped_ptr<SpdyHeaderBlock> headers, - SpdyStreamId stream_id, - RequestPriority request_priority); - - // Constructs a SPDY HEADERS frame for a WebSocket frame over SPDY. - SpdyFrame* ConstructSpdyWebSocketHeadersFrame(int stream_id, - const char* length, - bool fin); - - // Constructs a WebSocket over SPDY data packet. - SpdyFrame* ConstructSpdyWebSocketDataFrame(const char* data, - int len, - SpdyStreamId stream_id, - bool fin); - - // Forwards to |spdy_util_|. - SpdyFrame* ConstructSpdySettings(const SettingsMap& settings) const; - SpdyFrame* ConstructSpdySettingsAck() const; - SpdyMajorVersion spdy_version() const; - - private: - // Modify the header key based on the SPDY version and return it. - std::string GetHeaderKey(const std::string& key) const; - - SpdyTestUtil spdy_util_; -}; - -} // namespace net - -#endif // NET_SPDY_SPDY_WEBSOCKET_TEST_UTIL_H_
diff --git a/net/ssl/ssl_cipher_suite_names.h b/net/ssl/ssl_cipher_suite_names.h index 29c03a1..4e02fc6 100644 --- a/net/ssl/ssl_cipher_suite_names.h +++ b/net/ssl/ssl_cipher_suite_names.h
@@ -55,7 +55,7 @@ // Currently, this function follows these criteria: // 1) Only uses forward secure key exchanges // 2) Only uses AEADs -NET_EXPORT_PRIVATE bool IsSecureTLSCipherSuite(uint16 cipher_suite); +NET_EXPORT bool IsSecureTLSCipherSuite(uint16 cipher_suite); } // namespace net
diff --git a/net/test/embedded_test_server/http_request.cc b/net/test/embedded_test_server/http_request.cc index 3acb550..3156e51 100644 --- a/net/test/embedded_test_server/http_request.cc +++ b/net/test/embedded_test_server/http_request.cc
@@ -83,8 +83,10 @@ // Parse request's the first header line. // Request main main header, eg. GET /foobar.html HTTP/1.1 + std::string request_headers; { const std::string header_line = ShiftLine(); + http_request_->all_headers += header_line + "\r\n"; std::vector<std::string> header_line_tokens; base::SplitString(header_line, ' ', &header_line_tokens); DCHECK_EQ(3u, header_line_tokens.size()); @@ -111,6 +113,7 @@ if (header_line.empty()) break; + http_request_->all_headers += header_line + "\r\n"; if (header_line[0] == ' ' || header_line[0] == '\t') { // Continuation of the previous multi-line header. std::string header_value =
diff --git a/net/test/embedded_test_server/http_request.h b/net/test/embedded_test_server/http_request.h index cb9144b..fcbf54c 100644 --- a/net/test/embedded_test_server/http_request.h +++ b/net/test/embedded_test_server/http_request.h
@@ -36,6 +36,7 @@ std::string relative_url; // Starts with '/'. Example: "/test?query=foo" HttpMethod method; std::string method_string; + std::string all_headers; std::map<std::string, std::string> headers; std::string content; bool has_content;
diff --git a/net/test/embedded_test_server/http_request_unittest.cc b/net/test/embedded_test_server/http_request_unittest.cc index 1006e4e..56121bb 100644 --- a/net/test/embedded_test_server/http_request_unittest.cc +++ b/net/test/embedded_test_server/http_request_unittest.cc
@@ -44,6 +44,14 @@ EXPECT_EQ(1u, request->headers.count("Multi-line-header")); EXPECT_EQ(1u, request->headers.count("Content-Length")); + const char kExpectedAllHeaders[] = + "POST /foobar.html HTTP/1.1\r\n" + "Host: localhost:1234\r\n" + "Multi-line-header: abcd\r\n" + " efgh\r\n" + " ijkl\r\n" + "Content-Length: 10\r\n"; + EXPECT_EQ(kExpectedAllHeaders, request->all_headers); EXPECT_EQ("localhost:1234", request->headers["Host"]); EXPECT_EQ("abcd efgh ijkl", request->headers["Multi-line-header"]); EXPECT_EQ("10", request->headers["Content-Length"]);
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc index 0e23c7a..0c586f2 100644 --- a/net/test/spawned_test_server/base_test_server.cc +++ b/net/test/spawned_test_server/base_test_server.cc
@@ -101,6 +101,7 @@ tls_intolerance_type(TLS_INTOLERANCE_ALERT), fallback_scsv_enabled(false), staple_ocsp_response(false), + ocsp_server_unavailable(false), enable_npn(false), disable_session_cache(false) { } @@ -118,6 +119,7 @@ tls_intolerance_type(TLS_INTOLERANCE_ALERT), fallback_scsv_enabled(false), staple_ocsp_response(false), + ocsp_server_unavailable(false), enable_npn(false), disable_session_cache(false) { } @@ -476,6 +478,10 @@ } if (ssl_options_.staple_ocsp_response) arguments->Set("staple-ocsp-response", base::Value::CreateNullValue()); + if (ssl_options_.ocsp_server_unavailable) { + arguments->Set("ocsp-server-unavailable", + base::Value::CreateNullValue()); + } if (ssl_options_.enable_npn) arguments->Set("enable-npn", base::Value::CreateNullValue()); if (ssl_options_.disable_session_cache)
diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h index 45ea1ce..73ac855 100644 --- a/net/test/spawned_test_server/base_test_server.h +++ b/net/test/spawned_test_server/base_test_server.h
@@ -201,6 +201,10 @@ // Whether to staple the OCSP response. bool staple_ocsp_response; + // Whether to make the OCSP server unavailable. This does not affect the + // stapled OCSP response. + bool ocsp_server_unavailable; + // Whether to enable NPN support. bool enable_npn;
diff --git a/net/test/url_request/url_request_mock_http_job.cc b/net/test/url_request/url_request_mock_http_job.cc index 5542ac7..ede87cf 100644 --- a/net/test/url_request/url_request_mock_http_job.cc +++ b/net/test/url_request/url_request_mock_http_job.cc
@@ -5,24 +5,35 @@ #include "net/test/url_request/url_request_mock_http_job.h" #include "base/files/file_util.h" +#include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task_runner_util.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread_restrictions.h" #include "net/base/filename_util.h" +#include "net/base/net_errors.h" +#include "net/base/url_util.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_interceptor.h" +namespace net { + +namespace { + const char kMockHostname[] = "mock.http"; const base::FilePath::CharType kMockHeaderFileSuffix[] = FILE_PATH_LITERAL(".mock-http-headers"); -namespace net { - -namespace { +// String names of failure phases matching FailurePhase enum. +const char* kFailurePhase[] { + "start", // START + "readasync", // READ_ASYNC + "readsync", // READ_SYNC +}; class MockJobInterceptor : public net::URLRequestInterceptor { public: @@ -119,6 +130,22 @@ } // static +GURL URLRequestMockHTTPJob::GetMockUrlWithFailure(const base::FilePath& path, + FailurePhase phase, + int net_error) { + COMPILE_ASSERT(arraysize(kFailurePhase) == MAX_FAILURE_PHASE, + kFailurePhase_must_match_FailurePhase_enum); + DCHECK_GE(phase, START); + DCHECK_LE(phase, READ_SYNC); + std::string url(GetMockUrl(path).spec()); + url.append("?"); + url.append(kFailurePhase[phase]); + url.append("="); + url.append(base::IntToString(net_error)); + return GURL(url); +} + +// static scoped_ptr<net::URLRequestInterceptor> URLRequestMockHTTPJob::CreateInterceptor( const base::FilePath& base_path, const scoped_refptr<base::SequencedWorkerPool>& worker_pool) { @@ -163,23 +190,72 @@ // Public virtual version. void URLRequestMockHTTPJob::Start() { + if (MaybeReportErrorOnPhase(START)) + return; base::PostTaskAndReplyWithResult( task_runner_.get(), FROM_HERE, base::Bind(&DoFileIO, file_path_), - base::Bind(&URLRequestMockHTTPJob::GetRawHeaders, + base::Bind(&URLRequestMockHTTPJob::SetHeadersAndStart, weak_ptr_factory_.GetWeakPtr())); } -void URLRequestMockHTTPJob::GetRawHeaders(std::string raw_headers) { - // Handle CRLF line-endings. - ReplaceSubstringsAfterOffset(&raw_headers, 0, "\r\n", "\n"); - // ParseRawHeaders expects \0 to end each header line. - ReplaceSubstringsAfterOffset(&raw_headers, 0, "\n", std::string("\0", 1)); +// Public virtual version. +bool URLRequestMockHTTPJob::ReadRawData(IOBuffer* buf, + int buf_size, + int* bytes_read) { + if (MaybeReportErrorOnPhase(READ_SYNC)) + return false; + if (MaybeReportErrorOnPhase(READ_ASYNC)) + return false; + return URLRequestFileJob::ReadRawData(buf, buf_size, bytes_read); +} + +void URLRequestMockHTTPJob::SetHeadersAndStart(const std::string& raw_headers) { + if (MaybeReportErrorOnPhase(START)) + return; raw_headers_ = raw_headers; + // Handle CRLF line-endings. + ReplaceSubstringsAfterOffset(&raw_headers_, 0, "\r\n", "\n"); + // ParseRawHeaders expects \0 to end each header line. + ReplaceSubstringsAfterOffset(&raw_headers_, 0, "\n", std::string("\0", 1)); URLRequestFileJob::Start(); } +bool URLRequestMockHTTPJob::MaybeReportErrorOnPhase( + FailurePhase current_phase) { + DCHECK_GE(current_phase, START); + DCHECK_LE(current_phase, READ_SYNC); + std::string phase_key(kFailurePhase[current_phase]); + std::string phase_error_string; + if (!GetValueForKeyInQuery(request_->url(), phase_key, &phase_error_string)) + return false; + + int net_error; + if (!base::StringToInt(phase_error_string, &net_error)) + return false; + + if (net_error != net::ERR_IO_PENDING && + (current_phase == START || current_phase == READ_SYNC)) { + NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, net_error)); + return true; + } + + SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); + + if (current_phase != READ_ASYNC) + return true; + + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind( + &URLRequestMockHTTPJob::NotifyDone, + weak_ptr_factory_.GetWeakPtr(), + net::URLRequestStatus(net::URLRequestStatus::FAILED, net_error))); + + return true; +} + // Private const version. void URLRequestMockHTTPJob::GetResponseInfoConst( net::HttpResponseInfo* info) const {
diff --git a/net/test/url_request/url_request_mock_http_job.h b/net/test/url_request/url_request_mock_http_job.h index 1a01a30..6eb3c72 100644 --- a/net/test/url_request/url_request_mock_http_job.h +++ b/net/test/url_request/url_request_mock_http_job.h
@@ -28,6 +28,13 @@ class URLRequestMockHTTPJob : public URLRequestFileJob { public: + enum FailurePhase { + START = 0, + READ_ASYNC = 1, + READ_SYNC = 2, + MAX_FAILURE_PHASE = 3, + }; + // Note that all file IO is done using |worker_pool|. URLRequestMockHTTPJob(URLRequest* request, NetworkDelegate* network_delegate, @@ -35,6 +42,7 @@ const scoped_refptr<base::TaskRunner>& task_runner); void Start() override; + bool ReadRawData(IOBuffer* buf, int buf_size, int* bytes_read) override; bool GetMimeType(std::string* mime_type) const override; int GetResponseCode() const override; bool GetCharset(std::string* charset) override; @@ -57,6 +65,13 @@ // construct a mock URL. static GURL GetMockUrl(const base::FilePath& path); + // Given the path to a file relative to the path passed to AddUrlHandler(), + // construct a mock URL that reports |net_error| at given |phase| of the + // request. Reporting |net_error| ERR_IO_PENDING results in a hung request. + static GURL GetMockUrlWithFailure(const base::FilePath& path, + FailurePhase phase, + int net_error); + // Returns a URLRequestJobFactory::ProtocolHandler that serves // URLRequestMockHTTPJob's responding like an HTTP server. |base_path| is the // file path leading to the root of the directory to use as the root of the @@ -77,7 +92,13 @@ private: void GetResponseInfoConst(HttpResponseInfo* info) const; - void GetRawHeaders(std::string raw_headers); + void SetHeadersAndStart(const std::string& raw_headers); + // Checks query part of request url, and reports an error if it matches. + // Error is parsed out from the query and is reported synchronously. + // Reporting ERR_IO_PENDING results in a hung request. + // The "readasync" error is posted asynchronously. + bool MaybeReportErrorOnPhase(FailurePhase phase); + std::string raw_headers_; const scoped_refptr<base::TaskRunner> task_runner_;
diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc index 297045d..22da7db 100644 --- a/net/tools/flip_server/spdy_interface.cc +++ b/net/tools/flip_server/spdy_interface.cc
@@ -285,6 +285,8 @@ } void SpdySM::OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, const SpdyHeaderBlock& headers) { VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")";
diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index de53e87..15cf0e9 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h
@@ -81,6 +81,8 @@ // Called after all the header data for HEADERS control frame is received. void OnHeaders(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, bool fin, const SpdyHeaderBlock& headers) override;
diff --git a/net/tools/flip_server/spdy_interface_test.cc b/net/tools/flip_server/spdy_interface_test.cc index 9e66d0c..09909e2 100644 --- a/net/tools/flip_server/spdy_interface_test.cc +++ b/net/tools/flip_server/spdy_interface_test.cc
@@ -52,7 +52,12 @@ bool, const SpdyHeaderBlock&)); MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); - MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); + MOCK_METHOD5(OnHeaders, + void(SpdyStreamId, + bool, + SpdyPriority, + bool, + const SpdyHeaderBlock&)); MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool)); MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId, const char*, @@ -471,8 +476,9 @@ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) .WillOnce(SaveArg<2>(&actual_header_block)); } else { - EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + EXPECT_CALL(*spdy_framer_visitor_, + OnHeaders(stream_id, false, 0, false, _)) + .WillOnce(SaveArg<4>(&actual_header_block)); } EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*spdy_framer_visitor_, @@ -522,8 +528,8 @@ .WillOnce(SaveArg<2>(&actual_header_block)); } else { EXPECT_CALL(*spdy_framer_visitor_, - OnHeaders(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + OnHeaders(stream_id, false, 0, false, _)) + .WillOnce(SaveArg<4>(&actual_header_block)); } EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*spdy_framer_visitor_, @@ -641,8 +647,9 @@ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) .WillOnce(SaveArg<2>(&actual_header_block)); } else { - EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + EXPECT_CALL(*spdy_framer_visitor_, + OnHeaders(stream_id, false, 0, false, _)) + .WillOnce(SaveArg<4>(&actual_header_block)); } } @@ -677,8 +684,9 @@ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) .WillOnce(SaveArg<2>(&actual_header_block)); } else { - EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + EXPECT_CALL(*spdy_framer_visitor_, + OnHeaders(stream_id, false, 0, false, _)) + .WillOnce(SaveArg<4>(&actual_header_block)); } } @@ -854,8 +862,9 @@ EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) .WillOnce(SaveArg<2>(&actual_header_block)); } else { - EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _)) - .WillOnce(SaveArg<2>(&actual_header_block)); + EXPECT_CALL(*spdy_framer_visitor_, + OnHeaders(stream_id, false, 0, false, _)) + .WillOnce(SaveArg<4>(&actual_header_block)); } EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*spdy_framer_visitor_,
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 55e2d88..1fb3e79 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc
@@ -924,7 +924,7 @@ // Client tries to request twice the server's max initial window, and the // server limits it to the max. client_config_.SetInitialCongestionWindowToSend(2 * kMaxInitialWindow); - client_config_.SetInitialRoundTripTimeUsToSend(1000); + client_config_.SetInitialRoundTripTimeUsToSend(20000); ASSERT_TRUE(Initialize()); client_->client()->WaitForCryptoHandshakeConfirmed(); @@ -949,9 +949,9 @@ EXPECT_EQ(GetParam().use_pacing, server_sent_packet_manager.using_pacing()); EXPECT_EQ(GetParam().use_pacing, client_sent_packet_manager.using_pacing()); - // The client *should* set the intitial RTT. - EXPECT_EQ(1000u, client_sent_packet_manager.GetRttStats()->initial_rtt_us()); - EXPECT_EQ(1000u, server_sent_packet_manager.GetRttStats()->initial_rtt_us()); + // The client *should* set the intitial RTT, but it's increased to 10ms. + EXPECT_EQ(20000u, client_sent_packet_manager.GetRttStats()->initial_rtt_us()); + EXPECT_EQ(20000u, server_sent_packet_manager.GetRttStats()->initial_rtt_us()); // Now use the negotiated limits with packet loss. SetPacketLossPercentage(30);
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index 0dcbd25..93de5ca 100755 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py
@@ -1976,6 +1976,7 @@ if self.options.server_type == SERVER_HTTP: if self.options.https: pem_cert_and_key = None + ocsp_der = None if self.options.cert_and_key_file: if not os.path.isfile(self.options.cert_and_key_file): raise testserver_base.OptionError( @@ -1988,7 +1989,6 @@ print ('OCSP server started on %s:%d...' % (host, self.__ocsp_server.server_port)) - ocsp_der = None ocsp_state = None if self.options.ocsp == 'ok': @@ -2012,7 +2012,11 @@ ocsp_state = ocsp_state, serial = self.options.cert_serial) - self.__ocsp_server.ocsp_response = ocsp_der + if self.options.ocsp_server_unavailable: + # SEQUENCE containing ENUMERATED with value 3 (tryLater). + self.__ocsp_server.ocsp_response = '30030a0103'.decode('hex') + else: + self.__ocsp_server.ocsp_response = ocsp_der for ca_cert in self.options.ssl_client_ca: if not os.path.isfile(ca_cert): @@ -2021,8 +2025,8 @@ ' exiting...') stapled_ocsp_response = None - if self.__ocsp_server and self.options.staple_ocsp_response: - stapled_ocsp_response = self.__ocsp_server.ocsp_response + if self.options.staple_ocsp_response: + stapled_ocsp_response = ocsp_der server = HTTPSServer((host, port), TestPageHandler, pem_cert_and_key, self.options.ssl_client_auth, @@ -2269,6 +2273,12 @@ self.option_parser.add_option('--ws-basic-auth', action='store_true', dest='ws_basic_auth', help='Enable basic-auth for WebSocket') + self.option_parser.add_option('--ocsp-server-unavailable', + dest='ocsp_server_unavailable', + default=False, action='store_true', + help='If set, the OCSP server will return ' + 'a tryLater status rather than the actual ' + 'OCSP response.') if __name__ == '__main__':
diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc index b18aa0a..4c307fd 100644 --- a/net/udp/udp_socket_win.cc +++ b/net/udp/udp_socket_win.cc
@@ -630,7 +630,7 @@ int UDPSocketWin::InternalRecvFrom(IOBuffer* buf, int buf_len, IPEndPoint* address) { - DCHECK(!core_->read_iobuffer_); + DCHECK(!core_->read_iobuffer_.get()); SockaddrStorage& storage = core_->recv_addr_storage_; storage.addr_len = sizeof(storage.addr_storage); @@ -670,7 +670,7 @@ int UDPSocketWin::InternalSendTo(IOBuffer* buf, int buf_len, const IPEndPoint* address) { - DCHECK(!core_->write_iobuffer_); + DCHECK(!core_->write_iobuffer_.get()); SockaddrStorage storage; struct sockaddr* addr = storage.addr; // Convert address.
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc index 666bd03..04c190c 100644 --- a/net/url_request/url_request_context_builder.cc +++ b/net/url_request/url_request_context_builder.cc
@@ -351,6 +351,8 @@ http_network_session_params_.trusted_spdy_proxy; network_session_params.next_protos = http_network_session_params_.next_protos; network_session_params.enable_quic = http_network_session_params_.enable_quic; + network_session_params.quic_connection_options = + http_network_session_params_.quic_connection_options; HttpTransactionFactory* http_transaction_factory = NULL; if (http_cache_enabled_) {
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h index 9053372..a7d9d7b 100644 --- a/net/url_request/url_request_context_builder.h +++ b/net/url_request/url_request_context_builder.h
@@ -27,6 +27,7 @@ #include "net/dns/host_resolver.h" #include "net/proxy/proxy_config_service.h" #include "net/proxy/proxy_service.h" +#include "net/quic/quic_protocol.h" #include "net/socket/next_proto.h" namespace net { @@ -72,6 +73,7 @@ std::string trusted_spdy_proxy; bool use_alternate_protocols; bool enable_quic; + QuicTagVector quic_connection_options; }; URLRequestContextBuilder(); @@ -162,6 +164,12 @@ void SetSpdyAndQuicEnabled(bool spdy_enabled, bool quic_enabled); + void set_quic_connection_options( + const QuicTagVector& quic_connection_options) { + http_network_session_params_.quic_connection_options = + quic_connection_options; + } + void set_throttling_enabled(bool throttling_enabled) { throttling_enabled_ = throttling_enabled; }
diff --git a/net/url_request/url_request_file_job_unittest.cc b/net/url_request/url_request_file_job_unittest.cc index f4d7f7a..23ded74 100644 --- a/net/url_request/url_request_file_job_unittest.cc +++ b/net/url_request/url_request_file_job_unittest.cc
@@ -82,6 +82,19 @@ return job; } + net::URLRequestJob* MaybeInterceptRedirect( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const GURL& location) const override { + return nullptr; + } + + net::URLRequestJob* MaybeInterceptResponse( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + return nullptr; + } + bool IsHandledProtocol(const std::string& scheme) const override { return scheme == "file"; }
diff --git a/net/url_request/url_request_intercepting_job_factory.cc b/net/url_request/url_request_intercepting_job_factory.cc index 43789e1..ea2d1f2 100644 --- a/net/url_request/url_request_intercepting_job_factory.cc +++ b/net/url_request/url_request_intercepting_job_factory.cc
@@ -32,6 +32,33 @@ scheme, request, network_delegate); } +URLRequestJob* URLRequestInterceptingJobFactory::MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const { + DCHECK(CalledOnValidThread()); + URLRequestJob* job = interceptor_->MaybeInterceptRedirect(request, + network_delegate, + location); + if (job) + return job; + return job_factory_->MaybeInterceptRedirect(request, + network_delegate, + location); +} + +URLRequestJob* URLRequestInterceptingJobFactory::MaybeInterceptResponse( + URLRequest* request, + NetworkDelegate* network_delegate) const { + DCHECK(CalledOnValidThread()); + URLRequestJob* job = interceptor_->MaybeInterceptResponse(request, + network_delegate); + if (job) + return job; + return job_factory_->MaybeInterceptResponse(request, + network_delegate); +} + bool URLRequestInterceptingJobFactory::IsHandledProtocol( const std::string& scheme) const { return job_factory_->IsHandledProtocol(scheme);
diff --git a/net/url_request/url_request_intercepting_job_factory.h b/net/url_request/url_request_intercepting_job_factory.h index 857dbf4..056b385 100644 --- a/net/url_request/url_request_intercepting_job_factory.h +++ b/net/url_request/url_request_intercepting_job_factory.h
@@ -42,6 +42,16 @@ const std::string& scheme, URLRequest* request, NetworkDelegate* network_delegate) const override; + + URLRequestJob* MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const override; + + URLRequestJob* MaybeInterceptResponse( + URLRequest* request, + NetworkDelegate* network_delegate) const override; + bool IsHandledProtocol(const std::string& scheme) const override; bool IsHandledURL(const GURL& url) const override; bool IsSafeRedirectTarget(const GURL& location) const override;
diff --git a/net/url_request/url_request_interceptor.cc b/net/url_request/url_request_interceptor.cc index bcd0fd5..b93611b 100644 --- a/net/url_request/url_request_interceptor.cc +++ b/net/url_request/url_request_interceptor.cc
@@ -12,4 +12,16 @@ URLRequestInterceptor::~URLRequestInterceptor() { } +URLRequestJob* URLRequestInterceptor::MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const { + return nullptr; +} + +URLRequestJob* URLRequestInterceptor::MaybeInterceptResponse( + URLRequest* request, NetworkDelegate* network_delegate) const { + return nullptr; +} + } // namespace net
diff --git a/net/url_request/url_request_interceptor.h b/net/url_request/url_request_interceptor.h index 682368d..f7d5275 100644 --- a/net/url_request/url_request_interceptor.h +++ b/net/url_request/url_request_interceptor.h
@@ -8,6 +8,8 @@ #include "base/macros.h" #include "net/base/net_export.h" +class GURL; + namespace net { class URLRequest; @@ -28,6 +30,20 @@ virtual URLRequestJob* MaybeInterceptRequest( URLRequest* request, NetworkDelegate* network_delegate) const = 0; + // Returns a URLRequestJob to handle |request|, if the interceptor wants to + // take over the handling of the request after a redirect is received, + // instead of using the default ProtocolHandler. Otherwise, returns NULL. + virtual URLRequestJob* MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const; + + // Returns a URLRequestJob to handle |request, if the interceptor wants to + // take over the handling of the request after a response has started, + // instead of using the default ProtocolHandler. Otherwise, returns NULL. + virtual URLRequestJob* MaybeInterceptResponse( + URLRequest* request, NetworkDelegate* network_delegate) const; + private: DISALLOW_COPY_AND_ASSIGN(URLRequestInterceptor); };
diff --git a/net/url_request/url_request_job_factory.h b/net/url_request/url_request_job_factory.h index d4cc49e..96f218a 100644 --- a/net/url_request/url_request_job_factory.h +++ b/net/url_request/url_request_job_factory.h
@@ -47,6 +47,15 @@ URLRequest* request, NetworkDelegate* network_delegate) const = 0; + virtual URLRequestJob* MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const = 0; + + virtual URLRequestJob* MaybeInterceptResponse( + URLRequest* request, + NetworkDelegate* network_delegate) const = 0; + virtual bool IsHandledProtocol(const std::string& scheme) const = 0; virtual bool IsHandledURL(const GURL& url) const = 0;
diff --git a/net/url_request/url_request_job_factory_impl.cc b/net/url_request/url_request_job_factory_impl.cc index 57e775b..264a7a1 100644 --- a/net/url_request/url_request_job_factory_impl.cc +++ b/net/url_request/url_request_job_factory_impl.cc
@@ -63,6 +63,19 @@ return it->second->MaybeCreateJob(request, network_delegate); } +URLRequestJob* URLRequestJobFactoryImpl::MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const { + return nullptr; +} + +URLRequestJob* URLRequestJobFactoryImpl::MaybeInterceptResponse( + URLRequest* request, + NetworkDelegate* network_delegate) const { + return nullptr; +} + bool URLRequestJobFactoryImpl::IsHandledProtocol( const std::string& scheme) const { DCHECK(CalledOnValidThread());
diff --git a/net/url_request/url_request_job_factory_impl.h b/net/url_request/url_request_job_factory_impl.h index 695661e..8d1d8d9 100644 --- a/net/url_request/url_request_job_factory_impl.h +++ b/net/url_request/url_request_job_factory_impl.h
@@ -33,6 +33,16 @@ const std::string& scheme, URLRequest* request, NetworkDelegate* network_delegate) const override; + + URLRequestJob* MaybeInterceptRedirect( + URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const override; + + URLRequestJob* MaybeInterceptResponse( + URLRequest* request, + NetworkDelegate* network_delegate) const override; + bool IsHandledProtocol(const std::string& scheme) const override; bool IsHandledURL(const GURL& url) const override; bool IsSafeRedirectTarget(const GURL& location) const override;
diff --git a/net/url_request/url_request_job_manager.cc b/net/url_request/url_request_job_manager.cc index 0fe557b..6fcff22 100644 --- a/net/url_request/url_request_job_manager.cc +++ b/net/url_request/url_request_job_manager.cc
@@ -128,6 +128,13 @@ if (job) return job; } + + URLRequestJob* job = + request->context()->job_factory()->MaybeInterceptRedirect( + request, network_delegate, location); + if (job) + return job; + return NULL; } @@ -154,6 +161,13 @@ if (job) return job; } + + URLRequestJob* job = + request->context()->job_factory()->MaybeInterceptResponse( + request, network_delegate); + if (job) + return job; + return NULL; }
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index 82d8579..8f70ee7 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc
@@ -317,11 +317,14 @@ blocked_set_cookie_count_(0), set_cookie_count_(0), observed_before_proxy_headers_sent_callbacks_(0), + before_send_headers_count_(0), + headers_received_count_(0), has_load_timing_info_before_redirect_(false), has_load_timing_info_before_auth_(false), can_access_files_(true), can_throttle_requests_(true), - cancel_request_with_policy_violating_referrer_(false) { + cancel_request_with_policy_violating_referrer_(false), + will_be_intercepted_on_next_error_(false) { } TestNetworkDelegate::~TestNetworkDelegate() { @@ -386,7 +389,7 @@ next_states_[req_id] = kStageSendHeaders | kStageCompletedError; // request canceled by delegate - + before_send_headers_count_++; return OK; } @@ -406,9 +409,11 @@ event_order_[req_id] += "OnSendHeaders\n"; EXPECT_TRUE(next_states_[req_id] & kStageSendHeaders) << event_order_[req_id]; - next_states_[req_id] = - kStageHeadersReceived | - kStageCompletedError; + if (!will_be_intercepted_on_next_error_) + next_states_[req_id] = kStageHeadersReceived | kStageCompletedError; + else + next_states_[req_id] = kStageResponseStarted; + will_be_intercepted_on_next_error_ = false; } int TestNetworkDelegate::OnHeadersReceived( @@ -445,7 +450,7 @@ if (!allowed_unsafe_redirect_url_.is_empty()) *allowed_unsafe_redirect_url = allowed_unsafe_redirect_url_; } - + headers_received_count_++; return OK; }
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h index 5784931..1289db7 100644 --- a/net/url_request/url_request_test_util.h +++ b/net/url_request/url_request_test_util.h
@@ -275,12 +275,18 @@ int observed_before_proxy_headers_sent_callbacks() const { return observed_before_proxy_headers_sent_callbacks_; } + int before_send_headers_count() const { return before_send_headers_count_; } + int headers_received_count() const { return headers_received_count_; } // Last observed proxy in proxy header sent callback. HostPortPair last_observed_proxy() { return last_observed_proxy_; } + void set_can_be_intercepted_on_error(bool can_be_intercepted_on_error) { + will_be_intercepted_on_next_error_ = can_be_intercepted_on_error; + } + protected: // NetworkDelegate: int OnBeforeURLRequest(URLRequest* request, @@ -343,6 +349,8 @@ int blocked_set_cookie_count_; int set_cookie_count_; int observed_before_proxy_headers_sent_callbacks_; + int before_send_headers_count_; + int headers_received_count_; // Last observed proxy in before proxy header sent callback. HostPortPair last_observed_proxy_; @@ -365,6 +373,7 @@ bool can_access_files_; // true by default bool can_throttle_requests_; // true by default bool cancel_request_with_policy_violating_referrer_; // false by default + bool will_be_intercepted_on_next_error_; }; // Overrides the host used by the LocalHttpTestServer in
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 139b6f3..2aeecc6 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc
@@ -72,6 +72,8 @@ #include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_http_job.h" +#include "net/url_request/url_request_intercepting_job_factory.h" +#include "net/url_request/url_request_interceptor.h" #include "net/url_request/url_request_job_factory_impl.h" #include "net/url_request/url_request_redirect_job.h" #include "net/url_request/url_request_test_job.h" @@ -607,31 +609,52 @@ URLRequestTest() : default_context_(true) { default_context_.set_network_delegate(&default_network_delegate_); default_context_.set_net_log(&net_log_); - job_factory_.SetProtocolHandler("data", new DataProtocolHandler); -#if !defined(DISABLE_FILE_SUPPORT) - job_factory_.SetProtocolHandler( - "file", new FileProtocolHandler(base::MessageLoopProxy::current())); -#endif - default_context_.set_job_factory(&job_factory_); - default_context_.Init(); + job_factory_impl_ = new URLRequestJobFactoryImpl(); + job_factory_.reset(job_factory_impl_); } + ~URLRequestTest() override { // URLRequestJobs may post clean-up tasks on destruction. base::RunLoop().RunUntilIdle(); } + virtual void SetUp() { + SetUpFactory(); + default_context_.set_job_factory(job_factory_.get()); + default_context_.Init(); + PlatformTest::SetUp(); + } + + virtual void SetUpFactory() { + job_factory_impl_->SetProtocolHandler("data", new DataProtocolHandler); +#if !defined(DISABLE_FILE_SUPPORT) + job_factory_impl_->SetProtocolHandler( + "file", new FileProtocolHandler(base::MessageLoopProxy::current())); +#endif + } + + TestNetworkDelegate* default_network_delegate() { + return &default_network_delegate_; + } + + const TestURLRequestContext& default_context() const { + return default_context_; + } + + // Adds the TestJobInterceptor to the default context. TestJobInterceptor* AddTestInterceptor() { TestJobInterceptor* protocol_handler_ = new TestJobInterceptor(); - job_factory_.SetProtocolHandler("http", NULL); - job_factory_.SetProtocolHandler("http", protocol_handler_); + job_factory_impl_->SetProtocolHandler("http", NULL); + job_factory_impl_->SetProtocolHandler("http", protocol_handler_); return protocol_handler_; } protected: CapturingNetLog net_log_; TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. - URLRequestJobFactoryImpl job_factory_; + URLRequestJobFactoryImpl* job_factory_impl_; + scoped_ptr<URLRequestJobFactory> job_factory_; TestURLRequestContext default_context_; }; @@ -1574,29 +1597,570 @@ EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); } -LoadTimingInfo RunLoadTimingTest(const LoadTimingInfo& job_load_timing, - URLRequestContext* context) { - TestInterceptor interceptor; - interceptor.intercept_main_request_ = true; - interceptor.main_request_load_timing_info_ = job_load_timing; +// An Interceptor for use with interceptor tests. +class MockURLRequestInterceptor : public URLRequestInterceptor { + public: + // Static getters for canned response header and data strings. + static std::string ok_data() { + return URLRequestTestJob::test_data_1(); + } + + static std::string ok_headers() { + return URLRequestTestJob::test_headers(); + } + + static std::string redirect_data() { + return std::string(); + } + + static std::string redirect_headers() { + return URLRequestTestJob::test_redirect_headers(); + } + + static std::string error_data() { + return std::string("ohhh nooooo mr. bill!"); + } + + static std::string error_headers() { + return URLRequestTestJob::test_error_headers(); + } + + MockURLRequestInterceptor() + : intercept_main_request_(false), restart_main_request_(false), + cancel_main_request_(false), cancel_then_restart_main_request_(false), + simulate_main_network_error_(false), + intercept_redirect_(false), cancel_redirect_request_(false), + intercept_final_response_(false), cancel_final_request_(false), + use_url_request_http_job_(false), + did_intercept_main_(false), did_restart_main_(false), + did_cancel_main_(false), did_cancel_then_restart_main_(false), + did_simulate_error_main_(false), + did_intercept_redirect_(false), did_cancel_redirect_(false), + did_intercept_final_(false), did_cancel_final_(false) { + } + + ~MockURLRequestInterceptor() override { + } + + // URLRequestInterceptor implementation: + URLRequestJob* MaybeInterceptRequest( + URLRequest* request, + NetworkDelegate* network_delegate) const override { + if (restart_main_request_) { + restart_main_request_ = false; + did_restart_main_ = true; + return new RestartTestJob(request, network_delegate); + } + if (cancel_main_request_) { + cancel_main_request_ = false; + did_cancel_main_ = true; + return new CancelTestJob(request, network_delegate); + } + if (cancel_then_restart_main_request_) { + cancel_then_restart_main_request_ = false; + did_cancel_then_restart_main_ = true; + return new CancelThenRestartTestJob(request, network_delegate); + } + if (simulate_main_network_error_) { + simulate_main_network_error_ = false; + did_simulate_error_main_ = true; + if (use_url_request_http_job_) { + return URLRequestHttpJob::Factory(request, network_delegate, "http"); + } + // This job will result in error since the requested URL is not one of the + // URLs supported by these tests. + return new URLRequestTestJob(request, network_delegate, true); + } + if (!intercept_main_request_) + return nullptr; + intercept_main_request_ = false; + did_intercept_main_ = true; + URLRequestTestJob* job = new URLRequestTestJob(request, + network_delegate, + main_headers_, + main_data_, + true); + job->set_load_timing_info(main_request_load_timing_info_); + return job; + } + + URLRequestJob* MaybeInterceptRedirect(URLRequest* request, + NetworkDelegate* network_delegate, + const GURL& location) const override { + if (cancel_redirect_request_) { + cancel_redirect_request_ = false; + did_cancel_redirect_ = true; + return new CancelTestJob(request, network_delegate); + } + if (!intercept_redirect_) + return nullptr; + intercept_redirect_ = false; + did_intercept_redirect_ = true; + if (use_url_request_http_job_) { + return URLRequestHttpJob::Factory(request, network_delegate, "http"); + } + return new URLRequestTestJob(request, + network_delegate, + redirect_headers_, + redirect_data_, + true); + } + + URLRequestJob* MaybeInterceptResponse( + URLRequest* request, + NetworkDelegate* network_delegate) const override { + if (cancel_final_request_) { + cancel_final_request_ = false; + did_cancel_final_ = true; + return new CancelTestJob(request, network_delegate); + } + if (!intercept_final_response_) + return nullptr; + intercept_final_response_ = false; + did_intercept_final_ = true; + if (use_url_request_http_job_) { + return URLRequestHttpJob::Factory(request, network_delegate, "http"); + } + return new URLRequestTestJob(request, + network_delegate, + final_headers_, + final_data_, + true); + } + + void set_intercept_main_request(bool intercept_main_request) { + intercept_main_request_ = intercept_main_request; + } + + void set_main_headers(const std::string& main_headers) { + main_headers_ = main_headers; + } + + void set_main_data(const std::string& main_data) { + main_data_ = main_data; + } + + void set_main_request_load_timing_info( + const LoadTimingInfo& main_request_load_timing_info) { + main_request_load_timing_info_ = main_request_load_timing_info; + } + + void set_restart_main_request(bool restart_main_request) { + restart_main_request_ = restart_main_request; + } + + void set_cancel_main_request(bool cancel_main_request) { + cancel_main_request_ = cancel_main_request; + } + + void set_cancel_then_restart_main_request( + bool cancel_then_restart_main_request) { + cancel_then_restart_main_request_ = cancel_then_restart_main_request; + } + + void set_simulate_main_network_error(bool simulate_main_network_error) { + simulate_main_network_error_ = simulate_main_network_error; + } + + void set_intercept_redirect(bool intercept_redirect) { + intercept_redirect_ = intercept_redirect; + } + + void set_redirect_headers(const std::string& redirect_headers) { + redirect_headers_ = redirect_headers; + } + + void set_redirect_data(const std::string& redirect_data) { + redirect_data_ = redirect_data; + } + + void set_cancel_redirect_request(bool cancel_redirect_request) { + cancel_redirect_request_ = cancel_redirect_request; + } + + void set_intercept_final_response(bool intercept_final_response) { + intercept_final_response_ = intercept_final_response; + } + + void set_final_headers(const std::string& final_headers) { + final_headers_ = final_headers; + } + + void set_final_data(const std::string& final_data) { + final_data_ = final_data; + } + + void set_cancel_final_request(bool cancel_final_request) { + cancel_final_request_ = cancel_final_request; + } + + void set_use_url_request_http_job(bool use_url_request_http_job) { + use_url_request_http_job_ = use_url_request_http_job; + } + + bool did_intercept_main() const { + return did_intercept_main_; + } + + bool did_restart_main() const { + return did_restart_main_; + } + + bool did_cancel_main() const { + return did_cancel_main_; + } + + bool did_cancel_then_restart_main() const { + return did_cancel_then_restart_main_; + } + + bool did_simulate_error_main() const { + return did_simulate_error_main_; + } + + bool did_intercept_redirect() const { + return did_intercept_redirect_; + } + + bool did_cancel_redirect() const { + return did_cancel_redirect_; + } + + bool did_intercept_final() const { + return did_intercept_final_; + } + + bool did_cancel_final() const { + return did_cancel_final_; + } + + private: + // Indicate whether to intercept the main request, and if so specify the + // response to return and the LoadTimingInfo to use. + mutable bool intercept_main_request_; + mutable std::string main_headers_; + mutable std::string main_data_; + mutable LoadTimingInfo main_request_load_timing_info_; + + // These indicate actions that can be taken within MaybeInterceptRequest. + mutable bool restart_main_request_; + mutable bool cancel_main_request_; + mutable bool cancel_then_restart_main_request_; + mutable bool simulate_main_network_error_; + + // Indicate whether to intercept redirects, and if so specify the response to + // return. + mutable bool intercept_redirect_; + mutable std::string redirect_headers_; + mutable std::string redirect_data_; + + // Cancel the request within MaybeInterceptRedirect. + mutable bool cancel_redirect_request_; + + // Indicate whether to intercept the final response, and if so specify the + // response to return. + mutable bool intercept_final_response_; + mutable std::string final_headers_; + mutable std::string final_data_; + + // Cancel the final request within MaybeInterceptResponse. + mutable bool cancel_final_request_; + + // Instruct the interceptor to use a real URLRequestHTTPJob. + mutable bool use_url_request_http_job_; + + // These indicate if the interceptor did something or not. + mutable bool did_intercept_main_; + mutable bool did_restart_main_; + mutable bool did_cancel_main_; + mutable bool did_cancel_then_restart_main_; + mutable bool did_simulate_error_main_; + mutable bool did_intercept_redirect_; + mutable bool did_cancel_redirect_; + mutable bool did_intercept_final_; + mutable bool did_cancel_final_; +}; + +// Inherit PlatformTest since we require the autorelease pool on Mac OS X. +class URLRequestInterceptorTest : public URLRequestTest { + public: + URLRequestInterceptorTest() : URLRequestTest(), interceptor_(NULL) { + } + + ~URLRequestInterceptorTest() override { + // URLRequestJobs may post clean-up tasks on destruction. + base::RunLoop().RunUntilIdle(); + } + + void SetUpFactory() override { + interceptor_ = new MockURLRequestInterceptor(); + job_factory_.reset(new URLRequestInterceptingJobFactory( + job_factory_.Pass(), make_scoped_ptr(interceptor_))); + } + + MockURLRequestInterceptor* interceptor() const { + return interceptor_; + } + + private: + MockURLRequestInterceptor* interceptor_; +}; + +TEST_F(URLRequestInterceptorTest, Intercept) { + // Intercept the main request and respond with a simple response. + interceptor()->set_intercept_main_request(true); + interceptor()->set_main_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_main_data(MockURLRequestInterceptor::ok_data()); TestDelegate d; - scoped_ptr<URLRequest> req(context->CreateRequest( - GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, NULL)); + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + base::SupportsUserData::Data* user_data0 = new base::SupportsUserData::Data(); + base::SupportsUserData::Data* user_data1 = new base::SupportsUserData::Data(); + base::SupportsUserData::Data* user_data2 = new base::SupportsUserData::Data(); + req->SetUserData(nullptr, user_data0); + req->SetUserData(&user_data1, user_data1); + req->SetUserData(&user_data2, user_data2); + req->set_method("GET"); req->Start(); base::RunLoop().Run(); - LoadTimingInfo resulting_load_timing; - req->GetLoadTimingInfo(&resulting_load_timing); + // Make sure we can retrieve our specific user data. + EXPECT_EQ(user_data0, req->GetUserData(nullptr)); + EXPECT_EQ(user_data1, req->GetUserData(&user_data1)); + EXPECT_EQ(user_data2, req->GetUserData(&user_data2)); - // None of these should be modified by the URLRequest. - EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused); - EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id); - EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start); - EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end); - EXPECT_EQ(job_load_timing.receive_headers_end, - resulting_load_timing.receive_headers_end); + // Check that we got one good response. + EXPECT_TRUE(req->status().is_success()); + EXPECT_EQ(200, req->response_headers()->response_code()); + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); +} - return resulting_load_timing; +TEST_F(URLRequestInterceptorTest, InterceptRedirect) { + // Intercept the main request and respond with a redirect. + interceptor()->set_intercept_main_request(true); + interceptor()->set_main_headers( + MockURLRequestInterceptor::redirect_headers()); + interceptor()->set_main_data(MockURLRequestInterceptor::redirect_data()); + + // Intercept that redirect and respond with a final OK response. + interceptor()->set_intercept_redirect(true); + interceptor()->set_redirect_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_redirect_data(MockURLRequestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_intercept_main()); + EXPECT_TRUE(interceptor()->did_intercept_redirect()); + + // Check that we got one good response. + EXPECT_TRUE(req->status().is_success()); + if (req->status().is_success()) + EXPECT_EQ(200, req->response_headers()->response_code()); + + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); +} + +TEST_F(URLRequestInterceptorTest, InterceptServerError) { + // Intercept the main request to generate a server error response. + interceptor()->set_intercept_main_request(true); + interceptor()->set_main_headers(MockURLRequestInterceptor::error_headers()); + interceptor()->set_main_data(MockURLRequestInterceptor::error_data()); + + // Intercept that error and respond with an OK response. + interceptor()->set_intercept_final_response(true); + interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_intercept_main()); + EXPECT_TRUE(interceptor()->did_intercept_final()); + + // Check that we got one good response. + EXPECT_TRUE(req->status().is_success()); + EXPECT_EQ(200, req->response_headers()->response_code()); + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); +} + +TEST_F(URLRequestInterceptorTest, InterceptNetworkError) { + // Intercept the main request to simulate a network error. + interceptor()->set_simulate_main_network_error(true); + + // Intercept that error and respond with an OK response. + interceptor()->set_intercept_final_response(true); + interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_simulate_error_main()); + EXPECT_TRUE(interceptor()->did_intercept_final()); + + // Check that we received one good response. + EXPECT_TRUE(req->status().is_success()); + EXPECT_EQ(200, req->response_headers()->response_code()); + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); +} + +TEST_F(URLRequestInterceptorTest, InterceptRestartRequired) { + // Restart the main request. + interceptor()->set_restart_main_request(true); + + // then intercept the new main request and respond with an OK response + interceptor()->set_intercept_main_request(true); + interceptor()->set_main_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_main_data(MockURLRequestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_restart_main()); + EXPECT_TRUE(interceptor()->did_intercept_main()); + + // Check that we received one good response. + EXPECT_TRUE(req->status().is_success()); + if (req->status().is_success()) + EXPECT_EQ(200, req->response_headers()->response_code()); + + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); +} + +TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelMain) { + // Intercept the main request and cancel from within the restarted job. + interceptor()->set_cancel_main_request(true); + + // Set up to intercept the final response and override it with an OK response. + interceptor()->set_intercept_final_response(true); + interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_cancel_main()); + EXPECT_FALSE(interceptor()->did_intercept_final()); + + // Check that we see a canceled request. + EXPECT_FALSE(req->status().is_success()); + EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); +} + +TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelRedirect) { + // Intercept the main request and respond with a redirect. + interceptor()->set_intercept_main_request(true); + interceptor()->set_main_headers( + MockURLRequestInterceptor::redirect_headers()); + interceptor()->set_main_data(MockURLRequestInterceptor::redirect_data()); + + // Intercept the redirect and cancel from within that job. + interceptor()->set_cancel_redirect_request(true); + + // Set up to intercept the final response and override it with an OK response. + interceptor()->set_intercept_final_response(true); + interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_intercept_main()); + EXPECT_TRUE(interceptor()->did_cancel_redirect()); + EXPECT_FALSE(interceptor()->did_intercept_final()); + + // Check that we see a canceled request. + EXPECT_FALSE(req->status().is_success()); + EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); +} + +TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelFinal) { + // Intercept the main request to simulate a network error. + interceptor()->set_simulate_main_network_error(true); + + // Set up to intercept final the response and cancel from within that job. + interceptor()->set_cancel_final_request(true); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_simulate_error_main()); + EXPECT_TRUE(interceptor()->did_cancel_final()); + + // Check that we see a canceled request. + EXPECT_FALSE(req->status().is_success()); + EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); +} + +TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelInRestart) { + // Intercept the main request and cancel then restart from within that job. + interceptor()->set_cancel_then_restart_main_request(true); + + // Set up to intercept the final response and override it with an OK response. + interceptor()->set_intercept_final_response(true); + interceptor()->set_final_headers(TestInterceptor::ok_headers()); + interceptor()->set_final_data(TestInterceptor::ok_data()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + // Check that the interceptor got called as expected. + EXPECT_TRUE(interceptor()->did_cancel_then_restart_main()); + EXPECT_FALSE(interceptor()->did_intercept_final()); + + // Check that we see a canceled request. + EXPECT_FALSE(req->status().is_success()); + EXPECT_EQ(URLRequestStatus::CANCELED, req->status().status()); } // "Normal" LoadTimingInfo as returned by a job. Everything is in order, not @@ -1649,14 +2213,41 @@ return load_timing; } +LoadTimingInfo RunURLRequestInterceptorLoadTimingTest( + const LoadTimingInfo& job_load_timing, + const URLRequestContext& context, + MockURLRequestInterceptor* interceptor) { + interceptor->set_intercept_main_request(true); + interceptor->set_main_request_load_timing_info(job_load_timing); + TestDelegate d; + scoped_ptr<URLRequest> req(context.CreateRequest( + GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, nullptr)); + req->Start(); + base::RunLoop().Run(); + + LoadTimingInfo resulting_load_timing; + req->GetLoadTimingInfo(&resulting_load_timing); + + // None of these should be modified by the URLRequest. + EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused); + EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id); + EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start); + EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end); + EXPECT_EQ(job_load_timing.receive_headers_end, + resulting_load_timing.receive_headers_end); + + return resulting_load_timing; +} + // Basic test that the intercept + load timing tests work. -TEST_F(URLRequestTest, InterceptLoadTiming) { +TEST_F(URLRequestInterceptorTest, InterceptLoadTiming) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, false); LoadTimingInfo load_timing_result = - RunLoadTimingTest(job_load_timing, &default_context_); + RunURLRequestInterceptorLoadTimingTest( + job_load_timing, default_context(), interceptor()); // Nothing should have been changed by the URLRequest. EXPECT_EQ(job_load_timing.proxy_resolve_start, @@ -1681,13 +2272,14 @@ } // Another basic test, with proxy and SSL times, but no DNS times. -TEST_F(URLRequestTest, InterceptLoadTimingProxy) { +TEST_F(URLRequestInterceptorTest, InterceptLoadTimingProxy) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true); LoadTimingInfo load_timing_result = - RunLoadTimingTest(job_load_timing, &default_context_); + RunURLRequestInterceptorLoadTimingTest( + job_load_timing, default_context(), interceptor()); // Nothing should have been changed by the URLRequest. EXPECT_EQ(job_load_timing.proxy_resolve_start, @@ -1718,7 +2310,7 @@ // reused in this test (May be a preconnect). // // To mix things up from the test above, assumes DNS times but no SSL times. -TEST_F(URLRequestTest, InterceptLoadTimingEarlyProxyResolution) { +TEST_F(URLRequestInterceptorTest, InterceptLoadTimingEarlyProxyResolution) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true); @@ -1732,7 +2324,8 @@ now - base::TimeDelta::FromDays(1); LoadTimingInfo load_timing_result = - RunLoadTimingTest(job_load_timing, &default_context_); + RunURLRequestInterceptorLoadTimingTest( + job_load_timing, default_context(), interceptor()); // Proxy times, connect times, and DNS times should all be replaced with // request_start. @@ -1755,14 +2348,16 @@ } // Same as above, but in the reused case. -TEST_F(URLRequestTest, InterceptLoadTimingEarlyProxyResolutionReused) { +TEST_F(URLRequestInterceptorTest, + InterceptLoadTimingEarlyProxyResolutionReused) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true); job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(4); job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(3); LoadTimingInfo load_timing_result = - RunLoadTimingTest(job_load_timing, &default_context_); + RunURLRequestInterceptorLoadTimingTest( + job_load_timing, default_context(), interceptor()); // Proxy times and connect times should all be replaced with request_start. EXPECT_EQ(load_timing_result.request_start, @@ -1779,7 +2374,7 @@ // not considered reused in this test (May be a preconnect). // // To mix things up, the request has SSL times, but no DNS times. -TEST_F(URLRequestTest, InterceptLoadTimingEarlyConnect) { +TEST_F(URLRequestInterceptorTest, InterceptLoadTimingEarlyConnect) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, false); @@ -1791,7 +2386,8 @@ now - base::TimeDelta::FromDays(4); LoadTimingInfo load_timing_result = - RunLoadTimingTest(job_load_timing, &default_context_); + RunURLRequestInterceptorLoadTimingTest( + job_load_timing, default_context(), interceptor()); // Connect times, and SSL times should be replaced with request_start. EXPECT_EQ(load_timing_result.request_start, @@ -1813,7 +2409,7 @@ // test (May be a preconnect). // // In this test, there are no SSL or DNS times. -TEST_F(URLRequestTest, InterceptLoadTimingEarlyConnectWithProxy) { +TEST_F(URLRequestInterceptorTest, InterceptLoadTimingEarlyConnectWithProxy) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY, true); @@ -1823,7 +2419,8 @@ now - base::TimeDelta::FromDays(2); LoadTimingInfo load_timing_result = - RunLoadTimingTest(job_load_timing, &default_context_); + RunURLRequestInterceptorLoadTimingTest( + job_load_timing, default_context(), interceptor()); // Connect times should be replaced with proxy_resolve_end. EXPECT_EQ(load_timing_result.proxy_resolve_end, @@ -2665,6 +3262,11 @@ return is_success; } + LocalHttpTestServer* test_server() { + return &test_server_; + } + + protected: LocalHttpTestServer test_server_; }; @@ -5166,7 +5768,7 @@ EXPECT_FALSE(data_protocol_handler.IsSafeRedirectTarget(data_url)); // Test URLRequestJobFactoryImpl::IsSafeRedirectTarget(). - EXPECT_FALSE(job_factory_.IsSafeRedirectTarget(data_url)); + EXPECT_FALSE(job_factory_->IsSafeRedirectTarget(data_url)); } #if !defined(DISABLE_FILE_SUPPORT) @@ -5177,7 +5779,7 @@ EXPECT_FALSE(file_protocol_handler.IsSafeRedirectTarget(file_url)); // Test URLRequestJobFactoryImpl::IsSafeRedirectTarget(). - EXPECT_FALSE(job_factory_.IsSafeRedirectTarget(file_url)); + EXPECT_FALSE(job_factory_->IsSafeRedirectTarget(file_url)); } TEST_F(URLRequestTestHTTP, RestrictFileRedirects) { @@ -6446,6 +7048,124 @@ EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, req->status().error()); } +class URLRequestInterceptorTestHTTP : public URLRequestTestHTTP { + public: + // TODO(bengr): Merge this with the URLRequestInterceptorHTTPTest fixture, + // ideally remove the dependency on URLRequestTestJob, and maybe move these + // tests into the factory tests. + URLRequestInterceptorTestHTTP() : URLRequestTestHTTP(), interceptor_(NULL) { + } + + void SetUpFactory() override { + interceptor_ = new MockURLRequestInterceptor(); + job_factory_.reset(new URLRequestInterceptingJobFactory( + job_factory_.Pass(), make_scoped_ptr(interceptor_))); + } + + MockURLRequestInterceptor* interceptor() const { + return interceptor_; + } + + private: + MockURLRequestInterceptor* interceptor_; +}; + +TEST_F(URLRequestInterceptorTestHTTP, + NetworkDelegateNotificationOnRedirectIntercept) { + interceptor()->set_intercept_redirect(true); + interceptor()->set_redirect_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_redirect_data(MockURLRequestInterceptor::ok_data()); + + ASSERT_TRUE(test_server()->Start()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + test_server()->GetURL("files/redirect-test.html"), DEFAULT_PRIORITY, + &d, nullptr)); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(interceptor()->did_intercept_redirect()); + // Check we got one good response + EXPECT_TRUE(req->status().is_success()); + if (req->status().is_success()) + EXPECT_EQ(200, req->response_headers()->response_code()); + + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); + + EXPECT_EQ(1, default_network_delegate()->created_requests()); + EXPECT_EQ(1, default_network_delegate()->before_send_headers_count()); + EXPECT_EQ(1, default_network_delegate()->headers_received_count()); +} + +TEST_F(URLRequestInterceptorTestHTTP, + NetworkDelegateNotificationOnErrorIntercept) { + // Intercept that error and respond with an OK response. + interceptor()->set_intercept_final_response(true); + interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers()); + interceptor()->set_final_data(MockURLRequestInterceptor::ok_data()); + default_network_delegate()->set_can_be_intercepted_on_error(true); + + ASSERT_TRUE(test_server()->Start()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + test_server()->GetURL("files/two-content-lengths.html"), DEFAULT_PRIORITY, + &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(interceptor()->did_intercept_final()); + + // Check we received one good response. + EXPECT_TRUE(req->status().is_success()); + if (req->status().is_success()) + EXPECT_EQ(200, req->response_headers()->response_code()); + EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); + + EXPECT_EQ(1, default_network_delegate()->created_requests()); + EXPECT_EQ(1, default_network_delegate()->before_send_headers_count()); + EXPECT_EQ(0, default_network_delegate()->headers_received_count()); +} + +TEST_F(URLRequestInterceptorTestHTTP, + NetworkDelegateNotificationOnResponseIntercept) { + // Intercept that error and respond with an OK response. + interceptor()->set_intercept_final_response(true); + + // Intercept with a real URLRequestHttpJob. + interceptor()->set_use_url_request_http_job(true); + + ASSERT_TRUE(test_server()->Start()); + + TestDelegate d; + scoped_ptr<URLRequest> req(default_context().CreateRequest( + test_server()->GetURL("files/simple.html"), DEFAULT_PRIORITY, + &d, nullptr)); + req->set_method("GET"); + req->Start(); + base::RunLoop().Run(); + + EXPECT_TRUE(interceptor()->did_intercept_final()); + + // Check we received one good response. + EXPECT_TRUE(req->status().is_success()); + if (req->status().is_success()) + EXPECT_EQ(200, req->response_headers()->response_code()); + EXPECT_EQ("hello", d.data_received()); + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(0, d.received_redirect_count()); + + EXPECT_EQ(1, default_network_delegate()->created_requests()); + EXPECT_EQ(2, default_network_delegate()->before_send_headers_count()); + EXPECT_EQ(2, default_network_delegate()->headers_received_count()); +} + class HTTPSRequestTest : public testing::Test { public: HTTPSRequestTest() : default_context_(true) { @@ -7590,7 +8310,7 @@ } static bool SystemSupportsOCSP() { -#if defined(USE_OPENSSL) +#if defined(USE_OPENSSL_CERTS) // http://crbug.com/117478 - OpenSSL does not support OCSP. return false; #elif defined(OS_WIN) @@ -7603,6 +8323,16 @@ #endif } +static bool SystemSupportsOCSPStapling() { +#if defined(USE_NSS) + return true; +#elif defined(OS_WIN) + return base::win::GetVersion() >= base::win::VERSION_VISTA; +#else + return false; +#endif +} + TEST_F(HTTPSOCSPTest, Valid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; @@ -7666,6 +8396,51 @@ EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } +TEST_F(HTTPSOCSPTest, ValidStapled) { + if (!SystemSupportsOCSPStapling()) { + LOG(WARNING) + << "Skipping test because system doesn't support OCSP stapling"; + return; + } + + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_AUTO); + ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_OK; + ssl_options.staple_ocsp_response = true; + ssl_options.ocsp_server_unavailable = true; + + CertStatus cert_status; + DoConnection(ssl_options, &cert_status); + + EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); + + EXPECT_EQ(SystemUsesChromiumEVMetadata(), + static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); + + EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_F(HTTPSOCSPTest, RevokedStapled) { + if (!SystemSupportsOCSPStapling()) { + LOG(WARNING) + << "Skipping test because system doesn't support OCSP stapling"; + return; + } + + SpawnedTestServer::SSLOptions ssl_options( + SpawnedTestServer::SSLOptions::CERT_AUTO); + ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_REVOKED; + ssl_options.staple_ocsp_response = true; + ssl_options.ocsp_server_unavailable = true; + + CertStatus cert_status; + DoConnection(ssl_options, &cert_status); + + EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); + EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); + EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + class HTTPSHardFailTest : public HTTPSOCSPTest { protected: void SetupContext(URLRequestContext* context) override { @@ -7677,7 +8452,6 @@ } }; - TEST_F(HTTPSHardFailTest, FailsOnOCSPInvalid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP";
diff --git a/net/websockets/PRESUBMIT.py b/net/websockets/PRESUBMIT.py deleted file mode 100644 index 1da441c..0000000 --- a/net/websockets/PRESUBMIT.py +++ /dev/null
@@ -1,57 +0,0 @@ -# Copyright 2013 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. - -"""Chromium presubmit script for src/net/websockets. - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details on the presubmit API built into gcl. -""" - - -# TODO(ricea): Remove this once the old implementation has been removed and the -# list of files in the README file is no longer needed. -def _CheckReadMeComplete(input_api, output_api): - """Verifies that any new files have been added to the README file. - - Checks that if any source files were added in this CL, that they were - also added to the README file. We do not warn about pre-existing - errors, as that would be annoying. - - Args: - input_api: The InputApi object provided by the presubmit framework. - output_api: The OutputApi object provided by the framework. - - Returns: - A list of zero or more PresubmitPromptWarning objects. - """ - # None passed to AffectedSourceFiles means "use the default filter", which - # does what we want, ie. returns files in the CL with filenames that look like - # source code. - added_source_filenames = set(input_api.basename(af.LocalPath()) - for af in input_api.AffectedSourceFiles(None) - if af.Action().startswith('A')) - if not added_source_filenames: - return [] - readme = input_api.AffectedSourceFiles( - lambda af: af.LocalPath().endswith('/README')) - if not readme: - return [output_api.PresubmitPromptWarning( - 'One or more files were added to net/websockets without being added\n' - 'to net/websockets/README.\n', added_source_filenames)] - readme_added_filenames = set(line.strip() for line in readme[0].NewContents() - if line.strip() in added_source_filenames) - if readme_added_filenames < added_source_filenames: - return [output_api.PresubmitPromptWarning( - 'One or more files added to net/websockets but not found in the README ' - 'file.\n', added_source_filenames - readme_added_filenames)] - else: - return [] - - -def CheckChangeOnUpload(input_api, output_api): - return _CheckReadMeComplete(input_api, output_api) - - -def CheckChangeOnCommit(input_api, output_api): - return _CheckReadMeComplete(input_api, output_api)
diff --git a/net/websockets/README b/net/websockets/README deleted file mode 100644 index efe7a59..0000000 --- a/net/websockets/README +++ /dev/null
@@ -1,91 +0,0 @@ -This directory contains files related to Chromium's WebSocket -implementation. See http://www.websocket.org/ for an explanation of WebSockets. - -As of April 2013, the contents of this directory are in a transitional state, -and contain parts of two different WebSocket implementations. - -The following files are part of the legacy implementation. The legacy -implementation performs WebSocket framing within Blink and presents a -low-level socket-like interface to the renderer process. It is described in the -design doc at -https://docs.google.com/a/google.com/document/d/1_R6YjCIrm4kikJ3YeapcOU2Keqr3lVUPd-OeaIJ93qQ/preview - -websocket_handshake_handler_test.cc -websocket_handshake_handler_spdy_test.cc -websocket_job.cc -websocket_job.h -websocket_job_test.cc -websocket_net_log_params.cc -websocket_net_log_params.h -websocket_net_log_params_test.cc -websocket_throttle.cc -websocket_throttle.h -websocket_throttle_test.cc - -The following files are part of the new implementation. The new implementation -performs framing and implements protocol semantics in the browser process, and -presents a high-level interface to the renderer process similar to a -multiplexing proxy. This is the default implementation from M38. - -websocket_basic_handshake_stream.cc -websocket_basic_handshake_stream.h -websocket_basic_stream.cc -websocket_basic_stream.h -websocket_basic_stream_test.cc -websocket_channel.cc -websocket_channel.h -websocket_channel_test.cc -websocket_deflate_predictor.h -websocket_deflate_predictor_impl.cc -websocket_deflate_predictor_impl.h -websocket_deflate_predictor_impl_test.cc -websocket_deflate_stream.cc -websocket_deflate_stream.h -websocket_deflate_stream_test.cc -websocket_deflater.cc -websocket_deflater.h -websocket_deflater_test.cc -websocket_errors.cc -websocket_errors.h -websocket_errors_test.cc -websocket_event_interface.h -websocket_extension.cc -websocket_extension.h -websocket_extension_parser.cc -websocket_extension_parser.h -websocket_extension_parser_test.cc -websocket_frame.cc -websocket_frame.h -websocket_frame_parser.cc -websocket_frame_parser.h -websocket_frame_parser_test.cc -websocket_frame_test.cc -websocket_frame_perftest.cc -websocket_handshake_stream_base.h -websocket_handshake_stream_create_helper.cc -websocket_handshake_stream_create_helper.h -websocket_handshake_stream_create_helper_test.cc -websocket_handshake_request_info.cc -websocket_handshake_request_info.h -websocket_handshake_response_info.cc -websocket_handshake_response_info.h -websocket_inflater.cc -websocket_inflater.h -websocket_inflater_test.cc -websocket_mux.h -websocket_stream.cc -websocket_stream.h -websocket_stream_test.cc -websocket_test_util.cc -websocket_test_util.h - -These files are shared between the old and new implementations. - -websocket_handshake_constants.cc -websocket_handshake_constants.h -websocket_handshake_handler.cc -websocket_handshake_handler.h - -A pre-submit check helps us keep this README file up-to-date: - -PRESUBMIT.py
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc index 598b5e6..30abb2db 100644 --- a/net/websockets/websocket_channel.cc +++ b/net/websockets/websocket_channel.cc
@@ -44,9 +44,16 @@ const int kDefaultSendQuotaLowWaterMark = 1 << 16; const int kDefaultSendQuotaHighWaterMark = 1 << 17; const size_t kWebSocketCloseCodeLength = 2; -// This timeout is based on TCPMaximumSegmentLifetime * 2 from -// MainThreadWebSocketChannel.cpp in Blink. -const int kClosingHandshakeTimeoutSeconds = 2 * 2 * 60; +// Timeout for waiting for the server to acknowledge a closing handshake. +const int kClosingHandshakeTimeoutSeconds = 60; +// We wait for the server to close the underlying connection as recommended in +// https://tools.ietf.org/html/rfc6455#section-7.1.1 +// We don't use 2MSL since there're server implementations that don't follow +// the recommendation and wait for the client to close the underlying +// connection. It leads to unnecessarily long time before CloseEvent +// invocation. We want to avoid this rather than strictly following the spec +// recommendation. +const int kUnderlyingConnectionCloseTimeoutSeconds = 2; typedef WebSocketEventInterface::ChannelState ChannelState; const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; @@ -296,7 +303,10 @@ send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark), current_send_quota_(0), current_receive_quota_(0), - timeout_(base::TimeDelta::FromSeconds(kClosingHandshakeTimeoutSeconds)), + closing_handshake_timeout_(base::TimeDelta::FromSeconds( + kClosingHandshakeTimeoutSeconds)), + underlying_connection_close_timeout_(base::TimeDelta::FromSeconds( + kUnderlyingConnectionCloseTimeoutSeconds)), has_received_close_frame_(false), received_close_code_(0), state_(FRESHLY_CONSTRUCTED), @@ -312,7 +322,7 @@ stream_.reset(); // The timer may have a callback pointing back to us, so stop it just in case // someone decides to run the event loop from their destructor. - timer_.Stop(); + close_timer_.Stop(); } void WebSocketChannel::SendAddChannelRequest( @@ -478,6 +488,15 @@ NOTREACHED() << "StartClosingHandshake() called in state " << state_; return; } + + DCHECK(!close_timer_.IsRunning()); + // This use of base::Unretained() is safe because we stop the timer in the + // destructor. + close_timer_.Start( + FROM_HERE, + closing_handshake_timeout_, + base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); + // Javascript actually only permits 1000 and 3000-4999, but the implementation // itself may produce different codes. The length of |reason| is also checked // by Javascript. @@ -513,7 +532,12 @@ void WebSocketChannel::SetClosingHandshakeTimeoutForTesting( base::TimeDelta delay) { - timeout_ = delay; + closing_handshake_timeout_ = delay; +} + +void WebSocketChannel::SetUnderlyingConnectionCloseTimeoutForTesting( + base::TimeDelta delay) { + underlying_connection_close_timeout_ = delay; } void WebSocketChannel::SendAddChannelRequestWithSuppliedCreator( @@ -839,10 +863,20 @@ switch (state_) { case CONNECTED: SetState(RECV_CLOSED); + if (SendClose(code, reason) == CHANNEL_DELETED) return CHANNEL_DELETED; DCHECK_EQ(RECV_CLOSED, state_); + SetState(CLOSE_WAIT); + DCHECK(!close_timer_.IsRunning()); + // This use of base::Unretained() is safe because we stop the timer + // in the destructor. + close_timer_.Start( + FROM_HERE, + underlying_connection_close_timeout_, + base::Bind( + &WebSocketChannel::CloseTimeout, base::Unretained(this))); if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED) return CHANNEL_DELETED; @@ -853,6 +887,16 @@ case SEND_CLOSED: SetState(CLOSE_WAIT); + DCHECK(close_timer_.IsRunning()); + close_timer_.Stop(); + // This use of base::Unretained() is safe because we stop the timer + // in the destructor. + close_timer_.Start( + FROM_HERE, + underlying_connection_close_timeout_, + base::Bind( + &WebSocketChannel::CloseTimeout, base::Unretained(this))); + // From RFC6455 section 7.1.5: "Each endpoint // will see the status code sent by the other end as _The WebSocket // Connection Close Code_." @@ -1027,12 +1071,6 @@ std::copy( reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); } - // This use of base::Unretained() is safe because we stop the timer in the - // destructor. - timer_.Start( - FROM_HERE, - timeout_, - base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); if (SendFrameFromIOBuffer( true, WebSocketFrameHeader::kOpCodeClose, body, size) == CHANNEL_DELETED)
diff --git a/net/websockets/websocket_channel.h b/net/websockets/websocket_channel.h index 59e50a1..4df6a62 100644 --- a/net/websockets/websocket_channel.h +++ b/net/websockets/websocket_channel.h
@@ -110,6 +110,11 @@ // set it to a very small value for testing purposes. void SetClosingHandshakeTimeoutForTesting(base::TimeDelta delay); + // The default timout for the underlying connection close is a sensible value + // (see kUnderlyingConnectionCloseTimeoutSeconds in websocket_channel.cc). + // However, we can set it to a very small value for testing purposes. + void SetUnderlyingConnectionCloseTimeoutForTesting(base::TimeDelta delay); + // Called when the stream starts the WebSocket Opening Handshake. // This method is public for testing. void OnStartOpeningHandshake( @@ -371,10 +376,14 @@ uint64 current_receive_quota_; // Timer for the closing handshake. - base::OneShotTimer<WebSocketChannel> timer_; + base::OneShotTimer<WebSocketChannel> close_timer_; // Timeout for the closing handshake. - base::TimeDelta timeout_; + base::TimeDelta closing_handshake_timeout_; + + // Timeout for the underlying connection close after completion of closing + // handshake. + base::TimeDelta underlying_connection_close_timeout_; // Storage for the status code and reason from the time the Close frame // arrives until the connection is closed and they are passed to
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc index f814f10..d5ec3da 100644 --- a/net/websockets/websocket_channel_test.cc +++ b/net/websockets/websocket_channel_test.cc
@@ -127,6 +127,8 @@ // kDefaultSendQuotaLowWaterMark change. const size_t kDefaultQuotaRefreshTrigger = (1 << 16) + 1; +const int kVeryBigTimeoutMillis = 60 * 60 * 24 * 1000; + // TestTimeouts::tiny_timeout() is 100ms! I could run halfway around the world // in that time! I would like my tests to run a bit quicker. const int kVeryTinyTimeoutMillis = 1; @@ -2145,6 +2147,8 @@ // was fired by the behaviour of the WebSocketChannel object. channel_->SetClosingHandshakeTimeoutForTesting( TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); + channel_->SetUnderlyingConnectionCloseTimeoutForTesting( + TimeDelta::FromMilliseconds(kVeryBigTimeoutMillis)); channel_->StartClosingHandshake(kWebSocketNormalClosure, ""); checkpoint.Call(1); completion.WaitForResult(); @@ -2175,6 +2179,8 @@ } CreateChannelAndConnectSuccessfully(); channel_->SetClosingHandshakeTimeoutForTesting( + TimeDelta::FromMilliseconds(kVeryBigTimeoutMillis)); + channel_->SetUnderlyingConnectionCloseTimeoutForTesting( TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); checkpoint.Call(1); completion.WaitForResult(); @@ -3304,6 +3310,8 @@ channel_->SendFlowControl(kPlentyOfQuota); channel_->SetClosingHandshakeTimeoutForTesting( TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); + channel_->SetUnderlyingConnectionCloseTimeoutForTesting( + TimeDelta::FromMilliseconds(kVeryTinyTimeoutMillis)); connect_data_.creator.connect_delegate->OnSuccess(stream_.Pass()); } };
diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc index 6324710..6bcc230 100644 --- a/net/websockets/websocket_handshake_handler.cc +++ b/net/websockets/websocket_handshake_handler.cc
@@ -4,346 +4,12 @@ #include "net/websockets/websocket_handshake_handler.h" -#include <limits> - #include "base/base64.h" +#include "base/logging.h" #include "base/sha1.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_tokenizer.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "net/http/http_request_headers.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_util.h" #include "net/websockets/websocket_handshake_constants.h" -#include "url/gurl.h" namespace net { -namespace { - -const int kVersionHeaderValueForRFC6455 = 13; - -// Splits |handshake_message| into Status-Line or Request-Line (including CRLF) -// and headers (excluding 2nd CRLF of double CRLFs at the end of a handshake -// response). -void ParseHandshakeHeader( - const char* handshake_message, int len, - std::string* request_line, - std::string* headers) { - size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n"); - if (i == base::StringPiece::npos) { - *request_line = std::string(handshake_message, len); - *headers = ""; - return; - } - // |request_line| includes \r\n. - *request_line = std::string(handshake_message, i + 2); - - int header_len = len - (i + 2) - 2; - if (header_len > 0) { - // |handshake_message| includes trailing \r\n\r\n. - // |headers| doesn't include 2nd \r\n. - *headers = std::string(handshake_message + i + 2, header_len); - } else { - *headers = ""; - } -} - -void FetchHeaders(const std::string& headers, - const char* const headers_to_get[], - size_t headers_to_get_len, - std::vector<std::string>* values) { - net::HttpUtil::HeadersIterator iter(headers.begin(), headers.end(), "\r\n"); - while (iter.GetNext()) { - for (size_t i = 0; i < headers_to_get_len; i++) { - if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(), - headers_to_get[i])) { - values->push_back(iter.values()); - } - } - } -} - -bool GetHeaderName(std::string::const_iterator line_begin, - std::string::const_iterator line_end, - std::string::const_iterator* name_begin, - std::string::const_iterator* name_end) { - std::string::const_iterator colon = std::find(line_begin, line_end, ':'); - if (colon == line_end) { - return false; - } - *name_begin = line_begin; - *name_end = colon; - if (*name_begin == *name_end || net::HttpUtil::IsLWS(**name_begin)) - return false; - net::HttpUtil::TrimLWS(name_begin, name_end); - return true; -} - -// Similar to HttpUtil::StripHeaders, but it preserves malformed headers, that -// is, lines that are not formatted as "<name>: <value>\r\n". -std::string FilterHeaders( - const std::string& headers, - const char* const headers_to_remove[], - size_t headers_to_remove_len) { - std::string filtered_headers; - - base::StringTokenizer lines(headers.begin(), headers.end(), "\r\n"); - while (lines.GetNext()) { - std::string::const_iterator line_begin = lines.token_begin(); - std::string::const_iterator line_end = lines.token_end(); - std::string::const_iterator name_begin; - std::string::const_iterator name_end; - bool should_remove = false; - if (GetHeaderName(line_begin, line_end, &name_begin, &name_end)) { - for (size_t i = 0; i < headers_to_remove_len; ++i) { - if (LowerCaseEqualsASCII(name_begin, name_end, headers_to_remove[i])) { - should_remove = true; - break; - } - } - } - if (!should_remove) { - filtered_headers.append(line_begin, line_end); - filtered_headers.append("\r\n"); - } - } - return filtered_headers; -} - -bool CheckVersionInRequest(const std::string& request_headers) { - std::vector<std::string> values; - const char* const headers_to_get[1] = { - websockets::kSecWebSocketVersionLowercase}; - FetchHeaders(request_headers, headers_to_get, 1, &values); - DCHECK_LE(values.size(), 1U); - if (values.empty()) - return false; - - int version; - bool conversion_success = base::StringToInt(values[0], &version); - if (!conversion_success) - return false; - - return version == kVersionHeaderValueForRFC6455; -} - -// Append a header to a string. Equivalent to -// response_message += header + ": " + value + "\r\n" -// but avoids unnecessary allocations and copies. -void AppendHeader(const base::StringPiece& header, - const base::StringPiece& value, - std::string* response_message) { - static const char kColonSpace[] = ": "; - const size_t kColonSpaceSize = sizeof(kColonSpace) - 1; - static const char kCrNl[] = "\r\n"; - const size_t kCrNlSize = sizeof(kCrNl) - 1; - - size_t extra_size = - header.size() + kColonSpaceSize + value.size() + kCrNlSize; - response_message->reserve(response_message->size() + extra_size); - response_message->append(header.begin(), header.end()); - response_message->append(kColonSpace, kColonSpace + kColonSpaceSize); - response_message->append(value.begin(), value.end()); - response_message->append(kCrNl, kCrNl + kCrNlSize); -} - -} // namespace - -WebSocketHandshakeRequestHandler::WebSocketHandshakeRequestHandler() - : original_length_(0), - raw_length_(0) {} - -bool WebSocketHandshakeRequestHandler::ParseRequest( - const char* data, int length) { - DCHECK_GT(length, 0); - std::string input(data, length); - int input_header_length = - HttpUtil::LocateEndOfHeaders(input.data(), input.size(), 0); - if (input_header_length <= 0) - return false; - - ParseHandshakeHeader(input.data(), - input_header_length, - &request_line_, - &headers_); - - if (!CheckVersionInRequest(headers_)) { - NOTREACHED(); - return false; - } - - original_length_ = input_header_length; - return true; -} - -size_t WebSocketHandshakeRequestHandler::original_length() const { - return original_length_; -} - -void WebSocketHandshakeRequestHandler::AppendHeaderIfMissing( - const std::string& name, const std::string& value) { - DCHECK(!headers_.empty()); - HttpUtil::AppendHeaderIfMissing(name.c_str(), value, &headers_); -} - -void WebSocketHandshakeRequestHandler::RemoveHeaders( - const char* const headers_to_remove[], - size_t headers_to_remove_len) { - DCHECK(!headers_.empty()); - headers_ = FilterHeaders( - headers_, headers_to_remove, headers_to_remove_len); -} - -HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo( - const GURL& url, std::string* challenge) { - HttpRequestInfo request_info; - request_info.url = url; - size_t method_end = base::StringPiece(request_line_).find_first_of(" "); - if (method_end != base::StringPiece::npos) - request_info.method = std::string(request_line_.data(), method_end); - - request_info.extra_headers.Clear(); - request_info.extra_headers.AddHeadersFromString(headers_); - - request_info.extra_headers.RemoveHeader(websockets::kUpgrade); - request_info.extra_headers.RemoveHeader(HttpRequestHeaders::kConnection); - - std::string key; - bool header_present = request_info.extra_headers.GetHeader( - websockets::kSecWebSocketKey, &key); - DCHECK(header_present); - request_info.extra_headers.RemoveHeader(websockets::kSecWebSocketKey); - *challenge = key; - return request_info; -} - -bool WebSocketHandshakeRequestHandler::GetRequestHeaderBlock( - const GURL& url, - SpdyHeaderBlock* headers, - std::string* challenge, - int spdy_protocol_version) { - // Construct opening handshake request headers as a SPDY header block. - // For details, see WebSocket Layering over SPDY/3 Draft 8. - if (spdy_protocol_version <= 2) { - (*headers)["path"] = url.path(); - (*headers)["version"] = "WebSocket/13"; - (*headers)["scheme"] = url.scheme(); - } else { - (*headers)[":path"] = url.path(); - (*headers)[":version"] = "WebSocket/13"; - (*headers)[":scheme"] = url.scheme(); - } - - HttpUtil::HeadersIterator iter(headers_.begin(), headers_.end(), "\r\n"); - while (iter.GetNext()) { - if (LowerCaseEqualsASCII(iter.name_begin(), - iter.name_end(), - websockets::kUpgradeLowercase) || - LowerCaseEqualsASCII( - iter.name_begin(), iter.name_end(), "connection") || - LowerCaseEqualsASCII(iter.name_begin(), - iter.name_end(), - websockets::kSecWebSocketVersionLowercase)) { - // These headers must be ignored. - continue; - } else if (LowerCaseEqualsASCII(iter.name_begin(), - iter.name_end(), - websockets::kSecWebSocketKeyLowercase)) { - *challenge = iter.values(); - // Sec-WebSocket-Key is not sent to the server. - continue; - } else if (LowerCaseEqualsASCII( - iter.name_begin(), iter.name_end(), "host") || - LowerCaseEqualsASCII( - iter.name_begin(), iter.name_end(), "origin") || - LowerCaseEqualsASCII( - iter.name_begin(), - iter.name_end(), - websockets::kSecWebSocketProtocolLowercase) || - LowerCaseEqualsASCII( - iter.name_begin(), - iter.name_end(), - websockets::kSecWebSocketExtensionsLowercase)) { - // TODO(toyoshim): Some WebSocket extensions may not be compatible with - // SPDY. We should omit them from a Sec-WebSocket-Extension header. - std::string name; - if (spdy_protocol_version <= 2) - name = base::StringToLowerASCII(iter.name()); - else - name = ":" + base::StringToLowerASCII(iter.name()); - (*headers)[name] = iter.values(); - continue; - } - // Others should be sent out to |headers|. - std::string name = base::StringToLowerASCII(iter.name()); - SpdyHeaderBlock::iterator found = headers->find(name); - if (found == headers->end()) { - (*headers)[name] = iter.values(); - } else { - // For now, websocket doesn't use multiple headers, but follows to http. - found->second.append(1, '\0'); // +=() doesn't append 0's - found->second.append(iter.values()); - } - } - - return true; -} - -std::string WebSocketHandshakeRequestHandler::GetRawRequest() { - DCHECK(!request_line_.empty()); - DCHECK(!headers_.empty()); - - std::string raw_request = request_line_ + headers_ + "\r\n"; - raw_length_ = raw_request.size(); - return raw_request; -} - -size_t WebSocketHandshakeRequestHandler::raw_length() const { - DCHECK_GT(raw_length_, 0); - return raw_length_; -} - -WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler() - : original_header_length_(0) {} - -WebSocketHandshakeResponseHandler::~WebSocketHandshakeResponseHandler() {} - -size_t WebSocketHandshakeResponseHandler::ParseRawResponse( - const char* data, int length) { - DCHECK_GT(length, 0); - if (HasResponse()) { - DCHECK(!status_line_.empty()); - // headers_ might be empty for wrong response from server. - - return 0; - } - - size_t old_original_length = original_.size(); - - original_.append(data, length); - // TODO(ukai): fail fast when response gives wrong status code. - original_header_length_ = HttpUtil::LocateEndOfHeaders( - original_.data(), original_.size(), 0); - if (!HasResponse()) - return length; - - ParseHandshakeHeader(original_.data(), - original_header_length_, - &status_line_, - &headers_); - int header_size = status_line_.size() + headers_.size(); - DCHECK_GE(original_header_length_, header_size); - header_separator_ = std::string(original_.data() + header_size, - original_header_length_ - header_size); - return original_header_length_ - old_original_length; -} - -bool WebSocketHandshakeResponseHandler::HasResponse() const { - return original_header_length_ > 0 && - static_cast<size_t>(original_header_length_) <= original_.size(); -} void ComputeSecWebSocketAccept(const std::string& key, std::string* accept) { @@ -354,145 +20,4 @@ base::Base64Encode(hash, accept); } -bool WebSocketHandshakeResponseHandler::ParseResponseInfo( - const HttpResponseInfo& response_info, - const std::string& challenge) { - if (!response_info.headers.get()) - return false; - - // TODO(ricea): Eliminate all the reallocations and string copies. - std::string response_message; - response_message = response_info.headers->GetStatusLine(); - response_message += "\r\n"; - - AppendHeader(websockets::kUpgrade, - websockets::kWebSocketLowercase, - &response_message); - - AppendHeader( - HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message); - - std::string websocket_accept; - ComputeSecWebSocketAccept(challenge, &websocket_accept); - AppendHeader( - websockets::kSecWebSocketAccept, websocket_accept, &response_message); - - void* iter = NULL; - std::string name; - std::string value; - while (response_info.headers->EnumerateHeaderLines(&iter, &name, &value)) { - AppendHeader(name, value, &response_message); - } - response_message += "\r\n"; - - return ParseRawResponse(response_message.data(), - response_message.size()) == response_message.size(); -} - -bool WebSocketHandshakeResponseHandler::ParseResponseHeaderBlock( - const SpdyHeaderBlock& headers, - const std::string& challenge, - int spdy_protocol_version) { - SpdyHeaderBlock::const_iterator status; - if (spdy_protocol_version <= 2) - status = headers.find("status"); - else - status = headers.find(":status"); - if (status == headers.end()) - return false; - - std::string hash = - base::SHA1HashString(challenge + websockets::kWebSocketGuid); - std::string websocket_accept; - base::Base64Encode(hash, &websocket_accept); - - std::string response_message = base::StringPrintf( - "%s %s\r\n", websockets::kHttpProtocolVersion, status->second.c_str()); - - AppendHeader( - websockets::kUpgrade, websockets::kWebSocketLowercase, &response_message); - AppendHeader( - HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message); - AppendHeader( - websockets::kSecWebSocketAccept, websocket_accept, &response_message); - - for (SpdyHeaderBlock::const_iterator iter = headers.begin(); - iter != headers.end(); - ++iter) { - // For each value, if the server sends a NUL-separated list of values, - // we separate that back out into individual headers for each value - // in the list. - if ((spdy_protocol_version <= 2 && - LowerCaseEqualsASCII(iter->first, "status")) || - (spdy_protocol_version >= 3 && - LowerCaseEqualsASCII(iter->first, ":status"))) { - // The status value is already handled as the first line of - // |response_message|. Just skip here. - continue; - } - const std::string& value = iter->second; - size_t start = 0; - size_t end = 0; - do { - end = value.find('\0', start); - std::string tval; - if (end != std::string::npos) - tval = value.substr(start, (end - start)); - else - tval = value.substr(start); - if (spdy_protocol_version >= 3 && - (LowerCaseEqualsASCII(iter->first, - websockets::kSecWebSocketProtocolSpdy3) || - LowerCaseEqualsASCII(iter->first, - websockets::kSecWebSocketExtensionsSpdy3))) - AppendHeader(iter->first.substr(1), tval, &response_message); - else - AppendHeader(iter->first, tval, &response_message); - start = end + 1; - } while (end != std::string::npos); - } - response_message += "\r\n"; - - return ParseRawResponse(response_message.data(), - response_message.size()) == response_message.size(); -} - -void WebSocketHandshakeResponseHandler::GetHeaders( - const char* const headers_to_get[], - size_t headers_to_get_len, - std::vector<std::string>* values) { - DCHECK(HasResponse()); - DCHECK(!status_line_.empty()); - // headers_ might be empty for wrong response from server. - if (headers_.empty()) - return; - - FetchHeaders(headers_, headers_to_get, headers_to_get_len, values); -} - -void WebSocketHandshakeResponseHandler::RemoveHeaders( - const char* const headers_to_remove[], - size_t headers_to_remove_len) { - DCHECK(HasResponse()); - DCHECK(!status_line_.empty()); - // headers_ might be empty for wrong response from server. - if (headers_.empty()) - return; - - headers_ = FilterHeaders(headers_, headers_to_remove, headers_to_remove_len); -} - -std::string WebSocketHandshakeResponseHandler::GetRawResponse() const { - DCHECK(HasResponse()); - return original_.substr(0, original_header_length_); -} - -std::string WebSocketHandshakeResponseHandler::GetResponse() { - DCHECK(HasResponse()); - DCHECK(!status_line_.empty()); - // headers_ might be empty for wrong response from server. - - return status_line_ + headers_ + header_separator_; -} - } // namespace net
diff --git a/net/websockets/websocket_handshake_handler.h b/net/websockets/websocket_handshake_handler.h index 73af66d..c65e6d9 100644 --- a/net/websockets/websocket_handshake_handler.h +++ b/net/websockets/websocket_handshake_handler.h
@@ -1,124 +1,19 @@ // Copyright (c) 2012 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. -// -// WebSocketHandshake*Handler handles WebSocket handshake request message -// from WebKit renderer process, and WebSocket handshake response message -// from WebSocket server. -// It modifies messages for the following reason: -// - We don't trust WebKit renderer process, so we'll not expose HttpOnly -// cookies to the renderer process, so handles HttpOnly cookies in -// browser process. -// + #ifndef NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_ #define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_ #include <string> -#include <vector> - -#include "net/base/net_export.h" -#include "net/http/http_request_info.h" -#include "net/http/http_response_info.h" -#include "net/spdy/spdy_header_block.h" namespace net { +// Given a WebSocket handshake challenge, compute the correct response. +// TODO(ricea): There should probably be a test for this. void ComputeSecWebSocketAccept(const std::string& key, std::string* accept); -class NET_EXPORT_PRIVATE WebSocketHandshakeRequestHandler { - public: - WebSocketHandshakeRequestHandler(); - ~WebSocketHandshakeRequestHandler() {} - - // Parses WebSocket handshake request from renderer process. - // It assumes a WebSocket handshake request message is given at once, and - // no other data is added to the request message. - bool ParseRequest(const char* data, int length); - - size_t original_length() const; - - // Appends the header value pair for |name| and |value|, if |name| doesn't - // exist. - void AppendHeaderIfMissing(const std::string& name, - const std::string& value); - // Removes the headers that matches (case insensitive). - void RemoveHeaders(const char* const headers_to_remove[], - size_t headers_to_remove_len); - - // Gets request info to open WebSocket connection and fills challenge data in - // |challenge|. - HttpRequestInfo GetRequestInfo(const GURL& url, std::string* challenge); - // Gets request as SpdyHeaderBlock. - // Also, fills challenge data in |challenge|. - bool GetRequestHeaderBlock(const GURL& url, - SpdyHeaderBlock* headers, - std::string* challenge, - int spdy_protocol_version); - // Gets WebSocket handshake raw request message to open WebSocket - // connection. - std::string GetRawRequest(); - // Calling raw_length is valid only after GetRawRequest() call. - size_t raw_length() const; - - private: - std::string request_line_; - std::string headers_; - int original_length_; - int raw_length_; - - DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeRequestHandler); -}; - -class NET_EXPORT_PRIVATE WebSocketHandshakeResponseHandler { - public: - WebSocketHandshakeResponseHandler(); - ~WebSocketHandshakeResponseHandler(); - - // Parses WebSocket handshake response from WebSocket server. - // Returns number of bytes in |data| used for WebSocket handshake response - // message. If it already got whole WebSocket handshake response message, - // returns zero. In other words, [data + returned value, data + length) will - // be WebSocket frame data after handshake response message. - // TODO(ukai): fail fast when response gives wrong status code. - size_t ParseRawResponse(const char* data, int length); - // Returns true if it already parses full handshake response message. - bool HasResponse() const; - // Parses WebSocket handshake response info given as HttpResponseInfo. - bool ParseResponseInfo(const HttpResponseInfo& response_info, - const std::string& challenge); - // Parses WebSocket handshake response as SpdyHeaderBlock. - bool ParseResponseHeaderBlock(const SpdyHeaderBlock& headers, - const std::string& challenge, - int spdy_protocol_version); - - // Gets the headers value. - void GetHeaders(const char* const headers_to_get[], - size_t headers_to_get_len, - std::vector<std::string>* values); - // Removes the headers that matches (case insensitive). - void RemoveHeaders(const char* const headers_to_remove[], - size_t headers_to_remove_len); - - // Gets raw WebSocket handshake response received from WebSocket server. - std::string GetRawResponse() const; - - // Gets WebSocket handshake response message sent to renderer process. - std::string GetResponse(); - - private: - // Original bytes input by using ParseRawResponse(). - std::string original_; - // Number of bytes actually used for the handshake response in |original_|. - int original_header_length_; - - std::string status_line_; - std::string headers_; - std::string header_separator_; - - DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeResponseHandler); -}; - } // namespace net #endif // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
diff --git a/net/websockets/websocket_handshake_handler_spdy_test.cc b/net/websockets/websocket_handshake_handler_spdy_test.cc deleted file mode 100644 index 064bdcf..0000000 --- a/net/websockets/websocket_handshake_handler_spdy_test.cc +++ /dev/null
@@ -1,187 +0,0 @@ -// Copyright 2013 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 "net/websockets/websocket_handshake_handler.h" - -#include <string> - -#include "net/socket/next_proto.h" -#include "net/spdy/spdy_header_block.h" -#include "net/spdy/spdy_websocket_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace net { - -namespace { - -class WebSocketHandshakeHandlerSpdyTest - : public ::testing::Test, - public ::testing::WithParamInterface<NextProto> { - protected: - WebSocketHandshakeHandlerSpdyTest() : spdy_util_(GetParam()) {} - - SpdyWebSocketTestUtil spdy_util_; -}; - -INSTANTIATE_TEST_CASE_P( - NextProto, - WebSocketHandshakeHandlerSpdyTest, - testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); - -TEST_P(WebSocketHandshakeHandlerSpdyTest, RequestResponse) { - WebSocketHandshakeRequestHandler request_handler; - - static const char kHandshakeRequestMessage[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Extensions: foo\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n"; - - EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage, - strlen(kHandshakeRequestMessage))); - - GURL url("ws://example.com/demo"); - std::string challenge; - SpdyHeaderBlock headers; - ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, - &headers, - &challenge, - spdy_util_.spdy_version())); - - EXPECT_EQ(url.path(), spdy_util_.GetHeader(headers, "path")); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "upgrade").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Upgrade").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "connection").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Connection").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Key").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-websocket-key").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Version").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-webSocket-version").empty()); - EXPECT_EQ("example.com", spdy_util_.GetHeader(headers, "host")); - EXPECT_EQ("http://example.com", spdy_util_.GetHeader(headers, "origin")); - EXPECT_EQ("sample", spdy_util_.GetHeader(headers, "sec-websocket-protocol")); - EXPECT_EQ("foo", spdy_util_.GetHeader(headers, "sec-websocket-extensions")); - EXPECT_EQ("ws", spdy_util_.GetHeader(headers, "scheme")); - EXPECT_EQ("WebSocket/13", spdy_util_.GetHeader(headers, "version")); - - static const char expected_challenge[] = "dGhlIHNhbXBsZSBub25jZQ=="; - - EXPECT_EQ(expected_challenge, challenge); - - headers.clear(); - - spdy_util_.SetHeader("status", "101 Switching Protocols", &headers); - spdy_util_.SetHeader("sec-websocket-protocol", "sample", &headers); - spdy_util_.SetHeader("sec-websocket-extensions", "foo", &headers); - - WebSocketHandshakeResponseHandler response_handler; - EXPECT_TRUE(response_handler.ParseResponseHeaderBlock( - headers, challenge, spdy_util_.spdy_version())); - EXPECT_TRUE(response_handler.HasResponse()); - - // Note that order of sec-websocket-* is sensitive with hash_map order. - static const char kHandshakeResponseExpectedMessage[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "sec-websocket-extensions: foo\r\n" - "sec-websocket-protocol: sample\r\n" - "\r\n"; - - EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); -} - -TEST_P(WebSocketHandshakeHandlerSpdyTest, RequestResponseWithCookies) { - WebSocketHandshakeRequestHandler request_handler; - - // Note that websocket won't use multiple headers in request now. - static const char kHandshakeRequestMessage[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Extensions: foo\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n" - "\r\n"; - - EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage, - strlen(kHandshakeRequestMessage))); - - GURL url("ws://example.com/demo"); - std::string challenge; - SpdyHeaderBlock headers; - ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, - &headers, - &challenge, - spdy_util_.spdy_version())); - - EXPECT_EQ(url.path(), spdy_util_.GetHeader(headers, "path")); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "upgrade").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Upgrade").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "connection").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Connection").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Key").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-websocket-key").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "Sec-WebSocket-Version").empty()); - EXPECT_TRUE(spdy_util_.GetHeader(headers, "sec-webSocket-version").empty()); - EXPECT_EQ("example.com", spdy_util_.GetHeader(headers, "host")); - EXPECT_EQ("http://example.com", spdy_util_.GetHeader(headers, "origin")); - EXPECT_EQ("sample", spdy_util_.GetHeader(headers, "sec-websocket-protocol")); - EXPECT_EQ("foo", spdy_util_.GetHeader(headers, "sec-websocket-extensions")); - EXPECT_EQ("ws", spdy_util_.GetHeader(headers, "scheme")); - EXPECT_EQ("WebSocket/13", spdy_util_.GetHeader(headers, "version")); - EXPECT_EQ("WK-websocket-test=1; WK-websocket-test-httponly=1", - headers["cookie"]); - - const char expected_challenge[] = "dGhlIHNhbXBsZSBub25jZQ=="; - - EXPECT_EQ(expected_challenge, challenge); - - headers.clear(); - - spdy_util_.SetHeader("status", "101 Switching Protocols", &headers); - spdy_util_.SetHeader("sec-websocket-protocol", "sample", &headers); - spdy_util_.SetHeader("sec-websocket-extensions", "foo", &headers); - std::string cookie = "WK-websocket-test=1"; - cookie.append(1, '\0'); - cookie += "WK-websocket-test-httponly=1; HttpOnly"; - headers["set-cookie"] = cookie; - - - WebSocketHandshakeResponseHandler response_handler; - EXPECT_TRUE(response_handler.ParseResponseHeaderBlock( - headers, challenge, spdy_util_.spdy_version())); - EXPECT_TRUE(response_handler.HasResponse()); - - // Note that order of sec-websocket-* is sensitive with hash_map order. - static const char kHandshakeResponseExpectedMessage[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "sec-websocket-extensions: foo\r\n" - "sec-websocket-protocol: sample\r\n" - "set-cookie: WK-websocket-test=1\r\n" - "set-cookie: WK-websocket-test-httponly=1; HttpOnly\r\n" - "\r\n"; - - EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); -} - -} // namespace - -} // namespace net
diff --git a/net/websockets/websocket_handshake_handler_test.cc b/net/websockets/websocket_handshake_handler_test.cc index e59a982..8eff571 100644 --- a/net/websockets/websocket_handshake_handler_test.cc +++ b/net/websockets/websocket_handshake_handler_test.cc
@@ -4,238 +4,14 @@ #include "net/websockets/websocket_handshake_handler.h" -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_util.h" -#include "url/gurl.h" - #include "testing/gtest/include/gtest/gtest.h" -namespace { - -const char* const kCookieHeaders[] = { - "cookie", "cookie2" -}; - -const char* const kSetCookieHeaders[] = { - "set-cookie", "set-cookie2" -}; - -} // namespace - namespace net { -TEST(WebSocketHandshakeRequestHandlerTest, SimpleRequest) { - WebSocketHandshakeRequestHandler handler; +namespace { - static const char kHandshakeRequestMessage[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Sec-WebSocket-Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n"; +// TODO(ricea): Put a test for ComputeSecWebSocketAccept() here. - EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage, - strlen(kHandshakeRequestMessage))); - - handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders)); - - EXPECT_EQ(kHandshakeRequestMessage, handler.GetRawRequest()); -} - -TEST(WebSocketHandshakeRequestHandlerTest, ReplaceRequestCookies) { - WebSocketHandshakeRequestHandler handler; - - static const char kHandshakeRequestMessage[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Sec-WebSocket-Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Cookie: WK-websocket-test=1\r\n" - "\r\n"; - - EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage, - strlen(kHandshakeRequestMessage))); - - handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders)); - - handler.AppendHeaderIfMissing("Cookie", - "WK-websocket-test=1; " - "WK-websocket-test-httponly=1"); - - static const char kHandshakeRequestExpectedMessage[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Sec-WebSocket-Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n" - "\r\n"; - - EXPECT_EQ(kHandshakeRequestExpectedMessage, handler.GetRawRequest()); -} - -TEST(WebSocketHandshakeResponseHandlerTest, SimpleResponse) { - WebSocketHandshakeResponseHandler handler; - - static const char kHandshakeResponseMessage[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "\r\n"; - - EXPECT_EQ(strlen(kHandshakeResponseMessage), - handler.ParseRawResponse(kHandshakeResponseMessage, - strlen(kHandshakeResponseMessage))); - EXPECT_TRUE(handler.HasResponse()); - - handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders)); - - EXPECT_EQ(kHandshakeResponseMessage, handler.GetResponse()); -} - -TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) { - WebSocketHandshakeResponseHandler handler; - - static const char kHandshakeResponseMessage[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Set-Cookie: WK-websocket-test-1\r\n" - "Set-Cookie: WK-websocket-test-httponly=1; HttpOnly\r\n" - "\r\n"; - - EXPECT_EQ(strlen(kHandshakeResponseMessage), - handler.ParseRawResponse(kHandshakeResponseMessage, - strlen(kHandshakeResponseMessage))); - EXPECT_TRUE(handler.HasResponse()); - std::vector<std::string> cookies; - handler.GetHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders), &cookies); - ASSERT_EQ(2U, cookies.size()); - EXPECT_EQ("WK-websocket-test-1", cookies[0]); - EXPECT_EQ("WK-websocket-test-httponly=1; HttpOnly", cookies[1]); - handler.RemoveHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders)); - - static const char kHandshakeResponseExpectedMessage[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "\r\n"; - - EXPECT_EQ(kHandshakeResponseExpectedMessage, handler.GetResponse()); -} - -TEST(WebSocketHandshakeResponseHandlerTest, BadResponse) { - WebSocketHandshakeResponseHandler handler; - - static const char kBadMessage[] = "\n\n\r\net-Location: w"; - EXPECT_EQ(2U, handler.ParseRawResponse(kBadMessage, strlen(kBadMessage))); - EXPECT_TRUE(handler.HasResponse()); - EXPECT_EQ("\n\n", handler.GetResponse()); -} - -TEST(WebSocketHandshakeResponseHandlerTest, BadResponse2) { - WebSocketHandshakeResponseHandler handler; - - static const char kBadMessage[] = "\n\r\n\r\net-Location: w"; - EXPECT_EQ(3U, handler.ParseRawResponse(kBadMessage, strlen(kBadMessage))); - EXPECT_TRUE(handler.HasResponse()); - EXPECT_EQ("\n\r\n", handler.GetResponse()); -} - -TEST(WebSocketHandshakeHandlerTest, HttpRequestResponse) { - WebSocketHandshakeRequestHandler request_handler; - - static const char kHandshakeRequestMessage[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Sec-WebSocket-Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n"; - - EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage, - strlen(kHandshakeRequestMessage))); - - GURL url("ws://example.com/demo"); - std::string challenge; - const HttpRequestInfo& request_info = - request_handler.GetRequestInfo(url, &challenge); - - EXPECT_EQ(url, request_info.url); - EXPECT_EQ("GET", request_info.method); - EXPECT_FALSE(request_info.extra_headers.HasHeader("Upgrade")); - EXPECT_FALSE(request_info.extra_headers.HasHeader("Connection")); - EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key")); - std::string value; - EXPECT_TRUE(request_info.extra_headers.GetHeader("Host", &value)); - EXPECT_EQ("example.com", value); - EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Origin", - &value)); - EXPECT_EQ("http://example.com", value); - EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Protocol", - &value)); - EXPECT_EQ("sample", value); - - EXPECT_EQ("dGhlIHNhbXBsZSBub25jZQ==", challenge); - - static const char kHandshakeResponseHeader[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Sec-WebSocket-Protocol: sample\r\n"; - - std::string raw_headers = - HttpUtil::AssembleRawHeaders(kHandshakeResponseHeader, - strlen(kHandshakeResponseHeader)); - HttpResponseInfo response_info; - response_info.headers = new HttpResponseHeaders(raw_headers); - - EXPECT_TRUE(StartsWithASCII(response_info.headers->GetStatusLine(), - "HTTP/1.1 101 ", false)); - EXPECT_FALSE(response_info.headers->HasHeader("Upgrade")); - EXPECT_FALSE(response_info.headers->HasHeader("Connection")); - EXPECT_FALSE(response_info.headers->HasHeader("Sec-WebSocket-Accept")); - EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Protocol", - "sample")); - - WebSocketHandshakeResponseHandler response_handler; - - EXPECT_TRUE(response_handler.ParseResponseInfo(response_info, challenge)); - EXPECT_TRUE(response_handler.HasResponse()); - - static const char kHandshakeResponseExpectedMessage[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "\r\n"; - - EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse()); -} +} // namespace } // namespace net
diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc deleted file mode 100644 index eb653fa..0000000 --- a/net/websockets/websocket_job.cc +++ /dev/null
@@ -1,693 +0,0 @@ -// Copyright (c) 2012 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 "net/websockets/websocket_job.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" -#include "net/cookies/cookie_store.h" -#include "net/http/http_network_session.h" -#include "net/http/http_transaction_factory.h" -#include "net/http/http_util.h" -#include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_session_pool.h" -#include "net/url_request/url_request_context.h" -#include "net/websockets/websocket_handshake_handler.h" -#include "net/websockets/websocket_net_log_params.h" -#include "net/websockets/websocket_throttle.h" -#include "url/gurl.h" - -static const int kMaxPendingSendAllowed = 32768; // 32 kilobytes. - -namespace { - -// lower-case header names. -const char* const kCookieHeaders[] = { - "cookie", "cookie2" -}; -const char* const kSetCookieHeaders[] = { - "set-cookie", "set-cookie2" -}; - -net::SocketStreamJob* WebSocketJobFactory( - const GURL& url, net::SocketStream::Delegate* delegate, - net::URLRequestContext* context, net::CookieStore* cookie_store) { - net::WebSocketJob* job = new net::WebSocketJob(delegate); - job->InitSocketStream(new net::SocketStream(url, job, context, cookie_store)); - return job; -} - -class WebSocketJobInitSingleton { - private: - friend struct base::DefaultLazyInstanceTraits<WebSocketJobInitSingleton>; - WebSocketJobInitSingleton() { - net::SocketStreamJob::RegisterProtocolFactory("ws", WebSocketJobFactory); - net::SocketStreamJob::RegisterProtocolFactory("wss", WebSocketJobFactory); - } -}; - -static base::LazyInstance<WebSocketJobInitSingleton> g_websocket_job_init = - LAZY_INSTANCE_INITIALIZER; - -} // anonymous namespace - -namespace net { - -// static -void WebSocketJob::EnsureInit() { - g_websocket_job_init.Get(); -} - -WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate) - : delegate_(delegate), - state_(INITIALIZED), - waiting_(false), - handshake_request_(new WebSocketHandshakeRequestHandler), - handshake_response_(new WebSocketHandshakeResponseHandler), - started_to_send_handshake_request_(false), - handshake_request_sent_(0), - response_cookies_save_index_(0), - spdy_protocol_version_(0), - save_next_cookie_running_(false), - callback_pending_(false), - weak_ptr_factory_(this), - weak_ptr_factory_for_send_pending_(this) { -} - -WebSocketJob::~WebSocketJob() { - DCHECK_EQ(CLOSED, state_); - DCHECK(!delegate_); - DCHECK(!socket_.get()); -} - -void WebSocketJob::Connect() { - DCHECK(socket_.get()); - DCHECK_EQ(state_, INITIALIZED); - state_ = CONNECTING; - socket_->Connect(); -} - -bool WebSocketJob::SendData(const char* data, int len) { - switch (state_) { - case INITIALIZED: - return false; - - case CONNECTING: - return SendHandshakeRequest(data, len); - - case OPEN: - { - scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(len); - memcpy(buffer->data(), data, len); - if (current_send_buffer_.get() || !send_buffer_queue_.empty()) { - send_buffer_queue_.push_back(buffer); - return true; - } - current_send_buffer_ = new DrainableIOBuffer(buffer.get(), len); - return SendDataInternal(current_send_buffer_->data(), - current_send_buffer_->BytesRemaining()); - } - - case CLOSING: - case CLOSED: - return false; - } - return false; -} - -void WebSocketJob::Close() { - if (state_ == CLOSED) - return; - - state_ = CLOSING; - if (current_send_buffer_.get()) { - // Will close in SendPending. - return; - } - state_ = CLOSED; - CloseInternal(); -} - -void WebSocketJob::RestartWithAuth(const AuthCredentials& credentials) { - state_ = CONNECTING; - socket_->RestartWithAuth(credentials); -} - -void WebSocketJob::DetachDelegate() { - state_ = CLOSED; - WebSocketThrottle::GetInstance()->RemoveFromQueue(this); - - scoped_refptr<WebSocketJob> protect(this); - weak_ptr_factory_.InvalidateWeakPtrs(); - weak_ptr_factory_for_send_pending_.InvalidateWeakPtrs(); - - delegate_ = NULL; - if (socket_.get()) - socket_->DetachDelegate(); - socket_ = NULL; - if (!callback_.is_null()) { - waiting_ = false; - callback_.Reset(); - Release(); // Balanced with OnStartOpenConnection(). - } -} - -int WebSocketJob::OnStartOpenConnection( - SocketStream* socket, const CompletionCallback& callback) { - DCHECK(callback_.is_null()); - state_ = CONNECTING; - - addresses_ = socket->address_list(); - if (!WebSocketThrottle::GetInstance()->PutInQueue(this)) { - return ERR_WS_THROTTLE_QUEUE_TOO_LARGE; - } - - if (delegate_) { - int result = delegate_->OnStartOpenConnection(socket, callback); - DCHECK_EQ(OK, result); - } - if (waiting_) { - // PutInQueue() may set |waiting_| true for throttling. In this case, - // Wakeup() will be called later. - callback_ = callback; - AddRef(); // Balanced when callback_ is cleared. - return ERR_IO_PENDING; - } - return TrySpdyStream(); -} - -void WebSocketJob::OnConnected( - SocketStream* socket, int max_pending_send_allowed) { - if (state_ == CLOSED) - return; - DCHECK_EQ(CONNECTING, state_); - if (delegate_) - delegate_->OnConnected(socket, max_pending_send_allowed); -} - -void WebSocketJob::OnSentData(SocketStream* socket, int amount_sent) { - DCHECK_NE(INITIALIZED, state_); - DCHECK_GT(amount_sent, 0); - if (state_ == CLOSED) - return; - if (state_ == CONNECTING) { - OnSentHandshakeRequest(socket, amount_sent); - return; - } - if (delegate_) { - DCHECK(state_ == OPEN || state_ == CLOSING); - if (!current_send_buffer_.get()) { - VLOG(1) - << "OnSentData current_send_buffer=NULL amount_sent=" << amount_sent; - return; - } - current_send_buffer_->DidConsume(amount_sent); - if (current_send_buffer_->BytesRemaining() > 0) - return; - - // We need to report amount_sent of original buffer size, instead of - // amount sent to |socket|. - amount_sent = current_send_buffer_->size(); - DCHECK_GT(amount_sent, 0); - current_send_buffer_ = NULL; - if (!weak_ptr_factory_for_send_pending_.HasWeakPtrs()) { - base::MessageLoopForIO::current()->PostTask( - FROM_HERE, - base::Bind(&WebSocketJob::SendPending, - weak_ptr_factory_for_send_pending_.GetWeakPtr())); - } - delegate_->OnSentData(socket, amount_sent); - } -} - -void WebSocketJob::OnReceivedData( - SocketStream* socket, const char* data, int len) { - DCHECK_NE(INITIALIZED, state_); - if (state_ == CLOSED) - return; - if (state_ == CONNECTING) { - OnReceivedHandshakeResponse(socket, data, len); - return; - } - DCHECK(state_ == OPEN || state_ == CLOSING); - if (delegate_ && len > 0) - delegate_->OnReceivedData(socket, data, len); -} - -void WebSocketJob::OnClose(SocketStream* socket) { - state_ = CLOSED; - WebSocketThrottle::GetInstance()->RemoveFromQueue(this); - - scoped_refptr<WebSocketJob> protect(this); - weak_ptr_factory_.InvalidateWeakPtrs(); - - SocketStream::Delegate* delegate = delegate_; - delegate_ = NULL; - socket_ = NULL; - if (!callback_.is_null()) { - waiting_ = false; - callback_.Reset(); - Release(); // Balanced with OnStartOpenConnection(). - } - if (delegate) - delegate->OnClose(socket); -} - -void WebSocketJob::OnAuthRequired( - SocketStream* socket, AuthChallengeInfo* auth_info) { - if (delegate_) - delegate_->OnAuthRequired(socket, auth_info); -} - -void WebSocketJob::OnSSLCertificateError( - SocketStream* socket, const SSLInfo& ssl_info, bool fatal) { - if (delegate_) - delegate_->OnSSLCertificateError(socket, ssl_info, fatal); -} - -void WebSocketJob::OnError(const SocketStream* socket, int error) { - if (delegate_ && error != ERR_PROTOCOL_SWITCHED) - delegate_->OnError(socket, error); -} - -void WebSocketJob::OnCreatedSpdyStream(int result) { - DCHECK(spdy_websocket_stream_.get()); - DCHECK(socket_.get()); - DCHECK_NE(ERR_IO_PENDING, result); - - if (state_ == CLOSED) { - result = ERR_ABORTED; - } else if (result == OK) { - state_ = CONNECTING; - result = ERR_PROTOCOL_SWITCHED; - } else { - spdy_websocket_stream_.reset(); - } - - CompleteIO(result); -} - -void WebSocketJob::OnSentSpdyHeaders() { - DCHECK_NE(INITIALIZED, state_); - if (state_ != CONNECTING) - return; - size_t original_length = handshake_request_->original_length(); - handshake_request_.reset(); - if (delegate_) - delegate_->OnSentData(socket_.get(), original_length); -} - -void WebSocketJob::OnSpdyResponseHeadersUpdated( - const SpdyHeaderBlock& response_headers) { - DCHECK_NE(INITIALIZED, state_); - if (state_ != CONNECTING) - return; - // TODO(toyoshim): Fallback to non-spdy connection? - handshake_response_->ParseResponseHeaderBlock(response_headers, - challenge_, - spdy_protocol_version_); - - SaveCookiesAndNotifyHeadersComplete(); -} - -void WebSocketJob::OnSentSpdyData(size_t bytes_sent) { - DCHECK_NE(INITIALIZED, state_); - DCHECK_NE(CONNECTING, state_); - if (state_ == CLOSED) - return; - if (!spdy_websocket_stream_.get()) - return; - OnSentData(socket_.get(), static_cast<int>(bytes_sent)); -} - -void WebSocketJob::OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) { - DCHECK_NE(INITIALIZED, state_); - DCHECK_NE(CONNECTING, state_); - if (state_ == CLOSED) - return; - if (!spdy_websocket_stream_.get()) - return; - if (buffer) { - OnReceivedData( - socket_.get(), buffer->GetRemainingData(), buffer->GetRemainingSize()); - } else { - OnReceivedData(socket_.get(), NULL, 0); - } -} - -void WebSocketJob::OnCloseSpdyStream() { - spdy_websocket_stream_.reset(); - OnClose(socket_.get()); -} - -bool WebSocketJob::SendHandshakeRequest(const char* data, int len) { - DCHECK_EQ(state_, CONNECTING); - if (started_to_send_handshake_request_) - return false; - if (!handshake_request_->ParseRequest(data, len)) - return false; - - AddCookieHeaderAndSend(); - return true; -} - -void WebSocketJob::AddCookieHeaderAndSend() { - bool allow = true; - if (delegate_ && !delegate_->CanGetCookies(socket_.get(), GetURLForCookies())) - allow = false; - - if (socket_.get() && delegate_ && state_ == CONNECTING) { - handshake_request_->RemoveHeaders(kCookieHeaders, - arraysize(kCookieHeaders)); - if (allow && socket_->cookie_store()) { - // Add cookies, including HttpOnly cookies. - CookieOptions cookie_options; - cookie_options.set_include_httponly(); - socket_->cookie_store()->GetCookiesWithOptionsAsync( - GetURLForCookies(), cookie_options, - base::Bind(&WebSocketJob::LoadCookieCallback, - weak_ptr_factory_.GetWeakPtr())); - } else { - DoSendData(); - } - } -} - -void WebSocketJob::LoadCookieCallback(const std::string& cookie) { - if (!cookie.empty()) - // TODO(tyoshino): Sending cookie means that connection doesn't need - // PRIVACY_MODE_ENABLED as cookies may be server-bound and channel id - // wouldn't negatively affect privacy anyway. Need to restart connection - // or refactor to determine cookie status prior to connecting. - handshake_request_->AppendHeaderIfMissing("Cookie", cookie); - DoSendData(); -} - -void WebSocketJob::DoSendData() { - if (spdy_websocket_stream_.get()) { - scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); - handshake_request_->GetRequestHeaderBlock( - socket_->url(), headers.get(), &challenge_, spdy_protocol_version_); - spdy_websocket_stream_->SendRequest(headers.Pass()); - } else { - const std::string& handshake_request = - handshake_request_->GetRawRequest(); - handshake_request_sent_ = 0; - socket_->net_log()->AddEvent( - NetLog::TYPE_WEB_SOCKET_SEND_REQUEST_HEADERS, - base::Bind(&NetLogWebSocketHandshakeCallback, &handshake_request)); - socket_->SendData(handshake_request.data(), - handshake_request.size()); - } - // Just buffered in |handshake_request_|. - started_to_send_handshake_request_ = true; -} - -void WebSocketJob::OnSentHandshakeRequest( - SocketStream* socket, int amount_sent) { - DCHECK_EQ(state_, CONNECTING); - handshake_request_sent_ += amount_sent; - DCHECK_LE(handshake_request_sent_, handshake_request_->raw_length()); - if (handshake_request_sent_ >= handshake_request_->raw_length()) { - // handshake request has been sent. - // notify original size of handshake request to delegate. - // Reset the handshake_request_ first in case this object is deleted by the - // delegate. - size_t original_length = handshake_request_->original_length(); - handshake_request_.reset(); - if (delegate_) - delegate_->OnSentData(socket, original_length); - } -} - -void WebSocketJob::OnReceivedHandshakeResponse( - SocketStream* socket, const char* data, int len) { - DCHECK_EQ(state_, CONNECTING); - if (handshake_response_->HasResponse()) { - // If we already has handshake response, received data should be frame - // data, not handshake message. - received_data_after_handshake_.insert( - received_data_after_handshake_.end(), data, data + len); - return; - } - - size_t response_length = handshake_response_->ParseRawResponse(data, len); - if (!handshake_response_->HasResponse()) { - // not yet. we need more data. - return; - } - // handshake message is completed. - std::string raw_response = handshake_response_->GetRawResponse(); - socket_->net_log()->AddEvent( - NetLog::TYPE_WEB_SOCKET_READ_RESPONSE_HEADERS, - base::Bind(&NetLogWebSocketHandshakeCallback, &raw_response)); - if (len - response_length > 0) { - // If we received extra data, it should be frame data. - DCHECK(received_data_after_handshake_.empty()); - received_data_after_handshake_.assign(data + response_length, data + len); - } - SaveCookiesAndNotifyHeadersComplete(); -} - -void WebSocketJob::SaveCookiesAndNotifyHeadersComplete() { - // handshake message is completed. - DCHECK(handshake_response_->HasResponse()); - - // Extract cookies from the handshake response into a temporary vector. - response_cookies_.clear(); - response_cookies_save_index_ = 0; - - handshake_response_->GetHeaders( - kSetCookieHeaders, arraysize(kSetCookieHeaders), &response_cookies_); - - // Now, loop over the response cookies, and attempt to persist each. - SaveNextCookie(); -} - -void WebSocketJob::NotifyHeadersComplete() { - // Remove cookie headers, with malformed headers preserved. - // Actual handshake should be done in Blink. - handshake_response_->RemoveHeaders( - kSetCookieHeaders, arraysize(kSetCookieHeaders)); - std::string handshake_response = handshake_response_->GetResponse(); - handshake_response_.reset(); - std::vector<char> received_data(handshake_response.begin(), - handshake_response.end()); - received_data.insert(received_data.end(), - received_data_after_handshake_.begin(), - received_data_after_handshake_.end()); - received_data_after_handshake_.clear(); - - state_ = OPEN; - - DCHECK(!received_data.empty()); - if (delegate_) - delegate_->OnReceivedData( - socket_.get(), &received_data.front(), received_data.size()); - - WebSocketThrottle::GetInstance()->RemoveFromQueue(this); -} - -void WebSocketJob::SaveNextCookie() { - if (!socket_.get() || !delegate_ || state_ != CONNECTING) - return; - - callback_pending_ = false; - save_next_cookie_running_ = true; - - if (socket_->cookie_store()) { - GURL url_for_cookies = GetURLForCookies(); - - CookieOptions options; - options.set_include_httponly(); - - // Loop as long as SetCookieWithOptionsAsync completes synchronously. Since - // CookieMonster's asynchronous operation APIs queue the callback to run it - // on the thread where the API was called, there won't be race. I.e. unless - // the callback is run synchronously, it won't be run in parallel with this - // method. - while (!callback_pending_ && - response_cookies_save_index_ < response_cookies_.size()) { - std::string cookie = response_cookies_[response_cookies_save_index_]; - response_cookies_save_index_++; - - if (!delegate_->CanSetCookie( - socket_.get(), url_for_cookies, cookie, &options)) - continue; - - callback_pending_ = true; - socket_->cookie_store()->SetCookieWithOptionsAsync( - url_for_cookies, cookie, options, - base::Bind(&WebSocketJob::OnCookieSaved, - weak_ptr_factory_.GetWeakPtr())); - } - } - - save_next_cookie_running_ = false; - - if (callback_pending_) - return; - - response_cookies_.clear(); - response_cookies_save_index_ = 0; - - NotifyHeadersComplete(); -} - -void WebSocketJob::OnCookieSaved(bool cookie_status) { - // Tell the caller of SetCookieWithOptionsAsync() that this completion - // callback is invoked. - // - If the caller checks callback_pending earlier than this callback, the - // caller exits to let this method continue iteration. - // - Otherwise, the caller continues iteration. - callback_pending_ = false; - - // Resume SaveNextCookie if the caller of SetCookieWithOptionsAsync() exited - // the loop. Otherwise, return. - if (save_next_cookie_running_) - return; - - SaveNextCookie(); -} - -GURL WebSocketJob::GetURLForCookies() const { - GURL url = socket_->url(); - std::string scheme = socket_->is_secure() ? "https" : "http"; - url::Replacements<char> replacements; - replacements.SetScheme(scheme.c_str(), url::Component(0, scheme.length())); - return url.ReplaceComponents(replacements); -} - -const AddressList& WebSocketJob::address_list() const { - return addresses_; -} - -int WebSocketJob::TrySpdyStream() { - if (!socket_.get()) - return ERR_FAILED; - - // Check if we have a SPDY session available. - HttpTransactionFactory* factory = - socket_->context()->http_transaction_factory(); - if (!factory) - return OK; - scoped_refptr<HttpNetworkSession> session = factory->GetSession(); - if (!session.get() || !session->params().enable_websocket_over_spdy) - return OK; - SpdySessionPool* spdy_pool = session->spdy_session_pool(); - PrivacyMode privacy_mode = socket_->privacy_mode(); - const SpdySessionKey key(HostPortPair::FromURL(socket_->url()), - socket_->proxy_server(), privacy_mode); - // Forbid wss downgrade to SPDY without SSL. - // TODO(toyoshim): Does it realize the same policy with HTTP? - base::WeakPtr<SpdySession> spdy_session = - spdy_pool->FindAvailableSession(key, *socket_->net_log()); - if (!spdy_session) - return OK; - - SSLInfo ssl_info; - bool was_npn_negotiated; - NextProto protocol_negotiated = kProtoUnknown; - bool use_ssl = spdy_session->GetSSLInfo( - &ssl_info, &was_npn_negotiated, &protocol_negotiated); - if (socket_->is_secure() && !use_ssl) - return OK; - - // Create SpdyWebSocketStream. - spdy_protocol_version_ = spdy_session->GetProtocolVersion(); - spdy_websocket_stream_.reset(new SpdyWebSocketStream(spdy_session, this)); - - int result = spdy_websocket_stream_->InitializeStream( - socket_->url(), MEDIUM, *socket_->net_log()); - if (result == OK) { - OnConnected(socket_.get(), kMaxPendingSendAllowed); - return ERR_PROTOCOL_SWITCHED; - } - if (result != ERR_IO_PENDING) { - spdy_websocket_stream_.reset(); - return OK; - } - - return ERR_IO_PENDING; -} - -void WebSocketJob::SetWaiting() { - waiting_ = true; -} - -bool WebSocketJob::IsWaiting() const { - return waiting_; -} - -void WebSocketJob::Wakeup() { - if (!waiting_) - return; - waiting_ = false; - DCHECK(!callback_.is_null()); - base::MessageLoopForIO::current()->PostTask( - FROM_HERE, - base::Bind(&WebSocketJob::RetryPendingIO, - weak_ptr_factory_.GetWeakPtr())); -} - -void WebSocketJob::RetryPendingIO() { - int result = TrySpdyStream(); - - // In the case of ERR_IO_PENDING, CompleteIO() will be called from - // OnCreatedSpdyStream(). - if (result != ERR_IO_PENDING) - CompleteIO(result); -} - -void WebSocketJob::CompleteIO(int result) { - // |callback_| may be null if OnClose() or DetachDelegate() was called. - if (!callback_.is_null()) { - CompletionCallback callback = callback_; - callback_.Reset(); - callback.Run(result); - Release(); // Balanced with OnStartOpenConnection(). - } -} - -bool WebSocketJob::SendDataInternal(const char* data, int length) { - if (spdy_websocket_stream_.get()) - return ERR_IO_PENDING == spdy_websocket_stream_->SendData(data, length); - if (socket_.get()) - return socket_->SendData(data, length); - return false; -} - -void WebSocketJob::CloseInternal() { - if (spdy_websocket_stream_.get()) - spdy_websocket_stream_->Close(); - if (socket_.get()) - socket_->Close(); -} - -void WebSocketJob::SendPending() { - if (current_send_buffer_.get()) - return; - - // Current buffer has been sent. Try next if any. - if (send_buffer_queue_.empty()) { - // No more data to send. - if (state_ == CLOSING) - CloseInternal(); - return; - } - - scoped_refptr<IOBufferWithSize> next_buffer = send_buffer_queue_.front(); - send_buffer_queue_.pop_front(); - current_send_buffer_ = - new DrainableIOBuffer(next_buffer.get(), next_buffer->size()); - SendDataInternal(current_send_buffer_->data(), - current_send_buffer_->BytesRemaining()); -} - -} // namespace net
diff --git a/net/websockets/websocket_job.h b/net/websockets/websocket_job.h deleted file mode 100644 index bad06cf..0000000 --- a/net/websockets/websocket_job.h +++ /dev/null
@@ -1,154 +0,0 @@ -// Copyright (c) 2012 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 NET_WEBSOCKETS_WEBSOCKET_JOB_H_ -#define NET_WEBSOCKETS_WEBSOCKET_JOB_H_ - -#include <deque> -#include <string> -#include <vector> - -#include "base/memory/weak_ptr.h" -#include "net/base/address_list.h" -#include "net/base/completion_callback.h" -#include "net/socket_stream/socket_stream_job.h" -#include "net/spdy/spdy_header_block.h" -#include "net/spdy/spdy_websocket_stream.h" - -class GURL; - -namespace net { - -class DrainableIOBuffer; -class SSLInfo; -class WebSocketHandshakeRequestHandler; -class WebSocketHandshakeResponseHandler; - -// WebSocket protocol specific job on SocketStream. -// It captures WebSocket handshake message and handles cookie operations. -// Chrome security policy doesn't allow renderer process (except dev tools) -// see HttpOnly cookies, so it injects cookie header in handshake request and -// strips set-cookie headers in handshake response. -// TODO(ukai): refactor websocket.cc to use this. -class NET_EXPORT WebSocketJob - : public SocketStreamJob, - public SocketStream::Delegate, - public SpdyWebSocketStream::Delegate { - public: - // This is state of WebSocket, not SocketStream. - enum State { - INITIALIZED = -1, - CONNECTING = 0, - OPEN = 1, - CLOSING = 2, - CLOSED = 3, - }; - - explicit WebSocketJob(SocketStream::Delegate* delegate); - - static void EnsureInit(); - - State state() const { return state_; } - void Connect() override; - bool SendData(const char* data, int len) override; - void Close() override; - void RestartWithAuth(const AuthCredentials& credentials) override; - void DetachDelegate() override; - - // SocketStream::Delegate methods. - int OnStartOpenConnection(SocketStream* socket, - const CompletionCallback& callback) override; - void OnConnected(SocketStream* socket, int max_pending_send_allowed) override; - void OnSentData(SocketStream* socket, int amount_sent) override; - void OnReceivedData(SocketStream* socket, const char* data, int len) override; - void OnClose(SocketStream* socket) override; - void OnAuthRequired(SocketStream* socket, - AuthChallengeInfo* auth_info) override; - void OnSSLCertificateError(SocketStream* socket, - const SSLInfo& ssl_info, - bool fatal) override; - void OnError(const SocketStream* socket, int error) override; - - // SpdyWebSocketStream::Delegate methods. - void OnCreatedSpdyStream(int status) override; - void OnSentSpdyHeaders() override; - void OnSpdyResponseHeadersUpdated( - const SpdyHeaderBlock& response_headers) override; - void OnSentSpdyData(size_t bytes_sent) override; - void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) override; - void OnCloseSpdyStream() override; - - private: - friend class WebSocketThrottle; - friend class WebSocketJobTest; - ~WebSocketJob() override; - - bool SendHandshakeRequest(const char* data, int len); - void AddCookieHeaderAndSend(); - void LoadCookieCallback(const std::string& cookie); - - void OnSentHandshakeRequest(SocketStream* socket, int amount_sent); - // Parses received data into handshake_response_. When finished receiving the - // response, calls SaveCookiesAndNotifyHeadersComplete(). - void OnReceivedHandshakeResponse( - SocketStream* socket, const char* data, int len); - // Saves received cookies to the cookie store, and then notifies the - // delegate_ of completion of handshake. - void SaveCookiesAndNotifyHeadersComplete(); - void SaveNextCookie(); - void OnCookieSaved(bool cookie_status); - // Clears variables for handling cookies, rebuilds handshake string excluding - // cookies, and then pass the handshake string to delegate_. - void NotifyHeadersComplete(); - void DoSendData(); - - GURL GetURLForCookies() const; - - const AddressList& address_list() const; - int TrySpdyStream(); - void SetWaiting(); - bool IsWaiting() const; - void Wakeup(); - void RetryPendingIO(); - void CompleteIO(int result); - - bool SendDataInternal(const char* data, int length); - void CloseInternal(); - void SendPending(); - - SocketStream::Delegate* delegate_; - State state_; - bool waiting_; - AddressList addresses_; - CompletionCallback callback_; // for throttling. - - scoped_ptr<WebSocketHandshakeRequestHandler> handshake_request_; - scoped_ptr<WebSocketHandshakeResponseHandler> handshake_response_; - - bool started_to_send_handshake_request_; - size_t handshake_request_sent_; - - std::vector<std::string> response_cookies_; - size_t response_cookies_save_index_; - - std::deque<scoped_refptr<IOBufferWithSize> > send_buffer_queue_; - scoped_refptr<DrainableIOBuffer> current_send_buffer_; - std::vector<char> received_data_after_handshake_; - - int spdy_protocol_version_; - scoped_ptr<SpdyWebSocketStream> spdy_websocket_stream_; - std::string challenge_; - - bool save_next_cookie_running_; - bool callback_pending_; - - base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_; - base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_for_send_pending_; - - DISALLOW_COPY_AND_ASSIGN(WebSocketJob); -}; - -} // namespace - -#endif // NET_WEBSOCKETS_WEBSOCKET_JOB_H_
diff --git a/net/websockets/websocket_job_test.cc b/net/websockets/websocket_job_test.cc deleted file mode 100644 index c84af8f..0000000 --- a/net/websockets/websocket_job_test.cc +++ /dev/null
@@ -1,1287 +0,0 @@ -// Copyright 2013 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 "net/websockets/websocket_job.h" - -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "net/base/completion_callback.h" -#include "net/base/net_errors.h" -#include "net/base/test_completion_callback.h" -#include "net/cookies/cookie_store.h" -#include "net/cookies/cookie_store_test_helpers.h" -#include "net/dns/mock_host_resolver.h" -#include "net/http/http_transaction_factory.h" -#include "net/http/transport_security_state.h" -#include "net/proxy/proxy_service.h" -#include "net/socket/next_proto.h" -#include "net/socket/socket_test_util.h" -#include "net/socket_stream/socket_stream.h" -#include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_websocket_test_util.h" -#include "net/ssl/ssl_config_service.h" -#include "net/url_request/url_request_context.h" -#include "net/websockets/websocket_throttle.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#include "url/gurl.h" - -namespace net { - -namespace { - -class MockSocketStream : public SocketStream { - public: - MockSocketStream(const GURL& url, SocketStream::Delegate* delegate, - URLRequestContext* context, CookieStore* cookie_store) - : SocketStream(url, delegate, context, cookie_store) {} - - void Connect() override {} - bool SendData(const char* data, int len) override { - sent_data_ += std::string(data, len); - return true; - } - - void Close() override {} - void RestartWithAuth(const AuthCredentials& credentials) override {} - - void DetachDelegate() override { delegate_ = NULL; } - - const std::string& sent_data() const { - return sent_data_; - } - - protected: - ~MockSocketStream() override {} - - private: - std::string sent_data_; -}; - -class MockSocketStreamDelegate : public SocketStream::Delegate { - public: - MockSocketStreamDelegate() - : amount_sent_(0), allow_all_cookies_(true) {} - void set_allow_all_cookies(bool allow_all_cookies) { - allow_all_cookies_ = allow_all_cookies; - } - ~MockSocketStreamDelegate() override {} - - void SetOnStartOpenConnection(const base::Closure& callback) { - on_start_open_connection_ = callback; - } - void SetOnConnected(const base::Closure& callback) { - on_connected_ = callback; - } - void SetOnSentData(const base::Closure& callback) { - on_sent_data_ = callback; - } - void SetOnReceivedData(const base::Closure& callback) { - on_received_data_ = callback; - } - void SetOnClose(const base::Closure& callback) { - on_close_ = callback; - } - - int OnStartOpenConnection(SocketStream* socket, - const CompletionCallback& callback) override { - if (!on_start_open_connection_.is_null()) - on_start_open_connection_.Run(); - return OK; - } - void OnConnected(SocketStream* socket, - int max_pending_send_allowed) override { - if (!on_connected_.is_null()) - on_connected_.Run(); - } - void OnSentData(SocketStream* socket, int amount_sent) override { - amount_sent_ += amount_sent; - if (!on_sent_data_.is_null()) - on_sent_data_.Run(); - } - void OnReceivedData(SocketStream* socket, - const char* data, - int len) override { - received_data_ += std::string(data, len); - if (!on_received_data_.is_null()) - on_received_data_.Run(); - } - void OnClose(SocketStream* socket) override { - if (!on_close_.is_null()) - on_close_.Run(); - } - bool CanGetCookies(SocketStream* socket, const GURL& url) override { - return allow_all_cookies_; - } - bool CanSetCookie(SocketStream* request, - const GURL& url, - const std::string& cookie_line, - CookieOptions* options) override { - return allow_all_cookies_; - } - - size_t amount_sent() const { return amount_sent_; } - const std::string& received_data() const { return received_data_; } - - private: - int amount_sent_; - bool allow_all_cookies_; - std::string received_data_; - base::Closure on_start_open_connection_; - base::Closure on_connected_; - base::Closure on_sent_data_; - base::Closure on_received_data_; - base::Closure on_close_; -}; - -class MockCookieStore : public CookieStore { - public: - struct Entry { - GURL url; - std::string cookie_line; - CookieOptions options; - }; - - MockCookieStore() {} - - bool SetCookieWithOptions(const GURL& url, - const std::string& cookie_line, - const CookieOptions& options) { - Entry entry; - entry.url = url; - entry.cookie_line = cookie_line; - entry.options = options; - entries_.push_back(entry); - return true; - } - - std::string GetCookiesWithOptions(const GURL& url, - const CookieOptions& options) { - std::string result; - for (size_t i = 0; i < entries_.size(); i++) { - Entry& entry = entries_[i]; - if (url == entry.url) { - if (!result.empty()) { - result += "; "; - } - result += entry.cookie_line; - } - } - return result; - } - - // CookieStore: - void SetCookieWithOptionsAsync(const GURL& url, - const std::string& cookie_line, - const CookieOptions& options, - const SetCookiesCallback& callback) override { - bool result = SetCookieWithOptions(url, cookie_line, options); - if (!callback.is_null()) - callback.Run(result); - } - - void GetCookiesWithOptionsAsync(const GURL& url, - const CookieOptions& options, - const GetCookiesCallback& callback) override { - if (!callback.is_null()) - callback.Run(GetCookiesWithOptions(url, options)); - } - - void GetAllCookiesForURLAsync( - const GURL& url, - const GetCookieListCallback& callback) override { - ADD_FAILURE(); - } - - void DeleteCookieAsync(const GURL& url, - const std::string& cookie_name, - const base::Closure& callback) override { - ADD_FAILURE(); - } - - void DeleteAllCreatedBetweenAsync(const base::Time& delete_begin, - const base::Time& delete_end, - const DeleteCallback& callback) override { - ADD_FAILURE(); - } - - void DeleteAllCreatedBetweenForHostAsync( - const base::Time delete_begin, - const base::Time delete_end, - const GURL& url, - const DeleteCallback& callback) override { - ADD_FAILURE(); - } - - void DeleteSessionCookiesAsync(const DeleteCallback&) override { - ADD_FAILURE(); - } - - CookieMonster* GetCookieMonster() override { return NULL; } - - scoped_ptr<CookieStore::CookieChangedSubscription> - AddCallbackForCookie(const GURL& url, const std::string& name, - const CookieChangedCallback& callback) override { - ADD_FAILURE(); - return scoped_ptr<CookieChangedSubscription>(); - } - - const std::vector<Entry>& entries() const { return entries_; } - - private: - friend class base::RefCountedThreadSafe<MockCookieStore>; - ~MockCookieStore() override {} - - std::vector<Entry> entries_; -}; - -class MockSSLConfigService : public SSLConfigService { - public: - void GetSSLConfig(SSLConfig* config) override {} - - protected: - ~MockSSLConfigService() override {} -}; - -class MockURLRequestContext : public URLRequestContext { - public: - explicit MockURLRequestContext(CookieStore* cookie_store) - : transport_security_state_() { - set_cookie_store(cookie_store); - set_transport_security_state(&transport_security_state_); - base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000); - bool include_subdomains = false; - transport_security_state_.AddHSTS("upgrademe.com", expiry, - include_subdomains); - } - - ~MockURLRequestContext() override { AssertNoURLRequests(); } - - private: - TransportSecurityState transport_security_state_; -}; - -class MockHttpTransactionFactory : public HttpTransactionFactory { - public: - MockHttpTransactionFactory(NextProto next_proto, - OrderedSocketData* data, - bool enable_websocket_over_spdy) { - data_ = data; - MockConnect connect_data(SYNCHRONOUS, OK); - data_->set_connect_data(connect_data); - session_deps_.reset(new SpdySessionDependencies(next_proto)); - session_deps_->enable_websocket_over_spdy = enable_websocket_over_spdy; - session_deps_->socket_factory->AddSocketDataProvider(data_); - http_session_ = - SpdySessionDependencies::SpdyCreateSession(session_deps_.get()); - host_port_pair_.set_host("example.com"); - host_port_pair_.set_port(80); - spdy_session_key_ = SpdySessionKey(host_port_pair_, - ProxyServer::Direct(), - PRIVACY_MODE_DISABLED); - session_ = CreateInsecureSpdySession( - http_session_, spdy_session_key_, BoundNetLog()); - } - - int CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans) override { - NOTREACHED(); - return ERR_UNEXPECTED; - } - - HttpCache* GetCache() override { - NOTREACHED(); - return NULL; - } - - HttpNetworkSession* GetSession() override { return http_session_.get(); } - - private: - OrderedSocketData* data_; - scoped_ptr<SpdySessionDependencies> session_deps_; - scoped_refptr<HttpNetworkSession> http_session_; - base::WeakPtr<SpdySession> session_; - HostPortPair host_port_pair_; - SpdySessionKey spdy_session_key_; -}; - -class DeletingSocketStreamDelegate : public SocketStream::Delegate { - public: - DeletingSocketStreamDelegate() - : delete_next_(false) {} - - // Since this class needs to be able to delete |job_|, it must be the only - // reference holder (except for temporary references). Provide access to the - // pointer for tests to use. - WebSocketJob* job() { return job_.get(); } - - void set_job(WebSocketJob* job) { job_ = job; } - - // After calling this, the next call to a method on this delegate will delete - // the WebSocketJob object. - void set_delete_next(bool delete_next) { delete_next_ = delete_next; } - - void DeleteJobMaybe() { - if (delete_next_) { - job_->DetachContext(); - job_->DetachDelegate(); - job_ = NULL; - } - } - - // SocketStream::Delegate implementation - - // OnStartOpenConnection() is not implemented by SocketStreamDispatcherHost - - void OnConnected(SocketStream* socket, - int max_pending_send_allowed) override { - DeleteJobMaybe(); - } - - void OnSentData(SocketStream* socket, int amount_sent) override { - DeleteJobMaybe(); - } - - void OnReceivedData(SocketStream* socket, - const char* data, - int len) override { - DeleteJobMaybe(); - } - - void OnClose(SocketStream* socket) override { DeleteJobMaybe(); } - - void OnAuthRequired(SocketStream* socket, - AuthChallengeInfo* auth_info) override { - DeleteJobMaybe(); - } - - void OnSSLCertificateError(SocketStream* socket, - const SSLInfo& ssl_info, - bool fatal) override { - DeleteJobMaybe(); - } - - void OnError(const SocketStream* socket, int error) override { - DeleteJobMaybe(); - } - - // CanGetCookies() and CanSetCookies() do not appear to be able to delete the - // WebSocketJob object. - - private: - scoped_refptr<WebSocketJob> job_; - bool delete_next_; -}; - -} // namespace - -class WebSocketJobTest : public PlatformTest, - public ::testing::WithParamInterface<NextProto> { - public: - WebSocketJobTest() - : spdy_util_(GetParam()), - enable_websocket_over_spdy_(false) {} - - void SetUp() override { - stream_type_ = STREAM_INVALID; - cookie_store_ = new MockCookieStore; - context_.reset(new MockURLRequestContext(cookie_store_.get())); - } - void TearDown() override { - cookie_store_ = NULL; - context_.reset(); - websocket_ = NULL; - socket_ = NULL; - } - void DoSendRequest() { - EXPECT_TRUE(websocket_->SendData(kHandshakeRequestWithoutCookie, - kHandshakeRequestWithoutCookieLength)); - } - void DoSendData() { - if (received_data().size() == kHandshakeResponseWithoutCookieLength) - websocket_->SendData(kDataHello, kDataHelloLength); - } - void DoSync() { - sync_test_callback_.callback().Run(OK); - } - int WaitForResult() { - return sync_test_callback_.WaitForResult(); - } - - protected: - enum StreamType { - STREAM_INVALID, - STREAM_MOCK_SOCKET, - STREAM_SOCKET, - STREAM_SPDY_WEBSOCKET, - }; - enum ThrottlingOption { - THROTTLING_OFF, - THROTTLING_ON, - }; - enum SpdyOption { - SPDY_OFF, - SPDY_ON, - }; - void InitWebSocketJob(const GURL& url, - MockSocketStreamDelegate* delegate, - StreamType stream_type) { - DCHECK_NE(STREAM_INVALID, stream_type); - stream_type_ = stream_type; - websocket_ = new WebSocketJob(delegate); - - if (stream_type == STREAM_MOCK_SOCKET) - socket_ = new MockSocketStream(url, websocket_.get(), context_.get(), - NULL); - - if (stream_type == STREAM_SOCKET || stream_type == STREAM_SPDY_WEBSOCKET) { - if (stream_type == STREAM_SPDY_WEBSOCKET) { - http_factory_.reset(new MockHttpTransactionFactory( - GetParam(), data_.get(), enable_websocket_over_spdy_)); - context_->set_http_transaction_factory(http_factory_.get()); - } - - ssl_config_service_ = new MockSSLConfigService(); - context_->set_ssl_config_service(ssl_config_service_.get()); - proxy_service_.reset(ProxyService::CreateDirect()); - context_->set_proxy_service(proxy_service_.get()); - host_resolver_.reset(new MockHostResolver); - context_->set_host_resolver(host_resolver_.get()); - - socket_ = new SocketStream(url, websocket_.get(), context_.get(), NULL); - socket_factory_.reset(new MockClientSocketFactory); - DCHECK(data_.get()); - socket_factory_->AddSocketDataProvider(data_.get()); - socket_->SetClientSocketFactory(socket_factory_.get()); - } - - websocket_->InitSocketStream(socket_.get()); - // MockHostResolver resolves all hosts to 127.0.0.1; however, when we create - // a WebSocketJob purely to block another one in a throttling test, we don't - // perform a real connect. In that case, the following address is used - // instead. - IPAddressNumber ip; - ParseIPLiteralToNumber("127.0.0.1", &ip); - websocket_->addresses_ = AddressList::CreateFromIPAddress(ip, 80); - } - void SkipToConnecting() { - websocket_->state_ = WebSocketJob::CONNECTING; - ASSERT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(websocket_.get())); - } - WebSocketJob::State GetWebSocketJobState() { - return websocket_->state_; - } - void CloseWebSocketJob() { - if (websocket_->socket_.get()) { - websocket_->socket_->DetachDelegate(); - WebSocketThrottle::GetInstance()->RemoveFromQueue(websocket_.get()); - } - websocket_->state_ = WebSocketJob::CLOSED; - websocket_->delegate_ = NULL; - websocket_->socket_ = NULL; - } - SocketStream* GetSocket(SocketStreamJob* job) { - return job->socket_.get(); - } - const std::string& sent_data() const { - DCHECK_EQ(STREAM_MOCK_SOCKET, stream_type_); - MockSocketStream* socket = - static_cast<MockSocketStream*>(socket_.get()); - DCHECK(socket); - return socket->sent_data(); - } - const std::string& received_data() const { - DCHECK_NE(STREAM_INVALID, stream_type_); - MockSocketStreamDelegate* delegate = - static_cast<MockSocketStreamDelegate*>(websocket_->delegate_); - DCHECK(delegate); - return delegate->received_data(); - } - - void TestSimpleHandshake(); - void TestSlowHandshake(); - void TestHandshakeWithCookie(); - void TestHandshakeWithCookieButNotAllowed(); - void TestHSTSUpgrade(); - void TestInvalidSendData(); - void TestConnectByWebSocket(ThrottlingOption throttling); - void TestConnectBySpdy(SpdyOption spdy, ThrottlingOption throttling); - void TestThrottlingLimit(); - - SpdyWebSocketTestUtil spdy_util_; - StreamType stream_type_; - scoped_refptr<MockCookieStore> cookie_store_; - scoped_ptr<MockURLRequestContext> context_; - scoped_refptr<WebSocketJob> websocket_; - scoped_refptr<SocketStream> socket_; - scoped_ptr<MockClientSocketFactory> socket_factory_; - scoped_ptr<OrderedSocketData> data_; - TestCompletionCallback sync_test_callback_; - scoped_refptr<MockSSLConfigService> ssl_config_service_; - scoped_ptr<ProxyService> proxy_service_; - scoped_ptr<MockHostResolver> host_resolver_; - scoped_ptr<MockHttpTransactionFactory> http_factory_; - - // Must be set before call to enable_websocket_over_spdy, defaults to false. - bool enable_websocket_over_spdy_; - - static const char kHandshakeRequestWithoutCookie[]; - static const char kHandshakeRequestWithCookie[]; - static const char kHandshakeRequestWithFilteredCookie[]; - static const char kHandshakeResponseWithoutCookie[]; - static const char kHandshakeResponseWithCookie[]; - static const char kDataHello[]; - static const char kDataWorld[]; - static const char* const kHandshakeRequestForSpdy[]; - static const char* const kHandshakeResponseForSpdy[]; - static const size_t kHandshakeRequestWithoutCookieLength; - static const size_t kHandshakeRequestWithCookieLength; - static const size_t kHandshakeRequestWithFilteredCookieLength; - static const size_t kHandshakeResponseWithoutCookieLength; - static const size_t kHandshakeResponseWithCookieLength; - static const size_t kDataHelloLength; - static const size_t kDataWorldLength; -}; - -// Tests using this fixture verify that the WebSocketJob can handle being -// deleted while calling back to the delegate correctly. These tests need to be -// run under AddressSanitizer or other systems for detecting use-after-free -// errors in order to find problems. -class WebSocketJobDeleteTest : public ::testing::Test { - protected: - WebSocketJobDeleteTest() - : delegate_(new DeletingSocketStreamDelegate), - cookie_store_(new MockCookieStore), - context_(new MockURLRequestContext(cookie_store_.get())) { - WebSocketJob* websocket = new WebSocketJob(delegate_.get()); - delegate_->set_job(websocket); - - socket_ = new MockSocketStream( - GURL("ws://127.0.0.1/"), websocket, context_.get(), NULL); - - websocket->InitSocketStream(socket_.get()); - } - - void SetDeleteNext() { return delegate_->set_delete_next(true); } - WebSocketJob* job() { return delegate_->job(); } - - scoped_ptr<DeletingSocketStreamDelegate> delegate_; - scoped_refptr<MockCookieStore> cookie_store_; - scoped_ptr<MockURLRequestContext> context_; - scoped_refptr<SocketStream> socket_; -}; - -const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n"; - -const char WebSocketJobTest::kHandshakeRequestWithCookie[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Cookie: WK-test=1\r\n" - "\r\n"; - -const char WebSocketJobTest::kHandshakeRequestWithFilteredCookie[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Origin: http://example.com\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Cookie: CR-test=1; CR-test-httponly=1\r\n" - "\r\n"; - -const char WebSocketJobTest::kHandshakeResponseWithoutCookie[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "\r\n"; - -const char WebSocketJobTest::kHandshakeResponseWithCookie[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Set-Cookie: CR-set-test=1\r\n" - "\r\n"; - -const char WebSocketJobTest::kDataHello[] = "Hello, "; - -const char WebSocketJobTest::kDataWorld[] = "World!\n"; - -const size_t WebSocketJobTest::kHandshakeRequestWithoutCookieLength = - arraysize(kHandshakeRequestWithoutCookie) - 1; -const size_t WebSocketJobTest::kHandshakeRequestWithCookieLength = - arraysize(kHandshakeRequestWithCookie) - 1; -const size_t WebSocketJobTest::kHandshakeRequestWithFilteredCookieLength = - arraysize(kHandshakeRequestWithFilteredCookie) - 1; -const size_t WebSocketJobTest::kHandshakeResponseWithoutCookieLength = - arraysize(kHandshakeResponseWithoutCookie) - 1; -const size_t WebSocketJobTest::kHandshakeResponseWithCookieLength = - arraysize(kHandshakeResponseWithCookie) - 1; -const size_t WebSocketJobTest::kDataHelloLength = - arraysize(kDataHello) - 1; -const size_t WebSocketJobTest::kDataWorldLength = - arraysize(kDataWorld) - 1; - -void WebSocketJobTest::TestSimpleHandshake() { - GURL url("ws://example.com/demo"); - MockSocketStreamDelegate delegate; - InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); - SkipToConnecting(); - - DoSendRequest(); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - websocket_->OnSentData(socket_.get(), - kHandshakeRequestWithoutCookieLength); - EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); - - websocket_->OnReceivedData(socket_.get(), - kHandshakeResponseWithoutCookie, - kHandshakeResponseWithoutCookieLength); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); - EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); - CloseWebSocketJob(); -} - -void WebSocketJobTest::TestSlowHandshake() { - GURL url("ws://example.com/demo"); - MockSocketStreamDelegate delegate; - InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); - SkipToConnecting(); - - DoSendRequest(); - // We assume request is sent in one data chunk (from WebKit) - // We don't support streaming request. - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - websocket_->OnSentData(socket_.get(), - kHandshakeRequestWithoutCookieLength); - EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); - - std::vector<std::string> lines; - base::SplitString(kHandshakeResponseWithoutCookie, '\n', &lines); - for (size_t i = 0; i < lines.size() - 2; i++) { - std::string line = lines[i] + "\r\n"; - SCOPED_TRACE("Line: " + line); - websocket_->OnReceivedData(socket_.get(), line.c_str(), line.size()); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_TRUE(delegate.received_data().empty()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - } - websocket_->OnReceivedData(socket_.get(), "\r\n", 2); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_FALSE(delegate.received_data().empty()); - EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); - EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); - CloseWebSocketJob(); -} - -INSTANTIATE_TEST_CASE_P( - NextProto, - WebSocketJobTest, - testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); - -TEST_P(WebSocketJobTest, DelayedCookies) { - enable_websocket_over_spdy_ = true; - GURL url("ws://example.com/demo"); - GURL cookieUrl("http://example.com/demo"); - CookieOptions cookie_options; - scoped_refptr<DelayedCookieMonster> cookie_store = new DelayedCookieMonster(); - context_->set_cookie_store(cookie_store.get()); - cookie_store->SetCookieWithOptionsAsync(cookieUrl, - "CR-test=1", - cookie_options, - CookieMonster::SetCookiesCallback()); - cookie_options.set_include_httponly(); - cookie_store->SetCookieWithOptionsAsync( - cookieUrl, "CR-test-httponly=1", cookie_options, - CookieMonster::SetCookiesCallback()); - - MockSocketStreamDelegate delegate; - InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); - SkipToConnecting(); - - bool sent = websocket_->SendData(kHandshakeRequestWithCookie, - kHandshakeRequestWithCookieLength); - EXPECT_TRUE(sent); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - websocket_->OnSentData(socket_.get(), - kHandshakeRequestWithFilteredCookieLength); - EXPECT_EQ(kHandshakeRequestWithCookieLength, - delegate.amount_sent()); - - websocket_->OnReceivedData(socket_.get(), - kHandshakeResponseWithCookie, - kHandshakeResponseWithCookieLength); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); - EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); - - CloseWebSocketJob(); -} - -void WebSocketJobTest::TestHandshakeWithCookie() { - GURL url("ws://example.com/demo"); - GURL cookieUrl("http://example.com/demo"); - CookieOptions cookie_options; - cookie_store_->SetCookieWithOptions( - cookieUrl, "CR-test=1", cookie_options); - cookie_options.set_include_httponly(); - cookie_store_->SetCookieWithOptions( - cookieUrl, "CR-test-httponly=1", cookie_options); - - MockSocketStreamDelegate delegate; - InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); - SkipToConnecting(); - - bool sent = websocket_->SendData(kHandshakeRequestWithCookie, - kHandshakeRequestWithCookieLength); - EXPECT_TRUE(sent); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeRequestWithFilteredCookie, sent_data()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - websocket_->OnSentData(socket_.get(), - kHandshakeRequestWithFilteredCookieLength); - EXPECT_EQ(kHandshakeRequestWithCookieLength, - delegate.amount_sent()); - - websocket_->OnReceivedData(socket_.get(), - kHandshakeResponseWithCookie, - kHandshakeResponseWithCookieLength); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); - EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); - - EXPECT_EQ(3U, cookie_store_->entries().size()); - EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url); - EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line); - EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url); - EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line); - EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url); - EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line); - - CloseWebSocketJob(); -} - -void WebSocketJobTest::TestHandshakeWithCookieButNotAllowed() { - GURL url("ws://example.com/demo"); - GURL cookieUrl("http://example.com/demo"); - CookieOptions cookie_options; - cookie_store_->SetCookieWithOptions( - cookieUrl, "CR-test=1", cookie_options); - cookie_options.set_include_httponly(); - cookie_store_->SetCookieWithOptions( - cookieUrl, "CR-test-httponly=1", cookie_options); - - MockSocketStreamDelegate delegate; - delegate.set_allow_all_cookies(false); - InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); - SkipToConnecting(); - - bool sent = websocket_->SendData(kHandshakeRequestWithCookie, - kHandshakeRequestWithCookieLength); - EXPECT_TRUE(sent); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - websocket_->OnSentData(socket_.get(), kHandshakeRequestWithoutCookieLength); - EXPECT_EQ(kHandshakeRequestWithCookieLength, delegate.amount_sent()); - - websocket_->OnReceivedData(socket_.get(), - kHandshakeResponseWithCookie, - kHandshakeResponseWithCookieLength); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeResponseWithoutCookie, delegate.received_data()); - EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState()); - - EXPECT_EQ(2U, cookie_store_->entries().size()); - EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url); - EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line); - EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url); - EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line); - - CloseWebSocketJob(); -} - -void WebSocketJobTest::TestHSTSUpgrade() { - GURL url("ws://upgrademe.com/"); - MockSocketStreamDelegate delegate; - scoped_refptr<SocketStreamJob> job = - SocketStreamJob::CreateSocketStreamJob( - url, &delegate, context_->transport_security_state(), - context_->ssl_config_service(), NULL, NULL); - EXPECT_TRUE(GetSocket(job.get())->is_secure()); - job->DetachDelegate(); - - url = GURL("ws://donotupgrademe.com/"); - job = SocketStreamJob::CreateSocketStreamJob( - url, &delegate, context_->transport_security_state(), - context_->ssl_config_service(), NULL, NULL); - EXPECT_FALSE(GetSocket(job.get())->is_secure()); - job->DetachDelegate(); -} - -void WebSocketJobTest::TestInvalidSendData() { - GURL url("ws://example.com/demo"); - MockSocketStreamDelegate delegate; - InitWebSocketJob(url, &delegate, STREAM_MOCK_SOCKET); - SkipToConnecting(); - - DoSendRequest(); - // We assume request is sent in one data chunk (from WebKit) - // We don't support streaming request. - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(kHandshakeRequestWithoutCookie, sent_data()); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - websocket_->OnSentData(socket_.get(), - kHandshakeRequestWithoutCookieLength); - EXPECT_EQ(kHandshakeRequestWithoutCookieLength, delegate.amount_sent()); - - // We could not send any data until connection is established. - bool sent = websocket_->SendData(kHandshakeRequestWithoutCookie, - kHandshakeRequestWithoutCookieLength); - EXPECT_FALSE(sent); - EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState()); - CloseWebSocketJob(); -} - -// Following tests verify cooperation between WebSocketJob and SocketStream. -// Other former tests use MockSocketStream as SocketStream, so we could not -// check SocketStream behavior. -// OrderedSocketData provide socket level verifiation by checking out-going -// packets in comparison with the MockWrite array and emulating in-coming -// packets with MockRead array. - -void WebSocketJobTest::TestConnectByWebSocket( - ThrottlingOption throttling) { - // This is a test for verifying cooperation between WebSocketJob and - // SocketStream. If |throttling| was |THROTTLING_OFF|, it test basic - // situation. If |throttling| was |THROTTLING_ON|, throttling limits the - // latter connection. - MockWrite writes[] = { - MockWrite(ASYNC, - kHandshakeRequestWithoutCookie, - kHandshakeRequestWithoutCookieLength, - 1), - MockWrite(ASYNC, - kDataHello, - kDataHelloLength, - 3) - }; - MockRead reads[] = { - MockRead(ASYNC, - kHandshakeResponseWithoutCookie, - kHandshakeResponseWithoutCookieLength, - 2), - MockRead(ASYNC, - kDataWorld, - kDataWorldLength, - 4), - MockRead(SYNCHRONOUS, 0, 5) // EOF - }; - data_.reset(new OrderedSocketData( - reads, arraysize(reads), writes, arraysize(writes))); - - GURL url("ws://example.com/demo"); - MockSocketStreamDelegate delegate; - WebSocketJobTest* test = this; - if (throttling == THROTTLING_ON) - delegate.SetOnStartOpenConnection( - base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); - delegate.SetOnConnected( - base::Bind(&WebSocketJobTest::DoSendRequest, - base::Unretained(test))); - delegate.SetOnReceivedData( - base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test))); - delegate.SetOnClose( - base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); - InitWebSocketJob(url, &delegate, STREAM_SOCKET); - - scoped_refptr<WebSocketJob> block_websocket; - if (throttling == THROTTLING_ON) { - // Create former WebSocket object which obstructs the latter one. - block_websocket = new WebSocketJob(NULL); - block_websocket->addresses_ = AddressList(websocket_->address_list()); - ASSERT_TRUE( - WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get())); - } - - websocket_->Connect(); - - if (throttling == THROTTLING_ON) { - EXPECT_EQ(OK, WaitForResult()); - EXPECT_TRUE(websocket_->IsWaiting()); - - // Remove the former WebSocket object from throttling queue to unblock the - // latter. - block_websocket->state_ = WebSocketJob::CLOSED; - WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get()); - block_websocket = NULL; - } - - EXPECT_EQ(OK, WaitForResult()); - EXPECT_TRUE(data_->at_read_eof()); - EXPECT_TRUE(data_->at_write_eof()); - EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState()); -} - -void WebSocketJobTest::TestConnectBySpdy( - SpdyOption spdy, ThrottlingOption throttling) { - // This is a test for verifying cooperation between WebSocketJob and - // SocketStream in the situation we have SPDY session to the server. If - // |throttling| was |THROTTLING_ON|, throttling limits the latter connection. - // If you enabled spdy, you should specify |spdy| as |SPDY_ON|. Expected - // results depend on its configuration. - MockWrite writes_websocket[] = { - MockWrite(ASYNC, - kHandshakeRequestWithoutCookie, - kHandshakeRequestWithoutCookieLength, - 1), - MockWrite(ASYNC, - kDataHello, - kDataHelloLength, - 3) - }; - MockRead reads_websocket[] = { - MockRead(ASYNC, - kHandshakeResponseWithoutCookie, - kHandshakeResponseWithoutCookieLength, - 2), - MockRead(ASYNC, - kDataWorld, - kDataWorldLength, - 4), - MockRead(SYNCHRONOUS, 0, 5) // EOF - }; - - scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock()); - spdy_util_.SetHeader("path", "/demo", request_headers.get()); - spdy_util_.SetHeader("version", "WebSocket/13", request_headers.get()); - spdy_util_.SetHeader("scheme", "ws", request_headers.get()); - spdy_util_.SetHeader("host", "example.com", request_headers.get()); - spdy_util_.SetHeader("origin", "http://example.com", request_headers.get()); - spdy_util_.SetHeader("sec-websocket-protocol", "sample", - request_headers.get()); - - scoped_ptr<SpdyHeaderBlock> response_headers(new SpdyHeaderBlock()); - spdy_util_.SetHeader("status", "101 Switching Protocols", - response_headers.get()); - spdy_util_.SetHeader("sec-websocket-protocol", "sample", - response_headers.get()); - - const SpdyStreamId kStreamId = 1; - scoped_ptr<SpdyFrame> request_frame( - spdy_util_.ConstructSpdyWebSocketHandshakeRequestFrame( - request_headers.Pass(), - kStreamId, - MEDIUM)); - scoped_ptr<SpdyFrame> response_frame( - spdy_util_.ConstructSpdyWebSocketHandshakeResponseFrame( - response_headers.Pass(), - kStreamId, - MEDIUM)); - scoped_ptr<SpdyFrame> data_hello_frame( - spdy_util_.ConstructSpdyWebSocketDataFrame( - kDataHello, - kDataHelloLength, - kStreamId, - false)); - scoped_ptr<SpdyFrame> data_world_frame( - spdy_util_.ConstructSpdyWebSocketDataFrame( - kDataWorld, - kDataWorldLength, - kStreamId, - false)); - MockWrite writes_spdy[] = { - CreateMockWrite(*request_frame.get(), 1), - CreateMockWrite(*data_hello_frame.get(), 3), - }; - MockRead reads_spdy[] = { - CreateMockRead(*response_frame.get(), 2), - CreateMockRead(*data_world_frame.get(), 4), - MockRead(SYNCHRONOUS, 0, 5) // EOF - }; - - if (spdy == SPDY_ON) - data_.reset(new OrderedSocketData( - reads_spdy, arraysize(reads_spdy), - writes_spdy, arraysize(writes_spdy))); - else - data_.reset(new OrderedSocketData( - reads_websocket, arraysize(reads_websocket), - writes_websocket, arraysize(writes_websocket))); - - GURL url("ws://example.com/demo"); - MockSocketStreamDelegate delegate; - WebSocketJobTest* test = this; - if (throttling == THROTTLING_ON) - delegate.SetOnStartOpenConnection( - base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); - delegate.SetOnConnected( - base::Bind(&WebSocketJobTest::DoSendRequest, - base::Unretained(test))); - delegate.SetOnReceivedData( - base::Bind(&WebSocketJobTest::DoSendData, base::Unretained(test))); - delegate.SetOnClose( - base::Bind(&WebSocketJobTest::DoSync, base::Unretained(test))); - InitWebSocketJob(url, &delegate, STREAM_SPDY_WEBSOCKET); - - scoped_refptr<WebSocketJob> block_websocket; - if (throttling == THROTTLING_ON) { - // Create former WebSocket object which obstructs the latter one. - block_websocket = new WebSocketJob(NULL); - block_websocket->addresses_ = AddressList(websocket_->address_list()); - ASSERT_TRUE( - WebSocketThrottle::GetInstance()->PutInQueue(block_websocket.get())); - } - - websocket_->Connect(); - - if (throttling == THROTTLING_ON) { - EXPECT_EQ(OK, WaitForResult()); - EXPECT_TRUE(websocket_->IsWaiting()); - - // Remove the former WebSocket object from throttling queue to unblock the - // latter. - block_websocket->state_ = WebSocketJob::CLOSED; - WebSocketThrottle::GetInstance()->RemoveFromQueue(block_websocket.get()); - block_websocket = NULL; - } - - EXPECT_EQ(OK, WaitForResult()); - EXPECT_TRUE(data_->at_read_eof()); - EXPECT_TRUE(data_->at_write_eof()); - EXPECT_EQ(WebSocketJob::CLOSED, GetWebSocketJobState()); -} - -void WebSocketJobTest::TestThrottlingLimit() { - std::vector<scoped_refptr<WebSocketJob> > jobs; - const int kMaxWebSocketJobsThrottled = 1024; - IPAddressNumber ip; - ParseIPLiteralToNumber("127.0.0.1", &ip); - for (int i = 0; i < kMaxWebSocketJobsThrottled + 1; ++i) { - scoped_refptr<WebSocketJob> job = new WebSocketJob(NULL); - job->addresses_ = AddressList(AddressList::CreateFromIPAddress(ip, 80)); - if (i >= kMaxWebSocketJobsThrottled) - EXPECT_FALSE(WebSocketThrottle::GetInstance()->PutInQueue(job.get())); - else - EXPECT_TRUE(WebSocketThrottle::GetInstance()->PutInQueue(job.get())); - jobs.push_back(job); - } - - // Close the jobs in reverse order. Otherwise, We need to make them prepared - // for Wakeup call. - for (std::vector<scoped_refptr<WebSocketJob> >::reverse_iterator iter = - jobs.rbegin(); - iter != jobs.rend(); - ++iter) { - WebSocketJob* job = (*iter).get(); - job->state_ = WebSocketJob::CLOSED; - WebSocketThrottle::GetInstance()->RemoveFromQueue(job); - } -} - -// Execute tests in both spdy-disabled mode and spdy-enabled mode. -TEST_P(WebSocketJobTest, SimpleHandshake) { - TestSimpleHandshake(); -} - -TEST_P(WebSocketJobTest, SlowHandshake) { - TestSlowHandshake(); -} - -TEST_P(WebSocketJobTest, HandshakeWithCookie) { - TestHandshakeWithCookie(); -} - -TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowed) { - TestHandshakeWithCookieButNotAllowed(); -} - -TEST_P(WebSocketJobTest, HSTSUpgrade) { - TestHSTSUpgrade(); -} - -TEST_P(WebSocketJobTest, InvalidSendData) { - TestInvalidSendData(); -} - -TEST_P(WebSocketJobTest, SimpleHandshakeSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestSimpleHandshake(); -} - -TEST_P(WebSocketJobTest, SlowHandshakeSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestSlowHandshake(); -} - -TEST_P(WebSocketJobTest, HandshakeWithCookieSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestHandshakeWithCookie(); -} - -TEST_P(WebSocketJobTest, HandshakeWithCookieButNotAllowedSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestHandshakeWithCookieButNotAllowed(); -} - -TEST_P(WebSocketJobTest, HSTSUpgradeSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestHSTSUpgrade(); -} - -TEST_P(WebSocketJobTest, InvalidSendDataSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestInvalidSendData(); -} - -TEST_P(WebSocketJobTest, ConnectByWebSocket) { - enable_websocket_over_spdy_ = true; - TestConnectByWebSocket(THROTTLING_OFF); -} - -TEST_P(WebSocketJobTest, ConnectByWebSocketSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestConnectByWebSocket(THROTTLING_OFF); -} - -TEST_P(WebSocketJobTest, ConnectBySpdy) { - TestConnectBySpdy(SPDY_OFF, THROTTLING_OFF); -} - -TEST_P(WebSocketJobTest, ConnectBySpdySpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestConnectBySpdy(SPDY_ON, THROTTLING_OFF); -} - -TEST_P(WebSocketJobTest, ThrottlingWebSocket) { - TestConnectByWebSocket(THROTTLING_ON); -} - -TEST_P(WebSocketJobTest, ThrottlingMaxNumberOfThrottledJobLimit) { - TestThrottlingLimit(); -} - -TEST_P(WebSocketJobTest, ThrottlingWebSocketSpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestConnectByWebSocket(THROTTLING_ON); -} - -TEST_P(WebSocketJobTest, ThrottlingSpdy) { - TestConnectBySpdy(SPDY_OFF, THROTTLING_ON); -} - -TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) { - enable_websocket_over_spdy_ = true; - TestConnectBySpdy(SPDY_ON, THROTTLING_ON); -} - -TEST_F(WebSocketJobDeleteTest, OnClose) { - SetDeleteNext(); - job()->OnClose(socket_.get()); - // OnClose() sets WebSocketJob::_socket to NULL before we can detach it, so - // socket_->delegate is still set at this point. Clear it to avoid hitting - // DCHECK(!delegate_) in the SocketStream destructor. SocketStream::Finish() - // is the only caller of this method in real code, and it also sets delegate_ - // to NULL. - socket_->DetachDelegate(); - EXPECT_FALSE(job()); -} - -TEST_F(WebSocketJobDeleteTest, OnAuthRequired) { - SetDeleteNext(); - job()->OnAuthRequired(socket_.get(), NULL); - EXPECT_FALSE(job()); -} - -TEST_F(WebSocketJobDeleteTest, OnSSLCertificateError) { - SSLInfo ssl_info; - SetDeleteNext(); - job()->OnSSLCertificateError(socket_.get(), ssl_info, true); - EXPECT_FALSE(job()); -} - -TEST_F(WebSocketJobDeleteTest, OnError) { - SetDeleteNext(); - job()->OnError(socket_.get(), ERR_CONNECTION_RESET); - EXPECT_FALSE(job()); -} - -TEST_F(WebSocketJobDeleteTest, OnSentSpdyHeaders) { - job()->Connect(); - SetDeleteNext(); - job()->OnSentSpdyHeaders(); - EXPECT_FALSE(job()); -} - -TEST_F(WebSocketJobDeleteTest, OnSentHandshakeRequest) { - static const char kMinimalRequest[] = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" - "Origin: http://example.com\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n"; - const size_t kMinimalRequestSize = arraysize(kMinimalRequest) - 1; - job()->Connect(); - job()->SendData(kMinimalRequest, kMinimalRequestSize); - SetDeleteNext(); - job()->OnSentData(socket_.get(), kMinimalRequestSize); - EXPECT_FALSE(job()); -} - -TEST_F(WebSocketJobDeleteTest, NotifyHeadersComplete) { - static const char kMinimalResponse[] = - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" - "\r\n"; - job()->Connect(); - SetDeleteNext(); - job()->OnReceivedData( - socket_.get(), kMinimalResponse, arraysize(kMinimalResponse) - 1); - EXPECT_FALSE(job()); -} - -// TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. -// TODO(toyoshim,yutak): Add tests to verify closing handshake. -} // namespace net
diff --git a/net/websockets/websocket_net_log_params.cc b/net/websockets/websocket_net_log_params.cc deleted file mode 100644 index dd9bdde..0000000 --- a/net/websockets/websocket_net_log_params.cc +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright (c) 2012 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 "net/websockets/websocket_net_log_params.h" - -#include "base/strings/stringprintf.h" -#include "base/values.h" - -namespace net { - -base::Value* NetLogWebSocketHandshakeCallback( - const std::string* headers, - NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue(); - base::ListValue* header_list = new base::ListValue(); - - size_t last = 0; - size_t headers_size = headers->size(); - size_t pos = 0; - while (pos <= headers_size) { - if (pos == headers_size || - ((*headers)[pos] == '\r' && - pos + 1 < headers_size && (*headers)[pos + 1] == '\n')) { - std::string entry = headers->substr(last, pos - last); - pos += 2; - last = pos; - - header_list->Append(new base::StringValue(entry)); - - if (entry.empty()) { - // Dump WebSocket key3. - std::string key; - for (; pos < headers_size; ++pos) { - key += base::StringPrintf("\\x%02x", (*headers)[pos] & 0xff); - } - header_list->Append(new base::StringValue(key)); - break; - } - } else { - ++pos; - } - } - - dict->Set("headers", header_list); - return dict; -} - -} // namespace net
diff --git a/net/websockets/websocket_net_log_params.h b/net/websockets/websocket_net_log_params.h deleted file mode 100644 index 45dabb0..0000000 --- a/net/websockets/websocket_net_log_params.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright (c) 2012 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 NET_WEBSOCKETS_WEBSOCKET_NET_LOG_PARAMS_H_ -#define NET_WEBSOCKETS_WEBSOCKET_NET_LOG_PARAMS_H_ - -#include <string> - -#include "net/base/net_export.h" -#include "net/base/net_log.h" - -namespace net { - -NET_EXPORT_PRIVATE base::Value* NetLogWebSocketHandshakeCallback( - const std::string* headers, - NetLog::LogLevel /* log_level */); - -} // namespace net - -#endif // NET_WEBSOCKETS_WEBSOCKET_NET_LOG_PARAMS_H_
diff --git a/net/websockets/websocket_net_log_params_test.cc b/net/websockets/websocket_net_log_params_test.cc deleted file mode 100644 index d6d2a0d..0000000 --- a/net/websockets/websocket_net_log_params_test.cc +++ /dev/null
@@ -1,50 +0,0 @@ -// Copyright 2013 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 "net/websockets/websocket_net_log_params.h" - -#include <string> - -#include "base/callback.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(NetLogWebSocketHandshakeParameterTest, ToValue) { - base::ListValue* list = new base::ListValue(); - list->Append(new base::StringValue("GET /demo HTTP/1.1")); - list->Append(new base::StringValue("Host: example.com")); - list->Append(new base::StringValue("Connection: Upgrade")); - list->Append(new base::StringValue("Sec-WebSocket-Key2: 12998 5 Y3 1 .P00")); - list->Append(new base::StringValue("Sec-WebSocket-Protocol: sample")); - list->Append(new base::StringValue("Upgrade: WebSocket")); - list->Append(new base::StringValue( - "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5")); - list->Append(new base::StringValue("Origin: http://example.com")); - list->Append(new base::StringValue(std::string())); - list->Append(new base::StringValue( - "\\x00\\x01\\x0a\\x0d\\xff\\xfe\\x0d\\x0a")); - - base::DictionaryValue expected; - expected.Set("headers", list); - - const std::string key("\x00\x01\x0a\x0d\xff\xfe\x0d\x0a", 8); - const std::string testInput = - "GET /demo HTTP/1.1\r\n" - "Host: example.com\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" - "Sec-WebSocket-Protocol: sample\r\n" - "Upgrade: WebSocket\r\n" - "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" - "Origin: http://example.com\r\n" - "\r\n" + - key; - - scoped_ptr<base::Value> actual( - net::NetLogWebSocketHandshakeCallback(&testInput, - net::NetLog::LOG_ALL)); - - EXPECT_TRUE(expected.Equals(actual.get())); -}
diff --git a/net/websockets/websocket_throttle.cc b/net/websockets/websocket_throttle.cc deleted file mode 100644 index 59e73fd..0000000 --- a/net/websockets/websocket_throttle.cc +++ /dev/null
@@ -1,144 +0,0 @@ -// Copyright (c) 2012 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 "net/websockets/websocket_throttle.h" - -#include <algorithm> -#include <set> -#include <string> -#include <utility> - -#include "base/memory/singleton.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "net/base/io_buffer.h" -#include "net/socket_stream/socket_stream.h" -#include "net/websockets/websocket_job.h" - -namespace net { - -namespace { - -const size_t kMaxWebSocketJobsThrottled = 1024; - -} // namespace - -WebSocketThrottle::WebSocketThrottle() { -} - -WebSocketThrottle::~WebSocketThrottle() { - DCHECK(queue_.empty()); - DCHECK(addr_map_.empty()); -} - -// static -WebSocketThrottle* WebSocketThrottle::GetInstance() { - return Singleton<WebSocketThrottle>::get(); -} - -bool WebSocketThrottle::PutInQueue(WebSocketJob* job) { - if (queue_.size() >= kMaxWebSocketJobsThrottled) - return false; - - queue_.push_back(job); - const AddressList& address_list = job->address_list(); - std::set<IPEndPoint> address_set; - for (AddressList::const_iterator addr_iter = address_list.begin(); - addr_iter != address_list.end(); - ++addr_iter) { - const IPEndPoint& address = *addr_iter; - // If |address| is already processed, don't do it again. - if (!address_set.insert(address).second) - continue; - - ConnectingAddressMap::iterator iter = addr_map_.find(address); - if (iter == addr_map_.end()) { - ConnectingAddressMap::iterator new_queue = - addr_map_.insert(make_pair(address, ConnectingQueue())).first; - new_queue->second.push_back(job); - } else { - DCHECK(!iter->second.empty()); - iter->second.push_back(job); - job->SetWaiting(); - DVLOG(1) << "Waiting on " << address.ToString(); - } - } - - return true; -} - -void WebSocketThrottle::RemoveFromQueue(WebSocketJob* job) { - ConnectingQueue::iterator queue_iter = - std::find(queue_.begin(), queue_.end(), job); - if (queue_iter == queue_.end()) - return; - queue_.erase(queue_iter); - - std::set<WebSocketJob*> wakeup_candidates; - - const AddressList& resolved_address_list = job->address_list(); - std::set<IPEndPoint> address_set; - for (AddressList::const_iterator addr_iter = resolved_address_list.begin(); - addr_iter != resolved_address_list.end(); - ++addr_iter) { - const IPEndPoint& address = *addr_iter; - // If |address| is already processed, don't do it again. - if (!address_set.insert(address).second) - continue; - - ConnectingAddressMap::iterator map_iter = addr_map_.find(address); - DCHECK(map_iter != addr_map_.end()); - - ConnectingQueue& per_address_queue = map_iter->second; - DCHECK(!per_address_queue.empty()); - // Job may not be front of the queue if the socket is closed while waiting. - ConnectingQueue::iterator per_address_queue_iter = - std::find(per_address_queue.begin(), per_address_queue.end(), job); - bool was_front = false; - if (per_address_queue_iter != per_address_queue.end()) { - was_front = (per_address_queue_iter == per_address_queue.begin()); - per_address_queue.erase(per_address_queue_iter); - } - if (per_address_queue.empty()) { - addr_map_.erase(map_iter); - } else if (was_front) { - // The new front is a wake-up candidate. - wakeup_candidates.insert(per_address_queue.front()); - } - } - - WakeupSocketIfNecessary(wakeup_candidates); -} - -void WebSocketThrottle::WakeupSocketIfNecessary( - const std::set<WebSocketJob*>& wakeup_candidates) { - for (std::set<WebSocketJob*>::const_iterator iter = wakeup_candidates.begin(); - iter != wakeup_candidates.end(); - ++iter) { - WebSocketJob* job = *iter; - if (!job->IsWaiting()) - continue; - - bool should_wakeup = true; - const AddressList& resolved_address_list = job->address_list(); - for (AddressList::const_iterator addr_iter = resolved_address_list.begin(); - addr_iter != resolved_address_list.end(); - ++addr_iter) { - const IPEndPoint& address = *addr_iter; - ConnectingAddressMap::iterator map_iter = addr_map_.find(address); - DCHECK(map_iter != addr_map_.end()); - const ConnectingQueue& per_address_queue = map_iter->second; - if (job != per_address_queue.front()) { - should_wakeup = false; - break; - } - } - if (should_wakeup) - job->Wakeup(); - } -} - -} // namespace net
diff --git a/net/websockets/websocket_throttle.h b/net/websockets/websocket_throttle.h deleted file mode 100644 index 0b7a39f..0000000 --- a/net/websockets/websocket_throttle.h +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright (c) 2011 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 NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_ -#define NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_ - -#include <deque> -#include <map> -#include <set> -#include <string> - -#include "net/base/ip_endpoint.h" -#include "net/base/net_export.h" - -template <typename T> struct DefaultSingletonTraits; - -namespace net { - -class SocketStream; -class WebSocketJob; - -// SocketStreamThrottle for WebSocket protocol. -// Implements the client-side requirements in the spec. -// http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol -// 4.1 Handshake -// 1. If the user agent already has a Web Socket connection to the -// remote host (IP address) identified by /host/, even if known by -// another name, wait until that connection has been established or -// for that connection to have failed. -class NET_EXPORT_PRIVATE WebSocketThrottle { - public: - // Returns the singleton instance. - static WebSocketThrottle* GetInstance(); - - // Puts |job| in |queue_| and queues for the destination addresses - // of |job|. - // If other job is using the same destination address, set |job| waiting. - // - // Returns true if successful. If the number of pending jobs will exceed - // the limit, does nothing and returns false. - bool PutInQueue(WebSocketJob* job); - - // Removes |job| from |queue_| and queues for the destination addresses - // of |job|, and then wakes up jobs that can now resume establishing a - // connection. - void RemoveFromQueue(WebSocketJob* job); - - private: - typedef std::deque<WebSocketJob*> ConnectingQueue; - typedef std::map<IPEndPoint, ConnectingQueue> ConnectingAddressMap; - - WebSocketThrottle(); - ~WebSocketThrottle(); - friend struct DefaultSingletonTraits<WebSocketThrottle>; - - // Examines if any of the given jobs can resume establishing a connection. If - // for all per-address queues for each resolved addresses - // (job->address_list()) of a job, the job is at the front of the queues, the - // job can resume establishing a connection, so wakes up the job. - void WakeupSocketIfNecessary( - const std::set<WebSocketJob*>& wakeup_candidates); - - // Key: string of host's address. Value: queue of sockets for the address. - ConnectingAddressMap addr_map_; - - // Queue of sockets for websockets in opening state. - ConnectingQueue queue_; - - DISALLOW_COPY_AND_ASSIGN(WebSocketThrottle); -}; - -} // namespace net - -#endif // NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_
diff --git a/net/websockets/websocket_throttle_test.cc b/net/websockets/websocket_throttle_test.cc deleted file mode 100644 index 4e27c83..0000000 --- a/net/websockets/websocket_throttle_test.cc +++ /dev/null
@@ -1,359 +0,0 @@ -// Copyright 2013 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 "net/websockets/websocket_throttle.h" - -#include <string> - -#include "base/message_loop/message_loop.h" -#include "net/base/address_list.h" -#include "net/base/test_completion_callback.h" -#include "net/socket_stream/socket_stream.h" -#include "net/url_request/url_request_test_util.h" -#include "net/websockets/websocket_job.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#include "url/gurl.h" - -namespace net { - -namespace { - -class DummySocketStreamDelegate : public SocketStream::Delegate { - public: - DummySocketStreamDelegate() {} - ~DummySocketStreamDelegate() override {} - void OnConnected(SocketStream* socket, - int max_pending_send_allowed) override {} - void OnSentData(SocketStream* socket, int amount_sent) override {} - void OnReceivedData(SocketStream* socket, - const char* data, - int len) override {} - void OnClose(SocketStream* socket) override {} -}; - -class WebSocketThrottleTestContext : public TestURLRequestContext { - public: - explicit WebSocketThrottleTestContext(bool enable_websocket_over_spdy) - : TestURLRequestContext(true) { - HttpNetworkSession::Params params; - params.enable_websocket_over_spdy = enable_websocket_over_spdy; - Init(); - } -}; - -} // namespace - -class WebSocketThrottleTest : public PlatformTest { - protected: - static IPEndPoint MakeAddr(int a1, int a2, int a3, int a4) { - IPAddressNumber ip; - ip.push_back(a1); - ip.push_back(a2); - ip.push_back(a3); - ip.push_back(a4); - return IPEndPoint(ip, 0); - } - - static void MockSocketStreamConnect( - SocketStream* socket, const AddressList& list) { - socket->set_addresses(list); - // TODO(toyoshim): We should introduce additional tests on cases via proxy. - socket->proxy_info_.UseDirect(); - // In SocketStream::Connect(), it adds reference to socket, which is - // balanced with SocketStream::Finish() that is finally called from - // SocketStream::Close() or SocketStream::DetachDelegate(), when - // next_state_ is not STATE_NONE. - // If next_state_ is STATE_NONE, SocketStream::Close() or - // SocketStream::DetachDelegate() won't call SocketStream::Finish(), - // so Release() won't be called. Thus, we don't need socket->AddRef() - // here. - DCHECK_EQ(socket->next_state_, SocketStream::STATE_NONE); - } -}; - -TEST_F(WebSocketThrottleTest, Throttle) { - // TODO(toyoshim): We need to consider both spdy-enabled and spdy-disabled - // configuration. - WebSocketThrottleTestContext context(true); - DummySocketStreamDelegate delegate; - - // For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6 - AddressList addr; - addr.push_back(MakeAddr(1, 2, 3, 4)); - addr.push_back(MakeAddr(1, 2, 3, 5)); - addr.push_back(MakeAddr(1, 2, 3, 6)); - scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s1( - new SocketStream(GURL("ws://host1/"), w1.get(), &context, NULL)); - w1->InitSocketStream(s1.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr); - - DVLOG(1) << "socket1"; - TestCompletionCallback callback_s1; - // Trying to open connection to host1 will start without wait. - EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback())); - - // Now connecting to host1, so waiting queue looks like - // Address | head -> tail - // 1.2.3.4 | w1 - // 1.2.3.5 | w1 - // 1.2.3.6 | w1 - - // For host2: 1.2.3.4 - addr.clear(); - addr.push_back(MakeAddr(1, 2, 3, 4)); - scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s2( - new SocketStream(GURL("ws://host2/"), w2.get(), &context, NULL)); - w2->InitSocketStream(s2.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s2.get(), addr); - - DVLOG(1) << "socket2"; - TestCompletionCallback callback_s2; - // Trying to open connection to host2 will wait for w1. - EXPECT_EQ(ERR_IO_PENDING, - w2->OnStartOpenConnection(s2.get(), callback_s2.callback())); - // Now waiting queue looks like - // Address | head -> tail - // 1.2.3.4 | w1 w2 - // 1.2.3.5 | w1 - // 1.2.3.6 | w1 - - // For host3: 1.2.3.5 - addr.clear(); - addr.push_back(MakeAddr(1, 2, 3, 5)); - scoped_refptr<WebSocketJob> w3(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s3( - new SocketStream(GURL("ws://host3/"), w3.get(), &context, NULL)); - w3->InitSocketStream(s3.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s3.get(), addr); - - DVLOG(1) << "socket3"; - TestCompletionCallback callback_s3; - // Trying to open connection to host3 will wait for w1. - EXPECT_EQ(ERR_IO_PENDING, - w3->OnStartOpenConnection(s3.get(), callback_s3.callback())); - // Address | head -> tail - // 1.2.3.4 | w1 w2 - // 1.2.3.5 | w1 w3 - // 1.2.3.6 | w1 - - // For host4: 1.2.3.4, 1.2.3.6 - addr.clear(); - addr.push_back(MakeAddr(1, 2, 3, 4)); - addr.push_back(MakeAddr(1, 2, 3, 6)); - scoped_refptr<WebSocketJob> w4(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s4( - new SocketStream(GURL("ws://host4/"), w4.get(), &context, NULL)); - w4->InitSocketStream(s4.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s4.get(), addr); - - DVLOG(1) << "socket4"; - TestCompletionCallback callback_s4; - // Trying to open connection to host4 will wait for w1, w2. - EXPECT_EQ(ERR_IO_PENDING, - w4->OnStartOpenConnection(s4.get(), callback_s4.callback())); - // Address | head -> tail - // 1.2.3.4 | w1 w2 w4 - // 1.2.3.5 | w1 w3 - // 1.2.3.6 | w1 w4 - - // For host5: 1.2.3.6 - addr.clear(); - addr.push_back(MakeAddr(1, 2, 3, 6)); - scoped_refptr<WebSocketJob> w5(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s5( - new SocketStream(GURL("ws://host5/"), w5.get(), &context, NULL)); - w5->InitSocketStream(s5.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s5.get(), addr); - - DVLOG(1) << "socket5"; - TestCompletionCallback callback_s5; - // Trying to open connection to host5 will wait for w1, w4 - EXPECT_EQ(ERR_IO_PENDING, - w5->OnStartOpenConnection(s5.get(), callback_s5.callback())); - // Address | head -> tail - // 1.2.3.4 | w1 w2 w4 - // 1.2.3.5 | w1 w3 - // 1.2.3.6 | w1 w4 w5 - - // For host6: 1.2.3.6 - addr.clear(); - addr.push_back(MakeAddr(1, 2, 3, 6)); - scoped_refptr<WebSocketJob> w6(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s6( - new SocketStream(GURL("ws://host6/"), w6.get(), &context, NULL)); - w6->InitSocketStream(s6.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s6.get(), addr); - - DVLOG(1) << "socket6"; - TestCompletionCallback callback_s6; - // Trying to open connection to host6 will wait for w1, w4, w5 - EXPECT_EQ(ERR_IO_PENDING, - w6->OnStartOpenConnection(s6.get(), callback_s6.callback())); - // Address | head -> tail - // 1.2.3.4 | w1 w2 w4 - // 1.2.3.5 | w1 w3 - // 1.2.3.6 | w1 w4 w5 w6 - - // Receive partial response on w1, still connecting. - DVLOG(1) << "socket1 1"; - static const char kHeader[] = "HTTP/1.1 101 WebSocket Protocol\r\n"; - w1->OnReceivedData(s1.get(), kHeader, sizeof(kHeader) - 1); - EXPECT_FALSE(callback_s2.have_result()); - EXPECT_FALSE(callback_s3.have_result()); - EXPECT_FALSE(callback_s4.have_result()); - EXPECT_FALSE(callback_s5.have_result()); - EXPECT_FALSE(callback_s6.have_result()); - - // Receive rest of handshake response on w1. - DVLOG(1) << "socket1 2"; - static const char kHeader2[] = - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Origin: http://www.google.com\r\n" - "Sec-WebSocket-Location: ws://websocket.chromium.org\r\n" - "\r\n" - "8jKS'y:G*Co,Wxa-"; - w1->OnReceivedData(s1.get(), kHeader2, sizeof(kHeader2) - 1); - base::MessageLoopForIO::current()->RunUntilIdle(); - // Now, w1 is open. - EXPECT_EQ(WebSocketJob::OPEN, w1->state()); - // So, w2 and w3 can start connecting. w4 needs to wait w2 (1.2.3.4) - EXPECT_TRUE(callback_s2.have_result()); - EXPECT_TRUE(callback_s3.have_result()); - EXPECT_FALSE(callback_s4.have_result()); - // Address | head -> tail - // 1.2.3.4 | w2 w4 - // 1.2.3.5 | w3 - // 1.2.3.6 | w4 w5 w6 - - // Closing s1 doesn't change waiting queue. - DVLOG(1) << "socket1 close"; - w1->OnClose(s1.get()); - base::MessageLoopForIO::current()->RunUntilIdle(); - EXPECT_FALSE(callback_s4.have_result()); - s1->DetachDelegate(); - // Address | head -> tail - // 1.2.3.4 | w2 w4 - // 1.2.3.5 | w3 - // 1.2.3.6 | w4 w5 w6 - - // w5 can close while waiting in queue. - DVLOG(1) << "socket5 close"; - // w5 close() closes SocketStream that change state to STATE_CLOSE, calls - // DoLoop(), so OnClose() callback will be called. - w5->OnClose(s5.get()); - base::MessageLoopForIO::current()->RunUntilIdle(); - EXPECT_FALSE(callback_s4.have_result()); - // Address | head -> tail - // 1.2.3.4 | w2 w4 - // 1.2.3.5 | w3 - // 1.2.3.6 | w4 w6 - s5->DetachDelegate(); - - // w6 close abnormally (e.g. renderer finishes) while waiting in queue. - DVLOG(1) << "socket6 close abnormally"; - w6->DetachDelegate(); - base::MessageLoopForIO::current()->RunUntilIdle(); - EXPECT_FALSE(callback_s4.have_result()); - // Address | head -> tail - // 1.2.3.4 | w2 w4 - // 1.2.3.5 | w3 - // 1.2.3.6 | w4 - - // Closing s2 kicks w4 to start connecting. - DVLOG(1) << "socket2 close"; - w2->OnClose(s2.get()); - base::MessageLoopForIO::current()->RunUntilIdle(); - EXPECT_TRUE(callback_s4.have_result()); - // Address | head -> tail - // 1.2.3.4 | w4 - // 1.2.3.5 | w3 - // 1.2.3.6 | w4 - s2->DetachDelegate(); - - DVLOG(1) << "socket3 close"; - w3->OnClose(s3.get()); - base::MessageLoopForIO::current()->RunUntilIdle(); - s3->DetachDelegate(); - w4->OnClose(s4.get()); - s4->DetachDelegate(); - DVLOG(1) << "Done"; - base::MessageLoopForIO::current()->RunUntilIdle(); -} - -TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) { - WebSocketThrottleTestContext context(true); - DummySocketStreamDelegate delegate; - - // For localhost: 127.0.0.1, 127.0.0.1 - AddressList addr; - addr.push_back(MakeAddr(127, 0, 0, 1)); - addr.push_back(MakeAddr(127, 0, 0, 1)); - scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s1( - new SocketStream(GURL("ws://localhost/"), w1.get(), &context, NULL)); - w1->InitSocketStream(s1.get()); - WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr); - - DVLOG(1) << "socket1"; - TestCompletionCallback callback_s1; - // Trying to open connection to localhost will start without wait. - EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback())); - - DVLOG(1) << "socket1 close"; - w1->OnClose(s1.get()); - s1->DetachDelegate(); - DVLOG(1) << "Done"; - base::MessageLoopForIO::current()->RunUntilIdle(); -} - -// A connection should not be blocked by another connection to the same IP -// with a different port. -TEST_F(WebSocketThrottleTest, NoThrottleForDistinctPort) { - WebSocketThrottleTestContext context(false); - DummySocketStreamDelegate delegate; - IPAddressNumber localhost; - ParseIPLiteralToNumber("127.0.0.1", &localhost); - - // socket1: 127.0.0.1:80 - scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s1( - new SocketStream(GURL("ws://localhost:80/"), w1.get(), &context, NULL)); - w1->InitSocketStream(s1.get()); - MockSocketStreamConnect(s1.get(), - AddressList::CreateFromIPAddress(localhost, 80)); - - DVLOG(1) << "connecting socket1"; - TestCompletionCallback callback_s1; - // Trying to open connection to localhost:80 will start without waiting. - EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback())); - - // socket2: 127.0.0.1:81 - scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate)); - scoped_refptr<SocketStream> s2( - new SocketStream(GURL("ws://localhost:81/"), w2.get(), &context, NULL)); - w2->InitSocketStream(s2.get()); - MockSocketStreamConnect(s2.get(), - AddressList::CreateFromIPAddress(localhost, 81)); - - DVLOG(1) << "connecting socket2"; - TestCompletionCallback callback_s2; - // Trying to open connection to localhost:81 will start without waiting. - EXPECT_EQ(OK, w2->OnStartOpenConnection(s2.get(), callback_s2.callback())); - - DVLOG(1) << "closing socket1"; - w1->OnClose(s1.get()); - s1->DetachDelegate(); - - DVLOG(1) << "closing socket2"; - w2->OnClose(s2.get()); - s2->DetachDelegate(); - DVLOG(1) << "Done"; - base::MessageLoopForIO::current()->RunUntilIdle(); -} - -} // namespace net
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn index f73d8df..23baf4e 100644 --- a/sandbox/linux/BUILD.gn +++ b/sandbox/linux/BUILD.gn
@@ -229,6 +229,8 @@ "services/thread_helpers.h", "services/yama.h", "services/yama.cc", + "syscall_broker/broker_channel.cc", + "syscall_broker/broker_channel.h", "syscall_broker/broker_client.cc", "syscall_broker/broker_client.h", "syscall_broker/broker_common.h",
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc index c56c819..b3d9126 100644 --- a/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc +++ b/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc
@@ -759,26 +759,29 @@ allowed_files.push_back("/proc/allowed"); allowed_files.push_back("/proc/cpuinfo"); - broker_process_.reset( - new BrokerProcess(EPERM, allowed_files, std::vector<std::string>())); + broker_process_.reset(new syscall_broker::BrokerProcess( + EPERM, allowed_files, std::vector<std::string>())); BPF_ASSERT(broker_process() != NULL); BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback))); initialized_ = true; } bool initialized() { return initialized_; } - class BrokerProcess* broker_process() { return broker_process_.get(); } + class syscall_broker::BrokerProcess* broker_process() { + return broker_process_.get(); + } private: bool initialized_; - scoped_ptr<class BrokerProcess> broker_process_; + scoped_ptr<class syscall_broker::BrokerProcess> broker_process_; DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker); }; intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args, void* aux) { BPF_ASSERT(aux); - BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux); + syscall_broker::BrokerProcess* broker_process = + static_cast<syscall_broker::BrokerProcess*>(aux); switch (args.nr) { case __NR_faccessat: // access is a wrapper of faccessat in android BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD); @@ -824,7 +827,7 @@ #endif case __NR_openat: // We get a InitializedOpenBroker class, but our trap handler wants - // the BrokerProcess object. + // the syscall_broker::BrokerProcess object. return Trap(BrokerOpenTrapHandler, iob_->broker_process()); default: return Allow(); @@ -844,7 +847,7 @@ DenyOpenPolicy, InitializedOpenBroker /* (*BPF_AUX) */) { BPF_ASSERT(BPF_AUX->initialized()); - BrokerProcess* broker_process = BPF_AUX->broker_process(); + syscall_broker::BrokerProcess* broker_process = BPF_AUX->broker_process(); BPF_ASSERT(broker_process != NULL); // First, use the broker "manually"
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi index ba530c0..aa6341d 100644 --- a/sandbox/linux/sandbox_linux.gypi +++ b/sandbox/linux/sandbox_linux.gypi
@@ -228,6 +228,8 @@ 'services/thread_helpers.h', 'services/yama.cc', 'services/yama.h', + 'syscall_broker/broker_channel.cc', + 'syscall_broker/broker_channel.h', 'syscall_broker/broker_client.cc', 'syscall_broker/broker_client.h', 'syscall_broker/broker_common.h',
diff --git a/sandbox/linux/syscall_broker/broker_channel.cc b/sandbox/linux/syscall_broker/broker_channel.cc new file mode 100644 index 0000000..fa0f761 --- /dev/null +++ b/sandbox/linux/syscall_broker/broker_channel.cc
@@ -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. + +#include "sandbox/linux/syscall_broker/broker_channel.h" + +#include <sys/socket.h> +#include <sys/types.h> + +#include "base/logging.h" + +namespace sandbox { + +namespace syscall_broker { + +// static +void BrokerChannel::CreatePair(EndPoint* reader, EndPoint* writer) { + DCHECK(reader); + DCHECK(writer); + int socket_pair[2]; + // Use SOCK_SEQPACKET, to preserve message boundaries but we also want to be + // notified (recvmsg should return and not block) when the connection has + // been broken which could mean that the other end has been closed. + PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair)); + + reader->reset(socket_pair[0]); + PCHECK(0 == shutdown(reader->get(), SHUT_WR)); + + writer->reset(socket_pair[1]); + PCHECK(0 == shutdown(writer->get(), SHUT_RD)); +} + +} // namespace syscall_broker + +} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_channel.h b/sandbox/linux/syscall_broker/broker_channel.h new file mode 100644 index 0000000..2abdba4 --- /dev/null +++ b/sandbox/linux/syscall_broker/broker_channel.h
@@ -0,0 +1,31 @@ +// 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 SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_ +#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_ + +#include "base/files/scoped_file.h" +#include "base/macros.h" + +namespace sandbox { + +namespace syscall_broker { + +// A small class to create a pipe-like communication channel. It is based on a +// SOCK_SEQPACKET unix socket, which is connection-based and guaranteed to +// preserve message boundaries. +class BrokerChannel { + public: + typedef base::ScopedFD EndPoint; + static void CreatePair(EndPoint* reader, EndPoint* writer); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerChannel); +}; + +} // namespace syscall_broker + +} // namespace sandbox + +#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc index 60dc9ba..e63205f 100644 --- a/sandbox/linux/syscall_broker/broker_client.cc +++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -14,6 +14,7 @@ #include "base/logging.h" #include "base/pickle.h" #include "base/posix/unix_domain_socket_linux.h" +#include "sandbox/linux/syscall_broker/broker_channel.h" #include "sandbox/linux/syscall_broker/broker_common.h" #include "sandbox/linux/syscall_broker/broker_policy.h" @@ -75,12 +76,9 @@ // temporary socketpair (created internally by SendRecvMsg()). // Then read the reply on this new socketpair in reply_buf and put an // eventual attached file descriptor in |returned_fd|. - ssize_t msg_len = UnixDomainSocket::SendRecvMsgWithFlags(ipc_channel_, - reply_buf, - sizeof(reply_buf), - recvmsg_flags, - &returned_fd, - write_pickle); + ssize_t msg_len = UnixDomainSocket::SendRecvMsgWithFlags( + ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags, + &returned_fd, write_pickle); if (msg_len <= 0) { if (!quiet_failures_for_tests_) RAW_LOG(ERROR, "Could not make request to broker process"); @@ -119,11 +117,11 @@ } BrokerClient::BrokerClient(const BrokerPolicy& broker_policy, - int ipc_channel, + BrokerChannel::EndPoint ipc_channel, bool fast_check_in_client, bool quiet_failures_for_tests) : broker_policy_(broker_policy), - ipc_channel_(ipc_channel), + ipc_channel_(ipc_channel.Pass()), fast_check_in_client_(fast_check_in_client), quiet_failures_for_tests_(quiet_failures_for_tests) { }
diff --git a/sandbox/linux/syscall_broker/broker_client.h b/sandbox/linux/syscall_broker/broker_client.h index 7d06175..2dfef81 100644 --- a/sandbox/linux/syscall_broker/broker_client.h +++ b/sandbox/linux/syscall_broker/broker_client.h
@@ -6,6 +6,7 @@ #define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_ #include "base/macros.h" +#include "sandbox/linux/syscall_broker/broker_channel.h" #include "sandbox/linux/syscall_broker/broker_common.h" namespace sandbox { @@ -30,7 +31,7 @@ // |fast_check_in_client| should be set to true and // |quiet_failures_for_tests| to false unless you are writing tests. BrokerClient(const BrokerPolicy& policy, - int ipc_channel, + BrokerChannel::EndPoint ipc_channel, bool fast_check_in_client, bool quiet_failures_for_tests); ~BrokerClient(); @@ -48,9 +49,12 @@ // This is async signal safe. int Open(const char* pathname, int flags) const; + // Get the file descriptor used for IPC. This is used for tests. + int GetIPCDescriptor() const { return ipc_channel_.get(); } + private: const BrokerPolicy& broker_policy_; - const int ipc_channel_; + const BrokerChannel::EndPoint ipc_channel_; const bool fast_check_in_client_; // Whether to forward a request that we // know will be denied to the broker. (Used // for tests).
diff --git a/sandbox/linux/syscall_broker/broker_host.cc b/sandbox/linux/syscall_broker/broker_host.cc index 995641d..29300f7 100644 --- a/sandbox/linux/syscall_broker/broker_host.cc +++ b/sandbox/linux/syscall_broker/broker_host.cc
@@ -154,8 +154,9 @@ } // namespace -BrokerHost::BrokerHost(const BrokerPolicy& broker_policy, int ipc_channel) - : broker_policy_(broker_policy), ipc_channel_(ipc_channel) { +BrokerHost::BrokerHost(const BrokerPolicy& broker_policy, + BrokerChannel::EndPoint ipc_channel) + : broker_policy_(broker_policy), ipc_channel_(ipc_channel.Pass()) { } BrokerHost::~BrokerHost() { @@ -165,17 +166,16 @@ // A request should have a file descriptor attached on which we will reply and // that we will then close. // A request should start with an int that will be used as the command type. -bool BrokerHost::HandleRequest() const { +BrokerHost::RequestStatus BrokerHost::HandleRequest() const { ScopedVector<base::ScopedFD> fds; char buf[kMaxMessageLength]; errno = 0; const ssize_t msg_len = - UnixDomainSocket::RecvMsg(ipc_channel_, buf, sizeof(buf), &fds); + UnixDomainSocket::RecvMsg(ipc_channel_.get(), buf, sizeof(buf), &fds); if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) { // EOF from the client, or the client died, we should die. - // TODO(jln): change this. - _exit(0); + return RequestStatus::LOST_CLIENT; } // The client should send exactly one file descriptor, on which we @@ -183,7 +183,7 @@ // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'. if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) { PLOG(ERROR) << "Error reading message from the client"; - return false; + return RequestStatus::FAILURE; } base::ScopedFD temporary_ipc(fds[0]->Pass()); @@ -192,28 +192,32 @@ PickleIterator iter(pickle); int command_type; if (pickle.ReadInt(&iter, &command_type)) { - bool r = false; + bool command_handled = false; // Go through all the possible IPC messages. switch (command_type) { case COMMAND_ACCESS: case COMMAND_OPEN: // We reply on the file descriptor sent to us via the IPC channel. - r = HandleRemoteCommand(broker_policy_, - static_cast<IPCCommand>(command_type), - temporary_ipc.get(), - pickle, - iter); + command_handled = HandleRemoteCommand( + broker_policy_, static_cast<IPCCommand>(command_type), + temporary_ipc.get(), pickle, iter); break; default: NOTREACHED(); - r = false; break; } - return r; + + if (command_handled) { + return RequestStatus::SUCCESS; + } else { + return RequestStatus::FAILURE; + } + + NOTREACHED(); } LOG(ERROR) << "Error parsing IPC request"; - return false; + return RequestStatus::FAILURE; } } // namespace syscall_broker
diff --git a/sandbox/linux/syscall_broker/broker_host.h b/sandbox/linux/syscall_broker/broker_host.h index 04c20ed..9866507 100644 --- a/sandbox/linux/syscall_broker/broker_host.h +++ b/sandbox/linux/syscall_broker/broker_host.h
@@ -6,6 +6,7 @@ #define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_ #include "base/macros.h" +#include "sandbox/linux/syscall_broker/broker_channel.h" namespace sandbox { @@ -18,14 +19,18 @@ // |ipc_channel| according to |broker_policy|. class BrokerHost { public: - BrokerHost(const BrokerPolicy& broker_policy, int ipc_channel); + enum class RequestStatus { LOST_CLIENT = 0, SUCCESS, FAILURE }; + + BrokerHost(const BrokerPolicy& broker_policy, + BrokerChannel::EndPoint ipc_channel); ~BrokerHost(); - bool HandleRequest() const; + RequestStatus HandleRequest() const; private: const BrokerPolicy& broker_policy_; - const int ipc_channel_; + const BrokerChannel::EndPoint ipc_channel_; + DISALLOW_COPY_AND_ASSIGN(BrokerHost); };
diff --git a/sandbox/linux/syscall_broker/broker_process.cc b/sandbox/linux/syscall_broker/broker_process.cc index 66b7660..ebd7d05 100644 --- a/sandbox/linux/syscall_broker/broker_process.cc +++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -6,7 +6,6 @@ #include <fcntl.h> #include <signal.h> -#include <sys/socket.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> @@ -23,30 +22,33 @@ #include "base/posix/eintr_wrapper.h" #include "base/process/process_metrics.h" #include "build/build_config.h" +#include "sandbox/linux/syscall_broker/broker_channel.h" #include "sandbox/linux/syscall_broker/broker_client.h" #include "sandbox/linux/syscall_broker/broker_host.h" namespace sandbox { +namespace syscall_broker { + BrokerProcess::BrokerProcess(int denied_errno, const std::vector<std::string>& allowed_r_files, const std::vector<std::string>& allowed_w_files, bool fast_check_in_client, bool quiet_failures_for_tests) : initialized_(false), - is_child_(false), fast_check_in_client_(fast_check_in_client), quiet_failures_for_tests_(quiet_failures_for_tests), broker_pid_(-1), - policy_(denied_errno, allowed_r_files, allowed_w_files), - ipc_socketpair_(-1) { + policy_(denied_errno, allowed_r_files, allowed_w_files) { } BrokerProcess::~BrokerProcess() { - if (initialized_ && ipc_socketpair_ != -1) { - // Closing the socket should be enough to notify the child to die, - // unless it has been duplicated. - PCHECK(0 == IGNORE_EINTR(close(ipc_socketpair_))); + if (initialized_) { + if (broker_client_.get()) { + // Closing the socket should be enough to notify the child to die, + // unless it has been duplicated. + CloseChannel(); + } PCHECK(0 == kill(broker_pid_, SIGKILL)); siginfo_t process_info; // Reap the child. @@ -58,59 +60,50 @@ bool BrokerProcess::Init( const base::Callback<bool(void)>& broker_process_init_callback) { CHECK(!initialized_); - int socket_pair[2]; - // Use SOCK_SEQPACKET, because we need to preserve message boundaries - // but we also want to be notified (recvmsg should return and not block) - // when the connection has been broken (one of the processes died). - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair)) { - LOG(ERROR) << "Failed to create socketpair"; - return false; - } + BrokerChannel::EndPoint ipc_reader; + BrokerChannel::EndPoint ipc_writer; + BrokerChannel::CreatePair(&ipc_reader, &ipc_writer); #if !defined(THREAD_SANITIZER) DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle())); #endif int child_pid = fork(); if (child_pid == -1) { - close(socket_pair[0]); - close(socket_pair[1]); return false; } if (child_pid) { // We are the parent and we have just forked our broker process. - close(socket_pair[0]); - // We should only be able to write to the IPC channel. We'll always send - // a new file descriptor to receive the reply on. - shutdown(socket_pair[1], SHUT_RD); - ipc_socketpair_ = socket_pair[1]; - is_child_ = false; + ipc_reader.reset(); broker_pid_ = child_pid; - broker_client_.reset( - new syscall_broker::BrokerClient(policy_, - ipc_socketpair_, - fast_check_in_client_, - quiet_failures_for_tests_)); + broker_client_.reset(new BrokerClient(policy_, ipc_writer.Pass(), + fast_check_in_client_, + quiet_failures_for_tests_)); initialized_ = true; return true; } else { - // We are the broker. - close(socket_pair[1]); - // We should only be able to read from this IPC channel. We will send our - // replies on a new file descriptor attached to the requests. - shutdown(socket_pair[0], SHUT_WR); - ipc_socketpair_ = socket_pair[0]; - is_child_ = true; + // We are the broker process. Make sure to close the writer's end so that + // we get notified if the client disappears. + ipc_writer.reset(); CHECK(broker_process_init_callback.Run()); - syscall_broker::BrokerHost broker_host(policy_, ipc_socketpair_); - initialized_ = true; + BrokerHost broker_host(policy_, ipc_reader.Pass()); for (;;) { - broker_host.HandleRequest(); + switch (broker_host.HandleRequest()) { + case BrokerHost::RequestStatus::LOST_CLIENT: + _exit(1); + case BrokerHost::RequestStatus::SUCCESS: + case BrokerHost::RequestStatus::FAILURE: + continue; + } } _exit(1); } NOTREACHED(); } +void BrokerProcess::CloseChannel() { + broker_client_.reset(); +} + int BrokerProcess::Access(const char* pathname, int mode) const { RAW_CHECK(initialized_); return broker_client_->Access(pathname, mode); @@ -121,4 +114,6 @@ return broker_client_->Open(pathname, flags); } +} // namespace syscall_broker + } // namespace sandbox.
diff --git a/sandbox/linux/syscall_broker/broker_process.h b/sandbox/linux/syscall_broker/broker_process.h index d5b521b..50e7eee 100644 --- a/sandbox/linux/syscall_broker/broker_process.h +++ b/sandbox/linux/syscall_broker/broker_process.h
@@ -19,8 +19,8 @@ namespace sandbox { namespace syscall_broker { + class BrokerClient; -} // Create a new "broker" process to which we can send requests via an IPC // channel by forking the current process. @@ -68,21 +68,24 @@ int broker_pid() const { return broker_pid_; } private: + friend class BrokerProcessTestHelper; + + // Close the IPC channel with the other party. This should only be used + // by tests an none of the class methods should be used afterwards. + void CloseChannel(); + bool initialized_; // Whether we've been through Init() yet. - bool is_child_; // Whether we're the child (broker process). - bool fast_check_in_client_; - bool quiet_failures_for_tests_; + const bool fast_check_in_client_; + const bool quiet_failures_for_tests_; pid_t broker_pid_; // The PID of the broker (child). syscall_broker::BrokerPolicy policy_; // The sandboxing policy. - scoped_ptr<syscall_broker::BrokerClient> - broker_client_; // Can only exist if is_child_ is true. + scoped_ptr<syscall_broker::BrokerClient> broker_client_; - int ipc_socketpair_; // Our communication channel to parent or child. DISALLOW_COPY_AND_ASSIGN(BrokerProcess); - - friend class BrokerProcessTestHelper; }; +} // namespace syscall_broker + } // namespace sandbox #endif // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc index e7d5442..997667c 100644 --- a/sandbox/linux/syscall_broker/broker_process_unittest.cc +++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -6,6 +6,7 @@ #include <errno.h> #include <fcntl.h> +#include <poll.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> @@ -24,6 +25,7 @@ #include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/unix_domain_socket_linux.h" +#include "sandbox/linux/syscall_broker/broker_client.h" #include "sandbox/linux/tests/scoped_temporary_file.h" #include "sandbox/linux/tests/test_utils.h" #include "sandbox/linux/tests/unit_tests.h" @@ -31,10 +33,15 @@ namespace sandbox { +namespace syscall_broker { + class BrokerProcessTestHelper { public: - static int get_ipc_socketpair(const BrokerProcess* broker) { - return broker->ipc_socketpair_; + static void CloseChannel(BrokerProcess* broker) { broker->CloseChannel(); } + // Get the client's IPC descriptor to send IPC requests directly. + // TODO(jln): refator tests to get rid of this. + static int GetIPCDescriptor(const BrokerProcess* broker) { + return broker->broker_client_->GetIPCDescriptor(); } }; @@ -453,7 +460,7 @@ BrokerProcess open_broker(EPERM, read_whitelist, std::vector<std::string>()); SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback))); - const int ipc_fd = BrokerProcessTestHelper::get_ipc_socketpair(&open_broker); + const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker); SANDBOX_ASSERT(ipc_fd >= 0); static const char kBogus[] = "not a pickle"; @@ -472,4 +479,60 @@ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd))); } +bool CloseFD(int fd) { + PCHECK(0 == IGNORE_EINTR(close(fd))); + return true; +} + +// Return true if the other end of the |reader| pipe was closed, +// false if |timeout_in_seconds| was reached or another event +// or error occured. +bool WaitForClosedPipeWriter(int reader, int timeout_in_ms) { + struct pollfd poll_fd = {reader, POLLIN | POLLRDHUP, 0}; + const int num_events = HANDLE_EINTR(poll(&poll_fd, 1, timeout_in_ms)); + if (1 == num_events && poll_fd.revents | POLLHUP) + return true; + return false; +} + +// Closing the broker client's IPC channel should terminate the broker +// process. +TEST(BrokerProcess, BrokerDiesOnClosedChannel) { + std::vector<std::string> read_whitelist; + read_whitelist.push_back("/proc/cpuinfo"); + + // Get the writing end of a pipe into the broker (child) process so + // that we can reliably detect when it dies. + int lifeline_fds[2]; + PCHECK(0 == pipe(lifeline_fds)); + + BrokerProcess open_broker(EPERM, read_whitelist, std::vector<std::string>(), + true /* fast_check_in_client */, + false /* quiet_failures_for_tests */); + ASSERT_TRUE(open_broker.Init(base::Bind(&CloseFD, lifeline_fds[0]))); + // Make sure the writing end only exists in the broker process. + CloseFD(lifeline_fds[1]); + base::ScopedFD reader(lifeline_fds[0]); + + const pid_t broker_pid = open_broker.broker_pid(); + + // This should cause the broker process to exit. + BrokerProcessTestHelper::CloseChannel(&open_broker); + + const int kTimeoutInMilliseconds = 5000; + const bool broker_lifeline_closed = + WaitForClosedPipeWriter(reader.get(), kTimeoutInMilliseconds); + // If the broker exited, its lifeline fd should be closed. + ASSERT_TRUE(broker_lifeline_closed); + // Now check that the broker has exited, but do not reap it. + siginfo_t process_info; + ASSERT_EQ(0, HANDLE_EINTR(waitid(P_PID, broker_pid, &process_info, + WEXITED | WNOWAIT))); + EXPECT_EQ(broker_pid, process_info.si_pid); + EXPECT_EQ(CLD_EXITED, process_info.si_code); + EXPECT_EQ(1, process_info.si_status); +} + +} // namespace syscall_broker + } // namespace sandbox
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h index f745e13..6f150ac 100644 --- a/sandbox/linux/tests/unit_tests.h +++ b/sandbox/linux/tests/unit_tests.h
@@ -99,6 +99,13 @@ ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \ SANDBOX_STR(expr), __FILE__, __LINE__)) +#define SANDBOX_ASSERT_EQ(x, y) SANDBOX_ASSERT((x) == (y)) +#define SANDBOX_ASSERT_NE(x, y) SANDBOX_ASSERT((x) != (y)) +#define SANDBOX_ASSERT_LT(x, y) SANDBOX_ASSERT((x) < (y)) +#define SANDBOX_ASSERT_GT(x, y) SANDBOX_ASSERT((x) > (y)) +#define SANDBOX_ASSERT_LE(x, y) SANDBOX_ASSERT((x) <= (y)) +#define SANDBOX_ASSERT_GE(x, y) SANDBOX_ASSERT((x) >= (y)) + // This class allows to run unittests in their own process. The main method is // RunTestInProcess(). class UnitTests {
diff --git a/sdch/BUILD.gn b/sdch/BUILD.gn index 7a95f1f..727fb49 100644 --- a/sdch/BUILD.gn +++ b/sdch/BUILD.gn
@@ -45,6 +45,24 @@ "//third_party/zlib", ] + # gn orders flags on a target before flags from configs. The default config + # adds -Wall, and these flags have to be after -Wall -- so they need to come + # from a config and can't be on the target directly. + config("sdch_warnings") { + cflags = [] + if (is_linux) { + # TODO(mostynb): remove this if open-vcdiff is ever updated for c++11: + cflags += [ "-Wno-deprecated-declarations" ] + } + + if (is_clang) { + # sdch uses the pre-c++11 typedef-as-static_assert hack. + # https://code.google.com/p/open-vcdiff/issues/detail?id=44 + cflags += [ "-Wno-unused-local-typedef" ] + } + } + configs += [ ":sdch_warnings" ] + if (is_linux || is_android) { include_dirs = [ "linux" ] } else if (is_ios) { @@ -69,9 +87,5 @@ } else { logging_file = rebase_path("logging_forward.h", root_build_dir) cflags = [ "-include", logging_file ] - if (is_linux) { - # TODO(mostynb): remove this if open-vcdiff is ever updated for c++11: - cflags += [ "-Wno-deprecated-declarations" ] - } } }
diff --git a/sdch/sdch.gyp b/sdch/sdch.gyp index 52a92f3..e886288 100644 --- a/sdch/sdch.gyp +++ b/sdch/sdch.gyp
@@ -71,6 +71,11 @@ # introduce static initializers, and which prevents open-vcdiff's # logging.h from being used). 'variables': { + 'clang_warning_flags': [ + # sdch uses the pre-c++11 typedef-as-static_assert hack. + # https://code.google.com/p/open-vcdiff/issues/detail?id=44 + '-Wno-unused-local-typedef', + ], 'logging_path': 'logging_forward.h', 'conditions': [ # gyp leaves unspecified what the cwd is when running the compiler,
diff --git a/skia/BUILD.gn b/skia/BUILD.gn index a57b6a1..7daabd4 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn
@@ -302,8 +302,6 @@ "ext/vector_canvas.h", "ext/vector_platform_device_emf_win.cc", "ext/vector_platform_device_emf_win.h", - "ext/vector_platform_device_skia.cc", - "ext/vector_platform_device_skia.h", ] # The skia gypi values are relative to the skia_dir, so we need to rebase. @@ -416,16 +414,12 @@ if (is_posix) { sources -= [ "ext/SkThread_chrome.cc" ] } - if (is_ios) { - sources -= [ "ext/vector_platform_device_skia.cc" ] - } if (is_win) { sources -= [ "ext/SkThread_chrome.cc" ] } if (is_android && (!enable_basic_printing && !enable_print_preview)) { sources -= [ "ext/skia_utils_base.cc", - "ext/vector_platform_device_skia.cc" ] }
diff --git a/skia/ext/bitmap_platform_device_cairo.cc b/skia/ext/bitmap_platform_device_cairo.cc index 6d5d77c..97fc92a 100644 --- a/skia/ext/bitmap_platform_device_cairo.cc +++ b/skia/ext/bitmap_platform_device_cairo.cc
@@ -167,11 +167,11 @@ cairo_destroy(cairo_); } -SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info, - Usage /*usage*/) { - SkASSERT(info.colorType() == kN32_SkColorType); - return BitmapPlatformDevice::Create(info.width(), info.height(), - info.isOpaque()); +SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice( + const CreateInfo& info) { + SkASSERT(info.fInfo.colorType() == kN32_SkColorType); + return BitmapPlatformDevice::Create(info.fInfo.width(), info.fInfo.height(), + info.fInfo.isOpaque()); } cairo_t* BitmapPlatformDevice::BeginPlatformPaint() {
diff --git a/skia/ext/bitmap_platform_device_cairo.h b/skia/ext/bitmap_platform_device_cairo.h index 2932111..67a2f19 100644 --- a/skia/ext/bitmap_platform_device_cairo.h +++ b/skia/ext/bitmap_platform_device_cairo.h
@@ -93,8 +93,8 @@ const PlatformRect* src_rect) override; protected: - virtual SkBaseDevice* onCreateDevice(const SkImageInfo& info, - Usage usage) override; + virtual SkBaseDevice* onCreateCompatibleDevice(const CreateInfo& info) + override; private: static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
diff --git a/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc index 5204468..871ca83 100644 --- a/skia/ext/bitmap_platform_device_mac.cc +++ b/skia/ext/bitmap_platform_device_mac.cc
@@ -237,11 +237,12 @@ ReleaseBitmapContext(); } -SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info, - Usage /*usage*/) { - SkASSERT(info.colorType() == kN32_SkColorType); - return BitmapPlatformDevice::CreateAndClear(info.width(), info.height(), - info.isOpaque()); +SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice( + const CreateInfo& info) { + SkASSERT(info.fInfo.colorType() == kN32_SkColorType); + return BitmapPlatformDevice::CreateAndClear(info.fInfo.width(), + info.fInfo.height(), + info.fInfo.isOpaque()); } // PlatformCanvas impl
diff --git a/skia/ext/bitmap_platform_device_mac.h b/skia/ext/bitmap_platform_device_mac.h index dc78562..8921117 100644 --- a/skia/ext/bitmap_platform_device_mac.h +++ b/skia/ext/bitmap_platform_device_mac.h
@@ -66,7 +66,7 @@ BitmapPlatformDevice(CGContextRef context, const SkBitmap& bitmap); - SkBaseDevice* onCreateDevice(const SkImageInfo& info, Usage usage) override; + SkBaseDevice* onCreateCompatibleDevice(const CreateInfo& info) override; private: void ReleaseBitmapContext();
diff --git a/skia/ext/bitmap_platform_device_skia.cc b/skia/ext/bitmap_platform_device_skia.cc index ee44e26..1dcf13b 100644 --- a/skia/ext/bitmap_platform_device_skia.cc +++ b/skia/ext/bitmap_platform_device_skia.cc
@@ -51,11 +51,11 @@ BitmapPlatformDevice::~BitmapPlatformDevice() { } -SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info, - Usage /*usage*/) { - SkASSERT(info.colorType() == kN32_SkColorType); - return BitmapPlatformDevice::Create(info.width(), info.height(), - info.isOpaque()); +SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice( + const CreateInfo& info) { + SkASSERT(info.fInfo.colorType() == kN32_SkColorType); + return BitmapPlatformDevice::Create(info.fInfo.width(), info.fInfo.height(), + info.fInfo.isOpaque()); } PlatformSurface BitmapPlatformDevice::BeginPlatformPaint() {
diff --git a/skia/ext/bitmap_platform_device_skia.h b/skia/ext/bitmap_platform_device_skia.h index f670dea..3b8a28d 100644 --- a/skia/ext/bitmap_platform_device_skia.h +++ b/skia/ext/bitmap_platform_device_skia.h
@@ -47,8 +47,8 @@ const PlatformRect* src_rect) override; protected: - virtual SkBaseDevice* onCreateDevice(const SkImageInfo& info, - Usage usage) override; + virtual SkBaseDevice* onCreateCompatibleDevice(const CreateInfo& info) + override; private: DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
diff --git a/skia/ext/bitmap_platform_device_win.cc b/skia/ext/bitmap_platform_device_win.cc index e10a6de..3901ad5 100644 --- a/skia/ext/bitmap_platform_device_win.cc +++ b/skia/ext/bitmap_platform_device_win.cc
@@ -270,11 +270,12 @@ return SkBitmapDevice::onAccessBitmap(); } -SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info, - Usage /*usage*/) { - SkASSERT(info.colorType() == kN32_SkColorType); - return BitmapPlatformDevice::CreateAndClear(info.width(), info.height(), - info.isOpaque()); +SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice( + const CreateInfo& info) { + SkASSERT(info.fInfo.colorType() == kN32_SkColorType); + return BitmapPlatformDevice::CreateAndClear(info.fInfo.width(), + info.fInfo.height(), + info.fInfo.isOpaque()); } // PlatformCanvas impl
diff --git a/skia/ext/bitmap_platform_device_win.h b/skia/ext/bitmap_platform_device_win.h index a8b7d83..92b1408 100644 --- a/skia/ext/bitmap_platform_device_win.h +++ b/skia/ext/bitmap_platform_device_win.h
@@ -68,8 +68,8 @@ // starts accessing pixel data. virtual const SkBitmap& onAccessBitmap() override; - virtual SkBaseDevice* onCreateDevice(const SkImageInfo& info, - Usage usage) override; + virtual SkBaseDevice* onCreateCompatibleDevice(const CreateInfo& info) + override; private: // Private constructor.
diff --git a/skia/ext/vector_platform_device_emf_win.cc b/skia/ext/vector_platform_device_emf_win.cc index 19e3066..1271b16 100644 --- a/skia/ext/vector_platform_device_emf_win.cc +++ b/skia/ext/vector_platform_device_emf_win.cc
@@ -695,11 +695,11 @@ LoadClippingRegionToDC(hdc_, clip_region_, t); } -SkBaseDevice* VectorPlatformDeviceEmf::onCreateDevice(const SkImageInfo& info, - Usage /*usage*/) { - SkASSERT(info.colorType() == kN32_SkColorType); +SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice( + const CreateInfo& info) { + SkASSERT(info.fInfo.colorType() == kN32_SkColorType); return VectorPlatformDeviceEmf::CreateDevice( - info.width(), info.height(), info.isOpaque(), NULL); + info.fInfo.width(), info.fInfo.height(), info.fInfo.isOpaque(), NULL); } bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
diff --git a/skia/ext/vector_platform_device_emf_win.h b/skia/ext/vector_platform_device_emf_win.h index f65390f..8d5b393 100644 --- a/skia/ext/vector_platform_device_emf_win.h +++ b/skia/ext/vector_platform_device_emf_win.h
@@ -79,8 +79,7 @@ void LoadClipRegion(); protected: - virtual SkBaseDevice* onCreateDevice(const SkImageInfo& info, - Usage usage) override; + virtual SkBaseDevice* onCreateCompatibleDevice(const CreateInfo& info) override; private: // Applies the SkPaint's painting properties in the current GDI context, if
diff --git a/skia/ext/vector_platform_device_skia.cc b/skia/ext/vector_platform_device_skia.cc deleted file mode 100644 index 5b8217c..0000000 --- a/skia/ext/vector_platform_device_skia.cc +++ /dev/null
@@ -1,88 +0,0 @@ -// Copyright (c) 2012 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 "skia/ext/vector_platform_device_skia.h" - -#include "skia/ext/bitmap_platform_device.h" -#include "third_party/skia/include/core/SkClipStack.h" -#include "third_party/skia/include/core/SkDraw.h" -#include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/core/SkRegion.h" -#include "third_party/skia/include/core/SkScalar.h" - -namespace skia { - -static inline SkBitmap makeABitmap(int width, int height) { - SkBitmap bitmap; - bitmap.setInfo(SkImageInfo::MakeUnknown(width, height)); - return bitmap; -} - -VectorPlatformDeviceSkia::VectorPlatformDeviceSkia( - const SkISize& pageSize, - const SkISize& contentSize, - const SkMatrix& initialTransform) - : SkPDFDevice(pageSize, contentSize, initialTransform) { - SetPlatformDevice(this, this); -} - -VectorPlatformDeviceSkia::~VectorPlatformDeviceSkia() { -} - -bool VectorPlatformDeviceSkia::SupportsPlatformPaint() { - return false; -} - -PlatformSurface VectorPlatformDeviceSkia::BeginPlatformPaint() { - // Even when drawing a vector representation of the page, we have to - // provide a raster surface for plugins to render into - they don't have - // a vector interface. Therefore we create a BitmapPlatformDevice here - // and return the context from it, then layer on the raster data as an - // image in EndPlatformPaint. - DCHECK(raster_surface_ == NULL); - raster_surface_ = skia::AdoptRef( - BitmapPlatformDevice::CreateAndClear(width(), height(), false)); - return raster_surface_->BeginPlatformPaint(); -} - -void VectorPlatformDeviceSkia::EndPlatformPaint() { - DCHECK(raster_surface_ != NULL); - SkPaint paint; - // SkPDFDevice checks the passed SkDraw for an empty clip (only). Fake - // it out by setting a non-empty clip. - SkDraw draw; - SkRegion clip(SkIRect::MakeWH(width(), height())); - draw.fClip=&clip; - drawSprite(draw, raster_surface_->accessBitmap(false), 0, 0, paint); - // BitmapPlatformDevice matches begin and end calls. - raster_surface_->EndPlatformPaint(); - raster_surface_.clear(); -} - -#if defined(OS_WIN) -void VectorPlatformDeviceSkia::DrawToNativeContext(HDC dc, - int x, - int y, - const RECT* src_rect) { - SkASSERT(false); -} -#elif defined(OS_MACOSX) -void VectorPlatformDeviceSkia::DrawToNativeContext(CGContext* context, int x, - int y, const CGRect* src_rect) { - SkASSERT(false); -} - -CGContextRef VectorPlatformDeviceSkia::GetBitmapContext() { - SkASSERT(false); - return NULL; -} -#elif defined(OS_POSIX) -void VectorPlatformDeviceSkia::DrawToNativeContext( - PlatformSurface surface, int x, int y, const PlatformRect* src_rect) { - // Should never be called on Linux. - SkASSERT(false); -} -#endif - -} // namespace skia
diff --git a/skia/ext/vector_platform_device_skia.h b/skia/ext/vector_platform_device_skia.h deleted file mode 100644 index 528eac9..0000000 --- a/skia/ext/vector_platform_device_skia.h +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright (c) 2011 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 SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_ -#define SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "skia/ext/platform_device.h" -#include "skia/ext/refptr.h" -#include "third_party/skia/include/pdf/SkPDFDevice.h" - -class SkMatrix; - -namespace skia { - -class BitmapPlatformDevice; - -class VectorPlatformDeviceSkia : public SkPDFDevice, public PlatformDevice { - public: - SK_API VectorPlatformDeviceSkia(const SkISize& pageSize, - const SkISize& contentSize, - const SkMatrix& initialTransform); - ~VectorPlatformDeviceSkia() override; - - // PlatformDevice methods. - bool SupportsPlatformPaint() override; - - PlatformSurface BeginPlatformPaint() override; - void EndPlatformPaint() override; -#if defined(OS_WIN) - virtual void DrawToNativeContext(HDC dc, - int x, - int y, - const RECT* src_rect) override; -#elif defined(OS_MACOSX) - void DrawToNativeContext(CGContext* context, - int x, - int y, - const CGRect* src_rect) override; - CGContextRef GetBitmapContext() override; -#elif defined(OS_POSIX) - virtual void DrawToNativeContext(PlatformSurface surface, - int x, - int y, - const PlatformRect* src_rect) override; -#endif - - private: - skia::RefPtr<BitmapPlatformDevice> raster_surface_; - - DISALLOW_COPY_AND_ASSIGN(VectorPlatformDeviceSkia); -}; - -} // namespace skia - -#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_
diff --git a/skia/skia_chrome.gypi b/skia/skia_chrome.gypi index 0f97556..7db9009 100644 --- a/skia/skia_chrome.gypi +++ b/skia/skia_chrome.gypi
@@ -83,8 +83,6 @@ 'ext/vector_canvas.h', 'ext/vector_platform_device_emf_win.cc', 'ext/vector_platform_device_emf_win.h', - 'ext/vector_platform_device_skia.cc', - 'ext/vector_platform_device_skia.h', ], 'conditions': [ [ 'OS == "android" and ' @@ -93,15 +91,7 @@ 'ext/skia_utils_base.cc', ], }], - [ 'enable_basic_printing==0 and enable_print_preview==0', { - 'sources!': [ - 'ext/vector_platform_device_skia.cc', - ], - }], ['OS == "ios"', { - 'sources/': [ - ['exclude', '^ext/vector_platform_device_skia\\.'], - ], 'dependencies!': [ 'skia_chrome_opts', ],
diff --git a/sky/engine/core/css/CSSFontSelector.cpp b/sky/engine/core/css/CSSFontSelector.cpp index cd8faa2..8a18163 100644 --- a/sky/engine/core/css/CSSFontSelector.cpp +++ b/sky/engine/core/css/CSSFontSelector.cpp
@@ -97,15 +97,15 @@ static AtomicString familyNameFromSettings(const GenericFontFamilySettings& settings, const FontDescription& fontDescription, const AtomicString& genericFamilyName) { - UScriptCode script = fontDescription.script(); - #if OS(ANDROID) if (fontDescription.genericFamily() == FontDescription::StandardFamily) - return FontCache::getGenericFamilyNameForScript(FontFamilyNames::webkit_standard, script); + return FontCache::getGenericFamilyNameForScript(FontFamilyNames::webkit_standard, fontDescription); if (genericFamilyName.startsWith("-webkit-")) - return FontCache::getGenericFamilyNameForScript(genericFamilyName, script); + return FontCache::getGenericFamilyNameForScript(genericFamilyName, fontDescription); #else + UScriptCode script = fontDescription.script(); + if (fontDescription.genericFamily() == FontDescription::StandardFamily) return settings.standard(script); if (genericFamilyName == FontFamilyNames::webkit_serif)
diff --git a/sky/engine/platform/fonts/FontCache.h b/sky/engine/platform/fonts/FontCache.h index e68a6cc..c41c6ad 100644 --- a/sky/engine/platform/fonts/FontCache.h +++ b/sky/engine/platform/fonts/FontCache.h
@@ -94,7 +94,7 @@ #endif #if OS(ANDROID) - static AtomicString getGenericFamilyNameForScript(const AtomicString& familyName, UScriptCode); + static AtomicString getGenericFamilyNameForScript(const AtomicString& familyName, const FontDescription&); #else struct PlatformFallbackFont { String name;
diff --git a/sky/engine/platform/fonts/android/FontCacheAndroid.cpp b/sky/engine/platform/fonts/android/FontCacheAndroid.cpp index 8f070e8..25c4c75 100644 --- a/sky/engine/platform/fonts/android/FontCacheAndroid.cpp +++ b/sky/engine/platform/fonts/android/FontCacheAndroid.cpp
@@ -31,7 +31,7 @@ #include "config.h" #include "platform/fonts/FontCache.h" - +#include "platform/Language.h" #include "platform/fonts/SimpleFontData.h" #include "platform/fonts/FontDescription.h" #include "platform/fonts/FontFaceCreationParams.h" @@ -40,31 +40,19 @@ namespace blink { -static AtomicString getFamilyNameForCharacter(UChar32 c, UScriptCode script) +static AtomicString getFamilyNameForCharacter(UChar32 c, const FontDescription& fontDescription) { - // This is a hack to use the preferred font for CJK scripts. - // FIXME: Use new Skia API once Android system supports per-family and per-script fallback fonts. - const char* locale; - switch (script) { - case USCRIPT_SIMPLIFIED_HAN: - locale = "zh-CN"; - break; - case USCRIPT_TRADITIONAL_HAN: - locale = "zh-TW"; - break; - case USCRIPT_KATAKANA_OR_HIRAGANA: - locale = "ja"; - break; - case USCRIPT_HANGUL: - locale = "ko"; - break; - default: - locale = 0; - break; - } - RefPtr<SkFontMgr> fm = adoptRef(SkFontMgr::RefDefault()); - RefPtr<SkTypeface> typeface = adoptRef(fm->matchFamilyStyleCharacter(0, SkFontStyle(), locale, c)); + const char* bcp47Locales[2]; + int localeCount = 0; + CString defaultLocale = defaultLanguage().ascii(); + bcp47Locales[localeCount++] = defaultLocale.data(); + CString fontLocale; + if (!fontDescription.locale().isEmpty()) { + fontLocale = fontDescription.locale().ascii(); + bcp47Locales[localeCount++] = fontLocale.data(); + } + RefPtr<SkTypeface> typeface = adoptRef(fm->matchFamilyStyleCharacter(0, SkFontStyle(), bcp47Locales, localeCount, c)); if (!typeface) return emptyAtom; @@ -75,19 +63,19 @@ PassRefPtr<SimpleFontData> FontCache::fallbackFontForCharacter(const FontDescription& fontDescription, UChar32 c, const SimpleFontData*) { - AtomicString familyName = getFamilyNameForCharacter(c, fontDescription.script()); + AtomicString familyName = getFamilyNameForCharacter(c, fontDescription); if (familyName.isEmpty()) return getLastResortFallbackFont(fontDescription, DoNotRetain); return fontDataFromFontPlatformData(getFontPlatformData(fontDescription, FontFaceCreationParams(familyName)), DoNotRetain); } // static -AtomicString FontCache::getGenericFamilyNameForScript(const AtomicString& familyName, UScriptCode script) +AtomicString FontCache::getGenericFamilyNameForScript(const AtomicString& familyName, const FontDescription& fontDescription) { // This is a hack to use the preferred font for CJK scripts. // FIXME: Use new Skia API once Android system supports per-family and per-script fallback fonts. UChar32 examplerChar; - switch (script) { + switch (fontDescription.script()) { case USCRIPT_SIMPLIFIED_HAN: case USCRIPT_TRADITIONAL_HAN: case USCRIPT_KATAKANA_OR_HIRAGANA: @@ -101,7 +89,7 @@ return familyName; } - return getFamilyNameForCharacter(examplerChar, script); + return getFamilyNameForCharacter(examplerChar, fontDescription); } } // namespace blink
diff --git a/sky/viewer/platform/weblayertreeview_impl.cc b/sky/viewer/platform/weblayertreeview_impl.cc index 2aa5486..f718fc3 100644 --- a/sky/viewer/platform/weblayertreeview_impl.cc +++ b/sky/viewer/platform/weblayertreeview_impl.cc
@@ -7,6 +7,7 @@ #include "base/message_loop/message_loop_proxy.h" #include "cc/layers/layer.h" #include "cc/output/begin_frame_args.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/trees/layer_tree_host.h" #include "mojo/cc/context_provider_mojo.h" #include "mojo/cc/output_surface_mojo.h" @@ -47,7 +48,8 @@ gpu_memory_buffer_manager, settings, base::MessageLoopProxy::current(), - compositor_message_loop_proxy); + compositor_message_loop_proxy, + nullptr); DCHECK(layer_tree_host_); }
diff --git a/testing/android/java/AndroidManifest.xml b/testing/android/java/AndroidManifest.xml index c704cc4..a561f0c 100644 --- a/testing/android/java/AndroidManifest.xml +++ b/testing/android/java/AndroidManifest.xml
@@ -24,6 +24,10 @@ </activity> </application> + <instrumentation android:name="org.chromium.native_test.ChromiumNativeTestInstrumentationTestRunner" + android:targetPackage="org.chromium.native_test" + android:label="Instrumentation entry point for org.chromium.native_test"/> + <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
diff --git a/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java b/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java index f5d381a..42befe5 100644 --- a/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java +++ b/testing/android/java/src/org/chromium/native_test/ChromeNativeTestActivity.java
@@ -22,6 +22,11 @@ * the native activity class loader. */ public class ChromeNativeTestActivity extends Activity { + public static final String EXTRA_COMMAND_LINE_FILE = + "org.chromium.native_test.ChromeNativeTestActivity.CommandLineFile"; + public static final String EXTRA_COMMAND_LINE_FLAGS = + "org.chromium.native_test.ChromeNativeTestActivity.CommandLineFlags"; + private static final String TAG = "ChromeNativeTestActivity"; private static final String EXTRA_RUN_IN_SUB_THREAD = "RunInSubThread"; // We post a delayed task to run tests so that we do not block onCreate(). @@ -66,8 +71,15 @@ } private void runTests() { + String commandLineFlags = getIntent().getStringExtra(EXTRA_COMMAND_LINE_FLAGS); + if (commandLineFlags == null) commandLineFlags = ""; + + String commandLineFilePath = getIntent().getStringExtra(EXTRA_COMMAND_LINE_FILE); + if (commandLineFilePath == null) commandLineFilePath = ""; + // This directory is used by build/android/pylib/test_package_apk.py. - nativeRunTests(getFilesDir().getAbsolutePath(), getApplicationContext()); + nativeRunTests(commandLineFlags, commandLineFilePath, getFilesDir().getAbsolutePath(), + getApplicationContext()); } // Signal a failure of the native test loader to python scripts @@ -85,5 +97,6 @@ } } - private native void nativeRunTests(String filesDir, Context appContext); + private native void nativeRunTests(String commandLineFlags, String commandLineFilePath, + String filesDir, Context appContext); }
diff --git a/testing/android/java/src/org/chromium/native_test/ChromiumNativeTestInstrumentationTestRunner.java b/testing/android/java/src/org/chromium/native_test/ChromiumNativeTestInstrumentationTestRunner.java new file mode 100644 index 0000000..5e8d8e5 --- /dev/null +++ b/testing/android/java/src/org/chromium/native_test/ChromiumNativeTestInstrumentationTestRunner.java
@@ -0,0 +1,196 @@ +// 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. + +package org.chromium.native_test; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An Instrumentation that runs tests based on ChromeNativeTestActivity. + */ +public class ChromiumNativeTestInstrumentationTestRunner extends Instrumentation { + + private static final String TAG = "ChromiumNativeTestInstrumentationTestRunner"; + private static final Pattern RE_TEST_OUTPUT = Pattern.compile("\\[ *([^ ]*) *\\] ?([^ ]*) .*"); + + private static interface ResultsBundleGenerator { + public Bundle generate(Map<String, TestResult> rawResults); + } + + private String mCommandLineFile; + private String mCommandLineFlags; + private Bundle mLogBundle; + private ResultsBundleGenerator mBundleGenerator; + + @Override + public void onCreate(Bundle arguments) { + mCommandLineFile = arguments.getString(ChromeNativeTestActivity.EXTRA_COMMAND_LINE_FILE); + mCommandLineFlags = arguments.getString(ChromeNativeTestActivity.EXTRA_COMMAND_LINE_FLAGS); + mLogBundle = new Bundle(); + mBundleGenerator = new RobotiumBundleGenerator(); + start(); + } + + @Override + public void onStart() { + super.onStart(); + Bundle results = runTests(); + finish(Activity.RESULT_OK, results); + } + + /** Runs the tests in the ChromeNativeTestActivity and returns a Bundle containing the results. + */ + private Bundle runTests() { + Log.i(TAG, "Creating activity."); + Activity activityUnderTest = startNativeTestActivity(); + + Log.i(TAG, "Getting results from FIFO."); + Map<String, TestResult> results = parseResultsFromFifo(activityUnderTest); + + Log.i(TAG, "Finishing activity."); + activityUnderTest.finish(); + + Log.i(TAG, "Parsing results and generating output."); + return mBundleGenerator.generate(results); + } + + /** Starts the ChromeNativeTestActivty. + */ + private Activity startNativeTestActivity() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.setComponent(new ComponentName( + "org.chromium.native_test", + "org.chromium.native_test.ChromeNativeTestActivity")); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (mCommandLineFile != null) { + Log.i(TAG, "Passing command line file extra: " + mCommandLineFile); + i.putExtra(ChromeNativeTestActivity.EXTRA_COMMAND_LINE_FILE, mCommandLineFile); + } + if (mCommandLineFlags != null) { + Log.i(TAG, "Passing command line flag extra: " + mCommandLineFlags); + i.putExtra(ChromeNativeTestActivity.EXTRA_COMMAND_LINE_FLAGS, mCommandLineFlags); + } + return startActivitySync(i); + } + + private static enum TestResult { + PASSED, FAILED, ERROR, UNKNOWN + } + + /** + * Generates a map between test names and test results from the instrumented Activity's FIFO. + */ + private Map<String, TestResult> parseResultsFromFifo(Activity activityUnderTest) { + Map<String, TestResult> results = new HashMap<String, TestResult>(); + + File fifo = null; + BufferedReader r = null; + + try { + // Wait for the test to create the FIFO. + fifo = new File(getTargetContext().getFilesDir().getAbsolutePath(), "test.fifo"); + while (!fifo.exists()) { + Thread.sleep(1000); + } + + r = new BufferedReader( + new InputStreamReader(new BufferedInputStream(new FileInputStream(fifo)))); + + StringBuilder resultsStr = new StringBuilder(); + for (String l = r.readLine(); l != null && !l.equals("<<ScopedMainEntryLogger"); + l = r.readLine()) { + Matcher m = RE_TEST_OUTPUT.matcher(l); + if (m.matches()) { + if (m.group(1).equals("RUN")) { + results.put(m.group(2), TestResult.UNKNOWN); + } else if (m.group(1).equals("FAILED")) { + results.put(m.group(2), TestResult.FAILED); + } else if (m.group(1).equals("OK")) { + results.put(m.group(2), TestResult.PASSED); + } + } + resultsStr.append(l); + resultsStr.append("\n"); + } + mLogBundle.putString(Instrumentation.REPORT_KEY_STREAMRESULT, resultsStr.toString()); + sendStatus(0, mLogBundle); + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for FIFO file creation: " + e.toString()); + } catch (FileNotFoundException e) { + Log.e(TAG, "Couldn't find FIFO file: " + e.toString()); + } catch (IOException e) { + Log.e(TAG, "Error handling FIFO file: " + e.toString()); + } finally { + if (r != null) { + try { + r.close(); + } catch (IOException e) { + Log.e(TAG, "Error while closing FIFO reader."); + } + } + if (fifo != null) { + if (!fifo.delete()) { + Log.e(TAG, "Unable to delete " + fifo.getAbsolutePath()); + } + } + } + return results; + } + + /** + * Creates a results bundle that emulates the one created by Robotium. + */ + private static class RobotiumBundleGenerator implements ResultsBundleGenerator { + public Bundle generate(Map<String, TestResult> rawResults) { + Bundle resultsBundle = new Bundle(); + + int testsPassed = 0; + int testsFailed = 0; + + for (Map.Entry<String, TestResult> entry : rawResults.entrySet()) { + switch (entry.getValue()) { + case PASSED: + ++testsPassed; + break; + case FAILED: + ++testsFailed; + break; + default: + Log.w(TAG, "Unhandled: " + entry.getKey() + ", " + + entry.getValue().toString()); + break; + } + } + + StringBuilder resultBuilder = new StringBuilder(); + resultBuilder.append("\nOK (" + Integer.toString(testsPassed) + " tests)"); + if (testsFailed > 0) { + resultBuilder.append( + "\nFAILURES!!! Tests run: " + Integer.toString(rawResults.size()) + + ", Failures: " + Integer.toString(testsFailed) + ", Errors: 0"); + } + resultsBundle.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + resultBuilder.toString()); + return resultsBundle; + } + } + +}
diff --git a/testing/android/native_test_launcher.cc b/testing/android/native_test_launcher.cc index 63e389a..b7c9d28 100644 --- a/testing/android/native_test_launcher.cc +++ b/testing/android/native_test_launcher.cc
@@ -30,6 +30,7 @@ using testing::native_test_util::ArgsToArgv; using testing::native_test_util::ParseArgsFromCommandLineFile; +using testing::native_test_util::ParseArgsFromString; using testing::native_test_util::ScopedMainEntryLogger; // The main function of the program to be wrapped as a test apk. @@ -117,6 +118,8 @@ static void RunTests(JNIEnv* env, jobject obj, + jstring jcommand_line_flags, + jstring jcommand_line_file_path, jstring jfiles_dir, jobject app_context) { base::AtExitManager exit_manager; @@ -132,7 +135,17 @@ base::android::RegisterJni(env); std::vector<std::string> args; - ParseArgsFromCommandLineFile(kCommandLineFilePath, &args); + + const std::string command_line_file_path( + base::android::ConvertJavaStringToUTF8(env, jcommand_line_file_path)); + if (command_line_file_path.empty()) + ParseArgsFromCommandLineFile(kCommandLineFilePath, &args); + else + ParseArgsFromCommandLineFile(command_line_file_path.c_str(), &args); + + const std::string command_line_flags( + base::android::ConvertJavaStringToUTF8(env, jcommand_line_flags)); + ParseArgsFromString(command_line_flags, &args); std::vector<char*> argv; int argc = ArgsToArgv(args, &argv);
diff --git a/testing/android/native_test_util.cc b/testing/android/native_test_util.cc index 98f32c7..885f297 100644 --- a/testing/android/native_test_util.cc +++ b/testing/android/native_test_util.cc
@@ -9,7 +9,8 @@ #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" -namespace { +namespace testing { +namespace native_test_util { void ParseArgsFromString(const std::string& command_line, std::vector<std::string>* args) { @@ -22,11 +23,6 @@ } } -} // namespace - -namespace testing { -namespace native_test_util { - void ParseArgsFromCommandLineFile( const char* path, std::vector<std::string>* args) { base::FilePath command_line(path);
diff --git a/testing/android/native_test_util.h b/testing/android/native_test_util.h index a756739..ef17e52 100644 --- a/testing/android/native_test_util.h +++ b/testing/android/native_test_util.h
@@ -27,6 +27,8 @@ } }; +void ParseArgsFromString( + const std::string& command_line, std::vector<std::string>* args); void ParseArgsFromCommandLineFile( const char* path, std::vector<std::string>* args); int ArgsToArgv(const std::vector<std::string>& args, std::vector<char*>* argv);
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 2595ad8..d763318 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -61,7 +61,7 @@ "test": "sandbox_linux_unittests", "args": ["--test-launcher-print-test-stdio=always"] }, - "ui_unittests", + "ui_base_unittests", "views_unittests", "wm_unittests", "aura_unittests", @@ -133,7 +133,7 @@ }, "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", "unit_tests", "url_unittests", "views_unittests", @@ -180,7 +180,7 @@ "test": "sandbox_linux_unittests", "args": ["--test-launcher-print-test-stdio=always"] }, - "ui_unittests", + "ui_base_unittests", "views_unittests", "wm_unittests", "aura_unittests",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index c1c7be4..c9869f7 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -32,7 +32,7 @@ "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", + "ui_base_unittests", "ipc_tests", "sync_unit_tests", "unit_tests", @@ -98,7 +98,7 @@ "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", + "ui_base_unittests", "ipc_tests", "sync_unit_tests", "unit_tests", @@ -164,7 +164,7 @@ "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", + "ui_base_unittests", "ipc_tests", "sync_unit_tests", "unit_tests", @@ -230,7 +230,7 @@ "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", + "ui_base_unittests", "ipc_tests", "sync_unit_tests", "unit_tests", @@ -308,7 +308,7 @@ "sql_unittests", "sync_integration_tests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", "unit_tests", "url_unittests", "browser_tests" @@ -400,7 +400,7 @@ "sandbox_linux_unittests", "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", "unit_tests", "url_unittests", "views_unittests",
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json index 4fa07e6..850beb4 100644 --- a/testing/buildbot/chromium.linux.json +++ b/testing/buildbot/chromium.linux.json
@@ -75,7 +75,7 @@ "remoting_unittests", "sandbox_linux_unittests", "sql_unittests", - "ui_unittests", + "ui_base_unittests", { "test": "sync_integration_tests", "swarming": { @@ -184,7 +184,7 @@ "remoting_unittests", "sandbox_linux_unittests", "sql_unittests", - "ui_unittests", + "ui_base_unittests", { "test": "sync_integration_tests", "swarming": { @@ -289,7 +289,7 @@ "args": ["--test-launcher-print-test-stdio=always"] }, "sql_unittests", - "ui_unittests", + "ui_base_unittests", { "test": "sync_integration_tests", "swarming": { @@ -346,7 +346,7 @@ "sandbox_linux_unittests", "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", "unit_tests" ] }
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json index c1bdf11..cff26cd 100644 --- a/testing/buildbot/chromium.mac.json +++ b/testing/buildbot/chromium.mac.json
@@ -73,7 +73,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -171,7 +171,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -269,7 +269,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -360,7 +360,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": {
diff --git a/testing/buildbot/chromium.memory.fyi.json b/testing/buildbot/chromium.memory.fyi.json index adc4165..577ff7d 100644 --- a/testing/buildbot/chromium.memory.fyi.json +++ b/testing/buildbot/chromium.memory.fyi.json
@@ -42,7 +42,7 @@ "sandbox_linux_unittests", "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", "unit_tests", "url_unittests", "views_unittests",
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 1216d6e..8f72e65 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -62,7 +62,7 @@ "sandbox_linux_unittests", "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -150,7 +150,7 @@ "remoting_unittests", "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", "url_unittests" ] }, @@ -211,7 +211,7 @@ "remoting_unittests", "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -303,7 +303,7 @@ "shards": 2 } }, - "ui_unittests", + "ui_base_unittests", "url_unittests", "views_unittests" ]
diff --git a/testing/buildbot/chromium.webkit.json b/testing/buildbot/chromium.webkit.json index b121217..434d369 100644 --- a/testing/buildbot/chromium.webkit.json +++ b/testing/buildbot/chromium.webkit.json
@@ -58,7 +58,7 @@ "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", + "ui_base_unittests", "views_unittests", "wm_unittests", "aura_unittests", @@ -124,7 +124,7 @@ "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", + "ui_base_unittests", "views_unittests", "wm_unittests", "aura_unittests",
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json index d8c9672..0910b9f 100644 --- a/testing/buildbot/chromium.win.json +++ b/testing/buildbot/chromium.win.json
@@ -82,7 +82,7 @@ "can_use_on_swarming_builders": true } }, - "ui_unittests", + "ui_base_unittests", "url_unittests", "views_unittests", "wm_unittests" @@ -191,7 +191,7 @@ "can_use_on_swarming_builders": true } }, - "ui_unittests", + "ui_base_unittests", "url_unittests", "views_unittests", "wm_unittests" @@ -209,6 +209,7 @@ }, "Win7 Tests (1)": { "gtest_tests": [ + "app_installer_unittests", "accessibility_unittests", { "test": "ash_unittests", @@ -296,7 +297,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -402,7 +403,7 @@ }, "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": { @@ -513,7 +514,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": {
diff --git a/testing/buildbot/chromium_memory_trybot.json b/testing/buildbot/chromium_memory_trybot.json index 161071d..357735f 100644 --- a/testing/buildbot/chromium_memory_trybot.json +++ b/testing/buildbot/chromium_memory_trybot.json
@@ -65,7 +65,7 @@ }, "sql_unittests", "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": {
diff --git a/testing/buildbot/chromium_trybot.json b/testing/buildbot/chromium_trybot.json index 9c7f5c9..6e3cff5 100644 --- a/testing/buildbot/chromium_trybot.json +++ b/testing/buildbot/chromium_trybot.json
@@ -4,6 +4,10 @@ ], "gtest_tests": [ "accessibility_unittests", + { + "test": "app_installer_unittests", + "platforms": ["win"] + }, "app_list_unittests", { "test": "ash_unittests", @@ -33,7 +37,7 @@ "test": "browser_tests", "swarming": { "can_use_on_swarming_builders": true, - "shards": 5 + "shards": 8 } }, "cacheinvalidation_unittests", @@ -153,7 +157,7 @@ } }, "sync_unit_tests", - "ui_unittests", + "ui_base_unittests", { "test": "unit_tests", "swarming": {
diff --git a/testing/chromoting/browser_test_commands_linux.txt b/testing/chromoting/browser_test_commands_linux.txt index af64c09..3b6a61b 100644 --- a/testing/chromoting/browser_test_commands_linux.txt +++ b/testing/chromoting/browser_test_commands_linux.txt
@@ -1,4 +1,6 @@ /usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp --extension-name=Chromoting /usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail /usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gafyd -/usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=non-gmail \ No newline at end of file +/usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=non-gmail +/usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp.v2 --extension-name=Chromoting +/usr/bin/python ../xvfb.py $(PROD_DIR) $(PROD_DIR)/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=20000 --webapp-unpacked=$(PROD_DIR)/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail
diff --git a/testing/chromoting/browser_tests_launcher.py b/testing/chromoting/browser_tests_launcher.py index 66b54fe..8f4bba1 100644 --- a/testing/chromoting/browser_tests_launcher.py +++ b/testing/chromoting/browser_tests_launcher.py
@@ -14,14 +14,14 @@ cmd_line = [command] try: - results = subprocess.check_output( - cmd_line, stderr=subprocess.STDOUT, shell=True) + p = subprocess.Popen(cmd_line, stdout=subprocess.PIPE, shell=True) + results, err = p.communicate() + if 'SUCCESS: all tests passed.' not in results: + raise Exception('Test failed\n%s\n%s' % (results, err)) except subprocess.CalledProcessError, e: raise Exception('Exception %s running command %s' % (e, command)) else: print results - finally: - pass def main():
diff --git a/testing/chromoting/chromoting_integration_tests.isolate b/testing/chromoting/chromoting_integration_tests.isolate index db21d82..cdfd8c4 100644 --- a/testing/chromoting/chromoting_integration_tests.isolate +++ b/testing/chromoting/chromoting_integration_tests.isolate
@@ -16,7 +16,6 @@ '../xvfb.py', './browser_tests_launcher.py', './browser_test_commands_linux.txt', - '../../remoting/tools/internal/test_accounts.json', '<(PRODUCT_DIR)/libffmpegsumo.so', '<(PRODUCT_DIR)/libosmesa.so', '<(PRODUCT_DIR)/nacl_irt_x86_64.nexe', @@ -36,7 +35,9 @@ '../test_env.py', '<(PRODUCT_DIR)/browser_tests<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/remoting/remoting.webapp/', + '<(PRODUCT_DIR)/remoting/remoting.webapp.v2/', '<(PRODUCT_DIR)/resources.pak', + '../../remoting/tools/internal/test_accounts.json', ], 'read_only': 1, }, @@ -83,4 +84,4 @@ 'includes': [ '../../base/base.isolate', ], -} +} \ No newline at end of file
diff --git a/testing/chromoting/integration_tests.gyp b/testing/chromoting/integration_tests.gyp index 23fcf6f..78c1368 100644 --- a/testing/chromoting/integration_tests.gyp +++ b/testing/chromoting/integration_tests.gyp
@@ -20,6 +20,13 @@ 'sources': [ 'chromoting_integration_tests.isolate', ], + 'conditions': [ + ['OS=="linux"', { + 'dependencies': [ + '../../remoting/remoting.gyp:remoting_me2me_host_archive', + ], + }], # OS=="linux" + ], }, ], }],
diff --git a/testing/iossim/iossim.mm b/testing/iossim/iossim.mm index 5eaed5c..83cfe92 100644 --- a/testing/iossim/iossim.mm +++ b/testing/iossim/iossim.mm
@@ -504,13 +504,22 @@ [NSCharacterSet newlineCharacterSet]]; NSString* simulatedAppPID = [NSString stringWithFormat:@"%d", session.simulatedApplicationPID]; + NSArray* kErrorStrings = @[ + @"Service exited with abnormal code:", + @"Service exited due to signal:", + ]; for (NSString* line in lines) { - NSString* const kErrorString = @"Service exited with abnormal code:"; - if ([line rangeOfString:kErrorString].location != NSNotFound && - [line rangeOfString:simulatedAppPID].location != NSNotFound) { - LogWarning(@"Console message: %@", line); - badEntryFound = YES; - break; + if ([line rangeOfString:simulatedAppPID].location != NSNotFound) { + for (NSString* errorString in kErrorStrings) { + if ([line rangeOfString:errorString].location != NSNotFound) { + LogWarning(@"Console message: %@", line); + badEntryFound = YES; + break; + } + } + if (badEntryFound) { + break; + } } } // Remove the log file so subsequent invocations of iossim won't be
diff --git a/third_party/boringssl/boringssl_tests.gypi b/third_party/boringssl/boringssl_tests.gypi index 3f7cd53..cf2aff4 100644 --- a/third_party/boringssl/boringssl_tests.gypi +++ b/third_party/boringssl/boringssl_tests.gypi
@@ -85,6 +85,19 @@ 'msvs_disabled_warnings': [ 4267, ], }, { + 'target_name': 'boringssl_constant_time_test', + 'type': 'executable', + 'dependencies': [ + 'boringssl.gyp:boringssl', + ], + 'sources': [ + 'src/crypto/constant_time_test.c', + ], + # TODO(davidben): Fix size_t truncations in BoringSSL. + # https://crbug.com/429039 + 'msvs_disabled_warnings': [ 4267, ], + }, + { 'target_name': 'boringssl_dh_test', 'type': 'executable', 'dependencies': [ @@ -98,6 +111,19 @@ 'msvs_disabled_warnings': [ 4267, ], }, { + 'target_name': 'boringssl_digest_test', + 'type': 'executable', + 'dependencies': [ + 'boringssl.gyp:boringssl', + ], + 'sources': [ + 'src/crypto/digest/digest_test.c', + ], + # TODO(davidben): Fix size_t truncations in BoringSSL. + # https://crbug.com/429039 + 'msvs_disabled_warnings': [ 4267, ], + }, + { 'target_name': 'boringssl_dsa_test', 'type': 'executable', 'dependencies': [ @@ -111,6 +137,19 @@ 'msvs_disabled_warnings': [ 4267, ], }, { + 'target_name': 'boringssl_ec_test', + 'type': 'executable', + 'dependencies': [ + 'boringssl.gyp:boringssl', + ], + 'sources': [ + 'src/crypto/ec/ec_test.c', + ], + # TODO(davidben): Fix size_t truncations in BoringSSL. + # https://crbug.com/429039 + 'msvs_disabled_warnings': [ 4267, ], + }, + { 'target_name': 'boringssl_example_mul', 'type': 'executable', 'dependencies': [ @@ -189,19 +228,6 @@ 'msvs_disabled_warnings': [ 4267, ], }, { - 'target_name': 'boringssl_md5_test', - 'type': 'executable', - 'dependencies': [ - 'boringssl.gyp:boringssl', - ], - 'sources': [ - 'src/crypto/md5/md5_test.c', - ], - # TODO(davidben): Fix size_t truncations in BoringSSL. - # https://crbug.com/429039 - 'msvs_disabled_warnings': [ 4267, ], - }, - { 'target_name': 'boringssl_gcm_test', 'type': 'executable', 'dependencies': [ @@ -241,19 +267,6 @@ 'msvs_disabled_warnings': [ 4267, ], }, { - 'target_name': 'boringssl_sha1_test', - 'type': 'executable', - 'dependencies': [ - 'boringssl.gyp:boringssl', - ], - 'sources': [ - 'src/crypto/sha/sha1_test.c', - ], - # TODO(davidben): Fix size_t truncations in BoringSSL. - # https://crbug.com/429039 - 'msvs_disabled_warnings': [ 4267, ], - }, - { 'target_name': 'boringssl_pkcs7_test', 'type': 'executable', 'dependencies': [ @@ -301,8 +314,11 @@ 'boringssl_bn_test', 'boringssl_bytestring_test', 'boringssl_cipher_test', + 'boringssl_constant_time_test', 'boringssl_dh_test', + 'boringssl_digest_test', 'boringssl_dsa_test', + 'boringssl_ec_test', 'boringssl_ecdsa_test', 'boringssl_err_test', 'boringssl_evp_test', @@ -310,12 +326,10 @@ 'boringssl_gcm_test', 'boringssl_hmac_test', 'boringssl_lhash_test', - 'boringssl_md5_test', 'boringssl_pkcs12_test', 'boringssl_pkcs7_test', 'boringssl_pqueue_test', 'boringssl_rsa_test', - 'boringssl_sha1_test', 'boringssl_ssl_test', ], }
diff --git a/third_party/boringssl/boringssl_unittest.cc b/third_party/boringssl/boringssl_unittest.cc index 1a62cac..6d54ad2 100644 --- a/third_party/boringssl/boringssl_unittest.cc +++ b/third_party/boringssl/boringssl_unittest.cc
@@ -156,6 +156,10 @@ TestSimple("bytestring_test"); } +TEST(BoringSSL, ConstantTime) { + TestSimple("constant_time_test"); +} + TEST(BoringSSL, Cipher) { base::FilePath data_file; ASSERT_TRUE(CryptoCipherPath(&data_file)); @@ -171,10 +175,18 @@ TestSimple("dh_test"); } +TEST(BoringSSL, Digest) { + TestSimple("digest_test"); +} + TEST(BoringSSL, DSA) { TestSimple("dsa_test"); } +TEST(BoringSSL, EC) { + TestSimple("ec_test"); +} + TEST(BoringSSL, ECDSA) { TestSimple("ecdsa_test"); } @@ -195,18 +207,10 @@ TestSimple("lhash_test"); } -TEST(BoringSSL, MD5) { - TestSimple("md5_test"); -} - TEST(BoringSSL, RSA) { TestSimple("rsa_test"); } -TEST(BoringSSL, SHA1) { - TestSimple("sha1_test"); -} - TEST(BoringSSL, PKCS7) { TestSimple("pkcs7_test"); }
diff --git a/third_party/boringssl/win-x86_64/crypto/aes/aesni-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/aes/aesni-x86_64.asm index 30e8a85..e1c9e0e 100644 --- a/third_party/boringssl/win-x86_64/crypto/aes/aesni-x86_64.asm +++ b/third_party/boringssl/win-x86_64/crypto/aes/aesni-x86_64.asm
@@ -519,6 +519,12 @@ mov r8,QWORD[40+rsp] + lea rsp,[((-88))+rsp] + movaps XMMWORD[rsp],xmm6 + movaps XMMWORD[16+rsp],xmm7 + movaps XMMWORD[32+rsp],xmm8 + movaps XMMWORD[48+rsp],xmm9 +$L$ecb_enc_body: and rdx,-16 jz NEAR $L$ecb_ret @@ -813,6 +819,12 @@ movups XMMWORD[80+rsi],xmm7 $L$ecb_ret: + movaps xmm6,XMMWORD[rsp] + movaps xmm7,XMMWORD[16+rsp] + movaps xmm8,XMMWORD[32+rsp] + movaps xmm9,XMMWORD[48+rsp] + lea rsp,[88+rsp] +$L$ecb_enc_ret: mov rdi,QWORD[8+rsp] ;WIN64 epilogue mov rsi,QWORD[16+rsp] DB 0F3h,0C3h ;repret @@ -3378,26 +3390,7 @@ EXTERN __imp_RtlVirtualUnwind ALIGN 16 -ecb_se_handler: - push rsi - push rdi - push rbx - push rbp - push r12 - push r13 - push r14 - push r15 - pushfq - sub rsp,64 - - mov rax,QWORD[152+r8] - - jmp NEAR $L$common_seh_tail - - - -ALIGN 16 -ccm64_se_handler: +ecb_ccm64_se_handler: push rsi push rdi push rbx @@ -3600,14 +3593,15 @@ ALIGN 8 $L$SEH_info_ecb: DB 9,0,0,0 - DD ecb_se_handler wrt ..imagebase + DD ecb_ccm64_se_handler wrt ..imagebase + DD $L$ecb_enc_body wrt ..imagebase,$L$ecb_enc_ret wrt ..imagebase $L$SEH_info_ccm64_enc: DB 9,0,0,0 - DD ccm64_se_handler wrt ..imagebase + DD ecb_ccm64_se_handler wrt ..imagebase DD $L$ccm64_enc_body wrt ..imagebase,$L$ccm64_enc_ret wrt ..imagebase $L$SEH_info_ccm64_dec: DB 9,0,0,0 - DD ccm64_se_handler wrt ..imagebase + DD ecb_ccm64_se_handler wrt ..imagebase DD $L$ccm64_dec_body wrt ..imagebase,$L$ccm64_dec_ret wrt ..imagebase $L$SEH_info_ctr32: DB 9,0,0,0
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn index 7f5b2e3..195c5c8 100644 --- a/third_party/harfbuzz-ng/BUILD.gn +++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/linux/pkg_config.gni") + # The GYP build supports system harfbuzz for non-official builds when using # pangoft2 1.31.0 or greater (which pulls it in). # TODO(brettw) we can consider doing this as well, although the benefit is @@ -11,7 +13,28 @@ # don't want to bloat the binary more by including another copy. declare_args() { - use_system_harfbuzz = is_chromeos + if (is_chromeos) { + # ChromeOS includes an up-to-date system harfbuzz that we have control + # over, so it use the system one by default. + use_system_harfbuzz = true + } else if (is_official_build) { + # For official builds, we want to control the Harbuzz version so always + # use our included one. Currently the sysroot includes a version of pangoft + # that doesn't link to harfbuzz, so there are no linker problems. If we + # update that version, we'll need to work around the duplicate symbols some + # other way. + use_system_harfbuzz = false + } else if (is_linux) { + # Use the system harfbuzz for newer versions of pangoft, and not for older + # ones. pangoft links to the system harfbuzz starting with 1.31.0, which + # causes duplicate symbols when we link our own version. + use_system_harfbuzz = exec_script( + pkg_config_script, + pkg_config_args + [ "--atleast-version=1.31.0", "pangoft2" ], + "value") + } else { + use_system_harfbuzz = false + } } if (use_system_harfbuzz) { @@ -28,7 +51,7 @@ include_dirs = [ "src" ] } - source_set("harfbuzz-ng") { + static_library("harfbuzz-ng") { sources = [ "src/hb-atomic-private.hh", "src/hb-blob.cc",
diff --git a/third_party/libxml/BUILD.gn b/third_party/libxml/BUILD.gn index d42e0df..73bb3dc 100644 --- a/third_party/libxml/BUILD.gn +++ b/third_party/libxml/BUILD.gn
@@ -153,7 +153,6 @@ if (is_win) { cflags_c = [ "/wd4101", # Unreferenced local variable. - "/wd4267", # TODO(jschuh): crbug.com/167187 size_t to int truncations. ] } else if (is_mac || is_android) { # http://www.xmlsoft.org/threads.html says that this is required when using
diff --git a/third_party/mesa/mesa.gyp b/third_party/mesa/mesa.gyp index 6ccbb2a..bc115e1 100644 --- a/third_party/mesa/mesa.gyp +++ b/third_party/mesa/mesa.gyp
@@ -657,14 +657,6 @@ '_GLAPI_NO_EXPORTS', ], }], - ['ubsan==1', { - # Due to a bug in LLVM (http://llvm.org/bugs/show_bug.cgi?id=21349), - # compilation hangs for some Mesa source files. Disable -O2 - # temporarily until http://crbug.com/426271 is fixed. - 'cflags!': [ - '-O2', - ], - }], ], }, # Building this target will hide the native OpenGL shared library and
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index 923e8eb..5dbec0f 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn
@@ -12,9 +12,10 @@ cflags = [ "-msse2", "-msse4.2", "-mpclmul" ] } else { sources = [ "simd_stub.c"] - } - if (is_win) { - cflags = [ "/wd4324" ] + if (is_win) { + # TODO(GYP): crbug.com/431462 disable warning about structure padding. + cflags = [ "/wd4324" ] + } } }
diff --git a/third_party/zlib/simd_stub.c b/third_party/zlib/simd_stub.c index bb2ddc3..796f1f6 100644 --- a/third_party/zlib/simd_stub.c +++ b/third_party/zlib/simd_stub.c
@@ -7,7 +7,7 @@ #include "deflate.h" #include "x86.h" -int x86_cpu_enable_simd; +int x86_cpu_enable_simd = 0; void ZLIB_INTERNAL crc_fold_init(deflate_state *const s) { assert(0);
diff --git a/third_party/zlib/x86.c b/third_party/zlib/x86.c index 35ec516..0649306 100644 --- a/third_party/zlib/x86.c +++ b/third_party/zlib/x86.c
@@ -10,7 +10,7 @@ #include "x86.h" -int x86_cpu_enable_simd; +int x86_cpu_enable_simd = 0; #ifndef _MSC_VER #include <pthread.h>
diff --git a/third_party/zlib/zlib.gyp b/third_party/zlib/zlib.gyp index 8c5ae44..22a48a3 100644 --- a/third_party/zlib/zlib.gyp +++ b/third_party/zlib/zlib.gyp
@@ -8,9 +8,11 @@ 'target_name' : 'zlib_x86_simd', 'type': 'static_library', 'conditions': [ - # See http://crbug.com/420616 gyp on mac & ios doesn't apply cflags - ['OS!="ios" and OS!="mac" and (target_arch=="ia32" or target_arch=="x64")', { + ['OS!="ios" and (target_arch=="ia32" or target_arch=="x64")', { 'cflags' : ["-msse2", "-msse4.2", "-mpclmul"], + 'xcode_settings' : { + 'OTHER_CFLAGS' : ["-msse4.2", "-mpclmul"], + }, 'sources' : [ 'crc_folding.c', 'fill_window_sse.c'] }, { @@ -65,7 +67,7 @@ ], }, 'conditions': [ - ['OS!="ios" and OS!="mac" and (target_arch=="ia32" or target_arch=="x64")', { + ['OS!="ios" and (target_arch=="ia32" or target_arch=="x64")', { 'sources' : [ 'x86.c', ], }], ['OS!="win"', {
diff --git a/tools/clang/empty_string/EmptyStringConverter.cpp b/tools/clang/empty_string/EmptyStringConverter.cpp index 28cc602..d056755 100644 --- a/tools/clang/empty_string/EmptyStringConverter.cpp +++ b/tools/clang/empty_string/EmptyStringConverter.cpp
@@ -196,8 +196,8 @@ // tools. llvm::outs() << "==== BEGIN EDITS ====\n"; for (const auto& r : replacements) { - llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" - << r.getLength() << ":" << r.getReplacementText() << "\n"; + llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() << ":::" + << r.getLength() << ":::" << r.getReplacementText() << "\n"; } llvm::outs() << "==== END EDITS ====\n";
diff --git a/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp b/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp index b2cd31f..fe9d860 100644 --- a/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp +++ b/tools/clang/rewrite_scoped_refptr/RewriteScopedRefptr.cpp
@@ -419,8 +419,8 @@ for (const auto& r : replacements) { std::string replacement_text = r.getReplacementText().str(); std::replace(replacement_text.begin(), replacement_text.end(), '\n', '\0'); - llvm::outs() << "r:" << r.getFilePath() << ":" << r.getOffset() << ":" - << r.getLength() << ":" << replacement_text << "\n"; + llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() << ":::" + << r.getLength() << ":::" << replacement_text << "\n"; } llvm::outs() << "==== END EDITS ====\n";
diff --git a/tools/clang/scripts/run_tool.py b/tools/clang/scripts/run_tool.py index 33bb462..61417e4 100755 --- a/tools/clang/scripts/run_tool.py +++ b/tools/clang/scripts/run_tool.py
@@ -54,12 +54,17 @@ Args: paths: Prefix filter for the returned paths. May contain multiple entries. """ - args = ['git', 'ls-files'] + args = [] + if sys.platform == 'win32': + args.append('git.bat') + else: + args.append('git') + args.append('ls-files') if paths: args.extend(paths) command = subprocess.Popen(args, stdout=subprocess.PIPE) output, _ = command.communicate() - return output.splitlines() + return [os.path.realpath(p) for p in output.splitlines()] def _ExtractEditsFromStdout(build_directory, stdout): @@ -81,11 +86,10 @@ edits = collections.defaultdict(list) for line in lines[start_index + 1:end_index]: try: - edit_type, path, offset, length, replacement = line.split(':', 4) + edit_type, path, offset, length, replacement = line.split(':::', 4) replacement = replacement.replace("\0", "\n"); - # Normalize the file path emitted by the clang tool to be relative to the - # current working directory. - path = os.path.relpath(os.path.join(build_directory, path)) + # Normalize the file path emitted by the clang tool. + path = os.path.realpath(os.path.join(build_directory, path)) edits[path].append(Edit(edit_type, int(offset), int(length), replacement)) except ValueError: print 'Unable to parse edit: %s' % line @@ -140,6 +144,7 @@ self.__filenames = filenames self.__success_count = 0 self.__failed_count = 0 + self.__edit_count = 0 self.__edits = collections.defaultdict(list) @property @@ -172,6 +177,7 @@ self.__success_count += 1 for k, v in result['edits'].iteritems(): self.__edits[k].extend(v) + self.__edit_count += len(v) else: self.__failed_count += 1 sys.stdout.write('\nFailed to process %s\n' % result['filename']) @@ -180,8 +186,9 @@ percentage = ( float(self.__success_count + self.__failed_count) / len(self.__filenames)) * 100 - sys.stdout.write('Succeeded: %d, Failed: %d [%.2f%%]\r' % ( - self.__success_count, self.__failed_count, percentage)) + sys.stdout.write('Succeeded: %d, Failed: %d, Edits: %d [%.2f%%]\r' % ( + self.__success_count, self.__failed_count, self.__edit_count, + percentage)) sys.stdout.flush() @@ -283,7 +290,9 @@ '../../../third_party/llvm/tools/clang/tools/clang-format', 'clang-format-diff.py') # TODO(dcheng): Allow this to be controlled with a flag as well. - if not os.path.isfile(clang_format_diff_path): + # TODO(dcheng): Shell escaping of args to git diff to clang-format is broken + # on Windows. + if not os.path.isfile(clang_format_diff_path) or sys.platform == 'win32': clang_format_diff_path = None filenames = frozenset(_GetFilesFromGit(argv[2:])) @@ -297,7 +306,7 @@ # useful to modify files that aren't under source control--typically, these # are generated files or files in a git submodule that's not part of Chromium. _ApplyEdits({k : v for k, v in dispatcher.edits.iteritems() - if k in filenames}, + if os.path.realpath(k) in filenames}, clang_format_diff_path) if dispatcher.failed_count != 0: return 2
diff --git a/tools/clang/scripts/test_tool.py b/tools/clang/scripts/test_tool.py index d14dfda..1f64be4 100755 --- a/tools/clang/scripts/test_tool.py +++ b/tools/clang/scripts/test_tool.py
@@ -20,7 +20,7 @@ include_path_flags = ' '.join('-I %s' % include_path for include_path in include_paths) return json.dumps([{'directory': '.', - 'command': 'clang++ -fsyntax-only %s -c %s' % ( + 'command': 'clang++ -std=c++11 -fsyntax-only %s -c %s' % ( include_path_flags, f), 'file': f} for f in files], indent=2)
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index 5ee7c30..25aa5a5 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -32,6 +32,7 @@ 'Release+Asserts') COMPILER_RT_BUILD_DIR = os.path.join(LLVM_BUILD_DIR, '32bit-compiler-rt') CLANG_DIR = os.path.join(LLVM_DIR, 'tools', 'clang') +LLD_DIR = os.path.join(LLVM_DIR, 'tools', 'lld') COMPILER_RT_DIR = os.path.join(LLVM_DIR, 'projects', 'compiler-rt') STAMP_FILE = os.path.join(LLVM_BUILD_DIR, 'cr_build_revision') @@ -179,6 +180,7 @@ Checkout('LLVM', LLVM_REPO_URL + '/llvm/trunk', LLVM_DIR) Checkout('Clang', LLVM_REPO_URL + '/cfe/trunk', CLANG_DIR) + Checkout('LLD', LLVM_REPO_URL + '/lld/trunk', LLD_DIR) Checkout('compiler-rt', LLVM_REPO_URL + '/compiler-rt/trunk', COMPILER_RT_DIR) if not os.path.exists(LLVM_BUILD_DIR):
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids index 4681b25..f80cdd0 100644 --- a/tools/gritsettings/resource_ids +++ b/tools/gritsettings/resource_ids
@@ -133,6 +133,10 @@ "includes": [23000], "structures": [23200], }, + "chrome/browser/resources/options_test_resources.grd": { + "includes": [23400], + "structures": [23450], + }, "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": { "messages": [23500], "includes": [23550],
diff --git a/tools/valgrind/chrome_tests.py b/tools/valgrind/chrome_tests.py index 8735e0d..27d554e 100755 --- a/tools/valgrind/chrome_tests.py +++ b/tools/valgrind/chrome_tests.py
@@ -129,11 +129,14 @@ # TODO(timurrrr): also check TSan and MSan? # `nm` might not be available, so use try-except. try: - nm_output = subprocess.check_output(["nm", exe_path]) - if nm_output.find("__asan_init") != -1: - raise BadBinary("You're trying to run an executable instrumented " - "with AddressSanitizer under %s. Please provide " - "an uninstrumented executable." % tool_name) + # Do not perform this check on OS X, as 'nm' on 10.6 can't handle + # binaries built with Clang 3.5+. + if not common.IsMac(): + nm_output = subprocess.check_output(["nm", exe_path]) + if nm_output.find("__asan_init") != -1: + raise BadBinary("You're trying to run an executable instrumented " + "with AddressSanitizer under %s. Please provide " + "an uninstrumented executable." % tool_name) except OSError: pass
diff --git a/tools/valgrind/drmemory/suppressions.txt b/tools/valgrind/drmemory/suppressions.txt index 5840dc1..96ae6fa 100644 --- a/tools/valgrind/drmemory/suppressions.txt +++ b/tools/valgrind/drmemory/suppressions.txt
@@ -550,15 +550,6 @@ *!content::BrowserThreadImpl::CacheThreadRun *!content::BrowserThreadImpl::Run -UNADDRESSABLE ACCESS -name=http://crbug.com/379204 -... -*!WTF::MessageQueue<>::waitForMessageWithTimeout -*!blink::WorkerRunLoop::run -*!blink::WorkerRunLoop::run -*!blink::WorkerThread::workerThread -*!WTF::threadEntryPoint - GDI USAGE ERROR name=379774 system call NtUserCallOneParam.RELEASEDC @@ -667,3 +658,10 @@ blink_web.dll!blink::FrameView::prepareForDetach blink_web.dll!blink::LocalFrame::setView blink_web.dll!blink::FrameTree::~FrameTree + +UNADDRESSABLE ACCESS +name=http://crbug.com/432070 +blink_web.dll!blink::Frame::detach +content.dll!content::RenderFrameProxy::OnDeleteProxy +content.dll!content::RenderFrameProxy::OnMessageReceived +content.dll!content::MessageRouter::RouteMessage
diff --git a/tools/valgrind/drmemory/suppressions_full.txt b/tools/valgrind/drmemory/suppressions_full.txt index ebe6a15..3cd6ca0 100644 --- a/tools/valgrind/drmemory/suppressions_full.txt +++ b/tools/valgrind/drmemory/suppressions_full.txt
@@ -1864,3 +1864,10 @@ name=bug_425097 ... *!ash::test::ShelfViewTest_CheckDragAndDropFromOverflowBubbleToShelf_Test::TestBody + +UNINITIALIZED READ +name=bug_432067 +system call NtCreateFile parameter #9 +MSWSOCK.dll!* +content.dll!content::AppCacheStorageImpl::DatabaseTask::CallRun +
diff --git a/tools/valgrind/gtest_exclude/aura_unittests.gtest.txt b/tools/valgrind/gtest_exclude/aura_unittests.gtest.txt index 321f281..69ce8dc 100644 --- a/tools/valgrind/gtest_exclude/aura_unittests.gtest.txt +++ b/tools/valgrind/gtest_exclude/aura_unittests.gtest.txt
@@ -1,5 +1,2 @@ # Flaky under Valgrind, see http://crbug.com/348331 WindowEventDispatcherTest.TouchMovesHeld - -# Failing on cros/win. crbug.com/427729 -GestureRecognizerTest.GestureEventSmallPinchEnabled
diff --git a/tools/valgrind/gtest_exclude/gfx_unittests.gtest-memcheck.txt b/tools/valgrind/gtest_exclude/gfx_unittests.gtest-memcheck.txt new file mode 100644 index 0000000..3aaaafc --- /dev/null +++ b/tools/valgrind/gtest_exclude/gfx_unittests.gtest-memcheck.txt
@@ -0,0 +1,2 @@ +# http://crbug.com/402209 +FontRenderParamsTest.Default
diff --git a/tools/valgrind/gtest_exclude/ui_unittests.gtest-memcheck_linux.txt b/tools/valgrind/gtest_exclude/ui_unittests.gtest-memcheck_linux.txt new file mode 100644 index 0000000..282de6b --- /dev/null +++ b/tools/valgrind/gtest_exclude/ui_unittests.gtest-memcheck_linux.txt
@@ -0,0 +1,2 @@ +# http://crbug.com/431708 +TouchExplorationTest.TwoFingerTapAndHold
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index 852a638..fa0f599 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt
@@ -2506,19 +2506,6 @@ fun:nssTrustDomain_UpdateCachedTokenCerts } { - bug_331063 - Memcheck:Uninitialized - ... - fun:_ZN5blink11RenderImage12imageChangedEPvPKNS_7IntRectE - fun:_ZN5blink12RenderObject12imageChangedEPNS_13ImageResourceEPKNS_7IntRectE - fun:_ZN5blink13ImageResource12didAddClientEPNS_14ResourceClientE - fun:_ZN5blink8Resource9addClientEPNS_14ResourceClientE - fun:_ZN5blink19RenderImageResource16setImageResourceEPNS_13ImageResourceE - fun:_ZN5blink30PasswordGeneratorButtonElement11updateImageEv - fun:_ZN5blink30PasswordGeneratorButtonElement6attachERKNS_4Node13AttachContextE - fun:_ZN5blink13ContainerNode14attachChildrenERKNS_4Node13AttachContextE -} -{ bug_331925 Memcheck:Leak ... @@ -2845,19 +2832,6 @@ fun:_ZN7content27ServiceWorkerContextWrapper12InitInternalERKN4base8FilePathEPNS1_19SequencedTaskRunnerEPNS1_16MessageLoopProxyEPN5quota17QuotaManagerProxyE } { - bug_379359 - Memcheck:Leak - fun:_Znw* - fun:_ZN7content27ServiceWorkerContextWrapperC1EPNS_14BrowserContextE - fun:_ZN7content20StoragePartitionImpl6CreateEPNS_14BrowserContextEbRKN4base8FilePathE - fun:_ZN7content23StoragePartitionImplMap3GetERKSsS2_b - fun:_ZN7content12_GLOBAL__N_129GetStoragePartitionFromConfigEPNS_14BrowserContext* - fun:_ZN7content14BrowserContext19GetStoragePartitionEPS0_PNS_12SiteInstanceE - fun:_ZN7content14BrowserContext26GetDefaultStoragePartitionEPS0_ - fun:_ZN7content21ShellBrowserMainParts21PreMainMessageLoopRunEv - fun:_ZN7content15BrowserMainLoop21PreMainMessageLoopRunEv -} -{ bug_379943 Memcheck:Leak fun:_Znw* @@ -2867,6 +2841,7 @@ fun:_ZN7content12_GLOBAL__N_129GetStoragePartitionFromConfigEPNS_14BrowserContext* fun:_ZN7content14BrowserContext19GetStoragePartitionEPS0_PNS_12SiteInstanceE fun:_ZN7content14BrowserContext26GetDefaultStoragePartitionEPS0_ + ... fun:_ZN7content21ShellBrowserMainParts21PreMainMessageLoopRunEv fun:_ZN7content15BrowserMainLoop21PreMainMessageLoopRunEv } @@ -3435,3 +3410,28 @@ fun:_ZN7content19TestRenderFrameHost41SendNavigateWithTransitionAndResponseCodeEiRK4GURLN2ui14PageTransitionEi fun:_ZN7content19TestRenderFrameHost26SendNavigateWithTransitionEiRK4GURLN2ui14PageTransitionE } +{ + bug_431209a + Memcheck:Leak + fun:_Znw* + fun:_ZN8remoting13ClipboardAuraC1E13scoped_refptrIN4base22SingleThreadTaskRunnerEE + fun:_ZN8remoting17ClipboardAuraTest5SetUpEv +} +{ + bug_431209b + Memcheck:Leak + fun:_Znw* + fun:_ZN2ui9Clipboard6CreateEv + fun:_ZN2ui9Clipboard19GetForCurrentThreadEv + fun:_ZN2ui21ScopedClipboardWriterD1Ev + fun:_ZN8remoting13ClipboardAura4Core20InjectClipboardEventERKNS_8protocol14ClipboardEventE +} +{ + bug_431213 + Memcheck:Leak + fun:_Znw* + fun:_ZN3gin22CreateFunctionTemplateIFSsPN4mojo2js13HandleWrapperEEEEN2v85LocalINS6_16FunctionTemplateEEEPNS6_7IsolateEN4base8CallbackIT_EEi + fun:_ZN3gin12_GLOBAL__N_114CallbackTraitsIMN4mojo2js13HandleWrapperEFSsvEvE14CreateTemplateEPN2v87IsolateES6_ + fun:_ZN3gin21ObjectTemplateBuilder9SetMethodIMN4mojo2js13HandleWrapperEFSsvEEERS0_RKN4base16BasicStringPieceISsEERKT_ + fun:_ZN4mojo2js13HandleWrapper24GetObjectTemplateBuilderEPN2v87IsolateE +}
diff --git a/tools/valgrind/memcheck/suppressions_mac.txt b/tools/valgrind/memcheck/suppressions_mac.txt index fe8f8a1..154e4e9 100644 --- a/tools/valgrind/memcheck/suppressions_mac.txt +++ b/tools/valgrind/memcheck/suppressions_mac.txt
@@ -194,19 +194,6 @@ fun:_ZN3gfx9GLSurface24InitializeOneOffForTestsEv } { - bug_380568 - Memcheck:Leak - fun:calloc - fun:_internal_class_createInstanceFromZone - fun:_internal_class_createInstance - fun:NSAllocateObject - fun:+[NSObject(NSObject) alloc] - fun:-[VideoCaptureDeviceQTKit initWithFrameReceiver:] - fun:_ZN5media21VideoCaptureDeviceMac4InitENS_18VideoCaptureDevice4Name14CaptureApiTypeE - fun:_ZN5media28VideoCaptureDeviceFactoryMac6CreateERKNS_18VideoCaptureDevice4NameE - fun:_ZN5media45VideoCaptureDeviceTest_OpenInvalidDevice_Test8TestBodyEv -} -{ bug_385604_b Memcheck:Leak fun:calloc
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc index c9ad475..6bcb02a 100644 --- a/ui/compositor/compositor.cc +++ b/ui/compositor/compositor.cc
@@ -18,6 +18,7 @@ #include "cc/base/switches.h" #include "cc/input/input_handler.h" #include "cc/layers/layer.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/output/begin_frame_args.h" #include "cc/output/context_provider.h" #include "cc/surfaces/surface_id_allocator.h" @@ -168,7 +169,8 @@ context_factory_->GetGpuMemoryBufferManager(), settings, task_runner_, - compositor_thread_loop_); + compositor_thread_loop_, + nullptr); } else { host_ = cc::LayerTreeHost::CreateSingleThreaded( this, @@ -176,7 +178,8 @@ context_factory_->GetSharedBitmapManager(), context_factory_->GetGpuMemoryBufferManager(), settings, - task_runner_); + task_runner_, + nullptr); } UMA_HISTOGRAM_TIMES("GPU.CreateBrowserCompositor", base::TimeTicks::Now() - before_create);
diff --git a/ui/compositor/float_animation_curve_adapter.cc b/ui/compositor/float_animation_curve_adapter.cc index 308fc1e..3b5e155 100644 --- a/ui/compositor/float_animation_curve_adapter.cc +++ b/ui/compositor/float_animation_curve_adapter.cc
@@ -17,8 +17,8 @@ duration_(duration) { } -double FloatAnimationCurveAdapter::Duration() const { - return duration_.InSecondsF(); +base::TimeDelta FloatAnimationCurveAdapter::Duration() const { + return duration_; } scoped_ptr<cc::AnimationCurve> FloatAnimationCurveAdapter::Clone() const {
diff --git a/ui/compositor/float_animation_curve_adapter.h b/ui/compositor/float_animation_curve_adapter.h index 1c7b8b3..88d5870 100644 --- a/ui/compositor/float_animation_curve_adapter.h +++ b/ui/compositor/float_animation_curve_adapter.h
@@ -21,7 +21,7 @@ ~FloatAnimationCurveAdapter() override {} // FloatAnimationCurve implementation. - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<cc::AnimationCurve> Clone() const override; float GetValue(double t) const override;
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index abf7e02..ea30852 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/json/json_writer.h" +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "cc/base/scoped_ptr_algorithm.h" @@ -578,7 +579,7 @@ scoped_refptr<cc::SurfaceLayer> new_layer = cc::SurfaceLayer::Create(satisfy_callback, require_callback); - new_layer->SetSurfaceId(id); + new_layer->SetSurfaceId(id, 1.f, frame_size_in_dip); SwitchToLayer(new_layer); surface_layer_ = new_layer;
diff --git a/ui/compositor/transform_animation_curve_adapter.cc b/ui/compositor/transform_animation_curve_adapter.cc index fa73140..2c11488 100644 --- a/ui/compositor/transform_animation_curve_adapter.cc +++ b/ui/compositor/transform_animation_curve_adapter.cc
@@ -22,8 +22,8 @@ TransformAnimationCurveAdapter::~TransformAnimationCurveAdapter() { } -double TransformAnimationCurveAdapter::Duration() const { - return duration_.InSecondsF(); +base::TimeDelta TransformAnimationCurveAdapter::Duration() const { + return duration_; } scoped_ptr<cc::AnimationCurve> TransformAnimationCurveAdapter::Clone() const { @@ -86,8 +86,8 @@ InverseTransformCurveAdapter::~InverseTransformCurveAdapter() { } -double InverseTransformCurveAdapter::Duration() const { - return duration_.InSeconds(); +base::TimeDelta InverseTransformCurveAdapter::Duration() const { + return duration_; } scoped_ptr<cc::AnimationCurve> InverseTransformCurveAdapter::Clone() const {
diff --git a/ui/compositor/transform_animation_curve_adapter.h b/ui/compositor/transform_animation_curve_adapter.h index 7aa318e..c08024d 100644 --- a/ui/compositor/transform_animation_curve_adapter.h +++ b/ui/compositor/transform_animation_curve_adapter.h
@@ -25,7 +25,7 @@ ~TransformAnimationCurveAdapter() override; // TransformAnimationCurve implementation. - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<AnimationCurve> Clone() const override; gfx::Transform GetValue(double t) const override; bool AnimatedBoundsForBox(const gfx::BoxF& box, @@ -55,7 +55,7 @@ ~InverseTransformCurveAdapter() override; - double Duration() const override; + base::TimeDelta Duration() const override; scoped_ptr<AnimationCurve> Clone() const override; gfx::Transform GetValue(double t) const override; bool AnimatedBoundsForBox(const gfx::BoxF& box,
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index 641f51d..6968f2c 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn
@@ -204,6 +204,8 @@ sources += [ "gl_context_cgl.cc", "gl_context_cgl.h", + "gl_fence_apple.cc", + "gl_fence_apple.h", "gl_image_io_surface.cc", "gl_image_io_surface.h", "scoped_cgl.cc",
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py index 7670ec8..0156806 100755 --- a/ui/gl/generate_bindings.py +++ b/ui/gl/generate_bindings.py
@@ -67,6 +67,23 @@ 'names': ['glBindTexture'], 'arguments': 'GLenum target, GLuint texture', }, { 'return_type': 'void', + 'known_as': 'glBindVertexArrayOES', + 'versions': [{ 'name': 'glBindVertexArray', + 'gl_versions': ['gl3', 'gl4', 'es3'] }, + { 'name': 'glBindVertexArray', + 'extensions': ['GL_ARB_vertex_array_object'] }, + { 'name': 'glBindVertexArrayOES' }, + { 'name': 'glBindVertexArrayAPPLE', + 'extensions': ['GL_APPLE_vertex_array_object'] }], + 'arguments': 'GLuint array' }, +{ 'return_type': 'void', + 'known_as': 'glBlendBarrierKHR', + 'versions': [{ 'name': 'glBlendBarrierNV', + 'extensions': ['GL_NV_blend_equation_advanced'] }, + { 'name': 'glBlendBarrierKHR', + 'extensions': ['GL_KHR_blend_equation_advanced'] }], + 'arguments': 'void' }, +{ 'return_type': 'void', 'names': ['glBlendColor'], 'arguments': 'GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha', }, { 'return_type': 'void', @@ -88,12 +105,12 @@ 'GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, ' 'GLbitfield mask, GLenum filter', }, { 'return_type': 'void', - 'names': ['glBlitFramebufferEXT', 'glBlitFramebuffer'], + 'names': ['glBlitFramebufferANGLE', 'glBlitFramebuffer'], 'arguments': 'GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, ' 'GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, ' 'GLbitfield mask, GLenum filter', }, { 'return_type': 'void', - 'names': ['glBlitFramebufferANGLE', 'glBlitFramebuffer'], + 'names': ['glBlitFramebufferEXT', 'glBlitFramebuffer'], 'arguments': 'GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, ' 'GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, ' 'GLbitfield mask, GLenum filter', }, @@ -127,6 +144,9 @@ { 'return_type': 'void', 'names': ['glClearStencil'], 'arguments': 'GLint s', }, +{ 'return_type': 'GLenum', + 'names': ['glClientWaitSync'], + 'arguments': 'GLsync sync, GLbitfield flags, GLuint64 timeout', }, { 'return_type': 'void', 'names': ['glColorMask'], 'arguments': @@ -168,6 +188,14 @@ 'names': ['glDeleteBuffersARB', 'glDeleteBuffers'], 'arguments': 'GLsizei n, const GLuint* buffers', }, { 'return_type': 'void', + 'known_as': 'glDeleteFencesAPPLE', + 'versions': [{ 'name': 'glDeleteFencesAPPLE', + 'extensions': ['GL_APPLE_fence'] }], + 'arguments': 'GLsizei n, const GLuint* fences', }, +{ 'return_type': 'void', + 'names': ['glDeleteFencesNV'], + 'arguments': 'GLsizei n, const GLuint* fences', }, +{ 'return_type': 'void', 'names': ['glDeleteFramebuffersEXT', 'glDeleteFramebuffers'], 'arguments': 'GLsizei n, const GLuint* framebuffers', }, { 'return_type': 'void', @@ -186,9 +214,22 @@ 'names': ['glDeleteShader'], 'arguments': 'GLuint shader', }, { 'return_type': 'void', + 'names': ['glDeleteSync'], + 'arguments': 'GLsync sync', }, +{ 'return_type': 'void', 'names': ['glDeleteTextures'], 'arguments': 'GLsizei n, const GLuint* textures', }, { 'return_type': 'void', + 'known_as': 'glDeleteVertexArraysOES', + 'versions': [{ 'name': 'glDeleteVertexArrays', + 'gl_versions': ['gl3', 'gl4', 'es3'] }, + { 'name': 'glDeleteVertexArrays', + 'extensions': ['GL_ARB_vertex_array_object'] }, + { 'name': 'glDeleteVertexArraysOES' }, + { 'name': 'glDeleteVertexArraysAPPLE', + 'extensions': ['GL_APPLE_vertex_array_object'] }], + 'arguments': 'GLsizei n, const GLuint* arrays' }, +{ 'return_type': 'void', 'names': ['glDepthFunc'], 'arguments': 'GLenum func', }, { 'return_type': 'void', @@ -210,9 +251,23 @@ 'names': ['glDisableVertexAttribArray'], 'arguments': 'GLuint index', }, { 'return_type': 'void', + 'known_as': 'glDiscardFramebufferEXT', + 'versions': [{ 'name': 'glInvalidateFramebuffer', + 'gl_versions': ['es3'], + 'extensions': [] }, + { 'name': 'glDiscardFramebufferEXT', + 'gl_versions': ['es1', 'es2'] }], + 'arguments': 'GLenum target, GLsizei numAttachments, ' + 'const GLenum* attachments' }, +{ 'return_type': 'void', 'names': ['glDrawArrays'], 'arguments': 'GLenum mode, GLint first, GLsizei count', }, { 'return_type': 'void', + 'known_as': 'glDrawArraysInstancedANGLE', + 'names': ['glDrawArraysInstancedARB', 'glDrawArraysInstancedANGLE', + 'glDrawArraysInstanced'], + 'arguments': 'GLenum mode, GLint first, GLsizei count, GLsizei primcount', }, +{ 'return_type': 'void', 'names': ['glDrawBuffer'], 'arguments': 'GLenum mode', }, { 'return_type': 'void', @@ -223,12 +278,19 @@ 'arguments': 'GLenum mode, GLsizei count, GLenum type, const void* indices', }, { 'return_type': 'void', - 'names': ['glEGLImageTargetTexture2DOES'], - 'arguments': 'GLenum target, GLeglImageOES image', }, + 'known_as': 'glDrawElementsInstancedANGLE', + 'names': ['glDrawElementsInstancedARB', 'glDrawElementsInstancedANGLE', + 'glDrawElementsInstanced'], + 'arguments': + 'GLenum mode, GLsizei count, GLenum type, const void* indices, ' + 'GLsizei primcount', }, { 'return_type': 'void', 'names': ['glEGLImageTargetRenderbufferStorageOES'], 'arguments': 'GLenum target, GLeglImageOES image', }, { 'return_type': 'void', + 'names': ['glEGLImageTargetTexture2DOES'], + 'arguments': 'GLenum target, GLeglImageOES image', }, +{ 'return_type': 'void', 'names': ['glEnable'], 'arguments': 'GLenum cap', }, { 'return_type': 'void', @@ -240,15 +302,29 @@ { 'return_type': 'void', 'names': ['glEndQueryARB', 'glEndQueryEXT'], 'arguments': 'GLenum target', }, +{ 'return_type': 'GLsync', + 'names': ['glFenceSync'], + 'arguments': 'GLenum condition, GLbitfield flags', }, { 'return_type': 'void', 'names': ['glFinish'], 'arguments': 'void', }, { 'return_type': 'void', + 'known_as': 'glFinishFenceAPPLE', + 'versions': [{ 'name': 'glFinishFenceAPPLE', + 'extensions': ['GL_APPLE_fence'] }], + 'arguments': 'GLuint fence', }, +{ 'return_type': 'void', + 'names': ['glFinishFenceNV'], + 'arguments': 'GLuint fence', }, +{ 'return_type': 'void', 'names': ['glFlush'], 'arguments': 'void', }, { 'return_type': 'void', + 'names': ['glFlushMappedBufferRange'], + 'arguments': 'GLenum target, GLintptr offset, GLsizeiptr length', }, +{ 'return_type': 'void', 'names': ['glFramebufferRenderbufferEXT', 'glFramebufferRenderbuffer'], - 'arguments': \ + 'arguments': 'GLenum target, GLenum attachment, GLenum renderbuffertarget, ' 'GLuint renderbuffer', }, { 'return_type': 'void', @@ -273,24 +349,42 @@ 'names': ['glGenBuffersARB', 'glGenBuffers'], 'arguments': 'GLsizei n, GLuint* buffers', }, { 'return_type': 'void', + 'names': ['glGenerateMipmapEXT', 'glGenerateMipmap'], + 'arguments': 'GLenum target', }, +{ 'return_type': 'void', + 'known_as': 'glGenFencesAPPLE', + 'versions': [{ 'name': 'glGenFencesAPPLE', + 'extensions': ['GL_APPLE_fence'] }], + 'arguments': 'GLsizei n, GLuint* fences', }, +{ 'return_type': 'void', + 'names': ['glGenFencesNV'], + 'arguments': 'GLsizei n, GLuint* fences', }, +{ 'return_type': 'void', + 'names': ['glGenFramebuffersEXT', 'glGenFramebuffers'], + 'arguments': 'GLsizei n, GLuint* framebuffers', }, +{ 'return_type': 'void', 'names': ['glGenQueries'], 'arguments': 'GLsizei n, GLuint* ids', }, { 'return_type': 'void', 'names': ['glGenQueriesARB', 'glGenQueriesEXT'], 'arguments': 'GLsizei n, GLuint* ids', }, { 'return_type': 'void', - 'names': ['glGenerateMipmapEXT', 'glGenerateMipmap'], - 'arguments': 'GLenum target', }, -{ 'return_type': 'void', - 'names': ['glGenFramebuffersEXT', 'glGenFramebuffers'], - 'arguments': 'GLsizei n, GLuint* framebuffers', }, -{ 'return_type': 'void', 'names': ['glGenRenderbuffersEXT', 'glGenRenderbuffers'], 'arguments': 'GLsizei n, GLuint* renderbuffers', }, { 'return_type': 'void', 'names': ['glGenTextures'], 'arguments': 'GLsizei n, GLuint* textures', }, { 'return_type': 'void', + 'known_as': 'glGenVertexArraysOES', + 'versions': [{ 'name': 'glGenVertexArrays', + 'gl_versions': ['gl3', 'gl4', 'es3'] }, + { 'name': 'glGenVertexArrays', + 'extensions': ['GL_ARB_vertex_array_object'] }, + { 'name': 'glGenVertexArraysOES' }, + { 'name': 'glGenVertexArraysAPPLE', + 'extensions': ['GL_APPLE_vertex_array_object'] }], + 'arguments': 'GLsizei n, GLuint* arrays', }, +{ 'return_type': 'void', 'names': ['glGetActiveAttrib'], 'arguments': 'GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, ' @@ -320,6 +414,9 @@ GL_SERVICE_LOG("GL_RESULT: " << GLES2Util::GetStringError(result)); """, }, { 'return_type': 'void', + 'names': ['glGetFenceivNV'], + 'arguments': 'GLuint fence, GLenum pname, GLint* params', }, +{ 'return_type': 'void', 'names': ['glGetFloatv'], 'arguments': 'GLenum pname, GLfloat* params', }, { 'return_type': 'void', @@ -334,12 +431,12 @@ 'glGetGraphicsResetStatus'], 'arguments': 'void', }, { 'return_type': 'void', - 'names': ['glGetIntegerv'], - 'arguments': 'GLenum pname, GLint* params', }, -{ 'return_type': 'void', 'names': ['glGetInteger64v'], 'arguments': 'GLenum pname, GLint64* params', }, { 'return_type': 'void', + 'names': ['glGetIntegerv'], + 'arguments': 'GLenum pname, GLint* params', }, +{ 'return_type': 'void', 'known_as': 'glGetProgramBinary', 'versions': [{ 'name': 'glGetProgramBinaryOES' }, { 'name': 'glGetProgramBinary', @@ -348,13 +445,13 @@ 'arguments': 'GLuint program, GLsizei bufSize, GLsizei* length, ' 'GLenum* binaryFormat, GLvoid* binary' }, { 'return_type': 'void', - 'names': ['glGetProgramiv'], - 'arguments': 'GLuint program, GLenum pname, GLint* params', }, -{ 'return_type': 'void', 'names': ['glGetProgramInfoLog'], 'arguments': 'GLuint program, GLsizei bufsize, GLsizei* length, char* infolog', }, { 'return_type': 'void', + 'names': ['glGetProgramiv'], + 'arguments': 'GLuint program, GLenum pname, GLint* params', }, +{ 'return_type': 'void', 'names': ['glGetQueryiv'], 'arguments': 'GLenum target, GLenum pname, GLint* params', }, { 'return_type': 'void', @@ -364,6 +461,10 @@ 'names': ['glGetQueryObjecti64v'], 'arguments': 'GLuint id, GLenum pname, GLint64* params', }, { 'return_type': 'void', + 'names': ['glGetQueryObjectiv', 'glGetQueryObjectivARB', + 'glGetQueryObjectivEXT'], + 'arguments': 'GLuint id, GLenum pname, GLint* params', }, +{ 'return_type': 'void', 'names': ['glGetQueryObjectui64v', 'glGetQueryObjectui64vEXT'], 'arguments': 'GLuint id, GLenum pname, GLuint64* params', }, { 'return_type': 'void', @@ -373,20 +474,16 @@ 'names': ['glGetQueryObjectuivARB', 'glGetQueryObjectuivEXT'], 'arguments': 'GLuint id, GLenum pname, GLuint* params', }, { 'return_type': 'void', - 'names': ['glGetQueryObjectiv', 'glGetQueryObjectivARB', - 'glGetQueryObjectivEXT'], - 'arguments': 'GLuint id, GLenum pname, GLint* params', }, -{ 'return_type': 'void', 'names': ['glGetRenderbufferParameterivEXT', 'glGetRenderbufferParameteriv'], 'arguments': 'GLenum target, GLenum pname, GLint* params', }, { 'return_type': 'void', - 'names': ['glGetShaderiv'], - 'arguments': 'GLuint shader, GLenum pname, GLint* params', }, -{ 'return_type': 'void', 'names': ['glGetShaderInfoLog'], 'arguments': 'GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog', }, { 'return_type': 'void', + 'names': ['glGetShaderiv'], + 'arguments': 'GLuint shader, GLenum pname, GLint* params', }, +{ 'return_type': 'void', 'names': ['glGetShaderPrecisionFormat'], 'arguments': 'GLenum shadertype, GLenum precisiontype, ' 'GLint* range, GLint* precision', }, @@ -398,6 +495,11 @@ 'names': ['glGetString'], 'arguments': 'GLenum name', }, { 'return_type': 'void', + 'names': ['glGetSynciv'], + 'arguments': + 'GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length,' + 'GLint* values', }, +{ 'return_type': 'void', 'names': ['glGetTexLevelParameterfv'], 'arguments': 'GLenum target, GLint level, GLenum pname, GLfloat* params', }, { 'return_type': 'void', @@ -444,6 +546,14 @@ 'names': ['glIsEnabled'], 'arguments': 'GLenum cap', }, { 'return_type': 'GLboolean', + 'known_as': 'glIsFenceAPPLE', + 'versions': [{ 'name': 'glIsFenceAPPLE', + 'extensions': ['GL_APPLE_fence'] }], + 'arguments': 'GLuint fence', }, +{ 'return_type': 'GLboolean', + 'names': ['glIsFenceNV'], + 'arguments': 'GLuint fence', }, +{ 'return_type': 'GLboolean', 'names': ['glIsFramebufferEXT', 'glIsFramebuffer'], 'arguments': 'GLuint framebuffer', }, { 'return_type': 'GLboolean', @@ -459,8 +569,21 @@ 'names': ['glIsShader'], 'arguments': 'GLuint shader', }, { 'return_type': 'GLboolean', + 'names': ['glIsSync'], + 'arguments': 'GLsync sync', }, +{ 'return_type': 'GLboolean', 'names': ['glIsTexture'], 'arguments': 'GLuint texture', }, +{ 'return_type': 'GLboolean', + 'known_as': 'glIsVertexArrayOES', + 'versions': [{ 'name': 'glIsVertexArray', + 'gl_versions': ['gl3', 'gl4'] }, + { 'name': 'glIsVertexArray', + 'extensions': ['GL_ARB_vertex_array_object'] }, + { 'name': 'glIsVertexArrayOES' }, + { 'name': 'glIsVertexArrayAPPLE', + 'extensions': ['GL_APPLE_vertex_array_object'] }], + 'arguments': 'GLuint array' }, { 'return_type': 'void', 'names': ['glLineWidth'], 'arguments': 'GLfloat width', }, @@ -476,8 +599,23 @@ 'arguments': 'GLenum target, GLintptr offset, GLsizeiptr length, GLenum access', }, { 'return_type': 'void', - 'names': ['glFlushMappedBufferRange'], - 'arguments': 'GLenum target, GLintptr offset, GLsizeiptr length', }, + 'known_as': 'glMatrixLoadfEXT', + 'versions': [{ 'name': 'glMatrixLoadfEXT', + 'gl_versions': ['gl4'], + 'extensions': ['GL_EXT_direct_state_access'] }, + { 'name': 'glMatrixLoadfEXT', + 'gl_versions': ['es3'], + 'extensions': ['GL_NV_path_rendering'] }], + 'arguments': 'GLenum matrixMode, const GLfloat* m' }, +{ 'return_type': 'void', + 'known_as': 'glMatrixLoadIdentityEXT', + 'versions': [{ 'name': 'glMatrixLoadIdentityEXT', + 'gl_versions': ['gl4'], + 'extensions': ['GL_EXT_direct_state_access'] }, + { 'name': 'glMatrixLoadIdentityEXT', + 'gl_versions': ['es3'], + 'extensions': ['GL_NV_path_rendering'] }], + 'arguments': 'GLenum matrixMode' }, { 'return_type': 'void', 'names': ['glPixelStorei'], 'arguments': 'GLenum pname, GLint param', }, @@ -533,9 +671,18 @@ # function name appearing multiple times. # This is the ES3 function, which requires explicit resolve: { 'return_type': 'void', + 'names': ['glRenderbufferStorageEXT', 'glRenderbufferStorage'], + 'arguments': + 'GLenum target, GLenum internalformat, GLsizei width, GLsizei height', }, +{ 'return_type': 'void', 'names': ['glRenderbufferStorageMultisample'], 'arguments': 'GLenum target, GLsizei samples, GLenum internalformat, ' 'GLsizei width, GLsizei height', }, +{ 'return_type': 'void', + 'names': ['glRenderbufferStorageMultisampleANGLE', + 'glRenderbufferStorageMultisample'], + 'arguments': 'GLenum target, GLsizei samples, GLenum internalformat, ' + 'GLsizei width, GLsizei height', }, # In desktop GL, EXT and core versions both have an explicit resolve step, # though desktop core GL implicitly resolves when drawing to a window. # TODO(oetuaho@nvidia.com): Right now this function also doubles as ES2 EXT @@ -547,25 +694,24 @@ 'arguments': 'GLenum target, GLsizei samples, GLenum internalformat, ' 'GLsizei width, GLsizei height', }, { 'return_type': 'void', - 'names': ['glRenderbufferStorageMultisampleANGLE', - 'glRenderbufferStorageMultisample'], - 'arguments': 'GLenum target, GLsizei samples, GLenum internalformat, ' - 'GLsizei width, GLsizei height', }, -{ 'return_type': 'void', 'names': ['glRenderbufferStorageMultisampleIMG'], 'arguments': 'GLenum target, GLsizei samples, GLenum internalformat, ' 'GLsizei width, GLsizei height', }, { 'return_type': 'void', - 'names': ['glRenderbufferStorageEXT', 'glRenderbufferStorage'], - 'arguments': - 'GLenum target, GLenum internalformat, GLsizei width, GLsizei height', }, -{ 'return_type': 'void', 'names': ['glSampleCoverage'], 'arguments': 'GLclampf value, GLboolean invert', }, { 'return_type': 'void', 'names': ['glScissor'], 'arguments': 'GLint x, GLint y, GLsizei width, GLsizei height', }, { 'return_type': 'void', + 'known_as': 'glSetFenceAPPLE', + 'versions': [{ 'name': 'glSetFenceAPPLE', + 'extensions': ['GL_APPLE_fence'] }], + 'arguments': 'GLuint fence', }, +{ 'return_type': 'void', + 'names': ['glSetFenceNV'], + 'arguments': 'GLuint fence, GLenum condition', }, +{ 'return_type': 'void', 'names': ['glShaderBinary'], 'arguments': 'GLsizei n, const GLuint* shaders, GLenum binaryformat, ' 'const void* binary, GLsizei length', }, @@ -607,6 +753,14 @@ { 'return_type': 'void', 'names': ['glStencilOpSeparate'], 'arguments': 'GLenum face, GLenum fail, GLenum zfail, GLenum zpass', }, +{ 'return_type': 'GLboolean', + 'known_as': 'glTestFenceAPPLE', + 'versions': [{ 'name': 'glTestFenceAPPLE', + 'extensions': ['GL_APPLE_fence'] }], + 'arguments': 'GLuint fence', }, +{ 'return_type': 'GLboolean', + 'names': ['glTestFenceNV'], + 'arguments': 'GLuint fence', }, { 'return_type': 'void', 'names': ['glTexImage2D'], 'arguments': @@ -736,150 +890,28 @@ 'names': ['glVertexAttrib4fv'], 'arguments': 'GLuint indx, const GLfloat* values', }, { 'return_type': 'void', - 'names': ['glVertexAttribPointer'], - 'arguments': 'GLuint indx, GLint size, GLenum type, GLboolean normalized, ' - 'GLsizei stride, const void* ptr', }, -{ 'return_type': 'void', - 'names': ['glViewport'], - 'arguments': 'GLint x, GLint y, GLsizei width, GLsizei height', }, -{ 'return_type': 'void', - 'names': ['glGenFencesNV'], - 'arguments': 'GLsizei n, GLuint* fences', }, -{ 'return_type': 'void', - 'names': ['glDeleteFencesNV'], - 'arguments': 'GLsizei n, const GLuint* fences', }, -{ 'return_type': 'void', - 'names': ['glSetFenceNV'], - 'arguments': 'GLuint fence, GLenum condition', }, -{ 'return_type': 'GLboolean', - 'names': ['glTestFenceNV'], - 'arguments': 'GLuint fence', }, -{ 'return_type': 'void', - 'names': ['glFinishFenceNV'], - 'arguments': 'GLuint fence', }, -{ 'return_type': 'GLboolean', - 'names': ['glIsFenceNV'], - 'arguments': 'GLuint fence', }, -{ 'return_type': 'void', - 'names': ['glGetFenceivNV'], - 'arguments': 'GLuint fence, GLenum pname, GLint* params', }, -{ 'return_type': 'GLsync', - 'names': ['glFenceSync'], - 'arguments': 'GLenum condition, GLbitfield flags', }, -{ 'return_type': 'GLboolean', - 'names': ['glIsSync'], - 'arguments': 'GLsync sync', }, -{ 'return_type': 'void', - 'names': ['glDeleteSync'], - 'arguments': 'GLsync sync', }, -{ 'return_type': 'void', - 'names': ['glGetSynciv'], - 'arguments': - 'GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length,' - 'GLint* values', }, -{ 'return_type': 'GLenum', - 'names': ['glClientWaitSync'], - 'arguments': - 'GLsync sync, GLbitfield flags, GLuint64 timeout', }, -{ 'return_type': 'GLenum', - 'names': ['glWaitSync'], - 'arguments': - 'GLsync sync, GLbitfield flags, GLuint64 timeout', }, -{ 'return_type': 'void', - 'known_as': 'glDrawArraysInstancedANGLE', - 'names': ['glDrawArraysInstancedARB', 'glDrawArraysInstancedANGLE', - 'glDrawArraysInstanced'], - 'arguments': 'GLenum mode, GLint first, GLsizei count, GLsizei primcount', }, -{ 'return_type': 'void', - 'known_as': 'glDrawElementsInstancedANGLE', - 'names': ['glDrawElementsInstancedARB', 'glDrawElementsInstancedANGLE', - 'glDrawElementsInstanced'], - 'arguments': - 'GLenum mode, GLsizei count, GLenum type, const void* indices, ' - 'GLsizei primcount', }, -{ 'return_type': 'void', 'known_as': 'glVertexAttribDivisorANGLE', 'names': ['glVertexAttribDivisorARB', 'glVertexAttribDivisorANGLE', 'glVertexAttribDivisor'], 'arguments': 'GLuint index, GLuint divisor', }, { 'return_type': 'void', - 'known_as': 'glGenVertexArraysOES', - 'versions': [{ 'name': 'glGenVertexArrays', - 'gl_versions': ['gl3', 'gl4', 'es3'] }, - { 'name': 'glGenVertexArrays', - 'extensions': ['GL_ARB_vertex_array_object'] }, - { 'name': 'glGenVertexArraysOES' }, - { 'name': 'glGenVertexArraysAPPLE', - 'extensions': ['GL_APPLE_vertex_array_object'] }], - 'arguments': 'GLsizei n, GLuint* arrays', }, + 'names': ['glVertexAttribPointer'], + 'arguments': 'GLuint indx, GLint size, GLenum type, GLboolean normalized, ' + 'GLsizei stride, const void* ptr', }, { 'return_type': 'void', - 'known_as': 'glDeleteVertexArraysOES', - 'versions': [{ 'name': 'glDeleteVertexArrays', - 'gl_versions': ['gl3', 'gl4', 'es3'] }, - { 'name': 'glDeleteVertexArrays', - 'extensions': ['GL_ARB_vertex_array_object'] }, - { 'name': 'glDeleteVertexArraysOES' }, - { 'name': 'glDeleteVertexArraysAPPLE', - 'extensions': ['GL_APPLE_vertex_array_object'] }], - 'arguments': 'GLsizei n, const GLuint* arrays' }, -{ 'return_type': 'void', - 'known_as': 'glBindVertexArrayOES', - 'versions': [{ 'name': 'glBindVertexArray', - 'gl_versions': ['gl3', 'gl4', 'es3'] }, - { 'name': 'glBindVertexArray', - 'extensions': ['GL_ARB_vertex_array_object'] }, - { 'name': 'glBindVertexArrayOES' }, - { 'name': 'glBindVertexArrayAPPLE', - 'extensions': ['GL_APPLE_vertex_array_object'] }], - 'arguments': 'GLuint array' }, -{ 'return_type': 'GLboolean', - 'known_as': 'glIsVertexArrayOES', - 'versions': [{ 'name': 'glIsVertexArray', - 'gl_versions': ['gl3', 'gl4'] }, - { 'name': 'glIsVertexArray', - 'extensions': ['GL_ARB_vertex_array_object'] }, - { 'name': 'glIsVertexArrayOES' }, - { 'name': 'glIsVertexArrayAPPLE', - 'extensions': ['GL_APPLE_vertex_array_object'] }], - 'arguments': 'GLuint array' }, -{ 'return_type': 'void', - 'known_as': 'glDiscardFramebufferEXT', - 'versions': [{ 'name': 'glInvalidateFramebuffer', - 'gl_versions': ['es3'], - 'extensions': [] }, - { 'name': 'glDiscardFramebufferEXT', - 'gl_versions': ['es1', 'es2'] }], - 'arguments': 'GLenum target, GLsizei numAttachments, ' - 'const GLenum* attachments' }, -{ 'return_type': 'void', - 'known_as': 'glMatrixLoadfEXT', - 'versions': [{ 'name': 'glMatrixLoadfEXT', - 'gl_versions': ['gl4'], - 'extensions': ['GL_EXT_direct_state_access'] }, - { 'name': 'glMatrixLoadfEXT', - 'gl_versions': ['es3'], - 'extensions': ['GL_NV_path_rendering'] }], - 'arguments': 'GLenum matrixMode, const GLfloat* m' }, -{ 'return_type': 'void', - 'known_as': 'glMatrixLoadIdentityEXT', - 'versions': [{ 'name': 'glMatrixLoadIdentityEXT', - 'gl_versions': ['gl4'], - 'extensions': ['GL_EXT_direct_state_access'] }, - { 'name': 'glMatrixLoadIdentityEXT', - 'gl_versions': ['es3'], - 'extensions': ['GL_NV_path_rendering'] }], - 'arguments': 'GLenum matrixMode' }, - { 'return_type': 'void', - 'known_as': 'glBlendBarrierKHR', - 'versions': [{ 'name': 'glBlendBarrierNV', - 'extensions': ['GL_NV_blend_equation_advanced'] }, - { 'name': 'glBlendBarrierKHR', - 'extensions': ['GL_KHR_blend_equation_advanced'] }], - 'arguments': 'void' }, + 'names': ['glViewport'], + 'arguments': 'GLint x, GLint y, GLsizei width, GLsizei height', }, +{ 'return_type': 'GLenum', + 'names': ['glWaitSync'], + 'arguments': + 'GLsync sync, GLbitfield flags, GLuint64 timeout', }, ] OSMESA_FUNCTIONS = [ +{ 'return_type': 'void', + 'names': ['OSMesaColorClamp'], + 'arguments': 'GLboolean enable', }, { 'return_type': 'OSMesaContext', 'names': ['OSMesaCreateContext'], 'arguments': 'GLenum format, OSMesaContext sharelist', }, @@ -892,69 +924,56 @@ 'names': ['OSMesaDestroyContext'], 'arguments': 'OSMesaContext ctx', }, { 'return_type': 'GLboolean', - 'names': ['OSMesaMakeCurrent'], - 'arguments': 'OSMesaContext ctx, void* buffer, GLenum type, GLsizei width, ' - 'GLsizei height', }, + 'names': ['OSMesaGetColorBuffer'], + 'arguments': 'OSMesaContext c, GLint* width, GLint* height, GLint* format, ' + 'void** buffer', }, { 'return_type': 'OSMesaContext', 'names': ['OSMesaGetCurrentContext'], 'arguments': 'void', }, -{ 'return_type': 'void', - 'names': ['OSMesaPixelStore'], - 'arguments': 'GLint pname, GLint value', }, -{ 'return_type': 'void', - 'names': ['OSMesaGetIntegerv'], - 'arguments': 'GLint pname, GLint* value', }, { 'return_type': 'GLboolean', 'names': ['OSMesaGetDepthBuffer'], 'arguments': 'OSMesaContext c, GLint* width, GLint* height, GLint* bytesPerValue, ' 'void** buffer', }, -{ 'return_type': 'GLboolean', - 'names': ['OSMesaGetColorBuffer'], - 'arguments': 'OSMesaContext c, GLint* width, GLint* height, GLint* format, ' - 'void** buffer', }, +{ 'return_type': 'void', + 'names': ['OSMesaGetIntegerv'], + 'arguments': 'GLint pname, GLint* value', }, { 'return_type': 'OSMESAproc', 'names': ['OSMesaGetProcAddress'], 'arguments': 'const char* funcName', }, +{ 'return_type': 'GLboolean', + 'names': ['OSMesaMakeCurrent'], + 'arguments': 'OSMesaContext ctx, void* buffer, GLenum type, GLsizei width, ' + 'GLsizei height', }, { 'return_type': 'void', - 'names': ['OSMesaColorClamp'], - 'arguments': 'GLboolean enable', }, + 'names': ['OSMesaPixelStore'], + 'arguments': 'GLint pname, GLint value', }, ] EGL_FUNCTIONS = [ -{ 'return_type': 'EGLint', - 'names': ['eglGetError'], - 'arguments': 'void', }, -{ 'return_type': 'EGLDisplay', - 'names': ['eglGetDisplay'], - 'arguments': 'EGLNativeDisplayType display_id', }, -{ 'return_type': 'EGLDisplay', - 'known_as': 'eglGetPlatformDisplayEXT', - 'versions': [{ 'name': 'eglGetPlatformDisplayEXT', - 'extensions': ['EGL_ANGLE_platform_angle'] }], - 'arguments': 'EGLenum platform, void* native_display, ' - 'const EGLint* attrib_list', }, { 'return_type': 'EGLBoolean', - 'names': ['eglInitialize'], - 'arguments': 'EGLDisplay dpy, EGLint* major, EGLint* minor', }, + 'names': ['eglBindAPI'], + 'arguments': 'EGLenum api', }, { 'return_type': 'EGLBoolean', - 'names': ['eglTerminate'], - 'arguments': 'EGLDisplay dpy', }, -{ 'return_type': 'const char*', - 'names': ['eglQueryString'], - 'arguments': 'EGLDisplay dpy, EGLint name', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglGetConfigs'], - 'arguments': 'EGLDisplay dpy, EGLConfig* configs, EGLint config_size, ' - 'EGLint* num_config', }, + 'names': ['eglBindTexImage'], + 'arguments': 'EGLDisplay dpy, EGLSurface surface, EGLint buffer', }, { 'return_type': 'EGLBoolean', 'names': ['eglChooseConfig'], 'arguments': 'EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, ' 'EGLint config_size, EGLint* num_config', }, +{ 'return_type': 'EGLint', + 'versions': [{ 'name': 'eglClientWaitSyncKHR', + 'extensions': ['EGL_KHR_fence_sync'] }], + 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, ' + 'EGLTimeKHR timeout' }, { 'return_type': 'EGLBoolean', - 'names': ['eglGetConfigAttrib'], + 'names': ['eglCopyBuffers'], 'arguments': - 'EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value', }, + 'EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target', }, +{ 'return_type': 'EGLContext', + 'names': ['eglCreateContext'], + 'arguments': 'EGLDisplay dpy, EGLConfig config, EGLContext share_context, ' + 'const EGLint* attrib_list', }, { 'return_type': 'EGLImageKHR', 'versions': [{ 'name': 'eglCreateImageKHR', 'extensions': @@ -962,14 +981,11 @@ 'arguments': 'EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, ' 'const EGLint* attrib_list' }, -{ 'return_type': 'EGLBoolean', - 'versions': [{ 'name' : 'eglDestroyImageKHR', - 'extensions': ['EGL_KHR_image_base'] }], - 'arguments': 'EGLDisplay dpy, EGLImageKHR image' }, { 'return_type': 'EGLSurface', - 'names': ['eglCreateWindowSurface'], - 'arguments': 'EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, ' - 'const EGLint* attrib_list', }, + 'names': ['eglCreatePbufferFromClientBuffer'], + 'arguments': + 'EGLDisplay dpy, EGLenum buftype, void* buffer, EGLConfig config, ' + 'const EGLint* attrib_list', }, { 'return_type': 'EGLSurface', 'names': ['eglCreatePbufferSurface'], 'arguments': 'EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list', }, @@ -977,144 +993,184 @@ 'names': ['eglCreatePixmapSurface'], 'arguments': 'EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, ' 'const EGLint* attrib_list', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglDestroySurface'], - 'arguments': 'EGLDisplay dpy, EGLSurface surface', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglQuerySurface'], - 'arguments': - 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglBindAPI'], - 'arguments': 'EGLenum api', }, -{ 'return_type': 'EGLenum', - 'names': ['eglQueryAPI'], - 'arguments': 'void', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglWaitClient'], - 'arguments': 'void', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglReleaseThread'], - 'arguments': 'void', }, +{ 'return_type': 'EGLSyncKHR', + 'versions': [{ 'name': 'eglCreateSyncKHR', + 'extensions': ['EGL_KHR_fence_sync'] }], + 'arguments': 'EGLDisplay dpy, EGLenum type, const EGLint* attrib_list' }, { 'return_type': 'EGLSurface', - 'names': ['eglCreatePbufferFromClientBuffer'], - 'arguments': - 'EGLDisplay dpy, EGLenum buftype, void* buffer, EGLConfig config, ' - 'const EGLint* attrib_list', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglSurfaceAttrib'], - 'arguments': - 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglBindTexImage'], - 'arguments': 'EGLDisplay dpy, EGLSurface surface, EGLint buffer', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglReleaseTexImage'], - 'arguments': 'EGLDisplay dpy, EGLSurface surface, EGLint buffer', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglSwapInterval'], - 'arguments': 'EGLDisplay dpy, EGLint interval', }, -{ 'return_type': 'EGLContext', - 'names': ['eglCreateContext'], - 'arguments': 'EGLDisplay dpy, EGLConfig config, EGLContext share_context, ' - 'const EGLint* attrib_list', }, + 'names': ['eglCreateWindowSurface'], + 'arguments': 'EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, ' + 'const EGLint* attrib_list', }, { 'return_type': 'EGLBoolean', 'names': ['eglDestroyContext'], 'arguments': 'EGLDisplay dpy, EGLContext ctx', }, { 'return_type': 'EGLBoolean', - 'names': ['eglMakeCurrent'], + 'versions': [{ 'name' : 'eglDestroyImageKHR', + 'extensions': ['EGL_KHR_image_base'] }], + 'arguments': 'EGLDisplay dpy, EGLImageKHR image' }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglDestroySurface'], + 'arguments': 'EGLDisplay dpy, EGLSurface surface', }, +{ 'return_type': 'EGLBoolean', + 'versions': [{ 'name': 'eglDestroySyncKHR', + 'extensions': ['EGL_KHR_fence_sync'] }], + 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync' }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglGetConfigAttrib'], 'arguments': - 'EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx', }, + 'EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglGetConfigs'], + 'arguments': 'EGLDisplay dpy, EGLConfig* configs, EGLint config_size, ' + 'EGLint* num_config', }, { 'return_type': 'EGLContext', 'names': ['eglGetCurrentContext'], 'arguments': 'void', }, +{ 'return_type': 'EGLDisplay', + 'names': ['eglGetCurrentDisplay'], + 'arguments': 'void', }, { 'return_type': 'EGLSurface', 'names': ['eglGetCurrentSurface'], 'arguments': 'EGLint readdraw', }, { 'return_type': 'EGLDisplay', - 'names': ['eglGetCurrentDisplay'], + 'names': ['eglGetDisplay'], + 'arguments': 'EGLNativeDisplayType display_id', }, +{ 'return_type': 'EGLint', + 'names': ['eglGetError'], 'arguments': 'void', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglQueryContext'], - 'arguments': - 'EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglWaitGL'], - 'arguments': 'void', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglWaitNative'], - 'arguments': 'EGLint engine', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglSwapBuffers'], - 'arguments': 'EGLDisplay dpy, EGLSurface surface', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglCopyBuffers'], - 'arguments': - 'EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target', }, +{ 'return_type': 'EGLDisplay', + 'known_as': 'eglGetPlatformDisplayEXT', + 'versions': [{ 'name': 'eglGetPlatformDisplayEXT', + 'extensions': ['EGL_ANGLE_platform_angle'] }], + 'arguments': 'EGLenum platform, void* native_display, ' + 'const EGLint* attrib_list', }, { 'return_type': '__eglMustCastToProperFunctionPointerType', 'names': ['eglGetProcAddress'], 'arguments': 'const char* procname', }, { 'return_type': 'EGLBoolean', - 'names': ['eglPostSubBufferNV'], - 'arguments': 'EGLDisplay dpy, EGLSurface surface, ' - 'EGLint x, EGLint y, EGLint width, EGLint height', }, -{ 'return_type': 'EGLBoolean', - 'names': ['eglQuerySurfacePointerANGLE'], - 'arguments': - 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value', }, -{ 'return_type': 'EGLSyncKHR', - 'versions': [{ 'name': 'eglCreateSyncKHR', - 'extensions': ['EGL_KHR_fence_sync'] }], - 'arguments': 'EGLDisplay dpy, EGLenum type, const EGLint* attrib_list' }, -{ 'return_type': 'EGLint', - 'versions': [{ 'name': 'eglClientWaitSyncKHR', - 'extensions': ['EGL_KHR_fence_sync'] }], - 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, ' - 'EGLTimeKHR timeout' }, -{ 'return_type': 'EGLBoolean', 'versions': [{ 'name': 'eglGetSyncAttribKHR', 'extensions': ['EGL_KHR_fence_sync'] }], 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, ' 'EGLint* value' }, { 'return_type': 'EGLBoolean', - 'versions': [{ 'name': 'eglDestroySyncKHR', - 'extensions': ['EGL_KHR_fence_sync'] }], - 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync' }, -{ 'return_type': 'EGLBoolean', 'names': ['eglGetSyncValuesCHROMIUM'], 'arguments': 'EGLDisplay dpy, EGLSurface surface, ' 'EGLuint64CHROMIUM* ust, EGLuint64CHROMIUM* msc, ' 'EGLuint64CHROMIUM* sbc', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglInitialize'], + 'arguments': 'EGLDisplay dpy, EGLint* major, EGLint* minor', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglMakeCurrent'], + 'arguments': + 'EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglPostSubBufferNV'], + 'arguments': 'EGLDisplay dpy, EGLSurface surface, ' + 'EGLint x, EGLint y, EGLint width, EGLint height', }, +{ 'return_type': 'EGLenum', + 'names': ['eglQueryAPI'], + 'arguments': 'void', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglQueryContext'], + 'arguments': + 'EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value', }, +{ 'return_type': 'const char*', + 'names': ['eglQueryString'], + 'arguments': 'EGLDisplay dpy, EGLint name', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglQuerySurface'], + 'arguments': + 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglQuerySurfacePointerANGLE'], + 'arguments': + 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglReleaseTexImage'], + 'arguments': 'EGLDisplay dpy, EGLSurface surface, EGLint buffer', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglReleaseThread'], + 'arguments': 'void', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglSurfaceAttrib'], + 'arguments': + 'EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglSwapBuffers'], + 'arguments': 'EGLDisplay dpy, EGLSurface surface', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglSwapInterval'], + 'arguments': 'EGLDisplay dpy, EGLint interval', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglTerminate'], + 'arguments': 'EGLDisplay dpy', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglWaitClient'], + 'arguments': 'void', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglWaitGL'], + 'arguments': 'void', }, +{ 'return_type': 'EGLBoolean', + 'names': ['eglWaitNative'], + 'arguments': 'EGLint engine', }, { 'return_type': 'EGLint', 'versions': [{ 'name': 'eglWaitSyncKHR', 'extensions': ['EGL_KHR_fence_sync', 'EGL_KHR_wait_sync'] }], - 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags' } + 'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags' }, ] WGL_FUNCTIONS = [ +{ 'return_type': 'BOOL', + 'names': ['wglChoosePixelFormatARB'], + 'arguments': + 'HDC dc, const int* int_attrib_list, const float* float_attrib_list, ' + 'UINT max_formats, int* formats, UINT* num_formats', }, +{ 'return_type': 'BOOL', + 'names': ['wglCopyContext'], + 'arguments': 'HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask', }, { 'return_type': 'HGLRC', 'names': ['wglCreateContext'], 'arguments': 'HDC hdc', }, { 'return_type': 'HGLRC', 'names': ['wglCreateLayerContext'], 'arguments': 'HDC hdc, int iLayerPlane', }, -{ 'return_type': 'BOOL', - 'names': ['wglCopyContext'], - 'arguments': 'HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask', }, +{ 'return_type': 'HPBUFFERARB', + 'names': ['wglCreatePbufferARB'], + 'arguments': 'HDC hDC, int iPixelFormat, int iWidth, int iHeight, ' + 'const int* piAttribList', }, { 'return_type': 'BOOL', 'names': ['wglDeleteContext'], 'arguments': 'HGLRC hglrc', }, +{ 'return_type': 'BOOL', + 'names': ['wglDestroyPbufferARB'], + 'arguments': 'HPBUFFERARB hPbuffer', }, { 'return_type': 'HGLRC', 'names': ['wglGetCurrentContext'], 'arguments': '', }, { 'return_type': 'HDC', 'names': ['wglGetCurrentDC'], 'arguments': '', }, +{ 'return_type': 'const char*', + 'names': ['wglGetExtensionsStringARB'], + 'arguments': 'HDC hDC', }, +{ 'return_type': 'const char*', + 'names': ['wglGetExtensionsStringEXT'], + 'arguments': '', }, +{ 'return_type': 'HDC', + 'names': ['wglGetPbufferDCARB'], + 'arguments': 'HPBUFFERARB hPbuffer', }, { 'return_type': 'BOOL', 'names': ['wglMakeCurrent'], 'arguments': 'HDC hdc, HGLRC hglrc', }, { 'return_type': 'BOOL', + 'names': ['wglQueryPbufferARB'], + 'arguments': 'HPBUFFERARB hPbuffer, int iAttribute, int* piValue', }, +{ 'return_type': 'int', + 'names': ['wglReleasePbufferDCARB'], + 'arguments': 'HPBUFFERARB hPbuffer, HDC hDC', }, +{ 'return_type': 'BOOL', 'names': ['wglShareLists'], 'arguments': 'HGLRC hglrc1, HGLRC hglrc2', }, { 'return_type': 'BOOL', @@ -1123,43 +1179,25 @@ { 'return_type': 'BOOL', 'names': ['wglSwapLayerBuffers'], 'arguments': 'HDC hdc, UINT fuPlanes', }, -{ 'return_type': 'const char*', - 'names': ['wglGetExtensionsStringARB'], - 'arguments': 'HDC hDC', }, -{ 'return_type': 'const char*', - 'names': ['wglGetExtensionsStringEXT'], - 'arguments': '', }, -{ 'return_type': 'BOOL', - 'names': ['wglChoosePixelFormatARB'], - 'arguments': - 'HDC dc, const int* int_attrib_list, const float* float_attrib_list, ' - 'UINT max_formats, int* formats, UINT* num_formats', }, -{ 'return_type': 'HPBUFFERARB', - 'names': ['wglCreatePbufferARB'], - 'arguments': 'HDC hDC, int iPixelFormat, int iWidth, int iHeight, ' - 'const int* piAttribList', }, -{ 'return_type': 'HDC', - 'names': ['wglGetPbufferDCARB'], - 'arguments': 'HPBUFFERARB hPbuffer', }, -{ 'return_type': 'int', - 'names': ['wglReleasePbufferDCARB'], - 'arguments': 'HPBUFFERARB hPbuffer, HDC hDC', }, -{ 'return_type': 'BOOL', - 'names': ['wglDestroyPbufferARB'], - 'arguments': 'HPBUFFERARB hPbuffer', }, -{ 'return_type': 'BOOL', - 'names': ['wglQueryPbufferARB'], - 'arguments': 'HPBUFFERARB hPbuffer, int iAttribute, int* piValue', }, ] GLX_FUNCTIONS = [ -{ 'return_type': 'int', - 'names': ['glXWaitVideoSyncSGI'], - 'arguments': 'int divisor, int remainder, unsigned int* count', }, +{ 'return_type': 'void', + 'names': ['glXBindTexImageEXT'], + 'arguments': + 'Display* dpy, GLXDrawable drawable, int buffer, int* attribList', }, +{ 'return_type': 'GLXFBConfig*', + 'names': ['glXChooseFBConfig'], + 'arguments': + 'Display* dpy, int screen, const int* attribList, int* nitems', }, { 'return_type': 'XVisualInfo*', 'names': ['glXChooseVisual'], 'arguments': 'Display* dpy, int screen, int* attribList', }, { 'return_type': 'void', + 'names': ['glXCopyContext'], + 'arguments': + 'Display* dpy, GLXContext src, GLXContext dst, unsigned long mask', }, +{ 'return_type': 'void', 'names': ['glXCopySubBufferMESA'], 'arguments': 'Display* dpy, GLXDrawable drawable, ' 'int x, int y, int width, int height', }, @@ -1167,152 +1205,143 @@ 'names': ['glXCreateContext'], 'arguments': 'Display* dpy, XVisualInfo* vis, GLXContext shareList, int direct', }, -{ 'return_type': 'void', - 'names': ['glXBindTexImageEXT'], +{ 'return_type': 'GLXContext', + 'names': ['glXCreateContextAttribsARB'], 'arguments': - 'Display* dpy, GLXDrawable drawable, int buffer, int* attribList', }, -{ 'return_type': 'void', - 'names': ['glXReleaseTexImageEXT'], - 'arguments': 'Display* dpy, GLXDrawable drawable, int buffer', }, -{ 'return_type': 'void', - 'names': ['glXDestroyContext'], - 'arguments': 'Display* dpy, GLXContext ctx', }, -{ 'return_type': 'int', - 'names': ['glXMakeCurrent'], - 'arguments': 'Display* dpy, GLXDrawable drawable, GLXContext ctx', }, -{ 'return_type': 'void', - 'names': ['glXCopyContext'], - 'arguments': - 'Display* dpy, GLXContext src, GLXContext dst, unsigned long mask', }, -{ 'return_type': 'void', - 'names': ['glXSwapBuffers'], - 'arguments': 'Display* dpy, GLXDrawable drawable', }, + 'Display* dpy, GLXFBConfig config, GLXContext share_context, int direct, ' + 'const int* attrib_list', }, { 'return_type': 'GLXPixmap', 'names': ['glXCreateGLXPixmap'], 'arguments': 'Display* dpy, XVisualInfo* visual, Pixmap pixmap', }, +{ 'return_type': 'GLXContext', + 'names': ['glXCreateNewContext'], + 'arguments': 'Display* dpy, GLXFBConfig config, int renderType, ' + 'GLXContext shareList, int direct', }, +{ 'return_type': 'GLXPbuffer', + 'names': ['glXCreatePbuffer'], + 'arguments': 'Display* dpy, GLXFBConfig config, const int* attribList', }, +{ 'return_type': 'GLXPixmap', + 'names': ['glXCreatePixmap'], + 'arguments': 'Display* dpy, GLXFBConfig config, ' + 'Pixmap pixmap, const int* attribList', }, +{ 'return_type': 'GLXWindow', + 'names': ['glXCreateWindow'], + 'arguments': + 'Display* dpy, GLXFBConfig config, Window win, const int* attribList', }, +{ 'return_type': 'void', + 'names': ['glXDestroyContext'], + 'arguments': 'Display* dpy, GLXContext ctx', }, { 'return_type': 'void', 'names': ['glXDestroyGLXPixmap'], 'arguments': 'Display* dpy, GLXPixmap pixmap', }, -{ 'return_type': 'int', - 'names': ['glXQueryExtension'], - 'arguments': 'Display* dpy, int* errorb, int* event', }, -{ 'return_type': 'int', - 'names': ['glXQueryVersion'], - 'arguments': 'Display* dpy, int* maj, int* min', }, -{ 'return_type': 'int', - 'names': ['glXIsDirect'], - 'arguments': 'Display* dpy, GLXContext ctx', }, +{ 'return_type': 'void', + 'names': ['glXDestroyPbuffer'], + 'arguments': 'Display* dpy, GLXPbuffer pbuf', }, +{ 'return_type': 'void', + 'names': ['glXDestroyPixmap'], + 'arguments': 'Display* dpy, GLXPixmap pixmap', }, +{ 'return_type': 'void', + 'names': ['glXDestroyWindow'], + 'arguments': 'Display* dpy, GLXWindow window', }, +{ 'return_type': 'const char*', + 'names': ['glXGetClientString'], + 'arguments': 'Display* dpy, int name', }, { 'return_type': 'int', 'names': ['glXGetConfig'], 'arguments': 'Display* dpy, XVisualInfo* visual, int attrib, int* value', }, { 'return_type': 'GLXContext', 'names': ['glXGetCurrentContext'], 'arguments': 'void', }, +{ 'return_type': 'Display*', + 'names': ['glXGetCurrentDisplay'], + 'arguments': 'void', }, { 'return_type': 'GLXDrawable', 'names': ['glXGetCurrentDrawable'], 'arguments': 'void', }, -{ 'return_type': 'void', - 'names': ['glXWaitGL'], +{ 'return_type': 'GLXDrawable', + 'names': ['glXGetCurrentReadDrawable'], 'arguments': 'void', }, +{ 'return_type': 'int', + 'names': ['glXGetFBConfigAttrib'], + 'arguments': 'Display* dpy, GLXFBConfig config, int attribute, int* value', }, +{ 'return_type': 'GLXFBConfig', + 'names': ['glXGetFBConfigFromVisualSGIX'], + 'arguments': 'Display* dpy, XVisualInfo* visualInfo', }, +{ 'return_type': 'GLXFBConfig*', + 'names': ['glXGetFBConfigs'], + 'arguments': 'Display* dpy, int screen, int* nelements', }, +{ 'return_type': 'bool', + 'names': ['glXGetMscRateOML'], + 'arguments': + 'Display* dpy, GLXDrawable drawable, int32* numerator, ' + 'int32* denominator' }, { 'return_type': 'void', - 'names': ['glXWaitX'], - 'arguments': 'void', }, + 'names': ['glXGetSelectedEvent'], + 'arguments': 'Display* dpy, GLXDrawable drawable, unsigned long* mask', }, +{ 'return_type': 'bool', + 'names': ['glXGetSyncValuesOML'], + 'arguments': + 'Display* dpy, GLXDrawable drawable, int64* ust, int64* msc, ' + 'int64* sbc' }, +{ 'return_type': 'XVisualInfo*', + 'names': ['glXGetVisualFromFBConfig'], + 'arguments': 'Display* dpy, GLXFBConfig config', }, +{ 'return_type': 'int', + 'names': ['glXIsDirect'], + 'arguments': 'Display* dpy, GLXContext ctx', }, +{ 'return_type': 'int', + 'names': ['glXMakeContextCurrent'], + 'arguments': + 'Display* dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx', }, +{ 'return_type': 'int', + 'names': ['glXMakeCurrent'], + 'arguments': 'Display* dpy, GLXDrawable drawable, GLXContext ctx', }, +{ 'return_type': 'int', + 'names': ['glXQueryContext'], + 'arguments': 'Display* dpy, GLXContext ctx, int attribute, int* value', }, { 'return_type': 'void', - 'names': ['glXUseXFont'], - 'arguments': 'Font font, int first, int count, int list', }, + 'names': ['glXQueryDrawable'], + 'arguments': + 'Display* dpy, GLXDrawable draw, int attribute, unsigned int* value', }, +{ 'return_type': 'int', + 'names': ['glXQueryExtension'], + 'arguments': 'Display* dpy, int* errorb, int* event', }, { 'return_type': 'const char*', 'names': ['glXQueryExtensionsString'], 'arguments': 'Display* dpy, int screen', }, { 'return_type': 'const char*', 'names': ['glXQueryServerString'], 'arguments': 'Display* dpy, int screen, int name', }, -{ 'return_type': 'const char*', - 'names': ['glXGetClientString'], - 'arguments': 'Display* dpy, int name', }, -{ 'return_type': 'Display*', - 'names': ['glXGetCurrentDisplay'], - 'arguments': 'void', }, -{ 'return_type': 'GLXFBConfig*', - 'names': ['glXChooseFBConfig'], - 'arguments': - 'Display* dpy, int screen, const int* attribList, int* nitems', }, { 'return_type': 'int', - 'names': ['glXGetFBConfigAttrib'], - 'arguments': 'Display* dpy, GLXFBConfig config, int attribute, int* value', }, -{ 'return_type': 'GLXFBConfig*', - 'names': ['glXGetFBConfigs'], - 'arguments': 'Display* dpy, int screen, int* nelements', }, -{ 'return_type': 'XVisualInfo*', - 'names': ['glXGetVisualFromFBConfig'], - 'arguments': 'Display* dpy, GLXFBConfig config', }, -{ 'return_type': 'GLXWindow', - 'names': ['glXCreateWindow'], - 'arguments': - 'Display* dpy, GLXFBConfig config, Window win, const int* attribList', }, + 'names': ['glXQueryVersion'], + 'arguments': 'Display* dpy, int* maj, int* min', }, { 'return_type': 'void', - 'names': ['glXDestroyWindow'], - 'arguments': 'Display* dpy, GLXWindow window', }, -{ 'return_type': 'GLXPixmap', - 'names': ['glXCreatePixmap'], - 'arguments': 'Display* dpy, GLXFBConfig config, ' - 'Pixmap pixmap, const int* attribList', }, -{ 'return_type': 'void', - 'names': ['glXDestroyPixmap'], - 'arguments': 'Display* dpy, GLXPixmap pixmap', }, -{ 'return_type': 'GLXPbuffer', - 'names': ['glXCreatePbuffer'], - 'arguments': 'Display* dpy, GLXFBConfig config, const int* attribList', }, -{ 'return_type': 'void', - 'names': ['glXDestroyPbuffer'], - 'arguments': 'Display* dpy, GLXPbuffer pbuf', }, -{ 'return_type': 'void', - 'names': ['glXQueryDrawable'], - 'arguments': - 'Display* dpy, GLXDrawable draw, int attribute, unsigned int* value', }, -{ 'return_type': 'GLXContext', - 'names': ['glXCreateNewContext'], - 'arguments': 'Display* dpy, GLXFBConfig config, int renderType, ' - 'GLXContext shareList, int direct', }, -{ 'return_type': 'int', - 'names': ['glXMakeContextCurrent'], - 'arguments': - 'Display* dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx', }, -{ 'return_type': 'GLXDrawable', - 'names': ['glXGetCurrentReadDrawable'], - 'arguments': 'void', }, -{ 'return_type': 'int', - 'names': ['glXQueryContext'], - 'arguments': 'Display* dpy, GLXContext ctx, int attribute, int* value', }, + 'names': ['glXReleaseTexImageEXT'], + 'arguments': 'Display* dpy, GLXDrawable drawable, int buffer', }, { 'return_type': 'void', 'names': ['glXSelectEvent'], 'arguments': 'Display* dpy, GLXDrawable drawable, unsigned long mask', }, { 'return_type': 'void', - 'names': ['glXGetSelectedEvent'], - 'arguments': 'Display* dpy, GLXDrawable drawable, unsigned long* mask', }, + 'names': ['glXSwapBuffers'], + 'arguments': 'Display* dpy, GLXDrawable drawable', }, +{ 'return_type': 'void', + 'names': ['glXSwapIntervalEXT'], + 'arguments': 'Display* dpy, GLXDrawable drawable, int interval', }, { 'return_type': 'void', 'names': ['glXSwapIntervalMESA'], 'arguments': 'unsigned int interval', }, { 'return_type': 'void', - 'names': ['glXSwapIntervalEXT'], - 'arguments': 'Display* dpy, GLXDrawable drawable, int interval', }, -{ 'return_type': 'GLXFBConfig', - 'names': ['glXGetFBConfigFromVisualSGIX'], - 'arguments': 'Display* dpy, XVisualInfo* visualInfo', }, -{ 'return_type': 'GLXContext', - 'names': ['glXCreateContextAttribsARB'], - 'arguments': - 'Display* dpy, GLXFBConfig config, GLXContext share_context, int direct, ' - 'const int* attrib_list', }, -{ 'return_type': 'bool', - 'names': ['glXGetSyncValuesOML'], - 'arguments': - 'Display* dpy, GLXDrawable drawable, int64* ust, int64* msc, ' - 'int64* sbc' }, -{ 'return_type': 'bool', - 'names': ['glXGetMscRateOML'], - 'arguments': - 'Display* dpy, GLXDrawable drawable, int32* numerator, ' - 'int32* denominator' }, + 'names': ['glXUseXFont'], + 'arguments': 'Font font, int first, int count, int list', }, +{ 'return_type': 'void', + 'names': ['glXWaitGL'], + 'arguments': 'void', }, +{ 'return_type': 'int', + 'names': ['glXWaitVideoSyncSGI'], + 'arguments': 'int divisor, int remainder, unsigned int* count', }, +{ 'return_type': 'void', + 'names': ['glXWaitX'], + 'arguments': 'void', }, ] FUNCTION_SETS = [ @@ -1339,6 +1368,7 @@ [GLX_FUNCTIONS, 'glx', ['GL/glx.h', 'GL/glxext.h'], []], ] + def GenerateHeader(file, functions, set_name, used_extensions): """Generates gl_bindings_autogen_x.h""" @@ -2028,6 +2058,7 @@ parser = optparse.OptionParser() parser.add_option('--inputs', action='store_true') parser.add_option('--header-paths') + parser.add_option('--verify-order', action='store_true') options, args = parser.parse_args(argv) @@ -2056,6 +2087,16 @@ if 'names' in func: del func['names'] + # Check function names in each set is sorted in alphabetical order. + for index in range(len(functions) - 1): + func_name = functions[index]['known_as'] + next_func_name = functions[index + 1]['known_as'] + if func_name.lower() > next_func_name.lower(): + raise Exception( + 'function %s is not in alphabetical order' % next_func_name) + if options.verify_order: + continue + extension_headers = [ResolveHeader(h, options.header_paths) for h in extension_headers] used_extensions = FillExtensionsFromHeaders( @@ -2077,20 +2118,21 @@ GenerateSource(source_file, functions, set_name, used_extensions) source_file.close() - header_file = open( - os.path.join(directory, 'gl_mock_autogen_gl.h'), 'wb') - GenerateMockHeader(header_file, GL_FUNCTIONS, 'gl') - header_file.close() + if not options.verify_order: + header_file = open( + os.path.join(directory, 'gl_mock_autogen_gl.h'), 'wb') + GenerateMockHeader(header_file, GL_FUNCTIONS, 'gl') + header_file.close() - header_file = open(os.path.join(directory, 'gl_bindings_autogen_mock.h'), - 'wb') - GenerateMockBindingsHeader(header_file, GL_FUNCTIONS) - header_file.close() + header_file = open(os.path.join(directory, 'gl_bindings_autogen_mock.h'), + 'wb') + GenerateMockBindingsHeader(header_file, GL_FUNCTIONS) + header_file.close() - source_file = open(os.path.join(directory, 'gl_bindings_autogen_mock.cc'), - 'wb') - GenerateMockBindingsSource(source_file, GL_FUNCTIONS) - source_file.close() + source_file = open(os.path.join(directory, 'gl_bindings_autogen_mock.cc'), + 'wb') + GenerateMockBindingsSource(source_file, GL_FUNCTIONS) + source_file.close() return 0
diff --git a/ui/gl/gl.gyp b/ui/gl/gl.gyp index cd0a852..cd33d7a 100644 --- a/ui/gl/gl.gyp +++ b/ui/gl/gl.gyp
@@ -263,6 +263,8 @@ 'sources': [ 'gl_context_cgl.cc', 'gl_context_cgl.h', + 'gl_fence_apple.cc', + 'gl_fence_apple.h', 'gl_image_io_surface.cc', 'gl_image_io_surface.h', 'scoped_cgl.cc', @@ -310,14 +312,6 @@ '../android/ui_android.gyp:ui_java', ], }], - ['ubsan==1', { - # Due to a bug in LLVM (http://llvm.org/bugs/show_bug.cgi?id=21349), - # compilation hangs for some GL source files. Disable -O2 temporarily - # until http://crbug.com/426271 is fixed. - 'cflags!': [ - '-O2', - ], - }], ], }, {
diff --git a/ui/gl/gl_bindings.h b/ui/gl/gl_bindings.h index dd737f4..aea47fc 100644 --- a/ui/gl/gl_bindings.h +++ b/ui/gl/gl_bindings.h
@@ -143,6 +143,10 @@ #define GL_OVERLAY_TRANSFORM_ROTATE_180_CHROMIUM 0x9249 #define GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM 0x924A +// GL_CHROMIUM_subscribe_uniforms +#define GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM 0x924B +#define GL_MOUSE_POSITION_CHROMIUM 0x924C + // GL_OES_texure_3D #define GL_SAMPLER_3D_OES 0x8B5F
diff --git a/ui/gl/gl_context_egl.cc b/ui/gl/gl_context_egl.cc index 41c10dc..733ad8d 100644 --- a/ui/gl/gl_context_egl.cc +++ b/ui/gl/gl_context_egl.cc
@@ -183,8 +183,6 @@ if (!eglSwapInterval(display_, interval)) { LOG(ERROR) << "eglSwapInterval failed with error " << GetLastEGLErrorString(); - } else { - GLSurface::GetCurrent()->SetSwapInterval(interval); } }
diff --git a/ui/gl/gl_fence.cc b/ui/gl/gl_fence.cc index 5186f23..c254411 100644 --- a/ui/gl/gl_fence.cc +++ b/ui/gl/gl_fence.cc
@@ -13,6 +13,10 @@ #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_version_info.h" +#if defined(OS_MACOSX) +#include "ui/gl/gl_fence_apple.h" +#endif + namespace gfx { namespace { @@ -27,7 +31,10 @@ if (g_driver_gl.ext.b_GL_ARB_sync || GetGLVersionInfo()->is_es3) { fence.reset(new GLFenceARB(flush)); -#if !defined(OS_MACOSX) +#if defined(OS_MACOSX) + } else if (g_driver_gl.ext.b_GL_APPLE_fence) { + fence.reset(new GLFenceAPPLE(flush)); +#else } else if (g_driver_egl.ext.b_EGL_KHR_fence_sync) { fence.reset(new GLFenceEGL(flush)); #endif @@ -50,7 +57,9 @@ bool GLFence::IsSupported() { DCHECK(GetGLVersionInfo()); return g_driver_gl.ext.b_GL_ARB_sync || GetGLVersionInfo()->is_es3 || -#if !defined(OS_MACOSX) +#if defined(OS_MACOSX) + g_driver_gl.ext.b_GL_APPLE_fence || +#else g_driver_egl.ext.b_EGL_KHR_fence_sync || #endif g_driver_gl.ext.b_GL_NV_fence;
diff --git a/ui/gl/gl_fence_apple.cc b/ui/gl/gl_fence_apple.cc new file mode 100644 index 0000000..9df0cad --- /dev/null +++ b/ui/gl/gl_fence_apple.cc
@@ -0,0 +1,47 @@ +// 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 "ui/gl/gl_fence_apple.h" + +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" + +namespace gfx { + +GLFenceAPPLE::GLFenceAPPLE(bool flush) { + glGenFencesAPPLE(1, &fence_); + glSetFenceAPPLE(fence_); + DCHECK(glIsFenceAPPLE(fence_)); + if (flush) { + glFlush(); + } else { + flush_event_ = GLContext::GetCurrent()->SignalFlush(); + } +} + +bool GLFenceAPPLE::HasCompleted() { + DCHECK(glIsFenceAPPLE(fence_)); + return !!glTestFenceAPPLE(fence_); +} + +void GLFenceAPPLE::ClientWait() { + DCHECK(glIsFenceAPPLE(fence_)); + if (!flush_event_.get() || flush_event_->IsSignaled()) { + glFinishFenceAPPLE(fence_); + } else { + LOG(ERROR) << "Trying to wait for uncommitted fence. Skipping..."; + } +} + +void GLFenceAPPLE::ServerWait() { + DCHECK(glIsFenceAPPLE(fence_)); + ClientWait(); +} + +GLFenceAPPLE::~GLFenceAPPLE() { + DCHECK(glIsFenceAPPLE(fence_)); + glDeleteFencesAPPLE(1, &fence_); +} + +} // namespace gfx
diff --git a/ui/gl/gl_fence_apple.h b/ui/gl/gl_fence_apple.h new file mode 100644 index 0000000..6c8633d --- /dev/null +++ b/ui/gl/gl_fence_apple.h
@@ -0,0 +1,34 @@ +// 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 UI_GL_GL_FENCE_APPLE_H_ +#define UI_GL_GL_FENCE_APPLE_H_ + +#include "base/macros.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_fence.h" + +namespace gfx { + +class GL_EXPORT GLFenceAPPLE : public GLFence { + public: + GLFenceAPPLE(bool flush); + ~GLFenceAPPLE() override; + + // GLFence implementation: + bool HasCompleted() override; + void ClientWait() override; + void ServerWait() override; + + private: + GLuint fence_; + scoped_refptr<GLContext::FlushEvent> flush_event_; + + DISALLOW_COPY_AND_ASSIGN(GLFenceAPPLE); +}; + +} // namespace gfx + +#endif // UI_GL_GL_FENCE_APPLE_H_
diff --git a/ui/gl/gl_surface.cc b/ui/gl/gl_surface.cc index a818a60..4459d48 100644 --- a/ui/gl/gl_surface.cc +++ b/ui/gl/gl_surface.cc
@@ -266,9 +266,6 @@ return extensions.find(delimited_name) != std::string::npos; } -void GLSurface::SetSwapInterval(int interval) { -} - GLSurfaceAdapter::GLSurfaceAdapter(GLSurface* surface) : surface_(surface) {} bool GLSurfaceAdapter::Initialize() {
diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h index 0a7607f..d458063 100644 --- a/ui/gl/gl_surface.h +++ b/ui/gl/gl_surface.h
@@ -134,8 +134,6 @@ static GLSurface* GetCurrent(); - virtual void SetSwapInterval(int interval); - protected: virtual ~GLSurface(); static bool InitializeOneOffImplementation(GLImplementation impl,
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc index c34ca0e..19119a9 100644 --- a/ui/gl/gl_surface_egl.cc +++ b/ui/gl/gl_surface_egl.cc
@@ -45,8 +45,11 @@ #if !defined(EGL_PLATFORM_ANGLE_TYPE_ANGLE) #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3202 #endif -#if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE) -#define EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE 0x3206 +#if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) +#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3207 +#endif +#if !defined(EGL_PLATFORM_ANGLE_USE_WARP_ANGLE) +#define EGL_PLATFORM_ANGLE_USE_WARP_ANGLE 0x3208 #endif #endif // defined(OS_WIN) @@ -54,8 +57,6 @@ namespace gfx { -unsigned int NativeViewGLSurfaceEGL::current_swap_generation_ = 0; - namespace { EGLConfig g_config; @@ -252,8 +253,8 @@ #if defined(OS_WIN) static const EGLint kDisplayAttribsWarp[] { - EGL_PLATFORM_ANGLE_TYPE_ANGLE, - EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_USE_WARP_ANGLE, EGL_TRUE, EGL_NONE }; @@ -288,9 +289,7 @@ surface_(NULL), supports_post_sub_buffer_(false), config_(NULL), - size_(1, 1), - swap_interval_(1), - swap_generation_(0) { + size_(1, 1) { #if defined(OS_ANDROID) if (window) ANativeWindow_acquire(window); @@ -454,37 +453,12 @@ "width", GetSize().width(), "height", GetSize().height()); -#if defined(OS_WIN) - bool force_no_vsync = false; - if (swap_interval_ != 0) { - // This code is a simple way of enforcing that only one surface actually - // vsyncs per frame. This provides single window cases a stable refresh - // while allowing multi-window cases to not slow down due to multiple syncs - // on a single thread. A better way to fix this problem would be to have - // each surface present on its own thread. - if (current_swap_generation_ == swap_generation_) { - current_swap_generation_++; - } else { - force_no_vsync = true; - eglSwapInterval(GetDisplay(), 0); - } - - swap_generation_ = current_swap_generation_; - } -#endif - if (!eglSwapBuffers(GetDisplay(), surface_)) { DVLOG(1) << "eglSwapBuffers failed with error " << GetLastEGLErrorString(); return false; } -#if defined(OS_WIN) - if (force_no_vsync) { - eglSwapInterval(GetDisplay(), swap_interval_); - } -#endif - return true; } @@ -559,10 +533,6 @@ return vsync_provider_.get(); } -void NativeViewGLSurfaceEGL::SetSwapInterval(int interval) { - swap_interval_ = interval; -} - NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() { Destroy(); #if defined(OS_ANDROID)
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h index b832ef6..952ccad 100644 --- a/ui/gl/gl_surface_egl.h +++ b/ui/gl/gl_surface_egl.h
@@ -82,8 +82,6 @@ EGLNativeWindowType window_; - virtual void SetSwapInterval(int interval) override; - private: EGLSurface surface_; bool supports_post_sub_buffer_; @@ -92,10 +90,6 @@ scoped_ptr<VSyncProvider> vsync_provider_; - int swap_interval_; - unsigned int swap_generation_; - static unsigned int current_swap_generation_; - DISALLOW_COPY_AND_ASSIGN(NativeViewGLSurfaceEGL); };
diff --git a/ui/gl/gl_surface_win.cc b/ui/gl/gl_surface_win.cc index c424e87..5cf68af 100644 --- a/ui/gl/gl_surface_win.cc +++ b/ui/gl/gl_surface_win.cc
@@ -31,8 +31,11 @@ #if !defined(EGL_PLATFORM_ANGLE_TYPE_ANGLE) #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3202 #endif -#if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE) -#define EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE 0x3206 +#if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) +#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3207 +#endif +#if !defined(EGL_PLATFORM_ANGLE_USE_WARP_ANGLE) +#define EGL_PLATFORM_ANGLE_USE_WARP_ANGLE 0x3208 #endif namespace gfx {