Update from chromium a8e7c94b1b79a0948d05a1fcfff53391d22ce37a Review URL: https://codereview.chromium.org/664803003
diff --git a/DEPS b/DEPS index 6740daf..de6d2fe 100644 --- a/DEPS +++ b/DEPS
@@ -30,16 +30,16 @@ 'skia_git': 'https://skia.googlesource.com', 'boringssl_git': 'https://boringssl.googlesource.com', 'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac', - 'skia_revision': 'dd5a1e094c19fa10202c37c50a1f799e5af5dac0', + 'skia_revision': '22b5ab644e0381e672c6fcf8aff26bf82ee71989', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and V8 without interference from each other. 'v8_branch': 'trunk', - 'v8_revision': '233fccf1872781e1f8ac2314511de15ca7a232ad', # from svn revision 24772 + 'v8_revision': 'b404893cc89fe7419e614abccb1fddf69d308abc', # from svn revision 24791 # 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": "8e92923010c568a445c0f22e1a36f462fcabc8f3", + "angle_revision": "d2f756be4fd198d630b64dc6c3568184363e6d14", # 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. @@ -79,7 +79,7 @@ Var('chromium_git') + '/angle/angle.git' + '@' + Var('angle_revision'), 'src/third_party/icu': - Var('chromium_git') + '/chromium/deps/icu52.git' + '@' + '8ac906faf7b66180f2208380c35ae1e07136c5cc', # from svn revision 292317 + Var('chromium_git') + '/chromium/deps/icu52.git' + '@' + 'd8b2a9d7b0039a4950ee008c5b1d998902c44c60', # from svn revision 292476 'src/third_party/libc++/trunk': Var('chromium_git') + '/chromium/llvm-project/libcxx.git' + '@' + Var('libcxx_revision'),
diff --git a/base/PRESUBMIT.py b/base/PRESUBMIT.py index 758a790..4e9e64b 100644 --- a/base/PRESUBMIT.py +++ b/base/PRESUBMIT.py
@@ -8,10 +8,6 @@ for more details on the presubmit API built into gcl. """ -import re - -BASE_SOURCE_FILES=(r'^base/.*\.(cc|h|mm)$',) - def _CheckNoInterfacesInBase(input_api, output_api): """Checks to make sure no files in libbase.a have |@interface|.""" pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE) @@ -40,45 +36,8 @@ results.extend(_CheckNoInterfacesInBase(input_api, output_api)) return results -def _CheckOverrideFinal(input_api, output_api, - whitelist=BASE_SOURCE_FILES, blacklist=None): - """Make sure new lines of code don't use the OVERRIDE or FINAL macros.""" - - # TODO(mostynb): remove this check once the macros are removed - # from base/compiler_specific.h. - - errors = [] - - source_file_filter = lambda x: input_api.FilterSourceFile( - x, white_list=BASE_SOURCE_FILES, black_list=None) - - override_files = [] - final_files = [] - - for f in input_api.AffectedSourceFiles(source_file_filter): - contents = input_api.ReadFile(f, 'rb') - - # "override" and "final" should be used instead of OVERRIDE/FINAL now. - if re.search(r"\bOVERRIDE\b", contents): - override_files.append(f.LocalPath()) - - if re.search(r"\bFINAL\b", contents): - final_files.append(f.LocalPath()) - - if override_files: - return [output_api.PresubmitError( - 'These files use OVERRIDE instead of using override:', - items=override_files)] - if final_files: - return [output_api.PresubmitError( - 'These files use FINAL instead of using final:', - items=final_files)] - - return [] - def CheckChangeOnUpload(input_api, output_api): results = [] - results.extend(_CheckOverrideFinal(input_api, output_api)) results.extend(_CommonChecks(input_api, output_api)) return results @@ -92,12 +51,12 @@ def GetPreferredTryMasters(project, change): return { 'tryserver.chromium.linux': { - 'linux_chromium_rel_swarming': set(['defaulttests']), + 'linux_chromium_rel': set(['defaulttests']), }, 'tryserver.chromium.mac': { - 'mac_chromium_rel_swarming': set(['defaulttests']), + 'mac_chromium_rel': set(['defaulttests']), }, 'tryserver.chromium.win': { - 'win_chromium_rel_swarming': set(['defaulttests']), + 'win_chromium_rel': set(['defaulttests']), } }
diff --git a/base/basictypes.h b/base/basictypes.h index 12d4c97..bf75e67 100644 --- a/base/basictypes.h +++ b/base/basictypes.h
@@ -22,24 +22,11 @@ typedef int8_t int8; typedef uint8_t uint8; typedef int16_t int16; -typedef int32_t int32; typedef uint16_t uint16; +typedef int32_t int32; typedef uint32_t uint32; - -// TODO(vtl): Figure what's up with the 64-bit types. Can we just define them as -// |int64_t|/|uint64_t|? -// The NSPR system headers define 64-bit as |long| when possible, except on -// Mac OS X. In order to not have typedef mismatches, we do the same on LP64. -// -// On Mac OS X, |long long| is used for 64-bit types for compatibility with -// <inttypes.h> format macros even in the LP64 model. -#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) -typedef long int64; -typedef unsigned long uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif +typedef int64_t int64; +typedef uint64_t uint64; // DEPRECATED: Please use std::numeric_limits (from <limits>) instead. const uint8 kuint8max = 0xFF;
diff --git a/base/compiler_specific.h b/base/compiler_specific.h index 6210d1a..47ca977 100644 --- a/base/compiler_specific.h +++ b/base/compiler_specific.h
@@ -139,12 +139,6 @@ #define ALIGNOF(type) __alignof__(type) #endif -// Annotate a virtual method indicating it must be overriding a virtual -// method in the parent class. -// Use like: -// virtual void foo() OVERRIDE; -#define OVERRIDE override - // Annotate a function indicating the caller must examine the return value. // Use like: // int foo() WARN_UNUSED_RESULT;
diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc index fb5ba62..4078920 100644 --- a/base/files/file_path_watcher_linux.cc +++ b/base/files/file_path_watcher_linux.cc
@@ -105,25 +105,25 @@ bool is_dir); protected: - virtual ~FilePathWatcherImpl() {} + ~FilePathWatcherImpl() override {} private: // Start watching |path| for changes and notify |delegate| on each change. // Returns true if watch for |path| has been added successfully. - virtual bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; + bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) override; // Cancel the watch. This unregisters the instance with InotifyReader. - virtual void Cancel() override; + void Cancel() override; // Cleans up and stops observing the message_loop() thread. - virtual void CancelOnMessageLoopThread() override; + void CancelOnMessageLoopThread() override; // Deletion of the FilePathWatcher will call Cancel() to dispose of this // object in the right thread. This also observes destruction of the required // cleanup thread, in case it quits before Cancel() is called. - virtual void WillDestroyCurrentMessageLoop() override; + void WillDestroyCurrentMessageLoop() override; // Inotify watches are installed for all directory components of |target_|. A // WatchEntry instance holds the watch descriptor for a component and the
diff --git a/base/i18n/icu_string_conversions_unittest.cc b/base/i18n/icu_string_conversions_unittest.cc index 107e240..d4d3251 100644 --- a/base/i18n/icu_string_conversions_unittest.cc +++ b/base/i18n/icu_string_conversions_unittest.cc
@@ -343,7 +343,8 @@ {"foo-\xe4.html", "iso-8859-1", true, "foo-\xc3\xa4.html"}, {"foo-\xe4.html", "iso-8859-7", true, "foo-\xce\xb4.html"}, {"foo-\xe4.html", "foo-bar", false, ""}, - {"foo-\xff.html", "ascii", false, ""}, + // HTML Encoding spec treats US-ASCII as synonymous with windows-1252 + {"foo-\xff.html", "ascii", true, "foo-\xc3\xbf.html"}, {"foo.html", "ascii", true, "foo.html"}, {"foo-a\xcc\x88.html", "utf-8", true, "foo-\xc3\xa4.html"}, {"\x95\x32\x82\x36\xD2\xBB", "gb18030", true, "\xF0\xA0\x80\x80\xE4\xB8\x80"},
diff --git a/base/memory/scoped_ptr.h b/base/memory/scoped_ptr.h index f3bbd12..ae9eb0f 100644 --- a/base/memory/scoped_ptr.h +++ b/base/memory/scoped_ptr.h
@@ -73,16 +73,6 @@ // // scoped_ptr<Foo> foo(new Foo()); // scoped_ptr<FooParent> parent(foo.Pass()); -// -// PassAs<>() should be used to upcast return value in return statement: -// -// scoped_ptr<Foo> CreateFoo() { -// scoped_ptr<FooChild> result(new FooChild()); -// return result.PassAs<Foo>(); -// } -// -// Note that PassAs<>() is implemented only for scoped_ptr<T>, but not for -// scoped_ptr<T[]>. This is because casting array pointers may not be safe. #ifndef BASE_MEMORY_SCOPED_PTR_H_ #define BASE_MEMORY_SCOPED_PTR_H_ @@ -436,17 +426,6 @@ return impl_.release(); } - // C++98 doesn't support functions templates with default parameters which - // makes it hard to write a PassAs() that understands converting the deleter - // while preserving simple calling semantics. - // - // Until there is a use case for PassAs() with custom deleters, just ignore - // the custom deleter. - template <typename PassAsType> - scoped_ptr<PassAsType> PassAs() { - return scoped_ptr<PassAsType>(Pass()); - } - private: // Needed to reach into |impl_| in the constructor. template <typename U, typename V> friend class scoped_ptr;
diff --git a/base/memory/scoped_ptr_unittest.cc b/base/memory/scoped_ptr_unittest.cc index 6af19b6..3f169a7 100644 --- a/base/memory/scoped_ptr_unittest.cc +++ b/base/memory/scoped_ptr_unittest.cc
@@ -94,11 +94,6 @@ return scoped_ptr<ConDecLogger>(new ConDecLogger(constructed)); } -scoped_ptr<ConDecLoggerParent> UpcastUsingPassAs( - scoped_ptr<ConDecLogger> object) { - return object.PassAs<ConDecLoggerParent>(); -} - } // namespace TEST(ScopedPtrTest, ScopedPtr) { @@ -462,22 +457,6 @@ EXPECT_EQ(0, constructed); } -TEST(ScopedPtrTest, PassAs) { - int constructed = 0; - { - scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed)); - EXPECT_EQ(1, constructed); - EXPECT_TRUE(scoper.get()); - - scoped_ptr<ConDecLoggerParent> scoper_parent; - scoper_parent = UpcastUsingPassAs(scoper.Pass()); - EXPECT_EQ(1, constructed); - EXPECT_TRUE(scoper_parent.get()); - EXPECT_FALSE(scoper.get()); - } - EXPECT_EQ(0, constructed); -} - TEST(ScopedPtrTest, CustomDeleter) { double dummy_value; // Custom deleter never touches this value. int deletes = 0;
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h index f6001e2..8a43392 100644 --- a/base/memory/weak_ptr.h +++ b/base/memory/weak_ptr.h
@@ -48,17 +48,18 @@ // ------------------------- IMPORTANT: Thread-safety ------------------------- // Weak pointers may be passed safely between threads, but must always be -// dereferenced and invalidated on the same thread otherwise checking the -// pointer would be racey. +// dereferenced and invalidated on the same SequencedTaskRunner otherwise +// checking the pointer would be racey. // // To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory // is dereferenced, the factory and its WeakPtrs become bound to the calling -// thread, and cannot be dereferenced or invalidated on any other thread. Bound -// WeakPtrs can still be handed off to other threads, e.g. to use to post tasks -// back to object on the bound thread. +// thread or current SequencedWorkerPool token, and cannot be dereferenced or +// invalidated on any other task runner. Bound WeakPtrs can still be handed +// off to other task runners, e.g. to use to post tasks back to object on the +// bound sequence. // -// Invalidating the factory's WeakPtrs un-binds it from the thread, allowing it -// to be passed for a different thread to use or delete it. +// Invalidating the factory's WeakPtrs un-binds it from the sequence, allowing +// it to be passed for a different sequence to use or delete it. #ifndef BASE_MEMORY_WEAK_PTR_H_ #define BASE_MEMORY_WEAK_PTR_H_ @@ -81,8 +82,8 @@ class BASE_EXPORT WeakReference { public: - // Although Flag is bound to a specific thread, it may be deleted from another - // via base::WeakPtr::~WeakPtr(). + // Although Flag is bound to a specific SequencedTaskRunner, it may be + // deleted from another via base::WeakPtr::~WeakPtr(). class BASE_EXPORT Flag : public RefCountedThreadSafe<Flag> { public: Flag();
diff --git a/base/message_loop/message_pump_glib.h b/base/message_loop/message_pump_glib.h index 0f797c5..9f44571 100644 --- a/base/message_loop/message_pump_glib.h +++ b/base/message_loop/message_pump_glib.h
@@ -22,7 +22,7 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump { public: MessagePumpGlib(); - virtual ~MessagePumpGlib(); + ~MessagePumpGlib() override; // Internal methods used for processing the pump callbacks. They are // public for simplicity but should not be used directly. HandlePrepare @@ -35,10 +35,10 @@ void HandleDispatch(); // Overridden from MessagePump: - virtual void Run(Delegate* delegate) override; - virtual void Quit() override; - virtual void ScheduleWork() override; - virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; + void Run(Delegate* delegate) override; + void Quit() override; + void ScheduleWork() override; + void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; private: bool ShouldQuit() const;
diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc index bff7be4..b02453c 100644 --- a/base/process/kill_posix.cc +++ b/base/process/kill_posix.cc
@@ -415,7 +415,7 @@ } // Overridden from PlatformThread::Delegate: - virtual void ThreadMain() override { + void ThreadMain() override { WaitForChildToDie(); delete this; }
diff --git a/base/win/pe_image.h b/base/win/pe_image.h index 878ef52..d93e99d 100644 --- a/base/win/pe_image.h +++ b/base/win/pe_image.h
@@ -23,7 +23,7 @@ namespace win { // This class is a wrapper for the Portable Executable File Format (PE). -// It's main purpose is to provide an easy way to work with imports and exports +// Its main purpose is to provide an easy way to work with imports and exports // from a file, mapped in memory as image. class PEImage { public: @@ -84,6 +84,8 @@ module_ = reinterpret_cast<HMODULE>(const_cast<void*>(module)); } + virtual ~PEImage() {} + // Gets the HMODULE for this object. HMODULE module() const;
diff --git a/build/android/buildbot/OWNERS b/build/android/buildbot/OWNERS index 425f1d9..f289720 100644 --- a/build/android/buildbot/OWNERS +++ b/build/android/buildbot/OWNERS
@@ -1,9 +1,6 @@ set noparent cmp@chromium.org -craigdh@chromium.org -frankf@chromium.org +jbudorick@chromium.org navabi@chromium.org -# backup -ilevy@chromium.org
diff --git a/build/android/buildbot/bb_device_status_check.py b/build/android/buildbot/bb_device_status_check.py index 2eb3626..6d736a1 100755 --- a/build/android/buildbot/bb_device_status_check.py +++ b/build/android/buildbot/bb_device_status_check.py
@@ -252,7 +252,7 @@ try: if 'adb' in p.name: yield p - except (psutil.error.NoSuchProcess, psutil.error.AccessDenied): + except (psutil.NoSuchProcess, psutil.AccessDenied): pass for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]: @@ -261,12 +261,12 @@ print 'kill %d %d (%s [%s])' % (sig, p.pid, p.name, ' '.join(p.cmdline)) p.send_signal(sig) - except (psutil.error.NoSuchProcess, psutil.error.AccessDenied): + except (psutil.NoSuchProcess, psutil.AccessDenied): pass for p in GetAllAdb(): try: print 'Unable to kill %d (%s [%s])' % (p.pid, p.name, ' '.join(p.cmdline)) - except (psutil.error.NoSuchProcess, psutil.error.AccessDenied): + except (psutil.NoSuchProcess, psutil.AccessDenied): pass
diff --git a/build/android/buildbot/bb_device_steps.py b/build/android/buildbot/bb_device_steps.py index c0f9fe7..e95a552 100755 --- a/build/android/buildbot/bb_device_steps.py +++ b/build/android/buildbot/bb_device_steps.py
@@ -145,6 +145,9 @@ args.append('--tool=asan') if options.gtest_filter: args.append('--gtest-filter=%s' % options.gtest_filter) + if options.flakiness_server: + args.append('--flakiness-dashboard-server=%s' % + options.flakiness_server) for suite in suites: bb_annotations.PrintNamedStep(suite) @@ -186,7 +189,7 @@ test = I('ChromeSyncShell', 'ChromeSyncShell.apk', 'org.chromium.chrome.browser.sync', - 'ChromeSyncShellTest.apk', + 'ChromeSyncShellTest', 'chrome:chrome/test/data/android/device_files') RunInstrumentationSuite(options, test)
diff --git a/build/android/pylib/OWNERS b/build/android/pylib/OWNERS index 3899fa3..dbbbba7 100644 --- a/build/android/pylib/OWNERS +++ b/build/android/pylib/OWNERS
@@ -1,4 +1,4 @@ -frankf@chromium.org jbudorick@chromium.org +klundberg@chromium.org navabi@chromium.org skyostil@chromium.org
diff --git a/build/android/pylib/base/test_dispatcher.py b/build/android/pylib/base/test_dispatcher.py index cb789de..677a908 100644 --- a/build/android/pylib/base/test_dispatcher.py +++ b/build/android/pylib/base/test_dispatcher.py
@@ -272,7 +272,6 @@ # for the above are switched or wrapped. android_commands.errors.DeviceUnresponsiveError) as e: logging.error(e) - exit_code = constants.WARNING_EXIT_CODE if not all((len(tc) == 0 for tc in test_collections)): logging.error('Only ran %d tests (all devices are likely offline).' %
diff --git a/build/android/pylib/utils/json_results_generator.py b/build/android/pylib/utils/json_results_generator.py index 52446b1..d40860d 100644 --- a/build/android/pylib/utils/json_results_generator.py +++ b/build/android/pylib/utils/json_results_generator.py
@@ -516,7 +516,7 @@ this_test = this_test[segment] if not len(this_test): - self._PopulateResutlsAndTimesJSON(this_test) + self._PopulateResultsAndTimesJSON(this_test) if self.RESULTS in this_test: self._InsertItemRunLengthEncoded(result, this_test[self.RESULTS]) @@ -558,7 +558,7 @@ results[self.TESTS] = test_results_trie - def _PopulateResutlsAndTimesJSON(self, results_and_times): + def _PopulateResultsAndTimesJSON(self, results_and_times): results_and_times[self.RESULTS] = [] results_and_times[self.TIMES] = [] return results_and_times
diff --git a/build/android/pylib/utils/report_results.py b/build/android/pylib/utils/report_results.py index b13b9bc..9841dc6 100644 --- a/build/android/pylib/utils/report_results.py +++ b/build/android/pylib/utils/report_results.py
@@ -39,24 +39,28 @@ logging.info('Upload results for test type "%s", test package "%s" to %s' % (test_type, test_package, flakiness_server)) - # TODO(frankf): Enable uploading for gtests. - if test_type != 'Instrumentation': - logging.warning('Invalid test type.') - return - try: - if flakiness_server == constants.UPSTREAM_FLAKINESS_SERVER: - assert test_package in ['ContentShellTest', + if test_type == 'Instrumentation': + if flakiness_server == constants.UPSTREAM_FLAKINESS_SERVER: + assert test_package in ['ContentShellTest', 'ChromeShellTest', - 'AndroidWebViewTest'] - dashboard_test_type = ('%s_instrumentation_tests' % - test_package.lower().rstrip('test')) - # Downstream server. + 'AndroidWebViewTest'] + dashboard_test_type = ('%s_instrumentation_tests' % + test_package.lower().rstrip('test')) + # Downstream server. + else: + dashboard_test_type = 'Chromium_Android_Instrumentation' + + elif test_type == 'Unit test': + dashboard_test_type = test_package + else: - dashboard_test_type = 'Chromium_Android_Instrumentation' + logging.warning('Invalid test type') + return flakiness_dashboard_results_uploader.Upload( results, flakiness_server, dashboard_test_type) + except Exception as e: logging.error(e)
diff --git a/build/common.gypi b/build/common.gypi index 0ad62ee..8f9cb38 100644 --- a/build/common.gypi +++ b/build/common.gypi
@@ -903,7 +903,7 @@ 'use_allocator%': 'none', # sysroot needs to be an absolute path otherwise it generates # incorrect results when passed to pkg-config - 'sysroot%': '<!(cd <(DEPTH) && pwd -P)/arm-sysroot', + 'sysroot%': '<!(cd <(DEPTH) && pwd -P)/chrome/installer/linux/debian_wheezy_arm-sysroot', }], # OS=="linux" and target_arch=="arm" and chromeos==0 ['OS=="linux" and branding=="Chrome" and buildtype=="Official" and chromeos==0', {
diff --git a/build/go/rules.gni b/build/go/rules.gni index 79453d1..ab703d6 100644 --- a/build/go/rules.gni +++ b/build/go/rules.gni
@@ -53,8 +53,9 @@ rebase_path("//", root_build_dir), "-I" + rebase_path("//"), " -L" + rebase_path(target_out_dir) + + " -L" + rebase_path(root_build_dir + "/obj/third_party/libevent") + " -l" + static_library_name + - " -lstdc++ -lpthread -lm -lglib-2.0", + " -lstdc++ -lpthread -lm -lglib-2.0 -levent", "test", "-c", ] + rebase_path(invoker.sources, build_dir) }
diff --git a/build/linux/install-arm-sysroot.py b/build/linux/install-arm-sysroot.py index 4d593cc..495fc75 100755 --- a/build/linux/install-arm-sysroot.py +++ b/build/linux/install-arm-sysroot.py
@@ -3,103 +3,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Script to install ARM root image for cross building of ARM chrome on linux. -This script can be run manually but is more often run as part of gclient -hooks. When run from hooks this script should be a no-op on non-linux -platforms. - -The sysroot image could be constructed from scratch based on the current -state or precise/arm but for consistency we currently use a pre-built root -image which was originally designed for building trusted NaCl code. The image -will normally need to be rebuilt every time chrome's build dependancies are -changed. - -Steps to rebuild the arm sysroot image: - -- cd $SRC/native_client -- ./tools/trusted_cross_toolchains/trusted-toolchain-creator.armel.precise.sh \ - UpdatePackageLists -- ./tools/trusted_cross_toolchains/trusted-toolchain-creator.armel.precise.sh \ - BuildJail $SRC/out/arm-sysroot.tar.gz -- gsutil cp -a public-read $SRC/out/arm-sysroot.tar.gz \ - nativeclient-archive2/toolchain/$NACL_REV/sysroot-arm-trusted.tgz -""" - -# TODO(sbc): merge this script into: -# chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py - -import hashlib -import os -import shutil -import subprocess import sys - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -URL_PREFIX = 'https://storage.googleapis.com' -URL_PATH = 'chrome-linux-sysroot/toolchain' -REVISION = 285950 -TARBALL = 'debian_wheezy_arm_sysroot.tgz' -TARBALL_SHA1SUM = 'fc2f54db168887c5190c4c6686c869bedf668b4e' - - -def get_sha1(filename): - sha1 = hashlib.sha1() - with open(filename, 'rb') as f: - while True: - # Read in 1mb chunks, so it doesn't all have to be loaded into memory. - chunk = f.read(1024*1024) - if not chunk: - break - sha1.update(chunk) - return sha1.hexdigest() - - -def main(args): - if '--linux-only' in args: - # This argument is passed when run from the gclient hooks. - # In this case we return early on non-linux platforms - # or if GYP_DEFINES doesn't include target_arch=arm - if not sys.platform.startswith('linux'): - return 0 - - if "target_arch=arm" not in os.environ.get('GYP_DEFINES', ''): - return 0 - - src_root = os.path.dirname(os.path.dirname(SCRIPT_DIR)) - sysroot = os.path.join(src_root, 'arm-sysroot') - url = "%s/%s/%s/%s" % (URL_PREFIX, URL_PATH, REVISION, TARBALL) - - stamp = os.path.join(sysroot, ".stamp") - if os.path.exists(stamp): - with open(stamp) as s: - if s.read() == url: - print "ARM root image already up-to-date: %s" % sysroot - return 0 - - print "Installing ARM root image: %s" % sysroot - if os.path.isdir(sysroot): - shutil.rmtree(sysroot) - os.mkdir(sysroot) - tarball = os.path.join(sysroot, TARBALL) - curl = ['curl', '--fail', '-L', url, '-o', tarball] - if os.isatty(sys.stdout.fileno()): - curl.append('--progress') - else: - curl.append('--silent') - subprocess.check_call(curl) - sha1sum = get_sha1(tarball) - if sha1sum != TARBALL_SHA1SUM: - print 'Tarball sha1sum is wrong.' - print 'Expected %s, actual: %s' % (TARBALL_SHA1SUM, sha1sum) - return 1 - subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot]) - os.remove(tarball) - - with open(stamp, 'w') as s: - s.write(url) - return 0 - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) +msg = '''\ +ERROR: This script has merged with install-debian.wheezy.sysroot.py. +Please use that instead (with --arch=arm). +''' +sys.stderr.write(msg)
diff --git a/build/linux/install-chromeos-fonts.py b/build/linux/install-chromeos-fonts.py index 98c3a57..a24adc9 100755 --- a/build/linux/install-chromeos-fonts.py +++ b/build/linux/install-chromeos-fonts.py
@@ -12,16 +12,12 @@ import subprocess import sys -URL_PREFIX = 'https://commondatastorage.googleapis.com' -URL_DIR = 'chromeos-localmirror/distfiles' -URL_FILE = 'notofonts-20121206.tar.gz' +# Taken from the media-fonts/notofonts ebuild in chromiumos-overlay. +VERSION = '20140815' +URL = ('https://commondatastorage.googleapis.com/chromeos-localmirror/' + 'distfiles/notofonts-%s.tar.bz2') % (VERSION) FONTS_DIR = '/usr/local/share/fonts' -# The URL matches the URL in the ebuild script in chromiumos. See: -# /path/to/chromiumos/src/ -# third_party/chromiumos-overlay/media-fonts/notofonts/ -# notofonts-20121206.ebuild - def main(args): if not sys.platform.startswith('linux'): print "Error: %s must be run on Linux." % __file__ @@ -37,12 +33,10 @@ dest_dir = os.path.join(FONTS_DIR, 'chromeos') - url = "%s/%s/%s" % (URL_PREFIX, URL_DIR, URL_FILE) - stamp = os.path.join(dest_dir, ".stamp02") if os.path.exists(stamp): with open(stamp) as s: - if s.read() == url: + if s.read() == URL: print "Chrome OS fonts already up-to-date in %s." % dest_dir return 0 @@ -52,8 +46,8 @@ os.chmod(dest_dir, 0755) print "Installing Chrome OS fonts to %s." % dest_dir - tarball = os.path.join(dest_dir, URL_FILE) - subprocess.check_call(['curl', '-L', url, '-o', tarball]) + tarball = os.path.join(dest_dir, os.path.basename(URL)) + subprocess.check_call(['curl', '-L', URL, '-o', tarball]) subprocess.check_call(['tar', '--no-same-owner', '--no-same-permissions', '-xf', tarball, '-C', dest_dir]) os.remove(tarball) @@ -65,7 +59,7 @@ s.write("Script: %s\n" % __file__) with open(stamp, 'w') as s: - s.write(url) + s.write(URL) for base, dirs, files in os.walk(dest_dir): for dir in dirs:
diff --git a/build/whitespace_file.txt b/build/whitespace_file.txt index 4e9bf38..f11fa16 100644 --- a/build/whitespace_file.txt +++ b/build/whitespace_file.txt
@@ -141,3 +141,5 @@ It was love at first sight. The moment Yossarian first laid eyes on the chaplain, he fell madly in love with him. Cool whitespace change for git-cl land + +Oh god the bots are red! I'm blind! Mmmm, donuts.
diff --git a/cc/PRESUBMIT.py b/cc/PRESUBMIT.py index 1aa48f3..46c3a54 100644 --- a/cc/PRESUBMIT.py +++ b/cc/PRESUBMIT.py
@@ -191,12 +191,6 @@ errors.append(output_api.PresubmitError( '%s:%d uses scoped_ptr<T>(). Use nullptr instead.' % (f.LocalPath(), line_number))) - # Disallow: - # foo.PassAs<T>(); - if re.search(r'\bPassAs<.*?>\(\)', line): - errors.append(output_api.PresubmitError( - '%s:%d uses PassAs<T>(). Use Pass() instead.' % - (f.LocalPath(), line_number))) return errors def FindUnquotedQuote(contents, pos): @@ -332,42 +326,6 @@ else: return [] -def CheckOverrideFinal(input_api, output_api, - whitelist=CC_SOURCE_FILES, blacklist=None): - """Make sure new lines of code don't use the OVERRIDE or FINAL macros.""" - - # TODO(mostynb): remove this check once the macros are removed - # from base/compiler_specific.h. - - errors = [] - - source_file_filter = lambda x: input_api.FilterSourceFile( - x, white_list=CC_SOURCE_FILES, black_list=None) - - override_files = [] - final_files = [] - - for f in input_api.AffectedSourceFiles(source_file_filter): - contents = input_api.ReadFile(f, 'rb') - - # "override" and "final" should be used instead of OVERRIDE/FINAL now. - if re.search(r"\bOVERRIDE\b", contents): - override_files.append(f.LocalPath()) - - if re.search(r"\bFINAL\b", contents): - final_files.append(f.LocalPath()) - - if override_files: - return [output_api.PresubmitError( - 'These files use OVERRIDE instead of using override:', - items=override_files)] - if final_files: - return [output_api.PresubmitError( - 'These files use FINAL instead of using final:', - items=final_files)] - - return [] - def CheckChangeOnUpload(input_api, output_api): results = [] results += CheckAsserts(input_api, output_api) @@ -380,7 +338,6 @@ results += CheckNamespace(input_api, output_api) results += CheckForUseOfWrongClock(input_api, output_api) results += FindUselessIfdefs(input_api, output_api) - results += CheckOverrideFinal(input_api, output_api) results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) return results
diff --git a/cc/debug/rasterize_and_record_benchmark_impl.cc b/cc/debug/rasterize_and_record_benchmark_impl.cc index 77bc259..57e7f15 100644 --- a/cc/debug/rasterize_and_record_benchmark_impl.cc +++ b/cc/debug/rasterize_and_record_benchmark_impl.cc
@@ -211,9 +211,10 @@ FixedInvalidationPictureLayerTilingClient client( layer, gfx::Rect(layer->content_bounds())); - PictureLayerTilingSet tiling_set(&client, layer->content_bounds()); + PictureLayerTilingSet tiling_set(&client); - PictureLayerTiling* tiling = tiling_set.AddTiling(layer->contents_scale_x()); + PictureLayerTiling* tiling = + tiling_set.AddTiling(layer->contents_scale_x(), layer->bounds()); tiling->CreateAllTilesForTesting(); for (PictureLayerTiling::CoverageIterator it( tiling, layer->contents_scale_x(), layer->visible_content_rect());
diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc index e66cc76..3fb1c48 100644 --- a/cc/layers/delegated_renderer_layer_impl_unittest.cc +++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc
@@ -1416,7 +1416,12 @@ pass1_id, gfx::Rect(layer_size), gfx::Transform()); - AddRenderPassQuad(pass1, pass2, 0, FilterOperations(), transform); + AddRenderPassQuad(pass1, + pass2, + 0, + FilterOperations(), + transform, + SkXfermode::kSrcOver_Mode); delegated_renderer_layer_impl->SetFrameDataForRenderPasses( 1.f, &delegated_render_passes);
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 4c16da7..2973519 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc
@@ -625,6 +625,13 @@ float scale = layer_tree_impl()->page_scale_factor(); gfx::Vector2dF delta_from_scroll = scroll_clip_layer_->bounds_delta(); + + // In virtual-viewport mode, we don't need to compensate for pinch zoom or + // scale since the fixed container is the outer viewport, which sits below + // the page scale. + if (layer_tree_impl()->settings().use_pinch_virtual_viewport) + return delta_from_scroll; + delta_from_scroll.Scale(1.f / scale); // The delta-from-pinch component requires some explanation: A viewport of
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc index 2851392..6ae23a7 100644 --- a/cc/layers/picture_image_layer_impl_unittest.cc +++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -63,8 +63,7 @@ new TestablePictureImageLayerImpl(tree, id); layer->SetBounds(gfx::Size(100, 200)); layer->SetContentBounds(gfx::Size(100, 200)); - layer->tilings_.reset(new PictureLayerTilingSet(&tiling_client_, - layer->bounds())); + layer->tilings_.reset(new PictureLayerTilingSet(&tiling_client_)); layer->pile_ = tiling_client_.GetPile(); return make_scoped_ptr(layer); }
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index c0344e2..2599852 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc
@@ -483,6 +483,9 @@ was_screen_space_transform_animating_ = draw_properties().screen_space_transform_is_animating; + if (draw_transform_is_animating()) + pile_->set_likely_to_be_used_for_transform_animation(); + should_update_tile_priorities_ = true; UpdateTilePriorities(occlusion_in_content_space); @@ -790,7 +793,7 @@ const PictureLayerTiling* tiling) { if (!CanHaveTilingWithScale(tiling->contents_scale())) return; - tilings_->AddTiling(tiling->contents_scale()); + tilings_->AddTiling(tiling->contents_scale(), bounds()); // If this tree needs update draw properties, then the tiling will // get updated prior to drawing or activation. If this tree does not @@ -840,7 +843,7 @@ DCHECK(layer_tree_impl()->IsPendingTree()); if (!tilings_) - tilings_.reset(new PictureLayerTilingSet(this, bounds())); + tilings_ = make_scoped_ptr(new PictureLayerTilingSet(this)); DCHECK(!twin_layer_); twin_layer_ = static_cast<PictureLayerImpl*>( @@ -861,7 +864,7 @@ DCHECK(CanHaveTilingWithScale(contents_scale)) << "contents_scale: " << contents_scale; - PictureLayerTiling* tiling = tilings_->AddTiling(contents_scale); + PictureLayerTiling* tiling = tilings_->AddTiling(contents_scale, bounds()); DCHECK(pile_->HasRecordings()); @@ -1239,19 +1242,7 @@ } bool PictureLayerImpl::ShouldAdjustRasterScaleDuringScaleAnimations() const { - if (!layer_tree_impl()->use_gpu_rasterization()) - return false; - - // Re-rastering text at different scales using GPU rasterization causes - // texture uploads for glyphs at each scale (see crbug.com/366225). To - // workaround this performance issue, we don't re-rasterize layers with - // text during scale animations. - // TODO(ajuma): Remove this workaround once text can be efficiently - // re-rastered at different scales (e.g. by using distance-field fonts). - if (pile_->has_text()) - return false; - - return true; + return layer_tree_impl()->use_gpu_rasterization(); } float PictureLayerImpl::MaximumTilingContentsScale() const { @@ -1450,7 +1441,7 @@ PictureLayerTiling::TilingRasterTileIterator(tiling); } - if (tiling->resolution() == LOW_RESOLUTION) { + if (prioritize_low_res && tiling->resolution() == LOW_RESOLUTION) { iterators_[LOW_RES] = PictureLayerTiling::TilingRasterTileIterator(tiling); }
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 73906af..038dc2a 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -2194,16 +2194,20 @@ SetupDrawPropertiesAndUpdateTiles( active_layer_, 0.5f, 0.5f, 0.5f, 0.5f, false); + pending_layer_->tilings()->RemoveAllTilings(); active_layer_->tilings()->RemoveAllTilings(); - PictureLayerTiling* tiling = active_layer_->tilings()->AddTiling(0.5f); - active_layer_->tilings()->AddTiling(1.5f); - active_layer_->tilings()->AddTiling(0.25f); + PictureLayerTiling* tiling = active_layer_->AddTiling(0.5f); + active_layer_->AddTiling(1.5f); + active_layer_->AddTiling(0.25f); tiling->set_resolution(HIGH_RESOLUTION); // Sanity checks. ASSERT_EQ(3u, active_layer_->tilings()->num_tilings()); ASSERT_EQ(tiling, active_layer_->tilings()->TilingAtScale(0.5f)); + pending_layer_->tilings()->RemoveAllTilings(); + ASSERT_EQ(0u, pending_layer_->tilings()->num_tilings()); + // Now, set the bounds to be 1x1 (so that minimum contents scale becomes // 1.0f). Note that we should also ensure that the pending layer needs post // commit initialization, since this is what would happen during commit. In @@ -2602,8 +2606,9 @@ static_cast<FakePicturePileImpl*>(pending_layer_->pile())->set_has_text(true); static_cast<FakePicturePileImpl*>(active_layer_->pile())->set_has_text(true); - // Since we're GPU-rasterizing but have text, starting an animation should - // cause tiling resolution to get set to the maximum animation scale. + // When we're GPU-rasterizing, even if we have text, starting an animation + // should cause tiling resolution to get set to the content scale, since we + // render animating text at content scale using distance fields. animating_transform = true; contents_scale = 2.f; maximum_animation_scale = 3.f; @@ -2613,10 +2618,10 @@ page_scale, maximum_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale(), 3.f); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale(), 2.f); - // Further changes to scale during the animation should not cause a new - // high-res tiling to get created. + // Further changes to scale during the animation should still cause a new + // high-res tiling to get created at content scale. contents_scale = 4.f; maximum_animation_scale = 5.f; @@ -2625,7 +2630,7 @@ page_scale, maximum_animation_scale, animating_transform); - EXPECT_BOTH_EQ(HighResTiling()->contents_scale(), 3.f); + EXPECT_BOTH_EQ(HighResTiling()->contents_scale(), 4.f); // Once we stop animating, a new high-res tiling should be created. animating_transform = false; @@ -2704,7 +2709,7 @@ EXPECT_TRUE(reached_prepaint); EXPECT_EQ(0u, non_ideal_tile_count); - EXPECT_EQ(1u, low_res_tile_count); + EXPECT_EQ(0u, low_res_tile_count); EXPECT_EQ(16u, high_res_tile_count); EXPECT_EQ(low_res_tile_count + high_res_tile_count + non_ideal_tile_count, unique_tiles.size()); @@ -2761,8 +2766,7 @@ non_ideal_tile_count = 0; low_res_tile_count = 0; high_res_tile_count = 0; - for (it = PictureLayerImpl::LayerRasterTileIterator(pending_layer_, false); - it; + for (it = PictureLayerImpl::LayerRasterTileIterator(pending_layer_, true); it; ++it) { Tile* tile = *it; TilePriority priority = tile->priority(PENDING_TREE); @@ -3794,7 +3798,7 @@ if (tile_is_visible) unoccluded_tile_count++; } - EXPECT_EQ(unoccluded_tile_count, 25 + 4); + EXPECT_EQ(unoccluded_tile_count, 25); // Partial occlusion. pending_layer_->AddChild(LayerImpl::Create(host_impl_.pending_tree(), 1)); @@ -3824,7 +3828,7 @@ if (tile_is_visible) unoccluded_tile_count++; } - EXPECT_EQ(20 + 2, unoccluded_tile_count); + EXPECT_EQ(20, unoccluded_tile_count); // Full occlusion. layer1->SetPosition(gfx::Point(0, 0)); @@ -4490,6 +4494,65 @@ EXPECT_EQ(0u, active_layer_->tilings()->num_tilings()); } +TEST_F(PictureLayerImplTest, ChangeInViewportAllowsTilingUpdates) { + base::TimeTicks time_ticks; + time_ticks += base::TimeDelta::FromMilliseconds(1); + host_impl_.SetCurrentBeginFrameArgs( + CreateBeginFrameArgsForTesting(time_ticks)); + + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 4000); + + 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); + + Region invalidation; + gfx::Rect viewport = gfx::Rect(0, 0, 100, 100); + gfx::Transform transform; + + host_impl_.SetRequiresHighResToDraw(); + + // Update tiles. + pending_layer_->draw_properties().visible_content_rect = viewport; + pending_layer_->draw_properties().screen_space_transform = transform; + SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false); + pending_layer_->HighResTiling()->UpdateAllTilePrioritiesForTesting(); + + // Ensure we can't activate. + EXPECT_FALSE(pending_layer_->AllTilesRequiredForActivationAreReadyToDraw()); + + // Now in the same frame, move the viewport (this can happen during + // animation). + viewport = gfx::Rect(0, 2000, 100, 100); + + // Update tiles. + pending_layer_->draw_properties().visible_content_rect = viewport; + pending_layer_->draw_properties().screen_space_transform = transform; + SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false); + pending_layer_->HighResTiling()->UpdateAllTilePrioritiesForTesting(); + + // Make sure all viewport tiles (viewport from the tiling) are ready to draw. + std::vector<Tile*> tiles; + for (PictureLayerTiling::CoverageIterator iter( + pending_layer_->HighResTiling(), + 1.f, + pending_layer_->HighResTiling()->GetCurrentVisibleRectForTesting()); + iter; + ++iter) { + if (*iter) + tiles.push_back(*iter); + } + + host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles); + + // Ensure we can activate. + EXPECT_TRUE(pending_layer_->AllTilesRequiredForActivationAreReadyToDraw()); +} + class TileSizeSettings : public ImplSidePaintingSettings { public: TileSizeSettings() {
diff --git a/cc/layers/tiled_layer_unittest.cc b/cc/layers/tiled_layer_unittest.cc index 925cdc8..862ff1f 100644 --- a/cc/layers/tiled_layer_unittest.cc +++ b/cc/layers/tiled_layer_unittest.cc
@@ -128,8 +128,7 @@ nullptr, 0, false, - 1, - false); + 1); host_impl_ = make_scoped_ptr( new FakeLayerTreeHostImpl(proxy_, shared_bitmap_manager_.get())); }
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 6b15faf..24e9f30 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc
@@ -123,6 +123,44 @@ } } +BlendMode BlendModeFromSkXfermode(SkXfermode::Mode mode) { + switch (mode) { + case SkXfermode::kSrcOver_Mode: + return BlendModeNormal; + case SkXfermode::kOverlay_Mode: + return BlendModeOverlay; + case SkXfermode::kDarken_Mode: + return BlendModeDarken; + case SkXfermode::kLighten_Mode: + return BlendModeLighten; + case SkXfermode::kColorDodge_Mode: + return BlendModeColorDodge; + case SkXfermode::kColorBurn_Mode: + return BlendModeColorBurn; + case SkXfermode::kHardLight_Mode: + return BlendModeHardLight; + case SkXfermode::kSoftLight_Mode: + return BlendModeSoftLight; + case SkXfermode::kDifference_Mode: + return BlendModeDifference; + case SkXfermode::kExclusion_Mode: + return BlendModeExclusion; + case SkXfermode::kMultiply_Mode: + return BlendModeMultiply; + case SkXfermode::kHue_Mode: + return BlendModeHue; + case SkXfermode::kSaturation_Mode: + return BlendModeSaturation; + case SkXfermode::kColor_Mode: + return BlendModeColor; + case SkXfermode::kLuminosity_Mode: + return BlendModeLuminosity; + default: + NOTREACHED(); + return BlendModeNormal; + } +} + // Smallest unit that impact anti-aliasing output. We use this to // determine when anti-aliasing is unnecessary. const float kAntiAliasingEpsilon = 1.0f / 1024.0f; @@ -723,140 +761,6 @@ GLC(gl_, gl_->BlendEquation(GL_FUNC_ADD)); } -static skia::RefPtr<SkImage> ApplyBlendModeWithBackdrop( - scoped_ptr<GLRenderer::ScopedUseGrContext> use_gr_context, - ResourceProvider* resource_provider, - skia::RefPtr<SkImage> source_bitmap_with_filters, - ScopedResource* source_texture_resource, - ScopedResource* background_texture_resource, - SkXfermode::Mode blend_mode) { - if (!use_gr_context) - return source_bitmap_with_filters; - - DCHECK(background_texture_resource); - DCHECK(source_texture_resource); - - gfx::Size source_size = source_texture_resource->size(); - gfx::Size background_size = background_texture_resource->size(); - - DCHECK_LE(background_size.width(), source_size.width()); - DCHECK_LE(background_size.height(), source_size.height()); - - int source_texture_with_filters_id; - scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; - if (source_bitmap_with_filters) { - DCHECK_EQ(source_size.width(), source_bitmap_with_filters->width()); - DCHECK_EQ(source_size.height(), source_bitmap_with_filters->height()); - GrTexture* texture = - reinterpret_cast<GrTexture*>(source_bitmap_with_filters->getTexture()); - source_texture_with_filters_id = texture->getTextureHandle(); - } else { - lock.reset(new ResourceProvider::ScopedReadLockGL( - resource_provider, source_texture_resource->id())); - source_texture_with_filters_id = lock->texture_id(); - } - - ResourceProvider::ScopedReadLockGL lock_background( - resource_provider, background_texture_resource->id()); - - // Wrap the source texture in a Ganesh platform texture. - GrBackendTextureDesc backend_texture_description; - backend_texture_description.fConfig = kSkia8888_GrPixelConfig; - backend_texture_description.fOrigin = kBottomLeft_GrSurfaceOrigin; - - backend_texture_description.fWidth = source_size.width(); - backend_texture_description.fHeight = source_size.height(); - backend_texture_description.fTextureHandle = source_texture_with_filters_id; - skia::RefPtr<GrTexture> source_texture = - skia::AdoptRef(use_gr_context->context()->wrapBackendTexture( - backend_texture_description)); - if (!source_texture) { - TRACE_EVENT_INSTANT0( - "cc", - "ApplyBlendModeWithBackdrop wrap source texture failed", - TRACE_EVENT_SCOPE_THREAD); - return skia::RefPtr<SkImage>(); - } - - backend_texture_description.fWidth = background_size.width(); - backend_texture_description.fHeight = background_size.height(); - backend_texture_description.fTextureHandle = lock_background.texture_id(); - skia::RefPtr<GrTexture> background_texture = - skia::AdoptRef(use_gr_context->context()->wrapBackendTexture( - backend_texture_description)); - if (!background_texture) { - TRACE_EVENT_INSTANT0( - "cc", - "ApplyBlendModeWithBackdrop wrap background texture failed", - TRACE_EVENT_SCOPE_THREAD); - return skia::RefPtr<SkImage>(); - } - - SkImageInfo source_info = - SkImageInfo::MakeN32Premul(source_size.width(), source_size.height()); - // Place the platform texture inside an SkBitmap. - SkBitmap source; - source.setInfo(source_info); - skia::RefPtr<SkGrPixelRef> source_pixel_ref = - skia::AdoptRef(new SkGrPixelRef(source_info, source_texture.get())); - source.setPixelRef(source_pixel_ref.get()); - - SkImageInfo background_info = SkImageInfo::MakeN32Premul( - background_size.width(), background_size.height()); - - SkBitmap background; - background.setInfo(background_info); - skia::RefPtr<SkGrPixelRef> background_pixel_ref = - skia::AdoptRef(new SkGrPixelRef( - background_info, background_texture.get())); - background.setPixelRef(background_pixel_ref.get()); - - // Create a scratch texture for backing store. - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; - desc.fSampleCnt = 0; - desc.fWidth = source.width(); - desc.fHeight = source.height(); - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fOrigin = kBottomLeft_GrSurfaceOrigin; - skia::RefPtr<GrTexture> backing_store = - skia::AdoptRef(use_gr_context->context()->refScratchTexture( - desc, GrContext::kExact_ScratchTexMatch)); - if (!backing_store) { - TRACE_EVENT_INSTANT0( - "cc", - "ApplyBlendModeWithBackdrop scratch texture allocation failed", - TRACE_EVENT_SCOPE_THREAD); - return source_bitmap_with_filters; - } - - // Create a device and canvas using that backing store. - skia::RefPtr<SkSurface> surface = skia::AdoptRef( - SkSurface::NewRenderTargetDirect(backing_store->asRenderTarget())); - if (!surface) - return skia::RefPtr<SkImage>(); - skia::RefPtr<SkCanvas> canvas = skia::SharePtr(surface->getCanvas()); - - // Draw the source bitmap through the filter to the canvas. - canvas->clear(SK_ColorTRANSPARENT); - canvas->drawSprite(background, 0, 0); - SkPaint paint; - paint.setXfermodeMode(blend_mode); - canvas->drawSprite(source, 0, 0, &paint); - - skia::RefPtr<SkImage> image = skia::AdoptRef(surface->newImageSnapshot()); - if (!image || !image->getTexture()) { - return skia::RefPtr<SkImage>(); - } - - // Flush the GrContext to ensure all buffered GL calls are drawn to the - // backing store before we access and return it, and have cc begin using the - // GL context again. - canvas->flush(); - - return image; -} - bool GLRenderer::ShouldApplyBackgroundFilters(DrawingFrame* frame, const RenderPassDrawQuad* quad) { if (quad->background_filters.IsEmpty()) @@ -878,7 +782,8 @@ gfx::Rect GLRenderer::GetBackdropBoundingBoxForRenderPassQuad( DrawingFrame* frame, const RenderPassDrawQuad* quad, - const gfx::Transform& contents_device_transform) { + const gfx::Transform& contents_device_transform, + bool use_aa) { gfx::Rect backdrop_rect = gfx::ToEnclosingRect(MathUtil::MapClippedRect( contents_device_transform, SharedGeometryQuad().BoundingBox())); @@ -888,6 +793,11 @@ backdrop_rect.Inset(-left, -top, -right, -bottom); } + if (!backdrop_rect.IsEmpty() && use_aa) { + const int kOutsetForAntialiasing = 1; + backdrop_rect.Inset(-kOutsetForAntialiasing, -kOutsetForAntialiasing); + } + backdrop_rect.Intersect( MoveFromDrawToWindowSpace(frame->current_render_pass->output_rect)); return backdrop_rect; @@ -932,7 +842,6 @@ DrawingFrame* frame, const RenderPassDrawQuad* quad, const gfx::Transform& contents_device_transform_inverse, - ScopedResource* device_background_texture, 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 @@ -959,18 +868,9 @@ // TODO(danakj): When this algorithm changes, update // LayerTreeHost::PrioritizeTextures() accordingly. - DCHECK(device_background_texture); + DCHECK(filtered_device_background); - int filtered_device_background_texture_id = 0; - scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; - if (filtered_device_background) { - GrTexture* texture = filtered_device_background->getTexture(); - filtered_device_background_texture_id = texture->getTextureHandle(); - } else { - lock.reset(new ResourceProvider::ScopedReadLockGL( - resource_provider_, device_background_texture->id())); - filtered_device_background_texture_id = lock->texture_id(); - } + GrTexture* texture = filtered_device_background->getTexture(); scoped_ptr<ScopedResource> background_texture = ScopedResource::Create(resource_provider_); @@ -1004,7 +904,7 @@ bool flip_vertically = false; CopyTextureToFramebuffer(frame, - filtered_device_background_texture_id, + texture->getTextureHandle(), backdrop_bounding_rect, device_to_framebuffer_transform, flip_vertically); @@ -1020,9 +920,9 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, const RenderPassDrawQuad* quad) { SkXfermode::Mode blend_mode = quad->shared_quad_state->blend_mode; - SetBlendEnabled(quad->ShouldDrawWithBlending() || - (!IsDefaultBlendMode(blend_mode) && - CanApplyBlendModeUsingBlendFunc(blend_mode))); + SetBlendEnabled( + CanApplyBlendModeUsingBlendFunc(blend_mode) && + (quad->ShouldDrawWithBlending() || !IsDefaultBlendMode(blend_mode))); ScopedResource* contents_texture = render_pass_textures_.get(quad->render_pass_id); @@ -1041,44 +941,70 @@ if (!contents_device_transform.GetInverse(&contents_device_transform_inverse)) return; + bool clipped = false; + gfx::QuadF device_quad = MathUtil::MapQuad( + contents_device_transform, SharedGeometryQuad(), &clipped); + // Use anti-aliasing programs only when necessary. + bool use_aa = + !clipped && + (settings_->force_antialiasing || !device_quad.IsRectilinear() || + !gfx::IsNearestRectWithinDistance(device_quad.BoundingBox(), + kAntiAliasingEpsilon)); + bool need_background_texture = !CanApplyBlendModeUsingBlendFunc(blend_mode) || ShouldApplyBackgroundFilters(frame, quad); scoped_ptr<ScopedResource> background_texture; + skia::RefPtr<SkImage> background_image; + gfx::Rect background_rect; if (need_background_texture) { + // 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); - // Compute a bounding box around the pixels that will be visible through - // the quad. - gfx::Rect backdrop_rect = GetBackdropBoundingBoxForRenderPassQuad( - frame, quad, contents_device_transform); - // Read the pixels in the bounding box into a buffer R. scoped_ptr<ScopedResource> scoped_background_texture = - GetBackdropTexture(backdrop_rect); + GetBackdropTexture(background_rect); skia::RefPtr<SkImage> background_with_filters; - if (ShouldApplyBackgroundFilters(frame, quad)) { + 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()); } - // 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_inverse, - scoped_background_texture.get(), - background_with_filters, - backdrop_rect); + + 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_inverse, + 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 (disable_blending) SetBlendEnabled(true); @@ -1116,49 +1042,27 @@ } } - if (background_texture) { - if (CanApplyBlendModeUsingBlendFunc(blend_mode)) { - // Draw the background texture if it has some filters applied. - DCHECK(ShouldApplyBackgroundFilters(frame, quad)); - DCHECK(background_texture->size() == quad->rect.size()); - ResourceProvider::ScopedReadLockGL lock(resource_provider_, - background_texture->id()); + 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; + // 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); - } else { - // If blending is applied using shaders, the background texture with - // filters will be used as backdrop for blending operation, so we don't - // need to copy it to the frame buffer. - filter_image = - ApplyBlendModeWithBackdrop(ScopedUseGrContext::Create(this, frame), - resource_provider_, - filter_image, - contents_texture, - background_texture.get(), - quad->shared_quad_state->blend_mode); - } + CopyTextureToFramebuffer(frame, + lock.texture_id(), + quad->rect, + quad->quadTransform(), + flip_vertically); } - bool clipped = false; - gfx::QuadF device_quad = MathUtil::MapQuad( - contents_device_transform, SharedGeometryQuad(), &clipped); LayerQuad device_layer_bounds(gfx::QuadF(device_quad.BoundingBox())); LayerQuad device_layer_edges(device_quad); - - // Use anti-aliasing programs only when necessary. - bool use_aa = - !clipped && (!device_quad.IsRectilinear() || - !gfx::IsNearestRectWithinDistance(device_quad.BoundingBox(), - kAntiAliasingEpsilon)); if (use_aa) { device_layer_bounds.InflateAntiAliasingDistance(); device_layer_edges.InflateAntiAliasingDistance(); @@ -1210,10 +1114,17 @@ int shader_color_matrix_location = -1; int shader_color_offset_location = -1; int shader_tex_transform_location = -1; + int shader_backdrop_location = -1; + int shader_backdrop_rect_location = -1; + + BlendMode shader_blend_mode = ((background_texture || background_image) && + !CanApplyBlendModeUsingBlendFunc(blend_mode)) + ? BlendModeFromSkXfermode(blend_mode) + : BlendModeNormal; if (use_aa && mask_texture_id && !use_color_matrix) { const RenderPassMaskProgramAA* program = - GetRenderPassMaskProgramAA(tex_coord_precision); + GetRenderPassMaskProgramAA(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1230,9 +1141,12 @@ shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (!use_aa && mask_texture_id && !use_color_matrix) { const RenderPassMaskProgram* program = - GetRenderPassMaskProgram(tex_coord_precision); + GetRenderPassMaskProgram(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1246,9 +1160,12 @@ shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (use_aa && !mask_texture_id && !use_color_matrix) { const RenderPassProgramAA* program = - GetRenderPassProgramAA(tex_coord_precision); + GetRenderPassProgramAA(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1259,9 +1176,13 @@ shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (use_aa && mask_texture_id && use_color_matrix) { const RenderPassMaskColorMatrixProgramAA* program = - GetRenderPassMaskColorMatrixProgramAA(tex_coord_precision); + GetRenderPassMaskColorMatrixProgramAA(tex_coord_precision, + shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1282,9 +1203,13 @@ program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (use_aa && !mask_texture_id && use_color_matrix) { const RenderPassColorMatrixProgramAA* program = - GetRenderPassColorMatrixProgramAA(tex_coord_precision); + GetRenderPassColorMatrixProgramAA(tex_coord_precision, + shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1299,9 +1224,13 @@ program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (!use_aa && mask_texture_id && use_color_matrix) { const RenderPassMaskColorMatrixProgram* program = - GetRenderPassMaskColorMatrixProgram(tex_coord_precision); + GetRenderPassMaskColorMatrixProgram(tex_coord_precision, + shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1319,9 +1248,12 @@ program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (!use_aa && !mask_texture_id && use_color_matrix) { const RenderPassColorMatrixProgram* program = - GetRenderPassColorMatrixProgram(tex_coord_precision); + GetRenderPassColorMatrixProgram(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1333,9 +1265,12 @@ program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else { const RenderPassProgram* program = - GetRenderPassProgram(tex_coord_precision); + GetRenderPassProgram(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1343,6 +1278,9 @@ shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } float tex_scale_x = quad->rect.width() / static_cast<float>(contents_texture->size().width()); @@ -1362,6 +1300,7 @@ tex_scale_x, -tex_scale_y)); + GLint last_texture_unit = 0; scoped_ptr<ResourceProvider::ScopedSamplerGL> shader_mask_sampler_lock; if (shader_mask_sampler_location != -1) { DCHECK_NE(shader_mask_tex_coord_scale_location, 1); @@ -1382,6 +1321,8 @@ gl_->Uniform2f(shader_mask_tex_coord_scale_location, mask_uv_rect.width() / tex_scale_x, -mask_uv_rect.height() / tex_scale_y)); + + last_texture_unit = 1; } if (shader_edge_location != -1) { @@ -1417,6 +1358,37 @@ GLC(gl_, gl_->Uniform4fv(shader_color_offset_location, 1, offset)); } + scoped_ptr<ResourceProvider::ScopedSamplerGL> shader_background_sampler_lock; + if (shader_backdrop_location != -1) { + DCHECK(background_texture || background_image); + DCHECK_NE(shader_backdrop_location, 0); + DCHECK_NE(shader_backdrop_rect_location, 0); + + GLC(gl_, gl_->Uniform1i(shader_backdrop_location, ++last_texture_unit)); + + GLC(gl_, + gl_->Uniform4f(shader_backdrop_rect_location, + background_rect.x(), + background_rect.y(), + background_rect.width(), + background_rect.height())); + + if (background_image) { + GrTexture* texture = background_image->getTexture(); + GLC(gl_, gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit)); + gl_->BindTexture(GL_TEXTURE_2D, texture->getTextureHandle()); + GLC(gl_, gl_->ActiveTexture(GL_TEXTURE0)); + } else { + shader_background_sampler_lock = make_scoped_ptr( + new ResourceProvider::ScopedSamplerGL(resource_provider_, + background_texture->id(), + GL_TEXTURE0 + last_texture_unit, + GL_LINEAR)); + DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), + shader_background_sampler_lock->target()); + } + } + // Map device space quad to surface space. contents_device_transform has no 3d // component since it was flattened, so we don't need to project. gfx::QuadF surface_quad = MathUtil::MapQuad(contents_device_transform_inverse, @@ -2352,7 +2324,8 @@ TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( gl_, &highp_threshold_cache_, highp_threshold_min_, rect.bottom_right()); - const RenderPassProgram* program = GetRenderPassProgram(tex_coord_precision); + const RenderPassProgram* program = + GetRenderPassProgram(tex_coord_precision, BlendModeNormal); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -2831,112 +2804,155 @@ } const GLRenderer::RenderPassProgram* GLRenderer::GetRenderPassProgram( - TexCoordPrecision precision) { + TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassProgram* program = &render_pass_program_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassProgram* program = &render_pass_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassProgramAA* GLRenderer::GetRenderPassProgramAA( - TexCoordPrecision precision) { + TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassProgramAA* program = &render_pass_program_aa_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassProgramAA* program = + &render_pass_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskProgram* GLRenderer::GetRenderPassMaskProgram( - TexCoordPrecision precision) { + TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassMaskProgram* program = &render_pass_mask_program_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassMaskProgram* program = + &render_pass_mask_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskProgramAA* -GLRenderer::GetRenderPassMaskProgramAA(TexCoordPrecision precision) { +GLRenderer::GetRenderPassMaskProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassMaskProgramAA* program = &render_pass_mask_program_aa_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassMaskProgramAA* program = + &render_pass_mask_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassColorMatrixProgram* -GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision) { +GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassColorMatrixProgram* program = - &render_pass_color_matrix_program_[precision]; + &render_pass_color_matrix_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassColorMatrixProgramAA* -GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision) { +GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassColorMatrixProgramAA* program = - &render_pass_color_matrix_program_aa_[precision]; + &render_pass_color_matrix_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskColorMatrixProgram* -GLRenderer::GetRenderPassMaskColorMatrixProgram(TexCoordPrecision precision) { +GLRenderer::GetRenderPassMaskColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassMaskColorMatrixProgram* program = - &render_pass_mask_color_matrix_program_[precision]; + &render_pass_mask_color_matrix_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskColorMatrixProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskColorMatrixProgramAA* -GLRenderer::GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision) { +GLRenderer::GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassMaskColorMatrixProgramAA* program = - &render_pass_mask_color_matrix_program_aa_[precision]; + &render_pass_mask_color_matrix_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskColorMatrixProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } @@ -3162,15 +3178,16 @@ tile_program_aa_[i][j].Cleanup(gl_); tile_program_swizzle_aa_[i][j].Cleanup(gl_); } - - render_pass_mask_program_[i].Cleanup(gl_); - render_pass_program_[i].Cleanup(gl_); - render_pass_mask_program_aa_[i].Cleanup(gl_); - render_pass_program_aa_[i].Cleanup(gl_); - render_pass_color_matrix_program_[i].Cleanup(gl_); - render_pass_mask_color_matrix_program_aa_[i].Cleanup(gl_); - render_pass_color_matrix_program_aa_[i].Cleanup(gl_); - render_pass_mask_color_matrix_program_[i].Cleanup(gl_); + for (int j = 0; j < NumBlendModes; j++) { + render_pass_mask_program_[i][j].Cleanup(gl_); + render_pass_program_[i][j].Cleanup(gl_); + render_pass_mask_program_aa_[i][j].Cleanup(gl_); + render_pass_program_aa_[i][j].Cleanup(gl_); + render_pass_color_matrix_program_[i][j].Cleanup(gl_); + render_pass_mask_color_matrix_program_aa_[i][j].Cleanup(gl_); + render_pass_color_matrix_program_aa_[i][j].Cleanup(gl_); + render_pass_mask_color_matrix_program_[i][j].Cleanup(gl_); + } texture_program_[i].Cleanup(gl_); nonpremultiplied_texture_program_[i].Cleanup(gl_);
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index a379dc0..676e7b7 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h
@@ -153,7 +153,8 @@ gfx::Rect GetBackdropBoundingBoxForRenderPassQuad( DrawingFrame* frame, const RenderPassDrawQuad* quad, - const gfx::Transform& contents_device_transform); + const gfx::Transform& contents_device_transform, + bool use_aa); scoped_ptr<ScopedResource> GetBackdropTexture(const gfx::Rect& bounding_rect); static bool ShouldApplyBackgroundFilters(DrawingFrame* frame, @@ -166,7 +167,6 @@ DrawingFrame* frame, const RenderPassDrawQuad* quad, const gfx::Transform& contents_device_transform_inverse, - ScopedResource* background_texture, skia::RefPtr<SkImage> backdrop_bitmap, const gfx::Rect& backdrop_bounding_rect); @@ -328,22 +328,28 @@ const TileCheckerboardProgram* GetTileCheckerboardProgram(); - const RenderPassProgram* GetRenderPassProgram( - TexCoordPrecision precision); - const RenderPassProgramAA* GetRenderPassProgramAA( - TexCoordPrecision precision); + const RenderPassProgram* GetRenderPassProgram(TexCoordPrecision precision, + BlendMode blend_mode); + const RenderPassProgramAA* GetRenderPassProgramAA(TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskProgram* GetRenderPassMaskProgram( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskProgramAA* GetRenderPassMaskProgramAA( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassColorMatrixProgram* GetRenderPassColorMatrixProgram( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassColorMatrixProgramAA* GetRenderPassColorMatrixProgramAA( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskColorMatrixProgram* GetRenderPassMaskColorMatrixProgram( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskColorMatrixProgramAA* - GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision); + GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode); const TextureProgram* GetTextureProgram( TexCoordPrecision precision); @@ -388,18 +394,21 @@ nonpremultiplied_texture_background_program_[NumTexCoordPrecisions]; TextureProgram texture_io_surface_program_[NumTexCoordPrecisions]; - RenderPassProgram render_pass_program_[NumTexCoordPrecisions]; - RenderPassProgramAA render_pass_program_aa_[NumTexCoordPrecisions]; - RenderPassMaskProgram render_pass_mask_program_[NumTexCoordPrecisions]; - RenderPassMaskProgramAA render_pass_mask_program_aa_[NumTexCoordPrecisions]; + RenderPassProgram render_pass_program_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassProgramAA + render_pass_program_aa_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskProgram + render_pass_mask_program_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskProgramAA + render_pass_mask_program_aa_[NumTexCoordPrecisions][NumBlendModes]; RenderPassColorMatrixProgram - render_pass_color_matrix_program_[NumTexCoordPrecisions]; - RenderPassColorMatrixProgramAA - render_pass_color_matrix_program_aa_[NumTexCoordPrecisions]; - RenderPassMaskColorMatrixProgram - render_pass_mask_color_matrix_program_[NumTexCoordPrecisions]; - RenderPassMaskColorMatrixProgramAA - render_pass_mask_color_matrix_program_aa_[NumTexCoordPrecisions]; + render_pass_color_matrix_program_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassColorMatrixProgramAA render_pass_color_matrix_program_aa_ + [NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskColorMatrixProgram render_pass_mask_color_matrix_program_ + [NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskColorMatrixProgramAA render_pass_mask_color_matrix_program_aa_ + [NumTexCoordPrecisions][NumBlendModes]; VideoYUVProgram video_yuv_program_[NumTexCoordPrecisions]; VideoYUVAProgram video_yuva_program_[NumTexCoordPrecisions];
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc index 42e4b5e..8de90c4 100644 --- a/cc/output/gl_renderer_unittest.cc +++ b/cc/output/gl_renderer_unittest.cc
@@ -55,6 +55,44 @@ EXPECT_TRUE((program_binding)->initialized()); \ } while (false) +static inline SkXfermode::Mode BlendModeToSkXfermode(BlendMode blend_mode) { + switch (blend_mode) { + case BlendModeNormal: + return SkXfermode::kSrcOver_Mode; + case BlendModeOverlay: + return SkXfermode::kOverlay_Mode; + case BlendModeDarken: + return SkXfermode::kDarken_Mode; + case BlendModeLighten: + return SkXfermode::kLighten_Mode; + case BlendModeColorDodge: + return SkXfermode::kColorDodge_Mode; + case BlendModeColorBurn: + return SkXfermode::kColorBurn_Mode; + case BlendModeHardLight: + return SkXfermode::kHardLight_Mode; + case BlendModeSoftLight: + return SkXfermode::kSoftLight_Mode; + case BlendModeDifference: + return SkXfermode::kDifference_Mode; + case BlendModeExclusion: + return SkXfermode::kExclusion_Mode; + case BlendModeMultiply: + return SkXfermode::kMultiply_Mode; + case BlendModeHue: + return SkXfermode::kHue_Mode; + case BlendModeSaturation: + return SkXfermode::kSaturation_Mode; + case BlendModeColor: + return SkXfermode::kColor_Mode; + case BlendModeLuminosity: + return SkXfermode::kLuminosity_Mode; + case NumBlendModes: + NOTREACHED(); + } + return SkXfermode::kSrcOver_Mode; +} + // Explicitly named to be a friend in GLRenderer for shader access. class GLRendererShaderPixelTest : public GLRendererPixelTest { public: @@ -70,18 +108,25 @@ } void TestShadersWithTexCoordPrecision(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassProgram(precision)); - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassProgramAA(precision)); - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgram(precision)); - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgramAA(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassColorMatrixProgram(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassMaskColorMatrixProgramAA(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassColorMatrixProgramAA(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassMaskColorMatrixProgram(precision)); + for (int i = 0; i < NumBlendModes; ++i) { + BlendMode blend_mode = static_cast<BlendMode>(i); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassProgram(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassProgramAA(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassMaskProgram(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassMaskProgramAA(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassColorMatrixProgram(precision, blend_mode)); + EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgramAA( + precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassColorMatrixProgramAA(precision, blend_mode)); + EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgram( + precision, blend_mode)); + } EXPECT_PROGRAM_VALID(renderer()->GetTextureProgram(precision)); EXPECT_PROGRAM_VALID( renderer()->GetNonPremultipliedTextureProgram(precision)); @@ -159,8 +204,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); renderer_ = make_scoped_ptr(new FakeRendererGL(&renderer_client_, &settings_, output_surface_.get(), @@ -196,69 +240,91 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); renderer_.reset(new FakeRendererGL(&renderer_client_, &settings_, output_surface_.get(), resource_provider_.get())); } - void TestRenderPassProgram(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_program_[precision]); - EXPECT_EQ(renderer_->render_pass_program_[precision].program(), - renderer_->program_shadow_); - } - - void TestRenderPassColorMatrixProgram(TexCoordPrecision precision) { + void TestRenderPassProgram(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_color_matrix_program_[precision]); - EXPECT_EQ(renderer_->render_pass_color_matrix_program_[precision].program(), + &renderer_->render_pass_program_[precision][blend_mode]); + EXPECT_EQ(renderer_->render_pass_program_[precision][blend_mode].program(), renderer_->program_shadow_); } - void TestRenderPassMaskProgram(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_mask_program_[precision]); - EXPECT_EQ(renderer_->render_pass_mask_program_[precision].program(), - renderer_->program_shadow_); - } - - void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision) { + void TestRenderPassColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_mask_color_matrix_program_[precision]); + &renderer_->render_pass_color_matrix_program_[precision][blend_mode]); EXPECT_EQ( - renderer_->render_pass_mask_color_matrix_program_[precision].program(), + renderer_->render_pass_color_matrix_program_[precision][blend_mode] + .program(), renderer_->program_shadow_); } - void TestRenderPassProgramAA(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_program_aa_[precision]); - EXPECT_EQ(renderer_->render_pass_program_aa_[precision].program(), - renderer_->program_shadow_); - } - - void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision) { + void TestRenderPassMaskProgram(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_color_matrix_program_aa_[precision]); + &renderer_->render_pass_mask_program_[precision][blend_mode]); EXPECT_EQ( - renderer_->render_pass_color_matrix_program_aa_[precision].program(), + renderer_->render_pass_mask_program_[precision][blend_mode].program(), renderer_->program_shadow_); } - void TestRenderPassMaskProgramAA(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_mask_program_aa_[precision]); - EXPECT_EQ(renderer_->render_pass_mask_program_aa_[precision].program(), - renderer_->program_shadow_); + void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID( + &renderer_ + ->render_pass_mask_color_matrix_program_[precision][blend_mode]); + EXPECT_EQ( + renderer_->render_pass_mask_color_matrix_program_[precision][blend_mode] + .program(), + renderer_->program_shadow_); } - void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision) { + void TestRenderPassProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_mask_color_matrix_program_aa_[precision]); - EXPECT_EQ(renderer_->render_pass_mask_color_matrix_program_aa_[precision] + &renderer_->render_pass_program_aa_[precision][blend_mode]); + EXPECT_EQ( + renderer_->render_pass_program_aa_[precision][blend_mode].program(), + renderer_->program_shadow_); + } + + void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID( + &renderer_ + ->render_pass_color_matrix_program_aa_[precision][blend_mode]); + EXPECT_EQ( + renderer_->render_pass_color_matrix_program_aa_[precision][blend_mode] + .program(), + renderer_->program_shadow_); + } + + void TestRenderPassMaskProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID( + &renderer_->render_pass_mask_program_aa_[precision][blend_mode]); + EXPECT_EQ(renderer_->render_pass_mask_program_aa_[precision][blend_mode] .program(), renderer_->program_shadow_); } + void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID(&renderer_->render_pass_mask_color_matrix_program_aa_ + [precision][blend_mode]); + EXPECT_EQ( + renderer_ + ->render_pass_mask_color_matrix_program_aa_[precision][blend_mode] + .program(), + renderer_->program_shadow_); + } + void TestSolidColorProgramAA() { EXPECT_PROGRAM_VALID(&renderer_->solid_color_program_aa_); EXPECT_EQ(renderer_->solid_color_program_aa_.program(), @@ -470,8 +536,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -511,8 +576,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -551,8 +615,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -604,8 +667,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -650,8 +712,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -736,8 +797,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -808,8 +868,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -900,8 +959,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; settings.should_clear_root_render_pass = false; @@ -999,8 +1057,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -1098,8 +1155,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; settings.partial_swap_enabled = true; @@ -1288,8 +1344,7 @@ NULL, 0, false, - 1, - false)); + 1)); LayerTreeSettings settings; FakeRendererClient renderer_client; @@ -1361,191 +1416,224 @@ gfx::Transform transform_causing_aa; transform_causing_aa.Rotate(20.0); - // RenderPassProgram - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - gfx::Transform()); + for (int i = 0; i < NumBlendModes; ++i) { + BlendMode blend_mode = static_cast<BlendMode>(i); + SkXfermode::Mode xfer_mode = BlendModeToSkXfermode(blend_mode); + // RenderPassProgram + render_passes_in_draw_order_.clear(); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + gfx::Transform()); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, 0, FilterOperations(), gfx::Transform()); + AddRenderPassQuad(root_pass, + child_pass, + 0, + FilterOperations(), + gfx::Transform(), + xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassProgram(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassProgram(TexCoordPrecisionMedium, blend_mode); - // RenderPassColorMatrixProgram - render_passes_in_draw_order_.clear(); + // RenderPassColorMatrixProgram + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad(root_pass, child_pass, 0, filters, gfx::Transform()); + AddRenderPassQuad( + root_pass, child_pass, 0, filters, gfx::Transform(), xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassColorMatrixProgram(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassColorMatrixProgram(TexCoordPrecisionMedium, blend_mode); - // RenderPassMaskProgram - render_passes_in_draw_order_.clear(); + // RenderPassMaskProgram + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - gfx::Transform()); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + gfx::Transform()); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, mask, FilterOperations(), gfx::Transform()); + AddRenderPassQuad(root_pass, + child_pass, + mask, + FilterOperations(), + gfx::Transform(), + xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskProgram(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskProgram(TexCoordPrecisionMedium, blend_mode); - // RenderPassMaskColorMatrixProgram - render_passes_in_draw_order_.clear(); + // RenderPassMaskColorMatrixProgram + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - gfx::Transform()); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + gfx::Transform()); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad(root_pass, child_pass, mask, filters, gfx::Transform()); + AddRenderPassQuad( + root_pass, child_pass, mask, filters, gfx::Transform(), xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskColorMatrixProgram(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskColorMatrixProgram(TexCoordPrecisionMedium, blend_mode); - // RenderPassProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, 0, FilterOperations(), transform_causing_aa); + AddRenderPassQuad(root_pass, + child_pass, + 0, + FilterOperations(), + transform_causing_aa, + xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassProgramAA(TexCoordPrecisionMedium, blend_mode); - // RenderPassColorMatrixProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassColorMatrixProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad(root_pass, child_pass, 0, filters, transform_causing_aa); + AddRenderPassQuad( + root_pass, child_pass, 0, filters, transform_causing_aa, xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassColorMatrixProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassColorMatrixProgramAA(TexCoordPrecisionMedium, blend_mode); - // RenderPassMaskProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassMaskProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, mask, FilterOperations(), transform_causing_aa); + AddRenderPassQuad(root_pass, + child_pass, + mask, + FilterOperations(), + transform_causing_aa, + xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskProgramAA(TexCoordPrecisionMedium, blend_mode); - // RenderPassMaskColorMatrixProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassMaskColorMatrixProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - transform_causing_aa); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + transform_causing_aa); - AddRenderPassQuad(root_pass, child_pass, mask, filters, transform_causing_aa); + AddRenderPassQuad( + root_pass, child_pass, mask, filters, transform_causing_aa, xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecisionMedium, blend_mode); + } } // At this time, the AA code path cannot be taken if the surface's rect would @@ -1580,8 +1668,12 @@ viewport_rect, gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, 0, FilterOperations(), transform_preventing_aa); + AddRenderPassQuad(root_pass, + child_pass, + 0, + FilterOperations(), + transform_preventing_aa, + SkXfermode::kSrcOver_Mode); renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); renderer_->DrawFrame(&render_passes_in_draw_order_, @@ -1592,7 +1684,7 @@ // If use_aa incorrectly ignores clipping, it will use the // RenderPassProgramAA shader instead of the RenderPassProgram. - TestRenderPassProgram(TexCoordPrecisionMedium); + TestRenderPassProgram(TexCoordPrecisionMedium, BlendModeNormal); } TEST_F(GLRendererShaderTest, DrawSolidColorShader) { @@ -1668,8 +1760,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); renderer_.reset(new FakeRendererGL(&renderer_client_, &settings_,
diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc index 92b19c7..8b7dc2a 100644 --- a/cc/output/overlay_unittest.cc +++ b/cc/output/overlay_unittest.cc
@@ -244,15 +244,8 @@ scoped_ptr<SharedBitmapManager> shared_bitmap_manager( new TestSharedBitmapManager()); - scoped_ptr<ResourceProvider> resource_provider( - ResourceProvider::Create(&output_surface, - shared_bitmap_manager.get(), - NULL, - NULL, - 0, - false, - 1, - false)); + scoped_ptr<ResourceProvider> resource_provider(ResourceProvider::Create( + &output_surface, shared_bitmap_manager.get(), NULL, NULL, 0, false, 1)); scoped_ptr<DefaultOverlayProcessor> overlay_processor( new DefaultOverlayProcessor(&output_surface, resource_provider.get())); @@ -276,8 +269,7 @@ NULL, 0, false, - 1, - false); + 1); overlay_processor_.reset(new SingleOverlayProcessor( output_surface_.get(), resource_provider_.get())); @@ -588,7 +580,7 @@ output_surface_.reset(new OverlayOutputSurface(provider_)); CHECK(output_surface_->BindToClient(&output_surface_client_)); resource_provider_ = ResourceProvider::Create( - output_surface_.get(), NULL, NULL, NULL, 0, false, 1, false); + output_surface_.get(), NULL, NULL, NULL, 0, false, 1); provider_->support()->SetScheduleOverlayPlaneCallback(base::Bind( &MockOverlayScheduler::Schedule, base::Unretained(&scheduler_)));
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h index 912329e..722c782 100644 --- a/cc/output/program_binding.h +++ b/cc/output/program_binding.h
@@ -58,13 +58,16 @@ void Initialize(ContextProvider* context_provider, TexCoordPrecision precision, - SamplerType sampler) { + SamplerType sampler, + BlendMode blend_mode = BlendModeNormal) { DCHECK(context_provider); DCHECK(!initialized_); if (context_provider->IsContextLost()) return; + fragment_shader_.set_blend_mode(blend_mode); + if (!ProgramBindingBase::Init( context_provider->ContextGL(), vertex_shader_.GetShaderString(),
diff --git a/cc/output/renderer_unittest.cc b/cc/output/renderer_unittest.cc index 229558d..6e43cc0 100644 --- a/cc/output/renderer_unittest.cc +++ b/cc/output/renderer_unittest.cc
@@ -83,7 +83,7 @@ output_surface_.reset(new TestOutputSurface(context_provider_)); output_surface_->BindToClient(&output_surface_client_); resource_provider_ = ResourceProvider::Create( - output_surface_.get(), NULL, NULL, NULL, 0, false, 1, false); + output_surface_.get(), NULL, NULL, NULL, 0, false, 1); renderer_ = CreateRenderer<T>(&renderer_client_, &tree_settings_, output_surface_.get(),
diff --git a/cc/output/shader.cc b/cc/output/shader.cc index 8a25214..98bde8d 100644 --- a/cc/output/shader.cc +++ b/cc/output/shader.cc
@@ -13,9 +13,10 @@ #define SHADER0(Src) #Src #define VERTEX_SHADER(Src) SetVertexTexCoordPrecision(SHADER0(Src)) -#define FRAGMENT_SHADER(Src) \ - SetFragmentTexCoordPrecision(precision, \ - SetFragmentSamplerType(sampler, SHADER0(Src))) +#define FRAGMENT_SHADER(Src) \ + SetFragmentTexCoordPrecision( \ + precision, \ + SetFragmentSamplerType(sampler, SetBlendModeFunctions(SHADER0(Src)))) using gpu::gles2::GLES2Interface; @@ -666,6 +667,308 @@ // clang-format on } +#define BLEND_MODE_UNIFORMS "s_backdropTexture", "backdropRect" +#define UNUSED_BLEND_MODE_UNIFORMS (is_default_blend_mode() ? 2 : 0) +#define BLEND_MODE_SET_LOCATIONS(X, POS) \ + if (!is_default_blend_mode()) { \ + DCHECK_LT(static_cast<size_t>(POS) + 1, arraysize(X)); \ + backdrop_location_ = locations[POS]; \ + backdrop_rect_location_ = locations[POS + 1]; \ + } + +FragmentTexBlendMode::FragmentTexBlendMode() + : backdrop_location_(-1), + backdrop_rect_location_(-1), + blend_mode_(BlendModeNormal) { +} + +std::string FragmentTexBlendMode::SetBlendModeFunctions( + std::string shader_string) const { + if (shader_string.find("ApplyBlendMode") == std::string::npos) + return shader_string; + + if (is_default_blend_mode()) { + return "#define ApplyBlendMode(X) (X)\n" + shader_string; + } + + // clang-format off + static const std::string kFunctionApplyBlendMode = SHADER0( + // clang-format on + uniform SamplerType s_backdropTexture; + uniform TexCoordPrecision vec4 backdropRect; + + vec4 GetBackdropColor() { + TexCoordPrecision vec2 bgTexCoord = gl_FragCoord.xy - backdropRect.xy; + bgTexCoord.x /= backdropRect.z; + bgTexCoord.y /= backdropRect.w; + return TextureLookup(s_backdropTexture, bgTexCoord); + } + + vec4 ApplyBlendMode(vec4 src) { + vec4 dst = GetBackdropColor(); + return Blend(src, dst); + } + // clang-format off + ); + // clang-format on + + return "precision mediump float;" + GetHelperFunctions() + + GetBlendFunction() + kFunctionApplyBlendMode + shader_string; +} + +std::string FragmentTexBlendMode::GetHelperFunctions() const { + // clang-format off + static const std::string kFunctionHardLight = SHADER0( + // clang-format on + vec3 hardLight(vec4 src, vec4 dst) { + vec3 result; + result.r = + (2.0 * src.r <= src.a) + ? (2.0 * src.r * dst.r) + : (src.a * dst.a - 2.0 * (dst.a - dst.r) * (src.a - src.r)); + result.g = + (2.0 * src.g <= src.a) + ? (2.0 * src.g * dst.g) + : (src.a * dst.a - 2.0 * (dst.a - dst.g) * (src.a - src.g)); + result.b = + (2.0 * src.b <= src.a) + ? (2.0 * src.b * dst.b) + : (src.a * dst.a - 2.0 * (dst.a - dst.b) * (src.a - src.b)); + result.rgb += src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a); + return result; + } + // clang-format off + ); + + static const std::string kFunctionColorDodgeComponent = SHADER0( + // clang-format on + float getColorDodgeComponent( + float srcc, float srca, float dstc, float dsta) { + if (0.0 == dstc) + return srcc * (1.0 - dsta); + float d = srca - srcc; + if (0.0 == d) + return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + d = min(dsta, dstc * srca / d); + return d * srca + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + } + // clang-format off + ); + + static const std::string kFunctionColorBurnComponent = SHADER0( + // clang-format on + float getColorBurnComponent( + float srcc, float srca, float dstc, float dsta) { + if (dsta == dstc) + return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + if (0.0 == srcc) + return dstc * (1.0 - srca); + float d = max(0.0, dsta - (dsta - dstc) * srca / srcc); + return srca * d + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + } + // clang-format off + ); + + static const std::string kFunctionSoftLightComponentPosDstAlpha = SHADER0( + // clang-format on + float getSoftLightComponent( + float srcc, float srca, float dstc, float dsta) { + if (2.0 * srcc <= srca) { + return (dstc * dstc * (srca - 2.0 * srcc)) / dsta + + (1.0 - dsta) * srcc + dstc * (-srca + 2.0 * srcc + 1.0); + } else if (4.0 * dstc <= dsta) { + float DSqd = dstc * dstc; + float DCub = DSqd * dstc; + float DaSqd = dsta * dsta; + float DaCub = DaSqd * dsta; + return (-DaCub * srcc + + DaSqd * (srcc - dstc * (3.0 * srca - 6.0 * srcc - 1.0)) + + 12.0 * dsta * DSqd * (srca - 2.0 * srcc) - + 16.0 * DCub * (srca - 2.0 * srcc)) / + DaSqd; + } else { + return -sqrt(dsta * dstc) * (srca - 2.0 * srcc) - dsta * srcc + + dstc * (srca - 2.0 * srcc + 1.0) + srcc; + } + } + // clang-format off + ); + + static const std::string kFunctionLum = SHADER0( + // clang-format on + float luminance(vec3 color) { return dot(vec3(0.3, 0.59, 0.11), color); } + + vec3 set_luminance(vec3 hueSat, float alpha, vec3 lumColor) { + float diff = luminance(lumColor - hueSat); + vec3 outColor = hueSat + diff; + float outLum = luminance(outColor); + float minComp = min(min(outColor.r, outColor.g), outColor.b); + float maxComp = max(max(outColor.r, outColor.g), outColor.b); + if (minComp < 0.0) { + outColor = outLum + + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / + (outLum - minComp); + } + if (maxComp > alpha) { + outColor = + outLum + + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / + (maxComp - outLum); + } + return outColor; + } + // clang-format off + ); + + static const std::string kFunctionSat = SHADER0( + // clang-format on + float saturation(vec3 color) { + return max(max(color.r, color.g), color.b) - + min(min(color.r, color.g), color.b); + } + + vec3 set_saturation_helper( + float minComp, float midComp, float maxComp, float sat) { + if (minComp < maxComp) { + vec3 result; + result.r = 0.0; + result.g = sat * (midComp - minComp) / (maxComp - minComp); + result.b = sat; + return result; + } else { + return vec3(0, 0, 0); + } + } + + vec3 set_saturation(vec3 hueLumColor, vec3 satColor) { + float sat = saturation(satColor); + if (hueLumColor.r <= hueLumColor.g) { + if (hueLumColor.g <= hueLumColor.b) { + hueLumColor.rgb = set_saturation_helper( + hueLumColor.r, hueLumColor.g, hueLumColor.b, sat); + } else if (hueLumColor.r <= hueLumColor.b) { + hueLumColor.rbg = set_saturation_helper( + hueLumColor.r, hueLumColor.b, hueLumColor.g, sat); + } else { + hueLumColor.brg = set_saturation_helper( + hueLumColor.b, hueLumColor.r, hueLumColor.g, sat); + } + } else if (hueLumColor.r <= hueLumColor.b) { + hueLumColor.grb = set_saturation_helper( + hueLumColor.g, hueLumColor.r, hueLumColor.b, sat); + } else if (hueLumColor.g <= hueLumColor.b) { + hueLumColor.gbr = set_saturation_helper( + hueLumColor.g, hueLumColor.b, hueLumColor.r, sat); + } else { + hueLumColor.bgr = set_saturation_helper( + hueLumColor.b, hueLumColor.g, hueLumColor.r, sat); + } + return hueLumColor; + } + // clang-format off + ); + // clang-format on + + switch (blend_mode_) { + case BlendModeOverlay: + case BlendModeHardLight: + return kFunctionHardLight; + case BlendModeColorDodge: + return kFunctionColorDodgeComponent; + case BlendModeColorBurn: + return kFunctionColorBurnComponent; + case BlendModeSoftLight: + return kFunctionSoftLightComponentPosDstAlpha; + case BlendModeHue: + case BlendModeSaturation: + return kFunctionLum + kFunctionSat; + case BlendModeColor: + case BlendModeLuminosity: + return kFunctionLum; + default: + return std::string(); + } +} + +std::string FragmentTexBlendMode::GetBlendFunction() const { + return "vec4 Blend(vec4 src, vec4 dst) {" + " vec4 result;" + " result.a = src.a + (1.0 - src.a) * dst.a;" + + GetBlendFunctionBodyForRGB() + + " return result;" + "}"; +} + +std::string FragmentTexBlendMode::GetBlendFunctionBodyForRGB() const { + switch (blend_mode_) { + case BlendModeLighten: + return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb," + " (1.0 - dst.a) * src.rgb + dst.rgb);"; + case BlendModeOverlay: + return "result.rgb = hardLight(dst, src);"; + case BlendModeDarken: + return "result.rgb = min((1.0 - src.a) * dst.rgb + src.rgb," + " (1.0 - dst.a) * src.rgb + dst.rgb);"; + case BlendModeColorDodge: + return "result.r = getColorDodgeComponent(src.r, src.a, dst.r, dst.a);" + "result.g = getColorDodgeComponent(src.g, src.a, dst.g, dst.a);" + "result.b = getColorDodgeComponent(src.b, src.a, dst.b, dst.a);"; + case BlendModeColorBurn: + return "result.r = getColorBurnComponent(src.r, src.a, dst.r, dst.a);" + "result.g = getColorBurnComponent(src.g, src.a, dst.g, dst.a);" + "result.b = getColorBurnComponent(src.b, src.a, dst.b, dst.a);"; + case BlendModeHardLight: + return "result.rgb = hardLight(src, dst);"; + case BlendModeSoftLight: + return "if (0.0 == dst.a) {" + " result.rgb = src.rgb;" + "} else {" + " result.r = getSoftLightComponent(src.r, src.a, dst.r, dst.a);" + " result.g = getSoftLightComponent(src.g, src.a, dst.g, dst.a);" + " result.b = getSoftLightComponent(src.b, src.a, dst.b, dst.a);" + "}"; + case BlendModeDifference: + return "result.rgb = src.rgb + dst.rgb -" + " 2.0 * min(src.rgb * dst.a, dst.rgb * src.a);"; + case BlendModeExclusion: + return "result.rgb = dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb;"; + case BlendModeMultiply: + return "result.rgb = (1.0 - src.a) * dst.rgb +" + " (1.0 - dst.a) * src.rgb + src.rgb * dst.rgb;"; + case BlendModeHue: + return "vec4 dstSrcAlpha = dst * src.a;" + "result.rgb =" + " set_luminance(set_saturation(src.rgb * dst.a," + " dstSrcAlpha.rgb)," + " dstSrcAlpha.a," + " dstSrcAlpha.rgb);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + case BlendModeSaturation: + return "vec4 dstSrcAlpha = dst * src.a;" + "result.rgb = set_luminance(set_saturation(dstSrcAlpha.rgb," + " src.rgb * dst.a)," + " dstSrcAlpha.a," + " dstSrcAlpha.rgb);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + case BlendModeColor: + return "vec4 srcDstAlpha = src * dst.a;" + "result.rgb = set_luminance(srcDstAlpha.rgb," + " srcDstAlpha.a," + " dst.rgb * src.a);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + case BlendModeLuminosity: + return "vec4 srcDstAlpha = src * dst.a;" + "result.rgb = set_luminance(dst.rgb * src.a," + " srcDstAlpha.a," + " srcDstAlpha.rgb);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + default: + NOTREACHED(); + // simple alpha compositing + return "result.rgb = src.rgb * src.a + dst.rgb * dst.a * (1 - src.a)"; + } +} + FragmentTexAlphaBinding::FragmentTexAlphaBinding() : sampler_location_(-1), alpha_location_(-1) { } @@ -674,18 +977,19 @@ unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", + "s_texture", "alpha", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); sampler_location_ = locations[0]; alpha_location_ = locations[1]; + BLEND_MODE_SET_LOCATIONS(locations, 2); } FragmentTexColorMatrixAlphaBinding::FragmentTexColorMatrixAlphaBinding() @@ -699,13 +1003,13 @@ unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", "colorMatrix", "colorOffset", + "s_texture", "alpha", "colorMatrix", "colorOffset", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -713,6 +1017,7 @@ alpha_location_ = locations[1]; color_matrix_location_ = locations[2]; color_offset_location_ = locations[3]; + BLEND_MODE_SET_LOCATIONS(locations, 4); } FragmentTexOpaqueBinding::FragmentTexOpaqueBinding() : sampler_location_(-1) { @@ -747,7 +1052,7 @@ uniform float alpha; void main() { vec4 texColor = TextureLookup(s_texture, v_texCoord); - gl_FragColor = texColor * alpha; + gl_FragColor = ApplyBlendMode(texColor * alpha); } // clang-format off ); // NOLINT(whitespace/parens) @@ -773,7 +1078,7 @@ texColor = colorMatrix * texColor + colorOffset; texColor.rgb *= texColor.a; texColor = clamp(texColor, 0.0, 1.0); - gl_FragColor = texColor * alpha; + gl_FragColor = ApplyBlendMode(texColor * alpha); } // clang-format off ); // NOLINT(whitespace/parens) @@ -966,18 +1271,19 @@ unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", + "s_texture", "alpha", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); sampler_location_ = locations[0]; alpha_location_ = locations[1]; + BLEND_MODE_SET_LOCATIONS(locations, 2); } std::string FragmentShaderRGBATexAlphaAA::GetShaderString( @@ -997,7 +1303,7 @@ vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1097,13 +1403,18 @@ unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "s_mask", "alpha", "maskTexCoordScale", "maskTexCoordOffset", + "s_texture", + "s_mask", + "alpha", + "maskTexCoordScale", + "maskTexCoordOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1112,6 +1423,7 @@ alpha_location_ = locations[2]; mask_tex_coord_scale_location_ = locations[3]; mask_tex_coord_offset_location_ = locations[4]; + BLEND_MODE_SET_LOCATIONS(locations, 5); } std::string FragmentShaderRGBATexAlphaMask::GetShaderString( @@ -1133,7 +1445,7 @@ vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y); vec4 maskColor = TextureLookup(s_mask, maskTexCoord); - gl_FragColor = texColor * alpha * maskColor.w; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1152,13 +1464,18 @@ unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "s_mask", "alpha", "maskTexCoordScale", "maskTexCoordOffset", + "s_texture", + "s_mask", + "alpha", + "maskTexCoordScale", + "maskTexCoordOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1167,6 +1484,7 @@ alpha_location_ = locations[2]; mask_tex_coord_scale_location_ = locations[3]; mask_tex_coord_offset_location_ = locations[4]; + BLEND_MODE_SET_LOCATIONS(locations, 5); } std::string FragmentShaderRGBATexAlphaMaskAA::GetShaderString( @@ -1193,7 +1511,7 @@ vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * maskColor.w * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1222,12 +1540,13 @@ "maskTexCoordOffset", "colorMatrix", "colorOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1238,6 +1557,7 @@ mask_tex_coord_offset_location_ = locations[4]; color_matrix_location_ = locations[5]; color_offset_location_ = locations[6]; + BLEND_MODE_SET_LOCATIONS(locations, 7); } std::string FragmentShaderRGBATexAlphaMaskColorMatrixAA::GetShaderString( @@ -1271,7 +1591,7 @@ vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * maskColor.w * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1290,13 +1610,13 @@ unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", "colorMatrix", "colorOffset", + "s_texture", "alpha", "colorMatrix", "colorOffset", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1304,6 +1624,7 @@ alpha_location_ = locations[1]; color_matrix_location_ = locations[2]; color_offset_location_ = locations[3]; + BLEND_MODE_SET_LOCATIONS(locations, 4); } std::string FragmentShaderRGBATexAlphaColorMatrixAA::GetShaderString( @@ -1330,7 +1651,7 @@ vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1356,12 +1677,13 @@ "maskTexCoordOffset", "colorMatrix", "colorOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1372,6 +1694,7 @@ mask_tex_coord_offset_location_ = locations[4]; color_matrix_location_ = locations[5]; color_offset_location_ = locations[6]; + BLEND_MODE_SET_LOCATIONS(locations, 7); } std::string FragmentShaderRGBATexAlphaMaskColorMatrix::GetShaderString( @@ -1400,7 +1723,7 @@ vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y); vec4 maskColor = TextureLookup(s_mask, maskTexCoord); - gl_FragColor = texColor * alpha * maskColor.w; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w); } // clang-format off ); // NOLINT(whitespace/parens)
diff --git a/cc/output/shader.h b/cc/output/shader.h index 3039c80..0384366 100644 --- a/cc/output/shader.h +++ b/cc/output/shader.h
@@ -38,6 +38,25 @@ NumSamplerTypes = 4 }; +enum BlendMode { + BlendModeNormal, + BlendModeOverlay, + BlendModeDarken, + BlendModeLighten, + BlendModeColorDodge, + BlendModeColorBurn, + BlendModeHardLight, + BlendModeSoftLight, + BlendModeDifference, + BlendModeExclusion, + BlendModeMultiply, + BlendModeHue, + BlendModeSaturation, + BlendModeColor, + BlendModeLuminosity, + NumBlendModes +}; + // Note: The highp_threshold_cache must be provided by the caller to make // the caching multi-thread/context safe in an easy low-overhead manner. // The caller must make sure to clear highp_threshold_cache to 0, so it can be @@ -279,7 +298,32 @@ DISALLOW_COPY_AND_ASSIGN(VertexShaderVideoTransform); }; -class FragmentTexAlphaBinding { +class FragmentTexBlendMode { + public: + int backdrop_location() const { return backdrop_location_; } + int backdrop_rect_location() const { return backdrop_rect_location_; } + + 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; } + + protected: + FragmentTexBlendMode(); + + std::string SetBlendModeFunctions(std::string shader_string) const; + + int backdrop_location_; + int backdrop_rect_location_; + + private: + BlendMode blend_mode_; + + std::string GetHelperFunctions() const; + std::string GetBlendFunction() const; + std::string GetBlendFunctionBodyForRGB() const; +}; + +class FragmentTexAlphaBinding : public FragmentTexBlendMode { public: FragmentTexAlphaBinding(); @@ -297,7 +341,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentTexAlphaBinding); }; -class FragmentTexColorMatrixAlphaBinding { +class FragmentTexColorMatrixAlphaBinding : public FragmentTexBlendMode { public: FragmentTexColorMatrixAlphaBinding(); @@ -317,7 +361,7 @@ int color_offset_location_; }; -class FragmentTexOpaqueBinding { +class FragmentTexOpaqueBinding : public FragmentTexBlendMode { public: FragmentTexOpaqueBinding(); @@ -335,7 +379,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentTexOpaqueBinding); }; -class FragmentTexBackgroundBinding { +class FragmentTexBackgroundBinding : public FragmentTexBlendMode { public: FragmentTexBackgroundBinding(); @@ -417,7 +461,7 @@ TexCoordPrecision precision, SamplerType sampler) const; }; -class FragmentShaderRGBATexAlphaAA { +class FragmentShaderRGBATexAlphaAA : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaAA(); @@ -437,7 +481,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaAA); }; -class FragmentTexClampAlphaAABinding { +class FragmentTexClampAlphaAABinding : public FragmentTexBlendMode { public: FragmentTexClampAlphaAABinding(); @@ -473,7 +517,7 @@ TexCoordPrecision precision, SamplerType sampler) const; }; -class FragmentShaderRGBATexAlphaMask { +class FragmentShaderRGBATexAlphaMask : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMask(); std::string GetShaderString( @@ -502,7 +546,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaMask); }; -class FragmentShaderRGBATexAlphaMaskAA { +class FragmentShaderRGBATexAlphaMaskAA : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMaskAA(); std::string GetShaderString( @@ -531,7 +575,8 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaMaskAA); }; -class FragmentShaderRGBATexAlphaMaskColorMatrixAA { +class FragmentShaderRGBATexAlphaMaskColorMatrixAA + : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMaskColorMatrixAA(); std::string GetShaderString( @@ -562,7 +607,7 @@ int color_offset_location_; }; -class FragmentShaderRGBATexAlphaColorMatrixAA { +class FragmentShaderRGBATexAlphaColorMatrixAA : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaColorMatrixAA(); std::string GetShaderString( @@ -583,7 +628,7 @@ int color_offset_location_; }; -class FragmentShaderRGBATexAlphaMaskColorMatrix { +class FragmentShaderRGBATexAlphaMaskColorMatrix : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMaskColorMatrix(); std::string GetShaderString( @@ -614,7 +659,7 @@ int color_offset_location_; }; -class FragmentShaderYUVVideo { +class FragmentShaderYUVVideo : public FragmentTexBlendMode { public: FragmentShaderYUVVideo(); std::string GetShaderString( @@ -641,8 +686,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderYUVVideo); }; - -class FragmentShaderYUVAVideo { +class FragmentShaderYUVAVideo : public FragmentTexBlendMode { public: FragmentShaderYUVAVideo(); std::string GetShaderString( @@ -672,7 +716,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderYUVAVideo); }; -class FragmentShaderColor { +class FragmentShaderColor : public FragmentTexBlendMode { public: FragmentShaderColor(); std::string GetShaderString( @@ -689,7 +733,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderColor); }; -class FragmentShaderColorAA { +class FragmentShaderColorAA : public FragmentTexBlendMode { public: FragmentShaderColorAA(); std::string GetShaderString( @@ -706,7 +750,7 @@ DISALLOW_COPY_AND_ASSIGN(FragmentShaderColorAA); }; -class FragmentShaderCheckerboard { +class FragmentShaderCheckerboard : public FragmentTexBlendMode { public: FragmentShaderCheckerboard(); std::string GetShaderString(
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc index c3cb828..acc897f 100644 --- a/cc/output/software_renderer.cc +++ b/cc/output/software_renderer.cc
@@ -239,14 +239,16 @@ current_canvas_->setMatrix(sk_device_matrix); current_paint_.reset(); - if (!IsScaleAndIntegerTranslate(sk_device_matrix)) { + if (settings_->force_antialiasing || + !IsScaleAndIntegerTranslate(sk_device_matrix)) { // TODO(danakj): Until we can enable AA only on exterior edges of the // layer, disable AA if any interior edges are present. crbug.com/248175 bool all_four_edges_are_exterior = quad->IsTopEdge() && quad->IsLeftEdge() && quad->IsBottomEdge() && quad->IsRightEdge(); - if (settings_->allow_antialiasing && all_four_edges_are_exterior) + if (settings_->allow_antialiasing && + (settings_->force_antialiasing || all_four_edges_are_exterior)) current_paint_.setAntiAlias(true); current_paint_.setFilterLevel(SkPaint::kLow_FilterLevel); }
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc index 1e373b2..60f75d9 100644 --- a/cc/output/software_renderer_unittest.cc +++ b/cc/output/software_renderer_unittest.cc
@@ -42,8 +42,7 @@ NULL, 0, false, - 1, - false); + 1); renderer_ = SoftwareRenderer::Create( this, &settings_, output_surface_.get(), resource_provider()); }
diff --git a/cc/resources/gpu_raster_worker_pool.cc b/cc/resources/gpu_raster_worker_pool.cc index a0bc86f..cba427e 100644 --- a/cc/resources/gpu_raster_worker_pool.cc +++ b/cc/resources/gpu_raster_worker_pool.cc
@@ -26,17 +26,25 @@ public: RasterBufferImpl(ResourceProvider* resource_provider, const Resource* resource, - SkMultiPictureDraw* multi_picture_draw) + SkMultiPictureDraw* multi_picture_draw, + bool use_distance_field_text) : lock_(resource_provider, resource->id()), resource_(resource), - multi_picture_draw_(multi_picture_draw) {} + multi_picture_draw_(multi_picture_draw), + use_distance_field_text_(use_distance_field_text) {} // Overridden from RasterBuffer: void Playback(const PicturePileImpl* picture_pile, const gfx::Rect& rect, float scale, RenderingStatsInstrumentation* stats) override { - if (!lock_.sk_surface()) + // Turn on distance fields for layers that have ever animated. + bool use_distance_field_text = + use_distance_field_text_ || + picture_pile->likely_to_be_used_for_transform_animation(); + SkSurface* sk_surface = lock_.GetSkSurface(use_distance_field_text); + + if (!sk_surface) return; SkPictureRecorder recorder; @@ -50,13 +58,14 @@ // Add the canvas and recorded picture to |multi_picture_draw_|. skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording()); - multi_picture_draw_->add(lock_.sk_surface()->getCanvas(), picture.get()); + multi_picture_draw_->add(sk_surface->getCanvas(), picture.get()); } private: ResourceProvider::ScopedWriteLockGr lock_; const Resource* resource_; SkMultiPictureDraw* multi_picture_draw_; + bool use_distance_field_text_; DISALLOW_COPY_AND_ASSIGN(RasterBufferImpl); }; @@ -67,20 +76,26 @@ scoped_ptr<RasterWorkerPool> GpuRasterWorkerPool::Create( base::SequencedTaskRunner* task_runner, ContextProvider* context_provider, - ResourceProvider* resource_provider) { - return make_scoped_ptr<RasterWorkerPool>(new GpuRasterWorkerPool( - task_runner, context_provider, resource_provider)); + ResourceProvider* resource_provider, + bool use_distance_field_text) { + return make_scoped_ptr<RasterWorkerPool>( + new GpuRasterWorkerPool(task_runner, + context_provider, + resource_provider, + use_distance_field_text)); } GpuRasterWorkerPool::GpuRasterWorkerPool(base::SequencedTaskRunner* task_runner, ContextProvider* context_provider, - ResourceProvider* resource_provider) + ResourceProvider* resource_provider, + bool use_distance_field_text) : task_runner_(task_runner), task_graph_runner_(new TaskGraphRunner), namespace_token_(task_graph_runner_->GetNamespaceToken()), context_provider_(context_provider), resource_provider_(resource_provider), run_tasks_on_origin_thread_pending_(false), + use_distance_field_text_(use_distance_field_text), raster_finished_weak_ptr_factory_(this), weak_ptr_factory_(this) { DCHECK(context_provider_); @@ -190,7 +205,10 @@ scoped_ptr<RasterBuffer> GpuRasterWorkerPool::AcquireBufferForRaster( const Resource* resource) { return make_scoped_ptr<RasterBuffer>( - new RasterBufferImpl(resource_provider_, resource, &multi_picture_draw_)); + new RasterBufferImpl(resource_provider_, + resource, + &multi_picture_draw_, + use_distance_field_text_)); } void GpuRasterWorkerPool::ReleaseBufferForRaster(
diff --git a/cc/resources/gpu_raster_worker_pool.h b/cc/resources/gpu_raster_worker_pool.h index a4e4197..c5ff13f 100644 --- a/cc/resources/gpu_raster_worker_pool.h +++ b/cc/resources/gpu_raster_worker_pool.h
@@ -23,7 +23,8 @@ static scoped_ptr<RasterWorkerPool> Create( base::SequencedTaskRunner* task_runner, ContextProvider* context_provider, - ResourceProvider* resource_provider); + ResourceProvider* resource_provider, + bool use_distance_field_text); // Overridden from RasterWorkerPool: Rasterizer* AsRasterizer() override; @@ -42,7 +43,8 @@ private: GpuRasterWorkerPool(base::SequencedTaskRunner* task_runner, ContextProvider* context_provider, - ResourceProvider* resource_provider); + ResourceProvider* resource_provider, + bool use_distance_field_text); void OnRasterFinished(TaskSet task_set); void ScheduleRunTasksOnOriginThread(); @@ -58,6 +60,7 @@ SkMultiPictureDraw multi_picture_draw_; bool run_tasks_on_origin_thread_pending_; + bool use_distance_field_text_; TaskSetCollection raster_pending_;
diff --git a/cc/resources/picture_layer_tiling_perftest.cc b/cc/resources/picture_layer_tiling_perftest.cc index 9cc3a59..8f91763 100644 --- a/cc/resources/picture_layer_tiling_perftest.cc +++ b/cc/resources/picture_layer_tiling_perftest.cc
@@ -40,8 +40,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); } virtual void SetUp() override {
diff --git a/cc/resources/picture_layer_tiling_set.cc b/cc/resources/picture_layer_tiling_set.cc index 1806ebc..96b47a6 100644 --- a/cc/resources/picture_layer_tiling_set.cc +++ b/cc/resources/picture_layer_tiling_set.cc
@@ -19,12 +19,8 @@ } // namespace - -PictureLayerTilingSet::PictureLayerTilingSet( - PictureLayerTilingClient* client, - const gfx::Size& layer_bounds) - : client_(client), - layer_bounds_(layer_bounds) { +PictureLayerTilingSet::PictureLayerTilingSet(PictureLayerTilingClient* client) + : client_(client) { } PictureLayerTilingSet::~PictureLayerTilingSet() { @@ -47,7 +43,6 @@ float minimum_contents_scale) { if (new_layer_bounds.IsEmpty()) { RemoveAllTilings(); - layer_bounds_ = new_layer_bounds; return false; } @@ -99,17 +94,17 @@ } tilings_.sort(LargestToSmallestScaleFunctor()); - layer_bounds_ = new_layer_bounds; return have_high_res_tiling; } -PictureLayerTiling* PictureLayerTilingSet::AddTiling(float contents_scale) { +PictureLayerTiling* PictureLayerTilingSet::AddTiling( + float contents_scale, + const gfx::Size& layer_bounds) { for (size_t i = 0; i < tilings_.size(); ++i) DCHECK_NE(tilings_[i]->contents_scale(), contents_scale); - tilings_.push_back(PictureLayerTiling::Create(contents_scale, - layer_bounds_, - client_)); + tilings_.push_back( + PictureLayerTiling::Create(contents_scale, layer_bounds, client_)); PictureLayerTiling* appended = tilings_.back(); tilings_.sort(LargestToSmallestScaleFunctor());
diff --git a/cc/resources/picture_layer_tiling_set.h b/cc/resources/picture_layer_tiling_set.h index 63f3110..6f61243 100644 --- a/cc/resources/picture_layer_tiling_set.h +++ b/cc/resources/picture_layer_tiling_set.h
@@ -34,8 +34,7 @@ size_t end; }; - PictureLayerTilingSet(PictureLayerTilingClient* client, - const gfx::Size& layer_bounds); + explicit PictureLayerTilingSet(PictureLayerTilingClient* client); ~PictureLayerTilingSet(); void SetClient(PictureLayerTilingClient* client); @@ -53,9 +52,8 @@ const Region& layer_invalidation, float minimum_contents_scale); - gfx::Size layer_bounds() const { return layer_bounds_; } - - PictureLayerTiling* AddTiling(float contents_scale); + PictureLayerTiling* AddTiling(float contents_scale, + const gfx::Size& layer_bounds); size_t num_tilings() const { return tilings_.size(); } int NumHighResTilings() const; PictureLayerTiling* tiling_at(size_t idx) { return tilings_[idx]; } @@ -126,7 +124,6 @@ private: PictureLayerTilingClient* client_; - gfx::Size layer_bounds_; ScopedPtrVector<PictureLayerTiling> tilings_; friend class Iterator;
diff --git a/cc/resources/picture_layer_tiling_set_unittest.cc b/cc/resources/picture_layer_tiling_set_unittest.cc index f4059e4..3d249f3 100644 --- a/cc/resources/picture_layer_tiling_set_unittest.cc +++ b/cc/resources/picture_layer_tiling_set_unittest.cc
@@ -23,12 +23,12 @@ TEST(PictureLayerTilingSetTest, NoResources) { FakePictureLayerTilingClient client; gfx::Size layer_bounds(1000, 800); - PictureLayerTilingSet set(&client, layer_bounds); + PictureLayerTilingSet set(&client); client.SetTileSize(gfx::Size(256, 256)); - set.AddTiling(1.0); - set.AddTiling(1.5); - set.AddTiling(2.0); + set.AddTiling(1.0, layer_bounds); + set.AddTiling(1.5, layer_bounds); + set.AddTiling(2.0, layer_bounds); float contents_scale = 2.0; gfx::Size content_bounds( @@ -64,14 +64,14 @@ PictureLayerTiling* high_res_tiling; PictureLayerTiling* low_res_tiling; - PictureLayerTilingSet set(&client, layer_bounds); - set.AddTiling(2.0); - high_res_tiling = set.AddTiling(1.0); + PictureLayerTilingSet set(&client); + set.AddTiling(2.0, layer_bounds); + high_res_tiling = set.AddTiling(1.0, layer_bounds); high_res_tiling->set_resolution(HIGH_RESOLUTION); - set.AddTiling(0.5); - low_res_tiling = set.AddTiling(0.25); + set.AddTiling(0.5, layer_bounds); + low_res_tiling = set.AddTiling(0.25, layer_bounds); low_res_tiling->set_resolution(LOW_RESOLUTION); - set.AddTiling(0.125); + set.AddTiling(0.125, layer_bounds); higher_than_high_res_range = set.GetTilingRange(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES); @@ -96,12 +96,12 @@ EXPECT_EQ(4u, lower_than_low_res_range.start); EXPECT_EQ(5u, lower_than_low_res_range.end); - PictureLayerTilingSet set_without_low_res(&client, layer_bounds); - set_without_low_res.AddTiling(2.0); - high_res_tiling = set_without_low_res.AddTiling(1.0); + PictureLayerTilingSet set_without_low_res(&client); + set_without_low_res.AddTiling(2.0, layer_bounds); + high_res_tiling = set_without_low_res.AddTiling(1.0, layer_bounds); high_res_tiling->set_resolution(HIGH_RESOLUTION); - set_without_low_res.AddTiling(0.5); - set_without_low_res.AddTiling(0.25); + set_without_low_res.AddTiling(0.5, layer_bounds); + set_without_low_res.AddTiling(0.25, layer_bounds); higher_than_high_res_range = set_without_low_res.GetTilingRange( PictureLayerTilingSet::HIGHER_THAN_HIGH_RES); @@ -126,10 +126,10 @@ PictureLayerTilingSet::LOWER_THAN_LOW_RES); EXPECT_EQ(0u, lower_than_low_res_range.end - lower_than_low_res_range.start); - PictureLayerTilingSet set_with_only_high_and_low_res(&client, layer_bounds); - high_res_tiling = set_with_only_high_and_low_res.AddTiling(1.0); + PictureLayerTilingSet set_with_only_high_and_low_res(&client); + high_res_tiling = set_with_only_high_and_low_res.AddTiling(1.0, layer_bounds); high_res_tiling->set_resolution(HIGH_RESOLUTION); - low_res_tiling = set_with_only_high_and_low_res.AddTiling(0.5); + low_res_tiling = set_with_only_high_and_low_res.AddTiling(0.5, layer_bounds); low_res_tiling->set_resolution(LOW_RESOLUTION); higher_than_high_res_range = set_with_only_high_and_low_res.GetTilingRange( @@ -158,8 +158,8 @@ PictureLayerTilingSet::LOWER_THAN_LOW_RES); EXPECT_EQ(0u, lower_than_low_res_range.end - lower_than_low_res_range.start); - PictureLayerTilingSet set_with_only_high_res(&client, layer_bounds); - high_res_tiling = set_with_only_high_res.AddTiling(1.0); + PictureLayerTilingSet set_with_only_high_res(&client); + high_res_tiling = set_with_only_high_res.AddTiling(1.0, layer_bounds); high_res_tiling->set_resolution(HIGH_RESOLUTION); higher_than_high_res_range = set_with_only_high_res.GetTilingRange( @@ -209,18 +209,17 @@ NULL, 0, false, - 1, - false); + 1); FakePictureLayerTilingClient client(resource_provider.get()); client.SetTileSize(gfx::Size(256, 256)); client.set_tree(PENDING_TREE); gfx::Size layer_bounds(1000, 800); - PictureLayerTilingSet set(&client, layer_bounds); + PictureLayerTilingSet set(&client); float scale = min_scale; for (int i = 0; i < num_tilings; ++i, scale += scale_increment) { - PictureLayerTiling* tiling = set.AddTiling(scale); + PictureLayerTiling* tiling = set.AddTiling(scale, layer_bounds); tiling->CreateAllTilesForTesting(); std::vector<Tile*> tiles = tiling->AllTilesForTesting(); client.tile_manager()->InitializeTilesWithResourcesForTesting(tiles); @@ -298,8 +297,8 @@ source_client_.set_tree(PENDING_TREE); target_client_.SetTileSize(tile_size_); target_client_.set_tree(PENDING_TREE); - source_.reset(new PictureLayerTilingSet(&source_client_, source_bounds_)); - target_.reset(new PictureLayerTilingSet(&target_client_, target_bounds_)); + source_.reset(new PictureLayerTilingSet(&source_client_)); + target_.reset(new PictureLayerTilingSet(&target_client_)); } // Sync from source to target. @@ -329,7 +328,6 @@ void VerifyTargetEqualsSource(const gfx::Size& new_bounds) { ASSERT_FALSE(new_bounds.IsEmpty()); EXPECT_EQ(target_->num_tilings(), source_->num_tilings()); - EXPECT_EQ(target_->layer_bounds().ToString(), new_bounds.ToString()); for (size_t i = 0; i < target_->num_tilings(); ++i) { ASSERT_GT(source_->num_tilings(), i); @@ -404,21 +402,20 @@ TEST_F(PictureLayerTilingSetSyncTest, EmptyBounds) { float source_scales[] = {1.f, 1.2f}; for (size_t i = 0; i < arraysize(source_scales); ++i) - source_->AddTiling(source_scales[i]); + source_->AddTiling(source_scales[i], source_bounds_); - gfx::Size new_bounds; - SyncTilings(new_bounds); + gfx::Size empty_bounds; + SyncTilings(empty_bounds); EXPECT_EQ(target_->num_tilings(), 0u); - EXPECT_EQ(target_->layer_bounds().ToString(), new_bounds.ToString()); } TEST_F(PictureLayerTilingSetSyncTest, AllNew) { float source_scales[] = {0.5f, 1.f, 1.2f}; for (size_t i = 0; i < arraysize(source_scales); ++i) - source_->AddTiling(source_scales[i]); + source_->AddTiling(source_scales[i], source_bounds_); float target_scales[] = {0.75f, 1.4f, 3.f}; for (size_t i = 0; i < arraysize(target_scales); ++i) - target_->AddTiling(target_scales[i]); + target_->AddTiling(target_scales[i], target_bounds_); gfx::Size new_bounds(15, 40); SyncTilings(new_bounds); @@ -437,10 +434,10 @@ TEST_F(PictureLayerTilingSetSyncTest, KeepExisting) { float source_scales[] = {0.7f, 1.f, 1.1f, 2.f}; for (size_t i = 0; i < arraysize(source_scales); ++i) - source_->AddTiling(source_scales[i]); + source_->AddTiling(source_scales[i], source_bounds_); float target_scales[] = {0.5f, 1.f, 2.f}; for (size_t i = 0; i < arraysize(target_scales); ++i) - target_->AddTiling(target_scales[i]); + target_->AddTiling(target_scales[i], target_bounds_); PictureLayerTiling* tiling1 = source_->TilingAtScale(1.f); ASSERT_TRUE(tiling1); @@ -472,7 +469,7 @@ TEST_F(PictureLayerTilingSetSyncTest, EmptySet) { float target_scales[] = {0.2f, 1.f}; for (size_t i = 0; i < arraysize(target_scales); ++i) - target_->AddTiling(target_scales[i]); + target_->AddTiling(target_scales[i], target_bounds_); gfx::Size new_bounds(15, 40); SyncTilings(new_bounds); @@ -482,10 +479,10 @@ TEST_F(PictureLayerTilingSetSyncTest, MinimumScale) { float source_scales[] = {0.7f, 1.f, 1.1f, 2.f}; for (size_t i = 0; i < arraysize(source_scales); ++i) - source_->AddTiling(source_scales[i]); + source_->AddTiling(source_scales[i], source_bounds_); float target_scales[] = {0.5f, 0.7f, 1.f, 1.1f, 2.f}; for (size_t i = 0; i < arraysize(target_scales); ++i) - target_->AddTiling(target_scales[i]); + target_->AddTiling(target_scales[i], target_bounds_); gfx::Size new_bounds(15, 40); float minimum_scale = 1.5f; @@ -497,8 +494,8 @@ } TEST_F(PictureLayerTilingSetSyncTest, Invalidation) { - source_->AddTiling(2.f); - target_->AddTiling(2.f); + source_->AddTiling(2.f, source_bounds_); + target_->AddTiling(2.f, target_bounds_); target_->tiling_at(0)->CreateAllTilesForTesting(); Region layer_invalidation; @@ -535,8 +532,8 @@ } TEST_F(PictureLayerTilingSetSyncTest, TileSizeChange) { - source_->AddTiling(1.f); - target_->AddTiling(1.f); + source_->AddTiling(1.f, source_bounds_); + target_->AddTiling(1.f, target_bounds_); target_->tiling_at(0)->CreateAllTilesForTesting(); std::vector<Tile*> original_tiles =
diff --git a/cc/resources/picture_layer_tiling_unittest.cc b/cc/resources/picture_layer_tiling_unittest.cc index c871da4..07da353 100644 --- a/cc/resources/picture_layer_tiling_unittest.cc +++ b/cc/resources/picture_layer_tiling_unittest.cc
@@ -1262,15 +1262,8 @@ scoped_ptr<FakeOutputSurface> output_surface = FakeOutputSurface::Create3d(); CHECK(output_surface->BindToClient(&output_surface_client)); TestSharedBitmapManager shared_bitmap_manager; - scoped_ptr<ResourceProvider> resource_provider = - ResourceProvider::Create(output_surface.get(), - &shared_bitmap_manager, - NULL, - NULL, - 0, - false, - 1, - false); + scoped_ptr<ResourceProvider> resource_provider = ResourceProvider::Create( + output_surface.get(), &shared_bitmap_manager, NULL, NULL, 0, false, 1); FakePictureLayerTilingClient client(resource_provider.get()); scoped_ptr<TestablePictureLayerTiling> tiling; @@ -1463,9 +1456,9 @@ client_.SetTileSize(tile_size); client_.set_tree(PENDING_TREE); - PictureLayerTilingSet active_set(&client_, layer_bounds); + PictureLayerTilingSet active_set(&client_); - active_set.AddTiling(1.f); + active_set.AddTiling(1.f, layer_bounds); VerifyTiles(active_set.tiling_at(0), 1.f, @@ -1485,7 +1478,7 @@ base::Bind(&TileExists, true)); // Add the same tilings to the pending set. - PictureLayerTilingSet pending_set(&client_, layer_bounds); + PictureLayerTilingSet pending_set(&client_); Region invalidation; pending_set.SyncTilings(active_set, layer_bounds, invalidation, 0.f);
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc index f4fbdf2..e19dd2c 100644 --- a/cc/resources/picture_pile_impl.cc +++ b/cc/resources/picture_pile_impl.cc
@@ -28,11 +28,13 @@ return make_scoped_refptr(new PicturePileImpl(other)); } -PicturePileImpl::PicturePileImpl() { +PicturePileImpl::PicturePileImpl() + : likely_to_be_used_for_transform_animation_(false) { } PicturePileImpl::PicturePileImpl(const PicturePileBase* other) - : PicturePileBase(other) { + : PicturePileBase(other), + likely_to_be_used_for_transform_animation_(false) { } PicturePileImpl::~PicturePileImpl() {
diff --git a/cc/resources/picture_pile_impl.h b/cc/resources/picture_pile_impl.h index a9bef0c..243601e 100644 --- a/cc/resources/picture_pile_impl.h +++ b/cc/resources/picture_pile_impl.h
@@ -58,6 +58,13 @@ skia::RefPtr<SkPicture> GetFlattenedPicture(); + bool likely_to_be_used_for_transform_animation() const { + return likely_to_be_used_for_transform_animation_; + } + void set_likely_to_be_used_for_transform_animation() { + likely_to_be_used_for_transform_animation_ = true; + } + struct CC_EXPORT Analysis { Analysis(); ~Analysis(); @@ -124,6 +131,8 @@ RenderingStatsInstrumentation* rendering_stats_instrumentation, bool is_analysis) const; + bool likely_to_be_used_for_transform_animation_; + DISALLOW_COPY_AND_ASSIGN(PicturePileImpl); };
diff --git a/cc/resources/prioritized_resource_unittest.cc b/cc/resources/prioritized_resource_unittest.cc index 5109a60..8869e70 100644 --- a/cc/resources/prioritized_resource_unittest.cc +++ b/cc/resources/prioritized_resource_unittest.cc
@@ -34,8 +34,7 @@ NULL, 0, false, - 1, - false); + 1); } virtual ~PrioritizedResourceTest() {
diff --git a/cc/resources/raster_worker_pool_perftest.cc b/cc/resources/raster_worker_pool_perftest.cc index 4264373..0f7a845 100644 --- a/cc/resources/raster_worker_pool_perftest.cc +++ b/cc/resources/raster_worker_pool_perftest.cc
@@ -262,7 +262,8 @@ raster_worker_pool_ = GpuRasterWorkerPool::Create(task_runner_.get(), context_provider_.get(), - resource_provider_.get()); + resource_provider_.get(), + false); break; case RASTER_WORKER_POOL_TYPE_BITMAP: CreateSoftwareOutputSurfaceAndResourceProvider(); @@ -406,8 +407,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); } void CreateSoftwareOutputSurfaceAndResourceProvider() { @@ -420,8 +420,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); } std::string TestModifierString() const { @@ -491,7 +490,7 @@ CHECK(output_surface_->BindToClient(&output_surface_client_)); resource_provider_ = ResourceProvider::Create( - output_surface_.get(), NULL, NULL, NULL, 0, false, 1, false).Pass(); + output_surface_.get(), NULL, NULL, NULL, 0, false, 1).Pass(); } void RunBuildRasterTaskQueueTest(const std::string& test_name,
diff --git a/cc/resources/raster_worker_pool_unittest.cc b/cc/resources/raster_worker_pool_unittest.cc index 2227128..a6d6c65 100644 --- a/cc/resources/raster_worker_pool_unittest.cc +++ b/cc/resources/raster_worker_pool_unittest.cc
@@ -164,7 +164,8 @@ raster_worker_pool_ = GpuRasterWorkerPool::Create(base::MessageLoopProxy::current().get(), context_provider_.get(), - resource_provider_.get()); + resource_provider_.get(), + false); break; case RASTER_WORKER_POOL_TYPE_BITMAP: CreateSoftwareOutputSurfaceAndResourceProvider(); @@ -279,8 +280,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); } void CreateSoftwareOutputSurfaceAndResourceProvider() { @@ -293,8 +293,7 @@ NULL, 0, false, - 1, - false).Pass(); + 1).Pass(); } void OnTaskCompleted(scoped_ptr<ScopedResource> resource,
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc index 1697ac2..550475b 100644 --- a/cc/resources/resource_provider.cc +++ b/cc/resources/resource_provider.cc
@@ -411,8 +411,7 @@ BlockingTaskRunner* blocking_main_thread_task_runner, int highp_threshold_min, bool use_rgba_4444_texture_format, - size_t id_allocation_chunk_size, - bool use_distance_field_text) { + size_t id_allocation_chunk_size) { scoped_ptr<ResourceProvider> resource_provider( new ResourceProvider(output_surface, shared_bitmap_manager, @@ -420,8 +419,7 @@ blocking_main_thread_task_runner, highp_threshold_min, use_rgba_4444_texture_format, - id_allocation_chunk_size, - use_distance_field_text)); + id_allocation_chunk_size)); if (resource_provider->ContextGL()) resource_provider->InitializeGL(); @@ -995,38 +993,12 @@ resource->read_lock_fences_enabled = true; } -const ResourceProvider::Resource* ResourceProvider::LockForWriteToSkSurface( - ResourceId id) { +void ResourceProvider::LockForWriteToSkSurface(ResourceId id) { Resource* resource = GetResource(id); DCHECK_EQ(GLTexture, resource->type); DCHECK(CanLockForWrite(id)); resource->locked_for_write = true; - if (!resource->sk_surface) { - class GrContext* gr_context = GrContext(); - // TODO(alokp): Implement TestContextProvider::GrContext(). - if (!gr_context) - return resource; - - LazyAllocate(resource); - - GrBackendTextureDesc desc; - desc.fFlags = kRenderTarget_GrBackendTextureFlag; - desc.fWidth = resource->size.width(); - desc.fHeight = resource->size.height(); - desc.fConfig = ToGrPixelConfig(resource->format); - desc.fOrigin = kTopLeft_GrSurfaceOrigin; - desc.fTextureHandle = resource->gl_id; - skia::RefPtr<GrTexture> gr_texture = - skia::AdoptRef(gr_context->wrapBackendTexture(desc)); - SkSurface::TextRenderMode text_render_mode = - use_distance_field_text_ ? SkSurface::kDistanceField_TextRenderMode - : SkSurface::kStandard_TextRenderMode; - resource->sk_surface = skia::AdoptRef(SkSurface::NewRenderTargetDirect( - gr_texture->asRenderTarget(), text_render_mode)); - } - - return resource; } void ResourceProvider::UnlockForWriteToSkSurface(ResourceId id) { @@ -1141,16 +1113,49 @@ ResourceProvider::ScopedWriteLockGr::ScopedWriteLockGr( ResourceProvider* resource_provider, ResourceProvider::ResourceId resource_id) - : resource_provider_(resource_provider), - resource_id_(resource_id), - sk_surface_(resource_provider->LockForWriteToSkSurface(resource_id) - ->sk_surface.get()) { + : resource_provider_(resource_provider), resource_id_(resource_id) { + resource_provider->LockForWriteToSkSurface(resource_id); } ResourceProvider::ScopedWriteLockGr::~ScopedWriteLockGr() { resource_provider_->UnlockForWriteToSkSurface(resource_id_); } +SkSurface* ResourceProvider::ScopedWriteLockGr::GetSkSurface( + bool use_distance_field_text) { + Resource* resource = resource_provider_->GetResource(resource_id_); + DCHECK(resource->locked_for_write); + + // If the surface doesn't exist, or doesn't have the correct dff setting, + // recreate the surface within the resource. + if (!resource->sk_surface || + use_distance_field_text != + resource->sk_surface->props().isUseDistanceFieldFonts()) { + class GrContext* gr_context = resource_provider_->GrContext(); + // TODO(alokp): Implement TestContextProvider::GrContext(). + if (!gr_context) + return nullptr; + + resource_provider_->LazyAllocate(resource); + + GrBackendTextureDesc desc; + desc.fFlags = kRenderTarget_GrBackendTextureFlag; + desc.fWidth = resource->size.width(); + desc.fHeight = resource->size.height(); + desc.fConfig = ToGrPixelConfig(resource->format); + desc.fOrigin = kTopLeft_GrSurfaceOrigin; + desc.fTextureHandle = resource->gl_id; + skia::RefPtr<GrTexture> gr_texture = + skia::AdoptRef(gr_context->wrapBackendTexture(desc)); + SkSurface::TextRenderMode text_render_mode = + use_distance_field_text ? SkSurface::kDistanceField_TextRenderMode + : SkSurface::kStandard_TextRenderMode; + resource->sk_surface = skia::AdoptRef(SkSurface::NewRenderTargetDirect( + gr_texture->asRenderTarget(), text_render_mode)); + } + return resource->sk_surface.get(); +} + ResourceProvider::ResourceProvider( OutputSurface* output_surface, SharedBitmapManager* shared_bitmap_manager, @@ -1158,8 +1163,7 @@ BlockingTaskRunner* blocking_main_thread_task_runner, int highp_threshold_min, bool use_rgba_4444_texture_format, - size_t id_allocation_chunk_size, - bool use_distance_field_text) + size_t id_allocation_chunk_size) : output_surface_(output_surface), shared_bitmap_manager_(shared_bitmap_manager), gpu_memory_buffer_manager_(gpu_memory_buffer_manager), @@ -1177,8 +1181,7 @@ best_texture_format_(RGBA_8888), use_rgba_4444_texture_format_(use_rgba_4444_texture_format), id_allocation_chunk_size_(id_allocation_chunk_size), - use_sync_query_(false), - use_distance_field_text_(use_distance_field_text) { + use_sync_query_(false) { DCHECK(output_surface_->HasClient()); DCHECK(id_allocation_chunk_size_); }
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h index 1e70132..fbec8d1 100644 --- a/cc/resources/resource_provider.h +++ b/cc/resources/resource_provider.h
@@ -83,8 +83,7 @@ BlockingTaskRunner* blocking_main_thread_task_runner, int highp_threshold_min, bool use_rgba_4444_texture_format, - size_t id_allocation_chunk_size, - bool use_distance_field_text); + size_t id_allocation_chunk_size); virtual ~ResourceProvider(); void InitializeSoftware(); @@ -328,12 +327,11 @@ ResourceProvider::ResourceId resource_id); ~ScopedWriteLockGr(); - SkSurface* sk_surface() { return sk_surface_; } + SkSurface* GetSkSurface(bool use_distance_field_text); private: ResourceProvider* resource_provider_; ResourceProvider::ResourceId resource_id_; - SkSurface* sk_surface_; DISALLOW_COPY_AND_ASSIGN(ScopedWriteLockGr); }; @@ -488,8 +486,7 @@ BlockingTaskRunner* blocking_main_thread_task_runner, int highp_threshold_min, bool use_rgba_4444_texture_format, - size_t id_allocation_chunk_size, - bool use_distance_field_text); + size_t id_allocation_chunk_size); void CleanUpGLIfNeeded(); @@ -500,7 +497,7 @@ void UnlockForWrite(ResourceId id); const Resource* LockForWriteToGpuMemoryBuffer(ResourceId id); void UnlockForWriteToGpuMemoryBuffer(ResourceId id); - const Resource* LockForWriteToSkSurface(ResourceId id); + void LockForWriteToSkSurface(ResourceId id); void UnlockForWriteToSkSurface(ResourceId id); static void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap, @@ -562,8 +559,6 @@ bool use_sync_query_; - bool use_distance_field_text_; - DISALLOW_COPY_AND_ASSIGN(ResourceProvider); };
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc index 3aea34f..2c585c4 100644 --- a/cc/resources/resource_provider_unittest.cc +++ b/cc/resources/resource_provider_unittest.cc
@@ -423,8 +423,7 @@ main_thread_task_runner_.get(), 0, false, - 1, - false); + 1); child_resource_provider_ = ResourceProvider::Create(child_output_surface_.get(), shared_bitmap_manager_.get(), @@ -432,8 +431,7 @@ main_thread_task_runner_.get(), 0, false, - 1, - false); + 1); } static void CollectResources(ReturnedResourceArray* array, @@ -1168,8 +1166,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(1, 1); ResourceFormat format = RGBA_8888; @@ -1651,8 +1648,7 @@ NULL, 0, false, - 1, - false)); + 1)); scoped_ptr<TextureStateTrackingContext> parent_context_owned( new TextureStateTrackingContext); @@ -1670,8 +1666,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(1, 1); ResourceFormat format = RGBA_8888; @@ -2303,8 +2298,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(1, 1); ResourceFormat format = RGBA_8888; @@ -2391,8 +2385,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(1, 1); ResourceFormat format = RGBA_8888; @@ -2447,8 +2440,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(1, 1); ResourceFormat format = RGBA_8888; @@ -2507,8 +2499,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(1, 1); ResourceFormat format = RGBA_8888; @@ -2581,8 +2572,7 @@ main_thread_task_runner_.get(), 0, false, - 1, - false)); + 1)); uint32 release_sync_point = 0; bool lost_resource = false; @@ -2634,8 +2624,7 @@ main_thread_task_runner_.get(), 0, false, - 1, - false)); + 1)); unsigned texture_id = 1; uint32 sync_point = 30; @@ -2720,8 +2709,7 @@ NULL, 0, false, - 1, - false)); + 1)); unsigned texture_id = 1; uint32 sync_point = 30; @@ -2796,8 +2784,7 @@ NULL, 0, false, - 1, - false)); + 1)); uint32 sync_point = 30; unsigned target = GL_TEXTURE_2D; @@ -2856,8 +2843,7 @@ NULL, 0, false, - 1, - false)); + 1)); uint32 sync_point = 0; unsigned target = GL_TEXTURE_2D; @@ -2983,8 +2969,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(2, 2); gfx::Vector2d offset(0, 0); @@ -3064,8 +3049,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(2, 2); @@ -3125,8 +3109,7 @@ NULL, 0, false, - 1, - false)); + 1)); gfx::Size size(2, 2); const ResourceFormat formats[2] = {RGBA_8888, BGRA_8888}; @@ -3185,8 +3168,7 @@ NULL, 0, false, - 1, - false)); + 1)); id = resource_provider->CreateResource( size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format); @@ -3233,8 +3215,7 @@ NULL, 0, false, - 1, - false)); + 1)); id = resource_provider->CreateResource( size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format); @@ -3281,8 +3262,7 @@ NULL, 0, false, - 1, - false)); + 1)); EXPECT_CALL(*context, NextTextureId()).WillRepeatedly(Return(texture_id)); @@ -3327,8 +3307,7 @@ NULL, 0, false, - 1, - false)); + 1)); id = resource_provider->CreateResource( size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format); @@ -3415,8 +3394,7 @@ NULL, 0, false, - 1, - false)); + 1)); source_id = resource_provider->CreateResource( size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format); @@ -3498,8 +3476,7 @@ NULL, 0, false, - 1, - false)); + 1)); CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL); @@ -3538,8 +3515,7 @@ NULL, 0, false, - 1, - false)); + 1)); int texture_id = 123; ResourceProvider::ResourceId id = resource_provider->CreateResource( @@ -3575,8 +3551,7 @@ NULL, 0, false, - 1, - false)); + 1)); int texture_id = 123; uint8_t pixels[8]; @@ -3637,8 +3612,7 @@ NULL, 0, false, - kTextureAllocationChunkSize, - false)); + kTextureAllocationChunkSize)); ResourceProvider::ResourceId id = resource_provider->CreateResource( size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format); @@ -3658,8 +3632,7 @@ NULL, 0, false, - kTextureAllocationChunkSize, - false)); + kTextureAllocationChunkSize)); ResourceProvider::ResourceId id = resource_provider->CreateResource( size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
diff --git a/cc/resources/resource_update_controller_unittest.cc b/cc/resources/resource_update_controller_unittest.cc index 49f7b47..d6b4f39 100644 --- a/cc/resources/resource_update_controller_unittest.cc +++ b/cc/resources/resource_update_controller_unittest.cc
@@ -130,8 +130,7 @@ NULL, 0, false, - 1, - false); + 1); } void AppendFullUploadsOfIndexedTextureToUpdateQueue(int count,
diff --git a/cc/resources/scoped_resource_unittest.cc b/cc/resources/scoped_resource_unittest.cc index 7c7e055..a141218 100644 --- a/cc/resources/scoped_resource_unittest.cc +++ b/cc/resources/scoped_resource_unittest.cc
@@ -28,8 +28,7 @@ NULL, 0, false, - 1, - false)); + 1)); scoped_ptr<ScopedResource> texture = ScopedResource::Create(resource_provider.get()); @@ -55,8 +54,7 @@ NULL, 0, false, - 1, - false)); + 1)); scoped_ptr<ScopedResource> texture = ScopedResource::Create(resource_provider.get()); texture->Allocate( @@ -85,8 +83,7 @@ NULL, 0, false, - 1, - false)); + 1)); { scoped_ptr<ScopedResource> texture = ScopedResource::Create(resource_provider.get());
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc index 46f2e14..3671081 100644 --- a/cc/resources/tile_manager_unittest.cc +++ b/cc/resources/tile_manager_unittest.cc
@@ -157,21 +157,26 @@ } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(17u, tile_count); + EXPECT_EQ(16u, tile_count); // Sanity check, all tiles should be visible. std::set<Tile*> smoothness_tiles; queue.Reset(); host_impl_.BuildRasterQueue(&queue, SMOOTHNESS_TAKES_PRIORITY); + bool had_low_res = false; while (!queue.IsEmpty()) { Tile* tile = queue.Top(); EXPECT_TRUE(tile); EXPECT_EQ(TilePriority::NOW, tile->priority(ACTIVE_TREE).priority_bin); EXPECT_EQ(TilePriority::NOW, tile->priority(PENDING_TREE).priority_bin); - smoothness_tiles.insert(tile); + if (tile->priority(ACTIVE_TREE).resolution == LOW_RESOLUTION) + had_low_res = true; + else + smoothness_tiles.insert(tile); queue.Pop(); } EXPECT_EQ(all_tiles, smoothness_tiles); + EXPECT_TRUE(had_low_res); Region invalidation(gfx::Rect(0, 0, 500, 500)); @@ -198,10 +203,13 @@ // Populate all tiles directly from the tilings. all_tiles.clear(); + std::set<Tile*> high_res_tiles; std::vector<Tile*> pending_high_res_tiles = pending_layer_->HighResTiling()->AllTilesForTesting(); - for (size_t i = 0; i < pending_high_res_tiles.size(); ++i) + for (size_t i = 0; i < pending_high_res_tiles.size(); ++i) { all_tiles.insert(pending_high_res_tiles[i]); + high_res_tiles.insert(pending_high_res_tiles[i]); + } std::vector<Tile*> pending_low_res_tiles = pending_layer_->LowResTiling()->AllTilesForTesting(); @@ -210,8 +218,10 @@ std::vector<Tile*> active_high_res_tiles = active_layer_->HighResTiling()->AllTilesForTesting(); - for (size_t i = 0; i < active_high_res_tiles.size(); ++i) + for (size_t i = 0; i < active_high_res_tiles.size(); ++i) { all_tiles.insert(active_high_res_tiles[i]); + high_res_tiles.insert(active_high_res_tiles[i]); + } std::vector<Tile*> active_low_res_tiles = active_layer_->LowResTiling()->AllTilesForTesting(); @@ -275,6 +285,7 @@ // Here we expect to get increasing PENDING_TREE priority_bin. queue.Reset(); host_impl_.BuildRasterQueue(&queue, NEW_CONTENT_TAKES_PRIORITY); + tile_count = 0; while (!queue.IsEmpty()) { Tile* tile = queue.Top(); EXPECT_TRUE(tile); @@ -300,14 +311,15 @@ last_tile = tile; new_content_tiles.insert(tile); + ++tile_count; queue.Pop(); } EXPECT_EQ(tile_count, new_content_tiles.size()); - EXPECT_EQ(all_tiles, new_content_tiles); + EXPECT_EQ(high_res_tiles, new_content_tiles); // Since we don't guarantee increasing distance due to spiral iterator, we // should check that we're _mostly_ right. - EXPECT_GT(increasing_distance_tiles, 3 * tile_count / 4); + EXPECT_GE(increasing_distance_tiles, 3 * tile_count / 4); } TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) { @@ -332,7 +344,7 @@ } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(17u, tile_count); + EXPECT_EQ(16u, tile_count); tile_manager()->InitializeTilesWithResourcesForTesting( std::vector<Tile*>(all_tiles.begin(), all_tiles.end())); @@ -507,7 +519,7 @@ raster_queue.Pop(); } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(34u, tile_count); + EXPECT_EQ(32u, tile_count); pending_layer_->ResetAllTilesPriorities(); @@ -609,7 +621,7 @@ } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(17u, tile_count); + EXPECT_EQ(16u, tile_count); queue.Reset(); for (int i = 1; i < 10; ++i) { @@ -633,7 +645,7 @@ queue.Pop(); } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(17u, tile_count); + EXPECT_EQ(16u, tile_count); } TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueueEmptyLayers) { @@ -655,7 +667,7 @@ raster_queue.Pop(); } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(17u, tile_count); + EXPECT_EQ(16u, tile_count); std::vector<Tile*> tiles(all_tiles.begin(), all_tiles.end()); host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles); @@ -682,7 +694,7 @@ queue.Pop(); } EXPECT_EQ(tile_count, all_tiles.size()); - EXPECT_EQ(17u, tile_count); + EXPECT_EQ(16u, tile_count); } } // namespace
diff --git a/cc/resources/video_resource_updater_unittest.cc b/cc/resources/video_resource_updater_unittest.cc index bb6238c..c0a4af4 100644 --- a/cc/resources/video_resource_updater_unittest.cc +++ b/cc/resources/video_resource_updater_unittest.cc
@@ -35,8 +35,7 @@ NULL, 0, false, - 1, - false); + 1); } scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame() {
diff --git a/cc/surfaces/display.cc b/cc/surfaces/display.cc index a395f5c..29b0f8e 100644 --- a/cc/surfaces/display.cc +++ b/cc/surfaces/display.cc
@@ -58,7 +58,6 @@ int highp_threshold_min = 0; bool use_rgba_4444_texture_format = false; size_t id_allocation_chunk_size = 1; - bool use_distance_field_text = false; scoped_ptr<ResourceProvider> resource_provider = ResourceProvider::Create(output_surface_.get(), bitmap_manager_, @@ -66,8 +65,7 @@ blocking_main_thread_task_runner_.get(), highp_threshold_min, use_rgba_4444_texture_format, - id_allocation_chunk_size, - use_distance_field_text); + id_allocation_chunk_size); if (!resource_provider) return;
diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc index ef369f4..cea06ea 100644 --- a/cc/surfaces/surface_aggregator_unittest.cc +++ b/cc/surfaces/surface_aggregator_unittest.cc
@@ -1127,8 +1127,7 @@ NULL, 0, false, - 1, - false); + 1); aggregator_.reset( new SurfaceAggregator(&manager_, resource_provider_.get()));
diff --git a/cc/test/data/background_filter_blur_off_axis.png b/cc/test/data/background_filter_blur_off_axis.png index b5777f4..050ec54 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/blending_render_pass.png b/cc/test/data/blending_render_pass.png new file mode 100644 index 0000000..76fe369 --- /dev/null +++ b/cc/test/data/blending_render_pass.png Binary files differ
diff --git a/cc/test/data/blending_render_pass_cm.png b/cc/test/data/blending_render_pass_cm.png new file mode 100644 index 0000000..9a72d44 --- /dev/null +++ b/cc/test/data/blending_render_pass_cm.png Binary files differ
diff --git a/cc/test/data/blending_render_pass_mask.png b/cc/test/data/blending_render_pass_mask.png new file mode 100644 index 0000000..8b63f35 --- /dev/null +++ b/cc/test/data/blending_render_pass_mask.png Binary files differ
diff --git a/cc/test/data/blending_render_pass_mask_cm.png b/cc/test/data/blending_render_pass_mask_cm.png new file mode 100644 index 0000000..4e210aa --- /dev/null +++ b/cc/test/data/blending_render_pass_mask_cm.png Binary files differ
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc index c47a083..1565b8a 100644 --- a/cc/test/pixel_test.cc +++ b/cc/test/pixel_test.cc
@@ -124,8 +124,7 @@ main_thread_task_runner_.get(), 0, false, - 1, - false); + 1); texture_mailbox_deleter_ = make_scoped_ptr( new TextureMailboxDeleter(base::MessageLoopProxy::current())); @@ -173,8 +172,7 @@ main_thread_task_runner_.get(), 0, false, - 1, - false); + 1); renderer_ = SoftwareRenderer::Create( this, &settings_, output_surface_.get(), resource_provider_.get()); }
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc index 519c74a..c923b63 100644 --- a/cc/test/render_pass_test_utils.cc +++ b/cc/test/render_pass_test_utils.cc
@@ -111,7 +111,8 @@ TestRenderPass* contributing_pass, ResourceProvider::ResourceId mask_resource_id, const FilterOperations& filters, - gfx::Transform transform) { + gfx::Transform transform, + SkXfermode::Mode blend_mode) { gfx::Rect output_rect = contributing_pass->output_rect; SharedQuadState* shared_state = to_pass->CreateAndAppendSharedQuadState(); shared_state->SetAll(transform, @@ -120,7 +121,7 @@ output_rect, false, 1, - SkXfermode::kSrcOver_Mode, + blend_mode, 0); RenderPassDrawQuad* quad = to_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
diff --git a/cc/test/render_pass_test_utils.h b/cc/test/render_pass_test_utils.h index 7bf026c..49ea113 100644 --- a/cc/test/render_pass_test_utils.h +++ b/cc/test/render_pass_test_utils.h
@@ -53,7 +53,8 @@ TestRenderPass* contributing_pass, ResourceProvider::ResourceId mask_resource_id, const FilterOperations& filters, - gfx::Transform transform); + gfx::Transform transform, + SkXfermode::Mode blend_mode); } // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index ddb315f..4ccc5fe 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -2028,8 +2028,11 @@ GL_TEXTURE_2D, resource_provider_->best_texture_format()); - raster_worker_pool_ = GpuRasterWorkerPool::Create( - task_runner, context_provider, resource_provider_.get()); + raster_worker_pool_ = + GpuRasterWorkerPool::Create(task_runner, + context_provider, + resource_provider_.get(), + settings_.use_distance_field_text); } else if (UseZeroCopyRasterizer()) { resource_pool_ = ResourcePool::Create( resource_provider_.get(), @@ -2135,8 +2138,7 @@ proxy_->blocking_main_thread_task_runner(), settings_.highp_threshold_min, settings_.use_rgba_4444_textures, - settings_.texture_id_allocation_chunk_size, - settings_.use_distance_field_text); + settings_.texture_id_allocation_chunk_size); if (output_surface_->capabilities().deferred_gl_initialization) EnforceZeroBudget(true);
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 7b466d0..51d86d3 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -901,19 +901,10 @@ scroll_layer->SetScrollDelta(gfx::Vector2d()); float page_scale_delta = 2.f; - gfx::Vector2dF expected_container_size_delta( - container_layer->bounds().width(), container_layer->bounds().height()); - expected_container_size_delta.Scale((1.f - page_scale_delta) / - (page_scale_factor * page_scale_delta)); host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture); host_impl_->PinchGestureBegin(); host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50)); - // While the gesture is still active, the scroll layer should have a - // container size delta = container->bounds() * ((1.f - - // page_scale_delta)/()) - EXPECT_EQ(expected_container_size_delta, - scroll_layer->FixedContainerSizeDelta()); host_impl_->PinchGestureEnd(); host_impl_->ScrollEnd(); EXPECT_FALSE(did_request_animate_); @@ -2244,6 +2235,7 @@ clip_size_(layer_size_) { settings_.calculate_top_controls_position = true; settings_.top_controls_height = 50; + settings_.use_pinch_virtual_viewport = true; viewport_size_ = gfx::Size(clip_size_.width(), @@ -2386,8 +2378,8 @@ } TEST_F(LayerTreeHostImplTopControlsTest, ScrollTopControlsByFractionalAmount) { - CreateHostImpl(settings_, CreateOutputSurface()); - SetupTopControlsAndScrollLayer(); + SetupTopControlsAndScrollLayerWithVirtualViewport( + gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10)); DrawFrame(); EXPECT_EQ(InputHandler::ScrollStarted, @@ -2409,36 +2401,56 @@ inner_viewport_scroll_layer->FixedContainerSizeDelta()); } -TEST_F(LayerTreeHostImplTopControlsTest, ScrollTopControlsWithPageScale) { - CreateHostImpl(settings_, CreateOutputSurface()); - SetupTopControlsAndScrollLayer(); +// Test that the fixed position container delta is appropriately adjusted +// by the top controls showing/hiding and page scale doesn't affect it. +TEST_F(LayerTreeHostImplTopControlsTest, FixedContainerDelta) { + SetupTopControlsAndScrollLayerWithVirtualViewport( + gfx::Size(100, 100), gfx::Size(100, 100), gfx::Size(100, 100)); DrawFrame(); + float page_scale = 1.5f; + float top_controls_height = settings_.top_controls_height; + LayerImpl* outer_viewport_scroll_layer = + host_impl_->active_tree()->OuterViewportScrollLayer(); + + // Zoom in, since the fixed container is the outer viewport, the delta should + // not be scaled. + host_impl_->active_tree()->SetPageScaleFactorAndLimits(page_scale, 1.f, 2.f); + EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); - float page_scale = 1.5f; - host_impl_->active_tree()->SetPageScaleFactorAndLimits(page_scale, 1.f, 2.f); - - gfx::Vector2dF top_controls_scroll_delta(0.f, 5.f); - gfx::Vector2dF expected_container_size_delta = - ScaleVector2d(top_controls_scroll_delta, 1.f / page_scale); + // Scroll down, the top controls hiding should expand the viewport size so + // the delta should be equal to the scroll distance. + gfx::Vector2dF top_controls_scroll_delta(0.f, 20.f); host_impl_->top_controls_manager()->ScrollBegin(); host_impl_->top_controls_manager()->ScrollBy(top_controls_scroll_delta); - host_impl_->top_controls_manager()->ScrollEnd(); - - LayerImpl* inner_viewport_scroll_layer = - host_impl_->active_tree()->InnerViewportScrollLayer(); - DCHECK(inner_viewport_scroll_layer); + EXPECT_EQ(top_controls_height - top_controls_scroll_delta.y(), + host_impl_->top_controls_manager()->ContentTopOffset()); + EXPECT_VECTOR_EQ(top_controls_scroll_delta, + outer_viewport_scroll_layer->FixedContainerSizeDelta()); host_impl_->ScrollEnd(); - // Use a tolerance that requires the container size delta to be within 0.01 - // pixels. - double tolerance = 0.0001; - EXPECT_LT( - (expected_container_size_delta - - inner_viewport_scroll_layer->FixedContainerSizeDelta()).LengthSquared(), - tolerance); + // Scroll past the maximum extent. The delta shouldn't be greater than the + // top controls height. + host_impl_->top_controls_manager()->ScrollBegin(); + host_impl_->top_controls_manager()->ScrollBy(top_controls_scroll_delta); + host_impl_->top_controls_manager()->ScrollBy(top_controls_scroll_delta); + host_impl_->top_controls_manager()->ScrollBy(top_controls_scroll_delta); + EXPECT_EQ(0.f, host_impl_->top_controls_manager()->ContentTopOffset()); + EXPECT_VECTOR_EQ(gfx::Vector2dF(0, top_controls_height), + outer_viewport_scroll_layer->FixedContainerSizeDelta()); + host_impl_->ScrollEnd(); + + // Scroll in the direction to make the top controls show. + host_impl_->top_controls_manager()->ScrollBegin(); + host_impl_->top_controls_manager()->ScrollBy(-top_controls_scroll_delta); + EXPECT_EQ(top_controls_scroll_delta.y(), + host_impl_->top_controls_manager()->ContentTopOffset()); + EXPECT_VECTOR_EQ( + gfx::Vector2dF(0, top_controls_height - top_controls_scroll_delta.y()), + outer_viewport_scroll_layer->FixedContainerSizeDelta()); + host_impl_->top_controls_manager()->ScrollEnd(); } // Ensure setting the top controls position explicitly using the setters on the
diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc index f01d426..bebd612 100644 --- a/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "cc/layers/image_layer.h" #include "cc/layers/solid_color_layer.h" -#include "cc/layers/texture_layer.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/pixel_comparator.h" @@ -22,7 +22,36 @@ SkXfermode::kHue_Mode, SkXfermode::kSaturation_Mode, SkXfermode::kColor_Mode, SkXfermode::kLuminosity_Mode}; +SkColor kCSSTestColors[] = { + 0xffff0000, // red + 0xff00ff00, // lime + 0xff0000ff, // blue + 0xff00ffff, // aqua + 0xffff00ff, // fuchsia + 0xffffff00, // yellow + 0xff008000, // green + 0xff800000, // maroon + 0xff000080, // navy + 0xff800080, // purple + 0xff808000, // olive + 0xff008080, // teal + 0xfffa8072, // salmon + 0xffc0c0c0, // silver + 0xff000000, // black + 0xff808080, // gray + 0x80000000, // black with transparency + 0xffffffff, // white + 0x80ffffff, // white with transparency + 0x00000000 // transparent +}; + const int kBlendModesCount = arraysize(kBlendModes); +const int kCSSTestColorsCount = arraysize(kCSSTestColors); + +using RenderPassOptions = uint32; +const uint32 kUseMasks = 1 << 0; +const uint32 kUseAntialiasing = 1 << 1; +const uint32 kUseColorMatrix = 1 << 2; class LayerTreeHostBlendingPixelTest : public LayerTreePixelTest { public: @@ -30,6 +59,10 @@ pixel_comparator_.reset(new FuzzyPixelOffByOneComparator(true)); } + virtual void InitializeSettings(LayerTreeSettings* settings) override { + settings->force_antialiasing = force_antialiasing_; + } + protected: void RunBlendingWithRootPixelTestType(PixelTestType type) { const int kLaneWidth = 15; @@ -82,6 +115,175 @@ root, base::FilePath(FILE_PATH_LITERAL("blending_transparent.png"))); } + + scoped_refptr<Layer> CreateColorfulBackdropLayer(int width, int height) { + // Draw the backdrop with horizontal lanes. + const int kLaneWidth = width; + const int kLaneHeight = height / kCSSTestColorsCount; + SkBitmap backing_store; + backing_store.allocN32Pixels(width, height); + SkCanvas canvas(backing_store); + canvas.clear(SK_ColorTRANSPARENT); + for (int i = 0; i < kCSSTestColorsCount; ++i) { + SkPaint paint; + paint.setColor(kCSSTestColors[i]); + canvas.drawRect( + SkRect::MakeXYWH(0, i * kLaneHeight, kLaneWidth, kLaneHeight), paint); + } + scoped_refptr<ImageLayer> layer = ImageLayer::Create(); + layer->SetIsDrawable(true); + layer->SetBounds(gfx::Size(width, height)); + layer->SetBitmap(backing_store); + return layer; + } + + void SetupMaskLayer(scoped_refptr<Layer> layer) { + const int kMaskOffset = 5; + gfx::Size bounds = layer->bounds(); + scoped_refptr<ImageLayer> mask = ImageLayer::Create(); + mask->SetIsDrawable(true); + mask->SetIsMask(true); + mask->SetBounds(bounds); + + SkBitmap bitmap; + bitmap.allocN32Pixels(bounds.width(), bounds.height()); + SkCanvas canvas(bitmap); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.clear(SK_ColorTRANSPARENT); + canvas.drawRect(SkRect::MakeXYWH(kMaskOffset, + kMaskOffset, + bounds.width() - kMaskOffset * 2, + bounds.height() - kMaskOffset * 2), + paint); + mask->SetBitmap(bitmap); + layer->SetMaskLayer(mask.get()); + } + + void SetupColorMatrix(scoped_refptr<Layer> layer) { + FilterOperations filter_operations; + filter_operations.Append(FilterOperation::CreateSepiaFilter(1.f)); + layer->SetFilters(filter_operations); + } + + void CreateBlendingColorLayers(int width, + int 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); + SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; + float opacity = 1.f; + SkColor color = kMiscOpaqueColor; + + if (i < kBlendModesCount) { + blend_mode = kBlendModes[i]; + } else if (i == kBlendModesCount) { + blend_mode = kCoeffBlendMode; + opacity = 0.5f; + } else if (i == kBlendModesCount + 1) { + blend_mode = kCoeffBlendMode; + color = kMiscTransparentColor; + } else if (i == kBlendModesCount + 2) { + blend_mode = kShaderBlendMode; + opacity = 0.5f; + } else if (i == kBlendModesCount + 3) { + blend_mode = kShaderBlendMode; + color = kMiscTransparentColor; + } + + scoped_refptr<SolidColorLayer> lane = + CreateSolidColorLayer(child_rect, color); + lane->SetBlendMode(blend_mode); + lane->SetOpacity(opacity); + lane->SetForceRenderSurface(true); + if (flags & kUseMasks) + SetupMaskLayer(lane); + if (flags & kUseColorMatrix) { + SetupColorMatrix(lane); + } + background->AddChild(lane); + } + } + + void RunBlendingWithRenderPass(PixelTestType type, + const base::FilePath::CharType* expected_path, + RenderPassOptions flags) { + const int kRootSize = 400; + + scoped_refptr<SolidColorLayer> root = + CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), SK_ColorWHITE); + scoped_refptr<Layer> background = + CreateColorfulBackdropLayer(kRootSize, kRootSize); + + background->SetIsRootForIsolatedGroup(true); + root->AddChild(background); + + CreateBlendingColorLayers(kRootSize, kRootSize, background.get(), flags); + + this->impl_side_painting_ = false; + this->force_antialiasing_ = (flags & kUseAntialiasing); + + if ((flags & kUseAntialiasing) && (type == GL_WITH_BITMAP)) { + // Anti aliasing causes differences up to 7 pixels at the edges. + int large_error_allowed = 7; + // 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 = 12.5f; + // Because of anti-aliasing, around 3% of pixels (at the edges) have + // bigger errors (from small_error_allowed + 1 to large_error_allowed). + float percentage_pixels_error = 15.0f; + // The average error is still close to 1. + float average_error_allowed_in_bad_pixels = 1.3f; + + // The sepia filter generates more small errors, but the number of large + // errors remains around 3%. + if (flags & kUseColorMatrix) { + percentage_pixels_small_error = 26.f; + percentage_pixels_error = 29.f; + } + + // Anti-aliased pixels in combination with non-separable blend modes and + // a white background produces several black pixels (6 for these tests). + // Having a mask will hide the black pixels. + // TODO(rosca): fix this issue for non-separable blend modes. + if (!(flags & kUseMasks)) + large_error_allowed = 255; + + 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 == GL_WITH_BITMAP)) { + 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, + percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, + large_error_allowed, + small_error_allowed)); + } + + RunPixelTest(type, root, base::FilePath(expected_path)); + } + + bool force_antialiasing_ = false; }; TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_GL) { @@ -128,6 +330,113 @@ RunBlendingWithTransparentPixelTestType(SOFTWARE_WITH_BITMAP); } +// Tests for render passes +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_GL) { + RunBlendingWithRenderPass( + GL_WITH_BITMAP, FILE_PATH_LITERAL("blending_render_pass.png"), 0); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_Software) { + RunBlendingWithRenderPass( + SOFTWARE_WITH_BITMAP, FILE_PATH_LITERAL("blending_render_pass.png"), 0); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMask_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMask_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskAA_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskAA_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassColorMatrix_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseAntialiasing | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassColorMatrixAA_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseAntialiasing | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrix_GL) { + RunBlendingWithRenderPass( + GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrix_Software) { + RunBlendingWithRenderPass( + SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrixAA_GL) { + RunBlendingWithRenderPass( + GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrixAA_Software) { + RunBlendingWithRenderPass( + SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix); +} + } // namespace } // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc index b9e15c6..5dafb7f 100644 --- a/cc/trees/layer_tree_host_unittest_context.cc +++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -912,8 +912,7 @@ NULL, 0, false, - 1, - false); + 1); } static void EmptyReleaseCallback(unsigned sync_point, bool lost) {}
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc index 8e11015..e4d5f1d 100644 --- a/cc/trees/layer_tree_settings.cc +++ b/cc/trees/layer_tree_settings.cc
@@ -15,6 +15,7 @@ LayerTreeSettings::LayerTreeSettings() : impl_side_painting(false), allow_antialiasing(true), + force_antialiasing(false), throttle_frame_production(true), single_thread_proxy_scheduler(true), begin_frame_scheduling_enabled(false),
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h index c0c1453..fff1084 100644 --- a/cc/trees/layer_tree_settings.h +++ b/cc/trees/layer_tree_settings.h
@@ -20,6 +20,7 @@ bool impl_side_painting; bool allow_antialiasing; + bool force_antialiasing; bool throttle_frame_production; bool single_thread_proxy_scheduler; bool begin_frame_scheduling_enabled;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 1bc8aad..0b8d68d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -9746,11 +9746,11 @@ const void* cmd_data) { const gles2::cmds::WaitSyncPointCHROMIUM& c = *static_cast<const gles2::cmds::WaitSyncPointCHROMIUM*>(cmd_data); - group_->mailbox_manager()->PullTextureUpdates(); + uint32 sync_point = c.sync_point; if (wait_sync_point_callback_.is_null()) return error::kNoError; - return wait_sync_point_callback_.Run(c.sync_point) ? + return wait_sync_point_callback_.Run(sync_point) ? error::kNoError : error::kDeferCommandUntilLater; }
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc index 33e5a2c..1e7cc1f 100644 --- a/gpu/command_buffer/service/in_process_command_buffer.cc +++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -177,11 +177,6 @@ base::LazyInstance<SyncPointManager> g_sync_point_manager = LAZY_INSTANCE_INITIALIZER; -bool WaitSyncPoint(uint32 sync_point) { - g_sync_point_manager.Get().WaitSyncPoint(sync_point); - return true; -} - } // anonyous namespace InProcessCommandBuffer::Service::Service() {} @@ -422,7 +417,9 @@ decoder_->SetResizeCallback(base::Bind( &InProcessCommandBuffer::OnResizeView, gpu_thread_weak_ptr_)); } - decoder_->SetWaitSyncPointCallback(base::Bind(&WaitSyncPoint)); + decoder_->SetWaitSyncPointCallback( + base::Bind(&InProcessCommandBuffer::WaitSyncPointOnGpuThread, + base::Unretained(this))); return true; } @@ -658,7 +655,7 @@ make_current_success = MakeCurrent(); } if (make_current_success) - mailbox_manager->PushTextureUpdates(); + mailbox_manager->PushTextureUpdates(sync_point); } g_sync_point_manager.Get().RetireSyncPoint(sync_point); } @@ -672,6 +669,14 @@ WrapCallback(callback))); } +bool InProcessCommandBuffer::WaitSyncPointOnGpuThread(unsigned sync_point) { + g_sync_point_manager.Get().WaitSyncPoint(sync_point); + gles2::MailboxManager* mailbox_manager = + decoder_->GetContextGroup()->mailbox_manager(); + mailbox_manager->PullTextureUpdates(sync_point); + return true; +} + void InProcessCommandBuffer::SignalSyncPointOnGpuThread( unsigned sync_point, const base::Closure& callback) {
diff --git a/gpu/command_buffer/service/in_process_command_buffer.h b/gpu/command_buffer/service/in_process_command_buffer.h index ec06ecd..e7b2260 100644 --- a/gpu/command_buffer/service/in_process_command_buffer.h +++ b/gpu/command_buffer/service/in_process_command_buffer.h
@@ -181,6 +181,7 @@ void RetireSyncPointOnGpuThread(uint32 sync_point); void SignalSyncPointOnGpuThread(uint32 sync_point, const base::Closure& callback); + bool WaitSyncPointOnGpuThread(uint32 sync_point); void SignalQueryOnGpuThread(unsigned query_id, const base::Closure& callback); void DestroyTransferBufferOnGpuThread(int32 id);
diff --git a/gpu/command_buffer/service/mailbox_manager.cc b/gpu/command_buffer/service/mailbox_manager.cc index e6962df..b4d6e79 100644 --- a/gpu/command_buffer/service/mailbox_manager.cc +++ b/gpu/command_buffer/service/mailbox_manager.cc
@@ -84,14 +84,14 @@ sync_->TextureDeleted(texture); } -void MailboxManager::PushTextureUpdates() { +void MailboxManager::PushTextureUpdates(uint32 sync_point) { if (sync_) - sync_->PushTextureUpdates(this); + sync_->PushTextureUpdates(this, sync_point); } -void MailboxManager::PullTextureUpdates() { +void MailboxManager::PullTextureUpdates(uint32 sync_point) { if (sync_) - sync_->PullTextureUpdates(this); + sync_->PullTextureUpdates(this, sync_point); } MailboxManager::TargetName::TargetName(unsigned target, const Mailbox& mailbox)
diff --git a/gpu/command_buffer/service/mailbox_manager.h b/gpu/command_buffer/service/mailbox_manager.h index e1b36cb..a00b965 100644 --- a/gpu/command_buffer/service/mailbox_manager.h +++ b/gpu/command_buffer/service/mailbox_manager.h
@@ -41,8 +41,8 @@ // Used with the MailboxSynchronizer to push/pull texture state to/from // other manager instances. - void PushTextureUpdates(); - void PullTextureUpdates(); + void PushTextureUpdates(uint32 sync_point); + void PullTextureUpdates(uint32 sync_point); // Destroy any mailbox that reference the given texture. void TextureDeleted(Texture* texture);
diff --git a/gpu/command_buffer/service/mailbox_manager_unittest.cc b/gpu/command_buffer/service/mailbox_manager_unittest.cc index df1cd4e..63ffc6d 100644 --- a/gpu/command_buffer/service/mailbox_manager_unittest.cc +++ b/gpu/command_buffer/service/mailbox_manager_unittest.cc
@@ -300,8 +300,8 @@ EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); // Synchronize - manager_->PushTextureUpdates(); - manager2_->PullTextureUpdates(); + manager_->PushTextureUpdates(0); + manager2_->PullTextureUpdates(0); DestroyTexture(texture); EXPECT_EQ(NULL, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); @@ -321,8 +321,8 @@ EXPECT_EQ(texture, manager_->ConsumeTexture(GL_TEXTURE_2D, name)); // Synchronize - manager_->PushTextureUpdates(); - manager2_->PullTextureUpdates(); + manager_->PushTextureUpdates(0); + manager2_->PullTextureUpdates(0); EXPECT_CALL(*gl_, GenTextures(1, _)) .WillOnce(SetArgPointee<1>(kNewTextureId)); @@ -349,10 +349,10 @@ EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL); // Synchronize again - manager_->PushTextureUpdates(); + manager_->PushTextureUpdates(0); SetupUpdateTexParamExpectations( kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); - manager2_->PullTextureUpdates(); + manager2_->PullTextureUpdates(0); GLsizei width, height; new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height); EXPECT_EQ(16, width); @@ -382,7 +382,7 @@ // The last change to the texture should be visible without a sync point (i.e. // push). - manager2_->PullTextureUpdates(); + manager2_->PullTextureUpdates(0); new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height); EXPECT_EQ(64, width); EXPECT_EQ(64, height); @@ -410,8 +410,8 @@ manager2_->ProduceTexture(GL_TEXTURE_2D, name2, texture2); // Make visible. - manager_->PushTextureUpdates(); - manager2_->PushTextureUpdates(); + manager_->PushTextureUpdates(0); + manager2_->PushTextureUpdates(0); // Create textures in the other manager instances for texture1 and texture2, // respectively to create a real sharing scenario. Otherwise, there would @@ -438,7 +438,7 @@ SetParameter(texture1, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); // Make sure this does not clobber it with the previous version we pushed. - manager_->PullTextureUpdates(); + manager_->PullTextureUpdates(0); // Make a change to texture2 DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture2->mag_filter()); @@ -448,16 +448,16 @@ Mock::VerifyAndClearExpectations(gl_.get()); // Synchronize in both directions - manager_->PushTextureUpdates(); - manager2_->PushTextureUpdates(); + manager_->PushTextureUpdates(0); + manager2_->PushTextureUpdates(0); // manager1 should see the change to texture2 mag_filter being applied. SetupUpdateTexParamExpectations( new_texture2->service_id(), GL_LINEAR, GL_NEAREST, GL_REPEAT, GL_REPEAT); - manager_->PullTextureUpdates(); + manager_->PullTextureUpdates(0); // manager2 should see the change to texture1 min_filter being applied. SetupUpdateTexParamExpectations( new_texture1->service_id(), GL_NEAREST, GL_LINEAR, GL_REPEAT, GL_REPEAT); - manager2_->PullTextureUpdates(); + manager2_->PullTextureUpdates(0); DestroyTexture(texture1); DestroyTexture(texture2);
diff --git a/gpu/command_buffer/service/mailbox_synchronizer.cc b/gpu/command_buffer/service/mailbox_synchronizer.cc index eac31f9..81a2793 100644 --- a/gpu/command_buffer/service/mailbox_synchronizer.cc +++ b/gpu/command_buffer/service/mailbox_synchronizer.cc
@@ -7,8 +7,13 @@ #include "base/bind.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/texture_manager.h" +#include "ui/gl/gl_fence.h" #include "ui/gl/gl_implementation.h" +#if !defined(OS_MACOSX) +#include "ui/gl/gl_fence_egl.h" +#endif + namespace gpu { namespace gles2 { @@ -136,7 +141,8 @@ } } -void MailboxSynchronizer::PushTextureUpdates(MailboxManager* manager) { +void MailboxSynchronizer::PushTextureUpdates(MailboxManager* manager, + uint32 sync_point) { base::AutoLock lock(lock_); for (MailboxManager::MailboxToTextureMap::const_iterator texture_it = manager->mailbox_to_textures_.begin(); @@ -179,6 +185,33 @@ textures_.insert(std::make_pair(texture, TextureVersion(group))); } } + + CreateFenceLocked(sync_point); +} + +void MailboxSynchronizer::CreateFenceLocked(uint32 sync_point) { + lock_.AssertAcquired(); + if (gfx::GetGLImplementation() == gfx::kGLImplementationMockGL) + return; + +#if !defined(OS_MACOSX) + if (sync_point) { + while (!sync_points_.empty() && + sync_points_.front()->second->HasCompleted()) { + sync_point_to_fence_.erase(sync_points_.front()); + sync_points_.pop(); + } + // Need to use EGL fences since we are likely not in a single share group. + linked_ptr<gfx::GLFence> fence(make_linked_ptr(new gfx::GLFenceEGL(true))); + if (fence.get()) { + std::pair<SyncPointToFenceMap::iterator, bool> result = + sync_point_to_fence_.insert(std::make_pair(sync_point, fence)); + DCHECK(result.second); + sync_points_.push(result.first); + } + DCHECK(sync_points_.size() == sync_point_to_fence_.size()); + } +#endif } void MailboxSynchronizer::UpdateTextureLocked(Texture* texture, @@ -208,8 +241,20 @@ gl_image ? image_buffer : NULL); } -void MailboxSynchronizer::PullTextureUpdates(MailboxManager* manager) { +void MailboxSynchronizer::AcquireFenceLocked(uint32 sync_point) { + lock_.AssertAcquired(); + SyncPointToFenceMap::iterator fence_it = + sync_point_to_fence_.find(sync_point); + if (fence_it != sync_point_to_fence_.end()) { + fence_it->second->ServerWait(); + } +} + +void MailboxSynchronizer::PullTextureUpdates(MailboxManager* manager, + uint32 sync_point) { base::AutoLock lock(lock_); + AcquireFenceLocked(sync_point); + for (MailboxManager::MailboxToTextureMap::const_iterator texture_it = manager->mailbox_to_textures_.begin(); texture_it != manager->mailbox_to_textures_.end();
diff --git a/gpu/command_buffer/service/mailbox_synchronizer.h b/gpu/command_buffer/service/mailbox_synchronizer.h index a845963..3ddb9d0 100644 --- a/gpu/command_buffer/service/mailbox_synchronizer.h +++ b/gpu/command_buffer/service/mailbox_synchronizer.h
@@ -8,6 +8,7 @@ #include "gpu/command_buffer/common/mailbox.h" #include <map> +#include <queue> #include <set> #include "base/memory/linked_ptr.h" @@ -15,6 +16,10 @@ #include "gpu/command_buffer/service/texture_definition.h" #include "gpu/gpu_export.h" +namespace gfx { +class GLFence; +} + namespace gpu { namespace gles2 { @@ -34,8 +39,8 @@ // Create a texture from a globally visible mailbox. Texture* CreateTextureFromMailbox(unsigned target, const Mailbox& mailbox); - void PushTextureUpdates(MailboxManager* manager); - void PullTextureUpdates(MailboxManager* manager); + void PushTextureUpdates(MailboxManager* manager, uint32 sync_point); + void PullTextureUpdates(MailboxManager* manager, uint32 sync_point); void TextureDeleted(Texture* texture); @@ -85,6 +90,12 @@ const TargetName& target_name, TextureGroup* group); void UpdateTextureLocked(Texture* texture, TextureVersion& texture_version); + void CreateFenceLocked(uint32 sync_point); + void AcquireFenceLocked(uint32 sync_point); + + typedef std::map<uint32, linked_ptr<gfx::GLFence> > SyncPointToFenceMap; + SyncPointToFenceMap sync_point_to_fence_; + std::queue<SyncPointToFenceMap::iterator> sync_points_; DISALLOW_COPY_AND_ASSIGN(MailboxSynchronizer); };
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc index 63a64b7..ac7021a 100644 --- a/gpu/command_buffer/service/program_manager.cc +++ b/gpu/command_buffer/service/program_manager.cc
@@ -81,8 +81,8 @@ return true; } -bool IsBuiltInVarying(const std::string& name) { - // Built-in variables. +bool IsBuiltInFragmentVarying(const std::string& name) { + // Built-in variables for fragment shaders. const char* kBuiltInVaryings[] = { "gl_FragCoord", "gl_FrontFacing", @@ -95,6 +95,14 @@ return false; } +bool IsBuiltInInvariant( + const VaryingMap& varyings, const std::string& name) { + VaryingMap::const_iterator hit = varyings.find(name); + if (hit == varyings.end()) + return false; + return hit->second.isInvariant; +} + } // anonymous namespace. Program::UniformInfo::UniformInfo() @@ -526,6 +534,11 @@ set_log_info(ProcessLogInfo(info_log).c_str()); return false; } + if (DetectBuiltInInvariantConflicts()) { + set_log_info("Invariant settings for certain built-in varyings " + "have to match"); + return false; + } if (DetectGlobalNameConflicts(&conflicting_name)) { std::string info_log = "Name conflicts between an uniform and an " "attribute: " + conflicting_name; @@ -1071,7 +1084,7 @@ for (VaryingMap::const_iterator iter = fragment_varyings->begin(); iter != fragment_varyings->end(); ++iter) { const std::string& name = iter->first; - if (IsBuiltInVarying(name)) + if (IsBuiltInFragmentVarying(name)) continue; VaryingMap::const_iterator hit = vertex_varyings->find(name); @@ -1092,6 +1105,28 @@ return false; } +bool Program::DetectBuiltInInvariantConflicts() const { + DCHECK(attached_shaders_[0].get() && + attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && + attached_shaders_[1].get() && + attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER); + const VaryingMap& vertex_varyings = attached_shaders_[0]->varying_map(); + const VaryingMap& fragment_varyings = attached_shaders_[1]->varying_map(); + + bool gl_position_invariant = IsBuiltInInvariant( + vertex_varyings, "gl_Position"); + bool gl_point_size_invariant = IsBuiltInInvariant( + vertex_varyings, "gl_PointSize"); + + bool gl_frag_coord_invariant = IsBuiltInInvariant( + fragment_varyings, "gl_FragCoord"); + bool gl_point_coord_invariant = IsBuiltInInvariant( + fragment_varyings, "gl_PointCoord"); + + return ((gl_frag_coord_invariant && !gl_position_invariant) || + (gl_point_coord_invariant && !gl_point_size_invariant)); +} + bool Program::DetectGlobalNameConflicts(std::string* conflicting_name) const { DCHECK(attached_shaders_[0].get() && attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && @@ -1130,7 +1165,7 @@ iter != fragment_varyings->end(); ++iter) { if (!iter->second.staticUse && option == kCountOnlyStaticallyUsed) continue; - if (!IsBuiltInVarying(iter->first)) { + if (!IsBuiltInFragmentVarying(iter->first)) { VaryingMap::const_iterator vertex_iter = vertex_varyings->find(iter->first); if (vertex_iter == vertex_varyings->end() ||
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h index 9698fc1..dbe9c14 100644 --- a/gpu/command_buffer/service/program_manager.h +++ b/gpu/command_buffer/service/program_manager.h
@@ -215,6 +215,10 @@ // is not declared in vertex shader. bool DetectVaryingsMismatch(std::string* conflicting_name) const; + // Return true if any built-in invariant matching rules are broken as in + // GLSL ES spec 1.00.17, section 4.6.4, Invariance and Linkage. + bool DetectBuiltInInvariantConflicts() const; + // Return true if an uniform and an attribute share the same name. bool DetectGlobalNameConflicts(std::string* conflicting_name) const;
diff --git a/gpu/command_buffer/service/shader_translator_unittest.cc b/gpu/command_buffer/service/shader_translator_unittest.cc index 198ea82..40d5be7 100644 --- a/gpu/command_buffer/service/shader_translator_unittest.cc +++ b/gpu/command_buffer/service/shader_translator_unittest.cc
@@ -69,10 +69,11 @@ EXPECT_TRUE(info_log.empty()); // Translated shader must be valid and non-empty. ASSERT_FALSE(translated_source.empty()); - // There should be no attributes, uniforms, varyings. + // There should be no attributes, uniforms, and only one built-in + // varying: gl_Position. EXPECT_TRUE(attrib_map.empty()); EXPECT_TRUE(uniform_map.empty()); - EXPECT_TRUE(varying_map.empty()); + EXPECT_EQ(1u, varying_map.size()); // There should be no name mapping. EXPECT_TRUE(name_map.empty()); }
diff --git a/gpu/command_buffer/service/texture_definition.cc b/gpu/command_buffer/service/texture_definition.cc index cf385d2..abde56a 100644 --- a/gpu/command_buffer/service/texture_definition.cc +++ b/gpu/command_buffer/service/texture_definition.cc
@@ -15,7 +15,6 @@ #include "ui/gl/scoped_binders.h" #if !defined(OS_MACOSX) -#include "ui/gl/gl_fence_egl.h" #include "ui/gl/gl_surface_egl.h" #endif @@ -88,23 +87,15 @@ } void GLImageSync::WillUseTexImage() { - if (buffer_.get()) - buffer_->WillRead(this); } void GLImageSync::DidUseTexImage() { - if (buffer_.get()) - buffer_->DidRead(this); } void GLImageSync::WillModifyTexImage() { - if (buffer_.get()) - buffer_->WillWrite(this); } void GLImageSync::DidModifyTexImage() { - if (buffer_.get()) - buffer_->DidWrite(this); } bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, @@ -128,10 +119,6 @@ virtual void RemoveClient(gfx::GLImage* client) override; virtual bool IsClient(gfx::GLImage* client) override; virtual void BindToTexture(GLenum target) override; - virtual void WillRead(gfx::GLImage* client) override; - virtual void WillWrite(gfx::GLImage* client) override; - virtual void DidRead(gfx::GLImage* client) override; - virtual void DidWrite(gfx::GLImage* client) override; EGLDisplay egl_display_; EGLImageKHR egl_image_; @@ -144,10 +131,8 @@ gfx::GLImage* client; bool needs_wait_before_read; - linked_ptr<gfx::GLFence> read_fence; }; std::list<ClientInfo> client_infos_; - scoped_ptr<gfx::GLFence> write_fence_; gfx::GLImage* write_client_; DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL); @@ -164,8 +149,7 @@ DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base && gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image && - gfx::g_driver_gl.ext.b_GL_OES_EGL_image && - gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync); + gfx::g_driver_gl.ext.b_GL_OES_EGL_image); const EGLint egl_attrib_list[] = { EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; @@ -191,7 +175,6 @@ : NativeImageBuffer(), egl_display_(display), egl_image_(image), - write_fence_(new gfx::GLFenceEGL(true)), write_client_(NULL) { DCHECK(egl_display_ != EGL_NO_DISPLAY); DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); @@ -241,64 +224,6 @@ DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); } -void NativeImageBufferEGL::WillRead(gfx::GLImage* client) { - base::AutoLock lock(lock_); - if (!write_fence_.get() || write_client_ == client) - return; - - for (std::list<ClientInfo>::iterator it = client_infos_.begin(); - it != client_infos_.end(); - it++) { - if (it->client == client) { - if (it->needs_wait_before_read) { - it->needs_wait_before_read = false; - write_fence_->ServerWait(); - } - return; - } - } - NOTREACHED(); -} - -void NativeImageBufferEGL::WillWrite(gfx::GLImage* client) { - base::AutoLock lock(lock_); - if (write_client_ != client) - write_fence_->ServerWait(); - - for (std::list<ClientInfo>::iterator it = client_infos_.begin(); - it != client_infos_.end(); - it++) { - if (it->read_fence.get() && it->client != client) - it->read_fence->ServerWait(); - } -} - -void NativeImageBufferEGL::DidRead(gfx::GLImage* client) { - base::AutoLock lock(lock_); - for (std::list<ClientInfo>::iterator it = client_infos_.begin(); - it != client_infos_.end(); - it++) { - if (it->client == client) { - it->read_fence = make_linked_ptr(new gfx::GLFenceEGL(true)); - return; - } - } - NOTREACHED(); -} - -void NativeImageBufferEGL::DidWrite(gfx::GLImage* client) { - base::AutoLock lock(lock_); - // Sharing semantics require the client to flush in order to make changes - // visible to other clients. - write_fence_.reset(new gfx::GLFenceEGL(false)); - write_client_ = client; - for (std::list<ClientInfo>::iterator it = client_infos_.begin(); - it != client_infos_.end(); - it++) { - it->needs_wait_before_read = true; - } -} - #endif class NativeImageBufferStub : public NativeImageBuffer { @@ -311,10 +236,6 @@ void RemoveClient(gfx::GLImage* client) override {} bool IsClient(gfx::GLImage* client) override { return true; } void BindToTexture(GLenum target) override {} - void WillRead(gfx::GLImage* client) override {} - void WillWrite(gfx::GLImage* client) override {} - void DidRead(gfx::GLImage* client) override {} - void DidWrite(gfx::GLImage* client) override {} DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub); };
diff --git a/gpu/command_buffer/service/texture_definition.h b/gpu/command_buffer/service/texture_definition.h index 6df4b86..cb21abd 100644 --- a/gpu/command_buffer/service/texture_definition.h +++ b/gpu/command_buffer/service/texture_definition.h
@@ -27,10 +27,6 @@ virtual void RemoveClient(gfx::GLImage* client) = 0; virtual bool IsClient(gfx::GLImage* client) = 0; virtual void BindToTexture(GLenum target) = 0; - virtual void WillRead(gfx::GLImage* client) = 0; - virtual void WillWrite(gfx::GLImage* client) = 0; - virtual void DidRead(gfx::GLImage* client) = 0; - virtual void DidWrite(gfx::GLImage* client) = 0; protected: friend class base::RefCountedThreadSafe<NativeImageBuffer>;
diff --git a/net/PRESUBMIT.py b/net/PRESUBMIT.py index e898d99..e82166c 100644 --- a/net/PRESUBMIT.py +++ b/net/PRESUBMIT.py
@@ -11,13 +11,13 @@ def GetPreferredTryMasters(project, change): masters = { 'tryserver.chromium.linux': { - 'linux_chromium_rel_swarming': set(['defaulttests']), + 'linux_chromium_rel': set(['defaulttests']), }, 'tryserver.chromium.mac': { - 'mac_chromium_rel_swarming': set(['defaulttests']), + 'mac_chromium_rel': set(['defaulttests']), }, 'tryserver.chromium.win': { - 'win_chromium_rel_swarming': set(['defaulttests']), + 'win_chromium_rel': set(['defaulttests']), } } # Changes that touch NSS files will likely need a corresponding OpenSSL edit.
diff --git a/net/base/data_url_unittest.cc b/net/base/data_url_unittest.cc index 3876301..bcb2b49 100644 --- a/net/base/data_url_unittest.cc +++ b/net/base/data_url_unittest.cc
@@ -184,6 +184,44 @@ "", "" }, + // BiDi control characters should be unescaped and preserved as is, and + // should not be replaced with % versions. In the below case, \xE2\x80\x8F + // is the RTL mark and the parsed text should preserve it as is. + { + "data:text/plain;charset=utf-8,\xE2\x80\x8Ftest", + true, + "text/plain", + "utf-8", + "\xE2\x80\x8Ftest"}, + + // Same as above but with Arabic text after RTL mark. + { + "data:text/plain;charset=utf-8," + "\xE2\x80\x8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1", + true, + "text/plain", + "utf-8", + "\xE2\x80\x8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1"}, + + // RTL mark encoded as %E2%80%8F should be unescaped too. Note that when + // wrapped in a GURL, this URL and the next effectively become the same as + // the previous two URLs. + { + "data:text/plain;charset=utf-8,%E2%80%8Ftest", + true, + "text/plain", + "utf-8", + "\xE2\x80\x8Ftest"}, + + // Same as above but with Arabic text after RTL mark. + { + "data:text/plain;charset=utf-8," + "%E2%80%8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1", + true, + "text/plain", + "utf-8", + "\xE2\x80\x8F\xD8\xA7\xD8\xAE\xD8\xAA\xD8\xA8\xD8\xA7\xD8\xB1"} + // TODO(darin): add more interesting tests };
diff --git a/net/base/escape.cc b/net/base/escape.cc index 1798b6c..3c8adc6 100644 --- a/net/base/escape.cc +++ b/net/base/escape.cc
@@ -120,6 +120,44 @@ return false; } +// Returns true if there is an Arabic Language Mark at |index|. |first_byte| +// is the byte at |index|. +template<typename STR> +bool HasArabicLanguageMarkAtIndex(const STR& escaped_text, + unsigned char first_byte, + size_t index) { + if (first_byte != 0xD8) + return false; + unsigned char second_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 3, &second_byte)) + return false; + return second_byte == 0x9c; +} + +// Returns true if there is a BiDi control char at |index|. |first_byte| is the +// byte at |index|. +template<typename STR> +bool HasThreeByteBidiControlCharAtIndex(const STR& escaped_text, + unsigned char first_byte, + size_t index) { + if (first_byte != 0xE2) + return false; + unsigned char second_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 3, &second_byte)) + return false; + if (second_byte != 0x80 && second_byte != 0x81) + return false; + unsigned char third_byte; + if (!UnescapeUnsignedCharAtIndex(escaped_text, index + 6, &third_byte)) + return false; + if (second_byte == 0x80) { + return third_byte == 0x8E || + third_byte == 0x8F || + (third_byte >= 0xAA && third_byte <= 0xAE); + } + return third_byte >= 0xA6 && third_byte <= 0xA9; +} + // Unescapes |escaped_text| according to |rules|, returning the resulting // string. Fills in an |adjustments| parameter, if non-NULL, so it reflects // the alterations done to the string that are not one-character-to-one- @@ -172,27 +210,21 @@ // U+2067 RIGHT-TO-LEFT ISOLATE (%E2%81%A7) // U+2068 FIRST STRONG ISOLATE (%E2%81%A8) // U+2069 POP DIRECTIONAL ISOLATE (%E2%81%A9) - - unsigned char second_byte; - // Check for ALM. - if ((first_byte == 0xD8) && - UnescapeUnsignedCharAtIndex(escaped_text, i + 3, &second_byte) && - (second_byte == 0x9c)) { - result.append(escaped_text, i, 6); - i += 5; - continue; - } - - // Check for other BiDi control characters. - if ((first_byte == 0xE2) && - UnescapeUnsignedCharAtIndex(escaped_text, i + 3, &second_byte) && - ((second_byte == 0x80) || (second_byte == 0x81))) { - unsigned char third_byte; - if (UnescapeUnsignedCharAtIndex(escaped_text, i + 6, &third_byte) && - ((second_byte == 0x80) ? - ((third_byte == 0x8E) || (third_byte == 0x8F) || - ((third_byte >= 0xAA) && (third_byte <= 0xAE))) : - ((third_byte >= 0xA6) && (third_byte <= 0xA9)))) { + // + // However, some schemes such as data: and file: need to parse the exact + // binary data when loading the URL. For that reason, CONTROL_CHARS allows + // unescaping BiDi control characters. + // DO NOT use CONTROL_CHARS if the parsed URL is going to be displayed + // in the UI. + if (!(rules & UnescapeRule::CONTROL_CHARS)) { + if (HasArabicLanguageMarkAtIndex(escaped_text, first_byte, i)) { + // Keep Arabic Language Mark escaped. + result.append(escaped_text, i, 6); + i += 5; + continue; + } + if (HasThreeByteBidiControlCharAtIndex(escaped_text, first_byte, i)) { + // Keep BiDi control char escaped. result.append(escaped_text, i, 9); i += 8; continue;
diff --git a/net/base/escape.h b/net/base/escape.h index 1915d24..bc58439 100644 --- a/net/base/escape.h +++ b/net/base/escape.h
@@ -88,7 +88,10 @@ // Unescapes control characters such as %01. This INCLUDES NULLs. This is // used for rare cases such as data: URL decoding where the result is binary - // data. You should not use this for normal URLs! + // data. This flag also unescapes BiDi control characters. + // + // DO NOT use CONTROL_CHARS if the URL is going to be displayed in the UI + // for security reasons. CONTROL_CHARS = 8, // URL queries use "+" for space. This flag controls that replacement.
diff --git a/net/base/escape_unittest.cc b/net/base/escape_unittest.cc index 74ae293..1e82239 100644 --- a/net/base/escape_unittest.cc +++ b/net/base/escape_unittest.cc
@@ -232,7 +232,8 @@ {L"Some%20random text %25%E2%80%84OK", UnescapeRule::NORMAL, L"Some%20random text %25\xE2\x80\x84OK"}, - // BiDi Control characters should not be unescaped. + // BiDi Control characters should not be unescaped unless explicity told to + // do so with UnescapeRule::CONTROL_CHARS {L"Some%20random text %25%D8%9COK", UnescapeRule::NORMAL, L"Some%20random text %25%D8%9COK"}, {L"Some%20random text %25%E2%80%8EOK", UnescapeRule::NORMAL, @@ -249,6 +250,31 @@ L"Some%20random text %25%E2%81%A6OK"}, {L"Some%20random text %25%E2%81%A9OK", UnescapeRule::NORMAL, L"Some%20random text %25%E2%81%A9OK"}, + // UnescapeRule::CONTROL_CHARS should unescape BiDi Control characters. + {L"Some%20random text %25%D8%9COK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xD8\x9COK"}, + {L"Some%20random text %25%E2%80%8EOK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x80\x8EOK"}, + {L"Some%20random text %25%E2%80%8FOK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x80\x8FOK"}, + {L"Some%20random text %25%E2%80%AAOK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x80\xAAOK"}, + {L"Some%20random text %25%E2%80%ABOK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x80\xABOK"}, + {L"Some%20random text %25%E2%80%AEOK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x80\xAEOK"}, + {L"Some%20random text %25%E2%81%A6OK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x81\xA6OK"}, + {L"Some%20random text %25%E2%81%A9OK", + UnescapeRule::NORMAL | UnescapeRule::CONTROL_CHARS, + L"Some%20random text %25\xE2\x81\xA9OK"}, {L"Some%20random text %25%2dOK", UnescapeRule::SPACES, L"Some random text %25-OK"},
diff --git a/net/base/trace_net_log_observer.cc b/net/base/trace_net_log_observer.cc index 15960c3..5d98c41 100644 --- a/net/base/trace_net_log_observer.cc +++ b/net/base/trace_net_log_observer.cc
@@ -19,6 +19,9 @@ namespace { +// TraceLog category for NetLog events. +const char kNetLogTracingCategory[] = TRACE_DISABLED_BY_DEFAULT("netlog"); + class TracedValue : public base::debug::ConvertableToTraceFormat { public: explicit TracedValue(scoped_ptr<base::Value> value) : value_(value.Pass()) {} @@ -55,21 +58,24 @@ switch (entry.phase()) { case NetLog::PHASE_BEGIN: TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( - "netlog", NetLog::EventTypeToString(entry.type()), entry.source().id, + kNetLogTracingCategory, + NetLog::EventTypeToString(entry.type()), entry.source().id, "source_type", NetLog::SourceTypeToString(entry.source().type), "params", scoped_refptr<base::debug::ConvertableToTraceFormat>( new TracedValue(params.Pass()))); break; case NetLog::PHASE_END: TRACE_EVENT_NESTABLE_ASYNC_END2( - "netlog", NetLog::EventTypeToString(entry.type()), entry.source().id, + kNetLogTracingCategory, + NetLog::EventTypeToString(entry.type()), entry.source().id, "source_type", NetLog::SourceTypeToString(entry.source().type), "params", scoped_refptr<base::debug::ConvertableToTraceFormat>( new TracedValue(params.Pass()))); break; case NetLog::PHASE_NONE: TRACE_EVENT_NESTABLE_ASYNC_INSTANT2( - "netlog", NetLog::EventTypeToString(entry.type()), entry.source().id, + kNetLogTracingCategory, + NetLog::EventTypeToString(entry.type()), entry.source().id, "source_type", NetLog::SourceTypeToString(entry.source().type), "params", scoped_refptr<base::debug::ConvertableToTraceFormat>( new TracedValue(params.Pass())));
diff --git a/net/base/trace_net_log_observer_unittest.cc b/net/base/trace_net_log_observer_unittest.cc index c1a8803..2e20707 100644 --- a/net/base/trace_net_log_observer_unittest.cc +++ b/net/base/trace_net_log_observer_unittest.cc
@@ -27,6 +27,9 @@ namespace { +// TraceLog category for NetLog events. +const char kNetLogTracingCategory[] = TRACE_DISABLED_BY_DEFAULT("netlog"); + struct TraceEntryInfo { std::string category; std::string id; @@ -86,9 +89,10 @@ } static void EnableTraceLog() { - TraceLog::GetInstance()->SetEnabled(base::debug::CategoryFilter("netlog"), - TraceLog::RECORDING_MODE, - base::debug::TraceOptions()); + TraceLog::GetInstance()->SetEnabled( + base::debug::CategoryFilter(kNetLogTracingCategory), + TraceLog::RECORDING_MODE, + base::debug::TraceOptions()); } void EndTraceAndFlush() { @@ -120,7 +124,7 @@ << "Unexpected item without a category field in trace_events"; continue; } - if (category != "netlog") + if (category != kNetLogTracingCategory) continue; filtered_trace_events->Append(dict->DeepCopy()); } @@ -185,7 +189,7 @@ TraceEntryInfo actual_item1 = GetTraceEntryInfoFromValue(*item1); TraceEntryInfo actual_item2 = GetTraceEntryInfoFromValue(*item2); TraceEntryInfo actual_item3 = GetTraceEntryInfoFromValue(*item3); - EXPECT_EQ("netlog", actual_item1.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item1.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[0].source.id), actual_item1.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT), actual_item1.phase); @@ -194,7 +198,7 @@ EXPECT_EQ(NetLog::SourceTypeToString(entries[0].source.type), actual_item1.source_type); - EXPECT_EQ("netlog", actual_item2.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item2.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[1].source.id), actual_item2.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN), actual_item2.phase); @@ -203,7 +207,7 @@ EXPECT_EQ(NetLog::SourceTypeToString(entries[1].source.type), actual_item2.source_type); - EXPECT_EQ("netlog", actual_item3.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item3.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[2].source.id), actual_item3.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END), actual_item3.phase); @@ -236,7 +240,7 @@ TraceEntryInfo actual_item1 = GetTraceEntryInfoFromValue(*item1); TraceEntryInfo actual_item2 = GetTraceEntryInfoFromValue(*item2); - EXPECT_EQ("netlog", actual_item1.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item1.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[0].source.id), actual_item1.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT), actual_item1.phase); @@ -245,7 +249,7 @@ EXPECT_EQ(NetLog::SourceTypeToString(entries[0].source.type), actual_item1.source_type); - EXPECT_EQ("netlog", actual_item2.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item2.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[2].source.id), actual_item2.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT), actual_item2.phase); @@ -274,7 +278,7 @@ ASSERT_TRUE(trace_events()->GetDictionary(0, &item1)); TraceEntryInfo actual_item1 = GetTraceEntryInfoFromValue(*item1); - EXPECT_EQ("netlog", actual_item1.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item1.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[0].source.id), actual_item1.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT), actual_item1.phase); @@ -342,7 +346,7 @@ TraceEntryInfo actual_item1 = GetTraceEntryInfoFromValue(*item1); TraceEntryInfo actual_item2 = GetTraceEntryInfoFromValue(*item2); - EXPECT_EQ("netlog", actual_item1.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item1.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[0].source.id), actual_item1.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT), actual_item1.phase); @@ -351,7 +355,7 @@ EXPECT_EQ(NetLog::SourceTypeToString(entries[0].source.type), actual_item1.source_type); - EXPECT_EQ("netlog", actual_item2.category); + EXPECT_EQ(kNetLogTracingCategory, actual_item2.category); EXPECT_EQ(base::StringPrintf("0x%d", entries[1].source.id), actual_item2.id); EXPECT_EQ(std::string(1, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT), actual_item2.phase);
diff --git a/net/http/disk_cache_based_quic_server_info.cc b/net/http/disk_cache_based_quic_server_info.cc index d55349e..44332c9 100644 --- a/net/http/disk_cache_based_quic_server_info.cc +++ b/net/http/disk_cache_based_quic_server_info.cc
@@ -78,6 +78,7 @@ void DiskCacheBasedQuicServerInfo::Start() { DCHECK(CalledOnValidThread()); DCHECK_EQ(GET_BACKEND, state_); + load_start_time_ = base::TimeTicks::Now(); DoLoop(OK); } @@ -312,6 +313,8 @@ } entry_ = NULL; Parse(data_); + UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime", + base::TimeTicks::Now() - load_start_time_); return OK; }
diff --git a/net/http/disk_cache_based_quic_server_info.h b/net/http/disk_cache_based_quic_server_info.h index e5ced3f..f904042 100644 --- a/net/http/disk_cache_based_quic_server_info.h +++ b/net/http/disk_cache_based_quic_server_info.h
@@ -10,6 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" +#include "base/time/time.h" #include "net/base/completion_callback.h" #include "net/disk_cache/disk_cache.h" #include "net/quic/crypto/quic_server_info.h" @@ -98,6 +99,7 @@ scoped_refptr<IOBuffer> read_buffer_; scoped_refptr<IOBuffer> write_buffer_; std::string data_; + base::TimeTicks load_start_time_; base::WeakPtrFactory<DiskCacheBasedQuicServerInfo> weak_factory_; };
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index b5ca18b..27afbea 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc
@@ -339,6 +339,8 @@ num_incorrect_connection_ids_(0), num_undecryptable_packets_(0), num_duplicate_packets_(0), + num_blocked_frames_received_(0), + num_blocked_frames_sent_(0), connection_description_(GetConnectionDescriptionString()) { } @@ -355,6 +357,10 @@ num_undecryptable_packets_); UMA_HISTOGRAM_COUNTS("Net.QuicSession.DuplicatePacketsReceived", num_duplicate_packets_); + UMA_HISTOGRAM_COUNTS("Net.QuicSession.BlockedFrames.Received", + num_blocked_frames_received_); + UMA_HISTOGRAM_COUNTS("Net.QuicSession.BlockedFrames.Sent", + num_blocked_frames_sent_); if (num_frames_received_ > 0) { int duplicate_stream_frame_per_thousand = @@ -442,6 +448,7 @@ frame.window_update_frame)); break; case BLOCKED_FRAME: + ++num_blocked_frames_sent_; net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_SENT, base::Bind(&NetLogQuicBlockedFrameCallback, @@ -636,6 +643,7 @@ } void QuicConnectionLogger::OnBlockedFrame(const QuicBlockedFrame& frame) { + ++num_blocked_frames_received_; net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_RECEIVED, base::Bind(&NetLogQuicBlockedFrameCallback, &frame));
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h index 1d38049..29acfdc 100644 --- a/net/quic/quic_connection_logger.h +++ b/net/quic/quic_connection_logger.h
@@ -151,6 +151,10 @@ int num_undecryptable_packets_; // Count of the number of duplicate packets received. int num_duplicate_packets_; + // Count of the number of BLOCKED frames received. + int num_blocked_frames_received_; + // Count of the number of BLOCKED frames sent. + int num_blocked_frames_sent_; // Vector of inital packets status' indexed by packet sequence numbers, where // false means never received. Zero is not a valid packet sequence number, so // that offset is never used, and we'll track 150 packets.
diff --git a/net/quic/quic_reliable_client_stream.cc b/net/quic/quic_reliable_client_stream.cc index b1a82d1..e186b03 100644 --- a/net/quic/quic_reliable_client_stream.cc +++ b/net/quic/quic_reliable_client_stream.cc
@@ -42,12 +42,12 @@ return data_len; } -void QuicReliableClientStream::OnFinRead() { +void QuicReliableClientStream::OnClose() { if (delegate_) { delegate_->OnClose(connection_error()); delegate_ = nullptr; } - ReliableQuicStream::OnFinRead(); + ReliableQuicStream::OnClose(); } void QuicReliableClientStream::OnCanWrite() {
diff --git a/net/quic/quic_reliable_client_stream.h b/net/quic/quic_reliable_client_stream.h index 9ed6601..48f5cc9 100644 --- a/net/quic/quic_reliable_client_stream.h +++ b/net/quic/quic_reliable_client_stream.h
@@ -55,7 +55,7 @@ // QuicDataStream uint32 ProcessData(const char* data, uint32 data_len) override; - void OnFinRead() override; + void OnClose() override; void OnCanWrite() override; QuicPriority EffectivePriority() const override;
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index 0a5032e..4712620 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc
@@ -729,23 +729,26 @@ EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); QuicStreamId stream_id = 5; // Write until the header stream is flow control blocked. + SpdyHeaderBlock headers; while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2010) { EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); - SpdyHeaderBlock headers; headers["header"] = base::Uint64ToString(base::RandUint64()) + base::Uint64ToString(base::RandUint64()) + base::Uint64ToString(base::RandUint64()); headers_stream->WriteHeaders(stream_id, headers, true, nullptr); stream_id += 2; } + // Write one more to ensure that the headers stream has buffered data. The + // random headers may have exactly filled the flow control window. + headers_stream->WriteHeaders(stream_id, headers, true, nullptr); + EXPECT_TRUE(headers_stream->HasBufferedData()); + EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked()); EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked()); EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); EXPECT_FALSE(session_.HasDataToWrite()); - // TODO(rtenneti): crbug.com/423586 headers_stream->HasBufferedData is flaky. - // EXPECT_TRUE(headers_stream->HasBufferedData()); // Now complete the crypto handshake, resulting in an increased flow control // send window.
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index dac3096..046c0a1 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc
@@ -312,7 +312,6 @@ int QuicStreamFactory::Job::DoResolveHost() { // Start loading the data now, and wait for it after we resolve the host. if (server_info_) { - disk_cache_load_start_time_ = base::TimeTicks::Now(); server_info_->Start(); } @@ -351,6 +350,7 @@ if (!server_info_) return OK; + disk_cache_load_start_time_ = base::TimeTicks::Now(); return server_info_->WaitForDataReady( base::Bind(&QuicStreamFactory::Job::OnIOComplete, weak_factory_.GetWeakPtr())); @@ -358,7 +358,7 @@ int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) { if (server_info_) { - UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheReadTime", + UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheWaitForDataReadyTime", base::TimeTicks::Now() - disk_cache_load_start_time_); }
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index 13a3863..1dd9fb4 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -1822,7 +1822,7 @@ SynchronousErrorStreamSocket* raw_transport = transport.get(); scoped_ptr<SSLClientSocket> sock( - CreateSSLClientSocket(transport.PassAs<StreamSocket>(), + CreateSSLClientSocket(transport.Pass(), test_server.host_port_pair(), kDefaultSSLConfig)); @@ -1859,7 +1859,7 @@ SynchronousErrorStreamSocket* raw_transport = transport.get(); scoped_ptr<SSLClientSocket> sock( - CreateSSLClientSocket(transport.PassAs<StreamSocket>(), + CreateSSLClientSocket(transport.Pass(), test_server.host_port_pair(), ssl_config));
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 9d7ea87..fae97e8 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc
@@ -70,6 +70,9 @@ const size_t kPriorityDependencyPayloadSize = 4; const size_t kPriorityWeightPayloadSize = 1; +// Wire size of pad length field. +const size_t kPadLengthFieldSize = 1; + } // namespace const SpdyStreamId SpdyFramer::kInvalidStream = static_cast<SpdyStreamId>(-1); @@ -379,8 +382,8 @@ return "READING_COMMON_HEADER"; case SPDY_CONTROL_FRAME_PAYLOAD: return "CONTROL_FRAME_PAYLOAD"; - case SPDY_READ_PADDING_LENGTH: - return "SPDY_READ_PADDING_LENGTH"; + case SPDY_READ_DATA_FRAME_PADDING_LENGTH: + return "SPDY_READ_DATA_FRAME_PADDING_LENGTH"; case SPDY_CONSUME_PADDING: return "SPDY_CONSUME_PADDING"; case SPDY_IGNORE_REMAINING_PAYLOAD: @@ -601,8 +604,8 @@ break; } - case SPDY_READ_PADDING_LENGTH: { - size_t bytes_read = ProcessFramePaddingLength(data, len); + case SPDY_READ_DATA_FRAME_PADDING_LENGTH: { + size_t bytes_read = ProcessDataFramePaddingLength(data, len); len -= bytes_read; data += bytes_read; break; @@ -809,7 +812,7 @@ remaining_data_length_, current_frame_flags_ & DATA_FLAG_FIN); if (remaining_data_length_ > 0) { - CHANGE_STATE(SPDY_READ_PADDING_LENGTH); + CHANGE_STATE(SPDY_READ_DATA_FRAME_PADDING_LENGTH); } else { // Empty data frame. if (current_frame_flags_ & DATA_FLAG_FIN) { @@ -1083,15 +1086,23 @@ break; case HEADERS: frame_size_without_variable_data = GetHeadersMinimumSize(); - if (protocol_version() > SPDY3 && - current_frame_flags_ & HEADERS_FLAG_PRIORITY) { + if (protocol_version() > SPDY3) { + if (current_frame_flags_ & HEADERS_FLAG_PADDED) { + frame_size_without_variable_data += kPadLengthFieldSize; + } + if (current_frame_flags_ & HEADERS_FLAG_PRIORITY) { frame_size_without_variable_data += kPriorityDependencyPayloadSize + kPriorityWeightPayloadSize; + } } break; case PUSH_PROMISE: frame_size_without_variable_data = GetPushPromiseMinimumSize(); + if (protocol_version() > SPDY3 && + current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) { + frame_size_without_variable_data += kPadLengthFieldSize; + } break; case CONTINUATION: frame_size_without_variable_data = GetContinuationMinimumSize(); @@ -1457,6 +1468,14 @@ expect_continuation_ = current_frame_stream_id_; end_stream_when_done_ = current_frame_flags_ & CONTROL_FLAG_FIN; } + if (protocol_version() > SPDY3 && + current_frame_flags_ & HEADERS_FLAG_PADDED) { + uint8 pad_payload_len = 0; + DCHECK_EQ(remaining_padding_payload_length_, 0u); + successful_read = reader.ReadUInt8(&pad_payload_len); + DCHECK(successful_read); + remaining_padding_payload_length_ = pad_payload_len; + } const bool has_priority = (current_frame_flags_ & HEADERS_FLAG_PRIORITY) != 0; uint32 priority = 0; @@ -1502,7 +1521,7 @@ expect_continuation_ == 0); } } - CHANGE_STATE(SPDY_READ_PADDING_LENGTH); + CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); break; case PUSH_PROMISE: { @@ -1511,6 +1530,17 @@ set_error(SPDY_INVALID_CONTROL_FRAME); break; } + bool successful_read = true; + if (protocol_version() > SPDY3 && + current_frame_flags_ & PUSH_PROMISE_FLAG_PADDED) { + DCHECK_EQ(remaining_padding_payload_length_, 0u); + uint8 pad_payload_len = 0; + successful_read = reader.ReadUInt8(&pad_payload_len); + DCHECK(successful_read); + remaining_padding_payload_length_ = pad_payload_len; + } + } + { SpdyStreamId promised_stream_id = kInvalidStream; bool successful_read = reader.ReadUInt31(&promised_stream_id); DCHECK(successful_read); @@ -1533,7 +1563,7 @@ (current_frame_flags_ & PUSH_PROMISE_FLAG_END_PUSH_PROMISE) != 0); } - CHANGE_STATE(SPDY_READ_PADDING_LENGTH); + CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); break; case CONTINUATION: { @@ -2120,11 +2150,10 @@ return processed_bytes; } -// TODO(raullenchai): ProcessFramePaddingLength should be able to deal with -// HEADERS_FLAG_PADDED and PUSH_PROMISE_FLAG_PADDED as well (see b/15777051). -size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) { - DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_); - DCHECK_EQ(remaining_padding_payload_length_, 0u); +size_t SpdyFramer::ProcessDataFramePaddingLength(const char* data, size_t len) { + DCHECK_EQ(SPDY_READ_DATA_FRAME_PADDING_LENGTH, state_); + DCHECK_EQ(0u, remaining_padding_payload_length_); + DCHECK_EQ(DATA, current_frame_type_); size_t original_len = len; if (current_frame_flags_ & DATA_FLAG_PADDED) { @@ -2149,16 +2178,7 @@ set_error(SPDY_INVALID_DATA_FRAME_FLAGS); return 0; } - if (current_frame_type_ == DATA) { - CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); - } else { - DCHECK(current_frame_type_ == HEADERS || - current_frame_type_ == PUSH_PROMISE || - current_frame_type_ == SYN_STREAM || - current_frame_type_ == SYN_REPLY) - << current_frame_type_; - CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK); - } + CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME); return original_len - len; } @@ -2606,11 +2626,20 @@ if (headers.has_priority()) { flags |= HEADERS_FLAG_PRIORITY; } + if (headers.padded()) { + flags |= HEADERS_FLAG_PADDED; + } } - // The size of this frame, including variable-length name-value block. + // The size of this frame, including padding (if there is any) + // and variable-length name-value block. size_t size = GetHeadersMinimumSize(); + if (protocol_version() > SPDY3 && headers.padded()) { + size += kPadLengthFieldSize; + size += headers.padding_payload_len(); + } + uint32 priority = headers.priority(); if (headers.has_priority()) { if (priority > GetLowestPriority()) { @@ -2655,6 +2684,11 @@ DCHECK_EQ(GetHeadersMinimumSize(), builder.length()); if (protocol_version() > SPDY3) { + int padding_payload_len = 0; + if (headers.padded()) { + builder.WriteUInt8(headers.padding_payload_len()); + padding_payload_len = headers.padding_payload_len(); + } if (headers.has_priority()) { // TODO(jgraettinger): Plumb priorities and stream dependencies. builder.WriteUInt32(0); // Non-exclusive bit and root stream ID. @@ -2663,7 +2697,8 @@ WritePayloadWithContinuation(&builder, hpack_encoding, headers.stream_id(), - HEADERS); + HEADERS, + padding_payload_len); } else { SerializeNameValueBlock(&builder, headers); } @@ -2717,6 +2752,12 @@ // The size of this frame, including variable-length name-value block. size_t size = GetPushPromiseMinimumSize(); + if (push_promise.padded()) { + flags |= PUSH_PROMISE_FLAG_PADDED; + size += kPadLengthFieldSize; + size += push_promise.padding_payload_len(); + } + string hpack_encoding; if (enable_compression_) { GetHpackEncoder()->EncodeHeaderSet( @@ -2737,13 +2778,24 @@ PUSH_PROMISE, flags, push_promise.stream_id()); - builder.WriteUInt32(push_promise.promised_stream_id()); - DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); + int padding_payload_len = 0; + if (push_promise.padded()) { + builder.WriteUInt8(push_promise.padding_payload_len()); + builder.WriteUInt32(push_promise.promised_stream_id()); + DCHECK_EQ(GetPushPromiseMinimumSize() + kPadLengthFieldSize, + builder.length()); + + padding_payload_len = push_promise.padding_payload_len(); + } else { + builder.WriteUInt32(push_promise.promised_stream_id()); + DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length()); + } WritePayloadWithContinuation(&builder, hpack_encoding, push_promise.stream_id(), - PUSH_PROMISE); + PUSH_PROMISE, + padding_payload_len); if (debug_visitor_) { // SPDY4 uses HPACK for header compression. However, continue to @@ -2923,32 +2975,34 @@ void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder, const string& hpack_encoding, SpdyStreamId stream_id, - SpdyFrameType type) { + SpdyFrameType type, + int padding_payload_len) { const size_t kMaxControlFrameSize = GetHeaderFragmentMaxSize(); - // In addition to the prefix, fixed_field_size includes the size of - // any fields that come before the variable-length name/value block. - size_t fixed_field_size = 0; uint8 end_flag = 0; uint8 flags = 0; if (type == HEADERS) { - fixed_field_size = GetHeadersMinimumSize(); end_flag = HEADERS_FLAG_END_HEADERS; } else if (type == PUSH_PROMISE) { - fixed_field_size = GetPushPromiseMinimumSize(); end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; } else { DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type " << FrameTypeToString(type); } - // Write as much of the payload as possible into the initial frame. - size_t bytes_remaining = hpack_encoding.size() - - std::min(hpack_encoding.size(), - kMaxControlFrameSize - fixed_field_size); + // Write all the padding payload and as much of the data payload as possible + // into the initial frame. + size_t bytes_remaining = 0; + bytes_remaining = hpack_encoding.size() - + std::min(hpack_encoding.size(), + kMaxControlFrameSize - builder->length() - + padding_payload_len); builder->WriteBytes(&hpack_encoding[0], hpack_encoding.size() - bytes_remaining); - + if (padding_payload_len > 0) { + string padding = string(padding_payload_len, 0); + builder->WriteBytes(padding.data(), padding.length()); + } if (bytes_remaining > 0) { builder->OverwriteLength(*this, kMaxControlFrameSize - GetControlFrameHeaderSize());
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 42711c2..99db36a 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h
@@ -336,7 +336,7 @@ SPDY_AUTO_RESET, SPDY_READING_COMMON_HEADER, SPDY_CONTROL_FRAME_PAYLOAD, - SPDY_READ_PADDING_LENGTH, + SPDY_READ_DATA_FRAME_PADDING_LENGTH, SPDY_CONSUME_PADDING, SPDY_IGNORE_REMAINING_PAYLOAD, SPDY_FORWARD_STREAM_FRAME, @@ -601,6 +601,8 @@ UnclosedStreamDataCompressorsOneByteAtATime); FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UncompressLargerThanFrameBufferInitialSize); + FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, + CreatePushPromiseThenContinuationUncompressed); FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ReadLargeSettingsFrame); FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ReadLargeSettingsFrameInSmallChunks); @@ -634,7 +636,7 @@ size_t ProcessControlFrameHeaderBlock(const char* data, size_t len, bool is_hpack_header_block); - size_t ProcessFramePaddingLength(const char* data, size_t len); + size_t ProcessDataFramePaddingLength(const char* data, size_t len); size_t ProcessFramePadding(const char* data, size_t len); size_t ProcessDataFramePayload(const char* data, size_t len); size_t ProcessGoAwayFramePayload(const char* data, size_t len); @@ -672,7 +674,8 @@ void WritePayloadWithContinuation(SpdyFrameBuilder* builder, const std::string& hpack_encoding, SpdyStreamId stream_id, - SpdyFrameType type); + SpdyFrameType type, + int padding_payload_len); private: // Deliver the given control frame's uncompressed headers block to the
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index 35b8a46..84ea0d7 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc
@@ -2805,6 +2805,35 @@ CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); } } + + { + const char kDescription[] = + "HEADERS frame with a 0-length header name, FIN, max stream ID, padded"; + + const unsigned char kV4FrameData[] = { + 0x00, 0x00, 0x15, 0x01, // Headers + 0x0d, 0x7f, 0xff, 0xff, // FIN | END_HEADERS | PADDED, Stream + // 0x7fffffff + 0xff, 0x05, 0x00, 0x00, // Pad length field + 0x03, 0x66, 0x6f, 0x6f, // .foo + 0x00, 0x03, 0x66, 0x6f, // @.fo + 0x6f, 0x03, 0x62, 0x61, // o.ba + 0x72, // r + // Padding payload + 0x00, 0x00, 0x00, 0x00, 0x00, + }; + SpdyHeadersIR headers_ir(0x7fffffff); + headers_ir.set_fin(true); + headers_ir.SetHeader("", "foo"); + headers_ir.SetHeader("foo", "bar"); + headers_ir.set_padding_len(6); + scoped_ptr<SpdyFrame> frame(framer.SerializeHeaders(headers_ir)); + if (IsSpdy2() || IsSpdy3()) { + // Padding is not supported. + } else { + CompareFrame(kDescription, *frame, kV4FrameData, arraysize(kV4FrameData)); + } + } } // TODO(phajdan.jr): Clean up after we no longer need @@ -2982,27 +3011,98 @@ return; } - SpdyFramer framer(spdy_version_); - framer.set_enable_compression(false); - const char kDescription[] = "PUSH_PROMISE frame"; + { + // Test framing PUSH_PROMISE without padding. + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + const char kDescription[] = "PUSH_PROMISE frame without padding"; - const unsigned char kFrameData[] = { - 0x00, 0x00, 0x16, 0x05, 0x04, // PUSH_PROMISE: END_HEADERS - 0x00, 0x00, 0x00, 0x2a, // Stream 42 - 0x00, 0x00, 0x00, 0x39, // Promised stream 57 - 0x00, 0x03, 0x62, 0x61, // @.ba - 0x72, 0x03, 0x66, 0x6f, // r.fo - 0x6f, 0x00, 0x03, 0x66, // o@.f - 0x6f, 0x6f, 0x03, 0x62, // oo.b - 0x61, 0x72, // ar - }; + const unsigned char kFrameData[] = { + 0x00, 0x00, 0x16, 0x05, // PUSH_PROMISE + 0x04, 0x00, 0x00, 0x00, // END_HEADERS + 0x2a, 0x00, 0x00, 0x00, // Stream 42 + 0x39, 0x00, 0x03, 0x62, // Promised stream 57, @.b + 0x61, 0x72, 0x03, 0x66, // ar.f + 0x6f, 0x6f, 0x00, 0x03, // oo@. + 0x66, 0x6f, 0x6f, 0x03, // foo. + 0x62, 0x61, 0x72, // bar + }; - SpdyPushPromiseIR push_promise(42, 57); - push_promise.SetHeader("bar", "foo"); - push_promise.SetHeader("foo", "bar"); - scoped_ptr<SpdySerializedFrame> frame( - framer.SerializePushPromise(push_promise)); - CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + SpdyPushPromiseIR push_promise(42, 57); + push_promise.SetHeader("bar", "foo"); + push_promise.SetHeader("foo", "bar"); + scoped_ptr<SpdySerializedFrame> frame( + framer.SerializePushPromise(push_promise)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + } + + { + // Test framing PUSH_PROMISE with one byte of padding. + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + const char kDescription[] = "PUSH_PROMISE frame with one byte of padding"; + + const unsigned char kFrameData[] = { + 0x00, 0x00, 0x17, 0x05, // PUSH_PROMISE + 0x0c, 0x00, 0x00, 0x00, // END_HEADERS | PADDED + 0x2a, 0x00, 0x00, 0x00, // Stream 42, Pad length field + 0x00, 0x39, 0x00, 0x03, // Promised stream 57 + 0x62, 0x61, 0x72, 0x03, // bar. + 0x66, 0x6f, 0x6f, 0x00, // foo@ + 0x03, 0x66, 0x6f, 0x6f, // .foo + 0x03, 0x62, 0x61, 0x72, // .bar + }; + + SpdyPushPromiseIR push_promise(42, 57); + push_promise.set_padding_len(1); + push_promise.SetHeader("bar", "foo"); + push_promise.SetHeader("foo", "bar"); + scoped_ptr<SpdySerializedFrame> frame( + framer.SerializePushPromise(push_promise)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + } + + { + // Test framing PUSH_PROMISE with 177 bytes of padding. + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + const char kDescription[] = "PUSH_PROMISE frame with 177 bytes of padding"; + + const unsigned char kFrameData[] = { + 0x00, 0x00, 0xc7, 0x05, // PUSH_PROMISE + 0x0c, 0x00, 0x00, 0x00, // END_HEADERS | PADDED + 0x2a, 0xb0, 0x00, 0x00, // Stream 42, Pad length field + 0x00, 0x39, 0x00, 0x03, // Promised stream 57 + 0x62, 0x61, 0x72, 0x03, // bar. + 0x66, 0x6f, 0x6f, 0x00, // foo@ + 0x03, 0x66, 0x6f, 0x6f, // .foo + 0x03, 0x62, 0x61, 0x72, // .bar + // Padding of 176 0x00(s). + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + SpdyPushPromiseIR push_promise(42, 57); + push_promise.set_padding_len(177); + push_promise.SetHeader("bar", "foo"); + push_promise.SetHeader("foo", "bar"); + scoped_ptr<SpdySerializedFrame> frame( + framer.SerializePushPromise(push_promise)); + CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); + } } TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { @@ -3032,6 +3132,107 @@ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); } +TEST_P(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) { + if (spdy_version_ <= SPDY3) { + return; + } + + { + // Test framing in a case such that a PUSH_PROMISE frame, with one byte of + // padding, cannot hold all the data payload, which is overflowed to the + // consecutive CONTINUATION frame. + SpdyFramer framer(spdy_version_); + framer.set_enable_compression(false); + const char kDescription[] = + "PUSH_PROMISE and CONTINUATION frames with one byte of padding"; + + const unsigned char kPartialPushPromiseFrameData[] = { + 0x00, 0x03, 0xf6, 0x05, // PUSH_PROMISE + 0x08, 0x00, 0x00, 0x00, // PADDED + 0x2a, 0x00, 0x00, 0x00, // Stream 42 + 0x00, 0x39, 0x00, 0x03, // Promised stream 57 + 0x78, 0x78, 0x78, 0x7f, // xxx. + 0x80, 0x07, 0x78, 0x78, // ..xx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, // xx + }; + + const unsigned char kContinuationFrameData[] = { + 0x00, 0x00, 0x16, 0x09, // CONTINUATION + 0x04, 0x00, 0x00, 0x00, // END_HEADERS + 0x2a, 0x78, 0x78, 0x78, // Stream 42, xxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, 0x78, 0x78, // xxxx + 0x78, 0x78, + }; + + SpdyPushPromiseIR push_promise(42, 57); + push_promise.set_padding_len(1); + string big_value(framer.GetHeaderFragmentMaxSize(), 'x'); + push_promise.SetHeader("xxx", big_value); + scoped_ptr<SpdySerializedFrame> frame( + framer.SerializePushPromise(push_promise)); + + // The entire frame should look like below: + // Name Length in Byte + // ------------------------------------------- Begin of PUSH_PROMISE frame + // PUSH_PROMISE header 9 + // Pad length field 1 + // Promised stream 4 + // Length field of key 2 + // Content of key 3 + // Length field of value 3 + // Part of big_value 16361 + // ------------------------------------------- Begin of CONTINUATION frame + // CONTINUATION header 9 + // Remaining of big_value 22 + // ------------------------------------------- End + + // Length of everything listed above except big_value. + int len_non_data_payload = 31; + EXPECT_EQ(framer.GetHeaderFragmentMaxSize() + len_non_data_payload, + frame->size()); + + // Partially compare the PUSH_PROMISE frame against the template. + const unsigned char* frame_data = + reinterpret_cast<const unsigned char*>(frame->data()); + CompareCharArraysWithHexError(kDescription, + frame_data, + arraysize(kPartialPushPromiseFrameData), + kPartialPushPromiseFrameData, + arraysize(kPartialPushPromiseFrameData)); + + // Compare the CONTINUATION frame against the template. + frame_data += framer.GetHeaderFragmentMaxSize(); + CompareCharArraysWithHexError(kDescription, + frame_data, + arraysize(kContinuationFrameData), + kContinuationFrameData, + arraysize(kContinuationFrameData)); + } +} + TEST_P(SpdyFramerTest, CreateAltSvc) { if (spdy_version_ <= SPDY3) { return; @@ -3263,6 +3464,7 @@ SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); SpdyHeadersIR headers(1); + headers.set_padding_len(256); // Exact payload length will change with HPACK, but this should be long // enough to cause an overflow. @@ -3291,6 +3493,7 @@ SpdyFramer framer(spdy_version_); framer.set_enable_compression(false); SpdyPushPromiseIR push_promise(1, 2); + push_promise.set_padding_len(256); // Exact payload length will change with HPACK, but this should be long // enough to cause an overflow. @@ -3729,7 +3932,7 @@ CHECK_EQ(framer.GetDataFrameMinimumSize(), framer.ProcessInput(frame->data(), framer.GetDataFrameMinimumSize())); - CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_PADDING_LENGTH); + CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_DATA_FRAME_PADDING_LENGTH); CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR); bytes_consumed += framer.GetDataFrameMinimumSize(); @@ -3966,31 +4169,31 @@ } const unsigned char kInput[] = { - 0x00, 0x00, 0x17, 0x05, 0x08, // PUSH_PROMISE: PADDED - 0x00, 0x00, 0x00, 0x01, // Stream 1 - 0x00, 0x00, 0x00, 0x2A, // Promised stream 42 - 0x02, // Padding of 2. - 0x00, 0x06, 0x63, 0x6f, - 0x6f, 0x6b, 0x69, 0x65, - 0x07, 0x66, 0x6f, 0x6f, - 0x3d, 0x62, 0x61, 0x72, - 0x00, 0x00, + 0x00, 0x00, 0x17, 0x05, // PUSH_PROMISE + 0x08, 0x00, 0x00, 0x00, // PADDED + 0x01, 0x02, 0x00, 0x00, // Stream 1, Pad length field + 0x00, 0x2A, 0x00, 0x06, // Promised stream 42 + 0x63, 0x6f, 0x6f, 0x6b, + 0x69, 0x65, 0x07, 0x66, + 0x6f, 0x6f, 0x3d, 0x62, + 0x61, 0x72, 0x00, 0x00, - 0x00, 0x00, 0x14, 0x09, 0x00, // CONTINUATION - 0x00, 0x00, 0x00, 0x01, // Stream 1 - 0x00, 0x06, 0x63, 0x6f, - 0x6f, 0x6b, 0x69, 0x65, - 0x08, 0x62, 0x61, 0x7a, - 0x3d, 0x62, 0x69, 0x6e, - 0x67, 0x00, 0x06, 0x63, - - 0x00, 0x00, 0x12, 0x09, 0x04, // CONTINUATION: END_HEADERS - 0x00, 0x00, 0x00, 0x01, // Stream 1 + 0x00, 0x00, 0x14, 0x09, // CONTINUATION + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x06, 0x63, // Stream 1 0x6f, 0x6f, 0x6b, 0x69, - 0x65, 0x00, 0x00, 0x04, - 0x6e, 0x61, 0x6d, 0x65, - 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, + 0x65, 0x08, 0x62, 0x61, + 0x7a, 0x3d, 0x62, 0x69, + 0x6e, 0x67, 0x00, 0x06, + 0x63, + + 0x00, 0x00, 0x12, 0x09, // CONTINUATION + 0x04, 0x00, 0x00, 0x00, // END_HEADERS + 0x01, 0x6f, 0x6f, 0x6b, // Stream 1 + 0x69, 0x65, 0x00, 0x00, + 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, }; SpdyFramer framer(spdy_version_);
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index c408f0a..3a979b9 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h
@@ -845,9 +845,11 @@ class NET_EXPORT_PRIVATE SpdyHeadersIR : public SpdyFrameWithNameValueBlockIR { public: explicit SpdyHeadersIR(SpdyStreamId stream_id) - : SpdyFrameWithNameValueBlockIR(stream_id), - has_priority_(false), - priority_(0) {} + : SpdyFrameWithNameValueBlockIR(stream_id), + has_priority_(false), + priority_(0), + padded_(false), + padding_payload_len_(0) {} void Visit(SpdyFrameVisitor* visitor) const override; @@ -856,10 +858,24 @@ uint32 priority() const { return priority_; } void set_priority(SpdyPriority priority) { priority_ = priority; } + bool padded() const { return padded_; } + int padding_payload_len() const { return padding_payload_len_; } + void set_padding_len(int padding_len) { + DCHECK_GT(padding_len, 0); + DCHECK_LE(padding_len, kPaddingSizePerFrame); + padded_ = true; + // The pad field takes one octet on the wire. + padding_payload_len_ = padding_len - 1; + } + private: bool has_priority_; // 31-bit priority. uint32 priority_; + + bool padded_; + int padding_payload_len_; + DISALLOW_COPY_AND_ASSIGN(SpdyHeadersIR); }; @@ -901,14 +917,30 @@ public: SpdyPushPromiseIR(SpdyStreamId stream_id, SpdyStreamId promised_stream_id) : SpdyFrameWithNameValueBlockIR(stream_id), - promised_stream_id_(promised_stream_id) {} + promised_stream_id_(promised_stream_id), + padded_(false), + padding_payload_len_(0) {} SpdyStreamId promised_stream_id() const { return promised_stream_id_; } void set_promised_stream_id(SpdyStreamId id) { promised_stream_id_ = id; } void Visit(SpdyFrameVisitor* visitor) const override; + bool padded() const { return padded_; } + int padding_payload_len() const { return padding_payload_len_; } + void set_padding_len(int padding_len) { + DCHECK_GT(padding_len, 0); + DCHECK_LE(padding_len, kPaddingSizePerFrame); + padded_ = true; + // The pad field takes one octet on the wire. + padding_payload_len_ = padding_len - 1; + } + private: SpdyStreamId promised_stream_id_; + + bool padded_; + int padding_payload_len_; + DISALLOW_COPY_AND_ASSIGN(SpdyPushPromiseIR); };
diff --git a/net/ssl/default_channel_id_store.cc b/net/ssl/default_channel_id_store.cc index b71d7b3..60e06ed 100644 --- a/net/ssl/default_channel_id_store.cc +++ b/net/ssl/default_channel_id_store.cc
@@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" +#include "base/profiler/scoped_profile.h" #include "net/base/net_errors.h" namespace net { @@ -61,6 +62,11 @@ void DefaultChannelIDStore::GetChannelIDTask::Run( DefaultChannelIDStore* store) { + // TODO(vadimt): Remove ScopedProfile below once crbug.com/425814 is fixed. + tracked_objects::ScopedProfile tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "425814 DefaultChannelIDStore::GetChannelIDTask::Run")); + base::Time expiration_time; std::string private_key_result; std::string cert_result; @@ -112,6 +118,11 @@ void DefaultChannelIDStore::SetChannelIDTask::Run( DefaultChannelIDStore* store) { + // TODO(vadimt): Remove ScopedProfile below once crbug.com/425814 is fixed. + tracked_objects::ScopedProfile tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "425814 DefaultChannelIDStore::SetChannelIDTask::Run")); + store->SyncSetChannelID(server_identifier_, creation_time_, expiration_time_, private_key_, cert_); } @@ -145,6 +156,11 @@ void DefaultChannelIDStore::DeleteChannelIDTask::Run( DefaultChannelIDStore* store) { + // TODO(vadimt): Remove ScopedProfile below once crbug.com/425814 is fixed. + tracked_objects::ScopedProfile tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "425814 DefaultChannelIDStore::DeleteChannelIDTask::Run")); + store->SyncDeleteChannelID(server_identifier_); InvokeCallback(callback_); @@ -183,6 +199,11 @@ void DefaultChannelIDStore::DeleteAllCreatedBetweenTask::Run( DefaultChannelIDStore* store) { + // TODO(vadimt): Remove ScopedProfile below once crbug.com/425814 is fixed. + tracked_objects::ScopedProfile tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "425814 DefaultChannelIDStore::DeleteAllCreatedBetweenTask::Run")); + store->SyncDeleteAllCreatedBetween(delete_begin_, delete_end_); InvokeCallback(callback_); @@ -213,6 +234,11 @@ void DefaultChannelIDStore::GetAllChannelIDsTask::Run( DefaultChannelIDStore* store) { + // TODO(vadimt): Remove ScopedProfile below once crbug.com/425814 is fixed. + tracked_objects::ScopedProfile tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "425814 DefaultChannelIDStore::GetAllChannelIDsTask::Run")); + ChannelIDList cert_list; store->SyncGetAllChannelIDs(&cert_list);
diff --git a/skia/ext/SkDiscardableMemory_chrome.h b/skia/ext/SkDiscardableMemory_chrome.h index 18088c5..50946e6 100644 --- a/skia/ext/SkDiscardableMemory_chrome.h +++ b/skia/ext/SkDiscardableMemory_chrome.h
@@ -13,12 +13,12 @@ // base::DiscardableMemory. class SK_API SkDiscardableMemoryChrome : public SkDiscardableMemory { public: - virtual ~SkDiscardableMemoryChrome(); + ~SkDiscardableMemoryChrome() override; // SkDiscardableMemory: - virtual bool lock() override; - virtual void* data() override; - virtual void unlock() override; + bool lock() override; + void* data() override; + void unlock() override; private: friend class SkDiscardableMemory;
diff --git a/skia/ext/analysis_canvas.h b/skia/ext/analysis_canvas.h index d3d3b80..abedcaf 100644 --- a/skia/ext/analysis_canvas.h +++ b/skia/ext/analysis_canvas.h
@@ -18,7 +18,7 @@ class SK_API AnalysisCanvas : public SkCanvas, public SkDrawPictureCallback { public: AnalysisCanvas(int width, int height); - virtual ~AnalysisCanvas(); + ~AnalysisCanvas() override; // Returns true when a SkColor can be used to represent result. bool GetColorIfSolid(SkColor* color) const; @@ -27,92 +27,93 @@ void SetForceNotTransparent(bool flag); // SkDrawPictureCallback override. - virtual bool abortDrawing() override; + bool abortDrawing() override; // SkCanvas overrides. - virtual void clear(SkColor) override; - virtual void drawPaint(const SkPaint& paint) override; - virtual void drawPoints(PointMode, - size_t count, - const SkPoint pts[], - const SkPaint&) override; - virtual void drawOval(const SkRect&, const SkPaint&) override; - virtual void drawRect(const SkRect&, const SkPaint&) override; - virtual void drawRRect(const SkRRect&, const SkPaint&) override; - virtual void drawPath(const SkPath& path, const SkPaint&) override; - virtual void drawBitmap(const SkBitmap&, - SkScalar left, - SkScalar top, - const SkPaint* paint = NULL) override; - virtual void drawBitmapRectToRect(const SkBitmap&, - const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - DrawBitmapRectFlags flags) override; - virtual void drawBitmapMatrix(const SkBitmap&, - const SkMatrix&, - const SkPaint* paint = NULL) override; - virtual void drawBitmapNine(const SkBitmap& bitmap, - const SkIRect& center, - const SkRect& dst, - const SkPaint* paint = NULL) override; - virtual void drawSprite(const SkBitmap&, int left, int top, - const SkPaint* paint = NULL) override; - virtual void drawVertices(VertexMode, - int vertexCount, - const SkPoint vertices[], - const SkPoint texs[], - const SkColor colors[], - SkXfermode*, - const uint16_t indices[], - int indexCount, - const SkPaint&) override; + void clear(SkColor) override; + void drawPaint(const SkPaint& paint) override; + void drawPoints(PointMode, + size_t count, + const SkPoint pts[], + const SkPaint&) override; + void drawOval(const SkRect&, const SkPaint&) override; + void drawRect(const SkRect&, const SkPaint&) override; + void drawRRect(const SkRRect&, const SkPaint&) override; + void drawPath(const SkPath& path, const SkPaint&) override; + void drawBitmap(const SkBitmap&, + SkScalar left, + SkScalar top, + const SkPaint* paint = NULL) override; + void drawBitmapRectToRect(const SkBitmap&, + const SkRect* src, + const SkRect& dst, + const SkPaint* paint, + DrawBitmapRectFlags flags) override; + void drawBitmapMatrix(const SkBitmap&, + const SkMatrix&, + const SkPaint* paint = NULL) override; + void drawBitmapNine(const SkBitmap& bitmap, + const SkIRect& center, + const SkRect& dst, + const SkPaint* paint = NULL) override; + void drawSprite(const SkBitmap&, + int left, + int top, + const SkPaint* paint = NULL) override; + void drawVertices(VertexMode, + int vertexCount, + const SkPoint vertices[], + const SkPoint texs[], + const SkColor colors[], + SkXfermode*, + const uint16_t indices[], + int indexCount, + const SkPaint&) override; protected: - virtual void willSave() override; - virtual SaveLayerStrategy willSaveLayer(const SkRect*, - const SkPaint*, - SaveFlags) override; - virtual void willRestore() override; + void willSave() override; + SaveLayerStrategy willSaveLayer(const SkRect*, + const SkPaint*, + SaveFlags) override; + void willRestore() override; - virtual void onClipRect(const SkRect& rect, - SkRegion::Op op, - ClipEdgeStyle edge_style) override; - virtual void onClipRRect(const SkRRect& rrect, - SkRegion::Op op, - ClipEdgeStyle edge_style) override; - virtual void onClipPath(const SkPath& path, - SkRegion::Op op, - ClipEdgeStyle edge_style) override; - virtual void onClipRegion(const SkRegion& deviceRgn, - SkRegion::Op op) override; + void onClipRect(const SkRect& rect, + SkRegion::Op op, + ClipEdgeStyle edge_style) override; + void onClipRRect(const SkRRect& rrect, + SkRegion::Op op, + ClipEdgeStyle edge_style) override; + void onClipPath(const SkPath& path, + SkRegion::Op op, + ClipEdgeStyle edge_style) override; + void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override; - virtual void onDrawText(const void* text, - size_t byteLength, - SkScalar x, - SkScalar y, - const SkPaint&) override; - virtual void onDrawPosText(const void* text, - size_t byteLength, - const SkPoint pos[], - const SkPaint&) override; - virtual void onDrawPosTextH(const void* text, - size_t byteLength, - const SkScalar xpos[], - SkScalar constY, - const SkPaint&) override; - virtual void onDrawTextOnPath(const void* text, - size_t byteLength, - const SkPath& path, - const SkMatrix* matrix, - const SkPaint&) override; - virtual void onDrawTextBlob(const SkTextBlob* blob, - SkScalar x, - SkScalar y, - const SkPaint& paint) override; - virtual void onDrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const SkPaint&) override; + void onDrawText(const void* text, + size_t byteLength, + SkScalar x, + SkScalar y, + const SkPaint&) override; + void onDrawPosText(const void* text, + size_t byteLength, + const SkPoint pos[], + const SkPaint&) override; + void onDrawPosTextH(const void* text, + size_t byteLength, + const SkScalar xpos[], + SkScalar constY, + const SkPaint&) override; + void onDrawTextOnPath(const void* text, + size_t byteLength, + const SkPath& path, + const SkMatrix* matrix, + const SkPaint&) override; + void onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + void onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint&) override; void OnComplexClip();
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc index f3f8cc5..64d3f6c 100644 --- a/skia/ext/benchmarking_canvas.cc +++ b/skia/ext/benchmarking_canvas.cc
@@ -29,8 +29,7 @@ setProxy(canvas_.get()); } - virtual ~TimingCanvas() { - } + ~TimingCanvas() override {} double GetTime(size_t index) { TimingsMap::const_iterator timing_info = timings_map_.find(index); @@ -40,149 +39,172 @@ } // SkCanvas overrides. - virtual void willSave() override { + void willSave() override { AutoStamper stamper(this); SkProxyCanvas::willSave(); } - virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, - const SkPaint* paint, - SaveFlags flags) override { + SaveLayerStrategy willSaveLayer(const SkRect* bounds, + const SkPaint* paint, + SaveFlags flags) override { AutoStamper stamper(this); return SkProxyCanvas::willSaveLayer(bounds, paint, flags); } - virtual void willRestore() override { + void willRestore() override { AutoStamper stamper(this); SkProxyCanvas::willRestore(); } - virtual void drawPaint(const SkPaint& paint) override { + void drawPaint(const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawPaint(paint); } - virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[], - const SkPaint& paint) override { + void drawPoints(PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawPoints(mode, count, pts, paint); } - virtual void drawOval(const SkRect& rect, const SkPaint& paint) override { + void drawOval(const SkRect& rect, const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawOval(rect, paint); } - virtual void drawRect(const SkRect& rect, const SkPaint& paint) override { + void drawRect(const SkRect& rect, const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawRect(rect, paint); } - virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint) override { + void drawRRect(const SkRRect& rrect, const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawRRect(rrect, paint); } - virtual void drawPath(const SkPath& path, const SkPaint& paint) override { + void drawPath(const SkPath& path, const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawPath(path, paint); } - virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, - const SkPaint* paint = NULL) override { + void drawBitmap(const SkBitmap& bitmap, + SkScalar left, + SkScalar top, + const SkPaint* paint = NULL) override { AutoStamper stamper(this); SkProxyCanvas::drawBitmap(bitmap, left, top, paint); } - virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - DrawBitmapRectFlags flags) override { + void drawBitmapRectToRect(const SkBitmap& bitmap, + const SkRect* src, + const SkRect& dst, + const SkPaint* paint, + DrawBitmapRectFlags flags) override { AutoStamper stamper(this); SkProxyCanvas::drawBitmapRectToRect(bitmap, src, dst, paint, flags); } - virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, - const SkPaint* paint = NULL) override { + void drawBitmapMatrix(const SkBitmap& bitmap, + const SkMatrix& m, + const SkPaint* paint = NULL) override { AutoStamper stamper(this); SkProxyCanvas::drawBitmapMatrix(bitmap, m, paint); } - virtual void drawSprite(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint = NULL) override { + void drawSprite(const SkBitmap& bitmap, + int left, + int top, + const SkPaint* paint = NULL) override { AutoStamper stamper(this); SkProxyCanvas::drawSprite(bitmap, left, top, paint); } - virtual void drawVertices(VertexMode vmode, int vertexCount, - const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) override { + void drawVertices(VertexMode vmode, + int vertexCount, + const SkPoint vertices[], + const SkPoint texs[], + const SkColor colors[], + SkXfermode* xmode, + const uint16_t indices[], + int indexCount, + const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::drawVertices(vmode, vertexCount, vertices, texs, colors, xmode, indices, indexCount, paint); } - virtual void drawData(const void* data, size_t length) override { + void drawData(const void* data, size_t length) override { AutoStamper stamper(this); SkProxyCanvas::drawData(data, length); } protected: - virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, - SkScalar y, const SkPaint& paint) override { + void onDrawText(const void* text, + size_t byteLength, + SkScalar x, + SkScalar y, + const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::onDrawText(text, byteLength, x, y, paint); } - virtual void onDrawPosText(const void* text, size_t byteLength, - const SkPoint pos[], - const SkPaint& paint) override { + void onDrawPosText(const void* text, + size_t byteLength, + const SkPoint pos[], + const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::onDrawPosText(text, byteLength, pos, paint); } - virtual void onDrawPosTextH(const void* text, size_t byteLength, - const SkScalar xpos[], SkScalar constY, - const SkPaint& paint) override { + void onDrawPosTextH(const void* text, + size_t byteLength, + const SkScalar xpos[], + SkScalar constY, + const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::onDrawPosTextH(text, byteLength, xpos, constY, paint); } - virtual void onDrawTextOnPath(const void* text, size_t byteLength, - const SkPath& path, const SkMatrix* matrix, - const SkPaint& paint) override { + void onDrawTextOnPath(const void* text, + size_t byteLength, + const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) override { AutoStamper stamper(this); SkProxyCanvas::onDrawTextOnPath(text, byteLength, path, matrix, paint); } - virtual void onClipRect(const SkRect& rect, SkRegion::Op op, - ClipEdgeStyle edge_style) override { + void onClipRect(const SkRect& rect, + SkRegion::Op op, + ClipEdgeStyle edge_style) override { AutoStamper stamper(this); SkProxyCanvas::onClipRect(rect, op, edge_style); } - virtual void onClipRRect(const SkRRect& rrect, SkRegion::Op op, - ClipEdgeStyle edge_style) override { + void onClipRRect(const SkRRect& rrect, + SkRegion::Op op, + ClipEdgeStyle edge_style) override { AutoStamper stamper(this); SkProxyCanvas::onClipRRect(rrect, op, edge_style); } - virtual void onClipPath(const SkPath& path, SkRegion::Op op, - ClipEdgeStyle edge_style) override { + void onClipPath(const SkPath& path, + SkRegion::Op op, + ClipEdgeStyle edge_style) override { AutoStamper stamper(this); SkProxyCanvas::onClipPath(path, op, edge_style); } - virtual void onClipRegion(const SkRegion& region, - SkRegion::Op op) override { + void onClipRegion(const SkRegion& region, SkRegion::Op op) override { AutoStamper stamper(this); SkProxyCanvas::onClipRegion(region, op); } - virtual void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) override { + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override { AutoStamper stamper(this); SkProxyCanvas::onDrawPicture(picture, matrix, paint); }
diff --git a/skia/ext/benchmarking_canvas.h b/skia/ext/benchmarking_canvas.h index 7ef8204..50289a5 100644 --- a/skia/ext/benchmarking_canvas.h +++ b/skia/ext/benchmarking_canvas.h
@@ -17,7 +17,7 @@ class SK_API BenchmarkingCanvas : public SkNWayCanvas { public: BenchmarkingCanvas(int width, int height); - virtual ~BenchmarkingCanvas(); + ~BenchmarkingCanvas() override; // Returns the number of draw commands executed on this canvas. size_t CommandCount() const;
diff --git a/skia/ext/bitmap_platform_device_mac.h b/skia/ext/bitmap_platform_device_mac.h index eb142e2..dc78562 100644 --- a/skia/ext/bitmap_platform_device_mac.h +++ b/skia/ext/bitmap_platform_device_mac.h
@@ -48,23 +48,25 @@ int width, int height, bool is_opaque); - virtual ~BitmapPlatformDevice(); + ~BitmapPlatformDevice() override; // PlatformDevice overrides - virtual CGContextRef GetBitmapContext() override; - virtual void DrawToNativeContext(CGContextRef context, int x, int y, - const CGRect* src_rect) override; + CGContextRef GetBitmapContext() override; + void DrawToNativeContext(CGContextRef context, + int x, + int y, + const CGRect* src_rect) override; // SkBaseDevice overrides - virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region, - const SkClipStack&) override; + void setMatrixClip(const SkMatrix& transform, + const SkRegion& region, + const SkClipStack&) override; protected: BitmapPlatformDevice(CGContextRef context, const SkBitmap& bitmap); - virtual SkBaseDevice* onCreateDevice(const SkImageInfo& info, - Usage usage) override; + SkBaseDevice* onCreateDevice(const SkImageInfo& info, Usage usage) override; private: void ReleaseBitmapContext();
diff --git a/skia/ext/event_tracer_impl.cc b/skia/ext/event_tracer_impl.cc index f5e081d..53ddf2b 100644 --- a/skia/ext/event_tracer_impl.cc +++ b/skia/ext/event_tracer_impl.cc
@@ -9,23 +9,20 @@ namespace skia { class SkChromiumEventTracer: public SkEventTracer { - virtual const uint8_t* getCategoryGroupEnabled(const char* name) override; - virtual const char* getCategoryGroupName( - const uint8_t* categoryEnabledFlag) override; - virtual SkEventTracer::Handle - addTraceEvent(char phase, - const uint8_t* categoryEnabledFlag, - const char* name, - uint64_t id, - int32_t numArgs, - const char** argNames, - const uint8_t* argTypes, - const uint64_t* argValues, - uint8_t flags) override; - virtual void - updateTraceEventDuration(const uint8_t* categoryEnabledFlag, - const char *name, - SkEventTracer::Handle handle) override; + const uint8_t* getCategoryGroupEnabled(const char* name) override; + const char* getCategoryGroupName(const uint8_t* categoryEnabledFlag) override; + SkEventTracer::Handle addTraceEvent(char phase, + const uint8_t* categoryEnabledFlag, + const char* name, + uint64_t id, + int32_t numArgs, + const char** argNames, + const uint8_t* argTypes, + const uint64_t* argValues, + uint8_t flags) override; + void updateTraceEventDuration(const uint8_t* categoryEnabledFlag, + const char* name, + SkEventTracer::Handle handle) override; }; const uint8_t*
diff --git a/skia/ext/lazy_pixel_ref.h b/skia/ext/lazy_pixel_ref.h index b3f704d..2335fdf 100644 --- a/skia/ext/lazy_pixel_ref.h +++ b/skia/ext/lazy_pixel_ref.h
@@ -15,7 +15,7 @@ class SK_API LazyPixelRef : public SkPixelRef { public: explicit LazyPixelRef(const SkImageInfo& info); - virtual ~LazyPixelRef(); + ~LazyPixelRef() override; struct PrepareParams { // Clipping rect for this pixel ref.
diff --git a/skia/ext/opacity_draw_filter.h b/skia/ext/opacity_draw_filter.h index 9789cc2..7d11d68 100644 --- a/skia/ext/opacity_draw_filter.h +++ b/skia/ext/opacity_draw_filter.h
@@ -19,8 +19,8 @@ class SK_API OpacityDrawFilter : public SkDrawFilter { public: OpacityDrawFilter(float opacity, bool disable_image_filtering); - virtual ~OpacityDrawFilter(); - virtual bool filter(SkPaint* paint, SkDrawFilter::Type type) override; + ~OpacityDrawFilter() override; + bool filter(SkPaint* paint, SkDrawFilter::Type type) override; private: int alpha_;
diff --git a/skia/ext/pixel_ref_utils.cc b/skia/ext/pixel_ref_utils.cc index 6eb7361..4e85f7a 100644 --- a/skia/ext/pixel_ref_utils.cc +++ b/skia/ext/pixel_ref_utils.cc
@@ -51,8 +51,8 @@ DiscardablePixelRefSet* pixel_ref_set) : SkBitmapDevice(bm), pixel_ref_set_(pixel_ref_set) {} - virtual void clear(SkColor color) override {} - virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) override { + void clear(SkColor color) override {} + void drawPaint(const SkDraw& draw, const SkPaint& paint) override { SkBitmap bitmap; if (GetBitmapFromPaint(paint, &bitmap)) { SkRect clip_rect = SkRect::Make(draw.fRC->getBounds()); @@ -60,11 +60,11 @@ } } - virtual void drawPoints(const SkDraw& draw, - SkCanvas::PointMode mode, - size_t count, - const SkPoint points[], - const SkPaint& paint) override { + void drawPoints(const SkDraw& draw, + SkCanvas::PointMode mode, + size_t count, + const SkPoint points[], + const SkPaint& paint) override { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; @@ -87,9 +87,9 @@ GatherPixelRefDevice::drawRect(draw, bounds, paint); } - virtual void drawRect(const SkDraw& draw, - const SkRect& rect, - const SkPaint& paint) override { + void drawRect(const SkDraw& draw, + const SkRect& rect, + const SkPaint& paint) override { SkBitmap bitmap; if (GetBitmapFromPaint(paint, &bitmap)) { SkRect mapped_rect; @@ -98,21 +98,21 @@ AddBitmap(bitmap, mapped_rect); } } - virtual void drawOval(const SkDraw& draw, - const SkRect& rect, - const SkPaint& paint) override { + void drawOval(const SkDraw& draw, + const SkRect& rect, + const SkPaint& paint) override { GatherPixelRefDevice::drawRect(draw, rect, paint); } - virtual void drawRRect(const SkDraw& draw, - const SkRRect& rect, - const SkPaint& paint) override { + void drawRRect(const SkDraw& draw, + const SkRRect& rect, + const SkPaint& paint) override { GatherPixelRefDevice::drawRect(draw, rect.rect(), paint); } - virtual void drawPath(const SkDraw& draw, - const SkPath& path, - const SkPaint& paint, - const SkMatrix* pre_path_matrix, - bool path_is_mutable) override { + void drawPath(const SkDraw& draw, + const SkPath& path, + const SkPaint& paint, + const SkMatrix* pre_path_matrix, + bool path_is_mutable) override { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; @@ -126,10 +126,10 @@ GatherPixelRefDevice::drawRect(draw, final_rect, paint); } - virtual void drawBitmap(const SkDraw& draw, - const SkBitmap& bitmap, - const SkMatrix& matrix, - const SkPaint& paint) override { + void drawBitmap(const SkDraw& draw, + const SkBitmap& bitmap, + const SkMatrix& matrix, + const SkPaint& paint) override { SkMatrix total_matrix; total_matrix.setConcat(*draw.fMatrix, matrix); @@ -142,22 +142,22 @@ if (GetBitmapFromPaint(paint, &paint_bitmap)) AddBitmap(paint_bitmap, mapped_rect); } - virtual void drawBitmapRect(const SkDraw& draw, - const SkBitmap& bitmap, - const SkRect* src_or_null, - const SkRect& dst, - const SkPaint& paint, - SkCanvas::DrawBitmapRectFlags flags) override { + void drawBitmapRect(const SkDraw& draw, + const SkBitmap& bitmap, + const SkRect* src_or_null, + const SkRect& dst, + const SkPaint& paint, + SkCanvas::DrawBitmapRectFlags flags) override { SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height()); SkMatrix matrix; matrix.setRectToRect(bitmap_rect, dst, SkMatrix::kFill_ScaleToFit); GatherPixelRefDevice::drawBitmap(draw, bitmap, matrix, paint); } - virtual void drawSprite(const SkDraw& draw, - const SkBitmap& bitmap, - int x, - int y, - const SkPaint& paint) override { + void drawSprite(const SkDraw& draw, + const SkBitmap& bitmap, + int x, + int y, + const SkPaint& paint) override { // Sprites aren't affected by current matrix, so we can't reuse drawRect. SkMatrix matrix; matrix.setTranslate(x, y); @@ -171,12 +171,12 @@ if (GetBitmapFromPaint(paint, &paint_bitmap)) AddBitmap(paint_bitmap, mapped_rect); } - virtual void drawText(const SkDraw& draw, - const void* text, - size_t len, - SkScalar x, - SkScalar y, - const SkPaint& paint) override { + void drawText(const SkDraw& draw, + const void* text, + size_t len, + SkScalar x, + SkScalar y, + const SkPaint& paint) override { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; @@ -218,13 +218,13 @@ GatherPixelRefDevice::drawRect(draw, bounds, paint); } - virtual void drawPosText(const SkDraw& draw, - const void* text, - size_t len, - const SkScalar pos[], - int scalars_per_pos, - const SkPoint& offset, - const SkPaint& paint) override { + void drawPosText(const SkDraw& draw, + const void* text, + size_t len, + const SkScalar pos[], + int scalars_per_pos, + const SkPoint& offset, + const SkPaint& paint) override { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; @@ -263,12 +263,12 @@ GatherPixelRefDevice::drawRect(draw, bounds, paint); } - virtual void drawTextOnPath(const SkDraw& draw, - const void* text, - size_t len, - const SkPath& path, - const SkMatrix* matrix, - const SkPaint& paint) override { + void drawTextOnPath(const SkDraw& draw, + const void* text, + size_t len, + const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) override { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; @@ -286,39 +286,39 @@ GatherPixelRefDevice::drawRect(draw, bounds, paint); } - virtual void drawVertices(const SkDraw& draw, - SkCanvas::VertexMode, - int vertex_count, - const SkPoint verts[], - const SkPoint texs[], - const SkColor colors[], - SkXfermode* xmode, - const uint16_t indices[], - int index_count, - const SkPaint& paint) override { + void drawVertices(const SkDraw& draw, + SkCanvas::VertexMode, + int vertex_count, + const SkPoint verts[], + const SkPoint texs[], + const SkColor colors[], + SkXfermode* xmode, + const uint16_t indices[], + int index_count, + const SkPaint& paint) override { GatherPixelRefDevice::drawPoints( draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint); } - virtual void drawDevice(const SkDraw&, - SkBaseDevice*, - int x, - int y, - const SkPaint&) override {} + void drawDevice(const SkDraw&, + SkBaseDevice*, + int x, + int y, + const SkPaint&) override {} protected: - virtual bool onReadPixels(const SkImageInfo& info, - void* pixels, - size_t rowBytes, - int x, - int y) override { + bool onReadPixels(const SkImageInfo& info, + void* pixels, + size_t rowBytes, + int x, + int y) override { return false; } - virtual bool onWritePixels(const SkImageInfo& info, - const void* pixels, - size_t rowBytes, - int x, - int y) override { + bool onWritePixels(const SkImageInfo& info, + const void* pixels, + size_t rowBytes, + int x, + int y) override { return false; }
diff --git a/skia/ext/pixel_ref_utils_unittest.cc b/skia/ext/pixel_ref_utils_unittest.cc index aef5766..656ef5c 100644 --- a/skia/ext/pixel_ref_utils_unittest.cc +++ b/skia/ext/pixel_ref_utils_unittest.cc
@@ -34,20 +34,18 @@ CreateBitmap(gfx::Size(50, 50), "discardable", &bitmap_); } - virtual SkShader::BitmapType asABitmap(SkBitmap* bitmap, - SkMatrix* matrix, - TileMode xy[2]) const override { + SkShader::BitmapType asABitmap(SkBitmap* bitmap, + SkMatrix* matrix, + TileMode xy[2]) const override { if (bitmap) *bitmap = bitmap_; return SkShader::kDefault_BitmapType; } // not indended to return an actual context. Just need to supply this. - virtual size_t contextSize() const override { - return sizeof(SkShader::Context); - } + size_t contextSize() const override { return sizeof(SkShader::Context); } - virtual void flatten(SkWriteBuffer&) const override {} + void flatten(SkWriteBuffer&) const override {} SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(TestDiscardableShader);
diff --git a/skia/ext/vector_platform_device_skia.h b/skia/ext/vector_platform_device_skia.h index fc88904..528eac9 100644 --- a/skia/ext/vector_platform_device_skia.h +++ b/skia/ext/vector_platform_device_skia.h
@@ -23,24 +23,24 @@ SK_API VectorPlatformDeviceSkia(const SkISize& pageSize, const SkISize& contentSize, const SkMatrix& initialTransform); - virtual ~VectorPlatformDeviceSkia(); + ~VectorPlatformDeviceSkia() override; // PlatformDevice methods. - virtual bool SupportsPlatformPaint() override; + bool SupportsPlatformPaint() override; - virtual PlatformSurface BeginPlatformPaint() override; - virtual void EndPlatformPaint() 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) - virtual void DrawToNativeContext(CGContext* context, - int x, - int y, - const CGRect* src_rect) override; - virtual CGContextRef GetBitmapContext() override; + 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,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index a0187b9..a184a12 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -42,15 +42,12 @@ "browser_tests", "content_browsertests", "mojo_apps_js_unittests", - "mojo_clipboard_unittests", "mojo_common_unittests", "mojo_js_unittests", "mojo_public_bindings_unittests", "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_surfaces_lib_unittests", "mojo_system_unittests", "nacl_loader_unittests" @@ -99,15 +96,12 @@ "browser_tests", "content_browsertests", "mojo_apps_js_unittests", - "mojo_clipboard_unittests", "mojo_common_unittests", "mojo_js_unittests", "mojo_public_bindings_unittests", "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_surfaces_lib_unittests", "mojo_system_unittests", "nacl_loader_unittests" @@ -156,15 +150,12 @@ "browser_tests", "content_browsertests", "mojo_apps_js_unittests", - "mojo_clipboard_unittests", "mojo_common_unittests", "mojo_js_unittests", "mojo_public_bindings_unittests", "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_surfaces_lib_unittests", "mojo_system_unittests", "nacl_loader_unittests" @@ -213,15 +204,12 @@ "browser_tests", "content_browsertests", "mojo_apps_js_unittests", - "mojo_clipboard_unittests", "mojo_common_unittests", "mojo_js_unittests", "mojo_public_bindings_unittests", "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_surfaces_lib_unittests", "mojo_system_unittests", "nacl_loader_unittests" @@ -259,15 +247,12 @@ "media_unittests", "message_center_unittests", "mojo_apps_js_unittests", - "mojo_clipboard_unittests", "mojo_common_unittests", "mojo_js_unittests", "mojo_public_bindings_unittests", "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_system_unittests", "net_unittests", "ppapi_unittests", @@ -341,7 +326,6 @@ "ipc_tests", "jingle_unittests", "media_unittests", - "mojo_application_manager_unittests", "mojo_apps_js_unittests", "mojo_common_unittests", "mojo_js_unittests", @@ -349,7 +333,6 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_shell_tests", "mojo_system_unittests", "nacl_loader_unittests", "net_unittests",
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json index 9f814d3..ecce684 100644 --- a/testing/buildbot/chromium.linux.json +++ b/testing/buildbot/chromium.linux.json
@@ -57,7 +57,6 @@ "ipc_tests", "jingle_unittests", "media_unittests", - "mojo_application_manager_unittests", "mojo_apps_js_unittests", "mojo_common_unittests", "mojo_js_unittests", @@ -65,9 +64,7 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_shell_tests", "mojo_system_unittests", - "mojo_view_manager_unittests", "nacl_loader_unittests", { "test": "net_unittests", @@ -157,7 +154,6 @@ "ipc_tests", "jingle_unittests", "media_unittests", - "mojo_application_manager_unittests", "mojo_apps_js_unittests", "mojo_common_unittests", "mojo_js_unittests", @@ -165,9 +161,7 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_shell_tests", "mojo_system_unittests", - "mojo_view_manager_unittests", "nacl_loader_unittests", { "test": "net_unittests", @@ -258,7 +252,6 @@ "ipc_tests", "jingle_unittests", "media_unittests", - "mojo_application_manager_unittests", "mojo_apps_js_unittests", "mojo_common_unittests", "mojo_js_unittests", @@ -266,9 +259,7 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_shell_tests", "mojo_system_unittests", - "mojo_view_manager_unittests", "nacl_loader_unittests", { "test": "net_unittests", @@ -317,7 +308,6 @@ "google_apis_unittests", "ipc_mojo_unittests", "ipc_tests", - "mojo_application_manager_unittests", "mojo_apps_js_unittests", "mojo_common_unittests", "mojo_js_unittests", @@ -325,9 +315,7 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_shell_tests", "mojo_system_unittests", - "mojo_view_manager_unittests", "nacl_loader_unittests", "sandbox_linux_unittests", "sql_unittests",
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json index ef5867d..018a2e3 100644 --- a/testing/buildbot/chromium.mac.json +++ b/testing/buildbot/chromium.mac.json
@@ -56,8 +56,6 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_system_unittests", { "test": "net_unittests", @@ -144,8 +142,6 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_system_unittests", { "test": "net_unittests", @@ -232,8 +228,6 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_system_unittests", { "test": "net_unittests", @@ -321,8 +315,6 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_application_manager_unittests", - "mojo_shell_tests", "mojo_system_unittests", { "test": "net_unittests",
diff --git a/testing/buildbot/chromium.memory.fyi.json b/testing/buildbot/chromium.memory.fyi.json index c2e560e..3f9acc0 100644 --- a/testing/buildbot/chromium.memory.fyi.json +++ b/testing/buildbot/chromium.memory.fyi.json
@@ -28,7 +28,6 @@ "ipc_tests", "jingle_unittests", "media_unittests", - "mojo_application_manager_unittests", "mojo_apps_js_unittests", "mojo_common_unittests", "mojo_js_unittests", @@ -36,7 +35,6 @@ "mojo_public_environment_unittests", "mojo_public_system_unittests", "mojo_public_utility_unittests", - "mojo_shell_tests", "mojo_system_unittests", "nacl_loader_unittests", "net_unittests",
diff --git a/third_party/android_testrunner/LICENSE b/third_party/android_testrunner/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/android_testrunner/LICENSE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
diff --git a/third_party/android_testrunner/OWNERS b/third_party/android_testrunner/OWNERS new file mode 100644 index 0000000..9281d8a --- /dev/null +++ b/third_party/android_testrunner/OWNERS
@@ -0,0 +1,3 @@ +craigdh@chromium.org +frankf@chromium.org +jbudorick@chromium.org
diff --git a/third_party/android_testrunner/README.chromium b/third_party/android_testrunner/README.chromium new file mode 100644 index 0000000..7d382a1 --- /dev/null +++ b/third_party/android_testrunner/README.chromium
@@ -0,0 +1,34 @@ +Name: Android Test runner script +URL: http://source.android.com +Version: 3.1.4 +License: Apache Version 2.0 +License File: NOT_SHIPPED +Security Critical: no + +Description: +This package is the scripts used to run the unit test for Android and from +Android Gingerbread. + +Local Modifications: +1. Added |silent_log| argument to |StartInstrumentation| so that output can be + suppressed. +2. Changed error message handling in |StartInstrumentation| from |shortMsg| to + |longMsg| to provide more information when debugging. +3. Applied the patch file patch.diff to fix a race condition and subproccess + bugs in run_command.py. +4. Fixed a bug where wait_time wasn't properly respected in + _WaitForShellCommandContents. + +Here is the detail steps +1. Checkout Android source code + +$ repo init -u git://android.git.kernel.org/platform/manifest.git -b gingerbread +$ repo sync +$ cd development +$ git reset --hard 76f63551d36b1de63c63f357e5f0646ed8c306bb + +2. Copy the related files from + <android_gingerbread_tree>/development/testrunner/ + +More information can be found in +http://source.android.com/source/downloading.html
diff --git a/third_party/android_testrunner/adb_interface.py b/third_party/android_testrunner/adb_interface.py new file mode 100644 index 0000000..93e1963 --- /dev/null +++ b/third_party/android_testrunner/adb_interface.py
@@ -0,0 +1,514 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Provides an interface to communicate with the device via the adb command. + +Assumes adb binary is currently on system path. +""" +# Python imports +import os +import string +import time + +# local imports +import am_instrument_parser +import errors +import logger +import run_command + + +class AdbInterface: + """Helper class for communicating with Android device via adb.""" + + # argument to pass to adb, to direct command to specific device + _target_arg = "" + + DEVICE_TRACE_DIR = "/data/test_results/" + + def SetEmulatorTarget(self): + """Direct all future commands to the only running emulator.""" + self._target_arg = "-e" + + def SetDeviceTarget(self): + """Direct all future commands to the only connected USB device.""" + self._target_arg = "-d" + + def SetTargetSerial(self, serial): + """Direct all future commands to Android target with the given serial.""" + self._target_arg = "-s %s" % serial + + def SendCommand(self, command_string, timeout_time=20, retry_count=3): + """Send a command via adb. + + Args: + command_string: adb command to run + timeout_time: number of seconds to wait for command to respond before + retrying + retry_count: number of times to retry command before raising + WaitForResponseTimedOutError + Returns: + string output of command + + Raises: + WaitForResponseTimedOutError if device does not respond to command within time + """ + adb_cmd = "adb %s %s" % (self._target_arg, command_string) + logger.SilentLog("about to run %s" % adb_cmd) + return run_command.RunCommand(adb_cmd, timeout_time=timeout_time, + retry_count=retry_count) + + def SendShellCommand(self, cmd, timeout_time=20, retry_count=3): + """Send a adb shell command. + + Args: + cmd: adb shell command to run + timeout_time: number of seconds to wait for command to respond before + retrying + retry_count: number of times to retry command before raising + WaitForResponseTimedOutError + + Returns: + string output of command + + Raises: + WaitForResponseTimedOutError: if device does not respond to command + """ + return self.SendCommand("shell %s" % cmd, timeout_time=timeout_time, + retry_count=retry_count) + + def BugReport(self, path): + """Dumps adb bugreport to the file specified by the path. + + Args: + path: Path of the file where adb bugreport is dumped to. + """ + bug_output = self.SendShellCommand("bugreport", timeout_time=60) + bugreport_file = open(path, "w") + bugreport_file.write(bug_output) + bugreport_file.close() + + def Push(self, src, dest): + """Pushes the file src onto the device at dest. + + Args: + src: file path of host file to push + dest: destination absolute file path on device + """ + self.SendCommand("push %s %s" % (src, dest), timeout_time=60) + + def Pull(self, src, dest): + """Pulls the file src on the device onto dest on the host. + + Args: + src: absolute file path of file on device to pull + dest: destination file path on host + + Returns: + True if success and False otherwise. + """ + # Create the base dir if it doesn't exist already + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)) + + if self.DoesFileExist(src): + self.SendCommand("pull %s %s" % (src, dest), timeout_time=60) + return True + else: + logger.Log("ADB Pull Failed: Source file %s does not exist." % src) + return False + + def DoesFileExist(self, src): + """Checks if the given path exists on device target. + + Args: + src: file path to be checked. + + Returns: + True if file exists + """ + + output = self.SendShellCommand("ls %s" % src) + error = "No such file or directory" + + if error in output: + return False + return True + + def EnableAdbRoot(self): + """Enable adb root on device.""" + output = self.SendCommand("root") + if "adbd is already running as root" in output: + return True + elif "restarting adbd as root" in output: + # device will disappear from adb, wait for it to come back + self.SendCommand("wait-for-device") + return True + else: + logger.Log("Unrecognized output from adb root: %s" % output) + return False + + def StartInstrumentationForPackage( + self, package_name, runner_name, timeout_time=60*10, + no_window_animation=False, instrumentation_args={}): + """Run instrumentation test for given package and runner. + + Equivalent to StartInstrumentation, except instrumentation path is + separated into its package and runner components. + """ + instrumentation_path = "%s/%s" % (package_name, runner_name) + return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time, + no_window_animation=no_window_animation, + instrumentation_args=instrumentation_args) + + def StartInstrumentation( + self, instrumentation_path, timeout_time=60*10, no_window_animation=False, + profile=False, instrumentation_args={}, silent_log=False): + + """Runs an instrumentation class on the target. + + Returns a dictionary containing the key value pairs from the + instrumentations result bundle and a list of TestResults. Also handles the + interpreting of error output from the device and raises the necessary + exceptions. + + Args: + instrumentation_path: string. It should be the fully classified package + name, and instrumentation test runner, separated by "/" + e.g. com.android.globaltimelaunch/.GlobalTimeLaunch + timeout_time: Timeout value for the am command. + no_window_animation: boolean, Whether you want window animations enabled + or disabled + profile: If True, profiling will be turned on for the instrumentation. + instrumentation_args: Dictionary of key value bundle arguments to pass to + instrumentation. + silent_log: If True, the invocation of the instrumentation test runner + will not be logged. + + Returns: + (test_results, inst_finished_bundle) + + test_results: a list of TestResults + inst_finished_bundle (dict): Key/value pairs contained in the bundle that + is passed into ActivityManager.finishInstrumentation(). Included in this + bundle is the return code of the Instrumentation process, any error + codes reported by the activity manager, and any results explicitly added + by the instrumentation code. + + Raises: + WaitForResponseTimedOutError: if timeout occurred while waiting for + response to adb instrument command + DeviceUnresponsiveError: if device system process is not responding + InstrumentationError: if instrumentation failed to run + """ + + command_string = self._BuildInstrumentationCommandPath( + instrumentation_path, no_window_animation=no_window_animation, + profile=profile, raw_mode=True, + instrumentation_args=instrumentation_args) + if silent_log: + logger.SilentLog(command_string) + else: + logger.Log(command_string) + (test_results, inst_finished_bundle) = ( + am_instrument_parser.ParseAmInstrumentOutput( + self.SendShellCommand(command_string, timeout_time=timeout_time, + retry_count=2))) + if "code" not in inst_finished_bundle: + logger.Log('No code available. inst_finished_bundle contains: %s ' + % inst_finished_bundle) + raise errors.InstrumentationError("no test results... device setup " + "correctly?") + + if inst_finished_bundle["code"] == "0": + long_msg_result = "no error message" + if "longMsg" in inst_finished_bundle: + long_msg_result = inst_finished_bundle["longMsg"] + logger.Log("Error! Test run failed: %s" % long_msg_result) + raise errors.InstrumentationError(long_msg_result) + + if "INSTRUMENTATION_ABORTED" in inst_finished_bundle: + logger.Log("INSTRUMENTATION ABORTED!") + raise errors.DeviceUnresponsiveError + + return (test_results, inst_finished_bundle) + + def StartInstrumentationNoResults( + self, package_name, runner_name, no_window_animation=False, + raw_mode=False, instrumentation_args={}): + """Runs instrumentation and dumps output to stdout. + + Equivalent to StartInstrumentation, but will dump instrumentation + 'normal' output to stdout, instead of parsing return results. Command will + never timeout. + """ + adb_command_string = self.PreviewInstrumentationCommand( + package_name, runner_name, no_window_animation=no_window_animation, + raw_mode=raw_mode, instrumentation_args=instrumentation_args) + logger.Log(adb_command_string) + run_command.RunCommand(adb_command_string, return_output=False) + + def PreviewInstrumentationCommand( + self, package_name, runner_name, no_window_animation=False, + raw_mode=False, instrumentation_args={}): + """Returns a string of adb command that will be executed.""" + inst_command_string = self._BuildInstrumentationCommand( + package_name, runner_name, no_window_animation=no_window_animation, + raw_mode=raw_mode, instrumentation_args=instrumentation_args) + command_string = "adb %s shell %s" % (self._target_arg, inst_command_string) + return command_string + + def _BuildInstrumentationCommand( + self, package, runner_name, no_window_animation=False, profile=False, + raw_mode=True, instrumentation_args={}): + instrumentation_path = "%s/%s" % (package, runner_name) + + return self._BuildInstrumentationCommandPath( + instrumentation_path, no_window_animation=no_window_animation, + profile=profile, raw_mode=raw_mode, + instrumentation_args=instrumentation_args) + + def _BuildInstrumentationCommandPath( + self, instrumentation_path, no_window_animation=False, profile=False, + raw_mode=True, instrumentation_args={}): + command_string = "am instrument" + if no_window_animation: + command_string += " --no_window_animation" + if profile: + self._CreateTraceDir() + command_string += ( + " -p %s/%s.dmtrace" % + (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1])) + + for key, value in instrumentation_args.items(): + command_string += " -e %s '%s'" % (key, value) + if raw_mode: + command_string += " -r" + command_string += " -w %s" % instrumentation_path + return command_string + + def _CreateTraceDir(self): + ls_response = self.SendShellCommand("ls /data/trace") + if ls_response.strip("#").strip(string.whitespace) != "": + self.SendShellCommand("create /data/trace", "mkdir /data/trace") + self.SendShellCommand("make /data/trace world writeable", + "chmod 777 /data/trace") + + def WaitForDevicePm(self, wait_time=120): + """Waits for targeted device's package manager to be up. + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and pm still does not + respond. + """ + logger.Log("Waiting for device package manager...") + self.SendCommand("wait-for-device", timeout_time=wait_time, retry_count=0) + # Now the device is there, but may not be running. + # Query the package manager with a basic command + try: + self._WaitForShellCommandContents("pm path android", "package:", + wait_time) + except errors.WaitForResponseTimedOutError: + raise errors.WaitForResponseTimedOutError( + "Package manager did not respond after %s seconds" % wait_time) + + def WaitForInstrumentation(self, package_name, runner_name, wait_time=120): + """Waits for given instrumentation to be present on device + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and instrumentation + still not present. + """ + instrumentation_path = "%s/%s" % (package_name, runner_name) + logger.Log("Waiting for instrumentation to be present") + # Query the package manager + try: + command = "pm list instrumentation | grep %s" % instrumentation_path + self._WaitForShellCommandContents(command, "instrumentation:", wait_time, + raise_abort=False) + except errors.WaitForResponseTimedOutError : + logger.Log( + "Could not find instrumentation %s on device. Does the " + "instrumentation in test's AndroidManifest.xml match definition" + "in test_defs.xml?" % instrumentation_path) + raise + + def WaitForProcess(self, name, wait_time=120): + """Wait until a process is running on the device. + + Args: + name: the process name as it appears in `ps` + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and the process is + still not running + """ + logger.Log("Waiting for process %s" % name) + self.SendCommand("wait-for-device") + self._WaitForShellCommandContents("ps", name, wait_time) + + def WaitForProcessEnd(self, name, wait_time=120): + """Wait until a process is no longer running on the device. + + Args: + name: the process name as it appears in `ps` + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and the process is + still running + """ + logger.Log("Waiting for process %s to end" % name) + self._WaitForShellCommandContents("ps", name, wait_time, invert=True) + + def _WaitForShellCommandContents(self, command, expected, wait_time, + raise_abort=True, invert=False): + """Wait until the response to a command contains a given output. + + Assumes that a only successful execution of "adb shell <command>" contains + the substring expected. Assumes that a device is present. + + Args: + command: adb shell command to execute + expected: the string that should appear to consider the + command successful. + wait_time: time in seconds to wait + raise_abort: if False, retry when executing the command raises an + AbortError, rather than failing. + invert: if True, wait until the command output no longer contains the + expected contents. + + Raises: + WaitForResponseTimedOutError: If wait_time elapses and the command has not + returned an output containing expected yet. + """ + # Query the device with the command + success = False + attempts = 0 + wait_period = 5 + while not success and (attempts*wait_period) < wait_time: + # assume the command will always contain expected in the success case + try: + output = self.SendShellCommand(command, retry_count=1, + timeout_time=wait_time) + if ((not invert and expected in output) + or (invert and expected not in output)): + success = True + except errors.AbortError, e: + if raise_abort: + raise + # ignore otherwise + + if not success: + time.sleep(wait_period) + attempts += 1 + + if not success: + raise errors.WaitForResponseTimedOutError() + + def WaitForBootComplete(self, wait_time=120): + """Waits for targeted device's bootcomplete flag to be set. + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and pm still does not + respond. + """ + logger.Log("Waiting for boot complete...") + self.SendCommand("wait-for-device") + # Now the device is there, but may not be running. + # Query the package manager with a basic command + boot_complete = False + attempts = 0 + wait_period = 5 + while not boot_complete and (attempts*wait_period) < wait_time: + output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1) + output = output.strip() + if output == "1": + boot_complete = True + else: + time.sleep(wait_period) + attempts += 1 + if not boot_complete: + raise errors.WaitForResponseTimedOutError( + "dev.bootcomplete flag was not set after %s seconds" % wait_time) + + def Sync(self, retry_count=3, runtime_restart=False): + """Perform a adb sync. + + Blocks until device package manager is responding. + + Args: + retry_count: number of times to retry sync before failing + runtime_restart: stop runtime during sync and restart afterwards, useful + for syncing system libraries (core, framework etc) + + Raises: + WaitForResponseTimedOutError if package manager does not respond + AbortError if unrecoverable error occurred + """ + output = "" + error = None + if runtime_restart: + self.SendShellCommand("setprop ro.monkey 1", retry_count=retry_count) + # manual rest bootcomplete flag + self.SendShellCommand("setprop dev.bootcomplete 0", + retry_count=retry_count) + self.SendShellCommand("stop", retry_count=retry_count) + + try: + output = self.SendCommand("sync", retry_count=retry_count) + except errors.AbortError, e: + error = e + output = e.msg + if "Read-only file system" in output: + logger.SilentLog(output) + logger.Log("Remounting read-only filesystem") + self.SendCommand("remount") + output = self.SendCommand("sync", retry_count=retry_count) + elif "No space left on device" in output: + logger.SilentLog(output) + logger.Log("Restarting device runtime") + self.SendShellCommand("stop", retry_count=retry_count) + output = self.SendCommand("sync", retry_count=retry_count) + self.SendShellCommand("start", retry_count=retry_count) + elif error is not None: + # exception occurred that cannot be recovered from + raise error + logger.SilentLog(output) + if runtime_restart: + # start runtime and wait till boot complete flag is set + self.SendShellCommand("start", retry_count=retry_count) + self.WaitForBootComplete() + # press the MENU key, this will disable key guard if runtime is started + # with ro.monkey set to 1 + self.SendShellCommand("input keyevent 82", retry_count=retry_count) + else: + self.WaitForDevicePm() + return output + + def GetSerialNumber(self): + """Returns the serial number of the targeted device.""" + return self.SendCommand("get-serialno").strip()
diff --git a/third_party/android_testrunner/am_instrument_parser.py b/third_party/android_testrunner/am_instrument_parser.py new file mode 100644 index 0000000..4554c4d --- /dev/null +++ b/third_party/android_testrunner/am_instrument_parser.py
@@ -0,0 +1,169 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Module that assists in parsing the output of "am instrument" commands run on +the device.""" + +import re +import string + + +def ParseAmInstrumentOutput(result): + """Given the raw output of an "am instrument" command that targets and + InstrumentationTestRunner, return structured data. + + Args: + result (string): Raw output of "am instrument" + + Return + (test_results, inst_finished_bundle) + + test_results (list of am_output_parser.TestResult) + inst_finished_bundle (dict): Key/value pairs contained in the bundle that is + passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return + code of the Instrumentation process, any error codes reported by the + activity manager, and any results explicity added by the instrumentation + code. + """ + + re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P<status_code>-?\d)$') + test_results = [] + inst_finished_bundle = {} + + result_block_string = "" + for line in result.splitlines(): + result_block_string += line + '\n' + + if "INSTRUMENTATION_STATUS_CODE:" in line: + test_result = TestResult(result_block_string) + if test_result.GetStatusCode() == 1: # The test started + pass + elif test_result.GetStatusCode() in [0, -1, -2]: + test_results.append(test_result) + else: + pass + result_block_string = "" + if "INSTRUMENTATION_CODE:" in line: + inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string) + result_block_string = "" + + return (test_results, inst_finished_bundle) + + +def _ParseInstrumentationFinishedBundle(result): + """Given the raw output of "am instrument" returns a dictionary of the + key/value pairs from the bundle passed into + ActivityManager.finishInstrumentation(). + + Args: + result (string): Raw output of "am instrument" + + Return: + inst_finished_bundle (dict): Key/value pairs contained in the bundle that is + passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return + code of the Instrumentation process, any error codes reported by the + activity manager, and any results explicity added by the instrumentation + code. + """ + + re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$') + re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$') + result_dict = {} + key = '' + val = '' + last_tag = '' + + for line in result.split('\n'): + line = line.strip(string.whitespace) + if re_result.match(line): + last_tag = 'INSTRUMENTATION_RESULT' + key = re_result.search(line).group(1).strip(string.whitespace) + if key.startswith('performance.'): + key = key[len('performance.'):] + val = re_result.search(line).group(2).strip(string.whitespace) + try: + result_dict[key] = float(val) + except ValueError: + result_dict[key] = val + except TypeError: + result_dict[key] = val + elif re_code.match(line): + last_tag = 'INSTRUMENTATION_CODE' + key = 'code' + val = re_code.search(line).group(1).strip(string.whitespace) + result_dict[key] = val + elif 'INSTRUMENTATION_ABORTED:' in line: + last_tag = 'INSTRUMENTATION_ABORTED' + key = 'INSTRUMENTATION_ABORTED' + val = '' + result_dict[key] = val + elif last_tag == 'INSTRUMENTATION_RESULT': + result_dict[key] += '\n' + line + + if not result_dict.has_key('code'): + result_dict['code'] = '0' + result_dict['shortMsg'] = "No result returned from instrumentation" + + return result_dict + + +class TestResult(object): + """A class that contains information about a single test result.""" + + def __init__(self, result_block_string): + """ + Args: + result_block_string (string): Is a single "block" of output. A single + "block" would be either a "test started" status report, or a "test + finished" status report. + """ + + self._test_name = None + self._status_code = None + self._failure_reason = None + self._fields_map = {} + + re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: ' + '(?P<status_code>1|0|-1|-2)', result_block_string) + re_fields = re.compile(r'INSTRUMENTATION_STATUS: ' + '(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL) + + for field in re_fields.finditer(result_block_string): + key, value = (field.group('key').strip(), field.group('value').strip()) + if key.startswith('performance.'): + key = key[len('performance.'):] + self._fields_map[key] = value + self._fields_map.setdefault('class') + self._fields_map.setdefault('test') + + self._test_name = '%s:%s' % (self._fields_map['class'], + self._fields_map['test']) + self._status_code = int(re_status_code.group('status_code')) + if 'stack' in self._fields_map: + self._failure_reason = self._fields_map['stack'] + + def GetTestName(self): + return self._test_name + + def GetStatusCode(self): + return self._status_code + + def GetFailureReason(self): + return self._failure_reason + + def GetResultFields(self): + return self._fields_map
diff --git a/third_party/android_testrunner/errors.py b/third_party/android_testrunner/errors.py new file mode 100644 index 0000000..e163dd4 --- /dev/null +++ b/third_party/android_testrunner/errors.py
@@ -0,0 +1,46 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Defines common exception classes for this package.""" + + +class MsgException(Exception): + """Generic exception with an optional string msg.""" + def __init__(self, msg=""): + self.msg = msg + + +class WaitForResponseTimedOutError(Exception): + """We sent a command and had to wait too long for response.""" + + +class DeviceUnresponsiveError(Exception): + """Device is unresponsive to command.""" + + +class InstrumentationError(Exception): + """Failed to run instrumentation.""" + + +class AbortError(MsgException): + """Generic exception that indicates a fatal error has occurred and program + execution should be aborted.""" + + +class ParseError(MsgException): + """Raised when xml data to parse has unrecognized format.""" +
diff --git a/third_party/android_testrunner/logger.py b/third_party/android_testrunner/logger.py new file mode 100644 index 0000000..61463a1 --- /dev/null +++ b/third_party/android_testrunner/logger.py
@@ -0,0 +1,96 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2007, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Simple logging utility. Dumps log messages to stdout, and optionally, to a +log file. + +Init(path) must be called to enable logging to a file +""" + +import datetime + +_LOG_FILE = None +_verbose = False +_log_time = True + +def Init(log_file_path): + """Set the path to the log file""" + global _LOG_FILE + _LOG_FILE = log_file_path + print "Using log file: %s" % _LOG_FILE + +def GetLogFilePath(): + """Returns the path and name of the Log file""" + global _LOG_FILE + return _LOG_FILE + +def Log(new_str): + """Appends new_str to the end of _LOG_FILE and prints it to stdout. + + Args: + # new_str is a string. + new_str: 'some message to log' + """ + msg = _PrependTimeStamp(new_str) + print msg + _WriteLog(msg) + +def _WriteLog(msg): + global _LOG_FILE + if _LOG_FILE is not None: + file_handle = file(_LOG_FILE, 'a') + file_handle.write('\n' + str(msg)) + file_handle.close() + +def _PrependTimeStamp(log_string): + """Returns the log_string prepended with current timestamp """ + global _log_time + if _log_time: + return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"), + log_string) + else: + # timestamp logging disabled + return log_string + +def SilentLog(new_str): + """Silently log new_str. Unless verbose mode is enabled, will log new_str + only to the log file + Args: + # new_str is a string. + new_str: 'some message to log' + """ + global _verbose + msg = _PrependTimeStamp(new_str) + if _verbose: + print msg + _WriteLog(msg) + +def SetVerbose(new_verbose=True): + """ Enable or disable verbose logging""" + global _verbose + _verbose = new_verbose + +def SetTimestampLogging(new_timestamp=True): + """ Enable or disable outputting a timestamp with each log entry""" + global _log_time + _log_time = new_timestamp + +def main(): + pass + +if __name__ == '__main__': + main()
diff --git a/third_party/android_testrunner/patch.diff b/third_party/android_testrunner/patch.diff new file mode 100644 index 0000000..9b067cd --- /dev/null +++ b/third_party/android_testrunner/patch.diff
@@ -0,0 +1,104 @@ +diff --git a/third_party/android_testrunner/run_command.py b/third_party/android_testrunner/run_command.py +index d398daa..6b84156 100644 +--- a/third_party/android_testrunner/run_command.py ++++ b/third_party/android_testrunner/run_command.py +@@ -19,6 +19,7 @@ + import os + import signal + import subprocess ++import tempfile + import threading + import time + +@@ -80,31 +81,36 @@ def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): + """ + start_time = time.time() + so = [] +- pid = [] + global _abort_on_error, error_occurred + error_occurred = False + ++ if return_output: ++ output_dest = tempfile.TemporaryFile(bufsize=0) ++ else: ++ # None means direct to stdout ++ output_dest = None ++ if stdin_input: ++ stdin_dest = subprocess.PIPE ++ else: ++ stdin_dest = None ++ pipe = subprocess.Popen( ++ cmd, ++ executable='/bin/bash', ++ stdin=stdin_dest, ++ stdout=output_dest, ++ stderr=subprocess.STDOUT, ++ shell=True, close_fds=True, ++ preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) ++ + def Run(): + global error_occurred +- if return_output: +- output_dest = subprocess.PIPE +- else: +- # None means direct to stdout +- output_dest = None +- if stdin_input: +- stdin_dest = subprocess.PIPE +- else: +- stdin_dest = None +- pipe = subprocess.Popen( +- cmd, +- executable='/bin/bash', +- stdin=stdin_dest, +- stdout=output_dest, +- stderr=subprocess.STDOUT, +- shell=True) +- pid.append(pipe.pid) + try: +- output = pipe.communicate(input=stdin_input)[0] ++ pipe.communicate(input=stdin_input) ++ output = None ++ if return_output: ++ output_dest.seek(0) ++ output = output_dest.read() ++ output_dest.close() + if output is not None and len(output) > 0: + so.append(output) + except OSError, e: +@@ -119,27 +125,17 @@ def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): + + t = threading.Thread(target=Run) + t.start() +- +- break_loop = False +- while not break_loop: +- if not t.isAlive(): +- break_loop = True +- +- # Check the timeout +- if (not break_loop and timeout_time is not None +- and time.time() > start_time + timeout_time): +- try: +- os.kill(pid[0], signal.SIGKILL) +- except OSError: +- # process already dead. No action required. +- pass +- ++ t.join(timeout_time) ++ if t.isAlive(): ++ try: ++ pipe.kill() ++ except OSError: ++ # Can't kill a dead process. ++ pass ++ finally: + logger.SilentLog("about to raise a timeout for: %s" % cmd) + raise errors.WaitForResponseTimedOutError +- if not break_loop: +- time.sleep(0.1) + +- t.join() + output = "".join(so) + if _abort_on_error and error_occurred: + raise errors.AbortError(msg=output)
diff --git a/third_party/android_testrunner/run_command.py b/third_party/android_testrunner/run_command.py new file mode 100644 index 0000000..6b84156 --- /dev/null +++ b/third_party/android_testrunner/run_command.py
@@ -0,0 +1,192 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2007, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# System imports +import os +import signal +import subprocess +import tempfile +import threading +import time + +# local imports +import errors +import logger + +_abort_on_error = False + +def SetAbortOnError(abort=True): + """Sets behavior of RunCommand to throw AbortError if command process returns + a negative error code""" + global _abort_on_error + _abort_on_error = abort + +def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True, + stdin_input=None): + """Spawn and retry a subprocess to run the given shell command. + + Args: + cmd: shell command to run + timeout_time: time in seconds to wait for command to run before aborting. + retry_count: number of times to retry command + return_output: if True return output of command as string. Otherwise, + direct output of command to stdout. + stdin_input: data to feed to stdin + Returns: + output of command + """ + result = None + while True: + try: + result = RunOnce(cmd, timeout_time=timeout_time, + return_output=return_output, stdin_input=stdin_input) + except errors.WaitForResponseTimedOutError: + if retry_count == 0: + raise + retry_count -= 1 + logger.Log("No response for %s, retrying" % cmd) + else: + # Success + return result + +def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): + """Spawns a subprocess to run the given shell command. + + Args: + cmd: shell command to run + timeout_time: time in seconds to wait for command to run before aborting. + return_output: if True return output of command as string. Otherwise, + direct output of command to stdout. + stdin_input: data to feed to stdin + Returns: + output of command + Raises: + errors.WaitForResponseTimedOutError if command did not complete within + timeout_time seconds. + errors.AbortError is command returned error code and SetAbortOnError is on. + """ + start_time = time.time() + so = [] + global _abort_on_error, error_occurred + error_occurred = False + + if return_output: + output_dest = tempfile.TemporaryFile(bufsize=0) + else: + # None means direct to stdout + output_dest = None + if stdin_input: + stdin_dest = subprocess.PIPE + else: + stdin_dest = None + pipe = subprocess.Popen( + cmd, + executable='/bin/bash', + stdin=stdin_dest, + stdout=output_dest, + stderr=subprocess.STDOUT, + shell=True, close_fds=True, + preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) + + def Run(): + global error_occurred + try: + pipe.communicate(input=stdin_input) + output = None + if return_output: + output_dest.seek(0) + output = output_dest.read() + output_dest.close() + if output is not None and len(output) > 0: + so.append(output) + except OSError, e: + logger.SilentLog("failed to retrieve stdout from: %s" % cmd) + logger.Log(e) + so.append("ERROR") + error_occurred = True + if pipe.returncode: + logger.SilentLog("Error: %s returned %d error code" %(cmd, + pipe.returncode)) + error_occurred = True + + t = threading.Thread(target=Run) + t.start() + t.join(timeout_time) + if t.isAlive(): + try: + pipe.kill() + except OSError: + # Can't kill a dead process. + pass + finally: + logger.SilentLog("about to raise a timeout for: %s" % cmd) + raise errors.WaitForResponseTimedOutError + + output = "".join(so) + if _abort_on_error and error_occurred: + raise errors.AbortError(msg=output) + + return "".join(so) + + +def RunHostCommand(binary, valgrind=False): + """Run a command on the host (opt using valgrind). + + Runs the host binary and returns the exit code. + If successfull, the output (stdout and stderr) are discarded, + but printed in case of error. + The command can be run under valgrind in which case all the + output are always discarded. + + Args: + binary: full path of the file to be run. + valgrind: If True the command will be run under valgrind. + + Returns: + The command exit code (int) + """ + if not valgrind: + subproc = subprocess.Popen(binary, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + subproc.wait() + if subproc.returncode != 0: # In case of error print the output + print subproc.communicate()[0] + return subproc.returncode + else: + # Need the full path to valgrind to avoid other versions on the system. + subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck", + "--leak-check=yes", "-q", binary], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + # Cannot rely on the retcode of valgrind. Instead look for an empty output. + valgrind_out = subproc.communicate()[0].strip() + if valgrind_out: + print valgrind_out + return 1 + else: + return 0 + + +def HasValgrind(): + """Check that /usr/bin/valgrind exists. + + We look for the fullpath to avoid picking up 'alternative' valgrind + on the system. + + Returns: + True if a system valgrind was found. + """ + return os.path.exists("/usr/bin/valgrind")
diff --git a/third_party/libevent/BUILD.gn b/third_party/libevent/BUILD.gn index a1bd0c5..648dae3 100644 --- a/third_party/libevent/BUILD.gn +++ b/third_party/libevent/BUILD.gn
@@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("libevent") { +static_library("libevent") { sources = [ "buffer.c", "evbuffer.c",
diff --git a/third_party/modp_b64/BUILD.gn b/third_party/modp_b64/BUILD.gn index 2ec992b..539abe1 100644 --- a/third_party/modp_b64/BUILD.gn +++ b/third_party/modp_b64/BUILD.gn
@@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("modp_b64") { +static_library("modp_b64") { sources = [ "modp_b64.cc", "modp_b64.h",
diff --git a/third_party/pymock/LICENSE.txt b/third_party/pymock/LICENSE.txt new file mode 100644 index 0000000..7891703 --- /dev/null +++ b/third_party/pymock/LICENSE.txt
@@ -0,0 +1,26 @@ +Copyright (c) 2003-2012, Michael Foord +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/pymock/OWNERS b/third_party/pymock/OWNERS new file mode 100644 index 0000000..9946560 --- /dev/null +++ b/third_party/pymock/OWNERS
@@ -0,0 +1 @@ +sbc@chromium.org
diff --git a/third_party/pymock/README.chromium b/third_party/pymock/README.chromium new file mode 100644 index 0000000..aaf355d --- /dev/null +++ b/third_party/pymock/README.chromium
@@ -0,0 +1,10 @@ +Name: mock +URL: http://pypi.python.org/pypi/mock +Version: 1.0.1 +Security Critical: no +License: BSD +License File: LICENSE.txt +Description: +Python mock library, currently used by native_client_sdk. This is the +same mock library that is now part of python 3.3 where it is know as +unittest.mock.
diff --git a/third_party/pymock/mock.py b/third_party/pymock/mock.py new file mode 100644 index 0000000..c8fc5d1 --- /dev/null +++ b/third_party/pymock/mock.py
@@ -0,0 +1,2367 @@ +# mock.py +# Test tools for mocking and patching. +# Copyright (C) 2007-2012 Michael Foord & the mock team +# E-mail: fuzzyman AT voidspace DOT org DOT uk + +# mock 1.0 +# http://www.voidspace.org.uk/python/mock/ + +# Released subject to the BSD License +# Please see http://www.voidspace.org.uk/python/license.shtml + +# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml +# Comments, suggestions and bug reports welcome. + + +__all__ = ( + 'Mock', + 'MagicMock', + 'patch', + 'sentinel', + 'DEFAULT', + 'ANY', + 'call', + 'create_autospec', + 'FILTER_DIR', + 'NonCallableMock', + 'NonCallableMagicMock', + 'mock_open', + 'PropertyMock', +) + + +__version__ = '1.0.1' + + +import pprint +import sys + +try: + import inspect +except ImportError: + # for alternative platforms that + # may not have inspect + inspect = None + +try: + from functools import wraps as original_wraps +except ImportError: + # Python 2.4 compatibility + def wraps(original): + def inner(f): + f.__name__ = original.__name__ + f.__doc__ = original.__doc__ + f.__module__ = original.__module__ + f.__wrapped__ = original + return f + return inner +else: + if sys.version_info[:2] >= (3, 3): + wraps = original_wraps + else: + def wraps(func): + def inner(f): + f = original_wraps(func)(f) + f.__wrapped__ = func + return f + return inner + +try: + unicode +except NameError: + # Python 3 + basestring = unicode = str + +try: + long +except NameError: + # Python 3 + long = int + +try: + BaseException +except NameError: + # Python 2.4 compatibility + BaseException = Exception + +try: + next +except NameError: + def next(obj): + return obj.next() + + +BaseExceptions = (BaseException,) +if 'java' in sys.platform: + # jython + import java + BaseExceptions = (BaseException, java.lang.Throwable) + +try: + _isidentifier = str.isidentifier +except AttributeError: + # Python 2.X + import keyword + import re + regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I) + def _isidentifier(string): + if string in keyword.kwlist: + return False + return regex.match(string) + + +inPy3k = sys.version_info[0] == 3 + +# Needed to work around Python 3 bug where use of "super" interferes with +# defining __class__ as a descriptor +_super = super + +self = 'im_self' +builtin = '__builtin__' +if inPy3k: + self = '__self__' + builtin = 'builtins' + +FILTER_DIR = True + + +def _is_instance_mock(obj): + # can't use isinstance on Mock objects because they override __class__ + # The base class for all mocks is NonCallableMock + return issubclass(type(obj), NonCallableMock) + + +def _is_exception(obj): + return ( + isinstance(obj, BaseExceptions) or + isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) + ) + + +class _slotted(object): + __slots__ = ['a'] + + +DescriptorTypes = ( + type(_slotted.a), + property, +) + + +def _getsignature(func, skipfirst, instance=False): + if inspect is None: + raise ImportError('inspect module not available') + + if isinstance(func, ClassTypes) and not instance: + try: + func = func.__init__ + except AttributeError: + return + skipfirst = True + elif not isinstance(func, FunctionTypes): + # for classes where instance is True we end up here too + try: + func = func.__call__ + except AttributeError: + return + + if inPy3k: + try: + argspec = inspect.getfullargspec(func) + except TypeError: + # C function / method, possibly inherited object().__init__ + return + regargs, varargs, varkw, defaults, kwonly, kwonlydef, ann = argspec + else: + try: + regargs, varargs, varkwargs, defaults = inspect.getargspec(func) + except TypeError: + # C function / method, possibly inherited object().__init__ + return + + # instance methods and classmethods need to lose the self argument + if getattr(func, self, None) is not None: + regargs = regargs[1:] + if skipfirst: + # this condition and the above one are never both True - why? + regargs = regargs[1:] + + if inPy3k: + signature = inspect.formatargspec( + regargs, varargs, varkw, defaults, + kwonly, kwonlydef, ann, formatvalue=lambda value: "") + else: + signature = inspect.formatargspec( + regargs, varargs, varkwargs, defaults, + formatvalue=lambda value: "") + return signature[1:-1], func + + +def _check_signature(func, mock, skipfirst, instance=False): + if not _callable(func): + return + + result = _getsignature(func, skipfirst, instance) + if result is None: + return + signature, func = result + + # can't use self because "self" is common as an argument name + # unfortunately even not in the first place + src = "lambda _mock_self, %s: None" % signature + checksig = eval(src, {}) + _copy_func_details(func, checksig) + type(mock)._mock_check_sig = checksig + + +def _copy_func_details(func, funcopy): + funcopy.__name__ = func.__name__ + funcopy.__doc__ = func.__doc__ + #funcopy.__dict__.update(func.__dict__) + funcopy.__module__ = func.__module__ + if not inPy3k: + funcopy.func_defaults = func.func_defaults + return + funcopy.__defaults__ = func.__defaults__ + funcopy.__kwdefaults__ = func.__kwdefaults__ + + +def _callable(obj): + if isinstance(obj, ClassTypes): + return True + if getattr(obj, '__call__', None) is not None: + return True + return False + + +def _is_list(obj): + # checks for list or tuples + # XXXX badly named! + return type(obj) in (list, tuple) + + +def _instance_callable(obj): + """Given an object, return True if the object is callable. + For classes, return True if instances would be callable.""" + if not isinstance(obj, ClassTypes): + # already an instance + return getattr(obj, '__call__', None) is not None + + klass = obj + # uses __bases__ instead of __mro__ so that we work with old style classes + if klass.__dict__.get('__call__') is not None: + return True + + for base in klass.__bases__: + if _instance_callable(base): + return True + return False + + +def _set_signature(mock, original, instance=False): + # creates a function with signature (*args, **kwargs) that delegates to a + # mock. It still does signature checking by calling a lambda with the same + # signature as the original. + if not _callable(original): + return + + skipfirst = isinstance(original, ClassTypes) + result = _getsignature(original, skipfirst, instance) + if result is None: + # was a C function (e.g. object().__init__ ) that can't be mocked + return + + signature, func = result + + src = "lambda %s: None" % signature + checksig = eval(src, {}) + _copy_func_details(func, checksig) + + name = original.__name__ + if not _isidentifier(name): + name = 'funcopy' + context = {'_checksig_': checksig, 'mock': mock} + src = """def %s(*args, **kwargs): + _checksig_(*args, **kwargs) + return mock(*args, **kwargs)""" % name + exec (src, context) + funcopy = context[name] + _setup_func(funcopy, mock) + return funcopy + + +def _setup_func(funcopy, mock): + funcopy.mock = mock + + # can't use isinstance with mocks + if not _is_instance_mock(mock): + return + + def assert_called_with(*args, **kwargs): + return mock.assert_called_with(*args, **kwargs) + def assert_called_once_with(*args, **kwargs): + return mock.assert_called_once_with(*args, **kwargs) + def assert_has_calls(*args, **kwargs): + return mock.assert_has_calls(*args, **kwargs) + def assert_any_call(*args, **kwargs): + return mock.assert_any_call(*args, **kwargs) + def reset_mock(): + funcopy.method_calls = _CallList() + funcopy.mock_calls = _CallList() + mock.reset_mock() + ret = funcopy.return_value + if _is_instance_mock(ret) and not ret is mock: + ret.reset_mock() + + funcopy.called = False + funcopy.call_count = 0 + funcopy.call_args = None + funcopy.call_args_list = _CallList() + funcopy.method_calls = _CallList() + funcopy.mock_calls = _CallList() + + funcopy.return_value = mock.return_value + funcopy.side_effect = mock.side_effect + funcopy._mock_children = mock._mock_children + + funcopy.assert_called_with = assert_called_with + funcopy.assert_called_once_with = assert_called_once_with + funcopy.assert_has_calls = assert_has_calls + funcopy.assert_any_call = assert_any_call + funcopy.reset_mock = reset_mock + + mock._mock_delegate = funcopy + + +def _is_magic(name): + return '__%s__' % name[2:-2] == name + + +class _SentinelObject(object): + "A unique, named, sentinel object." + def __init__(self, name): + self.name = name + + def __repr__(self): + return 'sentinel.%s' % self.name + + +class _Sentinel(object): + """Access attributes to return a named object, usable as a sentinel.""" + def __init__(self): + self._sentinels = {} + + def __getattr__(self, name): + if name == '__bases__': + # Without this help(mock) raises an exception + raise AttributeError + return self._sentinels.setdefault(name, _SentinelObject(name)) + + +sentinel = _Sentinel() + +DEFAULT = sentinel.DEFAULT +_missing = sentinel.MISSING +_deleted = sentinel.DELETED + + +class OldStyleClass: + pass +ClassType = type(OldStyleClass) + + +def _copy(value): + if type(value) in (dict, list, tuple, set): + return type(value)(value) + return value + + +ClassTypes = (type,) +if not inPy3k: + ClassTypes = (type, ClassType) + +_allowed_names = set( + [ + 'return_value', '_mock_return_value', 'side_effect', + '_mock_side_effect', '_mock_parent', '_mock_new_parent', + '_mock_name', '_mock_new_name' + ] +) + + +def _delegating_property(name): + _allowed_names.add(name) + _the_name = '_mock_' + name + def _get(self, name=name, _the_name=_the_name): + sig = self._mock_delegate + if sig is None: + return getattr(self, _the_name) + return getattr(sig, name) + def _set(self, value, name=name, _the_name=_the_name): + sig = self._mock_delegate + if sig is None: + self.__dict__[_the_name] = value + else: + setattr(sig, name, value) + + return property(_get, _set) + + + +class _CallList(list): + + def __contains__(self, value): + if not isinstance(value, list): + return list.__contains__(self, value) + len_value = len(value) + len_self = len(self) + if len_value > len_self: + return False + + for i in range(0, len_self - len_value + 1): + sub_list = self[i:i+len_value] + if sub_list == value: + return True + return False + + def __repr__(self): + return pprint.pformat(list(self)) + + +def _check_and_set_parent(parent, value, name, new_name): + if not _is_instance_mock(value): + return False + if ((value._mock_name or value._mock_new_name) or + (value._mock_parent is not None) or + (value._mock_new_parent is not None)): + return False + + _parent = parent + while _parent is not None: + # setting a mock (value) as a child or return value of itself + # should not modify the mock + if _parent is value: + return False + _parent = _parent._mock_new_parent + + if new_name: + value._mock_new_parent = parent + value._mock_new_name = new_name + if name: + value._mock_parent = parent + value._mock_name = name + return True + + + +class Base(object): + _mock_return_value = DEFAULT + _mock_side_effect = None + def __init__(self, *args, **kwargs): + pass + + + +class NonCallableMock(Base): + """A non-callable version of `Mock`""" + + def __new__(cls, *args, **kw): + # every instance has its own class + # so we can create magic methods on the + # class without stomping on other mocks + new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) + instance = object.__new__(new) + return instance + + + def __init__( + self, spec=None, wraps=None, name=None, spec_set=None, + parent=None, _spec_state=None, _new_name='', _new_parent=None, + **kwargs + ): + if _new_parent is None: + _new_parent = parent + + __dict__ = self.__dict__ + __dict__['_mock_parent'] = parent + __dict__['_mock_name'] = name + __dict__['_mock_new_name'] = _new_name + __dict__['_mock_new_parent'] = _new_parent + + if spec_set is not None: + spec = spec_set + spec_set = True + + self._mock_add_spec(spec, spec_set) + + __dict__['_mock_children'] = {} + __dict__['_mock_wraps'] = wraps + __dict__['_mock_delegate'] = None + + __dict__['_mock_called'] = False + __dict__['_mock_call_args'] = None + __dict__['_mock_call_count'] = 0 + __dict__['_mock_call_args_list'] = _CallList() + __dict__['_mock_mock_calls'] = _CallList() + + __dict__['method_calls'] = _CallList() + + if kwargs: + self.configure_mock(**kwargs) + + _super(NonCallableMock, self).__init__( + spec, wraps, name, spec_set, parent, + _spec_state + ) + + + def attach_mock(self, mock, attribute): + """ + Attach a mock as an attribute of this one, replacing its name and + parent. Calls to the attached mock will be recorded in the + `method_calls` and `mock_calls` attributes of this one.""" + mock._mock_parent = None + mock._mock_new_parent = None + mock._mock_name = '' + mock._mock_new_name = None + + setattr(self, attribute, mock) + + + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + + + def _mock_add_spec(self, spec, spec_set): + _spec_class = None + + if spec is not None and not _is_list(spec): + if isinstance(spec, ClassTypes): + _spec_class = spec + else: + _spec_class = _get_class(spec) + + spec = dir(spec) + + __dict__ = self.__dict__ + __dict__['_spec_class'] = _spec_class + __dict__['_spec_set'] = spec_set + __dict__['_mock_methods'] = spec + + + def __get_return_value(self): + ret = self._mock_return_value + if self._mock_delegate is not None: + ret = self._mock_delegate.return_value + + if ret is DEFAULT: + ret = self._get_child_mock( + _new_parent=self, _new_name='()' + ) + self.return_value = ret + return ret + + + def __set_return_value(self, value): + if self._mock_delegate is not None: + self._mock_delegate.return_value = value + else: + self._mock_return_value = value + _check_and_set_parent(self, value, None, '()') + + __return_value_doc = "The value to be returned when the mock is called." + return_value = property(__get_return_value, __set_return_value, + __return_value_doc) + + + @property + def __class__(self): + if self._spec_class is None: + return type(self) + return self._spec_class + + called = _delegating_property('called') + call_count = _delegating_property('call_count') + call_args = _delegating_property('call_args') + call_args_list = _delegating_property('call_args_list') + mock_calls = _delegating_property('mock_calls') + + + def __get_side_effect(self): + sig = self._mock_delegate + if sig is None: + return self._mock_side_effect + return sig.side_effect + + def __set_side_effect(self, value): + value = _try_iter(value) + sig = self._mock_delegate + if sig is None: + self._mock_side_effect = value + else: + sig.side_effect = value + + side_effect = property(__get_side_effect, __set_side_effect) + + + def reset_mock(self): + "Restore the mock object to its initial state." + self.called = False + self.call_args = None + self.call_count = 0 + self.mock_calls = _CallList() + self.call_args_list = _CallList() + self.method_calls = _CallList() + + for child in self._mock_children.values(): + if isinstance(child, _SpecState): + continue + child.reset_mock() + + ret = self._mock_return_value + if _is_instance_mock(ret) and ret is not self: + ret.reset_mock() + + + def configure_mock(self, **kwargs): + """Set attributes on the mock through keyword arguments. + + Attributes plus return values and side effects can be set on child + mocks using standard dot notation and unpacking a dictionary in the + method call: + + >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} + >>> mock.configure_mock(**attrs)""" + for arg, val in sorted(kwargs.items(), + # we sort on the number of dots so that + # attributes are set before we set attributes on + # attributes + key=lambda entry: entry[0].count('.')): + args = arg.split('.') + final = args.pop() + obj = self + for entry in args: + obj = getattr(obj, entry) + setattr(obj, final, val) + + + def __getattr__(self, name): + if name == '_mock_methods': + raise AttributeError(name) + elif self._mock_methods is not None: + if name not in self._mock_methods or name in _all_magics: + raise AttributeError("Mock object has no attribute %r" % name) + elif _is_magic(name): + raise AttributeError(name) + + result = self._mock_children.get(name) + if result is _deleted: + raise AttributeError(name) + elif result is None: + wraps = None + if self._mock_wraps is not None: + # XXXX should we get the attribute without triggering code + # execution? + wraps = getattr(self._mock_wraps, name) + + result = self._get_child_mock( + parent=self, name=name, wraps=wraps, _new_name=name, + _new_parent=self + ) + self._mock_children[name] = result + + elif isinstance(result, _SpecState): + result = create_autospec( + result.spec, result.spec_set, result.instance, + result.parent, result.name + ) + self._mock_children[name] = result + + return result + + + def __repr__(self): + _name_list = [self._mock_new_name] + _parent = self._mock_new_parent + last = self + + dot = '.' + if _name_list == ['()']: + dot = '' + seen = set() + while _parent is not None: + last = _parent + + _name_list.append(_parent._mock_new_name + dot) + dot = '.' + if _parent._mock_new_name == '()': + dot = '' + + _parent = _parent._mock_new_parent + + # use ids here so as not to call __hash__ on the mocks + if id(_parent) in seen: + break + seen.add(id(_parent)) + + _name_list = list(reversed(_name_list)) + _first = last._mock_name or 'mock' + if len(_name_list) > 1: + if _name_list[1] not in ('()', '().'): + _first += '.' + _name_list[0] = _first + name = ''.join(_name_list) + + name_string = '' + if name not in ('mock', 'mock.'): + name_string = ' name=%r' % name + + spec_string = '' + if self._spec_class is not None: + spec_string = ' spec=%r' + if self._spec_set: + spec_string = ' spec_set=%r' + spec_string = spec_string % self._spec_class.__name__ + return "<%s%s%s id='%s'>" % ( + type(self).__name__, + name_string, + spec_string, + id(self) + ) + + + def __dir__(self): + """Filter the output of `dir(mock)` to only useful members. + XXXX + """ + extras = self._mock_methods or [] + from_type = dir(type(self)) + from_dict = list(self.__dict__) + + if FILTER_DIR: + from_type = [e for e in from_type if not e.startswith('_')] + from_dict = [e for e in from_dict if not e.startswith('_') or + _is_magic(e)] + return sorted(set(extras + from_type + from_dict + + list(self._mock_children))) + + + def __setattr__(self, name, value): + if name in _allowed_names: + # property setters go through here + return object.__setattr__(self, name, value) + elif (self._spec_set and self._mock_methods is not None and + name not in self._mock_methods and + name not in self.__dict__): + raise AttributeError("Mock object has no attribute '%s'" % name) + elif name in _unsupported_magics: + msg = 'Attempting to set unsupported magic method %r.' % name + raise AttributeError(msg) + elif name in _all_magics: + if self._mock_methods is not None and name not in self._mock_methods: + raise AttributeError("Mock object has no attribute '%s'" % name) + + if not _is_instance_mock(value): + setattr(type(self), name, _get_method(name, value)) + original = value + value = lambda *args, **kw: original(self, *args, **kw) + else: + # only set _new_name and not name so that mock_calls is tracked + # but not method calls + _check_and_set_parent(self, value, None, name) + setattr(type(self), name, value) + self._mock_children[name] = value + elif name == '__class__': + self._spec_class = value + return + else: + if _check_and_set_parent(self, value, name, name): + self._mock_children[name] = value + return object.__setattr__(self, name, value) + + + def __delattr__(self, name): + if name in _all_magics and name in type(self).__dict__: + delattr(type(self), name) + if name not in self.__dict__: + # for magic methods that are still MagicProxy objects and + # not set on the instance itself + return + + if name in self.__dict__: + object.__delattr__(self, name) + + obj = self._mock_children.get(name, _missing) + if obj is _deleted: + raise AttributeError(name) + if obj is not _missing: + del self._mock_children[name] + self._mock_children[name] = _deleted + + + + def _format_mock_call_signature(self, args, kwargs): + name = self._mock_name or 'mock' + return _format_call_signature(name, args, kwargs) + + + def _format_mock_failure_message(self, args, kwargs): + message = 'Expected call: %s\nActual call: %s' + expected_string = self._format_mock_call_signature(args, kwargs) + call_args = self.call_args + if len(call_args) == 3: + call_args = call_args[1:] + actual_string = self._format_mock_call_signature(*call_args) + return message % (expected_string, actual_string) + + + def assert_called_with(_mock_self, *args, **kwargs): + """assert that the mock was called with the specified arguments. + + Raises an AssertionError if the args and keyword args passed in are + different to the last call to the mock.""" + self = _mock_self + if self.call_args is None: + expected = self._format_mock_call_signature(args, kwargs) + raise AssertionError('Expected call: %s\nNot called' % (expected,)) + + if self.call_args != (args, kwargs): + msg = self._format_mock_failure_message(args, kwargs) + raise AssertionError(msg) + + + def assert_called_once_with(_mock_self, *args, **kwargs): + """assert that the mock was called exactly once and with the specified + arguments.""" + self = _mock_self + if not self.call_count == 1: + msg = ("Expected to be called once. Called %s times." % + self.call_count) + raise AssertionError(msg) + return self.assert_called_with(*args, **kwargs) + + + def assert_has_calls(self, calls, any_order=False): + """assert the mock has been called with the specified calls. + The `mock_calls` list is checked for the calls. + + If `any_order` is False (the default) then the calls must be + sequential. There can be extra calls before or after the + specified calls. + + If `any_order` is True then the calls can be in any order, but + they must all appear in `mock_calls`.""" + if not any_order: + if calls not in self.mock_calls: + raise AssertionError( + 'Calls not found.\nExpected: %r\n' + 'Actual: %r' % (calls, self.mock_calls) + ) + return + + all_calls = list(self.mock_calls) + + not_found = [] + for kall in calls: + try: + all_calls.remove(kall) + except ValueError: + not_found.append(kall) + if not_found: + raise AssertionError( + '%r not all found in call list' % (tuple(not_found),) + ) + + + def assert_any_call(self, *args, **kwargs): + """assert the mock has been called with the specified arguments. + + The assert passes if the mock has *ever* been called, unlike + `assert_called_with` and `assert_called_once_with` that only pass if + the call is the most recent one.""" + kall = call(*args, **kwargs) + if kall not in self.call_args_list: + expected_string = self._format_mock_call_signature(args, kwargs) + raise AssertionError( + '%s call not found' % expected_string + ) + + + def _get_child_mock(self, **kw): + """Create the child mocks for attributes and return value. + By default child mocks will be the same type as the parent. + Subclasses of Mock may want to override this to customize the way + child mocks are made. + + For non-callable mocks the callable variant will be used (rather than + any custom subclass).""" + _type = type(self) + if not issubclass(_type, CallableMixin): + if issubclass(_type, NonCallableMagicMock): + klass = MagicMock + elif issubclass(_type, NonCallableMock) : + klass = Mock + else: + klass = _type.__mro__[1] + return klass(**kw) + + + +def _try_iter(obj): + if obj is None: + return obj + if _is_exception(obj): + return obj + if _callable(obj): + return obj + try: + return iter(obj) + except TypeError: + # XXXX backwards compatibility + # but this will blow up on first call - so maybe we should fail early? + return obj + + + +class CallableMixin(Base): + + def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, + wraps=None, name=None, spec_set=None, parent=None, + _spec_state=None, _new_name='', _new_parent=None, **kwargs): + self.__dict__['_mock_return_value'] = return_value + + _super(CallableMixin, self).__init__( + spec, wraps, name, spec_set, parent, + _spec_state, _new_name, _new_parent, **kwargs + ) + + self.side_effect = side_effect + + + def _mock_check_sig(self, *args, **kwargs): + # stub method that can be replaced with one with a specific signature + pass + + + def __call__(_mock_self, *args, **kwargs): + # can't use self in-case a function / method we are mocking uses self + # in the signature + _mock_self._mock_check_sig(*args, **kwargs) + return _mock_self._mock_call(*args, **kwargs) + + + def _mock_call(_mock_self, *args, **kwargs): + self = _mock_self + self.called = True + self.call_count += 1 + self.call_args = _Call((args, kwargs), two=True) + self.call_args_list.append(_Call((args, kwargs), two=True)) + + _new_name = self._mock_new_name + _new_parent = self._mock_new_parent + self.mock_calls.append(_Call(('', args, kwargs))) + + seen = set() + skip_next_dot = _new_name == '()' + do_method_calls = self._mock_parent is not None + name = self._mock_name + while _new_parent is not None: + this_mock_call = _Call((_new_name, args, kwargs)) + if _new_parent._mock_new_name: + dot = '.' + if skip_next_dot: + dot = '' + + skip_next_dot = False + if _new_parent._mock_new_name == '()': + skip_next_dot = True + + _new_name = _new_parent._mock_new_name + dot + _new_name + + if do_method_calls: + if _new_name == name: + this_method_call = this_mock_call + else: + this_method_call = _Call((name, args, kwargs)) + _new_parent.method_calls.append(this_method_call) + + do_method_calls = _new_parent._mock_parent is not None + if do_method_calls: + name = _new_parent._mock_name + '.' + name + + _new_parent.mock_calls.append(this_mock_call) + _new_parent = _new_parent._mock_new_parent + + # use ids here so as not to call __hash__ on the mocks + _new_parent_id = id(_new_parent) + if _new_parent_id in seen: + break + seen.add(_new_parent_id) + + ret_val = DEFAULT + effect = self.side_effect + if effect is not None: + if _is_exception(effect): + raise effect + + if not _callable(effect): + result = next(effect) + if _is_exception(result): + raise result + return result + + ret_val = effect(*args, **kwargs) + if ret_val is DEFAULT: + ret_val = self.return_value + + if (self._mock_wraps is not None and + self._mock_return_value is DEFAULT): + return self._mock_wraps(*args, **kwargs) + if ret_val is DEFAULT: + ret_val = self.return_value + return ret_val + + + +class Mock(CallableMixin, NonCallableMock): + """ + Create a new `Mock` object. `Mock` takes several optional arguments + that specify the behaviour of the Mock object: + + * `spec`: This can be either a list of strings or an existing object (a + class or instance) that acts as the specification for the mock object. If + you pass in an object then a list of strings is formed by calling dir on + the object (excluding unsupported magic attributes and methods). Accessing + any attribute not in this list will raise an `AttributeError`. + + If `spec` is an object (rather than a list of strings) then + `mock.__class__` returns the class of the spec object. This allows mocks + to pass `isinstance` tests. + + * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* + or get an attribute on the mock that isn't on the object passed as + `spec_set` will raise an `AttributeError`. + + * `side_effect`: A function to be called whenever the Mock is called. See + the `side_effect` attribute. Useful for raising exceptions or + dynamically changing return values. The function is called with the same + arguments as the mock, and unless it returns `DEFAULT`, the return + value of this function is used as the return value. + + Alternatively `side_effect` can be an exception class or instance. In + this case the exception will be raised when the mock is called. + + If `side_effect` is an iterable then each call to the mock will return + the next value from the iterable. If any of the members of the iterable + are exceptions they will be raised instead of returned. + + * `return_value`: The value returned when the mock is called. By default + this is a new Mock (created on first access). See the + `return_value` attribute. + + * `wraps`: Item for the mock object to wrap. If `wraps` is not None then + calling the Mock will pass the call through to the wrapped object + (returning the real result). Attribute access on the mock will return a + Mock object that wraps the corresponding attribute of the wrapped object + (so attempting to access an attribute that doesn't exist will raise an + `AttributeError`). + + If the mock has an explicit `return_value` set then calls are not passed + to the wrapped object and the `return_value` is returned instead. + + * `name`: If the mock has a name then it will be used in the repr of the + mock. This can be useful for debugging. The name is propagated to child + mocks. + + Mocks can also be called with arbitrary keyword arguments. These will be + used to set attributes on the mock after it is created. + """ + + + +def _dot_lookup(thing, comp, import_path): + try: + return getattr(thing, comp) + except AttributeError: + __import__(import_path) + return getattr(thing, comp) + + +def _importer(target): + components = target.split('.') + import_path = components.pop(0) + thing = __import__(import_path) + + for comp in components: + import_path += ".%s" % comp + thing = _dot_lookup(thing, comp, import_path) + return thing + + +def _is_started(patcher): + # XXXX horrible + return hasattr(patcher, 'is_local') + + +class _patch(object): + + attribute_name = None + _active_patches = set() + + def __init__( + self, getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ): + if new_callable is not None: + if new is not DEFAULT: + raise ValueError( + "Cannot use 'new' and 'new_callable' together" + ) + if autospec is not None: + raise ValueError( + "Cannot use 'autospec' and 'new_callable' together" + ) + + self.getter = getter + self.attribute = attribute + self.new = new + self.new_callable = new_callable + self.spec = spec + self.create = create + self.has_local = False + self.spec_set = spec_set + self.autospec = autospec + self.kwargs = kwargs + self.additional_patchers = [] + + + def copy(self): + patcher = _patch( + self.getter, self.attribute, self.new, self.spec, + self.create, self.spec_set, + self.autospec, self.new_callable, self.kwargs + ) + patcher.attribute_name = self.attribute_name + patcher.additional_patchers = [ + p.copy() for p in self.additional_patchers + ] + return patcher + + + def __call__(self, func): + if isinstance(func, ClassTypes): + return self.decorate_class(func) + return self.decorate_callable(func) + + + def decorate_class(self, klass): + for attr in dir(klass): + if not attr.startswith(patch.TEST_PREFIX): + continue + + attr_value = getattr(klass, attr) + if not hasattr(attr_value, "__call__"): + continue + + patcher = self.copy() + setattr(klass, attr, patcher(attr_value)) + return klass + + + def decorate_callable(self, func): + if hasattr(func, 'patchings'): + func.patchings.append(self) + return func + + @wraps(func) + def patched(*args, **keywargs): + # don't use a with here (backwards compatability with Python 2.4) + extra_args = [] + entered_patchers = [] + + # can't use try...except...finally because of Python 2.4 + # compatibility + exc_info = tuple() + try: + try: + for patching in patched.patchings: + arg = patching.__enter__() + entered_patchers.append(patching) + if patching.attribute_name is not None: + keywargs.update(arg) + elif patching.new is DEFAULT: + extra_args.append(arg) + + args += tuple(extra_args) + return func(*args, **keywargs) + except: + if (patching not in entered_patchers and + _is_started(patching)): + # the patcher may have been started, but an exception + # raised whilst entering one of its additional_patchers + entered_patchers.append(patching) + # Pass the exception to __exit__ + exc_info = sys.exc_info() + # re-raise the exception + raise + finally: + for patching in reversed(entered_patchers): + patching.__exit__(*exc_info) + + patched.patchings = [self] + if hasattr(func, 'func_code'): + # not in Python 3 + patched.compat_co_firstlineno = getattr( + func, "compat_co_firstlineno", + func.func_code.co_firstlineno + ) + return patched + + + def get_original(self): + target = self.getter() + name = self.attribute + + original = DEFAULT + local = False + + try: + original = target.__dict__[name] + except (AttributeError, KeyError): + original = getattr(target, name, DEFAULT) + else: + local = True + + if not self.create and original is DEFAULT: + raise AttributeError( + "%s does not have the attribute %r" % (target, name) + ) + return original, local + + + def __enter__(self): + """Perform the patch.""" + new, spec, spec_set = self.new, self.spec, self.spec_set + autospec, kwargs = self.autospec, self.kwargs + new_callable = self.new_callable + self.target = self.getter() + + # normalise False to None + if spec is False: + spec = None + if spec_set is False: + spec_set = None + if autospec is False: + autospec = None + + if spec is not None and autospec is not None: + raise TypeError("Can't specify spec and autospec") + if ((spec is not None or autospec is not None) and + spec_set not in (True, None)): + raise TypeError("Can't provide explicit spec_set *and* spec or autospec") + + original, local = self.get_original() + + if new is DEFAULT and autospec is None: + inherit = False + if spec is True: + # set spec to the object we are replacing + spec = original + if spec_set is True: + spec_set = original + spec = None + elif spec is not None: + if spec_set is True: + spec_set = spec + spec = None + elif spec_set is True: + spec_set = original + + if spec is not None or spec_set is not None: + if original is DEFAULT: + raise TypeError("Can't use 'spec' with create=True") + if isinstance(original, ClassTypes): + # If we're patching out a class and there is a spec + inherit = True + + Klass = MagicMock + _kwargs = {} + if new_callable is not None: + Klass = new_callable + elif spec is not None or spec_set is not None: + this_spec = spec + if spec_set is not None: + this_spec = spec_set + if _is_list(this_spec): + not_callable = '__call__' not in this_spec + else: + not_callable = not _callable(this_spec) + if not_callable: + Klass = NonCallableMagicMock + + if spec is not None: + _kwargs['spec'] = spec + if spec_set is not None: + _kwargs['spec_set'] = spec_set + + # add a name to mocks + if (isinstance(Klass, type) and + issubclass(Klass, NonCallableMock) and self.attribute): + _kwargs['name'] = self.attribute + + _kwargs.update(kwargs) + new = Klass(**_kwargs) + + if inherit and _is_instance_mock(new): + # we can only tell if the instance should be callable if the + # spec is not a list + this_spec = spec + if spec_set is not None: + this_spec = spec_set + if (not _is_list(this_spec) and not + _instance_callable(this_spec)): + Klass = NonCallableMagicMock + + _kwargs.pop('name') + new.return_value = Klass(_new_parent=new, _new_name='()', + **_kwargs) + elif autospec is not None: + # spec is ignored, new *must* be default, spec_set is treated + # as a boolean. Should we check spec is not None and that spec_set + # is a bool? + if new is not DEFAULT: + raise TypeError( + "autospec creates the mock for you. Can't specify " + "autospec and new." + ) + if original is DEFAULT: + raise TypeError("Can't use 'autospec' with create=True") + spec_set = bool(spec_set) + if autospec is True: + autospec = original + + new = create_autospec(autospec, spec_set=spec_set, + _name=self.attribute, **kwargs) + elif kwargs: + # can't set keyword args when we aren't creating the mock + # XXXX If new is a Mock we could call new.configure_mock(**kwargs) + raise TypeError("Can't pass kwargs to a mock we aren't creating") + + new_attr = new + + self.temp_original = original + self.is_local = local + setattr(self.target, self.attribute, new_attr) + if self.attribute_name is not None: + extra_args = {} + if self.new is DEFAULT: + extra_args[self.attribute_name] = new + for patching in self.additional_patchers: + arg = patching.__enter__() + if patching.new is DEFAULT: + extra_args.update(arg) + return extra_args + + return new + + + def __exit__(self, *exc_info): + """Undo the patch.""" + if not _is_started(self): + raise RuntimeError('stop called on unstarted patcher') + + if self.is_local and self.temp_original is not DEFAULT: + setattr(self.target, self.attribute, self.temp_original) + else: + delattr(self.target, self.attribute) + if not self.create and not hasattr(self.target, self.attribute): + # needed for proxy objects like django settings + setattr(self.target, self.attribute, self.temp_original) + + del self.temp_original + del self.is_local + del self.target + for patcher in reversed(self.additional_patchers): + if _is_started(patcher): + patcher.__exit__(*exc_info) + + + def start(self): + """Activate a patch, returning any created mock.""" + result = self.__enter__() + self._active_patches.add(self) + return result + + + def stop(self): + """Stop an active patch.""" + self._active_patches.discard(self) + return self.__exit__() + + + +def _get_target(target): + try: + target, attribute = target.rsplit('.', 1) + except (TypeError, ValueError): + raise TypeError("Need a valid target to patch. You supplied: %r" % + (target,)) + getter = lambda: _importer(target) + return getter, attribute + + +def _patch_object( + target, attribute, new=DEFAULT, spec=None, + create=False, spec_set=None, autospec=None, + new_callable=None, **kwargs + ): + """ + patch.object(target, attribute, new=DEFAULT, spec=None, create=False, + spec_set=None, autospec=None, new_callable=None, **kwargs) + + patch the named member (`attribute`) on an object (`target`) with a mock + object. + + `patch.object` can be used as a decorator, class decorator or a context + manager. Arguments `new`, `spec`, `create`, `spec_set`, + `autospec` and `new_callable` have the same meaning as for `patch`. Like + `patch`, `patch.object` takes arbitrary keyword arguments for configuring + the mock object it creates. + + When used as a class decorator `patch.object` honours `patch.TEST_PREFIX` + for choosing which methods to wrap. + """ + getter = lambda: target + return _patch( + getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ) + + +def _patch_multiple(target, spec=None, create=False, spec_set=None, + autospec=None, new_callable=None, **kwargs): + """Perform multiple patches in a single call. It takes the object to be + patched (either as an object or a string to fetch the object by importing) + and keyword arguments for the patches:: + + with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): + ... + + Use `DEFAULT` as the value if you want `patch.multiple` to create + mocks for you. In this case the created mocks are passed into a decorated + function by keyword, and a dictionary is returned when `patch.multiple` is + used as a context manager. + + `patch.multiple` can be used as a decorator, class decorator or a context + manager. The arguments `spec`, `spec_set`, `create`, + `autospec` and `new_callable` have the same meaning as for `patch`. These + arguments will be applied to *all* patches done by `patch.multiple`. + + When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX` + for choosing which methods to wrap. + """ + if type(target) in (unicode, str): + getter = lambda: _importer(target) + else: + getter = lambda: target + + if not kwargs: + raise ValueError( + 'Must supply at least one keyword argument with patch.multiple' + ) + # need to wrap in a list for python 3, where items is a view + items = list(kwargs.items()) + attribute, new = items[0] + patcher = _patch( + getter, attribute, new, spec, create, spec_set, + autospec, new_callable, {} + ) + patcher.attribute_name = attribute + for attribute, new in items[1:]: + this_patcher = _patch( + getter, attribute, new, spec, create, spec_set, + autospec, new_callable, {} + ) + this_patcher.attribute_name = attribute + patcher.additional_patchers.append(this_patcher) + return patcher + + +def patch( + target, new=DEFAULT, spec=None, create=False, + spec_set=None, autospec=None, new_callable=None, **kwargs + ): + """ + `patch` acts as a function decorator, class decorator or a context + manager. Inside the body of the function or with statement, the `target` + is patched with a `new` object. When the function/with statement exits + the patch is undone. + + If `new` is omitted, then the target is replaced with a + `MagicMock`. If `patch` is used as a decorator and `new` is + omitted, the created mock is passed in as an extra argument to the + decorated function. If `patch` is used as a context manager the created + mock is returned by the context manager. + + `target` should be a string in the form `'package.module.ClassName'`. The + `target` is imported and the specified object replaced with the `new` + object, so the `target` must be importable from the environment you are + calling `patch` from. The target is imported when the decorated function + is executed, not at decoration time. + + The `spec` and `spec_set` keyword arguments are passed to the `MagicMock` + if patch is creating one for you. + + In addition you can pass `spec=True` or `spec_set=True`, which causes + patch to pass in the object being mocked as the spec/spec_set object. + + `new_callable` allows you to specify a different class, or callable object, + that will be called to create the `new` object. By default `MagicMock` is + used. + + A more powerful form of `spec` is `autospec`. If you set `autospec=True` + then the mock with be created with a spec from the object being replaced. + All attributes of the mock will also have the spec of the corresponding + attribute of the object being replaced. Methods and functions being + mocked will have their arguments checked and will raise a `TypeError` if + they are called with the wrong signature. For mocks replacing a class, + their return value (the 'instance') will have the same spec as the class. + + Instead of `autospec=True` you can pass `autospec=some_object` to use an + arbitrary object as the spec instead of the one being replaced. + + By default `patch` will fail to replace attributes that don't exist. If + you pass in `create=True`, and the attribute doesn't exist, patch will + create the attribute for you when the patched function is called, and + delete it again afterwards. This is useful for writing tests against + attributes that your production code creates at runtime. It is off by by + default because it can be dangerous. With it switched on you can write + passing tests against APIs that don't actually exist! + + Patch can be used as a `TestCase` class decorator. It works by + decorating each test method in the class. This reduces the boilerplate + code when your test methods share a common patchings set. `patch` finds + tests by looking for method names that start with `patch.TEST_PREFIX`. + By default this is `test`, which matches the way `unittest` finds tests. + You can specify an alternative prefix by setting `patch.TEST_PREFIX`. + + Patch can be used as a context manager, with the with statement. Here the + patching applies to the indented block after the with statement. If you + use "as" then the patched object will be bound to the name after the + "as"; very useful if `patch` is creating a mock object for you. + + `patch` takes arbitrary keyword arguments. These will be passed to + the `Mock` (or `new_callable`) on construction. + + `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are + available for alternate use-cases. + """ + getter, attribute = _get_target(target) + return _patch( + getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ) + + +class _patch_dict(object): + """ + Patch a dictionary, or dictionary like object, and restore the dictionary + to its original state after the test. + + `in_dict` can be a dictionary or a mapping like container. If it is a + mapping then it must at least support getting, setting and deleting items + plus iterating over keys. + + `in_dict` can also be a string specifying the name of the dictionary, which + will then be fetched by importing it. + + `values` can be a dictionary of values to set in the dictionary. `values` + can also be an iterable of `(key, value)` pairs. + + If `clear` is True then the dictionary will be cleared before the new + values are set. + + `patch.dict` can also be called with arbitrary keyword arguments to set + values in the dictionary:: + + with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()): + ... + + `patch.dict` can be used as a context manager, decorator or class + decorator. When used as a class decorator `patch.dict` honours + `patch.TEST_PREFIX` for choosing which methods to wrap. + """ + + def __init__(self, in_dict, values=(), clear=False, **kwargs): + if isinstance(in_dict, basestring): + in_dict = _importer(in_dict) + self.in_dict = in_dict + # support any argument supported by dict(...) constructor + self.values = dict(values) + self.values.update(kwargs) + self.clear = clear + self._original = None + + + def __call__(self, f): + if isinstance(f, ClassTypes): + return self.decorate_class(f) + @wraps(f) + def _inner(*args, **kw): + self._patch_dict() + try: + return f(*args, **kw) + finally: + self._unpatch_dict() + + return _inner + + + def decorate_class(self, klass): + for attr in dir(klass): + attr_value = getattr(klass, attr) + if (attr.startswith(patch.TEST_PREFIX) and + hasattr(attr_value, "__call__")): + decorator = _patch_dict(self.in_dict, self.values, self.clear) + decorated = decorator(attr_value) + setattr(klass, attr, decorated) + return klass + + + def __enter__(self): + """Patch the dict.""" + self._patch_dict() + + + def _patch_dict(self): + values = self.values + in_dict = self.in_dict + clear = self.clear + + try: + original = in_dict.copy() + except AttributeError: + # dict like object with no copy method + # must support iteration over keys + original = {} + for key in in_dict: + original[key] = in_dict[key] + self._original = original + + if clear: + _clear_dict(in_dict) + + try: + in_dict.update(values) + except AttributeError: + # dict like object with no update method + for key in values: + in_dict[key] = values[key] + + + def _unpatch_dict(self): + in_dict = self.in_dict + original = self._original + + _clear_dict(in_dict) + + try: + in_dict.update(original) + except AttributeError: + for key in original: + in_dict[key] = original[key] + + + def __exit__(self, *args): + """Unpatch the dict.""" + self._unpatch_dict() + return False + + start = __enter__ + stop = __exit__ + + +def _clear_dict(in_dict): + try: + in_dict.clear() + except AttributeError: + keys = list(in_dict) + for key in keys: + del in_dict[key] + + +def _patch_stopall(): + """Stop all active patches.""" + for patch in list(_patch._active_patches): + patch.stop() + + +patch.object = _patch_object +patch.dict = _patch_dict +patch.multiple = _patch_multiple +patch.stopall = _patch_stopall +patch.TEST_PREFIX = 'test' + +magic_methods = ( + "lt le gt ge eq ne " + "getitem setitem delitem " + "len contains iter " + "hash str sizeof " + "enter exit " + "divmod neg pos abs invert " + "complex int float index " + "trunc floor ceil " +) + +numerics = "add sub mul div floordiv mod lshift rshift and xor or pow " +inplace = ' '.join('i%s' % n for n in numerics.split()) +right = ' '.join('r%s' % n for n in numerics.split()) +extra = '' +if inPy3k: + extra = 'bool next ' +else: + extra = 'unicode long nonzero oct hex truediv rtruediv ' + +# not including __prepare__, __instancecheck__, __subclasscheck__ +# (as they are metaclass methods) +# __del__ is not supported at all as it causes problems if it exists + +_non_defaults = set('__%s__' % method for method in [ + 'cmp', 'getslice', 'setslice', 'coerce', 'subclasses', + 'format', 'get', 'set', 'delete', 'reversed', + 'missing', 'reduce', 'reduce_ex', 'getinitargs', + 'getnewargs', 'getstate', 'setstate', 'getformat', + 'setformat', 'repr', 'dir' +]) + + +def _get_method(name, func): + "Turns a callable object (like a mock) into a real function" + def method(self, *args, **kw): + return func(self, *args, **kw) + method.__name__ = name + return method + + +_magics = set( + '__%s__' % method for method in + ' '.join([magic_methods, numerics, inplace, right, extra]).split() +) + +_all_magics = _magics | _non_defaults + +_unsupported_magics = set([ + '__getattr__', '__setattr__', + '__init__', '__new__', '__prepare__' + '__instancecheck__', '__subclasscheck__', + '__del__' +]) + +_calculate_return_value = { + '__hash__': lambda self: object.__hash__(self), + '__str__': lambda self: object.__str__(self), + '__sizeof__': lambda self: object.__sizeof__(self), + '__unicode__': lambda self: unicode(object.__str__(self)), +} + +_return_values = { + '__lt__': NotImplemented, + '__gt__': NotImplemented, + '__le__': NotImplemented, + '__ge__': NotImplemented, + '__int__': 1, + '__contains__': False, + '__len__': 0, + '__exit__': False, + '__complex__': 1j, + '__float__': 1.0, + '__bool__': True, + '__nonzero__': True, + '__oct__': '1', + '__hex__': '0x1', + '__long__': long(1), + '__index__': 1, +} + + +def _get_eq(self): + def __eq__(other): + ret_val = self.__eq__._mock_return_value + if ret_val is not DEFAULT: + return ret_val + return self is other + return __eq__ + +def _get_ne(self): + def __ne__(other): + if self.__ne__._mock_return_value is not DEFAULT: + return DEFAULT + return self is not other + return __ne__ + +def _get_iter(self): + def __iter__(): + ret_val = self.__iter__._mock_return_value + if ret_val is DEFAULT: + return iter([]) + # if ret_val was already an iterator, then calling iter on it should + # return the iterator unchanged + return iter(ret_val) + return __iter__ + +_side_effect_methods = { + '__eq__': _get_eq, + '__ne__': _get_ne, + '__iter__': _get_iter, +} + + + +def _set_return_value(mock, method, name): + fixed = _return_values.get(name, DEFAULT) + if fixed is not DEFAULT: + method.return_value = fixed + return + + return_calulator = _calculate_return_value.get(name) + if return_calulator is not None: + try: + return_value = return_calulator(mock) + except AttributeError: + # XXXX why do we return AttributeError here? + # set it as a side_effect instead? + return_value = AttributeError(name) + method.return_value = return_value + return + + side_effector = _side_effect_methods.get(name) + if side_effector is not None: + method.side_effect = side_effector(mock) + + + +class MagicMixin(object): + def __init__(self, *args, **kw): + _super(MagicMixin, self).__init__(*args, **kw) + self._mock_set_magics() + + + def _mock_set_magics(self): + these_magics = _magics + + if self._mock_methods is not None: + these_magics = _magics.intersection(self._mock_methods) + + remove_magics = set() + remove_magics = _magics - these_magics + + for entry in remove_magics: + if entry in type(self).__dict__: + # remove unneeded magic methods + delattr(self, entry) + + # don't overwrite existing attributes if called a second time + these_magics = these_magics - set(type(self).__dict__) + + _type = type(self) + for entry in these_magics: + setattr(_type, entry, MagicProxy(entry, self)) + + + +class NonCallableMagicMock(MagicMixin, NonCallableMock): + """A version of `MagicMock` that isn't callable.""" + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + self._mock_set_magics() + + + +class MagicMock(MagicMixin, Mock): + """ + MagicMock is a subclass of Mock with default implementations + of most of the magic methods. You can use MagicMock without having to + configure the magic methods yourself. + + If you use the `spec` or `spec_set` arguments then *only* magic + methods that exist in the spec will be created. + + Attributes and the return value of a `MagicMock` will also be `MagicMocks`. + """ + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + self._mock_set_magics() + + + +class MagicProxy(object): + def __init__(self, name, parent): + self.name = name + self.parent = parent + + def __call__(self, *args, **kwargs): + m = self.create_mock() + return m(*args, **kwargs) + + def create_mock(self): + entry = self.name + parent = self.parent + m = parent._get_child_mock(name=entry, _new_name=entry, + _new_parent=parent) + setattr(parent, entry, m) + _set_return_value(parent, m, entry) + return m + + def __get__(self, obj, _type=None): + return self.create_mock() + + + +class _ANY(object): + "A helper object that compares equal to everything." + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + def __repr__(self): + return '<ANY>' + +ANY = _ANY() + + + +def _format_call_signature(name, args, kwargs): + message = '%s(%%s)' % name + formatted_args = '' + args_string = ', '.join([repr(arg) for arg in args]) + kwargs_string = ', '.join([ + '%s=%r' % (key, value) for key, value in kwargs.items() + ]) + if args_string: + formatted_args = args_string + if kwargs_string: + if formatted_args: + formatted_args += ', ' + formatted_args += kwargs_string + + return message % formatted_args + + + +class _Call(tuple): + """ + A tuple for holding the results of a call to a mock, either in the form + `(args, kwargs)` or `(name, args, kwargs)`. + + If args or kwargs are empty then a call tuple will compare equal to + a tuple without those values. This makes comparisons less verbose:: + + _Call(('name', (), {})) == ('name',) + _Call(('name', (1,), {})) == ('name', (1,)) + _Call(((), {'a': 'b'})) == ({'a': 'b'},) + + The `_Call` object provides a useful shortcut for comparing with call:: + + _Call(((1, 2), {'a': 3})) == call(1, 2, a=3) + _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3) + + If the _Call has no name then it will match any name. + """ + def __new__(cls, value=(), name=None, parent=None, two=False, + from_kall=True): + name = '' + args = () + kwargs = {} + _len = len(value) + if _len == 3: + name, args, kwargs = value + elif _len == 2: + first, second = value + if isinstance(first, basestring): + name = first + if isinstance(second, tuple): + args = second + else: + kwargs = second + else: + args, kwargs = first, second + elif _len == 1: + value, = value + if isinstance(value, basestring): + name = value + elif isinstance(value, tuple): + args = value + else: + kwargs = value + + if two: + return tuple.__new__(cls, (args, kwargs)) + + return tuple.__new__(cls, (name, args, kwargs)) + + + def __init__(self, value=(), name=None, parent=None, two=False, + from_kall=True): + self.name = name + self.parent = parent + self.from_kall = from_kall + + + def __eq__(self, other): + if other is ANY: + return True + try: + len_other = len(other) + except TypeError: + return False + + self_name = '' + if len(self) == 2: + self_args, self_kwargs = self + else: + self_name, self_args, self_kwargs = self + + other_name = '' + if len_other == 0: + other_args, other_kwargs = (), {} + elif len_other == 3: + other_name, other_args, other_kwargs = other + elif len_other == 1: + value, = other + if isinstance(value, tuple): + other_args = value + other_kwargs = {} + elif isinstance(value, basestring): + other_name = value + other_args, other_kwargs = (), {} + else: + other_args = () + other_kwargs = value + else: + # len 2 + # could be (name, args) or (name, kwargs) or (args, kwargs) + first, second = other + if isinstance(first, basestring): + other_name = first + if isinstance(second, tuple): + other_args, other_kwargs = second, {} + else: + other_args, other_kwargs = (), second + else: + other_args, other_kwargs = first, second + + if self_name and other_name != self_name: + return False + + # this order is important for ANY to work! + return (other_args, other_kwargs) == (self_args, self_kwargs) + + + def __ne__(self, other): + return not self.__eq__(other) + + + def __call__(self, *args, **kwargs): + if self.name is None: + return _Call(('', args, kwargs), name='()') + + name = self.name + '()' + return _Call((self.name, args, kwargs), name=name, parent=self) + + + def __getattr__(self, attr): + if self.name is None: + return _Call(name=attr, from_kall=False) + name = '%s.%s' % (self.name, attr) + return _Call(name=name, parent=self, from_kall=False) + + + def __repr__(self): + if not self.from_kall: + name = self.name or 'call' + if name.startswith('()'): + name = 'call%s' % name + return name + + if len(self) == 2: + name = 'call' + args, kwargs = self + else: + name, args, kwargs = self + if not name: + name = 'call' + elif not name.startswith('()'): + name = 'call.%s' % name + else: + name = 'call%s' % name + return _format_call_signature(name, args, kwargs) + + + def call_list(self): + """For a call object that represents multiple calls, `call_list` + returns a list of all the intermediate calls as well as the + final call.""" + vals = [] + thing = self + while thing is not None: + if thing.from_kall: + vals.append(thing) + thing = thing.parent + return _CallList(reversed(vals)) + + +call = _Call(from_kall=False) + + + +def create_autospec(spec, spec_set=False, instance=False, _parent=None, + _name=None, **kwargs): + """Create a mock object using another object as a spec. Attributes on the + mock will use the corresponding attribute on the `spec` object as their + spec. + + Functions or methods being mocked will have their arguments checked + to check that they are called with the correct signature. + + If `spec_set` is True then attempting to set attributes that don't exist + on the spec object will raise an `AttributeError`. + + If a class is used as a spec then the return value of the mock (the + instance of the class) will have the same spec. You can use a class as the + spec for an instance object by passing `instance=True`. The returned mock + will only be callable if instances of the mock are callable. + + `create_autospec` also takes arbitrary keyword arguments that are passed to + the constructor of the created mock.""" + if _is_list(spec): + # can't pass a list instance to the mock constructor as it will be + # interpreted as a list of strings + spec = type(spec) + + is_type = isinstance(spec, ClassTypes) + + _kwargs = {'spec': spec} + if spec_set: + _kwargs = {'spec_set': spec} + elif spec is None: + # None we mock with a normal mock without a spec + _kwargs = {} + + _kwargs.update(kwargs) + + Klass = MagicMock + if type(spec) in DescriptorTypes: + # descriptors don't have a spec + # because we don't know what type they return + _kwargs = {} + elif not _callable(spec): + Klass = NonCallableMagicMock + elif is_type and instance and not _instance_callable(spec): + Klass = NonCallableMagicMock + + _new_name = _name + if _parent is None: + # for a top level object no _new_name should be set + _new_name = '' + + mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name, + name=_name, **_kwargs) + + if isinstance(spec, FunctionTypes): + # should only happen at the top level because we don't + # recurse for functions + mock = _set_signature(mock, spec) + else: + _check_signature(spec, mock, is_type, instance) + + if _parent is not None and not instance: + _parent._mock_children[_name] = mock + + if is_type and not instance and 'return_value' not in kwargs: + mock.return_value = create_autospec(spec, spec_set, instance=True, + _name='()', _parent=mock) + + for entry in dir(spec): + if _is_magic(entry): + # MagicMock already does the useful magic methods for us + continue + + if isinstance(spec, FunctionTypes) and entry in FunctionAttributes: + # allow a mock to actually be a function + continue + + # XXXX do we need a better way of getting attributes without + # triggering code execution (?) Probably not - we need the actual + # object to mock it so we would rather trigger a property than mock + # the property descriptor. Likewise we want to mock out dynamically + # provided attributes. + # XXXX what about attributes that raise exceptions other than + # AttributeError on being fetched? + # we could be resilient against it, or catch and propagate the + # exception when the attribute is fetched from the mock + try: + original = getattr(spec, entry) + except AttributeError: + continue + + kwargs = {'spec': original} + if spec_set: + kwargs = {'spec_set': original} + + if not isinstance(original, FunctionTypes): + new = _SpecState(original, spec_set, mock, entry, instance) + mock._mock_children[entry] = new + else: + parent = mock + if isinstance(spec, FunctionTypes): + parent = mock.mock + + new = MagicMock(parent=parent, name=entry, _new_name=entry, + _new_parent=parent, **kwargs) + mock._mock_children[entry] = new + skipfirst = _must_skip(spec, entry, is_type) + _check_signature(original, new, skipfirst=skipfirst) + + # so functions created with _set_signature become instance attributes, + # *plus* their underlying mock exists in _mock_children of the parent + # mock. Adding to _mock_children may be unnecessary where we are also + # setting as an instance attribute? + if isinstance(new, FunctionTypes): + setattr(mock, entry, new) + + return mock + + +def _must_skip(spec, entry, is_type): + if not isinstance(spec, ClassTypes): + if entry in getattr(spec, '__dict__', {}): + # instance attribute - shouldn't skip + return False + spec = spec.__class__ + if not hasattr(spec, '__mro__'): + # old style class: can't have descriptors anyway + return is_type + + for klass in spec.__mro__: + result = klass.__dict__.get(entry, DEFAULT) + if result is DEFAULT: + continue + if isinstance(result, (staticmethod, classmethod)): + return False + return is_type + + # shouldn't get here unless function is a dynamically provided attribute + # XXXX untested behaviour + return is_type + + +def _get_class(obj): + try: + return obj.__class__ + except AttributeError: + # in Python 2, _sre.SRE_Pattern objects have no __class__ + return type(obj) + + +class _SpecState(object): + + def __init__(self, spec, spec_set=False, parent=None, + name=None, ids=None, instance=False): + self.spec = spec + self.ids = ids + self.spec_set = spec_set + self.parent = parent + self.instance = instance + self.name = name + + +FunctionTypes = ( + # python function + type(create_autospec), + # instance method + type(ANY.__eq__), + # unbound method + type(_ANY.__eq__), +) + +FunctionAttributes = set([ + 'func_closure', + 'func_code', + 'func_defaults', + 'func_dict', + 'func_doc', + 'func_globals', + 'func_name', +]) + + +file_spec = None + + +def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` method of the file handle to return. + This is an empty string by default. + """ + global file_spec + if file_spec is None: + # set on first use + if inPy3k: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + else: + file_spec = file + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.write.return_value = None + handle.__enter__.return_value = handle + handle.read.return_value = read_data + + mock.return_value = handle + return mock + + +class PropertyMock(Mock): + """ + A mock intended to be used as a property, or other descriptor, on a class. + `PropertyMock` provides `__get__` and `__set__` methods so you can specify + a return value when it is fetched. + + Fetching a `PropertyMock` instance from an object calls the mock, with + no args. Setting it calls the mock with the value being set. + """ + def _get_child_mock(self, **kwargs): + return MagicMock(**kwargs) + + def __get__(self, obj, obj_type): + return self() + def __set__(self, obj, val): + self(val)
diff --git a/tools/android/checkstyle/chromium-style-5.0.xml b/tools/android/checkstyle/chromium-style-5.0.xml index 17b44b4..351b5b3 100644 --- a/tools/android/checkstyle/chromium-style-5.0.xml +++ b/tools/android/checkstyle/chromium-style-5.0.xml
@@ -164,6 +164,17 @@ <property name="throwsIndent" value="8"/> <property name="lineWrappingIndentation" value="8"/> </module> + <!-- TODO(aurimas): make OperatorWrap into an error once all the warnings are fixed. --> + <module name="OperatorWrap"> + <property name="severity" value="warning"/> + <property name="option" value="NL" /> + <property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR " /> + </module> + <module name="OperatorWrap"> + <property name="severity" value="warning"/> + <property name="option" value="eol"/> + <property name="tokens" value="ASSIGN"/> + </module> </module> <module name="FileTabCharacter"> <property name="severity" value="error"/>
diff --git a/tools/android/forwarder2/device_forwarder_main.cc b/tools/android/forwarder2/device_forwarder_main.cc index cad46f4..aeb2853 100644 --- a/tools/android/forwarder2/device_forwarder_main.cc +++ b/tools/android/forwarder2/device_forwarder_main.cc
@@ -68,7 +68,7 @@ } // Daemon::ServerDelegate: - virtual void Init() OVERRIDE { + virtual void Init() override { DCHECK(!g_notifier); g_notifier = new forwarder2::PipeNotifier(); signal(SIGTERM, KillHandler); @@ -77,7 +77,7 @@ controller_thread_->Start(); } - virtual void OnClientConnected(scoped_ptr<Socket> client_socket) OVERRIDE { + virtual void OnClientConnected(scoped_ptr<Socket> client_socket) override { if (initialized_) { client_socket->WriteString("OK"); return; @@ -119,7 +119,7 @@ bool has_failed() const { return has_failed_; } // Daemon::ClientDelegate: - virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE { + virtual void OnDaemonReady(Socket* daemon_socket) override { char buf[kBufSize]; const int bytes_read = daemon_socket->Read( buf, sizeof(buf) - 1 /* leave space for null terminator */);
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc index 59571b6..514ad93 100644 --- a/tools/android/forwarder2/host_forwarder_main.cc +++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -311,7 +311,7 @@ } // Daemon::ServerDelegate: - virtual void Init() OVERRIDE { + virtual void Init() override { LOG(INFO) << "Starting host process daemon (pid=" << getpid() << ")"; DCHECK(!g_notifier); g_notifier = new PipeNotifier(); @@ -319,7 +319,7 @@ signal(SIGINT, KillHandler); } - virtual void OnClientConnected(scoped_ptr<Socket> client_socket) OVERRIDE { + virtual void OnClientConnected(scoped_ptr<Socket> client_socket) override { char buf[kBufSize]; const int bytes_read = client_socket->Read(buf, sizeof(buf)); if (bytes_read <= 0) { @@ -362,7 +362,7 @@ bool has_failed() const { return has_failed_; } // Daemon::ClientDelegate: - virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE { + virtual void OnDaemonReady(Socket* daemon_socket) override { // Send the forward command to the daemon. CHECK_EQ(command_pickle_.size(), daemon_socket->WriteNumBytes(command_pickle_.data(),
diff --git a/tools/android/heap_profiler/heap_profiler_unittest.cc b/tools/android/heap_profiler/heap_profiler_unittest.cc index 65c2700..d69af5f 100644 --- a/tools/android/heap_profiler/heap_profiler_unittest.cc +++ b/tools/android/heap_profiler/heap_profiler_unittest.cc
@@ -14,9 +14,9 @@ class HeapProfilerTest : public testing::Test { public: - virtual void SetUp() OVERRIDE { heap_profiler_init(&stats_); } + virtual void SetUp() override { heap_profiler_init(&stats_); } - virtual void TearDown() OVERRIDE { + virtual void TearDown() override { CheckAllocVsStacktaceConsistency(); heap_profiler_cleanup(); }
diff --git a/tools/valgrind/chrome_tests.py b/tools/valgrind/chrome_tests.py index d5f3d18..9b0b52f 100755 --- a/tools/valgrind/chrome_tests.py +++ b/tools/valgrind/chrome_tests.py
@@ -475,6 +475,9 @@ return 0; return self.SimpleTest("chrome", "unit_tests") + def TestUIBaseUnit(self): + return self.SimpleTest("chrome", "ui_base_unittests") + def TestUIUnit(self): return self.SimpleTest("chrome", "ui_unittests") @@ -723,6 +726,7 @@ "sync": TestSync, "sync_unit_tests": TestSync, "sync_integration_tests": TestSyncIntegration, "sync_integration": TestSyncIntegration, + "ui_base_unit": TestUIBaseUnit, "ui_base_unittests": TestUIBaseUnit, "ui_unit": TestUIUnit, "ui_unittests": TestUIUnit, "unit": TestUnit, "unit_tests": TestUnit, "url": TestURL, "url_unittests": TestURL,
diff --git a/url/url_canon_icu.h b/url/url_canon_icu.h index 18b1f09..80d7953 100644 --- a/url/url_canon_icu.h +++ b/url/url_canon_icu.h
@@ -24,11 +24,11 @@ // be managed by the creator such that it is alive as long as this is. ICUCharsetConverter(UConverter* converter); - virtual ~ICUCharsetConverter(); + ~ICUCharsetConverter() override; - virtual void ConvertFromUTF16(const base::char16* input, - int input_len, - CanonOutput* output) override; + void ConvertFromUTF16(const base::char16* input, + int input_len, + CanonOutput* output) override; private: // The ICU converter, not owned by this class.
diff --git a/url/url_canon_stdstring.h b/url/url_canon_stdstring.h index 62622b4..c3d8ba1 100644 --- a/url/url_canon_stdstring.h +++ b/url/url_canon_stdstring.h
@@ -36,12 +36,12 @@ class URL_EXPORT StdStringCanonOutput : public CanonOutput { public: StdStringCanonOutput(std::string* str); - virtual ~StdStringCanonOutput(); + ~StdStringCanonOutput() override; // Must be called after writing has completed but before the string is used. void Complete(); - virtual void Resize(int sz) override; + void Resize(int sz) override; protected: std::string* str_;