diff --git a/.gitignore b/.gitignore index f9f952a..c485b68b 100644 --- a/.gitignore +++ b/.gitignore
@@ -303,7 +303,6 @@ /third_party/class-dump/src /third_party/cld_2/src /third_party/colorama/src -/third_party/crashpad/crashpad /third_party/cros /third_party/cros_system_api /third_party/custom_tabs_client/src
diff --git a/DEPS b/DEPS index eaaca04..d158fe8 100644 --- a/DEPS +++ b/DEPS
@@ -39,11 +39,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '2028d7ff744c36855ed36d602e3e050e9f18ec9f', + 'skia_revision': '201efb5ae00828f85bb16d63580898961e4085ce', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '1266d06c4aebbf0227723f472b6e856db1f8363e', + 'v8_revision': 'f45287d1b36c2e981d7bef0e687d0f3111e7e73c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -59,7 +59,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '93181f9a20db7ac706bb9405750303db93762a5b', + 'pdfium_revision': '94edf0c37d1ee6a03697375b9e227071ff2ee69d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -126,9 +126,6 @@ 'src/third_party/colorama/src': Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', - 'src/third_party/crashpad/crashpad': - Var('chromium_git') + '/crashpad/crashpad.git' + '@' + '6bebb10829332dee5c7315abafb0a8bf32840c15', - 'src/third_party/icu': Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '42c58d4e49f2250039f0e98d43e0b76e8f5ca024', @@ -274,7 +271,7 @@ 'src/third_party/catapult': Var('chromium_git') + '/external/github.com/catapult-project/catapult.git' + '@' + - '8ee1919cae827e33a3f217366d4a05e64a948615', + '97d95119247a42aa5590fcdf61a23554ad8899a4', 'src/third_party/openh264/src': Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'b37cda248234162033e3e11b0335f3131cdfe488',
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index c3ff0c4..444bd60 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -366,6 +366,9 @@ <message name="IDS_ASH_INTERNAL_DISPLAY_NAME" desc="The name of the internal display which is shown in the display settings."> Internal Display </message> + <message name="IDS_ASH_VIRTUAL_DISPLAY_NAME" desc="The name of the virtual display which is shown in the display settings."> + Virtual Display + </message> <message name="IDS_ASH_EXIT_WARNING_POPUP_TEXT" desc="The text of the popup when the user preses the exit shortcut."> Press Ctrl+Shift+Q twice to quit. </message>
diff --git a/ash/display/display_change_observer_chromeos.cc b/ash/display/display_change_observer_chromeos.cc index f167f9c..3ff8670 100644 --- a/ash/display/display_change_observer_chromeos.cc +++ b/ash/display/display_change_observer_chromeos.cc
@@ -218,10 +218,18 @@ } gfx::Rect display_bounds(state->origin(), mode_info->size()); - std::string name = - state->type() == ui::DISPLAY_CONNECTION_TYPE_INTERNAL - ? l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) - : state->display_name(); + std::string name; + switch (state->type()) { + case ui::DISPLAY_CONNECTION_TYPE_INTERNAL: + name = l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME); + break; + case ui::DISPLAY_CONNECTION_TYPE_VIRTUAL: + name = l10n_util::GetStringUTF8(IDS_ASH_VIRTUAL_DISPLAY_NAME); + break; + default: + name = state->display_name(); + } + if (name.empty()) name = l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index a0c5f61f..b5e9c65 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc
@@ -300,7 +300,12 @@ if (run_loop_) { run_loop_->quit_when_idle_received_ = true; } else { - NOTREACHED() << "Must be inside Run to call Quit"; + // We don't assert that run_loop_ is valid for custom message pumps. Some, + // for example MojoMessagePump, might have shutdown already based on other + // shutdown signals. + if (type_ != MessageLoop::TYPE_CUSTOM) { + NOTREACHED() << "Must be inside Run to call Quit"; + } } }
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index 1bbcab7..083856bf 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc
@@ -18,6 +18,7 @@ #include "base/debug/alias.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/metrics/metrics_hashes.h" #include "base/metrics/sample_vector.h" #include "base/metrics/statistics_recorder.h" #include "base/pickle.h" @@ -259,6 +260,10 @@ return true; } +uint64_t Histogram::name_hash() const { + return samples_->id(); +} + HistogramType Histogram::GetHistogramType() const { return HISTOGRAM; } @@ -335,7 +340,7 @@ declared_min_(minimum), declared_max_(maximum) { if (ranges) - samples_.reset(new SampleVector(ranges)); + samples_.reset(new SampleVector(HashMetricName(name), ranges)); } Histogram::~Histogram() { @@ -392,7 +397,8 @@ } scoped_ptr<SampleVector> Histogram::SnapshotSampleVector() const { - scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges())); + scoped_ptr<SampleVector> samples( + new SampleVector(samples_->id(), bucket_ranges())); samples->Add(*samples_); return samples; }
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 15e0ec2..8840493 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h
@@ -176,6 +176,7 @@ size_t* bucket_count); // HistogramBase implementation: + uint64_t name_hash() const override; HistogramType GetHistogramType() const override; bool HasConstructionArguments(Sample expected_minimum, Sample expected_maximum,
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h index d246691c..f45cc9c 100644 --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h
@@ -104,6 +104,9 @@ // in more compact machine code being generated by the macros. void CheckName(const StringPiece& name) const; + // Get a unique ID for this histogram's samples. + virtual uint64_t name_hash() const = 0; + // Operations with Flags enum. int32_t flags() const { return subtle::NoBarrier_Load(&flags_); } void SetFlags(int32_t flags);
diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc index f5e03b9..b6303a53 100644 --- a/base/metrics/histogram_samples.cc +++ b/base/metrics/histogram_samples.cc
@@ -59,30 +59,50 @@ } // namespace -HistogramSamples::HistogramSamples() : sum_(0), redundant_count_(0) {} +HistogramSamples::HistogramSamples(uint64_t id) + : HistogramSamples(id, &local_meta_) {} + +HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) + : meta_(meta) { + DCHECK(meta_->id == 0 || meta_->id == id); + meta_->id = id; +} HistogramSamples::~HistogramSamples() {} +// Despite using atomic operations, the increment/add actions below are *not* +// atomic! Race conditions may cause loss of samples or even completely corrupt +// the 64-bit sum on 32-bit machines. This is done intentionally to reduce the +// cost of these operations that could be executed in performance-significant +// points of the code. +// +// TODO(bcwhite): Gather quantitative information as to the cost of using +// proper atomic increments and improve either globally or for those histograms +// that really need it. + void HistogramSamples::Add(const HistogramSamples& other) { - sum_ += other.sum(); + meta_->sum += other.sum(); + HistogramBase::Count old_redundant_count = - subtle::NoBarrier_Load(&redundant_count_); - subtle::NoBarrier_Store(&redundant_count_, + subtle::NoBarrier_Load(&meta_->redundant_count); + subtle::NoBarrier_Store(&meta_->redundant_count, old_redundant_count + other.redundant_count()); bool success = AddSubtractImpl(other.Iterator().get(), ADD); DCHECK(success); } bool HistogramSamples::AddFromPickle(PickleIterator* iter) { - int64 sum; + int64_t sum; HistogramBase::Count redundant_count; if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) return false; - sum_ += sum; + + meta_->sum += sum; + HistogramBase::Count old_redundant_count = - subtle::NoBarrier_Load(&redundant_count_); - subtle::NoBarrier_Store(&redundant_count_, + subtle::NoBarrier_Load(&meta_->redundant_count); + subtle::NoBarrier_Store(&meta_->redundant_count, old_redundant_count + redundant_count); SampleCountPickleIterator pickle_iter(iter); @@ -90,18 +110,20 @@ } void HistogramSamples::Subtract(const HistogramSamples& other) { - sum_ -= other.sum(); + meta_->sum -= other.sum(); + HistogramBase::Count old_redundant_count = - subtle::NoBarrier_Load(&redundant_count_); - subtle::NoBarrier_Store(&redundant_count_, + subtle::NoBarrier_Load(&meta_->redundant_count); + subtle::NoBarrier_Store(&meta_->redundant_count, old_redundant_count - other.redundant_count()); bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT); DCHECK(success); } bool HistogramSamples::Serialize(Pickle* pickle) const { - if (!pickle->WriteInt64(sum_) || - !pickle->WriteInt(subtle::NoBarrier_Load(&redundant_count_))) + if (!pickle->WriteInt64(meta_->sum)) + return false; + if (!pickle->WriteInt(subtle::NoBarrier_Load(&meta_->redundant_count))) return false; HistogramBase::Sample min; @@ -119,13 +141,13 @@ return true; } -void HistogramSamples::IncreaseSum(int64 diff) { - sum_ += diff; +void HistogramSamples::IncreaseSum(int64_t diff) { + meta_->sum += diff; } void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) { - subtle::NoBarrier_Store(&redundant_count_, - subtle::NoBarrier_Load(&redundant_count_) + diff); + subtle::NoBarrier_Store(&meta_->redundant_count, + subtle::NoBarrier_Load(&meta_->redundant_count) + diff); } SampleCountIterator::~SampleCountIterator() {}
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h index 54185cf..98e372b 100644 --- a/base/metrics/histogram_samples.h +++ b/base/metrics/histogram_samples.h
@@ -5,6 +5,7 @@ #ifndef BASE_METRICS_HISTOGRAM_SAMPLES_H_ #define BASE_METRICS_HISTOGRAM_SAMPLES_H_ +#include "base/atomicops.h" #include "base/basictypes.h" #include "base/metrics/histogram_base.h" #include "base/memory/scoped_ptr.h" @@ -18,7 +19,35 @@ // HistogramSamples is a container storing all samples of a histogram. class BASE_EXPORT HistogramSamples { public: - HistogramSamples(); + struct Metadata { + // Initialized when the sample-set is first created with a value provided + // by the caller. It is generally used to identify the sample-set across + // threads and processes, though not necessarily uniquely as it is possible + // to have multiple sample-sets representing subsets of the data. + uint64_t id; + + // The sum of all the entries, effectivly the sum(sample * count) for + // all samples. Despite being atomic, no guarantees are made on the + // accuracy of this value; there may be races during histogram + // accumulation and snapshotting that we choose to accept. It should + // be treated as approximate. + // TODO(bcwhite): Change this to std::atomic<int64_t>. + int64_t sum; + + // A "redundant" count helps identify memory corruption. It redundantly + // stores the total number of samples accumulated in the histogram. We + // can compare this count to the sum of the counts (TotalCount() function), + // and detect problems. Note, depending on the implementation of different + // histogram types, there might be races during histogram accumulation + // and snapshotting that we choose to accept. In this case, the tallies + // might mismatch even when no memory corruption has happened. + HistogramBase::AtomicCount redundant_count; + + Metadata() : id(0), sum(0), redundant_count(0) {} + }; + + explicit HistogramSamples(uint64_t id); + HistogramSamples(uint64_t id, Metadata* meta); virtual ~HistogramSamples(); virtual void Accumulate(HistogramBase::Sample value, @@ -37,9 +66,10 @@ virtual bool Serialize(Pickle* pickle) const; // Accessor fuctions. - int64 sum() const { return sum_; } + uint64_t id() const { return meta_->id; } + int64_t sum() const { return meta_->sum; } HistogramBase::Count redundant_count() const { - return subtle::NoBarrier_Load(&redundant_count_); + return subtle::NoBarrier_Load(&meta_->redundant_count); } protected: @@ -47,20 +77,17 @@ enum Operator { ADD, SUBTRACT }; virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; - void IncreaseSum(int64 diff); + void IncreaseSum(int64_t diff); void IncreaseRedundantCount(HistogramBase::Count diff); private: - int64 sum_; + // In order to support histograms shared through an external memory segment, + // meta values may be the local storage or external storage depending on the + // wishes of the derived class. + Metadata local_meta_; + Metadata* meta_; - // |redundant_count_| helps identify memory corruption. It redundantly stores - // the total number of samples accumulated in the histogram. We can compare - // this count to the sum of the counts (TotalCount() function), and detect - // problems. Note, depending on the implementation of different histogram - // types, there might be races during histogram accumulation and snapshotting - // that we choose to accept. In this case, the tallies might mismatch even - // when no memory corruption has happened. - HistogramBase::AtomicCount redundant_count_; + DISALLOW_COPY_AND_ASSIGN(HistogramSamples); }; class BASE_EXPORT SampleCountIterator {
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc index a7605aa..02f87f0 100644 --- a/base/metrics/histogram_snapshot_manager.cc +++ b/base/metrics/histogram_snapshot_manager.cc
@@ -41,13 +41,11 @@ // Get up-to-date snapshot of sample stats. scoped_ptr<HistogramSamples> snapshot(histogram.SnapshotSamples()); - const std::string& histogram_name = histogram.histogram_name(); - - int corruption = histogram.FindCorruption(*snapshot); // Crash if we detect that our histograms have been overwritten. This may be // a fair distance from the memory smasher, but we hope to correlate these // crashes with other events, such as plugins, or usage patterns, etc. + int corruption = histogram.FindCorruption(*snapshot); if (HistogramBase::BUCKET_ORDER_ERROR & corruption) { // The checksum should have caught this, so crash separately if it didn't. CHECK_NE(0, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); @@ -59,29 +57,29 @@ // Note, at this point corruption can only be COUNT_HIGH_ERROR or // COUNT_LOW_ERROR and they never arise together, so we don't need to extract // bits from corruption. + const uint64_t histogram_hash = histogram.name_hash(); if (corruption) { - DLOG(ERROR) << "Histogram: " << histogram_name + DLOG(ERROR) << "Histogram: " << histogram.histogram_name() << " has data corruption: " << corruption; histogram_flattener_->InconsistencyDetected( static_cast<HistogramBase::Inconsistency>(corruption)); // Don't record corrupt data to metrics services. - int old_corruption = inconsistencies_[histogram_name]; + int old_corruption = inconsistencies_[histogram_hash]; if (old_corruption == (corruption | old_corruption)) return; // We've already seen this corruption for this histogram. - inconsistencies_[histogram_name] |= corruption; + inconsistencies_[histogram_hash] |= corruption; histogram_flattener_->UniqueInconsistencyDetected( static_cast<HistogramBase::Inconsistency>(corruption)); return; } HistogramSamples* to_log; - std::map<std::string, HistogramSamples*>::iterator it = - logged_samples_.find(histogram_name); + auto it = logged_samples_.find(histogram_hash); if (it == logged_samples_.end()) { to_log = snapshot.release(); // This histogram has not been logged before, add a new entry. - logged_samples_[histogram_name] = to_log; + logged_samples_[histogram_hash] = to_log; } else { HistogramSamples* already_logged = it->second; InspectLoggedSamplesInconsistency(*snapshot, already_logged);
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h index 5a5f2e93..867049f 100644 --- a/base/metrics/histogram_snapshot_manager.h +++ b/base/metrics/histogram_snapshot_manager.h
@@ -48,10 +48,12 @@ // For histograms, track what we've already recorded (as a sample for // each histogram) so that we can record only the delta with the next log. - std::map<std::string, HistogramSamples*> logged_samples_; + // The information is indexed by the hash of the histogram name. + std::map<uint64_t, HistogramSamples*> logged_samples_; - // List of histograms found to be corrupt, and their problems. - std::map<std::string, int> inconsistencies_; + // Set of histograms found to be corrupt and their problems, indexed + // by the hash of the histogram name. + std::map<uint64_t, int> inconsistencies_; // |histogram_flattener_| handles the logistics of recording the histogram // deltas.
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc index b144379..daa0398 100644 --- a/base/metrics/histogram_unittest.cc +++ b/base/metrics/histogram_unittest.cc
@@ -81,7 +81,7 @@ } TEST_F(HistogramTest, ExponentialRangesTest) { - // Check that we got a nice exponential when there was enough rooom. + // Check that we got a nice exponential when there was enough room. BucketRanges ranges(9); Histogram::InitializeBucketRanges(1, 64, &ranges); EXPECT_EQ(0, ranges.range(0));
diff --git a/base/metrics/sample_map.cc b/base/metrics/sample_map.cc index f2540a4..a691243 100644 --- a/base/metrics/sample_map.cc +++ b/base/metrics/sample_map.cc
@@ -11,7 +11,9 @@ typedef HistogramBase::Count Count; typedef HistogramBase::Sample Sample; -SampleMap::SampleMap() {} +SampleMap::SampleMap() : SampleMap(0) {} + +SampleMap::SampleMap(uint64_t id) : HistogramSamples(id) {} SampleMap::~SampleMap() {}
diff --git a/base/metrics/sample_map.h b/base/metrics/sample_map.h index d1d7cd6..95dc126 100644 --- a/base/metrics/sample_map.h +++ b/base/metrics/sample_map.h
@@ -20,6 +20,7 @@ class BASE_EXPORT SampleMap : public HistogramSamples { public: SampleMap(); + explicit SampleMap(uint64_t id); ~SampleMap() override; // HistogramSamples implementation:
diff --git a/base/metrics/sample_map_unittest.cc b/base/metrics/sample_map_unittest.cc index 22ce8e1c..c941d65 100644 --- a/base/metrics/sample_map_unittest.cc +++ b/base/metrics/sample_map_unittest.cc
@@ -11,7 +11,7 @@ namespace { TEST(SampleMapTest, AccumulateTest) { - SampleMap samples; + SampleMap samples(1); samples.Accumulate(1, 100); samples.Accumulate(2, 200); @@ -25,8 +25,8 @@ } TEST(SampleMapTest, AddSubtractTest) { - SampleMap samples1; - SampleMap samples2; + SampleMap samples1(1); + SampleMap samples2(2); samples1.Accumulate(1, 100); samples1.Accumulate(2, 100); @@ -56,7 +56,7 @@ } TEST(SampleMapIteratorTest, IterateTest) { - SampleMap samples; + SampleMap samples(1); samples.Accumulate(1, 100); samples.Accumulate(2, 200); samples.Accumulate(4, -300); @@ -91,14 +91,14 @@ } TEST(SampleMapIteratorTest, SkipEmptyRanges) { - SampleMap samples; + SampleMap samples(1); samples.Accumulate(5, 1); samples.Accumulate(10, 2); samples.Accumulate(15, 3); samples.Accumulate(20, 4); samples.Accumulate(25, 5); - SampleMap samples2; + SampleMap samples2(2); samples2.Accumulate(5, 1); samples2.Accumulate(20, 4); samples2.Accumulate(25, 5); @@ -132,7 +132,7 @@ #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST TEST(SampleMapIteratorDeathTest, IterateDoneTest) { - SampleMap samples; + SampleMap samples(1); scoped_ptr<SampleCountIterator> it = samples.Iterator();
diff --git a/base/metrics/sample_vector.cc b/base/metrics/sample_vector.cc index 1202527..46faef0 100644 --- a/base/metrics/sample_vector.cc +++ b/base/metrics/sample_vector.cc
@@ -13,11 +13,30 @@ typedef HistogramBase::Sample Sample; SampleVector::SampleVector(const BucketRanges* bucket_ranges) - : counts_(bucket_ranges->bucket_count()), + : SampleVector(0, bucket_ranges) {} + +SampleVector::SampleVector(uint64_t id, const BucketRanges* bucket_ranges) + : HistogramSamples(id), + local_counts_(bucket_ranges->bucket_count()), + counts_(&local_counts_[0]), + counts_size_(local_counts_.size()), bucket_ranges_(bucket_ranges) { CHECK_GE(bucket_ranges_->bucket_count(), 1u); } +SampleVector::SampleVector(uint64_t id, + HistogramBase::AtomicCount* counts, + size_t counts_size, + Metadata* meta, + const BucketRanges* bucket_ranges) + : HistogramSamples(id, meta), + counts_(counts), + counts_size_(bucket_ranges->bucket_count()), + bucket_ranges_(bucket_ranges) { + CHECK_LE(bucket_ranges_->bucket_count(), counts_size_); + CHECK_GE(bucket_ranges_->bucket_count(), 1u); +} + SampleVector::~SampleVector() {} void SampleVector::Accumulate(Sample value, Count count) { @@ -35,20 +54,20 @@ Count SampleVector::TotalCount() const { Count count = 0; - for (size_t i = 0; i < counts_.size(); i++) { + for (size_t i = 0; i < counts_size_; i++) { count += subtle::NoBarrier_Load(&counts_[i]); } return count; } Count SampleVector::GetCountAtIndex(size_t bucket_index) const { - DCHECK(bucket_index < counts_.size()); + DCHECK(bucket_index < counts_size_); return subtle::NoBarrier_Load(&counts_[bucket_index]); } scoped_ptr<SampleCountIterator> SampleVector::Iterator() const { return scoped_ptr<SampleCountIterator>( - new SampleVectorIterator(&counts_, bucket_ranges_)); + new SampleVectorIterator(counts_, counts_size_, bucket_ranges_)); } bool SampleVector::AddSubtractImpl(SampleCountIterator* iter, @@ -59,7 +78,7 @@ // Go through the iterator and add the counts into correct bucket. size_t index = 0; - while (index < counts_.size() && !iter->Done()) { + while (index < counts_size_ && !iter->Done()) { iter->Get(&min, &max, &count); if (min == bucket_ranges_->range(index) && max == bucket_ranges_->range(index + 1)) { @@ -109,19 +128,33 @@ return mid; } -SampleVectorIterator::SampleVectorIterator(const std::vector<Count>* counts, - const BucketRanges* bucket_ranges) - : counts_(counts), +SampleVectorIterator::SampleVectorIterator( + const std::vector<HistogramBase::AtomicCount>* counts, + const BucketRanges* bucket_ranges) + : counts_(&(*counts)[0]), + counts_size_(counts->size()), bucket_ranges_(bucket_ranges), index_(0) { - CHECK_GE(bucket_ranges_->bucket_count(), counts_->size()); + CHECK_GE(bucket_ranges_->bucket_count(), counts_size_); + SkipEmptyBuckets(); +} + +SampleVectorIterator::SampleVectorIterator( + const HistogramBase::AtomicCount* counts, + size_t counts_size, + const BucketRanges* bucket_ranges) + : counts_(counts), + counts_size_(counts_size), + bucket_ranges_(bucket_ranges), + index_(0) { + CHECK_GE(bucket_ranges_->bucket_count(), counts_size_); SkipEmptyBuckets(); } SampleVectorIterator::~SampleVectorIterator() {} bool SampleVectorIterator::Done() const { - return index_ >= counts_->size(); + return index_ >= counts_size_; } void SampleVectorIterator::Next() { @@ -139,7 +172,7 @@ if (max != NULL) *max = bucket_ranges_->range(index_ + 1); if (count != NULL) - *count = subtle::NoBarrier_Load(&(*counts_)[index_]); + *count = subtle::NoBarrier_Load(&counts_[index_]); } bool SampleVectorIterator::GetBucketIndex(size_t* index) const { @@ -153,8 +186,8 @@ if (Done()) return; - while (index_ < counts_->size()) { - if (subtle::NoBarrier_Load(&(*counts_)[index_]) != 0) + while (index_ < counts_size_) { + if (subtle::NoBarrier_Load(&counts_[index_]) != 0) return; index_++; }
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h index 8cb2ab9..51803b0 100644 --- a/base/metrics/sample_vector.h +++ b/base/metrics/sample_vector.h
@@ -8,6 +8,7 @@ #ifndef BASE_METRICS_SAMPLE_VECTOR_H_ #define BASE_METRICS_SAMPLE_VECTOR_H_ +#include <stdint.h> #include <vector> #include "base/compiler_specific.h" @@ -23,6 +24,12 @@ class BASE_EXPORT SampleVector : public HistogramSamples { public: explicit SampleVector(const BucketRanges* bucket_ranges); + SampleVector(uint64_t id, const BucketRanges* bucket_ranges); + SampleVector(uint64_t id, + HistogramBase::AtomicCount* counts, + size_t counts_size, + Metadata* meta, + const BucketRanges* bucket_ranges); ~SampleVector() override; // HistogramSamples implementation: @@ -45,7 +52,14 @@ private: FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); - std::vector<HistogramBase::AtomicCount> counts_; + // In the case where this class manages the memory, here it is. + std::vector<HistogramBase::AtomicCount> local_counts_; + + // These are raw pointers rather than objects for flexibility. The actual + // memory is either managed by local_counts_ above or by an external object + // and passed in directly. + HistogramBase::AtomicCount* counts_; + size_t counts_size_; // Shares the same BucketRanges with Histogram object. const BucketRanges* const bucket_ranges_; @@ -57,6 +71,9 @@ public: SampleVectorIterator(const std::vector<HistogramBase::AtomicCount>* counts, const BucketRanges* bucket_ranges); + SampleVectorIterator(const HistogramBase::AtomicCount* counts, + size_t counts_size, + const BucketRanges* bucket_ranges); ~SampleVectorIterator() override; // SampleCountIterator implementation: @@ -72,7 +89,8 @@ private: void SkipEmptyBuckets(); - const std::vector<HistogramBase::AtomicCount>* counts_; + const HistogramBase::AtomicCount* counts_; + size_t counts_size_; const BucketRanges* bucket_ranges_; size_t index_;
diff --git a/base/metrics/sample_vector_unittest.cc b/base/metrics/sample_vector_unittest.cc index fd42376..8bd80dd0 100644 --- a/base/metrics/sample_vector_unittest.cc +++ b/base/metrics/sample_vector_unittest.cc
@@ -20,7 +20,7 @@ ranges.set_range(0, 1); ranges.set_range(1, 5); ranges.set_range(2, 10); - SampleVector samples(&ranges); + SampleVector samples(1, &ranges); samples.Accumulate(1, 200); samples.Accumulate(2, -300); @@ -50,7 +50,7 @@ ranges.set_range(3, 3); ranges.set_range(4, INT_MAX); - SampleVector samples1(&ranges); + SampleVector samples1(1, &ranges); samples1.Accumulate(0, 100); samples1.Accumulate(2, 100); samples1.Accumulate(4, 100); @@ -58,7 +58,7 @@ EXPECT_EQ(300, samples1.TotalCount()); EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); - SampleVector samples2(&ranges); + SampleVector samples2(2, &ranges); samples2.Accumulate(1, 200); samples2.Accumulate(2, 200); samples2.Accumulate(4, 200); @@ -91,7 +91,7 @@ // [0, 1) [1, 2) [2, 4) [4, 8) [8, 16) [16, 32) [32, 64) [64, INT_MAX) BucketRanges ranges(9); Histogram::InitializeBucketRanges(1, 64, &ranges); - SampleVector samples(&ranges); + SampleVector samples(1, &ranges); // Normal case samples.Accumulate(0, 1); @@ -113,7 +113,7 @@ ranges2.set_range(0, 1); ranges2.set_range(1, 5); ranges2.set_range(2, 10); - SampleVector samples2(&ranges2); + SampleVector samples2(2, &ranges2); // Normal case. samples2.Accumulate(1, 1); @@ -134,7 +134,7 @@ ranges1.set_range(0, 1); ranges1.set_range(1, 3); ranges1.set_range(2, 5); - SampleVector samples1(&ranges1); + SampleVector samples1(1, &ranges1); // Custom buckets 2: [0, 1) [1, 3) [3, 6) [6, 7) BucketRanges ranges2(5); @@ -143,7 +143,7 @@ ranges2.set_range(2, 3); ranges2.set_range(3, 6); ranges2.set_range(4, 7); - SampleVector samples2(&ranges2); + SampleVector samples2(2, &ranges2); samples2.Accumulate(1, 100); samples1.Add(samples2); @@ -209,7 +209,7 @@ EXPECT_TRUE(it.Done()); // Create iterator from SampleVector. - SampleVector samples(&ranges); + SampleVector samples(1, &ranges); samples.Accumulate(0, 0); samples.Accumulate(1, 1); samples.Accumulate(2, 2); @@ -239,7 +239,7 @@ ranges.set_range(2, 2); ranges.set_range(3, 3); ranges.set_range(4, INT_MAX); - SampleVector samples(&ranges); + SampleVector samples(1, &ranges); scoped_ptr<SampleCountIterator> it = samples.Iterator();
diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc index 7955c57..db76a8a 100644 --- a/base/metrics/sparse_histogram.cc +++ b/base/metrics/sparse_histogram.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/metrics/metrics_hashes.h" #include "base/metrics/sample_map.h" #include "base/metrics/statistics_recorder.h" #include "base/pickle.h" @@ -35,6 +36,10 @@ SparseHistogram::~SparseHistogram() {} +uint64_t SparseHistogram::name_hash() const { + return samples_.id(); +} + HistogramType SparseHistogram::GetHistogramType() const { return SPARSE_HISTOGRAM; } @@ -65,7 +70,7 @@ } scoped_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const { - scoped_ptr<SampleMap> snapshot(new SampleMap()); + scoped_ptr<SampleMap> snapshot(new SampleMap(name_hash())); base::AutoLock auto_lock(lock_); snapshot->Add(samples_); @@ -97,7 +102,8 @@ } SparseHistogram::SparseHistogram(const std::string& name) - : HistogramBase(name) {} + : HistogramBase(name), + samples_(HashMetricName(name)) {} HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) { std::string histogram_name;
diff --git a/base/metrics/sparse_histogram.h b/base/metrics/sparse_histogram.h index 241d975..1c35ed8 100644 --- a/base/metrics/sparse_histogram.h +++ b/base/metrics/sparse_histogram.h
@@ -37,6 +37,7 @@ ~SparseHistogram() override; // HistogramBase implementation: + uint64_t name_hash() const override; HistogramType GetHistogramType() const override; bool HasConstructionArguments(Sample expected_minimum, Sample expected_maximum,
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc index 87ffa3d..15e48d8ae 100644 --- a/base/metrics/statistics_recorder.cc +++ b/base/metrics/statistics_recorder.cc
@@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" +#include "base/metrics/metrics_hashes.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" @@ -58,9 +59,10 @@ histogram_to_return = histogram; } else { const std::string& name = histogram->histogram_name(); - HistogramMap::iterator it = histograms_->find(HistogramNameRef(name)); + uint64_t name_hash = histogram->name_hash(); + HistogramMap::iterator it = histograms_->find(name_hash); if (histograms_->end() == it) { - (*histograms_)[HistogramNameRef(name)] = histogram; + (*histograms_)[name_hash] = histogram; ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 // If there are callbacks for this histogram, we set the kCallbackExists // flag. @@ -77,6 +79,8 @@ histogram_to_return = histogram; } else { // We already have one histogram with this name. + DCHECK_EQ(histogram->histogram_name(), + it->second->histogram_name()) << "hash collision"; histogram_to_return = it->second; histogram_to_delete = histogram; } @@ -200,7 +204,7 @@ return; for (const auto& entry : *histograms_) { - DCHECK_EQ(entry.first.name_, entry.second->histogram_name()); + DCHECK_EQ(entry.first, entry.second->name_hash()); output->push_back(entry.second); } } @@ -229,9 +233,10 @@ if (histograms_ == NULL) return NULL; - HistogramMap::iterator it = histograms_->find(HistogramNameRef(name)); + HistogramMap::iterator it = histograms_->find(HashMetricName(name)); if (histograms_->end() == it) return NULL; + DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; return it->second; } @@ -250,9 +255,11 @@ return false; callbacks_->insert(std::make_pair(name, cb)); - auto histogram_iterator = histograms_->find(HistogramNameRef(name)); - if (histogram_iterator != histograms_->end()) - histogram_iterator->second->SetFlags(HistogramBase::kCallbackExists); + HistogramMap::iterator it = histograms_->find(HashMetricName(name)); + if (it != histograms_->end()) { + DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; + it->second->SetFlags(HistogramBase::kCallbackExists); + } return true; } @@ -268,9 +275,11 @@ callbacks_->erase(name); // We also clear the flag from the histogram (if it exists). - auto histogram_iterator = histograms_->find(HistogramNameRef(name)); - if (histogram_iterator != histograms_->end()) - histogram_iterator->second->ClearFlags(HistogramBase::kCallbackExists); + HistogramMap::iterator it = histograms_->find(HashMetricName(name)); + if (it != histograms_->end()) { + DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; + it->second->ClearFlags(HistogramBase::kCallbackExists); + } } // static @@ -297,7 +306,7 @@ return; for (const auto& entry : *histograms_) { - if (entry.first.name_.find(query) != std::string::npos) + if (entry.second->histogram_name().find(query) != std::string::npos) snapshot->push_back(entry.second); } }
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h index 0e5168f..5771abcb 100644 --- a/base/metrics/statistics_recorder.h +++ b/base/metrics/statistics_recorder.h
@@ -95,24 +95,9 @@ static OnSampleCallback FindCallback(const std::string& histogram_name); private: - // HistogramNameRef holds a weak const ref to the name field of the associated - // Histogram object, allowing re-use of the underlying string storage for the - // map keys. The wrapper is required as using "const std::string&" as the key - // results in compile errors. - struct HistogramNameRef { - explicit HistogramNameRef(const std::string& name) : name_(name) {}; - - // Operator < is necessary to use this type as a std::map key. - bool operator<(const HistogramNameRef& other) const { - return name_ < other.name_; - } - - // Weak, owned by the associated Histogram object. - const std::string& name_; - }; - - // We keep all registered histograms in a map, from name to histogram. - typedef std::map<HistogramNameRef, HistogramBase*> HistogramMap; + // We keep all registered histograms in a map, indexed by the hash of the + // name of the histogram. + typedef std::map<uint64_t, HistogramBase*> HistogramMap; // We keep a map of callbacks to histograms, so that as histograms are // created, we can set the callback properly.
diff --git a/base/test/histogram_tester.cc b/base/test/histogram_tester.cc index 7fcde6d..8ba09847 100644 --- a/base/test/histogram_tester.cc +++ b/base/test/histogram_tester.cc
@@ -6,6 +6,7 @@ #include "base/metrics/histogram.h" #include "base/metrics/histogram_samples.h" +#include "base/metrics/metrics_hashes.h" #include "base/metrics/sample_map.h" #include "base/metrics/statistics_recorder.h" #include "base/stl_util.h" @@ -121,8 +122,10 @@ // response which is independent of the previously run tests, this method // creates empty samples in the absence of the histogram, rather than // returning null. - if (!histogram) - return scoped_ptr<HistogramSamples>(new SampleMap); + if (!histogram) { + return scoped_ptr<HistogramSamples>( + new SampleMap(HashMetricName(histogram_name))); + } scoped_ptr<HistogramSamples> named_samples(histogram->SnapshotSamples()); auto original_samples_it = histograms_snapshot_.find(histogram_name); if (original_samples_it != histograms_snapshot_.end())
diff --git a/blimp/client/compositor/blimp_layer_tree_settings.cc b/blimp/client/compositor/blimp_layer_tree_settings.cc index e80becd1..029e328 100644 --- a/blimp/client/compositor/blimp_layer_tree_settings.cc +++ b/blimp/client/compositor/blimp_layer_tree_settings.cc
@@ -66,13 +66,12 @@ settings->renderer_settings.highp_threshold_min = 2048; settings->ignore_root_layer_flings = false; bool use_low_memory_policy = base::SysInfo::IsLowEndDevice(); + settings->renderer_settings.use_rgba_4444_textures = use_low_memory_policy; if (use_low_memory_policy) { // On low-end we want to be very carefull about killing other // apps. So initially we use 50% more memory to avoid flickering // or raster-on-demand. settings->max_memory_for_prepaint_percentage = 67; - - settings->renderer_settings.preferred_tile_format = cc::RGBA_4444; } else { // On other devices we have increased memory excessively to avoid // raster-on-demand already, so now we reserve 50% _only_ to avoid
diff --git a/build/android/provision_devices.py b/build/android/provision_devices.py index caa9e23f6..a474837 100755 --- a/build/android/provision_devices.py +++ b/build/android/provision_devices.py
@@ -27,6 +27,7 @@ from devil.android import device_errors from devil.android import device_temp_file from devil.android import device_utils +from devil.android.sdk import keyevent from devil.android.sdk import version_codes from devil.utils import run_tests_helper from devil.utils import timeout_retry @@ -104,8 +105,8 @@ try: if should_run_phase(_PHASES.WIPE): - if (options.chrome_specific_wipe or device.build_version_sdk >= - version_codes.MARSHMALLOW): + if (options.chrome_specific_wipe or device.IsUserBuild() or + device.build_version_sdk >= version_codes.MARSHMALLOW): run_phase(WipeChromeData) else: run_phase(WipeDevice) @@ -175,19 +176,25 @@ return try: - device.EnableRoot() - _UninstallIfMatch(device, _CHROME_PACKAGE_REGEX, - constants.PACKAGE_INFO['chrome_stable'].package) - _WipeUnderDirIfMatch(device, '/data/app-lib/', _CHROME_PACKAGE_REGEX) - _WipeUnderDirIfMatch(device, '/data/tombstones/', _TOMBSTONE_REGEX) + if device.IsUserBuild(): + _UninstallIfMatch(device, _CHROME_PACKAGE_REGEX, + constants.PACKAGE_INFO['chrome_stable'].package) + device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(), + check_return=True) + device.RunShellCommand('rm -rf /data/local/tmp/*', check_return=True) + else: + device.EnableRoot() + _UninstallIfMatch(device, _CHROME_PACKAGE_REGEX, + constants.PACKAGE_INFO['chrome_stable'].package) + _WipeUnderDirIfMatch(device, '/data/app-lib/', _CHROME_PACKAGE_REGEX) + _WipeUnderDirIfMatch(device, '/data/tombstones/', _TOMBSTONE_REGEX) - _WipeFileOrDir(device, '/data/local.prop') - _WipeFileOrDir(device, '/data/local/chrome-command-line') - _WipeFileOrDir(device, '/data/local/.config/') - _WipeFileOrDir(device, '/data/local/tmp/') - - device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(), - check_return=True) + _WipeFileOrDir(device, '/data/local.prop') + _WipeFileOrDir(device, '/data/local/chrome-command-line') + _WipeFileOrDir(device, '/data/local/.config/') + _WipeFileOrDir(device, '/data/local/tmp/') + device.RunShellCommand('rm -rf %s/*' % device.GetExternalStoragePath(), + check_return=True) except device_errors.CommandFailedError: logging.exception('Possible failure while wiping the device. ' 'Attempting to continue.') @@ -250,7 +257,10 @@ except device_errors.CommandFailedError as e: logging.warning(str(e)) - _ConfigureLocalProperties(device, options.enable_java_debug) + if not device.IsUserBuild(): + _ConfigureLocalProperties(device, options.enable_java_debug) + else: + logging.warning('Cannot configure properties in user builds.') device_settings.ConfigureContentSettings( device, device_settings.DETERMINISTIC_DEVICE_SETTINGS) if options.disable_location: @@ -315,6 +325,11 @@ def FinishProvisioning(device, options): + # The lockscreen can't be disabled on user builds, so send a keyevent + # to unlock it. + if device.IsUserBuild(): + device.SendKeyEvent(keyevent.KEYCODE_MENU) + if options.min_battery_level is not None: try: battery = battery_utils.BatteryUtils(device) @@ -355,10 +370,14 @@ return False # Sometimes the date is not set correctly on the devices. Retry on failure. - if not timeout_retry.WaitFor(_set_and_verify_date, wait_period=1, - max_tries=2): - raise device_errors.CommandFailedError( - 'Failed to set date & time.', device_serial=str(device)) + if device.IsUserBuild(): + # TODO(bpastene): Figure out how to set the date & time on user builds. + pass + else: + if not timeout_retry.WaitFor( + _set_and_verify_date, wait_period=1, max_tries=2): + raise device_errors.CommandFailedError( + 'Failed to set date & time.', device_serial=str(device)) props = device.RunShellCommand('getprop', check_return=True) for prop in props:
diff --git a/build/android/pylib/device_settings.py b/build/android/pylib/device_settings.py index 1147c0242..36ac5be3 100644 --- a/build/android/pylib/device_settings.py +++ b/build/android/pylib/device_settings.py
@@ -28,14 +28,13 @@ desired_settings: A list of (table, [(key: value), ...]) for all settings to configure. """ - if device.build_type in _COMPATIBLE_BUILD_TYPES: - for table, key_value in desired_settings: - settings = content_settings.ContentSettings(table, device) - for key, value in key_value: - settings[key] = value - logging.info('\n%s %s', table, (80 - len(table)) * '-') - for key, value in sorted(settings.iteritems()): - logging.info('\t%s: %s', key, value) + for table, key_value in desired_settings: + settings = content_settings.ContentSettings(table, device) + for key, value in key_value: + settings[key] = value + logging.info('\n%s %s', table, (80 - len(table)) * '-') + for key, value in sorted(settings.iteritems()): + logging.info('\t%s: %s', key, value) def SetLockScreenSettings(device):
diff --git a/build/common.gypi b/build/common.gypi index e51b958..5d02b3c 100644 --- a/build/common.gypi +++ b/build/common.gypi
@@ -2228,8 +2228,7 @@ 'clang_dynlib_flags%': '', }], ], - 'clang_plugin_args%': '-Xclang -plugin-arg-find-bad-constructs -Xclang check-templates ' - '-Xclang -plugin-arg-find-bad-constructs -Xclang enforce-overriding-blink ', + 'clang_plugin_args%': '-Xclang -plugin-arg-find-bad-constructs -Xclang check-templates', }, # If you change these, also change build/config/clang/BUILD.gn. 'clang_chrome_plugins_flags%':
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn index e5604314..02c5f2ca 100644 --- a/build/config/clang/BUILD.gn +++ b/build/config/clang/BUILD.gn
@@ -40,10 +40,6 @@ "-plugin-arg-find-bad-constructs", "-Xclang", "check-templates", - "-Xclang", - "-plugin-arg-find-bad-constructs", - "-Xclang", - "enforce-overriding-blink", ] } }
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc index d0e41e56..cba767b 100644 --- a/build/sanitizers/tsan_suppressions.cc +++ b/build/sanitizers/tsan_suppressions.cc
@@ -186,13 +186,6 @@ // http://crbug.com/345240 "race:WTF::s_shutdown\n" -// http://crbug.com/345245 -"race:jingle_glue::JingleThreadWrapper::~JingleThreadWrapper\n" -"race:webrtc::voe::Channel::UpdatePacketDelay\n" -"race:webrtc::voe::Channel::GetDelayEstimate\n" -"race:webrtc::VCMCodecDataBase::DeregisterReceiveCodec\n" -"race:webrtc::GainControlImpl::set_stream_analog_level\n" - // http://crbug.com/345618 "race:WebCore::AudioDestinationNode::render\n"
diff --git a/cc/base/switches.cc b/cc/base/switches.cc index e73d17a..9cf6d5c 100644 --- a/cc/base/switches.cc +++ b/cc/base/switches.cc
@@ -50,9 +50,6 @@ const char kEnablePropertyTreeVerification[] = "enable-property-tree-verification"; -// Compress tile textures for GPUs supporting it. -const char kEnableTileCompression[] = "enable-tile-compression"; - // Use a BeginFrame signal from browser to renderer to schedule rendering. const char kEnableBeginFrameScheduling[] = "enable-begin-frame-scheduling";
diff --git a/cc/base/switches.h b/cc/base/switches.h index 52da2c7..d8c67170 100644 --- a/cc/base/switches.h +++ b/cc/base/switches.h
@@ -26,7 +26,6 @@ CC_EXPORT extern const char kSlowDownRasterScaleFactor[]; CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[]; CC_EXPORT extern const char kEnablePropertyTreeVerification[]; -CC_EXPORT extern const char kEnableTileCompression[]; // Switches for both the renderer and ui compositors. CC_EXPORT extern const char kEnableBeginFrameScheduling[];
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index f547648..405d685 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc
@@ -48,10 +48,6 @@ // of using the same tile size. const int kTileRoundUp = 64; -// For performance reasons and to support compressed tile textures, tile -// width and height should be an even multiple of 4 in size. -const int kTileMinimalAlignment = 4; - } // namespace namespace cc { @@ -771,10 +767,6 @@ tile_height = std::min(tile_height, default_tile_height); } - // Ensure that tile width and height are properly aligned. - tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileMinimalAlignment); - tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileMinimalAlignment); - // Under no circumstance should we be larger than the max texture size. tile_width = std::min(tile_width, max_texture_size); tile_height = std::min(tile_height, max_texture_size);
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index a437074..1839690c 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -4984,10 +4984,8 @@ layer->set_gpu_raster_max_texture_size(host_impl_.device_viewport_size()); result = layer->CalculateTileSize(gfx::Size(10000, 10000)); - EXPECT_EQ(result.width(), - MathUtil::UncheckedRoundUp( - 2000 + 2 * PictureLayerTiling::kBorderTexels, 4)); - EXPECT_EQ(result.height(), 504); // 500 + 2, 4-byte aligned. + EXPECT_EQ(result.width(), 2000 + 2 * PictureLayerTiling::kBorderTexels); + EXPECT_EQ(result.height(), 500 + 2); // Clamp and round-up, when smaller than viewport. // Tile-height doubles to 50% when width shrinks to <= 50%. @@ -4995,7 +4993,7 @@ layer->set_gpu_raster_max_texture_size(host_impl_.device_viewport_size()); result = layer->CalculateTileSize(gfx::Size(447, 10000)); EXPECT_EQ(result.width(), 448); - EXPECT_EQ(result.height(), 504); // 500 + 2, 4-byte aliged. + EXPECT_EQ(result.height(), 500 + 2); // Largest layer is 50% of viewport width (rounded up), and // 50% of viewport in height. @@ -5004,7 +5002,7 @@ EXPECT_EQ(result.height(), 448); result = layer->CalculateTileSize(gfx::Size(500, 499)); EXPECT_EQ(result.width(), 512); - EXPECT_EQ(result.height(), 504); // 500 + 2, 4-byte aligned. + EXPECT_EQ(result.height(), 500 + 2); } TEST_F(NoLowResPictureLayerImplTest, LowResWasHighResCollision) {
diff --git a/cc/output/renderer_settings.cc b/cc/output/renderer_settings.cc index 4c747101..da79a48 100644 --- a/cc/output/renderer_settings.cc +++ b/cc/output/renderer_settings.cc
@@ -8,7 +8,6 @@ #include "base/logging.h" #include "cc/proto/renderer_settings.pb.h" -#include "cc/resources/platform_color.h" namespace cc { @@ -23,9 +22,9 @@ delay_releasing_overlay_resources(false), refresh_rate(60.0), highp_threshold_min(0), + use_rgba_4444_textures(false), texture_id_allocation_chunk_size(64), - use_gpu_memory_buffer_resources(false), - preferred_tile_format(PlatformColor::BestTextureFormat()) {} + use_gpu_memory_buffer_resources(false) {} RendererSettings::~RendererSettings() { } @@ -42,9 +41,9 @@ delay_releasing_overlay_resources); proto->set_refresh_rate(refresh_rate); proto->set_highp_threshold_min(highp_threshold_min); + proto->set_use_rgba_4444_textures(use_rgba_4444_textures); proto->set_texture_id_allocation_chunk_size(texture_id_allocation_chunk_size); proto->set_use_gpu_memory_buffer_resources(use_gpu_memory_buffer_resources); - proto->set_preferred_tile_format(preferred_tile_format); } void RendererSettings::FromProtobuf(const proto::RendererSettings& proto) { @@ -58,13 +57,9 @@ delay_releasing_overlay_resources = proto.delay_releasing_overlay_resources(); refresh_rate = proto.refresh_rate(); highp_threshold_min = proto.highp_threshold_min(); + use_rgba_4444_textures = proto.use_rgba_4444_textures(); texture_id_allocation_chunk_size = proto.texture_id_allocation_chunk_size(); use_gpu_memory_buffer_resources = proto.use_gpu_memory_buffer_resources(); - - DCHECK_LE(proto.preferred_tile_format(), - static_cast<uint32_t>(RESOURCE_FORMAT_MAX)); - preferred_tile_format = - static_cast<ResourceFormat>(proto.preferred_tile_format()); } bool RendererSettings::operator==(const RendererSettings& other) const { @@ -79,11 +74,11 @@ other.delay_releasing_overlay_resources && refresh_rate == other.refresh_rate && highp_threshold_min == other.highp_threshold_min && + use_rgba_4444_textures == other.use_rgba_4444_textures && texture_id_allocation_chunk_size == other.texture_id_allocation_chunk_size && use_gpu_memory_buffer_resources == - other.use_gpu_memory_buffer_resources && - preferred_tile_format == other.preferred_tile_format; + other.use_gpu_memory_buffer_resources; } } // namespace cc
diff --git a/cc/output/renderer_settings.h b/cc/output/renderer_settings.h index 3d7bf10..4910a25 100644 --- a/cc/output/renderer_settings.h +++ b/cc/output/renderer_settings.h
@@ -7,7 +7,6 @@ #include "base/basictypes.h" #include "cc/base/cc_export.h" -#include "cc/resources/resource_format.h" namespace cc { @@ -30,9 +29,9 @@ bool delay_releasing_overlay_resources; double refresh_rate; int highp_threshold_min; + bool use_rgba_4444_textures; size_t texture_id_allocation_chunk_size; bool use_gpu_memory_buffer_resources; - ResourceFormat preferred_tile_format; void ToProtobuf(proto::RendererSettings* proto) const; void FromProtobuf(const proto::RendererSettings& proto);
diff --git a/cc/output/renderer_settings_unittest.cc b/cc/output/renderer_settings_unittest.cc index 40d4f072..b7febd41 100644 --- a/cc/output/renderer_settings_unittest.cc +++ b/cc/output/renderer_settings_unittest.cc
@@ -30,9 +30,9 @@ settings.delay_releasing_overlay_resources = true; settings.refresh_rate = 6.0; settings.highp_threshold_min = 1; + settings.use_rgba_4444_textures = true; settings.texture_id_allocation_chunk_size = 46; settings.use_gpu_memory_buffer_resources = true; - settings.preferred_tile_format = RGBA_4444; VerifySerializeAndDeserializeProto(settings); } @@ -48,9 +48,9 @@ settings.delay_releasing_overlay_resources = true; settings.refresh_rate = 999.0; settings.highp_threshold_min = 1; + settings.use_rgba_4444_textures = true; settings.texture_id_allocation_chunk_size = 12; settings.use_gpu_memory_buffer_resources = true; - settings.preferred_tile_format = RGBA_4444; VerifySerializeAndDeserializeProto(settings); }
diff --git a/cc/proto/renderer_settings.proto b/cc/proto/renderer_settings.proto index ab6c4c1..6666f500 100644 --- a/cc/proto/renderer_settings.proto +++ b/cc/proto/renderer_settings.proto
@@ -19,7 +19,7 @@ optional bool delay_releasing_overlay_resources = 8; optional double refresh_rate = 9; optional uint32 highp_threshold_min = 10; - optional uint32 texture_id_allocation_chunk_size = 11; - optional bool use_gpu_memory_buffer_resources = 12; - optional uint32 preferred_tile_format = 13; + optional bool use_rgba_4444_textures = 11; + optional uint32 texture_id_allocation_chunk_size = 12; + optional bool use_gpu_memory_buffer_resources = 13; }
diff --git a/cc/quads/draw_polygon.cc b/cc/quads/draw_polygon.cc index b4f132a8..b279fe6 100644 --- a/cc/quads/draw_polygon.cc +++ b/cc/quads/draw_polygon.cc
@@ -70,7 +70,7 @@ for (int i = 0; i < num_vertices_in_clipped_quad; i++) { points_.push_back(points[i]); } - ApplyTransformToNormal(transform); + ConstructNormal(); } DrawPolygon::~DrawPolygon() { @@ -88,6 +88,37 @@ return new_polygon; } +// +// If this were to be more generally used and expected to be applicable +// replacing this with Newell's algorithm (or an improvement thereof) +// would be preferable, but usually this is coming in from a rectangle +// that has been transformed to screen space and clipped. +// Averaging a few near diagonal cross products is pretty good in that case. +// +void DrawPolygon::ConstructNormal() { + normal_.set_x(0.0f); + normal_.set_y(0.0f); + normal_.set_z(0.0f); + int delta = points_.size() / 2; + for (size_t i = 1; i + delta < points_.size(); i++) { + normal_ += + CrossProduct(points_[i] - points_[0], points_[i + delta] - points_[0]); + } + float normal_magnitude = normal_.Length(); + if (normal_magnitude != 0 && normal_magnitude != 1) { + normal_.Scale(1.0f / normal_magnitude); + } +} + +#if defined(OS_WIN) +// +// Allows the unittest to invoke this for the more general constructor. +// +void DrawPolygon::RecomputeNormalForTesting() { + ConstructNormal(); +} +#endif + float DrawPolygon::SignedPointDistance(const gfx::Point3F& point) const { return gfx::DotProduct(point - points_[0], normal_); } @@ -210,7 +241,7 @@ // be transformed along with the vertices. void DrawPolygon::TransformToScreenSpace(const gfx::Transform& transform) { ApplyTransform(transform); - ApplyTransformToNormal(transform); + ConstructNormal(); } // In the case of TransformToLayerSpace, we assume that we are giving the
diff --git a/cc/quads/draw_polygon.h b/cc/quads/draw_polygon.h index 93c3602..668d9f2 100644 --- a/cc/quads/draw_polygon.h +++ b/cc/quads/draw_polygon.h
@@ -59,10 +59,14 @@ bool is_split() const { return is_split_; } scoped_ptr<DrawPolygon> CreateCopy(); + void RecomputeNormalForTesting(); + private: void ApplyTransform(const gfx::Transform& transform); void ApplyTransformToNormal(const gfx::Transform& transform); + void ConstructNormal(); + std::vector<gfx::Point3F> points_; // Normalized, necessitated by distance calculations and tests of coplanarity. gfx::Vector3dF normal_;
diff --git a/cc/quads/draw_polygon_unittest.cc b/cc/quads/draw_polygon_unittest.cc index 0fc25e3..95eaf6b 100644 --- a/cc/quads/draw_polygon_unittest.cc +++ b/cc/quads/draw_polygon_unittest.cc
@@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// We would like to use M_PI on windows too. +#ifdef _WIN32 +#define _USE_MATH_DEFINES +#endif + #include <limits> #include <vector> @@ -11,11 +16,22 @@ #include "ui/gfx/transform.h" namespace cc { + +#if !defined(OS_WIN) +void DrawPolygon::RecomputeNormalForTesting() { + ConstructNormal(); +} +#endif + namespace { #define CREATE_NEW_DRAW_POLYGON(name, points_vector, normal, polygon_id) \ DrawPolygon name(NULL, points_vector, normal, polygon_id) +#define CREATE_TEST_DRAW_POLYGON(name, points_vector, polygon_id) \ + DrawPolygon name(NULL, points_vector, gfx::Vector3dF(1, 2, 3), polygon_id); \ + name.RecomputeNormalForTesting() + #define EXPECT_FLOAT_WITHIN_EPSILON_OF(a, b) \ EXPECT_TRUE(std::abs(a - b) < std::numeric_limits<float>::epsilon()); @@ -24,6 +40,11 @@ EXPECT_FLOAT_EQ(point_a.y(), point_b.y()); \ EXPECT_FLOAT_EQ(point_a.z(), point_b.z()); +#define EXPECT_NORMAL(poly, n_x, n_y, n_z) \ + EXPECT_FLOAT_WITHIN_EPSILON_OF(poly.normal().x(), n_x); \ + EXPECT_FLOAT_WITHIN_EPSILON_OF(poly.normal().y(), n_y); \ + EXPECT_FLOAT_WITHIN_EPSILON_OF(poly.normal().z(), n_z); + static void ValidatePoints(const DrawPolygon& polygon, const std::vector<gfx::Point3F>& points) { EXPECT_EQ(polygon.points().size(), points.size()); @@ -32,6 +53,101 @@ } } +// A simple square in a plane. +TEST(DrawPolygonConstructionTest, NormalNormal) { + gfx::Transform Identity; + DrawPolygon polygon(NULL, gfx::RectF(10.0f, 10.0f), Identity, 1); + EXPECT_NORMAL(polygon, 0.0f, 0.0f, 1.0f); +} + +// More complicated shapes. +TEST(DrawPolygonConstructionTest, TestNormal) { + std::vector<gfx::Point3F> vertices; + vertices.push_back(gfx::Point3F(0.0f, 10.0f, 0.0f)); + vertices.push_back(gfx::Point3F(0.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10.0f, 10.0f, 0.0f)); + + CREATE_TEST_DRAW_POLYGON(polygon, vertices, 1); + EXPECT_NORMAL(polygon, 0.0f, 0.0f, 1.0f); +} + +TEST(DrawPolygonConstructionTest, InverseNormal) { + std::vector<gfx::Point3F> vertices; + vertices.push_back(gfx::Point3F(0.0f, 10.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10.0f, 10.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(0.0f, 0.0f, 0.0f)); + + CREATE_TEST_DRAW_POLYGON(polygon, vertices, 1); + EXPECT_NORMAL(polygon, 0.0f, 0.0f, -1.0f); +} + +TEST(DrawPolygonConstructionTest, ClippedNormal) { + std::vector<gfx::Point3F> vertices; + vertices.push_back(gfx::Point3F(0.1f, 10.0f, 0.0f)); + vertices.push_back(gfx::Point3F(0.0f, 9.9f, 0.0f)); + vertices.push_back(gfx::Point3F(0.0f, 10.0f, 0.0f)); + vertices.push_back(gfx::Point3F(0.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10.0f, 10.0f, 0.0f)); + + CREATE_TEST_DRAW_POLYGON(polygon, vertices, 1); + EXPECT_NORMAL(polygon, 0.0f, 0.0f, 1.0f); +} + +TEST(DrawPolygonConstructionTest, SlimTriangleNormal) { + std::vector<gfx::Point3F> vertices; + vertices.push_back(gfx::Point3F(0.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(5000.0f, 0.0f, 0.0f)); + vertices.push_back(gfx::Point3F(10000.0f, 1.0f, 0.0f)); + + CREATE_TEST_DRAW_POLYGON(polygon, vertices, 2); + EXPECT_NORMAL(polygon, 0.0f, 0.0f, 1.0f); +} + +TEST(DrawPolygonConstructionTest, ManyVertexNormal) { + std::vector<gfx::Point3F> vertices_c; + std::vector<gfx::Point3F> vertices_d; + for (int i = 0; i < 100; i++) { + vertices_c.push_back( + gfx::Point3F(cos(i * M_PI / 50), sin(i * M_PI / 50), 0.0f)); + vertices_d.push_back(gfx::Point3F(cos(i * M_PI / 50) + 99.0f, + sin(i * M_PI / 50) + 99.0f, 100.0f)); + } + CREATE_TEST_DRAW_POLYGON(polygon_c, vertices_c, 3); + EXPECT_NORMAL(polygon_c, 0.0f, 0.0f, 1.0f); + + CREATE_TEST_DRAW_POLYGON(polygon_d, vertices_d, 4); + EXPECT_NORMAL(polygon_c, 0.0f, 0.0f, 1.0f); +} + +// A simple rect being transformed. +TEST(DrawPolygonConstructionTest, DizzyNormal) { + gfx::RectF src(-0.1f, -10.0f, 0.2f, 20.0f); + + gfx::Transform transform_i(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + DrawPolygon polygon_i(NULL, src, transform_i, 1); + + EXPECT_NORMAL(polygon_i, 0.0f, 0.0f, 1.0f); + + gfx::Transform tranform_a(0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + DrawPolygon polygon_a(NULL, src, tranform_a, 2); + EXPECT_NORMAL(polygon_a, 0.0f, 0.0f, -1.0f); + + gfx::Transform tranform_b(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1); + DrawPolygon polygon_b(NULL, src, tranform_b, 3); + EXPECT_NORMAL(polygon_b, -1.0f, 0.0f, 0.0f); + + gfx::Transform tranform_c(1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1); + DrawPolygon polygon_c(NULL, src, tranform_c, 4); + EXPECT_NORMAL(polygon_c, 0.0f, -1.0f, 0.0f); + + gfx::Transform tranform_d(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + DrawPolygon polygon_d(NULL, src, tranform_d, 5); + EXPECT_NORMAL(polygon_d, 0.0f, 0.0f, -1.0f); +} + // Two quads are nearly touching but definitely ordered. Second one should // compare in front. TEST(DrawPolygonSplitTest, NearlyTouchingOrder) { @@ -62,7 +178,7 @@ vertices_a.push_back(gfx::Point3F(87.2f, 1196.0f, 0.9f)); gfx::Vector3dF normal_a = gfx::CrossProduct(vertices_a[1] - vertices_a[0], vertices_a[1] - vertices_a[2]); - normal_a.Scale(1.0 / normal_a.Length()); + normal_a.Scale(1.0f / normal_a.Length()); std::vector<gfx::Point3F> vertices_b; vertices_b.push_back(gfx::Point3F(62.1f, 1034.7f, 1.0f)); @@ -71,7 +187,7 @@ vertices_b.push_back(gfx::Point3F(62.1f, 1196.0f, 1.0f)); gfx::Vector3dF normal_b = gfx::CrossProduct(vertices_b[1] - vertices_b[0], vertices_b[1] - vertices_b[2]); - normal_b.Scale(1.0 / normal_b.Length()); + normal_b.Scale(1.0f / normal_b.Length()); CREATE_NEW_DRAW_POLYGON(polygon_a, vertices_a, normal_a, 0); CREATE_NEW_DRAW_POLYGON(polygon_b, vertices_b, normal_b, 1); @@ -211,14 +327,21 @@ } TEST(DrawPolygonTransformTest, TransformNormal) { - // We give this polygon no actual vertices because we're not interested - // in actually transforming any points, just the normal. std::vector<gfx::Point3F> vertices_a; + vertices_a.push_back(gfx::Point3F(1.0f, 0.0f, 1.0f)); + vertices_a.push_back(gfx::Point3F(-1.0f, 0.0f, -1.0f)); + vertices_a.push_back(gfx::Point3F(0.0f, 1.0f, 0.0f)); CREATE_NEW_DRAW_POLYGON( polygon_a, vertices_a, gfx::Vector3dF(0.707107f, 0.0f, -0.707107f), 0); + // Check we believe your little white lie. + EXPECT_NORMAL(polygon_a, 0.707107f, 0.0f, -0.707107f); + + polygon_a.RecomputeNormalForTesting(); + // Check that we recompute it more accurately. + EXPECT_NORMAL(polygon_a, sqrt(2) / 2, 0.0f, -sqrt(2) / 2); gfx::Transform transform; - transform.RotateAboutYAxis(45.0); + transform.RotateAboutYAxis(45.0f); // This would transform the vertices as well, but we are transforming a // DrawPolygon with 0 vertices just to make sure our normal transformation // using the inverse tranpose matrix gives us the right result. @@ -228,9 +351,7 @@ // because some architectures (e.g., Arm64) employ a fused multiply-add // instruction which causes rounding asymmetry and reduces precision. // http://crbug.com/401117. - EXPECT_FLOAT_WITHIN_EPSILON_OF(polygon_a.normal().x(), 0); - EXPECT_FLOAT_WITHIN_EPSILON_OF(polygon_a.normal().y(), 0); - EXPECT_FLOAT_WITHIN_EPSILON_OF(polygon_a.normal().z(), -1); + EXPECT_NORMAL(polygon_a, 0.0f, 0.0f, -1.0f); } } // namespace
diff --git a/cc/raster/one_copy_tile_task_worker_pool.cc b/cc/raster/one_copy_tile_task_worker_pool.cc index c7b07b7..28677343 100644 --- a/cc/raster/one_copy_tile_task_worker_pool.cc +++ b/cc/raster/one_copy_tile_task_worker_pool.cc
@@ -181,11 +181,11 @@ int max_copy_texture_chromium_size, bool use_partial_raster, int max_staging_buffer_usage_in_bytes, - ResourceFormat preferred_tile_format) { + bool use_rgba_4444_texture_format) { return make_scoped_ptr<TileTaskWorkerPool>(new OneCopyTileTaskWorkerPool( task_runner, task_graph_runner, resource_provider, max_copy_texture_chromium_size, use_partial_raster, - max_staging_buffer_usage_in_bytes, preferred_tile_format)); + max_staging_buffer_usage_in_bytes, use_rgba_4444_texture_format)); } OneCopyTileTaskWorkerPool::OneCopyTileTaskWorkerPool( @@ -195,7 +195,7 @@ int max_copy_texture_chromium_size, bool use_partial_raster, int max_staging_buffer_usage_in_bytes, - ResourceFormat preferred_tile_format) + bool use_rgba_4444_texture_format) : task_runner_(task_runner), task_graph_runner_(task_graph_runner), namespace_token_(task_graph_runner->GetNamespaceToken()), @@ -208,7 +208,7 @@ use_partial_raster_(use_partial_raster), bytes_scheduled_since_last_flush_(0), max_staging_buffer_usage_in_bytes_(max_staging_buffer_usage_in_bytes), - preferred_tile_format_(preferred_tile_format), + use_rgba_4444_texture_format_(use_rgba_4444_texture_format), staging_buffer_usage_in_bytes_(0), free_staging_buffer_usage_in_bytes_(0), staging_buffer_expiration_delay_( @@ -281,13 +281,9 @@ ResourceFormat OneCopyTileTaskWorkerPool::GetResourceFormat( bool must_support_alpha) const { - if (resource_provider_->IsResourceFormatSupported(preferred_tile_format_) && - (DoesResourceFormatSupportAlpha(preferred_tile_format_) || - !must_support_alpha)) { - return preferred_tile_format_; - } - - return resource_provider_->best_texture_format(); + return use_rgba_4444_texture_format_ + ? RGBA_4444 + : resource_provider_->best_texture_format(); } bool OneCopyTileTaskWorkerPool::GetResourceRequiresSwizzle( @@ -431,41 +427,32 @@ #endif } - // Since compressed texture's cannot be pre-allocated we might have an - // unallocated resource in which case we need to perform a full size copy. - if (IsResourceFormatCompressed(resource->format())) { - gl->CompressedCopyTextureCHROMIUM(GL_TEXTURE_2D, - staging_buffer->texture_id, - resource_lock->texture_id()); - } else { - int bytes_per_row = - (BitsPerPixel(resource->format()) * resource->size().width()) / 8; - int chunk_size_in_rows = - std::max(1, max_bytes_per_copy_operation_ / bytes_per_row); - // Align chunk size to 4. Required to support compressed texture formats. - chunk_size_in_rows = MathUtil::UncheckedRoundUp(chunk_size_in_rows, 4); - int y = 0; - int height = resource->size().height(); - while (y < height) { - // Copy at most |chunk_size_in_rows|. - int rows_to_copy = std::min(chunk_size_in_rows, height - y); - DCHECK_GT(rows_to_copy, 0); + int bytes_per_row = + (BitsPerPixel(resource->format()) * resource->size().width()) / 8; + int chunk_size_in_rows = + std::max(1, max_bytes_per_copy_operation_ / bytes_per_row); + // Align chunk size to 4. Required to support compressed texture formats. + chunk_size_in_rows = MathUtil::UncheckedRoundUp(chunk_size_in_rows, 4); + int y = 0; + int height = resource->size().height(); + while (y < height) { + // Copy at most |chunk_size_in_rows|. + int rows_to_copy = std::min(chunk_size_in_rows, height - y); + DCHECK_GT(rows_to_copy, 0); - gl->CopySubTextureCHROMIUM(GL_TEXTURE_2D, staging_buffer->texture_id, - resource_lock->texture_id(), 0, y, 0, y, - resource->size().width(), rows_to_copy, - false, false, false); - y += rows_to_copy; + gl->CopySubTextureCHROMIUM(GL_TEXTURE_2D, staging_buffer->texture_id, + resource_lock->texture_id(), 0, y, 0, y, + resource->size().width(), rows_to_copy, false, + false, false); + y += rows_to_copy; - // Increment |bytes_scheduled_since_last_flush_| by the amount of memory - // used for this copy operation. - bytes_scheduled_since_last_flush_ += rows_to_copy * bytes_per_row; + // Increment |bytes_scheduled_since_last_flush_| by the amount of memory + // used for this copy operation. + bytes_scheduled_since_last_flush_ += rows_to_copy * bytes_per_row; - if (bytes_scheduled_since_last_flush_ >= - max_bytes_per_copy_operation_) { - gl->ShallowFlushCHROMIUM(); - bytes_scheduled_since_last_flush_ = 0; - } + if (bytes_scheduled_since_last_flush_ >= max_bytes_per_copy_operation_) { + gl->ShallowFlushCHROMIUM(); + bytes_scheduled_since_last_flush_ = 0; } }
diff --git a/cc/raster/one_copy_tile_task_worker_pool.h b/cc/raster/one_copy_tile_task_worker_pool.h index 8ba2516..8dc861f 100644 --- a/cc/raster/one_copy_tile_task_worker_pool.h +++ b/cc/raster/one_copy_tile_task_worker_pool.h
@@ -50,7 +50,7 @@ int max_copy_texture_chromium_size, bool use_partial_raster, int max_staging_buffer_usage_in_bytes, - ResourceFormat preferred_tile_format); + bool use_rgba_4444_texture_format); // Overridden from TileTaskWorkerPool: TileTaskRunner* AsTileTaskRunner() override; @@ -92,7 +92,7 @@ int max_copy_texture_chromium_size, bool use_partial_raster, int max_staging_buffer_usage_in_bytes, - ResourceFormat preferred_tile_format); + bool use_rgba_4444_texture_format); private: struct StagingBuffer { @@ -149,7 +149,7 @@ StagingBufferDeque busy_buffers_; int bytes_scheduled_since_last_flush_; const int max_staging_buffer_usage_in_bytes_; - ResourceFormat preferred_tile_format_; + bool use_rgba_4444_texture_format_; int staging_buffer_usage_in_bytes_; int free_staging_buffer_usage_in_bytes_; const base::TimeDelta staging_buffer_expiration_delay_;
diff --git a/cc/raster/texture_compressor_etc1_sse.cc b/cc/raster/texture_compressor_etc1_sse.cc index 2297822..6f1005bf 100644 --- a/cc/raster/texture_compressor_etc1_sse.cc +++ b/cc/raster/texture_compressor_etc1_sse.cc
@@ -38,13 +38,9 @@ __m128i* red; }; -// Commonly used registers throughout the code. -static const __m128i __sse_zero = _mm_set1_epi32(0); -static const __m128i __sse_max_int = _mm_set1_epi32(0x7FFFFFFF); - inline __m128i AddAndClamp(const __m128i x, const __m128i y) { static const __m128i color_max = _mm_set1_epi32(0xFF); - return _mm_max_epi16(__sse_zero, + return _mm_max_epi16(_mm_setzero_si128(), _mm_min_epi16(_mm_add_epi16(x, y), color_max)); } @@ -72,7 +68,7 @@ const __m128i* green_avg, const __m128i* red_avg, uint32_t* verror) { - __m128i error = __sse_zero; + __m128i error = _mm_setzero_si128(); for (int i = 0; i < 4; i++) { error = _mm_add_epi32(error, GetColorErrorSSE(data->blue[i], blue_avg[0])); @@ -94,7 +90,7 @@ const __m128i* green_avg, const __m128i* red_avg, uint32_t* verror) { - __m128i error = __sse_zero; + __m128i error = _mm_setzero_si128(); int first_index, second_index; for (int i = 0; i < 2; i++) { @@ -301,11 +297,11 @@ test_green = AddAndClamp(tmp, base_green); test_red = AddAndClamp(tmp, base_red); - first_half_min = __sse_max_int; - second_half_min = __sse_max_int; + first_half_min = _mm_set1_epi32(0x7FFFFFFF); + second_half_min = _mm_set1_epi32(0x7FFFFFFF); - first_half_pattern = __sse_zero; - second_half_pattern = __sse_zero; + first_half_pattern = _mm_setzero_si128(); + second_half_pattern = _mm_setzero_si128(); for (uint8_t imm8 : shuffle_mask) { switch (imm8) {
diff --git a/cc/raster/tile_task_worker_pool.cc b/cc/raster/tile_task_worker_pool.cc index 81946252..b55f69d 100644 --- a/cc/raster/tile_task_worker_pool.cc +++ b/cc/raster/tile_task_worker_pool.cc
@@ -6,7 +6,6 @@ #include "base/trace_event/trace_event.h" #include "cc/playback/display_list_raster_source.h" -#include "cc/raster/texture_compressor.h" #include "skia/ext/refptr.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkDrawFilter.h" @@ -41,11 +40,11 @@ case RGBA_4444: case RGBA_8888: case BGRA_8888: - case ETC1: return true; case ALPHA_8: case LUMINANCE_8: case RGB_565: + case ETC1: case RED_8: return false; } @@ -82,6 +81,8 @@ // Uses kPremul_SkAlphaType since the result is not known to be opaque. SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType); + SkColorType buffer_color_type = ResourceFormatToSkColorType(format); + bool needs_copy = buffer_color_type != info.colorType(); // Use unknown pixel geometry to disable LCD text. SkSurfaceProps surface_props(0, kUnknown_SkPixelGeometry); @@ -98,59 +99,33 @@ if (!include_images) image_filter = skia::AdoptRef(new SkipImageFilter); + if (!needs_copy) { + skia::RefPtr<SkSurface> surface = skia::AdoptRef( + SkSurface::NewRasterDirect(info, memory, stride, &surface_props)); + skia::RefPtr<SkCanvas> canvas = skia::SharePtr(surface->getCanvas()); + canvas->setDrawFilter(image_filter.get()); + raster_source->PlaybackToCanvas(canvas.get(), canvas_bitmap_rect, + canvas_playback_rect, scale); + return; + } + + skia::RefPtr<SkSurface> surface = + skia::AdoptRef(SkSurface::NewRaster(info, &surface_props)); + skia::RefPtr<SkCanvas> canvas = skia::SharePtr(surface->getCanvas()); + canvas->setDrawFilter(image_filter.get()); + // TODO(reveman): Improve partial raster support by reducing the size of + // playback rect passed to PlaybackToCanvas. crbug.com/519070 + raster_source->PlaybackToCanvas(canvas.get(), canvas_bitmap_rect, + canvas_bitmap_rect, scale); + { TRACE_EVENT0("cc", "TileTaskWorkerPool::PlaybackToMemory::ConvertPixels"); - switch (format) { - case RGBA_8888: - case BGRA_8888: { - skia::RefPtr<SkSurface> surface = skia::AdoptRef( - SkSurface::NewRasterDirect(info, memory, stride, &surface_props)); - skia::RefPtr<SkCanvas> canvas = skia::SharePtr(surface->getCanvas()); - canvas->setDrawFilter(image_filter.get()); - raster_source->PlaybackToCanvas(canvas.get(), canvas_bitmap_rect, - canvas_playback_rect, scale); - return; - } - case RGBA_4444: - case ETC1: { - skia::RefPtr<SkSurface> surface = - skia::AdoptRef(SkSurface::NewRaster(info, &surface_props)); - skia::RefPtr<SkCanvas> canvas = skia::SharePtr(surface->getCanvas()); - canvas->setDrawFilter(image_filter.get()); - // TODO(reveman): Improve partial raster support by reducing the size of - // playback rect passed to PlaybackToCanvas. crbug.com/519070 - raster_source->PlaybackToCanvas(canvas.get(), canvas_bitmap_rect, - canvas_bitmap_rect, scale); - - if (format == ETC1) { - DCHECK_EQ(size.width() % 4, 0); - DCHECK_EQ(size.height() % 4, 0); - scoped_ptr<TextureCompressor> texture_compressor = - TextureCompressor::Create(TextureCompressor::kFormatETC1); - texture_compressor->Compress( - reinterpret_cast<const uint8_t*>( - surface->peekPixels(nullptr, nullptr)), - reinterpret_cast<uint8_t*>(memory), size.width(), size.height(), - TextureCompressor::kQualityHigh); - } else { - SkImageInfo dst_info = SkImageInfo::Make( - info.width(), info.height(), ResourceFormatToSkColorType(format), - info.alphaType(), info.profileType()); - bool rv = canvas->readPixels(dst_info, memory, stride, 0, 0); - DCHECK(rv); - } - return; - } - case ALPHA_8: - case LUMINANCE_8: - case RGB_565: - case RED_8: - NOTREACHED(); - return; - } - - NOTREACHED(); + SkImageInfo dst_info = + SkImageInfo::Make(info.width(), info.height(), buffer_color_type, + info.alphaType(), info.profileType()); + bool rv = canvas->readPixels(dst_info, memory, stride, 0, 0); + DCHECK(rv); } }
diff --git a/cc/raster/tile_task_worker_pool_perftest.cc b/cc/raster/tile_task_worker_pool_perftest.cc index 1e1ff1c..70902c8 100644 --- a/cc/raster/tile_task_worker_pool_perftest.cc +++ b/cc/raster/tile_task_worker_pool_perftest.cc
@@ -254,7 +254,7 @@ Create3dOutputSurfaceAndResourceProvider(); tile_task_worker_pool_ = ZeroCopyTileTaskWorkerPool::Create( task_runner_.get(), task_graph_runner_.get(), - resource_provider_.get(), PlatformColor::BestTextureFormat()); + resource_provider_.get(), false); break; case TILE_TASK_WORKER_POOL_TYPE_ONE_COPY: Create3dOutputSurfaceAndResourceProvider(); @@ -262,8 +262,7 @@ task_runner_.get(), task_graph_runner_.get(), context_provider_.get(), resource_provider_.get(), std::numeric_limits<int>::max(), false, - std::numeric_limits<int>::max(), - PlatformColor::BestTextureFormat()); + std::numeric_limits<int>::max(), false); break; case TILE_TASK_WORKER_POOL_TYPE_GPU: Create3dOutputSurfaceAndResourceProvider();
diff --git a/cc/raster/tile_task_worker_pool_unittest.cc b/cc/raster/tile_task_worker_pool_unittest.cc index c5d3a416..62239c16 100644 --- a/cc/raster/tile_task_worker_pool_unittest.cc +++ b/cc/raster/tile_task_worker_pool_unittest.cc
@@ -146,15 +146,14 @@ Create3dOutputSurfaceAndResourceProvider(); tile_task_worker_pool_ = ZeroCopyTileTaskWorkerPool::Create( base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_, - resource_provider_.get(), PlatformColor::BestTextureFormat()); + resource_provider_.get(), false); break; case TILE_TASK_WORKER_POOL_TYPE_ONE_COPY: Create3dOutputSurfaceAndResourceProvider(); tile_task_worker_pool_ = OneCopyTileTaskWorkerPool::Create( base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_, context_provider_.get(), resource_provider_.get(), - kMaxBytesPerCopyOperation, false, kMaxStagingBuffers, - PlatformColor::BestTextureFormat()); + kMaxBytesPerCopyOperation, false, kMaxStagingBuffers, false); break; case TILE_TASK_WORKER_POOL_TYPE_GPU: Create3dOutputSurfaceAndResourceProvider();
diff --git a/cc/raster/zero_copy_tile_task_worker_pool.cc b/cc/raster/zero_copy_tile_task_worker_pool.cc index c2358a6..8830c1f02 100644 --- a/cc/raster/zero_copy_tile_task_worker_pool.cc +++ b/cc/raster/zero_copy_tile_task_worker_pool.cc
@@ -65,22 +65,22 @@ base::SequencedTaskRunner* task_runner, TaskGraphRunner* task_graph_runner, ResourceProvider* resource_provider, - ResourceFormat preferred_tile_format) { - return make_scoped_ptr<TileTaskWorkerPool>( - new ZeroCopyTileTaskWorkerPool(task_runner, task_graph_runner, - resource_provider, preferred_tile_format)); + bool use_rgba_4444_texture_format) { + return make_scoped_ptr<TileTaskWorkerPool>(new ZeroCopyTileTaskWorkerPool( + task_runner, task_graph_runner, resource_provider, + use_rgba_4444_texture_format)); } ZeroCopyTileTaskWorkerPool::ZeroCopyTileTaskWorkerPool( base::SequencedTaskRunner* task_runner, TaskGraphRunner* task_graph_runner, ResourceProvider* resource_provider, - ResourceFormat preferred_tile_format) + bool use_rgba_4444_texture_format) : task_runner_(task_runner), task_graph_runner_(task_graph_runner), namespace_token_(task_graph_runner->GetNamespaceToken()), resource_provider_(resource_provider), - preferred_tile_format_(preferred_tile_format) {} + use_rgba_4444_texture_format_(use_rgba_4444_texture_format) {} ZeroCopyTileTaskWorkerPool::~ZeroCopyTileTaskWorkerPool() { } @@ -122,13 +122,9 @@ ResourceFormat ZeroCopyTileTaskWorkerPool::GetResourceFormat( bool must_support_alpha) const { - if (resource_provider_->IsResourceFormatSupported(preferred_tile_format_) && - (DoesResourceFormatSupportAlpha(preferred_tile_format_) || - !must_support_alpha)) { - return preferred_tile_format_; - } - - return resource_provider_->best_texture_format(); + return use_rgba_4444_texture_format_ + ? RGBA_4444 + : resource_provider_->best_texture_format(); } bool ZeroCopyTileTaskWorkerPool::GetResourceRequiresSwizzle(
diff --git a/cc/raster/zero_copy_tile_task_worker_pool.h b/cc/raster/zero_copy_tile_task_worker_pool.h index 79be7f50..3ca9b06 100644 --- a/cc/raster/zero_copy_tile_task_worker_pool.h +++ b/cc/raster/zero_copy_tile_task_worker_pool.h
@@ -29,7 +29,7 @@ base::SequencedTaskRunner* task_runner, TaskGraphRunner* task_graph_runner, ResourceProvider* resource_provider, - ResourceFormat preferred_tile_format); + bool use_rgba_4444_texture_format); // Overridden from TileTaskWorkerPool: TileTaskRunner* AsTileTaskRunner() override; @@ -52,7 +52,7 @@ ZeroCopyTileTaskWorkerPool(base::SequencedTaskRunner* task_runner, TaskGraphRunner* task_graph_runner, ResourceProvider* resource_provider, - ResourceFormat preferred_tile_format); + bool use_rgba_4444_texture_format); private: scoped_refptr<base::trace_event::ConvertableToTraceFormat> StateAsValue() @@ -63,7 +63,7 @@ const NamespaceToken namespace_token_; ResourceProvider* resource_provider_; - ResourceFormat preferred_tile_format_; + bool use_rgba_4444_texture_format_; Task::Vector completed_tasks_;
diff --git a/cc/resources/platform_color.h b/cc/resources/platform_color.h index 7e49863..17eddc1f 100644 --- a/cc/resources/platform_color.h +++ b/cc/resources/platform_color.h
@@ -24,19 +24,7 @@ } // Returns the most efficient texture format for this platform. - static ResourceFormat BestTextureFormat() { - switch (Format()) { - case SOURCE_FORMAT_BGRA8: - return BGRA_8888; - case SOURCE_FORMAT_RGBA8: - return RGBA_8888; - } - NOTREACHED(); - return RGBA_8888; - } - - // Returns the most efficient supported texture format for this platform. - static ResourceFormat BestSupportedTextureFormat(bool supports_bgra8888) { + static ResourceFormat BestTextureFormat(bool supports_bgra8888) { switch (Format()) { case SOURCE_FORMAT_BGRA8: return (supports_bgra8888) ? BGRA_8888 : RGBA_8888;
diff --git a/cc/resources/resource_format.cc b/cc/resources/resource_format.cc index e762464..e11c097b 100644 --- a/cc/resources/resource_format.cc +++ b/cc/resources/resource_format.cc
@@ -97,36 +97,14 @@ return gfx::BufferFormat::RGBA_4444; case RGBA_8888: return gfx::BufferFormat::RGBA_8888; - case ETC1: - return gfx::BufferFormat::ETC1; case ALPHA_8: case LUMINANCE_8: case RGB_565: + case ETC1: break; } NOTREACHED(); return gfx::BufferFormat::RGBA_8888; } -bool IsResourceFormatCompressed(ResourceFormat format) { - return format == ETC1; -} - -bool DoesResourceFormatSupportAlpha(ResourceFormat format) { - switch (format) { - case RGBA_4444: - case RGBA_8888: - case BGRA_8888: - case ALPHA_8: - return true; - case LUMINANCE_8: - case RGB_565: - case ETC1: - case RED_8: - return false; - } - NOTREACHED(); - return false; -} - } // namespace cc
diff --git a/cc/resources/resource_format.h b/cc/resources/resource_format.h index e1a5fea..79b815f 100644 --- a/cc/resources/resource_format.h +++ b/cc/resources/resource_format.h
@@ -38,9 +38,6 @@ CC_EXPORT GLenum GLInternalFormat(ResourceFormat format); CC_EXPORT gfx::BufferFormat BufferFormat(ResourceFormat format); -bool IsResourceFormatCompressed(ResourceFormat format); -bool DoesResourceFormatSupportAlpha(ResourceFormat format); - } // namespace cc #endif // CC_RESOURCES_RESOURCE_FORMAT_H_
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc index cc6392306..4e490750 100644 --- a/cc/resources/resource_provider.cc +++ b/cc/resources/resource_provider.cc
@@ -368,29 +368,6 @@ gl->Finish(); } -bool ResourceProvider::IsResourceFormatSupported(ResourceFormat format) const { - const ContextProvider::Capabilities& caps = - output_surface_->context_provider()->ContextCapabilities(); - - switch (format) { - case ALPHA_8: - case RGBA_4444: - case RGBA_8888: - case RGB_565: - case LUMINANCE_8: - return true; - case BGRA_8888: - return caps.gpu.texture_format_bgra8888; - case ETC1: - return caps.gpu.texture_format_etc1; - case RED_8: - return caps.gpu.texture_rg; - } - - NOTREACHED(); - return false; -} - bool ResourceProvider::InUseByConsumer(ResourceId id) { Resource* resource = GetResource(id); return resource->lock_for_read_count > 0 || resource->exported_count > 0 || @@ -1090,10 +1067,10 @@ max_texture_size_ = 0; // Context expects cleared value. gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_); best_texture_format_ = - PlatformColor::BestSupportedTextureFormat(use_texture_format_bgra_); + PlatformColor::BestTextureFormat(use_texture_format_bgra_); - best_render_buffer_format_ = PlatformColor::BestSupportedTextureFormat( - caps.gpu.render_buffer_format_bgra8888); + best_render_buffer_format_ = + PlatformColor::BestTextureFormat(caps.gpu.render_buffer_format_bgra8888); texture_id_allocator_.reset( new TextureIdAllocator(gl, id_allocation_chunk_size_));
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h index e7bf671..5a03cda 100644 --- a/cc/resources/resource_provider.h +++ b/cc/resources/resource_provider.h
@@ -106,8 +106,6 @@ } size_t num_resources() const { return resources_.size(); } - bool IsResourceFormatSupported(ResourceFormat format) const; - // Checks whether a resource is in use by a consumer. bool InUseByConsumer(ResourceId id);
diff --git a/cc/test/layer_tree_pixel_resource_test.cc b/cc/test/layer_tree_pixel_resource_test.cc index 5d810ab..347933ce3 100644 --- a/cc/test/layer_tree_pixel_resource_test.cc +++ b/cc/test/layer_tree_pixel_resource_test.cc
@@ -147,8 +147,7 @@ *resource_pool = ResourcePool::Create(resource_provider, task_runner); *tile_task_worker_pool = ZeroCopyTileTaskWorkerPool::Create( - task_runner, task_graph_runner(), resource_provider, - PlatformColor::BestTextureFormat()); + task_runner, task_graph_runner(), resource_provider, false); break; case ONE_COPY_TILE_TASK_WORKER_POOL: EXPECT_TRUE(context_provider); @@ -159,8 +158,7 @@ *tile_task_worker_pool = OneCopyTileTaskWorkerPool::Create( task_runner, task_graph_runner(), context_provider, resource_provider, max_bytes_per_copy_operation, false, - max_staging_buffer_usage_in_bytes, - PlatformColor::BestTextureFormat()); + max_staging_buffer_usage_in_bytes, false); break; } }
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc index 6ae1235..9804da0 100644 --- a/cc/tiles/tile_manager.cc +++ b/cc/tiles/tile_manager.cc
@@ -796,6 +796,8 @@ if (resource) { resource_content_id = tile->invalidated_id(); DCHECK_EQ(DetermineResourceFormat(tile), resource->format()); + DCHECK_EQ(tile->desired_texture_size().ToString(), + resource->size().ToString()); } else { resource = resource_pool_->AcquireResource(tile->desired_texture_size(), DetermineResourceFormat(tile));
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 974d821..9524e41 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -2229,7 +2229,7 @@ *tile_task_worker_pool = ZeroCopyTileTaskWorkerPool::Create( GetTaskRunner(), task_graph_runner, resource_provider_.get(), - settings_.renderer_settings.preferred_tile_format); + settings_.renderer_settings.use_rgba_4444_textures); return; } @@ -2243,7 +2243,7 @@ GetTaskRunner(), task_graph_runner, context_provider, resource_provider_.get(), max_copy_texture_chromium_size, settings_.use_partial_raster, settings_.max_staging_buffer_usage_in_bytes, - settings_.renderer_settings.preferred_tile_format); + settings_.renderer_settings.use_rgba_4444_textures); } void LayerTreeHostImpl::RecordMainFrameTiming(
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index fa263503..b7562983 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn
@@ -543,6 +543,7 @@ ] deps = [ "//base", + "//components/startup_metric_utils/browser:lib", ] } }
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml index 94768632..8a416c8 100644 --- a/chrome/android/java/res/xml/privacy_preferences.xml +++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -28,6 +28,7 @@ <org.chromium.chrome.browser.preferences.privacy.NetworkPredictionPreference android:key="network_predictions" android:title="@string/network_predictions_title" + android:persistent="false" android:entries="@array/bandwidth_entries" android:entryValues="@array/bandwidth_entry_values" android:defaultValue="@string/network_prediction_wifi_only_value" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java index beb8190..e880603 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -352,7 +352,7 @@ * @return The vertical scroll position of the content. */ public float getContentVerticalScroll() { - return mContent.getContentVerticalScroll(); + return mContent != null ? mContent.getContentVerticalScroll() : 0.0f; } // ============================================================================================ @@ -450,7 +450,7 @@ * @param ty The movement's total displacement in dps. */ public void handleSwipeMove(float ty) { - if (ty > 0 && getPanelState() == PanelState.MAXIMIZED) { + if (mContent != null && ty > 0 && getPanelState() == PanelState.MAXIMIZED) { // Resets the Content View scroll position when swiping the Panel down // after being maximized. mContent.resetContentViewScroll();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java index e58462f..29b1996 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -270,7 +270,7 @@ webContents = WebContentsFactory.createWebContents(false, false); } mTab.initialize(webContents, getTabContentManager(), - new CustomTabDelegateFactory(getApplication(), mSession), false); + new CustomTabDelegateFactory(getApplication(), mSession), false, false); mTab.getView().requestFocus(); mTabObserver = new CustomTabObserver(getApplication(), mSession); mTab.addObserver(mTabObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java index 0e06062..342b660 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java
@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.KeyboardShortcuts; import org.chromium.chrome.browser.TabState; +import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason; import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument; import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocumentTabSwitcher; @@ -68,6 +69,7 @@ import org.chromium.components.service_tab_launcher.ServiceTabLauncher; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.NavigationEntry; +import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.PageTransition; /** @@ -123,10 +125,10 @@ @Override public void onUrlUpdated(Tab tab) { - assert mDocumentTab == tab; + assert mTab == tab; updateTaskDescription(); - mTabModel.updateEntry(getIntent(), mDocumentTab); + mTabModel.updateEntry(getIntent(), mTab); } @Override @@ -151,10 +153,10 @@ @Override public void onLoadStopped(Tab tab, boolean toDifferentDocument) { - assert mDocumentTab == tab; + assert mTab == tab; updateTaskDescription(); - mTabModel.updateEntry(getIntent(), mDocumentTab); + mTabModel.updateEntry(getIntent(), mTab); } @Override @@ -213,7 +215,7 @@ private int mDefaultThemeColor; - private DocumentTab mDocumentTab; + private Tab mTab; private FindToolbarManager mFindToolbarManager; private boolean mRecordedStartupUma; @@ -221,7 +223,7 @@ // used from the UI thread. private static boolean sIsFirstPageLoadStart = true; - /** Whether the DocumentTab has already been added to the TabModel. */ + /** Whether the Tab has already been added to the TabModel. */ private boolean mNeedsToBeAddedToTabModel; @Override @@ -356,8 +358,8 @@ if (mNeedsToBeAddedToTabModel) { mNeedsToBeAddedToTabModel = false; - mTabModel.addTab(getIntent(), mDocumentTab); - getTabModelSelector().setTab(mDocumentTab); + mTabModel.addTab(getIntent(), mTab); + getTabModelSelector().setTab(mTab); } super.finishNativeInitialization(); @@ -368,7 +370,7 @@ */ protected final int determineTabId() { int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); - if (tabId == Tab.INVALID_TAB_ID && mDocumentTab != null) tabId = mDocumentTab.getId(); + if (tabId == Tab.INVALID_TAB_ID && mTab != null) tabId = mTab.getId(); return tabId; } @@ -403,10 +405,10 @@ } // Check the Tab's history. - if (TextUtils.isEmpty(initialUrl) && mDocumentTab != null - && mDocumentTab.getWebContents() != null) { + if (TextUtils.isEmpty(initialUrl) && mTab != null + && mTab.getWebContents() != null) { NavigationEntry entry = - mDocumentTab.getWebContents().getNavigationController().getEntryAtIndex(0); + mTab.getWebContents().getNavigationController().getEntryAtIndex(0); if (entry != null) initialUrl = entry.getOriginalUrl(); } @@ -415,12 +417,12 @@ @Override public CharSequence onCreateDescription() { - return mDocumentTab != null ? mDocumentTab.getTitle() : ""; + return mTab != null ? mTab.getTitle() : ""; } @Override - public final DocumentTab getActivityTab() { - return mDocumentTab; + public final Tab getActivityTab() { + return mTab; } @Override @@ -457,8 +459,8 @@ // If finishing, release all the active media players as we don't know when onStop() // will get called. super.onPause(); - if (isFinishing() && mDocumentTab != null && mDocumentTab.getWebContents() != null) { - mDocumentTab.getWebContents().releaseMediaPlayers(); + if (isFinishing() && mTab != null && mTab.getWebContents() != null) { + mTab.getWebContents().releaseMediaPlayers(); } } @@ -482,7 +484,7 @@ public void onResumeWithNative() { super.onResumeWithNative(); - if (mDocumentTab != null) { + if (mTab != null) { AsyncTabCreationParams asyncParams = AsyncTabCreationParamsManager.remove( ActivityDelegate.getTabIdFromIntent(getIntent())); if (asyncParams != null && asyncParams.getLoadUrlParams().getUrl() != null) { @@ -534,7 +536,7 @@ } if (asyncParams != null && asyncParams.getOriginalIntent() != null) { - mDocumentTab.getTabRedirectHandler().updateIntent(asyncParams.getOriginalIntent()); + mTab.getTabRedirectHandler().updateIntent(asyncParams.getOriginalIntent()); } else { if (getIntent() != null) { try { @@ -544,14 +546,14 @@ // Ignore exception. } } - mDocumentTab.getTabRedirectHandler().updateIntent(intent); + mTab.getTabRedirectHandler().updateIntent(intent); } - mDocumentTab.loadUrl(loadUrlParams); + mTab.loadUrl(loadUrlParams); if (asyncParams != null && asyncParams.getRequestId() != null && asyncParams.getRequestId() > 0) { ServiceTabLauncher.onWebContentsForRequestAvailable( - asyncParams.getRequestId(), mDocumentTab.getWebContents()); + asyncParams.getRequestId(), mTab.getWebContents()); } } @@ -567,31 +569,27 @@ : ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color); AsyncTabCreationParams asyncParams = AsyncTabCreationParamsManager.remove( ActivityDelegate.getTabIdFromIntent(getIntent())); - int tabId = determineTabId(); - TabState tabState = mTabModel.getTabStateForDocument(tabId); boolean isAffiliated = asyncParams != null ? asyncParams.isAffiliated() : false; boolean isCreatedWithWebContents = asyncParams != null && asyncParams.getWebContents() != null; - mDocumentTab = DocumentTab.create(DocumentActivity.this, isIncognito(), getWindowAndroid(), - determineLastKnownUrl(), asyncParams != null ? asyncParams.getWebContents() : null, - tabState, isAffiliated, getTabCreationState(tabState != null, isAffiliated)); + mTab = createActivityTab(asyncParams); if (asyncParams != null && asyncParams.getWebContents() != null) { Intent parentIntent = IntentUtils.safeGetParcelableExtra(getIntent(), IntentHandler.EXTRA_PARENT_INTENT); - mDocumentTab.setParentIntent(parentIntent); + mTab.setParentIntent(parentIntent); } if (mTabModel.isNativeInitialized()) { - mTabModel.addTab(getIntent(), mDocumentTab); + mTabModel.addTab(getIntent(), mTab); } else { mNeedsToBeAddedToTabModel = true; } - getTabModelSelector().setTab(mDocumentTab); + getTabModelSelector().setTab(mTab); - if (mDocumentTab.didFailToRestore() + if (mTab.didFailToRestore() || (asyncParams != null && asyncParams.getLoadUrlParams().getUrl() != null)) { if (!isCreatedWithWebContents) { // Don't load tabs in the background on low end devices. We will call @@ -612,7 +610,7 @@ loadLastKnownUrl(asyncParams); } } - mDocumentTab.setShouldPreserve(IntentUtils.safeGetBooleanExtra(getIntent(), + mTab.setShouldPreserve(IntentUtils.safeGetBooleanExtra(getIntent(), IntentHandler.EXTRA_PRESERVE_TASK, false)); } @@ -691,15 +689,15 @@ mFindToolbarManager, overviewModeBehavior, layoutDriver, tabSwitcherClickHandler, null, null, null); - mDocumentTab.setFullscreenManager(getFullscreenManager()); + mTab.setFullscreenManager(getFullscreenManager()); - mDocumentTab.addObserver(new DocumentTabObserver()); + mTab.addObserver(new DocumentTabObserver()); removeWindowBackground(); - if (mDocumentTab != null) { + if (mTab != null) { DataReductionPreferences.launchDataReductionSSLInfoBar( - DocumentActivity.this, mDocumentTab.getWebContents()); + DocumentActivity.this, mTab.getWebContents()); } } @@ -710,20 +708,20 @@ private void updateLastTabId() { ChromeApplication.getDocumentTabModelSelector().selectModel(isIncognito()); - int tabId = mDocumentTab == null - ? ActivityDelegate.getTabIdFromIntent(getIntent()) : mDocumentTab.getId(); + int tabId = mTab == null + ? ActivityDelegate.getTabIdFromIntent(getIntent()) : mTab.getId(); mTabModel.setLastShownId(tabId); } @Override public boolean handleBackPressed() { - if (mDocumentTab == null) return false; + if (mTab == null) return false; if (exitFullscreenIfShowing()) return true; - if (mDocumentTab.canGoBack()) { - mDocumentTab.goBack(); - } else if (!mDocumentTab.shouldPreserve()) { + if (mTab.canGoBack()) { + mTab.goBack(); + } else if (!mTab.shouldPreserve()) { finishAndRemoveTask(); } else { moveTaskToBack(true); @@ -737,6 +735,31 @@ return (TabDelegate) super.getTabCreator(incognito); } + private Tab createActivityTab(AsyncTabCreationParams asyncParams) { + boolean isAffiliated = asyncParams != null ? asyncParams.isAffiliated() : false; + boolean isCreatedWithWebContents = asyncParams != null + && asyncParams.getWebContents() != null; + int tabId = determineTabId(); + int parentTabId = getIntent().getIntExtra( + IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID); + TabState tabState = mTabModel.getTabStateForDocument(tabId); + boolean hasTabState = tabState != null; + String url = determineLastKnownUrl(); + + Tab tab = new Tab(tabId, parentTabId, isIncognito(), this, getWindowAndroid(), + getTabLaunchType(url, hasTabState, isAffiliated), + getTabCreationState(hasTabState, isAffiliated), tabState); + + // Initialize tab and web contents. + WebContents webContents = isCreatedWithWebContents ? asyncParams.getWebContents() : null; + tab.initialize(webContents, getTabContentManager(), + new DocumentTabDelegateFactory(), isAffiliated, hasTabState); + tab.getView().requestFocus(); + if (isCreatedWithWebContents) webContents.resumeLoadingCreatedWebContents(); + + return tab; + } + /** * This cannot return {@link TabCreationState#FROZEN_ON_RESTORE_FAILED} since the Tab has * to be created first to even attempt restore. @@ -750,6 +773,16 @@ return TabCreationState.LIVE_IN_FOREGROUND; } + private TabLaunchType getTabLaunchType( + String url, boolean hasTabState, boolean isAffiliated) { + if (hasTabState) return TabLaunchType.FROM_RESTORE; + if (isAffiliated) return TabLaunchType.FROM_LONGPRESS_BACKGROUND; + if (!TextUtils.isEmpty(url) && url.equals(UrlConstants.NTP_URL)) { + return TabLaunchType.FROM_MENU_OR_OVERVIEW; + } + return TabLaunchType.FROM_EXTERNAL_APP; + } + @Override public void createContextualSearchTab(String searchUrl) { AsyncTabCreationParams asyncParams = @@ -784,11 +817,11 @@ } else if (id == R.id.all_bookmarks_menu_id) { StartupMetrics.getInstance().recordOpenedBookmarks(); if (!EnhancedBookmarkUtils.showEnhancedBookmarkIfEnabled(this)) { - NewTabPage.launchBookmarksDialog(this, mDocumentTab, getTabModelSelector()); + NewTabPage.launchBookmarksDialog(this, mTab, getTabModelSelector()); } RecordUserAction.record("MobileMenuAllBookmarks"); } else if (id == R.id.recent_tabs_menu_id) { - NewTabPage.launchRecentTabsDialog(this, mDocumentTab); + NewTabPage.launchRecentTabsDialog(this, mTab); RecordUserAction.record("MobileMenuOpenTabs"); } else if (id == R.id.find_in_page_id) { mFindToolbarManager.showToolbar(); @@ -826,7 +859,7 @@ @Override public boolean shouldShowAppMenu() { - if (mDocumentTab == null || !getToolbarManager().isInitialized()) { + if (mTab == null || !getToolbarManager().isInitialized()) { return false; } @@ -840,7 +873,7 @@ } private void updateTaskDescription() { - if (mDocumentTab == null) { + if (mTab == null) { updateTaskDescription(null, null); return; } @@ -851,8 +884,8 @@ return; } - String label = mDocumentTab.getTitle(); - String domain = UrlUtilities.getDomainAndRegistry(mDocumentTab.getUrl(), false); + String label = mTab.getTitle(); + String domain = UrlUtilities.getDomainAndRegistry(mTab.getUrl(), false); if (TextUtils.isEmpty(label)) { label = domain; } @@ -863,7 +896,7 @@ Bitmap bitmap = null; if (!isIncognito()) { - bitmap = mIcon.getBitmap(mDocumentTab.getUrl(), mLargestFavicon); + bitmap = mIcon.getBitmap(mTab.getUrl(), mLargestFavicon); } updateTaskDescription(label, bitmap); @@ -889,7 +922,7 @@ */ boolean isNewTabPage() { String url; - if (mDocumentTab == null) { + if (mTab == null) { // If the Tab hasn't been created yet, then we're really early in initialization. // Use a combination of the original URL from the Intent and whether or not the Tab is // retargetable to know whether or not the user navigated away from the NTP. @@ -899,7 +932,7 @@ url = IntentHandler.getUrlFromIntent(getIntent()); if (mTabModel.hasEntryForTabId(tabId) && !mTabModel.isRetargetable(tabId)) return false; } else { - url = mDocumentTab.getUrl(); + url = mTab.getUrl(); } return NewTabPage.isNTPUrl(url); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentTab.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentTab.java deleted file mode 100644 index c18cb36..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentTab.java +++ /dev/null
@@ -1,160 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.document; - -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeApplication; -import org.chromium.chrome.browser.IntentHandler; -import org.chromium.chrome.browser.TabState; -import org.chromium.chrome.browser.WarmupManager; -import org.chromium.chrome.browser.WebContentsFactory; -import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tab.TabDelegateFactory; -import org.chromium.chrome.browser.tab.TabUma.TabCreationState; -import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid; -import org.chromium.chrome.browser.tabmodel.TabModel; -import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; -import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; -import org.chromium.content_public.browser.WebContents; -import org.chromium.ui.base.WindowAndroid; - -/** - * A Tab child class with Chrome documents specific functionality. - */ -public class DocumentTab extends Tab { - /** - * Standard constructor for the document tab. - * @param activity The document activity that will hold on to this tab. - * @param incognito Whether the tab is incognito. - * @param windowAndroid The window that this tab should be using. - * @param url The url to load on creation. - * @param parentTabId The id of the parent tab. - * @param initiallyHidden Whether or not the {@link WebContents} should be initially hidden. - */ - private DocumentTab(DocumentActivity activity, boolean incognito, WindowAndroid windowAndroid, - String url, int parentTabId, boolean initiallyHidden, TabCreationState creationState) { - super(ActivityDelegate.getTabIdFromIntent(activity.getIntent()), parentTabId, incognito, - activity, windowAndroid, TabLaunchType.FROM_EXTERNAL_APP, creationState, null); - initialize(url, null, activity.getTabContentManager(), false, initiallyHidden); - } - - /** - * Constructor for document tab from a frozen state. - * @param activity The document activity that will hold on to this tab. - * @param incognito Whether the tab is incognito. - * @param windowAndroid The window that this tab should be using. - * @param url The url to load on creation. - * @param tabState The {@link TabState} the tab will be recreated from. - * @param parentTabId The id of the parent tab. - */ - private DocumentTab(DocumentActivity activity, boolean incognito, - WindowAndroid windowAndroid, String url, TabState tabState, int parentTabId, - TabCreationState creationState) { - super(ActivityDelegate.getTabIdFromIntent(activity.getIntent()), parentTabId, incognito, - activity, windowAndroid, TabLaunchType.FROM_RESTORE, creationState, tabState); - initialize(url, null, activity.getTabContentManager(), true, false); - } - - /** - * Constructor for tab opened via JS. - * @param activity The document activity that will hold on to this tab. - * @param incognito Whether the tab is incognito. - * @param windowAndroid The window that this tab should be using. - * @param url The url to load on creation. - * @param parentTabId The id of the parent tab. - * @param webContents An optional {@link WebContents} object to use. - */ - private DocumentTab(DocumentActivity activity, boolean incognito, - WindowAndroid windowAndroid, String url, int parentTabId, WebContents webContents, - TabCreationState creationState) { - super(ActivityDelegate.getTabIdFromIntent(activity.getIntent()), parentTabId, incognito, - activity, windowAndroid, TabLaunchType.FROM_LONGPRESS_FOREGROUND, - creationState, null); - initialize(url, webContents, activity.getTabContentManager(), false, false); - } - - @Override - protected void initContentViewCore(WebContents webContents) { - super.initContentViewCore(webContents); - getContentViewCore().setFullscreenRequiredForOrientationLock(false); - } - - /** - * Initializes the tab with native web contents. - * @param url The url to use for looking up potentially pre-rendered web contents. - * @param webContents Optionally, a pre-created web contents. - * @param unfreeze Whether we want to initialize the tab from tab state. - * @param initiallyHidden Whether or not the {@link WebContents} should be initially hidden. - */ - private void initialize(String url, WebContents webContents, - TabContentManager tabContentManager, boolean unfreeze, boolean initiallyHidden) { - if (!unfreeze && webContents == null) { - webContents = WarmupManager.getInstance().hasPrerenderedUrl(url) - ? WarmupManager.getInstance().takePrerenderedWebContents() - : WebContentsFactory.createWebContents(isIncognito(), initiallyHidden); - } - initialize(webContents, tabContentManager, new TabDelegateFactory() { - @Override - public TabWebContentsDelegateAndroid createWebContentsDelegate( - Tab tab, ChromeActivity activity) { - return new DocumentTabWebContentsDelegateAndroid(DocumentTab.this, mActivity); - } - }, initiallyHidden); - if (unfreeze) unfreezeContents(); - - getView().requestFocus(); - } - - /** - * A web contents delegate for handling opening new windows in Document mode. - */ - public class DocumentTabWebContentsDelegateAndroid extends TabWebContentsDelegateAndroid { - public DocumentTabWebContentsDelegateAndroid(Tab tab, ChromeActivity activity) { - super(tab, activity); - } - - /** - * TODO(dfalcantara): Remove this when DocumentActivity.getTabModelSelector() - * can return a TabModelSelector that activateContents() can use. - */ - @Override - protected TabModel getTabModel() { - return ChromeApplication.getDocumentTabModelSelector().getModel(isIncognito()); - } - } - - /** - * Create a DocumentTab. - * @param activity The activity the tab will be residing in. - * @param incognito Whether the tab is incognito. - * @param window The window the activity is using. - * @param url The url that should be displayed by the tab. - * @param webContents A {@link WebContents} object. - * @param tabState State that was previously persisted to disk for the Tab. - * @return The created {@link DocumentTab}. - * @param initiallyHidden Whether or not the {@link WebContents} should be initially hidden. - */ - static DocumentTab create(DocumentActivity activity, boolean incognito, WindowAndroid window, - String url, WebContents webContents, TabState tabState, boolean initiallyHidden, - TabCreationState creationState) { - int parentTabId = activity.getIntent().getIntExtra( - IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID); - if (webContents != null) { - DocumentTab tab = new DocumentTab( - activity, incognito, window, url, parentTabId, webContents, creationState); - webContents.resumeLoadingCreatedWebContents(); - return tab; - } - - if (tabState == null) { - return new DocumentTab( - activity, incognito, window, url, parentTabId, initiallyHidden, creationState); - } else { - return new DocumentTab( - activity, incognito, window, "", tabState, parentTabId, creationState); - } - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentTabDelegateFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentTabDelegateFactory.java new file mode 100644 index 0000000..7a9907b4 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentTabDelegateFactory.java
@@ -0,0 +1,46 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.document; + +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeApplication; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabDelegateFactory; +import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid; +import org.chromium.chrome.browser.tabmodel.TabModel; + +/** + * A {@link TabDelegateFactory} class to be used in all {@link Tab} owned + * by a {@link DocumentActivity}. + */ +public class DocumentTabDelegateFactory extends TabDelegateFactory { + /** + * A web contents delegate for handling opening new windows in Document mode. + */ + private static class DocumentTabWebContentsDelegateAndroid + extends TabWebContentsDelegateAndroid { + private boolean mIsIncognito; + + public DocumentTabWebContentsDelegateAndroid(Tab tab, ChromeActivity activity) { + super(tab, activity); + mIsIncognito = tab.isIncognito(); + } + + /** + * TODO(dfalcantara): Remove this when DocumentActivity.getTabModelSelector() + * can return a TabModelSelector that activateContents() can use. + */ + @Override + protected TabModel getTabModel() { + return ChromeApplication.getDocumentTabModelSelector().getModel(mIsIncognito); + } + } + + @Override + public TabWebContentsDelegateAndroid createWebContentsDelegate( + Tab tab, ChromeActivity activity) { + return new DocumentTabWebContentsDelegateAndroid(tab, activity); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastRouteController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastRouteController.java index 46d7967c..f2c934e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastRouteController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastRouteController.java
@@ -239,7 +239,7 @@ } else { mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP); } - MediaNotificationManager.show(context, mNotificationBuilder); + MediaNotificationManager.show(context, mNotificationBuilder.build()); } }); } @@ -256,7 +256,7 @@ .setActions(MediaNotificationInfo.ACTION_STOP) .setId(R.id.presentation_notification) .setListener(this); - MediaNotificationManager.show(context, mNotificationBuilder); + MediaNotificationManager.show(context, mNotificationBuilder.build()); synchronized (INIT_LOCK) { if (sMediaOverloadedMessageTypes == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java index c18df6c2..736cf0c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java
@@ -5,13 +5,8 @@ package org.chromium.chrome.browser.media.ui; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; - -import java.util.List; /** * MediaButtonReceiver is a basic BroadcastReceiver class that receives @@ -20,30 +15,11 @@ * This is there for backward compatibility with JB_MR0 and JB_MR1. */ public abstract class MediaButtonReceiver extends BroadcastReceiver { - private static final String LISTENER_SERVICE_CLASS_NAME = - "org.chromium.chrome.browser.media.ui" - + "MediaNotificationManager$ListenerService"; - public static final String EXTRA_NOTIFICATION_ID = - "MediaNotificationManager.ListenerService.NOTIFICATION_ID"; - - public abstract int getNotificationId(); + public abstract String getServiceClassName(); @Override public void onReceive(Context context, Intent intent) { - Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - queryIntent.setPackage(context.getPackageName()); - - PackageManager pm = context.getPackageManager(); - List<ResolveInfo> infos = pm.queryIntentServices(queryIntent, 0); - assert infos.size() == 1; - - ResolveInfo info = infos.get(0); - ComponentName component = new ComponentName(info.serviceInfo.packageName, - info.serviceInfo.name); - assert LISTENER_SERVICE_CLASS_NAME.equals(component.getClassName()); - - intent.setComponent(component); - intent.putExtra(EXTRA_NOTIFICATION_ID, getNotificationId()); + intent.setClassName(context, getServiceClassName()); context.startService(intent); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java index 7ed81e46..ad4bd08 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -29,11 +29,12 @@ import android.widget.RemoteViews; import org.chromium.base.ApiCompatibilityUtils; -import org.chromium.base.Log; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import org.chromium.chrome.browser.tab.Tab; +import javax.annotation.Nullable; + /** * A class for notifications that provide information and optional media controls for a given media. * Internally implements a Service for transforming notification Intents into @@ -63,16 +64,6 @@ "MediaNotificationManager.ListenerService.PAUSE"; private static final String ACTION_STOP = "MediaNotificationManager.ListenerService.STOP"; - private static final String EXTRA_NOTIFICATION_ID = - MediaButtonReceiver.EXTRA_NOTIFICATION_ID; - - // The notification id this service instance corresponds to. - private int mNotificationId = MediaNotificationInfo.INVALID_ID; - - private PendingIntent getPendingIntent(String action) { - Intent intent = getIntent(this, mNotificationId).setAction(action); - return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); - } @Override public IBinder onBind(Intent intent) { @@ -83,10 +74,10 @@ public void onDestroy() { super.onDestroy(); - MediaNotificationManager manager = getManager(mNotificationId); + MediaNotificationManager manager = getManager(); if (manager == null) return; - manager.onServiceDestroyed(mNotificationId); + manager.onServiceDestroyed(); } @Override @@ -96,27 +87,13 @@ return START_NOT_STICKY; } + @Nullable + protected abstract MediaNotificationManager getManager(); + private boolean processIntent(Intent intent) { if (intent == null) return false; - int notificationId = intent.getIntExtra( - EXTRA_NOTIFICATION_ID, MediaNotificationInfo.INVALID_ID); - - // The notification id must always be valid and should match the first notification id - // the service got via the intent. - if (notificationId == MediaNotificationInfo.INVALID_ID - || (mNotificationId != MediaNotificationInfo.INVALID_ID - && mNotificationId != notificationId)) { - Log.w(TAG, "The service intent's notification id is invalid: ", notificationId); - return false; - } - - // Either the notification id matches or it's the first intent we've got. - mNotificationId = notificationId; - - assert mNotificationId != MediaNotificationInfo.INVALID_ID; - - MediaNotificationManager manager = getManager(mNotificationId); + MediaNotificationManager manager = getManager(); if (manager == null || manager.mMediaNotificationInfo == null) return false; manager.onServiceStarted(this); @@ -191,6 +168,12 @@ super.onDestroy(); } + @Override + @Nullable + protected MediaNotificationManager getManager() { + return MediaNotificationManager.getManager(NOTIFICATION_ID); + } + private BroadcastReceiver mAudioBecomingNoisyReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -200,7 +183,6 @@ Intent i = new Intent(context, PlaybackListenerService.class); i.setAction(intent.getAction()); - i.putExtra(ListenerService.EXTRA_NOTIFICATION_ID, NOTIFICATION_ID); context.startService(i); } }; @@ -211,6 +193,12 @@ */ public static final class PresentationListenerService extends ListenerService { private static final int NOTIFICATION_ID = R.id.presentation_notification; + + @Override + @Nullable + protected MediaNotificationManager getManager() { + return MediaNotificationManager.getManager(NOTIFICATION_ID); + } } // Two classes to specify the right notification id in the intent. @@ -220,8 +208,8 @@ */ public static final class PlaybackMediaButtonReceiver extends MediaButtonReceiver { @Override - public int getNotificationId() { - return PlaybackListenerService.NOTIFICATION_ID; + public String getServiceClassName() { + return PlaybackListenerService.class.getName(); } } @@ -230,29 +218,33 @@ */ public static final class PresentationMediaButtonReceiver extends MediaButtonReceiver { @Override - public int getNotificationId() { - return PresentationListenerService.NOTIFICATION_ID; + public String getServiceClassName() { + return PresentationListenerService.class.getName(); } } - private static Intent getIntent(Context context, int notificationId) { + private Intent createIntent(Context context) { Intent intent = null; - if (notificationId == PlaybackListenerService.NOTIFICATION_ID) { + if (mMediaNotificationInfo.id == PlaybackListenerService.NOTIFICATION_ID) { intent = new Intent(context, PlaybackListenerService.class); - } else if (notificationId == PresentationListenerService.NOTIFICATION_ID) { + } else if (mMediaNotificationInfo.id == PresentationListenerService.NOTIFICATION_ID) { intent = new Intent(context, PresentationListenerService.class); - } else { - return null; } - return intent.putExtra(ListenerService.EXTRA_NOTIFICATION_ID, notificationId); + return intent; } - private static String getButtonReceiverClassName(int notificationId) { - if (notificationId == PlaybackListenerService.NOTIFICATION_ID) { + private PendingIntent createPendingIntent(String action) { + assert mService != null; + Intent intent = createIntent(mService).setAction(action); + return PendingIntent.getService(mService, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + } + + private String getButtonReceiverClassName() { + if (mMediaNotificationInfo.id == PlaybackListenerService.NOTIFICATION_ID) { return PlaybackMediaButtonReceiver.class.getName(); } - if (notificationId == PresentationListenerService.NOTIFICATION_ID) { + if (mMediaNotificationInfo.id == PresentationListenerService.NOTIFICATION_ID) { return PresentationMediaButtonReceiver.class.getName(); } @@ -266,24 +258,22 @@ * changed from the last one. * * @param applicationContext context to create the notification with - * @param notificationInfoBuilder information to show in the notification + * @param notificationInfo information to show in the notification */ public static void show(Context applicationContext, - MediaNotificationInfo.Builder notificationInfoBuilder) { + MediaNotificationInfo notificationInfo) { synchronized (LOCK) { if (sManagers == null) { sManagers = new SparseArray<MediaNotificationManager>(); } } - MediaNotificationInfo notificationInfo = notificationInfoBuilder.build(); MediaNotificationManager manager = sManagers.get(notificationInfo.id); if (manager == null) { - manager = new MediaNotificationManager(applicationContext); + manager = new MediaNotificationManager(applicationContext, notificationInfo.id); sManagers.put(notificationInfo.id, manager); } - manager.mNotificationInfoBuilder = notificationInfoBuilder; manager.showNotification(notificationInfo); } @@ -352,8 +342,8 @@ private final Bitmap mMediaSessionIcon; + // |mMediaNotificationInfo| should be not null if and only if the notification is showing. private MediaNotificationInfo mMediaNotificationInfo; - private MediaNotificationInfo.Builder mNotificationInfoBuilder; private MediaSessionCompat mMediaSession; @@ -377,7 +367,7 @@ private final MediaSessionCallback mMediaSessionCallback = new MediaSessionCallback(this); - private MediaNotificationManager(Context context) { + private MediaNotificationManager(Context context, int notificationId) { mContext = context; mPlayDescription = context.getResources().getString(R.string.accessibility_play); mPauseDescription = context.getResources().getString(R.string.accessibility_pause); @@ -403,69 +393,51 @@ /** * Handles the service destruction destruction. */ - private void onServiceDestroyed(int notificationId) { + private void onServiceDestroyed() { + // Service already detached if (mService == null) return; + // Notification is not showing + if (mMediaNotificationInfo == null) return; - if (notificationId != -1) clear(notificationId); + clear(mMediaNotificationInfo.id); mNotificationBuilder = null; mService = null; } private void onPlay(int actionSource) { - if (!mMediaNotificationInfo.isPaused) return; - - mMediaNotificationInfo = mNotificationInfoBuilder.setPaused(false).build(); - updateNotification(); - mMediaNotificationInfo.listener.onPlay(actionSource); } private void onPause(int actionSource) { - if (mMediaNotificationInfo.isPaused) return; - - mMediaNotificationInfo = mNotificationInfoBuilder.setPaused(true).build(); - updateNotification(); - mMediaNotificationInfo.listener.onPause(actionSource); } private void onStop(int actionSource) { - // hideNotification() below will clear |mMediaNotificationInfo| but {@link - // MediaNotificationListener}.onStop() might also clear it so keep the listener and call it - // later. - // TODO(avayvod): make the notification delegate update its state, not the notification - // manager. See https://crbug.com/546981 - MediaNotificationListener listener = mMediaNotificationInfo.listener; - - hideNotification(mMediaNotificationInfo.tabId); - - listener.onStop(actionSource); + mMediaNotificationInfo.listener.onStop(actionSource); } private void showNotification(MediaNotificationInfo mediaNotificationInfo) { - mContext.startService(getIntent(mContext, mediaNotificationInfo.id)); - if (mediaNotificationInfo.equals(mMediaNotificationInfo)) return; mMediaNotificationInfo = mediaNotificationInfo; + mContext.startService(createIntent(mContext)); + updateNotification(); } private void clearNotification() { if (mMediaNotificationInfo == null) return; - int notificationId = mMediaNotificationInfo.id; - NotificationManagerCompat manager = NotificationManagerCompat.from(mContext); - manager.cancel(notificationId); + manager.cancel(mMediaNotificationInfo.id); if (mMediaSession != null) { mMediaSession.setActive(false); mMediaSession.release(); mMediaSession = null; } - mContext.stopService(getIntent(mContext, notificationId)); + mContext.stopService(createIntent(mContext)); mMediaNotificationInfo = null; } @@ -486,7 +458,7 @@ && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || mMediaNotificationInfo.supportsStop()) { contentView.setOnClickPendingIntent(R.id.button1, - mService.getPendingIntent(ListenerService.ACTION_STOP)); + createPendingIntent(ListenerService.ACTION_STOP)); contentView.setContentDescription(R.id.button1, mStopDescription); // If the play/pause needs to be shown, it moves over to the second button from the end. @@ -506,13 +478,13 @@ contentView.setImageViewResource(playPauseButtonId, R.drawable.ic_vidcontrol_play); contentView.setContentDescription(playPauseButtonId, mPlayDescription); contentView.setOnClickPendingIntent(playPauseButtonId, - mService.getPendingIntent(ListenerService.ACTION_PLAY)); + createPendingIntent(ListenerService.ACTION_PLAY)); } else { // If we're here, the notification supports play/pause button and is playing. contentView.setImageViewResource(playPauseButtonId, R.drawable.ic_vidcontrol_pause); contentView.setContentDescription(playPauseButtonId, mPauseDescription); contentView.setOnClickPendingIntent(playPauseButtonId, - mService.getPendingIntent(ListenerService.ACTION_PAUSE)); + createPendingIntent(ListenerService.ACTION_PAUSE)); } contentView.setViewVisibility(playPauseButtonId, View.VISIBLE); @@ -547,11 +519,7 @@ private void updateNotification() { if (mService == null) return; - if (mMediaNotificationInfo == null) { - // Notification was hidden before we could update it. - assert mNotificationBuilder == null; - return; - } + if (mMediaNotificationInfo == null) return; // Android doesn't badge the icons for RemoteViews automatically when // running the app under the Work profile. @@ -566,7 +534,7 @@ .setSmallIcon(mMediaNotificationInfo.icon) .setAutoCancel(false) .setLocalOnly(true) - .setDeleteIntent(mService.getPendingIntent(ListenerService.ACTION_STOP)); + .setDeleteIntent(createPendingIntent(ListenerService.ACTION_STOP)); } if (mMediaNotificationInfo.supportsSwipeAway()) { @@ -624,7 +592,7 @@ mContext, mContext.getString(R.string.app_name), new ComponentName(mContext.getPackageName(), - getButtonReceiverClassName(mMediaNotificationInfo.id)), + getButtonReceiverClassName()), null); mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java index 4b1ceb72..4dce275 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaSessionTabHelper.java
@@ -35,6 +35,7 @@ private WebContents mWebContents; private WebContentsObserver mWebContentsObserver; private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE; + private MediaNotificationInfo.Builder mNotificationInfoBuilder = null; private MediaNotificationListener mControlsListener = new MediaNotificationListener() { @Override @@ -71,18 +72,14 @@ if (activity != null) { activity.setVolumeControlStream(mPreviousVolumeControlStream); } + mNotificationInfoBuilder = null; } private WebContentsObserver createWebContentsObserver(WebContents webContents) { return new WebContentsObserver(webContents) { @Override public void destroy() { - if (mTab == null) { - MediaNotificationManager.clear(R.id.media_playback_notification); - } else { - hideNotification(); - } - + hideNotification(); super.destroy(); } @@ -100,25 +97,27 @@ + "Showing the full URL instead."); } + mNotificationInfoBuilder = new MediaNotificationInfo.Builder() + .setTitle(sanitizeMediaTitle(mTab.getTitle())) + .setPaused(isPaused) + .setOrigin(origin) + .setTabId(mTab.getId()) + .setPrivate(mTab.isIncognito()) + .setIcon(R.drawable.audio_playing) + .setActions(MediaNotificationInfo.ACTION_PLAY_PAUSE + | MediaNotificationInfo.ACTION_SWIPEAWAY) + .setId(R.id.media_playback_notification) + .setListener(mControlsListener); + MediaNotificationManager.show(ApplicationStatus.getApplicationContext(), - new MediaNotificationInfo.Builder() - .setTitle(sanitizeMediaTitle(mTab.getTitle())) - .setPaused(isPaused) - .setOrigin(origin) - .setTabId(mTab.getId()) - .setPrivate(mTab.isIncognito()) - .setIcon(R.drawable.audio_playing) - .setActions(MediaNotificationInfo.ACTION_PLAY_PAUSE - | MediaNotificationInfo.ACTION_SWIPEAWAY) - .setId(R.id.media_playback_notification) - .setListener(mControlsListener)); + mNotificationInfoBuilder.build()); + Activity activity = getActivityFromTab(mTab); if (activity != null) { activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); } } }; - } private void setWebContents(WebContents webContents) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java index ac75a281..683f501 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -68,6 +68,7 @@ import org.chromium.chrome.browser.ssl.ConnectionSecurityLevel; import org.chromium.chrome.browser.ssl.SecurityStateModel; import org.chromium.chrome.browser.tab.TabUma.TabCreationState; +import org.chromium.chrome.browser.tabmodel.SingleTabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; @@ -1246,9 +1247,11 @@ * @param initiallyHidden Only used if {@code webContents} is {@code null}. Determines * whether or not the newly created {@link WebContents} will be hidden * or not. + * @param unfreeze Whether there should be an attempt to restore state at the end of + * the initialization. */ public final void initialize(WebContents webContents, TabContentManager tabContentManager, - TabDelegateFactory delegateFactory, boolean initiallyHidden) { + TabDelegateFactory delegateFactory, boolean initiallyHidden, boolean unfreeze) { try { TraceEvent.begin("Tab.initialize"); @@ -1267,7 +1270,10 @@ // If there is a frozen WebContents state or a pending lazy load, don't create a new // WebContents. - if (getFrozenContentsState() != null || getPendingLoadParams() != null) return; + if (getFrozenContentsState() != null || getPendingLoadParams() != null) { + if (unfreeze) unfreezeContents(); + return; + } boolean creatingWebContents = webContents == null; if (creatingWebContents) { @@ -1485,6 +1491,9 @@ R.string.accessibility_content_view)); cvc.initialize(cv, cv, webContents, getWindowAndroid()); setContentViewCore(cvc); + if (mActivity.getTabModelSelector() instanceof SingleTabModelSelector) { + getContentViewCore().setFullscreenRequiredForOrientationLock(false); + } } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java index d438337..0b9382b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -129,7 +129,8 @@ == TabModel.INVALID_TAB_INDEX; tab = Tab.createLiveTab(assignedTabId, mActivity, mIncognito, mNativeWindow, type, parentId, !openInForeground); - tab.initialize(webContents, mTabContentManager, delegateFactory, !openInForeground); + tab.initialize( + webContents, mTabContentManager, delegateFactory, !openInForeground, false); tab.setParentIntent(parentIntent); webContents.resumeLoadingCreatedWebContents(); } else if (!openInForeground && SysUtils.isLowEndDevice()) { @@ -141,7 +142,7 @@ // currently no way to pipe that information to this function. tab = Tab.createTabForLazyLoad(mActivity, mIncognito, mNativeWindow, type, parentId, loadUrlParams); - tab.initialize(null, mTabContentManager, delegateFactory, !openInForeground); + tab.initialize(null, mTabContentManager, delegateFactory, !openInForeground, false); mTabSaver.addTabToSaveQueue(tab); } else { webContents = @@ -150,7 +151,8 @@ tab = Tab.createLiveTab(Tab.INVALID_TAB_ID, mActivity, mIncognito, mNativeWindow, type, parentId, !openInForeground); - tab.initialize(webContents, mTabContentManager, delegateFactory, !openInForeground); + tab.initialize( + webContents, mTabContentManager, delegateFactory, !openInForeground, false); tab.loadUrl(loadUrlParams); } tab.getTabRedirectHandler().updateIntent(intent); @@ -183,7 +185,8 @@ Tab tab = Tab.createLiveTab(Tab.INVALID_TAB_ID, mActivity, mIncognito, mNativeWindow, type, parentId, !openInForeground); tab.initialize( - webContents, mTabContentManager, new TabDelegateFactory(), !openInForeground); + webContents, mTabContentManager, + new TabDelegateFactory(), !openInForeground, false); mTabModel.addTab(tab, position, type); return true; } @@ -276,7 +279,7 @@ id, mActivity, state.isIncognito(), mNativeWindow, state.parentId, state); boolean selectTab = mOrderController.willOpenInForeground(TabLaunchType.FROM_RESTORE, state.isIncognito()); - tab.initialize(null, mTabContentManager, new TabDelegateFactory(), !selectTab); + tab.initialize(null, mTabContentManager, new TabDelegateFactory(), !selectTab, false); assert state.isIncognito() == mIncognito; mTabModel.addTab(tab, index, TabLaunchType.FROM_RESTORE); return tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java index f9194fe4..256a8ca 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivityTab.java
@@ -17,7 +17,6 @@ import org.chromium.chrome.browser.tab.TabUma.TabCreationState; import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; import org.chromium.content_public.browser.LoadUrlParams; -import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContentsObserver; import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.WindowAndroid; @@ -73,8 +72,7 @@ private void initializeFullScreenActivityTab(TabContentManager tabContentManager, boolean unfreeze, TopControlsVisibilityDelegate topControlsVisibilityDelegate) { - initialize(null, tabContentManager, new FullScreenDelegateFactory(), false); - if (unfreeze) unfreezeContents(); + initialize(null, tabContentManager, new FullScreenDelegateFactory(), false, unfreeze); mObserver = createWebContentsObserver(); mTopControlsVisibilityDelegate = topControlsVisibilityDelegate; } @@ -105,12 +103,6 @@ }; } - @Override - protected void initContentViewCore(WebContents webContents) { - super.initContentViewCore(webContents); - getContentViewCore().setFullscreenRequiredForOrientationLock(false); - } - /** * Loads the given {@code url}. * @param url URL to load.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerInDocumentModeIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerInDocumentModeIntegrationTest.java index beb0bc84..4391335 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerInDocumentModeIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerInDocumentModeIntegrationTest.java
@@ -17,7 +17,6 @@ import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.base.test.util.Restriction; import org.chromium.chrome.browser.document.DocumentModeTestBase; -import org.chromium.chrome.browser.document.DocumentTab; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; import org.chromium.chrome.test.util.ChromeTabUtils; @@ -194,7 +193,7 @@ } }); - switchToTab((DocumentTab) tabs[1]); + switchToTab(tabs[1]); // Verify that the renderer visibility was flipped. mBindingManager.assertIsInBackground(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeLowEndTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeLowEndTest.java index 2215adad..6c2f5dc 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeLowEndTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeLowEndTest.java
@@ -73,7 +73,7 @@ assertEquals(1, tabCreatedCallback.getCallCount()); assertEquals(0, tabLoadStartedCallback.getCallCount()); - switchToTab((DocumentTab) backgroundTab.get()); + switchToTab(backgroundTab.get()); tabLoadStartedCallback.waitForCallback(0); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java index 7b33af79..4187a706 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/document/DocumentModeTestBase.java
@@ -284,7 +284,7 @@ /** * Switches to the specified tab and waits until its activity is brought to the foreground. */ - protected void switchToTab(final DocumentTab tab) throws Exception { + protected void switchToTab(final Tab tab) throws Exception { final TabModel tabModel = ChromeApplication.getDocumentTabModelSelector().getCurrentModel(); CriteriaHelper.pollForCriteria(new Criteria() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/media/OWNERS new file mode 100644 index 0000000..dc2593f --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/OWNERS
@@ -0,0 +1,2 @@ +avayvod@chromium.org +mlamouri@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/PauseOnHeadsetUnplugTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/PauseOnHeadsetUnplugTest.java index 944174b..4f6731e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/PauseOnHeadsetUnplugTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/PauseOnHeadsetUnplugTest.java
@@ -67,8 +67,6 @@ Intent i = new Intent(getInstrumentation().getTargetContext(), MediaNotificationManager.PlaybackListenerService.class); i.setAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); - i.putExtra(MediaButtonReceiver.EXTRA_NOTIFICATION_ID, - R.id.media_playback_notification); getInstrumentation().getContext().startService(i); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java index a67dc1f..8b933b0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabUmaTest.java
@@ -51,7 +51,7 @@ Tab bgTab = Tab.createTabForLazyLoad(getActivity(), false, getActivity().getWindowAndroid(), TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID, new LoadUrlParams(TEST_URL)); - bgTab.initialize(null, null, new TabDelegateFactory(), true); + bgTab.initialize(null, null, new TabDelegateFactory(), true, false); return bgTab; } }); @@ -104,7 +104,7 @@ Tab bgTab = Tab.createLiveTab(Tab.INVALID_TAB_ID, getActivity(), false, getActivity().getWindowAndroid(), TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID, true); - bgTab.initialize(null, null, new TabDelegateFactory(), true); + bgTab.initialize(null, null, new TabDelegateFactory(), true, false); bgTab.loadUrl(new LoadUrlParams(TEST_URL)); bgTab.show(TabSelectionType.FROM_USER); return bgTab; @@ -121,7 +121,7 @@ Tab bgTab = Tab.createLiveTab(Tab.INVALID_TAB_ID, getActivity(), false, getActivity().getWindowAndroid(), TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID, true); - bgTab.initialize(null, null, new TabDelegateFactory(), true); + bgTab.initialize(null, null, new TabDelegateFactory(), true, false); bgTab.loadUrl(new LoadUrlParams(TEST_URL)); // Simulate the renderer being killed by the OS. bgTab.simulateRendererKilledForTesting(false); @@ -140,7 +140,7 @@ Tab bgTab = Tab.createTabForLazyLoad(getActivity(), false, getActivity().getWindowAndroid(), TabLaunchType.FROM_LONGPRESS_BACKGROUND, Tab.INVALID_TAB_ID, new LoadUrlParams(TEST_URL)); - bgTab.initialize(null, null, new TabDelegateFactory(), true); + bgTab.initialize(null, null, new TabDelegateFactory(), true, false); bgTab.show(TabSelectionType.FROM_USER); return bgTab; }
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/PassphraseTypeDialogFragmentTest.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/PassphraseTypeDialogFragmentTest.java new file mode 100644 index 0000000..b9f7474 --- /dev/null +++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/PassphraseTypeDialogFragmentTest.java
@@ -0,0 +1,127 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.sync.ui; + +import android.test.suitebuilder.annotation.SmallTest; +import android.widget.CheckedTextView; +import android.widget.ListView; + +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.test.ChromeActivityTestCaseBase; +import org.chromium.sync.PassphraseType; + +/** + * Tests to make sure that PassphraseTypeDialogFragment presents the correct options. + */ +public class PassphraseTypeDialogFragmentTest extends ChromeActivityTestCaseBase<ChromeActivity> { + private static final String TAG = "PassphraseTypeDialogFragmentTest"; + + private static final boolean ENABLED = true; + private static final boolean DISABLED = false; + private static final boolean CHECKED = true; + private static final boolean UNCHECKED = false; + + private static class TypeOptions { + public final PassphraseType type; + public final boolean isEnabled; + public final boolean isChecked; + public TypeOptions(PassphraseType type, boolean isEnabled, boolean isChecked) { + this.type = type; + this.isEnabled = isEnabled; + this.isChecked = isChecked; + } + } + + private PassphraseTypeDialogFragment mTypeFragment; + + public PassphraseTypeDialogFragmentTest() { + super(ChromeActivity.class); + } + + @Override + public void startMainActivity() throws InterruptedException { + startMainActivityOnBlankPage(); + } + + @SmallTest + @Feature({"Sync"}) + public void testKeystoreEncryptionOptions() throws Exception { + createFragment(PassphraseType.KEYSTORE_PASSPHRASE, true); + assertPassphraseTypeOptions( + new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, ENABLED, UNCHECKED), + new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, ENABLED, CHECKED)); + } + + @SmallTest + @Feature({"Sync"}) + public void testCustomEncryptionOptions() throws Exception { + createFragment(PassphraseType.CUSTOM_PASSPHRASE, true); + assertPassphraseTypeOptions( + new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, DISABLED, CHECKED), + new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, DISABLED, UNCHECKED)); + } + + @SmallTest + @Feature({"Sync"}) + public void testFrozenImplicitEncryptionOptions() throws Exception { + createFragment(PassphraseType.FROZEN_IMPLICIT_PASSPHRASE, true); + assertPassphraseTypeOptions( + new TypeOptions(PassphraseType.FROZEN_IMPLICIT_PASSPHRASE, DISABLED, CHECKED), + new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, DISABLED, UNCHECKED)); + } + + @SmallTest + @Feature({"Sync"}) + public void testImplicitEncryptionOptions() throws Exception { + createFragment(PassphraseType.IMPLICIT_PASSPHRASE, true); + assertPassphraseTypeOptions( + new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, ENABLED, UNCHECKED), + new TypeOptions(PassphraseType.IMPLICIT_PASSPHRASE, ENABLED, CHECKED)); + } + + @SmallTest + @Feature({"Sync"}) + public void testKeystoreEncryptionOptionsEncryptEverythingDisallowed() throws Exception { + createFragment(PassphraseType.KEYSTORE_PASSPHRASE, false); + assertPassphraseTypeOptions( + new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, DISABLED, UNCHECKED), + new TypeOptions(PassphraseType.KEYSTORE_PASSPHRASE, ENABLED, CHECKED)); + } + + @SmallTest + @Feature({"Sync"}) + public void testImplicitEncryptionOptionsEncryptEverythingDisallowed() throws Exception { + createFragment(PassphraseType.IMPLICIT_PASSPHRASE, false); + assertPassphraseTypeOptions( + new TypeOptions(PassphraseType.CUSTOM_PASSPHRASE, DISABLED, UNCHECKED), + new TypeOptions(PassphraseType.IMPLICIT_PASSPHRASE, ENABLED, CHECKED)); + } + + public void createFragment(PassphraseType type, boolean isEncryptEverythingAllowed) { + mTypeFragment = PassphraseTypeDialogFragment.create(type, 0, isEncryptEverythingAllowed); + mTypeFragment.show(getActivity().getFragmentManager(), TAG); + getInstrumentation().waitForIdleSync(); + } + + public void assertPassphraseTypeOptions(TypeOptions... optionsList) { + ListView listView = + (ListView) mTypeFragment.getDialog().findViewById(R.id.passphrase_type_list); + assertEquals("Number of options doesn't match.", optionsList.length, listView.getCount()); + PassphraseTypeDialogFragment.Adapter adapter = + (PassphraseTypeDialogFragment.Adapter) listView.getAdapter(); + + for (int i = 0; i < optionsList.length; i++) { + TypeOptions options = optionsList[i]; + assertEquals("Option " + i + " type is wrong.", options.type, adapter.getType(i)); + CheckedTextView checkedView = (CheckedTextView) listView.getChildAt(i); + assertEquals("Option " + i + " enabled state is wrong.", + options.isEnabled, checkedView.isEnabled()); + assertEquals("Option " + i + " checked state is wrong.", + options.isChecked, checkedView.isChecked()); + } + } +}
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java index 6b00ee1d..0ca49a1 100644 --- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java +++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java
@@ -14,9 +14,7 @@ import android.support.v7.app.AlertDialog; import android.test.suitebuilder.annotation.SmallTest; import android.widget.Button; -import android.widget.CheckedTextView; import android.widget.EditText; -import android.widget.ListView; import android.widget.TextView; import org.chromium.base.ThreadUtils; @@ -225,41 +223,6 @@ } /** - * Make sure that the encryption UI presents the correct options. - * - * By default it should show the CUSTOM and KEYSTORE options, in that order. - * KEYSTORE should be selected but both should be enabled. - */ - @SmallTest - @Feature({"Sync"}) - public void testDefaultEncryptionOptions() throws Exception { - setUpTestAccountAndSignInToSync(); - SyncTestUtil.waitForSyncActive(); - final SyncCustomizationFragment fragment = startSyncCustomizationFragment(); - Preference encryption = getEncryption(fragment); - clickPreference(encryption); - - PassphraseTypeDialogFragment typeFragment = getPassphraseTypeDialogFragment(); - ListView listView = (ListView) typeFragment.getDialog() - .findViewById(R.id.passphrase_type_list); - PassphraseTypeDialogFragment.Adapter adapter = - (PassphraseTypeDialogFragment.Adapter) listView.getAdapter(); - - // Confirm that correct types show up in the correct order. - assertEquals(PassphraseType.CUSTOM_PASSPHRASE, adapter.getType(0)); - assertEquals(PassphraseType.KEYSTORE_PASSPHRASE, adapter.getType(1)); - assertEquals(2, listView.getCount()); - - // Make sure they are both enabled and the correct one is selected. - CheckedTextView customView = (CheckedTextView) listView.getChildAt(0); - CheckedTextView keystoreView = (CheckedTextView) listView.getChildAt(1); - assertTrue("The custom passphrase view should be enabled.", customView.isEnabled()); - assertFalse("The custom passphrase option should be checked.", customView.isChecked()); - assertTrue("The keystore passphrase view should be enabled.", keystoreView.isEnabled()); - assertTrue("The keystore passphrase option should be checked.", keystoreView.isChecked()); - } - - /** * Test that choosing a passphrase type while sync is off doesn't crash. * * This is a regression test for http://crbug.com/507557.
diff --git a/chrome/app/file_pre_reader_win.cc b/chrome/app/file_pre_reader_win.cc index fb8febb..6062612 100644 --- a/chrome/app/file_pre_reader_win.cc +++ b/chrome/app/file_pre_reader_win.cc
@@ -7,12 +7,15 @@ #include <windows.h> #include <stdint.h> +#include "base/debug/alias.h" #include "base/files/file.h" +#include "base/files/memory_mapped_file.h" #include "base/logging.h" #include "base/scoped_native_library.h" #include "base/threading/thread_restrictions.h" #include "base/win/pe_image.h" #include "base/win/windows_version.h" +#include "components/startup_metric_utils/browser/startup_metric_utils.h" namespace { @@ -47,8 +50,7 @@ } // namespace -bool PreReadFile(const base::FilePath& file_path, int step_size) { - DCHECK_GT(step_size, 0); +void PreReadFile(const base::FilePath& file_path) { base::ThreadRestrictions::AssertIOAllowed(); if (base::win::GetVersion() > base::win::VERSION_XP) { @@ -57,21 +59,22 @@ base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_SEQUENTIAL_SCAN); if (!file.IsValid()) - return false; + return; - char* buffer = reinterpret_cast<char*>(::VirtualAlloc( - nullptr, static_cast<DWORD>(step_size), MEM_COMMIT, PAGE_READWRITE)); + const DWORD kStepSize = 1024 * 1024; + char* buffer = reinterpret_cast<char*>( + ::VirtualAlloc(nullptr, kStepSize, MEM_COMMIT, PAGE_READWRITE)); if (!buffer) - return false; + return; - while (file.ReadAtCurrentPos(buffer, step_size) > 0) {} + while (file.ReadAtCurrentPos(buffer, kStepSize) > 0) {} ::VirtualFree(buffer, 0, MEM_RELEASE); } else { // WinXP branch. Here, reading the DLL from disk doesn't do what we want so // instead we pull the pages into memory and touch pages at a stride. We use - // the system's page size as the stride, ignoring the passed in |step_size|, - // to make sure each page in the range is touched. + // the system's page size as the stride, to make sure each page in the range + // is touched. // Don't show an error popup when |file_path| is not a valid PE file. UINT previous_error_mode = ::SetErrorMode(SEM_FAILCRITICALERRORS); @@ -85,11 +88,11 @@ // Pre-reading non-PE files is not supported on XP. if (!dll_module.is_valid()) - return false; + return; base::win::PEImage pe_image(dll_module.get()); if (!pe_image.VerifyMagic()) - return false; + return; // We don't want to read past the end of the module (which could trigger // an access violation), so make sure to check the image size. @@ -99,6 +102,53 @@ // Page in the module. TouchPagesInRange(dll_module.get(), dll_module_length); } +} - return true; +bool IsMemoryMappedFileWarm(const base::MemoryMappedFile& memory_mapped_file) { + base::ThreadRestrictions::AssertIOAllowed(); + if (!memory_mapped_file.IsValid()) + return false; + + uint32_t initial_hard_fault_count = 0; + if (!startup_metric_utils::GetHardFaultCountForCurrentProcess( + &initial_hard_fault_count)) + return false; + + // Read a byte from the first page of the memory map. + const uint8_t dummy = *(memory_mapped_file.data()); + base::debug::Alias(&dummy); + + uint32_t final_hard_fault_count = 0; + if (!startup_metric_utils::GetHardFaultCountForCurrentProcess( + &final_hard_fault_count)) + return false; + + // Return true if reading a byte from the first page of the memory map + // generated a hard fault. + return initial_hard_fault_count == final_hard_fault_count; +} + +void PreReadMemoryMappedFile(const base::MemoryMappedFile& memory_mapped_file, + const base::FilePath& file_path) { + base::ThreadRestrictions::AssertIOAllowed(); + if (!memory_mapped_file.IsValid()) + return; + + // Load ::PrefetchVirtualMemory dynamically, because it is only available on + // Win8+. + using PrefetchVirtualMemoryPtr = decltype(::PrefetchVirtualMemory)*; + PrefetchVirtualMemoryPtr prefetch_virtual_memory = + reinterpret_cast<PrefetchVirtualMemoryPtr>(::GetProcAddress( + ::GetModuleHandle(L"kernel32.dll"), "PrefetchVirtualMemory")); + if (!prefetch_virtual_memory) { + // If ::PrefetchVirtualMemory is not available, fall back to PreReadFile. + PreReadFile(file_path); + return; + } + + WIN32_MEMORY_RANGE_ENTRY memory_range; + memory_range.VirtualAddress = const_cast<void*>( + reinterpret_cast<const void*>(memory_mapped_file.data())); + memory_range.NumberOfBytes = memory_mapped_file.length(); + prefetch_virtual_memory(::GetCurrentProcess(), 1U, &memory_range, 0); }
diff --git a/chrome/app/file_pre_reader_win.h b/chrome/app/file_pre_reader_win.h index bf7008b..d2f75f76 100644 --- a/chrome/app/file_pre_reader_win.h +++ b/chrome/app/file_pre_reader_win.h
@@ -10,14 +10,26 @@ namespace base { class FilePath; +class MemoryMappedFile; } // Reads |file_path| to avoid touching the disk when the file is actually used. // The function checks the Windows version to determine which pre-reading -// mechanism to use. On Vista+, chunks of |step_size| bytes are read into a -// buffer. The bigger |step_size| is, the faster the file is pre-read (up to -// about 4MB according to local tests), but also the more memory is allocated -// for the buffer. On XP, pre-reading non-PE files is not supported. -bool PreReadFile(const base::FilePath& file_path, int step_size); +// mechanism to use. On Vista+, chunks of 1 MB are read into a buffer. On XP, +// pre-reading non-PE files is not supported. +void PreReadFile(const base::FilePath& file_path); + +// Returns true if the first page of |memory_mapped_file| is warm. This function +// must be called from a single-threaded process because it relies on the +// process-wide hard fault counter. Returns false on failure. +bool IsMemoryMappedFileWarm(const base::MemoryMappedFile& memory_mapped_file); + +// Reads |memory_mapped_file| to avoid touching the disk when the mapped file is +// actually used. The function checks the Windows version to determine which +// pre-reading mechanism to use. On Win8+, it uses ::PrefetchVirtualMemory. On +// previous Windows versions, is uses PreReadFile (declared above). |file_path| +// is the path to the memory mapped file. +void PreReadMemoryMappedFile(const base::MemoryMappedFile& memory_mapped_file, + const base::FilePath& file_path); #endif // CHROME_APP_FILE_PRE_READER_WIN_H_
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 1e36c0f1..a8318659 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -6020,6 +6020,9 @@ <message name="IDS_SAVE_PASSWORD" desc="The title of the save password bubble when a password can be saved."> Do you want <ph name="PASSWORD_MANAGER_BRAND">$1<ex>Google Chrome</ex></ph> to save your password for this site? </message> + <message name="IDS_SAVE_ACCOUNT" desc="The title of the save password bubble when a federated credential can be saved."> + Do you want <ph name="PASSWORD_MANAGER_BRAND">$1<ex>Google Chrome</ex></ph> to save your account for this site? + </message> <message name="IDS_UPDATE_PASSWORD" desc="The title of the save password bubble when a password can be updated."> Do you want <ph name="PASSWORD_MANAGER_BRAND">$1<ex>Google Chrome</ex></ph> to update your password for this site? </message> @@ -7437,16 +7440,9 @@ Enable with no field highlighting </message> <if expr="not is_android"> - <if expr="use_titlecase"> - <message name="IDS_PASSWORD_MANAGER_SAVE_BUTTON" desc="Save button text for password manager"> - Save Password - </message> - </if> - <if expr="not use_titlecase"> - <message name="IDS_PASSWORD_MANAGER_SAVE_BUTTON" desc="Save button text for password manager"> - Save password - </message> - </if> + <message name="IDS_PASSWORD_MANAGER_SAVE_BUTTON" desc="Save button text for password manager"> + Save + </message> <message name="IDS_PASSWORD_MANAGER_UPDATE_BUTTON" desc="Update button text for password manager"> Update password </message> @@ -13711,6 +13707,9 @@ <message name="IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME" desc="Name for screens in the desktop media picker UI when there are multiple monitors."> Screen <ph name="SCREEN_INDEX">$1<ex>1</ex></ph> </message> + <message name="IDS_DESTOP_MEDIA_PICKER_VIRTUAL_SCREEN_NAME" desc="The name of the virtual display which is shown in the picker"> + Virtual Display + </message> <!-- Local Device Discovery display strings --> <if expr="enable_service_discovery">
diff --git a/chrome/app/main_dll_loader_win.cc b/chrome/app/main_dll_loader_win.cc index fe64b1d..4f5a519a 100644 --- a/chrome/app/main_dll_loader_win.cc +++ b/chrome/app/main_dll_loader_win.cc
@@ -13,6 +13,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/environment.h" +#include "base/files/memory_mapped_file.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -63,26 +64,44 @@ ::SetCurrentDirectoryW(module.DirName().value().c_str()); // Get pre-read options from the PreRead field trial. - bool trial_should_pre_read = true; - bool trial_should_pre_read_high_priority = false; + bool trial_no_pre_read = false; + bool trial_high_priority = false; + bool trial_only_if_cold = false; + bool trial_prefetch_virtual_memory = false; startup_metric_utils::GetPreReadOptions( BrowserDistribution::GetDistribution()->GetRegistryPath(), - &trial_should_pre_read, &trial_should_pre_read_high_priority); + &trial_no_pre_read, &trial_high_priority, &trial_only_if_cold, + &trial_prefetch_virtual_memory); - if (pre_read && trial_should_pre_read) { + // Pre-read the binary to warm the memory caches (avoids a lot of random IO). + if (pre_read && !trial_no_pre_read) { base::ThreadPriority previous_priority = base::ThreadPriority::NORMAL; - if (trial_should_pre_read_high_priority) { + if (trial_high_priority) { previous_priority = base::PlatformThread::GetCurrentThreadPriority(); base::PlatformThread::SetCurrentThreadPriority( base::ThreadPriority::DISPLAY); } - // We pre-read the binary to warm the memory caches (fewer hard faults to - // page parts of the binary in). - const size_t kStepSize = 1024 * 1024; - PreReadFile(module, kStepSize); + if (trial_only_if_cold) { + base::MemoryMappedFile module_memory_map; + const bool map_initialize_success = module_memory_map.Initialize(module); + DCHECK(map_initialize_success); + if (!IsMemoryMappedFileWarm(module_memory_map)) { + if (trial_prefetch_virtual_memory) + PreReadMemoryMappedFile(module_memory_map, module); + else + PreReadFile(module); + } + } else if (trial_prefetch_virtual_memory) { + base::MemoryMappedFile module_memory_map; + const bool map_initialize_success = module_memory_map.Initialize(module); + DCHECK(map_initialize_success); + PreReadMemoryMappedFile(module_memory_map, module); + } else { + PreReadFile(module); + } - if (trial_should_pre_read_high_priority) + if (trial_high_priority) base::PlatformThread::SetCurrentThreadPriority(previous_priority); }
diff --git a/chrome/app/nibs/WrenchMenu.xib b/chrome/app/nibs/AppMenu.xib similarity index 100% rename from chrome/app/nibs/WrenchMenu.xib rename to chrome/app/nibs/AppMenu.xib
diff --git a/chrome/app/nibs/Toolbar.xib b/chrome/app/nibs/Toolbar.xib index 4eaf6c93..c2724a1 100644 --- a/chrome/app/nibs/Toolbar.xib +++ b/chrome/app/nibs/Toolbar.xib
@@ -15,7 +15,7 @@ <outlet property="locationBar_" destination="4" id="31"/> <outlet property="reloadButton_" destination="3" id="19"/> <outlet property="view" destination="1" id="22"/> - <outlet property="wrenchButton_" destination="38" id="41"/> + <outlet property="appMenuButton_" destination="38" id="41"/> </connections> </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index 77b7c33..39fa95e 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd
@@ -384,9 +384,6 @@ <structure type="chrome_scaled_image" name="IDR_INFOBAR_3D_BLOCKED" file="common/infobar_3d_blocked.png" /> <structure type="chrome_scaled_image" name="IDR_INFOBAR_ALT_NAV_URL" file="common/infobar_alt_nav_url.png" /> <structure type="chrome_scaled_image" name="IDR_INFOBAR_APP_BANNER" file="common/infobar_apps.png" /> - <if expr="is_ios"> - <structure type="chrome_scaled_image" name="IDR_INFOBAR_AUTOLOGIN" file="common/infobar_autologin.png" /> - </if> <structure type="chrome_scaled_image" name="IDR_INFOBAR_COOKIE" file="common/infobar_cookie.png" /> <if expr="is_macosx"> <structure type="chrome_scaled_image" name="IDR_INFOBAR_DESKTOP_NOTIFICATIONS" file="legacy/infobar_desktop_notifications.png" />
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc index 1d83f066..c90a0f3 100644 --- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc +++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -142,8 +142,9 @@ // Just UX. emk::kIcons, - // No constant in manifest_constants.cc. - // "author", + // Documented in https://developer.chrome.com/extensions/manifest but not + // implemented anywhere. Still, a lot of apps use it. + "author", // TBD // emk::kBluetooth, @@ -243,9 +244,15 @@ emk::kWebview, }; -// List of permissions based on -// https://developer.chrome.com/apps/declare_permissions. -// TODO(tnagel): Explain generic rationale for decisions. +// List of permissions based on [1] and [2]. Since Public Session users may be +// fully unaware of any apps being installed, their consent to access any kind +// of sensitive information cannot be assumed. Therefore only APIs are +// whitelisted which should not leak sensitive data to the caller. Since the +// privacy boundary is drawn at the API level, no safeguards are required to +// prevent exfiltration and thus apps may communicate freely over any kind of +// network. +// [1] https://developer.chrome.com/apps/declare_permissions +// [2] https://developer.chrome.com/apps/api_other const char* const kSafePermissions[] = { // Risky: Reading accessibility settings could allow to infer health // information. @@ -289,6 +296,15 @@ // potentially buggy, subject to change without notice. // "experimental", + // Fullscreen is a no-op for Public Session. Whitelisting nevertheless to + // broaden the range of supported apps. (The recommended permission names + // are "app.window.*" but their unprefixed counterparts are still + // supported.) + "app.window.fullscreen", + "app.window.fullscreen.overrideEsc", + "fullscreen", + "overrideEscFullscreen", + // TBD // "fileSystem", @@ -426,12 +442,21 @@ LOG(ERROR) << it.key() << " contains a non-string."; return false; } - if (!ArrayContains(kSafePermissions, permission_string)) { - LOG(ERROR) << extension->id() - << " requested non-whitelisted permission: " - << permission_string; - return false; + // Accept whitelisted permissions. + if (ArrayContains(kSafePermissions, permission_string)) { + continue; } + // Allow arbitrary web requests. Don't include <all_urls> because that + // also matches file:// schemes. + if (permission_string.find("https://") == 0 || + permission_string.find("http://") == 0 || + permission_string.find("ftp://") == 0) { + continue; + } + LOG(ERROR) << extension->id() + << " requested non-whitelisted permission: " + << permission_string; + return false; } // "app" may only contain "background". } else if (it.key() == emk::kApp) { @@ -531,8 +556,8 @@ return true; } } else if (account_type_ == policy::DeviceLocalAccount::TYPE_KIOSK_APP) { - // For single-app kiosk sessions, allow platform apps, extesions and - // shared modules. + // For single-app kiosk sessions, allow platform apps, extesions and shared + // modules. if (extension->GetType() == extensions::Manifest::TYPE_PLATFORM_APP || extension->GetType() == extensions::Manifest::TYPE_SHARED_MODULE || extension->GetType() == extensions::Manifest::TYPE_EXTENSION) {
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc index f5717ed..b7e95e3 100644 --- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc +++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc
@@ -339,6 +339,44 @@ EXPECT_EQ(base::string16(), error); error.clear(); } + + // Verify that a platform app with remote URL permissions can be installed. + { + base::ListValue* const permissions = new base::ListValue(); + permissions->AppendString("https://example.com/"); + permissions->AppendString("http://example.com/"); + permissions->AppendString("ftp://example.com/"); + base::DictionaryValue values; + values.Set(extensions::manifest_keys::kPermissions, permissions); + + extension = CreatePlatformAppWithExtraValues( + &values, + extensions::Manifest::EXTERNAL_POLICY, + extensions::Extension::NO_FLAGS); + ASSERT_TRUE(extension); + + EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error)); + EXPECT_EQ(base::string16(), error); + error.clear(); + } + + // Verify that a platform app with a local URL permission cannot be installed. + { + base::ListValue* const permissions = new base::ListValue(); + permissions->AppendString("file:///some/where"); + base::DictionaryValue values; + values.Set(extensions::manifest_keys::kPermissions, permissions); + + extension = CreatePlatformAppWithExtraValues( + &values, + extensions::Manifest::EXTERNAL_POLICY, + extensions::Extension::NO_FLAGS); + ASSERT_TRUE(extension); + + EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error)); + EXPECT_NE(base::string16(), error); + error.clear(); + } } TEST(DeviceLocalAccountManagementPolicyProviderTest, KioskAppSession) {
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc index f327236..72366612 100644 --- a/chrome/browser/download/save_page_browsertest.cc +++ b/chrome/browser/download/save_page_browsertest.cc
@@ -976,16 +976,27 @@ nullptr, nullptr); EXPECT_EQ(1, actual_number_of_matches) - << "Verifying if \"" << expected_substring << "\" appears " - << "exactly once in the web-contents text"; + << "Verifying that \"" << expected_substring << "\" appears " + << "exactly once in the text of web contents"; } - int actual_number_of_errors = ui_test_utils::FindInPage( - GetCurrentTab(browser()), base::UTF8ToUTF16("err"), - true, // |forward| - false, // |case_sensitive| - nullptr, nullptr); - EXPECT_EQ(0, actual_number_of_errors); + std::string forbidden_substrings[] = { + "head" // Html markup should not be visible. + "err", // "err" is a prefix of error messages + is included as text + // content of <object> elements in some test files (text content + // would be rendered in case the object itself doesn't work). + }; + for (const auto& forbidden_substring : forbidden_substrings) { + int actual_number_of_matches = ui_test_utils::FindInPage( + GetCurrentTab(browser()), base::UTF8ToUTF16(forbidden_substring), + true, // |forward| + true, // |case_sensitive| + nullptr, nullptr); + + EXPECT_EQ(0, actual_number_of_matches) + << "Verifying that \"" << forbidden_substring << "\" doesn't " + << "appear in the text of web contents"; + } } static void IncrementInteger(int* i, content::RenderFrameHost* /* unused */) { @@ -1130,6 +1141,46 @@ TestMultiFramePage(save_page_type, url, 5, expected_substrings); } +// Test for saving frames with various encodings: +// - iso-8859-2: encoding declared via <meta> element +// - utf16-le-bom.htm, utf16-be-bom.htm: encoding detected via BOM +// - utf16-le-nobom.htm, utf16-le-nobom.htm, utf32.htm - encoding declared via +// mocked http headers +IN_PROC_BROWSER_TEST_P(SavePageMultiFrameBrowserTest, Encoding) { + content::SavePageType save_page_type = GetParam(); + + std::string arr[] = { + "frames-encodings.htm: f53295dd-a95b-4b32-85f5-b6e15377fb20", + "iso-8859-2.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 " + "g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84", + "utf16-le-nobom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 " + "g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84", + "utf16-le-bom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 " + "g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84", + "utf16-be-nobom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 " + "g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84", + "utf16-be-bom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 " + "g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84", + "utf32.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 " + "g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84", + }; + std::vector<std::string> expected_substrings(std::begin(arr), std::end(arr)); + + GURL url(embedded_test_server()->GetURL("a.com", + "/save_page/frames-encodings.htm")); + + // TODO(lukasza): crbug.com/541699: MHTML needs to handle multi-byte encodings + // by either: + // 1. Continuing to preserve the original encoding, but starting to round-trip + // the encoding declaration (in Content-Type MIME/MHTML header?) + // 2. Saving html docs in UTF8. + // 3. Saving the BOM (not sure if this will help for all cases though). + if (save_page_type == content::SAVE_PAGE_TYPE_AS_MHTML) + return; + + TestMultiFramePage(save_page_type, url, 7, expected_substrings); +} + INSTANTIATE_TEST_CASE_P( SaveType, SavePageMultiFrameBrowserTest,
diff --git a/chrome/browser/errorpage_browsertest.cc b/chrome/browser/errorpage_browsertest.cc index 763d5f9..08005f8 100644 --- a/chrome/browser/errorpage_browsertest.cc +++ b/chrome/browser/errorpage_browsertest.cc
@@ -164,6 +164,9 @@ // The diagnostics button isn't displayed when corrections were // retrieved from a remote server. EXPECT_FALSE(IsDisplayingDiagnosticsButton(browser)); + + // Close help box again, to return page to original state. + ToggleHelpBox(browser); } std::string GetShowSavedButtonLabel() { @@ -733,9 +736,56 @@ nav_observer.Wait(); ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED); - // There should have two more requests to the correction service: One for the - // new error page, and one for tracking purposes. Have to make sure to wait - // for the tracking request, since the new error page does not depend on it. + // There should have been two more requests to the correction service: One + // for the new error page, and one for tracking purposes. Have to make sure + // to wait for the tracking request, since the new error page does not depend + // on it. + link_doctor_interceptor()->WaitForRequests(3); + EXPECT_EQ(3, link_doctor_interceptor()->num_requests()); +} + +// Test that the reload button on a DNS error page works after a same page +// navigation on the error page. Error pages don't seem to do this, but some +// traces indicate this may actually happen. This test may hang on regression. +IN_PROC_BROWSER_TEST_F(ErrorPageTest, + DNSError_DoReloadAfterSamePageNavigation) { + // The first navigation should fail, and the second one should be the error + // page. + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( + browser(), GetDnsErrorURL(), 2); + ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED); + EXPECT_EQ(1, link_doctor_interceptor()->num_requests()); + + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + + // Do a same page navigation. + content::TestNavigationObserver nav_observer1(web_contents, 1); + web_contents->GetMainFrame()->ExecuteJavaScriptForTests( + base::ASCIIToUTF16("document.location='#';")); + // The same page navigation counts as a single navigation as far as the + // TestNavigationObserver is concerned. + nav_observer1.Wait(); + // Page being displayed should not change. + ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED); + // No new requests should have been issued. + EXPECT_EQ(1, link_doctor_interceptor()->num_requests()); + + // Clicking the reload button should load the error page again, and there + // should be two commits, as before. + content::TestNavigationObserver nav_observer2(web_contents, 2); + // Can't use content::ExecuteScript because it waits for scripts to send + // notification that they've run, and scripts that trigger a navigation may + // not send that notification. + web_contents->GetMainFrame()->ExecuteJavaScriptForTests( + base::ASCIIToUTF16("document.getElementById('reload-button').click();")); + nav_observer2.Wait(); + ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED); + + // There should have been two more requests to the correction service: One + // for the new error page, and one for tracking purposes. Have to make sure + // to wait for the tracking request, since the new error page does not depend + // on it. link_doctor_interceptor()->WaitForRequests(3); EXPECT_EQ(3, link_doctor_interceptor()->num_requests()); } @@ -1063,6 +1113,41 @@ EXPECT_FALSE(IsDisplayingText(browser(), "error.page.auto.reload")); } +// Make sure that a same page navigation does not cause issues with the +// auto-reload timer. Note that this test was added due to this case causing +// a crash. On regression, this test may hang due to a crashed renderer. +IN_PROC_BROWSER_TEST_F(ErrorPageAutoReloadTest, IgnoresSamePageNavigation) { + GURL test_url("http://error.page.auto.reload"); + InstallInterceptor(test_url, 2); + + // Wait for the error page and first autoreload, which happens immediately. + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( + browser(), test_url, 2); + + EXPECT_EQ(2, interceptor()->failures()); + EXPECT_EQ(2, interceptor()->requests()); + + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + content::TestNavigationObserver observer(web_contents, 1); + web_contents->GetMainFrame()->ExecuteJavaScriptForTests( + base::ASCIIToUTF16("document.location='#';")); + // The same page navigation counts as a navigation as far as the + // TestNavigationObserver is concerned. + observer.Wait(); + + // No new requests should have been issued. + EXPECT_EQ(2, interceptor()->failures()); + EXPECT_EQ(2, interceptor()->requests()); + + // Wait for the second auto reload, which succeeds. + content::TestNavigationObserver observer2(web_contents, 1); + observer2.Wait(); + + EXPECT_EQ(2, interceptor()->failures()); + EXPECT_EQ(3, interceptor()->requests()); +} + // Interceptor that fails all requests with net::ERR_ADDRESS_UNREACHABLE. class AddressUnreachableInterceptor : public net::URLRequestInterceptor { public:
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc index bccbd59..b5f2620 100644 --- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.cc
@@ -49,12 +49,6 @@ return is_possible_search_referrer && !IsFromGoogle(url); } -bool ShouldLogEvent(const base::TimeDelta& event, - const base::TimeDelta& first_background) { - return !event.is_zero() && - (first_background.is_zero() || event < first_background); -} - } // namespace FromGWSPageLoadMetricsObserver::FromGWSPageLoadMetricsObserver() @@ -69,46 +63,45 @@ void FromGWSPageLoadMetricsObserver::OnComplete( const page_load_metrics::PageLoadTiming& timing, const page_load_metrics::PageLoadExtraInfo& extra_info) { + using page_load_metrics::EventOccurredInForeground; + if (!navigation_from_gws_) return; - // Filter out navigations that started in the background. - if (!extra_info.started_in_foreground) - return; - const base::TimeDelta& first_background = extra_info.first_background_time; - if (ShouldLogEvent(timing.dom_content_loaded_event_start, first_background)) { + if (EventOccurredInForeground(timing.dom_content_loaded_event_start, + extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2." "NavigationToDOMContentLoadedEventFired", timing.dom_content_loaded_event_start); } - if (ShouldLogEvent(timing.load_event_start, first_background)) { + if (EventOccurredInForeground(timing.load_event_start, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2.NavigationToLoadEventFired", timing.load_event_start); } - if (ShouldLogEvent(timing.first_layout, first_background)) { + if (EventOccurredInForeground(timing.first_layout, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2.NavigationToFirstLayout", timing.first_layout); } - if (ShouldLogEvent(timing.first_text_paint, first_background)) { + if (EventOccurredInForeground(timing.first_text_paint, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2.NavigationToFirstTextPaint", timing.first_text_paint); } - if (ShouldLogEvent(timing.first_image_paint, first_background)) { + if (EventOccurredInForeground(timing.first_image_paint, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2.NavigationToFirstImagePaint", timing.first_image_paint); } - if (ShouldLogEvent(timing.first_paint, first_background)) { + if (EventOccurredInForeground(timing.first_paint, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2.NavigationToFirstPaint", timing.first_paint); } base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing); - if (ShouldLogEvent(first_contentful_paint, first_background)) { + if (EventOccurredInForeground(first_contentful_paint, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.FromGWS.Timing2.NavigationToFirstContentfulPaint", first_contentful_paint);
diff --git a/chrome/browser/page_load_metrics/observers/stale_while_revalidate_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/stale_while_revalidate_metrics_observer.cc index 58df2d1..f474d2f 100644 --- a/chrome/browser/page_load_metrics/observers/stale_while_revalidate_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/stale_while_revalidate_metrics_observer.cc
@@ -23,22 +23,24 @@ void StaleWhileRevalidateMetricsObserver::OnComplete( const page_load_metrics::PageLoadTiming& timing, const page_load_metrics::PageLoadExtraInfo& extra_info) { + using page_load_metrics::EventOccurredInForeground; + if (!is_interesting_domain_) return; - if (!timing.load_event_start.is_zero()) { + if (EventOccurredInForeground(timing.load_event_start, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.StaleWhileRevalidateExperiment.Timing2." "NavigationToLoadEventFired", timing.load_event_start); } - if (!timing.first_layout.is_zero()) { + if (EventOccurredInForeground(timing.first_layout, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.StaleWhileRevalidateExperiment.Timing2." "NavigationToFirstLayout", timing.first_layout); } - if (!timing.first_text_paint.is_zero()) { + if (EventOccurredInForeground(timing.first_text_paint, extra_info)) { PAGE_LOAD_HISTOGRAM( "PageLoad.Clients.StaleWhileRevalidateExperiment.Timing2." "NavigationToFirstTextPaint",
diff --git a/chrome/browser/password_manager/save_password_infobar_delegate.cc b/chrome/browser/password_manager/save_password_infobar_delegate.cc index 5d33470..6a831b9 100644 --- a/chrome/browser/password_manager/save_password_infobar_delegate.cc +++ b/chrome/browser/password_manager/save_password_infobar_delegate.cc
@@ -102,9 +102,14 @@ } base::string16 message; gfx::Range message_link_range = gfx::Range(); + PasswordTittleType type = + form_to_save_->pending_credentials().federation_url.is_empty() + ? PasswordTittleType::SAVE_PASSWORD + : PasswordTittleType::UPDATE_PASSWORD; GetSavePasswordDialogTitleTextAndLinkRange( web_contents->GetVisibleURL(), form_to_save_->observed_form().origin, - is_smartlock_branding_enabled, false, &message, &message_link_range); + is_smartlock_branding_enabled, type, + &message, &message_link_range); SetMessage(message); SetMessageLinkRange(message_link_range); }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index a54bf83..3d37f7f 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1017,6 +1017,7 @@ } void RenderViewContextMenu::AppendAudioItems() { + AppendMediaItems(); menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, IDS_CONTENT_CONTEXT_OPENAUDIONEWTAB); @@ -1035,6 +1036,7 @@ } void RenderViewContextMenu::AppendVideoItems() { + AppendMediaItems(); menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENAVNEWTAB, IDS_CONTENT_CONTEXT_OPENVIDEONEWTAB); @@ -1045,6 +1047,13 @@ AppendMediaRouterItem(); } +void RenderViewContextMenu::AppendMediaItems() { + menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_LOOP, + IDS_CONTENT_CONTEXT_LOOP); + menu_model_.AddCheckItemWithStringId(IDC_CONTENT_CONTEXT_CONTROLS, + IDS_CONTENT_CONTEXT_CONTROLS); +} + void RenderViewContextMenu::AppendPluginItems() { if (params_.page_url == params_.src_url || guest_view::GuestViewBase::IsGuest(source_web_contents_)) {
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h index 89669ff..df77e1f 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.h +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -119,6 +119,7 @@ void AppendAudioItems(); void AppendCanvasItems(); void AppendVideoItems(); + void AppendMediaItems(); void AppendPluginItems(); void AppendPageItems(); void AppendCopyItem();
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js index fd0a7547..34e73eba 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -169,7 +169,8 @@ } else { // When in compat mode, if the focus is within the desktop tree proper, // then do not disable content scripts. - if (this.currentRange_ && !this.currentRange_.isWebRange()) + if (this.currentRange_ && + this.currentRange_.start.node.root.role == RoleType.desktop) return; this.disableClassicChromeVox_(); @@ -195,8 +196,7 @@ if (mode != ChromeVoxMode.FORCE_NEXT) { if (this.isWhitelistedForNext_(url)) mode = ChromeVoxMode.NEXT; - else if (this.isBlacklistedForClassic_(url) || (this.currentRange_ && - !this.currentRange_.isWebRange())) + else if (this.isBlacklistedForClassic_(url)) mode = ChromeVoxMode.COMPAT; else mode = ChromeVoxMode.CLASSIC; @@ -440,8 +440,9 @@ case 'toggleChromeVoxVersion': var newMode; if (this.mode_ == ChromeVoxMode.FORCE_NEXT) { - var inWeb = current.isWebRange(); - newMode = inWeb ? ChromeVoxMode.CLASSIC : ChromeVoxMode.COMPAT; + var inViews = + this.currentRange_.start.node.root.role == RoleType.desktop; + newMode = inViews ? ChromeVoxMode.COMPAT : ChromeVoxMode.CLASSIC; } else { newMode = ChromeVoxMode.FORCE_NEXT; } @@ -572,7 +573,7 @@ * @private */ isBlacklistedForClassic_: function(url) { - return this.classicBlacklistRegExp_.test(url); + return url === '' || this.classicBlacklistRegExp_.test(url); }, /**
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs index 2c573bb..36191c46 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -467,31 +467,3 @@ } ); }); - -TEST_F('BackgroundTest', 'ModeSwitching', function() { - this.runWithLoadedTree('<button></button>', function(root) { - // Tests default to force next mode. - assertEquals('force_next', global.backgroundObj.mode); - - // Force next mode stays set regardless of where the range lands. - global.backgroundObj.refreshMode('http://google.com'); - assertEquals('force_next', global.backgroundObj.mode); - // Empty urls occur before document load or when root is desktop. - global.backgroundObj.refreshMode(''); - assertEquals('force_next', global.backgroundObj.mode); - - // Verify compat -> classic switching. - global.backgroundObj.setMode('compat'); - global.backgroundObj.refreshMode('http://google.com'); - assertEquals('classic', global.backgroundObj.mode); - - global.backgroundObj.setCurrentRange(cursors.Range.fromNode(root.parent)); - global.backgroundObj.refreshMode(''); - assertEquals('compat', global.backgroundObj.mode); - - // And back to classic. - global.backgroundObj.setCurrentRange(cursors.Range.fromNode(root)); - global.backgroundObj.refreshMode(''); - assertEquals('classic', global.backgroundObj.mode); - }.bind(this)); -});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js index 5b5a869..5067cf8 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
@@ -433,15 +433,6 @@ break; } return new cursors.Range(newStart, newEnd); - }, - - /** - * Returns true if this range has either cursor end on web content. - * @return {boolean} - */ - isWebRange: function() { - return this.start.node.root.role != Role.desktop || - this.end.node.root.role != Role.desktop; } };
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs index bb50767..fab8d6ab 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -303,13 +303,3 @@ assertEquals(root.firstChild.firstChild, cursor.node); }); }); - -TEST_F('CursorsTest', 'IsInWebRange', function() { - this.runWithLoadedTree(this.simpleDoc, function(root) { - var para = root.firstChild; - var webRange = new cursors.Range.fromNode(para); - var auraRange = cursors.Range.fromNode(root.parent); - assertFalse(auraRange.isWebRange()); - assertTrue(webRange.isWebRange()); - }); -});
diff --git a/chrome/browser/resources/media_router/elements/route_details/route_details.css b/chrome/browser/resources/media_router/elements/route_details/route_details.css index 22b88fd..1b948dd 100644 --- a/chrome/browser/resources/media_router/elements/route_details/route_details.css +++ b/chrome/browser/resources/media_router/elements/route_details/route_details.css
@@ -5,8 +5,8 @@ #close-route-button { -webkit-padding-end: 24px; background-color: white; - line-height: 32px; - margin: 0; + line-height: 12px; + margin: 12px 0; text-align: end; width: 100%; } @@ -18,10 +18,9 @@ } #route-information { - -webkit-padding-start: 30px; + -webkit-padding-start: 44px; background-color: white; font-size: 1.2em; - height: 40px; + margin-top: 16px; overflow: hidden; - padding: 20px 30px; }
diff --git a/chrome/browser/resources/media_router/elements/route_details/route_details.html b/chrome/browser/resources/media_router/elements/route_details/route_details.html index d0237e0..f1feac4 100644 --- a/chrome/browser/resources/media_router/elements/route_details/route_details.html +++ b/chrome/browser/resources/media_router/elements/route_details/route_details.html
@@ -10,7 +10,7 @@ <extensionview id="custom-controller" hidden$="[[isCustomControllerHidden_]]"> </extensionview> - <paper-button raised id="close-route-button" class="button" + <paper-button flat id="close-route-button" class="button" on-click="closeRoute_"> <span>[[stopCastingButtonText_]]</span> </paper-button>
diff --git a/chrome/browser/resources/media_router/media_router.css b/chrome/browser/resources/media_router/media_router.css index e44f31b..00e539a2 100644 --- a/chrome/browser/resources/media_router/media_router.css +++ b/chrome/browser/resources/media_router/media_router.css
@@ -9,7 +9,6 @@ } #media-router-container { - background-color: rgb(246, 246, 246); display: flex; flex-direction: column; }
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html index 64e5334..a60efce 100644 --- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html +++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
@@ -4,6 +4,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-icon-item.html"> <dom-module id="settings-startup-urls-page"> + <link rel="import" type="css" href="chrome://md-settings/settings_dialog.css"> <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="on_startup_shared.css"> <template> @@ -30,15 +31,17 @@ <paper-dialog modal id="addUrlDialog" class="layout vertical"> <div id="dialog-content"> - <div class="dialog-title" - i18n-content="onStartupSetPages"></div> - <div class="dialog-body"> - <paper-icon-item class="start"> - <div i18n-content="onStartupAddPage"></div> - <paper-input class="flex" no-label-float + <div class="top-row"> + <div class="title" i18n-content="onStartupAddPage"></div> + <paper-icon-button icon="clear" on-tap="onCancelTap_" id="close"> + </paper-icon-button> + </div> + <div class="body"> + <div class="explanation"> + <paper-input class="flex" always-float-label i18n-values="label:onStartupEnterUrl" value="{{newUrl}}"> </paper-input> - </paper-icon-item> + </div> <div class="button-container"> <div class="action-buttons"> <paper-button class="cancel-button" on-tap="onCancelTap_"
diff --git a/chrome/browser/resources/settings/reset_page/powerwash_dialog.html b/chrome/browser/resources/settings/reset_page/powerwash_dialog.html index 824b52d..82b07e55 100644 --- a/chrome/browser/resources/settings/reset_page/powerwash_dialog.html +++ b/chrome/browser/resources/settings/reset_page/powerwash_dialog.html
@@ -6,16 +6,17 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <dom-module id="settings-powerwash-dialog"> + <link rel="import" type="css" href="chrome://md-settings/settings_dialog.css"> <link rel="import" type="css" href="reset_page_dialog.css"> <template> <paper-dialog modal id="dialog"> <div id="dialog-content"> - <div class="dialog-top-row"> - <span class="dialog-title" i18n-content="powerwashDialogTitle"></span> + <div class="top-row"> + <span class="title" i18n-content="powerwashDialogTitle"></span> <paper-icon-button icon="clear" on-tap="onCancelTap_" id="close"> </paper-icon-button> </div> - <div class="dialog-body"> + <div class="body"> <div class="explanation"> <span i18n-content="powerwashDialogExplanation"></span> <a i18n-values="href:powerwashLearnMoreUrl"
diff --git a/chrome/browser/resources/settings/reset_page/reset_page_dialog.css b/chrome/browser/resources/settings/reset_page/reset_page_dialog.css index 9bdac00e..43afe64 100644 --- a/chrome/browser/resources/settings/reset_page/reset_page_dialog.css +++ b/chrome/browser/resources/settings/reset_page/reset_page_dialog.css
@@ -2,48 +2,6 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#dialog-content { - -webkit-padding-end: 0; - -webkit-padding-start: 0; - margin-top: 0; -} - -.dialog-top-row { - align-items: center; - border-bottom: 1px solid gainsboro; - display: flex; - padding-bottom: 5px; - padding-top: 5px; -} - -.dialog-title { - flex: 1; - font-size: 1.13em; -} - -.dialog-body { - font-size: 1em; - margin: 20px 0; -} - -.dialog-title, -.dialog-body { - -webkit-padding-end: 24px; - -webkit-padding-start: 24px; -} - -.action-button { - -webkit-margin-start: 10px; - background-color: rgb(66, 133, 244); - color: white; - font-weight: 500; -} - -.cancel-button { - color: rgb(109, 109, 109); - font-weight: 500; -} - paper-button { margin: 0; } @@ -52,13 +10,3 @@ color: rgb(66, 133, 244); text-decoration: none; } - -.explanation { - margin-bottom: 35px; -} - -.button-container { - display: flex; - flex: 1; - justify-content: flex-end; -}
diff --git a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html index 6d164ac..9be9d87 100644 --- a/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html +++ b/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
@@ -7,17 +7,18 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> <dom-module id="settings-reset-profile-dialog"> + <link rel="import" type="css" href="chrome://md-settings/settings_dialog.css"> <link rel="import" type="css" href="reset_page_dialog.css"> <link rel="import" type="css" href="reset_profile_dialog.css"> <template> <paper-dialog modal id="dialog"> <div id="dialog-content"> - <div class="dialog-top-row"> - <span class="dialog-title" i18n-content="resetPageTitle"></span> + <div class="top-row"> + <span class="title" i18n-content="resetPageTitle"></span> <paper-icon-button icon="clear" on-tap="onCancelTap_" id="close"> </paper-icon-button> </div> - <div class="dialog-body"> + <div class="body"> <div class="explanation"> <span i18n-content="resetPageExplanation"></span> <a i18n-values="href:resetPageLearnMoreUrl"
diff --git a/chrome/browser/resources/settings/settings_dialog.css b/chrome/browser/resources/settings/settings_dialog.css new file mode 100644 index 0000000..f868244 --- /dev/null +++ b/chrome/browser/resources/settings/settings_dialog.css
@@ -0,0 +1,70 @@ +/* Copyright 2015 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +/** + * @fileoverview + * Common dialog styles for Material Design settings. + */ + +#dialog-content { + -webkit-padding-end: 0; + -webkit-padding-start: 0; + margin-bottom: 0; + margin-top: 0; +} + +paper-dialog .top-row { + align-items: center; + border-bottom: 1px solid gainsboro; + display: flex; + padding-bottom: 5px; + padding-top: 5px; +} + +paper-dialog .title { + flex: 1; + font-size: 1.13em; +} + +paper-dialog .body { + font-size: 1em; + margin: 20px 0; +} + +paper-dialog .title, +paper-dialog .body { + -webkit-padding-end: 24px; + -webkit-padding-start: 24px; +} + +paper-dialog .action-button { + -webkit-margin-start: 10px; + background-color: rgb(66, 133, 244); + color: white; + font-weight: 500; +} + +paper-dialog .cancel-button { + color: rgb(109, 109, 109); + font-weight: 500; +} + +paper-dialog .explanation { + margin-bottom: 35px; +} + +paper-dialog .button-container { + display: flex; + flex: 1; + justify-content: flex-end; +} + +paper-dialog paper-button { + margin: 0; + min-width: auto; +} + +paper-dialog paper-button[toggles][active] { + background-color: LightGray; +}
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js index c55f835..27eb6340 100644 --- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js +++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
@@ -10,7 +10,7 @@ * Example: * * <settings-animated-pages current-route="{{currentRoute}}" - route-root="advanced/privacy" redirect-root-route-to="advanced"> + * section="privacy"> * <!-- Insert your section controls here --> * </settings-animated-pages> * @@ -34,6 +34,8 @@ * Routes with this section activate this element. For instance, if this * property is 'search', and currentRoute.section is also set to 'search', * this element will display the subpage in currentRoute.subpage. + * + * The section name must match the name specified in settings_router.js. */ section: { type: String,
diff --git a/chrome/browser/resources/settings/settings_page/settings_section.js b/chrome/browser/resources/settings/settings_page/settings_section.js index fd8da6d..7a39fd7 100644 --- a/chrome/browser/resources/settings/settings_page/settings_section.js +++ b/chrome/browser/resources/settings/settings_page/settings_section.js
@@ -9,7 +9,7 @@ * * Example: * - * <settings-section page-title="[[pageTitle]]"> + * <settings-section page-title="[[pageTitle]]" section="privacy"> * <!-- Insert your section controls here --> * </settings-section> * @@ -35,6 +35,8 @@ /** * The section is expanded to a full-page view when this property matches * currentRoute.section. + * + * The section name must match the name specified in settings_router.js. */ section: { type: String,
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 27e2192..9dba0d5 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -127,6 +127,9 @@ <structure name="IDR_SETTINGS_CR_SETTINGS_SUBHEADER_JS" file="settings_page/settings_subheader.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_CR_SETTINGS_DIALOG_CSS" + file="settings_dialog.css" + type="chrome_html" /> <structure name="IDR_SETTINGS_CR_SETTINGS_SHARED_CSS" file="settings_shared.css" type="chrome_html" />
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index 3301b13..6d2dae1 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h
@@ -133,7 +133,7 @@ // Called to force the zoom state to for the active tab to be recalculated. // |can_show_bubble| is true when a user presses the zoom up or down keyboard // shortcuts and will be false in other cases (e.g. switching tabs, "clicking" - // + or - in the wrench menu to change zoom). + // + or - in the app menu to change zoom). virtual void ZoomChangedForActiveTab(bool can_show_bubble) = 0; // Windows and GTK remove the top controls in fullscreen, but Mac and Ash
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h index 4b2ff57..b4d17f4 100644 --- a/chrome/browser/ui/chrome_pages.h +++ b/chrome/browser/ui/chrome_pages.h
@@ -28,7 +28,7 @@ // Keyboard accelerators. HELP_SOURCE_KEYBOARD, - // Menus (e.g. wrench menu or Chrome OS system menu). + // Menus (e.g. app menu or Chrome OS system menu). HELP_SOURCE_MENU, // WebUI (the "About" page).
diff --git a/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm b/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm index 4cc6cd6..596cb02 100644 --- a/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm +++ b/chrome/browser/ui/cocoa/app_menu/app_menu_controller.mm
@@ -588,7 +588,7 @@ @synthesize overflowActionsContainerView = overflowActionsContainerView_; - (id)initWithController:(AppMenuController*)controller { - if ((self = [super initWithNibName:@"WrenchMenu" + if ((self = [super initWithNibName:@"AppMenu" bundle:base::mac::FrameworkBundle()])) { propertyReleaser_.Init(self, [AppMenuButtonViewController class]); controller_ = controller;
diff --git a/chrome/browser/ui/cocoa/autofill/new_credit_card_bubble_cocoa.mm b/chrome/browser/ui/cocoa/autofill/new_credit_card_bubble_cocoa.mm index 6bfa922..b44cc70 100644 --- a/chrome/browser/ui/cocoa/autofill/new_credit_card_bubble_cocoa.mm +++ b/chrome/browser/ui/cocoa/autofill/new_credit_card_bubble_cocoa.mm
@@ -24,7 +24,7 @@ namespace { -const CGFloat kWrenchBubblePointOffsetY = 6; +const CGFloat kAppMenuBubblePointOffsetY = 6; const CGFloat kVerticalSpacing = 8; const CGFloat kHorizontalSpacing = 4; const CGFloat kInset = 20.0; @@ -210,10 +210,10 @@ NSPoint anchor_point; NSView* anchor_view; if ([bwc isTabbedWindow]) { - anchor_view = [[bwc toolbarController] wrenchButton]; + anchor_view = [[bwc toolbarController] appMenuButton]; anchor_point = NSMakePoint( NSMidX([anchor_view bounds]), - NSMinY([anchor_view bounds]) + kWrenchBubblePointOffsetY); + NSMinY([anchor_view bounds]) + kAppMenuBubblePointOffsetY); [[bubbleController_ bubble] setArrowLocation:info_bubble::kTopRight]; [[bubbleController_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor]; } else {
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm index d92a7785..0eb9c79 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -849,7 +849,7 @@ AvatarBaseController* controller = [controller_ avatarButtonController]; NSView* anchor = [controller buttonView]; if ([anchor isHiddenOrHasHiddenAncestor]) - anchor = [[controller_ toolbarController] wrenchButton]; + anchor = [[controller_ toolbarController] appMenuButton]; [controller showAvatarBubbleAnchoredAt:anchor withMode:mode withServiceType:manage_accounts_params.service_type
diff --git a/chrome/browser/ui/cocoa/browser_window_command_handler.mm b/chrome/browser/ui/cocoa/browser_window_command_handler.mm index f30b90d..3bbdb3b 100644 --- a/chrome/browser/ui/cocoa/browser_window_command_handler.mm +++ b/chrome/browser/ui/cocoa/browser_window_command_handler.mm
@@ -184,8 +184,8 @@ case IDC_BOOKMARK_PAGE: { // Extensions have the ability to hide the bookmark page menu item. // This only affects the bookmark page menu item under the main menu. - // The bookmark page menu item under the wrench menu has its - // visibility controlled by AppMenuModel. + // The bookmark page menu item under the app menu has its visibility + // controlled by AppMenuModel. bool shouldHide = chrome::ShouldRemoveBookmarkThisPageUI(browser->profile()); NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item); @@ -195,7 +195,7 @@ case IDC_BOOKMARK_ALL_TABS: { // Extensions have the ability to hide the bookmark all tabs menu // item. This only affects the bookmark page menu item under the main - // menu. The bookmark page menu item under the wrench menu has its + // menu. The bookmark page menu item under the app menu has its // visibility controlled by AppMenuModel. bool shouldHide = chrome::ShouldRemoveBookmarkOpenPagesUI(browser->profile());
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm index c775514..c01b1621 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm +++ b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
@@ -156,7 +156,7 @@ ToolbarController* toolbarController() { return toolbarController_; } AppMenuController* appMenuController() { return appMenuController_; } ToolbarActionsModel* model() { return model_; } - NSView* wrenchButton() { return [toolbarController_ wrenchButton]; } + NSView* appMenuButton() { return [toolbarController_ appMenuButton]; } private: scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_redesign_; @@ -263,8 +263,8 @@ model()->SetVisibleIconCount(0); EXPECT_EQ(nil, [actionButton superview]); - // Move the mouse over the app button. - MoveMouseToCenter(wrenchButton()); + // Move the mouse over the app menu button. + MoveMouseToCenter(appMenuButton()); { // No menu yet (on the browser action). @@ -287,10 +287,10 @@ } // Checks the layout of the overflow bar in the app menu. -void CheckWrenchMenuLayout(ToolbarController* toolbarController, - int overflowStartIndex, - const std::string& error_message, - const base::Closure& closure) { +void CheckAppMenuLayout(ToolbarController* toolbarController, + int overflowStartIndex, + const std::string& error_message, + const base::Closure& closure) { AppMenuController* appMenuController = [toolbarController appMenuController]; // The app menu should start as open (since that's where the overflowed @@ -370,19 +370,19 @@ ASSERT_EQ(kNumExtensions, static_cast<int>(model()->toolbar_items().size())); // A helper function to open the app menu and call the check function. - auto resizeAndActivateWrench = [this](int visible_count, - const std::string& error_message) { + auto resizeAndActivateAppMenu = [this](int visible_count, + const std::string& error_message) { model()->SetVisibleIconCount(kNumExtensions - visible_count); - MoveMouseToCenter(wrenchButton()); + MoveMouseToCenter(appMenuButton()); { base::RunLoop runLoop; // Click on the app menu, and pass in a callback to continue the test in - // CheckWrenchMenuLayout (due to the blocking nature of Cocoa menus, + // CheckAppMenuLayout (due to the blocking nature of Cocoa menus, // passing in runLoop.QuitClosure() is not sufficient here.) ui_controls::SendMouseEventsNotifyWhenDone( ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP, - base::Bind(&CheckWrenchMenuLayout, + base::Bind(&CheckAppMenuLayout, base::Unretained(toolbarController()), kNumExtensions - visible_count, error_message, @@ -393,7 +393,7 @@ // Test the layout with gradually more extensions hidden. for (int i = 1; i <= kNumExtensions; ++i) - resizeAndActivateWrench(i, base::StringPrintf("Normal: %d", i)); + resizeAndActivateAppMenu(i, base::StringPrintf("Normal: %d", i)); // Adding a global error adjusts the app menu size, and has been known to mess // up the overflow container's bounds (crbug.com/511326). @@ -404,9 +404,9 @@ // It's probably excessive to test every level of the overflow here. Test // having all actions overflowed, some actions overflowed, and one action // overflowed. - resizeAndActivateWrench(kNumExtensions, "GlobalError Full"); - resizeAndActivateWrench(kNumExtensions / 2, "GlobalError Half"); - resizeAndActivateWrench(1, "GlobalError One"); + resizeAndActivateAppMenu(kNumExtensions, "GlobalError Full"); + resizeAndActivateAppMenu(kNumExtensions / 2, "GlobalError Half"); + resizeAndActivateAppMenu(1, "GlobalError One"); } void AddExtensionWithMenuOpen(ToolbarController* toolbarController, @@ -443,7 +443,7 @@ ASSERT_EQ(1, static_cast<int>(model()->toolbar_items().size())); model()->SetVisibleIconCount(0); - MoveMouseToCenter(wrenchButton()); + MoveMouseToCenter(appMenuButton()); base::RunLoop runLoop; // Click on the app menu, and pass in a callback to continue the test in
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h b/chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h index daaba44d..c92eff2 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h +++ b/chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h
@@ -84,7 +84,7 @@ BOOL resizable_; // Whether or not the container is the overflow container, and is shown in the - // wrench menu. + // app menu. BOOL isOverflow_; // Whether the user is allowed to drag the grippy to the left. NO if all
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h index fea685a..9af7224 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h +++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.h
@@ -119,7 +119,7 @@ // Returns the associated ToolbarActionsBar. - (ToolbarActionsBar*)toolbarActionsBar; -// Sets whether or not the overflow container is focused in the wrench menu. +// Sets whether or not the overflow container is focused in the app menu. - (void)setFocusedInOverflow:(BOOL)focused; // Returns the size for the provided |maxWidth| of the overflow menu.
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm index 0ddd6aa..f9a32af 100644 --- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
@@ -161,7 +161,7 @@ // Returns the associated ToolbarController. - (ToolbarController*)toolbarController; -// Creates a message bubble anchored to the given |anchorAction|, or the wrench +// Creates a message bubble anchored to the given |anchorAction|, or the app // menu if no |anchorAction| is null. - (ToolbarActionsBarBubbleMac*)createMessageBubble: (scoped_ptr<ToolbarActionsBarBubbleDelegate>)delegate @@ -438,7 +438,7 @@ NSView* referenceButton = button; if ([button superview] != containerView_ || isOverflow_) { referenceButton = toolbarActionsBar_->platform_settings().chevron_enabled ? - chevronMenuButton_.get() : [[self toolbarController] wrenchButton]; + chevronMenuButton_.get() : [[self toolbarController] appMenuButton]; bounds = [referenceButton bounds]; } else { bounds = [button convertRect:[button frameAfterAnimation] @@ -1023,7 +1023,7 @@ anchorToSelf:(BOOL)anchorToSelf { DCHECK_GE([buttons_ count], 0u); NSView* anchorView = - anchorToSelf ? containerView_ : [[self toolbarController] wrenchButton]; + anchorToSelf ? containerView_ : [[self toolbarController] appMenuButton]; NSPoint anchor = [self popupPointForView:anchorView withBounds:[anchorView bounds]];
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm index 77c9a1be..eca6ecf7 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
@@ -303,12 +303,12 @@ } else { DCHECK(type_ == extension_installed_bubble::kBundle || type_ == extension_installed_bubble::kGeneric); - // Point at the bottom of the wrench menu. - NSView* wrenchButton = - [[window->cocoa_controller() toolbarController] wrenchButton]; - const NSRect bounds = [wrenchButton bounds]; + // Point at the bottom of the app menu. + NSView* appMenuButton = + [[window->cocoa_controller() toolbarController] appMenuButton]; + const NSRect bounds = [appMenuButton bounds]; NSPoint anchor = NSMakePoint(NSMidX(bounds), NSMaxY(bounds)); - arrowPoint = [wrenchButton convertPoint:anchor toView:nil]; + arrowPoint = [appMenuButton convertPoint:anchor toView:nil]; } return arrowPoint; }
diff --git a/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm b/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm index 34ea225e..d4e4fd7 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm
@@ -96,7 +96,7 @@ anchorView = [actionsController buttonWithIndex:0]; break; case ANCHOR_APP_MENU: - anchorView = [toolbarController wrenchButton]; + anchorView = [toolbarController appMenuButton]; break; } CheckBubbleAndReferenceView(bubble, anchorView);
diff --git a/chrome/browser/ui/cocoa/global_error_bubble_controller.h b/chrome/browser/ui/cocoa/global_error_bubble_controller.h index 5d80a35..425deeb 100644 --- a/chrome/browser/ui/cocoa/global_error_bubble_controller.h +++ b/chrome/browser/ui/cocoa/global_error_bubble_controller.h
@@ -21,7 +21,7 @@ class Bridge; } -// This is a bubble view shown from the wrench menu to display information +// This is a bubble view shown from the app menu to display information // about a global error. @interface GlobalErrorBubbleController : BaseBubbleController { @private
diff --git a/chrome/browser/ui/cocoa/global_error_bubble_controller.mm b/chrome/browser/ui/cocoa/global_error_bubble_controller.mm index 951c643..14c05cb 100644 --- a/chrome/browser/ui/cocoa/global_error_bubble_controller.mm +++ b/chrome/browser/ui/cocoa/global_error_bubble_controller.mm
@@ -52,15 +52,15 @@ NSWindow* parentWindow = browser->window()->GetNativeWindow(); BrowserWindowController* bwc = [BrowserWindowController browserWindowControllerForWindow:parentWindow]; - NSView* wrenchButton = [[bwc toolbarController] wrenchButton]; + NSView* appMenuButton = [[bwc toolbarController] appMenuButton]; NSPoint offset = NSMakePoint( - NSMidX([wrenchButton bounds]), + NSMidX([appMenuButton bounds]), app_menu_controller::kAppMenuBubblePointOffsetY); // The bubble will be automatically deleted when the window is closed. GlobalErrorBubbleController* bubble = [[GlobalErrorBubbleController alloc] initWithWindowNibPath:@"GlobalErrorBubble" - relativeToView:wrenchButton + relativeToView:appMenuButton offset:offset]; bubble->error_ = error; bubble->bridge_.reset(new GlobalErrorBubbleControllerInternal::Bridge(
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h index b2adbb32..7502ec6 100644 --- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h +++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
@@ -96,7 +96,7 @@ // false when the change in zoom for the active tab wasn't an explicit user // action (e.g. switching tabs, creating a new tab, creating a new browser). // Additionally, |can_show_bubble| will only be true when the bubble wouldn't - // be obscured by other UI (wrench menu) or redundant (+/- from wrench). + // be obscured by other UI (app menu) or redundant (+/- from app menu). void ZoomChangedForActiveTab(bool can_show_bubble); // Checks if the bookmark star should be enabled or not.
diff --git a/chrome/browser/ui/cocoa/menu_button.h b/chrome/browser/ui/cocoa/menu_button.h index 0cc07c6..78be3af 100644 --- a/chrome/browser/ui/cocoa/menu_button.h +++ b/chrome/browser/ui/cocoa/menu_button.h
@@ -17,11 +17,11 @@ // There are two different behaviors of this button depending on the value of // the |openMenuOnClick| property. If YES, the target-action mechanism will be // handled internally to always show the menu when clicked. This behavior is -// used for the Wrench menu, for example. When the property is NO, the button -// can have a separate target-action but will open the menu when clicked and -// held. This is used for the toolbar back/forward buttons, which have a -// primary action and the menu as a secondary click-hold action. The default -// value is NO so that custom actions can be hooked up in Interface Builder. +// used for the App menu, for example. When the property is NO, the button can +// have a separate target-action but will open the menu when clicked and held. +// This is used for the toolbar back/forward buttons, which have a primary +// action and the menu as a secondary click-hold action. The default value is NO +// so that custom actions can be hooked up in Interface Builder. @interface MenuButton : ToolbarButton { @private base::scoped_nsobject<NSMenu> attachedMenu_;
diff --git a/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.h b/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.h index 5298ef04..f8fbaa8 100644 --- a/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.h +++ b/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.h
@@ -24,12 +24,12 @@ @property(readonly, nonatomic) OneClickSigninViewController* viewController; -// Initializes with a browser window |controller|, under whose wrench -// menu this bubble will be displayed, and callbacks which are called -// if the user clicks the corresponding link. |errorMessage| is an -// alternate message that will be displayed in the case of an authentication -// error, and |syncCallback| is called to start sync. |webContents| is used -// to open the Learn More and Advanced links +// Initializes with a browser window |controller|, under whose app menu this +// bubble will be displayed, and callbacks which are called if the user clicks +// the corresponding link. |errorMessage| is an alternate message that will be +// displayed in the case of an authentication error, and |syncCallback| is +// called to start sync. |webContents| is used to open the Learn More and +// Advanced links. // // The bubble is not automatically displayed; call showWindow:id to // display. The bubble is auto-released on close.
diff --git a/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.mm b/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.mm index e66f5ff6..5acb2d8a 100644 --- a/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.mm +++ b/chrome/browser/ui/cocoa/one_click_signin_bubble_controller.mm
@@ -36,11 +36,11 @@ NSWindow* parentWindow = [controller window]; - // Set the anchor point to right below the wrench menu. - NSView* wrenchButton = [[controller toolbarController] wrenchButton]; - const NSRect bounds = [wrenchButton bounds]; + // Set the anchor point to right below the app menu. + NSView* appMenuButton = [[controller toolbarController] appMenuButton]; + const NSRect bounds = [appMenuButton bounds]; NSPoint anchorPoint = NSMakePoint(NSMidX(bounds), NSMaxY(bounds)); - anchorPoint = [wrenchButton convertPoint:anchorPoint toView:nil]; + anchorPoint = [appMenuButton convertPoint:anchorPoint toView:nil]; anchorPoint = [parentWindow convertBaseToScreen:anchorPoint]; // Create an empty window into which content is placed.
diff --git a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h index bd4031a..665853f 100644 --- a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h +++ b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.h
@@ -54,7 +54,7 @@ IBOutlet MenuButton* forwardButton_; IBOutlet ReloadButton* reloadButton_; IBOutlet ToolbarButton* homeButton_; - IBOutlet MenuButton* wrenchButton_; + IBOutlet MenuButton* appMenuButton_; IBOutlet AutocompleteTextField* locationBar_; IBOutlet BrowserActionsContainerView* browserActionsContainerView_; @@ -171,8 +171,8 @@ // Return the BrowserActionsController for this toolbar. - (BrowserActionsController*)browserActionsController; -// Returns the wrench button. -- (NSButton*)wrenchButton; +// Returns the app menu button. +- (NSButton*)appMenuButton; // Returns the app menu controller. - (AppMenuController*)appMenuController; @@ -184,7 +184,7 @@ // Returns an array of views in the order of the outlets above. - (NSArray*)toolbarViews; - (void)showOptionalHomeButton; -- (void)installWrenchMenu; +- (void)installAppMenu; // Return a hover button for the current event. - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent; @end
diff --git a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm index 5fd4f66..ca935fd 100644 --- a/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm +++ b/chrome/browser/ui/cocoa/toolbar/toolbar_controller.mm
@@ -88,8 +88,8 @@ // The minimum width of the location bar in pixels. const CGFloat kMinimumLocationBarWidth = 100.0; -// The amount of left padding that the wrench menu should have. -const CGFloat kWrenchMenuLeftPadding = 3.0; +// The amount of left padding that the app menu should have. +const CGFloat kAppMenuLeftPadding = 3.0; class BrowserActionsContainerDelegate : public BrowserActionsContainerViewSizeDelegate { @@ -145,8 +145,8 @@ - (void)browserActionsVisibilityChanged:(NSNotification*)notification; - (void)browserActionsContainerWillAnimate:(NSNotification*)notification; - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate; -- (void)updateWrenchButtonSeverity:(AppMenuIconPainter::Severity)severity - animate:(BOOL)animate; +- (void)updateAppMenuButtonSeverity:(AppMenuIconPainter::Severity)severity + animate:(BOOL)animate; @end namespace ToolbarControllerInternal { @@ -193,7 +193,7 @@ void UpdateBadgeSeverity(AppMenuBadgeController::BadgeType type, AppMenuIconPainter::Severity severity, bool animate) override { - [controller_ updateWrenchButtonSeverity:severity animate:animate]; + [controller_ updateAppMenuButtonSeverity:severity animate:animate]; } void OnPreferenceChanged(const std::string& pref_name) { @@ -298,18 +298,18 @@ [[homeButton_ cell] setImageID:IDR_HOME_P forButtonState:image_button_cell::kPressedState]; - [[wrenchButton_ cell] setImageID:IDR_TOOLS - forButtonState:image_button_cell::kDefaultState]; - [[wrenchButton_ cell] setImageID:IDR_TOOLS_H - forButtonState:image_button_cell::kHoverState]; - [[wrenchButton_ cell] setImageID:IDR_TOOLS_P - forButtonState:image_button_cell::kPressedState]; + [[appMenuButton_ cell] setImageID:IDR_TOOLS + forButtonState:image_button_cell::kDefaultState]; + [[appMenuButton_ cell] setImageID:IDR_TOOLS_H + forButtonState:image_button_cell::kHoverState]; + [[appMenuButton_ cell] setImageID:IDR_TOOLS_P + forButtonState:image_button_cell::kPressedState]; notificationBridge_.reset( new ToolbarControllerInternal::NotificationBridge(self)); notificationBridge_->UpdateBadgeSeverity(); - [wrenchButton_ setOpenMenuOnClick:YES]; + [appMenuButton_ setOpenMenuOnClick:YES]; [backButton_ setOpenMenuOnRightClick:YES]; [forwardButton_ setOpenMenuOnRightClick:YES]; @@ -335,7 +335,7 @@ &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged, base::Unretained(notificationBridge_.get()))); [self showOptionalHomeButton]; - [self installWrenchMenu]; + [self installAppMenu]; [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO]; @@ -375,7 +375,7 @@ view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON); view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON); view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON); - view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU); + view_id_util::SetID(appMenuButton_, VIEW_ID_APP_MENU); [self addAccessibilityDescriptions]; } @@ -407,7 +407,7 @@ view_id_util::UnsetID(backButton_); view_id_util::UnsetID(forwardButton_); view_id_util::UnsetID(homeButton_); - view_id_util::UnsetID(wrenchButton_); + view_id_util::UnsetID(appMenuButton_); // Make sure any code in the base class which assumes [self view] is // the "parent" view continues to work. @@ -461,7 +461,7 @@ accessibilitySetOverrideValue:description forAttribute:NSAccessibilityDescriptionAttribute]; description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP); - [[wrenchButton_ cell] + [[appMenuButton_ cell] accessibilitySetOverrideValue:description forAttribute:NSAccessibilityDescriptionAttribute]; } @@ -626,8 +626,8 @@ // Returns an array of views in the order of the outlets above. - (NSArray*)toolbarViews { return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_, - homeButton_, wrenchButton_, locationBar_, - browserActionsContainerView_, nil]; + homeButton_, appMenuButton_, locationBar_, + browserActionsContainerView_, nil]; } // Moves |rect| to the right by |delta|, keeping the right side fixed by @@ -660,22 +660,22 @@ [homeButton_ setHidden:hide]; } -// Install the menu wrench buttons. Calling this repeatedly is inexpensive so it +// Install the app menu buttons. Calling this repeatedly is inexpensive so it // can be done every time the buttons are shown. -- (void)installWrenchMenu { +- (void)installAppMenu { if (appMenuController_.get()) return; appMenuController_.reset( [[AppMenuController alloc] initWithBrowser:browser_]); [appMenuController_ setUseWithPopUpButtonCell:YES]; - [wrenchButton_ setAttachedMenu:[appMenuController_ menu]]; + [appMenuButton_ setAttachedMenu:[appMenuController_ menu]]; } -- (void)updateWrenchButtonSeverity:(AppMenuIconPainter::Severity)severity - animate:(BOOL)animate { +- (void)updateAppMenuButtonSeverity:(AppMenuIconPainter::Severity)severity + animate:(BOOL)animate { AppToolbarButtonCell* cell = - base::mac::ObjCCastStrict<AppToolbarButtonCell>([wrenchButton_ cell]); + base::mac::ObjCCastStrict<AppToolbarButtonCell>([appMenuButton_ cell]); [cell setSeverity:severity shouldAnimate:animate]; } @@ -758,8 +758,8 @@ CGFloat leftDistance = 0.0; if ([browserActionsContainerView_ isHidden]) { - CGFloat edgeXPos = [wrenchButton_ frame].origin.x; - leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding; + CGFloat edgeXPos = [appMenuButton_ frame].origin.x; + leftDistance = edgeXPos - locationBarXPos - kAppMenuLeftPadding; } else { leftDistance = NSMinX([browserActionsContainerView_ animationEndFrame]) - locationBarXPos; @@ -835,7 +835,7 @@ } } -// Hide the back, forward, reload, home, and wrench buttons of the toolbar. +// Hide the back, forward, reload, home, and app menu buttons of the toolbar. // This allows the location bar to occupy the entire width. There is no way to // undo this operation, and once it is called, no other programmatic changes // to the toolbar or location bar width should be made. This message is @@ -857,7 +857,7 @@ [backButton_ setHidden:YES]; [forwardButton_ setHidden:YES]; [reloadButton_ setHidden:YES]; - [wrenchButton_ setHidden:YES]; + [appMenuButton_ setHidden:YES]; [homeButton_ setHidden:YES]; } @@ -879,7 +879,7 @@ return locationBarView_->GetBookmarkBubblePoint(); // Grab bottom middle of hotdogs. - NSRect frame = wrenchButton_.frame; + NSRect frame = appMenuButton_.frame; NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame)); // Inset to account for the whitespace around the hotdogs. point.y += app_menu_controller::kAppMenuBubblePointOffsetY; @@ -913,8 +913,8 @@ return browserActionsController_.get(); } -- (NSView*)wrenchButton { - return wrenchButton_; +- (NSView*)appMenuButton { + return appMenuButton_; } - (AppMenuController*)appMenuController {
diff --git a/chrome/browser/ui/cocoa/toolbar/toolbar_controller_unittest.mm b/chrome/browser/ui/cocoa/toolbar/toolbar_controller_unittest.mm index 0cd9275..8f4c0594 100644 --- a/chrome/browser/ui/cocoa/toolbar/toolbar_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/toolbar/toolbar_controller_unittest.mm
@@ -72,7 +72,7 @@ // |-toolbarViews| method. enum SubviewIndex { kBackIndex, kForwardIndex, kReloadIndex, kHomeIndex, - kWrenchIndex, kLocationIndex, kBrowserActionContainerViewIndex + kAppMenuIndex, kLocationIndex, kBrowserActionContainerViewIndex }; void SetUp() override { @@ -172,7 +172,7 @@ EXPECT_FALSE([GetSubviewAt(kBackIndex) isHidden]); EXPECT_FALSE([GetSubviewAt(kForwardIndex) isHidden]); EXPECT_FALSE([GetSubviewAt(kReloadIndex) isHidden]); - EXPECT_FALSE([GetSubviewAt(kWrenchIndex) isHidden]); + EXPECT_FALSE([GetSubviewAt(kAppMenuIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kHomeIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kBrowserActionContainerViewIndex) isHidden]); @@ -183,7 +183,7 @@ EXPECT_FALSE([GetSubviewAt(kBackIndex) isHidden]); EXPECT_FALSE([GetSubviewAt(kForwardIndex) isHidden]); EXPECT_FALSE([GetSubviewAt(kReloadIndex) isHidden]); - EXPECT_FALSE([GetSubviewAt(kWrenchIndex) isHidden]); + EXPECT_FALSE([GetSubviewAt(kAppMenuIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kHomeIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kBrowserActionContainerViewIndex) isHidden]); @@ -199,7 +199,7 @@ EXPECT_TRUE([GetSubviewAt(kBackIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kForwardIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kReloadIndex) isHidden]); - EXPECT_TRUE([GetSubviewAt(kWrenchIndex) isHidden]); + EXPECT_TRUE([GetSubviewAt(kAppMenuIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kHomeIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kBrowserActionContainerViewIndex) isHidden]); @@ -215,7 +215,7 @@ EXPECT_TRUE([GetSubviewAt(kBackIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kForwardIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kReloadIndex) isHidden]); - EXPECT_TRUE([GetSubviewAt(kWrenchIndex) isHidden]); + EXPECT_TRUE([GetSubviewAt(kAppMenuIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kHomeIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kBrowserActionContainerViewIndex) isHidden]); @@ -241,7 +241,7 @@ EXPECT_TRUE([GetSubviewAt(kBackIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kForwardIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kReloadIndex) isHidden]); - EXPECT_TRUE([GetSubviewAt(kWrenchIndex) isHidden]); + EXPECT_TRUE([GetSubviewAt(kAppMenuIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kHomeIndex) isHidden]); EXPECT_TRUE([GetSubviewAt(kBrowserActionContainerViewIndex) isHidden]); }
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc index b8354944..4b08c7fa 100644 --- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc +++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -396,12 +396,17 @@ void ManagePasswordsBubbleModel::UpdatePendingStateTitle() { title_brand_link_range_ = gfx::Range(); + PasswordTittleType type = + state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE + ? PasswordTittleType::UPDATE_PASSWORD + : (pending_password_.federation_url.is_empty() + ? PasswordTittleType::SAVE_PASSWORD + : PasswordTittleType::SAVE_ACCOUNT); GetSavePasswordDialogTitleTextAndLinkRange( web_contents()->GetVisibleURL(), origin(), GetSmartLockBrandingState(GetProfile()) != password_bubble_experiment::SmartLockBranding::NONE, - state_ == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE, &title_, - &title_brand_link_range_); + type, &title_, &title_brand_link_range_); } void ManagePasswordsBubbleModel::UpdateManageStateTitle() {
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc index f6e60b1..01a4cdeb 100644 --- a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc +++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
@@ -52,13 +52,23 @@ const GURL& user_visible_url, const GURL& form_origin_url, bool is_smartlock_branding_enabled, - bool is_update_password_bubble, + PasswordTittleType dialog_type, base::string16* title, gfx::Range* title_link_range) { std::vector<size_t> offsets; std::vector<base::string16> replacements; - int title_id = - is_update_password_bubble ? IDS_UPDATE_PASSWORD : IDS_SAVE_PASSWORD; + int title_id = 0; + switch (dialog_type) { + case PasswordTittleType::SAVE_PASSWORD: + title_id = IDS_SAVE_PASSWORD; + break; + case PasswordTittleType::SAVE_ACCOUNT: + title_id = IDS_SAVE_ACCOUNT; + break; + case PasswordTittleType::UPDATE_PASSWORD: + title_id = IDS_UPDATE_PASSWORD; + break; + } // Check whether the registry controlled domains for user-visible URL (i.e. // the one seen in the omnibox) and the password form post-submit navigation
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.h b/chrome/browser/ui/passwords/manage_passwords_view_utils.h index 7a1fa27..6846eb4 100644 --- a/chrome/browser/ui/passwords/manage_passwords_view_utils.h +++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.h
@@ -18,6 +18,12 @@ class GURL; +enum class PasswordTittleType { + SAVE_PASSWORD, // plain password + SAVE_ACCOUNT, // login via IDP + UPDATE_PASSWORD, // update plain password +}; + // The desired width and height in pixels for an account avatar. extern const int kAvatarImageSize; @@ -25,9 +31,9 @@ gfx::ImageSkia ScaleImageForAccountAvatar(gfx::ImageSkia image_skia); // Sets the formatted |title| in the Save Password bubble or the Update Password -// bubble (depending on |is_update_password_bubble|). If the registry -// controlled domain of |user_visible_url| (i.e. the one seen in the omnibox) -// differs from the registry controlled domain of |form_origin_url|, sets +// bubble (depending on |dialog_type|). If the registry controlled domain of +// |user_visible_url| (i.e. the one seen in the omnibox) differs from the +// registry controlled domain of |form_origin_url|, sets // |IDS_SAVE_PASSWORD_TITLE| as the |title| so that it replaces "this site" in // title text with output of |FormatUrlForSecurityDisplay(form_origin_url)|. // Otherwise, sets |IDS_SAVE_PASSWORD| as the |title| having "this site". @@ -38,7 +44,7 @@ const GURL& user_visible_url, const GURL& form_origin_url, bool is_smartlock_branding_enabled, - bool is_update_password_bubble, + PasswordTittleType dialog_type, base::string16* title, gfx::Range* title_link_range);
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_view_utils_unittest.cc index 2f2f97b..144943de 100644 --- a/chrome/browser/ui/passwords/manage_passwords_view_utils_unittest.cc +++ b/chrome/browser/ui/passwords/manage_passwords_view_utils_unittest.cc
@@ -18,50 +18,65 @@ const char* const user_visible_url; const char* const form_origin_url; bool is_smartlock_branding_enabled; - bool is_update_password_bubble; + PasswordTittleType bubble_type; const char* const expected_domain_placeholder; // "this site" or domain name size_t expected_link_range_start; size_t expected_link_range_end; } kDomainsTestCases[] = { // Same domains. {"http://example.com/landing", "http://example.com/login#form?value=3", - false, false, "this site", 0, 0}, + false, PasswordTittleType::SAVE_PASSWORD, "this site", 0, 0}, {"http://example.com/landing", "http://example.com/login#form?value=3", - true, false, "this site", 12, 29}, + true, PasswordTittleType::SAVE_PASSWORD, "this site", 12, 29}, // Different subdomains. {"https://a.example.com/landing", - "https://b.example.com/login#form?value=3", false, false, "this site", 0, - 0}, + "https://b.example.com/login#form?value=3", false, + PasswordTittleType::SAVE_PASSWORD, "this site", 0, 0}, {"https://a.example.com/landing", - "https://b.example.com/login#form?value=3", true, false, "this site", 12, - 29}, + "https://b.example.com/login#form?value=3", true, + PasswordTittleType::SAVE_PASSWORD, "this site", 12, 29}, // Different domains. {"https://another.org", "https://example.com:/login#form?value=3", false, - false, "https://example.com", 0, 0}, + PasswordTittleType::SAVE_PASSWORD, "https://example.com", 0, 0}, {"https://another.org", "https://example.com/login#form?value=3", true, - false, "https://example.com", 12, 29}, + PasswordTittleType::SAVE_PASSWORD, "https://example.com", 12, 29}, // Different domains and password form origin url with // default port for the scheme. {"https://another.org", "https://example.com:443/login#form?value=3", false, - false, "https://example.com", 0, 0}, + PasswordTittleType::SAVE_PASSWORD, "https://example.com", 0, 0}, {"https://another.org", "http://example.com:80/login#form?value=3", true, - false, "http://example.com", 12, 29}, + PasswordTittleType::SAVE_PASSWORD, "http://example.com", 12, 29}, // Different domains and password form origin url with // non-default port for the scheme. {"https://another.org", "https://example.com:8001/login#form?value=3", - false, false, "https://example.com:8001", 0, 0}, + false, PasswordTittleType::SAVE_PASSWORD, "https://example.com:8001", 0, + 0}, {"https://another.org", "https://example.com:8001/login#form?value=3", true, - false, "https://example.com:8001", 12, 29}, + PasswordTittleType::SAVE_PASSWORD, "https://example.com:8001", 12, 29}, // Update bubble. {"http://example.com/landing", "http://example.com/login#form?value=3", - false, true, "this site", 0, 0}, + false, PasswordTittleType::UPDATE_PASSWORD, "this site", 0, 0}, {"http://example.com/landing", "http://example.com/login#form?value=3", - true, true, "this site", 12, 29}}; + true, PasswordTittleType::UPDATE_PASSWORD, "this site", 12, 29}, + + // Same domains, federated credential. + {"http://example.com/landing", "http://example.com/login#form?value=3", + false, PasswordTittleType::SAVE_ACCOUNT, "this site", 0, 0}, + {"http://example.com/landing", "http://example.com/login#form?value=3", + true, PasswordTittleType::SAVE_ACCOUNT, "this site", 12, 29}, + + // Different subdomains, federated credential. + {"https://a.example.com/landing", + "https://b.example.com/login#form?value=3", false, + PasswordTittleType::SAVE_ACCOUNT, "this site", 0, 0}, + {"https://a.example.com/landing", + "https://b.example.com/login#form?value=3", true, + PasswordTittleType::SAVE_ACCOUNT, "this site", 12, 29}}; } // namespace @@ -79,8 +94,7 @@ GURL(kDomainsTestCases[i].user_visible_url), GURL(kDomainsTestCases[i].form_origin_url), kDomainsTestCases[i].is_smartlock_branding_enabled, - kDomainsTestCases[i].is_update_password_bubble, &title, - &title_link_range); + kDomainsTestCases[i].bubble_type, &title, &title_link_range); // Verify against expectations. base::string16 domain = @@ -90,7 +104,8 @@ title_link_range.start()); EXPECT_EQ(kDomainsTestCases[i].expected_link_range_end, title_link_range.end()); - if (kDomainsTestCases[i].is_update_password_bubble) { + if (kDomainsTestCases[i].bubble_type == + PasswordTittleType::UPDATE_PASSWORD) { EXPECT_TRUE(title.find(base::ASCIIToUTF16("update")) != base::string16::npos); } else {
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc index f25c0fd7..b441079 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.cc +++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -982,7 +982,7 @@ AddSeparator(ui::LOWER_SEPARATOR); // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the - // layout for this menu item in WrenchMenu.xib. It does, however, use the + // layout for this menu item in AppMenu.xib. It does, however, use the // command_id value from AddButtonItem() to identify this special item. edit_menu_item_model_.reset(new ui::ButtonMenuItemModel(IDS_EDIT, this)); edit_menu_item_model_->AddGroupItemWithStringId(IDC_CUT, IDS_CUT); @@ -998,7 +998,7 @@ AddSeparator(ui::LOWER_SEPARATOR); // WARNING: Mac does not use the ButtonMenuItemModel, but instead defines the - // layout for this menu item in WrenchMenu.xib. It does, however, use the + // layout for this menu item in AppMenu.xib. It does, however, use the // command_id value from AddButtonItem() to identify this special item. zoom_menu_item_model_.reset( new ui::ButtonMenuItemModel(IDS_ZOOM_MENU, this));
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc index 7cdc6b0..54e273a 100644 --- a/chrome/browser/ui/views/content_setting_bubble_contents.cc +++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -337,7 +337,7 @@ } } - UpdateMenuButtonSizes(); + UpdateMenuButtonSizes(GetNativeTheme()); const gfx::FontList& domain_font = ui::ResourceBundle::GetSharedInstance().GetFontList( @@ -409,6 +409,12 @@ GetWidget()->Close(); } +void ContentSettingBubbleContents::OnNativeThemeChanged( + const ui::NativeTheme* theme) { + views::BubbleDelegateView::OnNativeThemeChanged(theme); + UpdateMenuButtonSizes(theme); +} + void ContentSettingBubbleContents::ButtonPressed(views::Button* sender, const ui::Event& event) { RadioGroup::const_iterator i( @@ -466,8 +472,9 @@ ui::MENU_SOURCE_NONE)); } -void ContentSettingBubbleContents::UpdateMenuButtonSizes() { - const views::MenuConfig& config = views::MenuConfig::instance(); +void ContentSettingBubbleContents::UpdateMenuButtonSizes( + const ui::NativeTheme* theme) { + const views::MenuConfig config = views::MenuConfig(theme); const int margins = config.item_left_margin + config.check_width + config.label_to_arrow_padding + config.arrow_width + config.arrow_to_edge_padding;
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h index c6bc636..62968a4 100644 --- a/chrome/browser/ui/views/content_setting_bubble_contents.h +++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -76,6 +76,9 @@ const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) override; + // views::View: + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override; @@ -87,7 +90,7 @@ const gfx::Point& point) override; // Helper to get the preferred width of the media menu. - void UpdateMenuButtonSizes(); + void UpdateMenuButtonSizes(const ui::NativeTheme* theme); // Provides data for this bubble. scoped_ptr<ContentSettingBubbleModel> content_setting_bubble_model_;
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc index cd65d6ae..3b920e7 100644 --- a/chrome/browser/ui/views/toolbar/app_menu.cc +++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -272,7 +272,6 @@ set_background(in_menu_background_); SetBorder(views::Border::CreateEmptyBorder(0, kHorizontalPadding, 0, kHorizontalPadding)); - SetFontList(MenuConfig::instance().font_list); } void SetOtherButtons(const InMenuButton* left, const InMenuButton* right) { @@ -281,6 +280,9 @@ // views::LabelButton void OnNativeThemeChanged(const ui::NativeTheme* theme) override { + const MenuConfig& menu_config = MenuConfig::instance(theme); + SetFontList(menu_config.font_list); + if (theme) { SetTextColor( views::Button::STATE_DISABLED, @@ -594,9 +596,10 @@ void OnNativeThemeChanged(const ui::NativeTheme* theme) override { AppMenuView::OnNativeThemeChanged(theme); + const MenuConfig& menu_config = MenuConfig::instance(theme); zoom_label_->SetBorder(views::Border::CreateEmptyBorder( 0, kZoomLabelHorizontalPadding, 0, kZoomLabelHorizontalPadding)); - zoom_label_->SetFontList(MenuConfig::instance().font_list); + zoom_label_->SetFontList(menu_config.font_list); zoom_label_max_width_valid_ = false; if (theme) { @@ -1112,7 +1115,7 @@ if (model->GetCommandIdAt(i) == IDC_EDIT_MENU || model->GetCommandIdAt(i) == IDC_ZOOM_MENU) { - const MenuConfig& config = views::MenuConfig::instance(); + const MenuConfig& config = item->GetMenuConfig(); int top_margin = config.item_top_margin + config.separator_height / 2; int bottom_margin = config.item_bottom_margin + config.separator_height / 2;
diff --git a/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc index 0232e6c2..20e3fab 100644 --- a/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc +++ b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc
@@ -74,7 +74,8 @@ int ExtensionToolbarMenuView::GetHeightForWidth(int width) const { // The width passed in here includes the full width of the menu, so we need // to omit the necessary padding. - const views::MenuConfig& menu_config = views::MenuConfig::instance(); + const views::MenuConfig& menu_config = + static_cast<const views::MenuItemView*>(parent())->GetMenuConfig(); int end_padding = menu_config.arrow_to_edge_padding - container_->toolbar_actions_bar()->platform_settings().item_spacing; width -= start_padding() + end_padding;
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc index f756bc5..c6062162 100644 --- a/chrome/browser/ui/webui/engagement/site_engagement_ui.cc +++ b/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -12,7 +12,7 @@ #include "content/public/browser/web_ui_data_source.h" #include "grit/browser_resources.h" #include "mojo/common/url_type_converters.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/binding.h" namespace { @@ -51,7 +51,7 @@ // The Profile* handed to us in our constructor. Profile* profile_; - mojo::StrongBinding<SiteEngagementUIHandler> binding_; + mojo::Binding<SiteEngagementUIHandler> binding_; DISALLOW_COPY_AND_ASSIGN(SiteEngagementUIHandlerImpl); }; @@ -83,6 +83,6 @@ void SiteEngagementUI::BindUIHandler( mojo::InterfaceRequest<SiteEngagementUIHandler> request) { - // SiteEngagementUIHandlerImpl deletes itself when the pipe is closed. - new SiteEngagementUIHandlerImpl(Profile::FromWebUI(web_ui()), request.Pass()); + ui_handler_.reset(new SiteEngagementUIHandlerImpl( + Profile::FromWebUI(web_ui()), request.Pass())); }
diff --git a/chrome/browser/ui/webui/engagement/site_engagement_ui.h b/chrome/browser/ui/webui/engagement/site_engagement_ui.h index 678362a0..dbedebf 100644 --- a/chrome/browser/ui/webui/engagement/site_engagement_ui.h +++ b/chrome/browser/ui/webui/engagement/site_engagement_ui.h
@@ -20,6 +20,8 @@ void BindUIHandler( mojo::InterfaceRequest<SiteEngagementUIHandler> request) override; + scoped_ptr<SiteEngagementUIHandler> ui_handler_; + DISALLOW_COPY_AND_ASSIGN(SiteEngagementUI); };
diff --git a/chrome/chrome_exe.gypi b/chrome/chrome_exe.gypi index 9117c87e6..07f2a78 100644 --- a/chrome/chrome_exe.gypi +++ b/chrome/chrome_exe.gypi
@@ -531,6 +531,7 @@ ], 'dependencies': [ '../base/base.gyp:base', + '../components/components.gyp:startup_metric_utils_browser', ], }, ],
diff --git a/chrome/chrome_nibs.gypi b/chrome/chrome_nibs.gypi index 6c6632e..58a2ef5 100644 --- a/chrome/chrome_nibs.gypi +++ b/chrome/chrome_nibs.gypi
@@ -9,6 +9,7 @@ { 'variables': { 'mac_translated_xibs': [ + 'app/nibs/AppMenu.xib', 'app/nibs/AvatarMenuItem.xib', 'app/nibs/BookmarkAllTabs.xib', 'app/nibs/BookmarkBar.xib', @@ -18,12 +19,12 @@ 'app/nibs/CollectedCookies.xib', 'app/nibs/ContentBlockedCookies.xib', 'app/nibs/ContentBlockedDownloads.xib', - 'app/nibs/ContentBlockedMIDISysEx.xib', + 'app/nibs/ContentBlockedGeolocation.xib', 'app/nibs/ContentBlockedMedia.xib', + 'app/nibs/ContentBlockedMIDISysEx.xib', 'app/nibs/ContentBlockedMixedScript.xib', 'app/nibs/ContentBlockedPlugins.xib', 'app/nibs/ContentBlockedPopups.xib', - 'app/nibs/ContentBlockedGeolocation.xib', 'app/nibs/ContentBlockedSimple.xib', 'app/nibs/ContentProtocolHandlers.xib', 'app/nibs/CookieDetailsView.xib', @@ -31,11 +32,11 @@ 'app/nibs/DownloadShelf.xib', 'app/nibs/EditSearchEngine.xib', 'app/nibs/ExclusiveAccessBubble.xib', - 'app/nibs/ExtensionInstallPrompt.xib', + 'app/nibs/ExtensionInstalledBubble.xib', 'app/nibs/ExtensionInstallPromptBundle.xib', 'app/nibs/ExtensionInstallPromptNoWarnings.xib', 'app/nibs/ExtensionInstallPromptWebstoreData.xib', - 'app/nibs/ExtensionInstalledBubble.xib', + 'app/nibs/ExtensionInstallPrompt.xib', 'app/nibs/FirstRunBubble.xib', 'app/nibs/FirstRunDialog.xib', 'app/nibs/HttpAuthLoginSheet.xib', @@ -47,7 +48,6 @@ 'app/nibs/SaveAccessoryView.xib', 'app/nibs/TaskManager.xib', 'app/nibs/Toolbar.xib', - 'app/nibs/WrenchMenu.xib', ], # mac_translated_xibs 'mac_untranslated_xibs': [ 'app/nibs/ActionBoxMenuItem.xib',
diff --git a/chrome/common/localized_error.cc b/chrome/common/localized_error.cc index 114c252..a70f308 100644 --- a/chrome/common/localized_error.cc +++ b/chrome/common/localized_error.cc
@@ -486,7 +486,7 @@ } // Returns a dictionary containing the strings for the settings menu under the -// wrench, and the advanced settings button. +// app menu, and the advanced settings button. base::DictionaryValue* GetStandardMenuItemsText() { base::DictionaryValue* standard_menu_items_text = new base::DictionaryValue(); standard_menu_items_text->SetString("settingsTitle",
diff --git a/chrome/ct_dm.isolate b/chrome/ct_dm.isolate deleted file mode 100644 index ee573c4..0000000 --- a/chrome/ct_dm.isolate +++ /dev/null
@@ -1,23 +0,0 @@ -# Copyright (c) 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'conditions': [ - ['OS=="linux"', { - 'variables': { - 'files': [ - '../../skia/resources/', - '../../skia/out/Debug/dm', - '../content/test/ct/run_ct_dm.py', - '../../skps/slave<(SLAVE_NUM)/', - ], - 'command': [ - 'python', - '../content/test/ct/run_ct_dm.py', - '--slave_num=<(SLAVE_NUM)', - ], - }, - }], - ] -}
diff --git a/chrome/ct_skps.isolate b/chrome/ct_skps.isolate new file mode 100644 index 0000000..25e1c6de --- /dev/null +++ b/chrome/ct_skps.isolate
@@ -0,0 +1,26 @@ +# Copyright (c) 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'conditions': [ + ['OS=="linux"', { + 'variables': { + 'files': [ + '../../skia/resources/', + '../../skia/out/Debug/<(TOOL_NAME)', + '../content/test/ct/run_ct_skps.py', + '../../skps/slave<(SLAVE_NUM)/', + ], + 'command': [ + 'python', + '../content/test/ct/run_ct_skps.py', + '--slave_num', '<(SLAVE_NUM)', + '--tool', '<(TOOL_NAME)', + '--git_hash', '<(GIT_HASH)', + '--isolated_outdir', '${ISOLATED_OUTDIR}', + ], + }, + }], + ] +}
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc index d739cdc..e15afb0 100644 --- a/chrome/renderer/net/net_error_helper.cc +++ b/chrome/renderer/net/net_error_helper.cc
@@ -112,6 +112,11 @@ void NetErrorHelper::DidCommitProvisionalLoad(bool is_new_navigation, bool is_same_page_navigation) { + // If this is a "same page" navigation, it's not a real navigation. There + // wasn't a start event for it, either, so just ignore it. + if (is_same_page_navigation) + return; + // Invalidate weak pointers from old error page controllers. If loading a new // error page, the controller has not yet been attached, so this won't affect // it.
diff --git a/chrome/renderer/spellchecker/spellcheck.cc b/chrome/renderer/spellchecker/spellcheck.cc index a1c3e4c..a10e17c 100644 --- a/chrome/renderer/spellchecker/spellcheck.cc +++ b/chrome/renderer/spellchecker/spellcheck.cc
@@ -12,7 +12,6 @@ #include "base/location.h" #include "base/logging.h" #include "base/single_thread_task_runner.h" -#include "base/sys_info.h" #include "base/thread_task_runner_handle.h" #include "chrome/common/channel_info.h" #include "chrome/common/chrome_switches.h" @@ -21,7 +20,6 @@ #include "chrome/common/spellcheck_result.h" #include "chrome/renderer/spellchecker/spellcheck_language.h" #include "chrome/renderer/spellchecker/spellcheck_provider.h" -#include "components/version_info/version_info.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/render_view_visitor.h" @@ -537,14 +535,7 @@ bool SpellCheck::IsSpellcheckEnabled() { #if defined(OS_ANDROID) - if (base::SysInfo::IsLowEndDevice()) - return false; - - version_info::Channel channel = chrome::GetChannel(); - if (channel == version_info::Channel::DEV || - channel == version_info::Channel::CANARY) { - return true; - } else if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableAndroidSpellChecker)) { return false; }
diff --git a/chrome/test/data/save_page/frames-encodings.htm b/chrome/test/data/save_page/frames-encodings.htm new file mode 100644 index 0000000..1655c76 --- /dev/null +++ b/chrome/test/data/save_page/frames-encodings.htm
@@ -0,0 +1,21 @@ +<!doctype html> +<html> +<head></head> +<body> + <h1>Test page features</h1> + + This page embeds iframes that use various different encodings. + + <h1>Test content</h1> + + Content verification marker: + frames-encodings.htm: f53295dd-a95b-4b32-85f5-b6e15377fb20 + + <iframe src="encoding-iso-8859-2.htm"></iframe> + <iframe src="encoding-utf16-le-bom.htm"></iframe> + <iframe src="encoding-utf16-le-nobom.htm"></iframe> + <iframe src="encoding-utf16-be-bom.htm"></iframe> + <iframe src="encoding-utf16-be-nobom.htm"></iframe> + <iframe src="encoding-utf32.htm"></iframe> +</body> +</html>
diff --git a/components/autofill.gypi b/components/autofill.gypi index 2f2ac05..af4f855 100644 --- a/components/autofill.gypi +++ b/components/autofill.gypi
@@ -11,7 +11,6 @@ 'dependencies': [ '../base/base.gyp:base', '../base/base.gyp:base_i18n', - '../third_party/re2/re2.gyp:re2', '../ui/base/ui_base.gyp:ui_base', '../ui/gfx/gfx.gyp:gfx', '../url/url.gyp:url_lib',
diff --git a/components/autofill/core/browser/address_field.cc b/components/autofill/core/browser/address_field.cc index b9948dbc..bf96dc2 100644 --- a/components/autofill/core/browser/address_field.cc +++ b/components/autofill/core/browser/address_field.cc
@@ -8,12 +8,16 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_scanner.h" #include "components/autofill/core/browser/field_types.h" +using base::UTF8ToUTF16; + namespace autofill { namespace { @@ -44,6 +48,9 @@ const AutofillField* const initial_field = scanner->Cursor(); size_t saved_cursor = scanner->SaveCursor(); + base::string16 attention_ignored = UTF8ToUTF16(kAttentionIgnoredRe); + base::string16 region_ignored = UTF8ToUTF16(kRegionIgnoredRe); + // Allow address fields to appear in any order. size_t begin_trailing_non_labeled_fields = 0; bool has_trailing_non_labeled_fields = false; @@ -55,8 +62,8 @@ address_field->ParseCompany(scanner)) { has_trailing_non_labeled_fields = false; continue; - } else if (ParseField(scanner, kAttentionIgnoredRe, NULL) || - ParseField(scanner, kRegionIgnoredRe, NULL)) { + } else if (ParseField(scanner, attention_ignored, NULL) || + ParseField(scanner, region_ignored, NULL)) { // We ignore the following: // * Attention. // * Province/Region/Other. @@ -141,7 +148,7 @@ if (company_ && !company_->IsEmpty()) return false; - return ParseField(scanner, kCompanyRe, &company_); + return ParseField(scanner, UTF8ToUTF16(kCompanyRe), &company_); } bool AddressField::ParseAddressLines(AutofillScanner* scanner) { @@ -157,17 +164,19 @@ return false; // Ignore "Address Lookup" field. http://crbug.com/427622 - if (ParseField(scanner, kAddressLookupRe, NULL)) + if (ParseField(scanner, base::UTF8ToUTF16(kAddressLookupRe), NULL)) return false; - if (!ParseFieldSpecifics(scanner, kAddressLine1Re, MATCH_DEFAULT, + base::string16 pattern = UTF8ToUTF16(kAddressLine1Re); + base::string16 label_pattern = UTF8ToUTF16(kAddressLine1LabelRe); + if (!ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, &address1_) && + !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, &address1_) && - !ParseFieldSpecifics(scanner, kAddressLine1LabelRe, - MATCH_LABEL | MATCH_TEXT, &address1_) && - !ParseFieldSpecifics(scanner, kAddressLine1Re, - MATCH_DEFAULT | MATCH_TEXT_AREA, &street_address_) && - !ParseFieldSpecifics(scanner, kAddressLine1LabelRe, - MATCH_LABEL | MATCH_TEXT_AREA, &street_address_)) + !ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_TEXT_AREA, + &street_address_) && + !ParseFieldSpecifics(scanner, label_pattern, + MATCH_LABEL | MATCH_TEXT_AREA, + &street_address_)) return false; if (street_address_) @@ -176,16 +185,19 @@ // This code may not pick up pages that have an address field consisting of a // sequence of unlabeled address fields. If we need to add this, see // discussion on https://codereview.chromium.org/741493003/ - if (!ParseField(scanner, kAddressLine2Re, &address2_) && - !ParseFieldSpecifics(scanner, kAddressLine2LabelRe, - MATCH_LABEL | MATCH_TEXT, &address2_)) + pattern = UTF8ToUTF16(kAddressLine2Re); + label_pattern = UTF8ToUTF16(kAddressLine2LabelRe); + if (!ParseField(scanner, pattern, &address2_) && + !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, + &address2_)) return true; // Optionally parse address line 3. This uses the same label regexp as // address 2 above. - if (!ParseField(scanner, kAddressLinesExtraRe, &address3_) && - !ParseFieldSpecifics(scanner, kAddressLine2LabelRe, - MATCH_LABEL | MATCH_TEXT, &address3_)) + pattern = UTF8ToUTF16(kAddressLinesExtraRe); + if (!ParseField(scanner, pattern, &address3_) && + !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, + &address3_)) return true; // Try for surplus lines, which we will promptly discard. Some pages have 4 @@ -193,7 +205,8 @@ // // Since these are rare, don't bother considering unlabeled lines as extra // address lines. - while (ParseField(scanner, kAddressLinesExtraRe, NULL)) { + pattern = UTF8ToUTF16(kAddressLinesExtraRe); + while (ParseField(scanner, pattern, NULL)) { // Consumed a surplus line, try for another. } return true; @@ -204,7 +217,9 @@ return false; scanner->SaveCursor(); - if (ParseFieldSpecifics(scanner, kCountryRe, MATCH_DEFAULT | MATCH_SELECT, + if (ParseFieldSpecifics(scanner, + UTF8ToUTF16(kCountryRe), + MATCH_DEFAULT | MATCH_SELECT, &country_)) { return true; } @@ -212,7 +227,8 @@ // The occasional page (e.g. google account registration page) calls this a // "location". However, this only makes sense for select tags. scanner->Rewind(); - return ParseFieldSpecifics(scanner, kCountryLocationRe, + return ParseFieldSpecifics(scanner, + UTF8ToUTF16(kCountryLocationRe), MATCH_LABEL | MATCH_NAME | MATCH_SELECT, &country_); } @@ -221,13 +237,16 @@ if (zip_) return false; - if (!ParseFieldSpecifics(scanner, kZipCodeRe, kZipCodeMatchType, &zip_)) { + if (!ParseFieldSpecifics(scanner, + UTF8ToUTF16(kZipCodeRe), + kZipCodeMatchType, + &zip_)) { return false; } // Look for a zip+4, whose field name will also often contain // the substring "zip". - ParseFieldSpecifics(scanner, kZip4Re, kZipCodeMatchType, &zip4_); + ParseFieldSpecifics(scanner, UTF8ToUTF16(kZip4Re), kZipCodeMatchType, &zip4_); return true; } @@ -235,21 +254,20 @@ if (city_) return false; - return ParseFieldSpecifics(scanner, kCityRe, kCityMatchType, &city_); + return ParseFieldSpecifics(scanner, + UTF8ToUTF16(kCityRe), + kCityMatchType, + &city_); } bool AddressField::ParseState(AutofillScanner* scanner) { if (state_) return false; - // Ignore spurious matches for "United States". - size_t saved_cursor = scanner->SaveCursor(); - if (ParseFieldSpecifics(scanner, "United States", kStateMatchType, nullptr)) { - scanner->RewindTo(saved_cursor); - return false; - } - - return ParseFieldSpecifics(scanner, kStateRe, kStateMatchType, &state_); + return ParseFieldSpecifics(scanner, + UTF8ToUTF16(kStateRe), + kStateMatchType, + &state_); } bool AddressField::ParseCityStateZipCode(AutofillScanner* scanner) { @@ -311,7 +329,7 @@ return RESULT_MATCH_NONE; ParseNameLabelResult result = ParseNameAndLabelSeparately( - scanner, kZipCodeRe, kZipCodeMatchType, &zip_); + scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, &zip_); if (result != RESULT_MATCH_NAME_LABEL || scanner->IsEnd()) return result; @@ -331,7 +349,10 @@ if (!found_non_zip4) { // Look for a zip+4, whose field name will also often contain // the substring "zip". - ParseFieldSpecifics(scanner, kZip4Re, kZipCodeMatchType, &zip4_); + ParseFieldSpecifics(scanner, + UTF8ToUTF16(kZip4Re), + kZipCodeMatchType, + &zip4_); } return result; } @@ -341,7 +362,8 @@ if (city_) return RESULT_MATCH_NONE; - return ParseNameAndLabelSeparately(scanner, kCityRe, kCityMatchType, &city_); + return ParseNameAndLabelSeparately( + scanner, UTF8ToUTF16(kCityRe), kCityMatchType, &city_); } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState( @@ -349,14 +371,8 @@ if (state_) return RESULT_MATCH_NONE; - size_t saved_cursor = scanner->SaveCursor(); - if (ParseFieldSpecifics(scanner, "United States", kStateMatchType, nullptr)) { - scanner->RewindTo(saved_cursor); - return RESULT_MATCH_NONE; - } - - return ParseNameAndLabelSeparately(scanner, kStateRe, kStateMatchType, - &state_); + return ParseNameAndLabelSeparately( + scanner, UTF8ToUTF16(kStateRe), kStateMatchType, &state_); } } // namespace autofill
diff --git a/components/autofill/core/browser/autofill_country.cc b/components/autofill/core/browser/autofill_country.cc index 9740eb4..b45719b7 100644 --- a/components/autofill/core/browser/autofill_country.cc +++ b/components/autofill/core/browser/autofill_country.cc
@@ -15,6 +15,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/common/autofill_l10n_util.h" #include "grit/components_strings.h" #include "third_party/icu/source/common/unicode/locid.h" #include "third_party/icu/source/common/unicode/uloc.h" @@ -869,7 +870,7 @@ const std::string& locale); // Returns an ICU collator -- i.e. string comparator -- appropriate for the - // given |locale|. + // given |locale|, or null if no collator is available. icu::Collator* GetCollatorForLocale(const std::string& locale); // Returns the ICU sort key corresponding to |str| for the given |collator|. @@ -985,6 +986,9 @@ AddLocalizedNamesForLocale(locale); icu::Collator* collator = GetCollatorForLocale(locale); + // In very rare cases, the collator fails to initialize. + if (!collator) + return std::string(); // As recommended[1] by ICU, initialize the buffer size to four times the // source string length. @@ -1009,12 +1013,13 @@ icu::Collator* CountryNames::GetCollatorForLocale(const std::string& locale) { if (!collators_.count(locale)) { - icu::Locale icu_locale(locale.c_str()); - UErrorCode ignored = U_ZERO_ERROR; - icu::Collator* collator(icu::Collator::createInstance(icu_locale, ignored)); + icu::Collator* collator( + autofill::l10n::GetCollatorForLocale(icu::Locale(locale.c_str()))); + if (!collator) + return nullptr; // Compare case-insensitively and ignoring punctuation. - ignored = U_ZERO_ERROR; + UErrorCode ignored = U_ZERO_ERROR; collator->setAttribute(UCOL_STRENGTH, UCOL_SECONDARY, ignored); ignored = U_ZERO_ERROR; collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, ignored);
diff --git a/components/autofill/core/browser/autofill_regex_constants.cc b/components/autofill/core/browser/autofill_regex_constants.cc index d4b49cb..4aed1a53 100644 --- a/components/autofill/core/browser/autofill_regex_constants.cc +++ b/components/autofill/core/browser/autofill_regex_constants.cc
@@ -8,10 +8,6 @@ #include "components/autofill/core/browser/autofill_regex_constants.h" -// This macro is to workaround the fact that RE2 library only supports ASCII -// word boundaries and it is supposed to be the same as \b. -#define WORDBREAK "(\\A|\\z|\\PL)" - namespace autofill { ///////////////////////////////////////////////////////////////////////////// @@ -88,11 +84,11 @@ "zip|postal|post.*code|pcode" "|pin.?code" // en-IN "|postleitzahl" // de-DE - "|" WORDBREAK "cp" WORDBREAK // es - "|" WORDBREAK "cdp" WORDBREAK // fr-FR - "|" WORDBREAK "cap" WORDBREAK // it-IT + "|\\bcp\\b" // es + "|\\bcdp\\b" // fr-FR + "|\\bcap\\b" // it-IT "|郵便番号" // ja-JP - "|codigo|codpos|" WORDBREAK "cep" WORDBREAK // pt-BR, pt-PT + "|codigo|codpos|\\bcep\\b" // pt-BR, pt-PT "|Почтовый.?Индекс" // ru "|邮政编码|邮编" // zh-CN "|郵遞區號" // zh-TW @@ -102,7 +98,7 @@ "|codpos2"; // pt-BR, pt-PT const char kCityRe[] = "city|town" - "|" WORDBREAK "ort" WORDBREAK "|stadt" // de-DE + "|\\bort\\b|stadt" // de-DE "|suburb" // en-AU "|ciudad|provincia|localidad|poblacion" // es "|ville|commune" // fr-FR @@ -114,7 +110,7 @@ "|分區" // zh-TW "|^시[^도·・]|시[·・]?군[·・]?구"; // ko-KR const char kStateRe[] = - "state|county|region|province" + "(?<!united )state|county|region|province" "|land" // de-DE "|county|principality" // en-UK "|都道府県" // ja-JP @@ -128,8 +124,7 @@ // credit_card_field.cc ///////////////////////////////////////////////////////////////////////////// const char kNameOnCardRe[] = - "card.?(holder|owner)|name.*" WORDBREAK "on" WORDBREAK ".*card" - "|(card|cc).?name|cc.?full.?name" + "card.?(holder|owner)|name.*\\bon\\b.*card|(card|cc).?name|cc.?full.?name" "|karteninhaber" // de-DE "|nombre.*tarjeta" // es "|nom.*carte" // fr-FR @@ -153,7 +148,7 @@ const char kCardCvcRe[] = "verification|card identification|security code|card code" "|cvn|cvv|cvc|csc|cvd|cid|ccv" - "|" WORDBREAK "cid" WORDBREAK; + "|\\bcid\\b"; // "Expiration date" is the most common label here, but some pages have // "Expires", "exp. date" or "exp. month" and "exp. year". We also look @@ -252,8 +247,7 @@ "|nome" // pt-BR, pt-PT "|Имя" // ru "|이름"; // ko-KR -const char kMiddleInitialRe[] = - "middle.*initial|m\\.i\\.|mi$|" WORDBREAK "mi" WORDBREAK; +const char kMiddleInitialRe[] = "middle.*initial|m\\.i\\.|mi$|\\bmi\\b"; const char kMiddleNameRe[] = "middle.*name|mname|middle$" "|apellido.?materno|lastlastname"; // es @@ -299,9 +293,7 @@ const char kPhoneSuffixRe[] = "suffix"; const char kPhoneExtensionRe[] = - WORDBREAK "ext|ext" WORDBREAK "|extension" + "\\bext|ext\\b|extension" "|ramal"; // pt-BR, pt-PT } // namespace autofill - -#undef WORDBREAK
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc index 51d697f..00593e7 100644 --- a/components/autofill/core/browser/credit_card.cc +++ b/components/autofill/core/browser/credit_card.cc
@@ -444,7 +444,7 @@ void CreditCard::SetInfoForMonthInputType(const base::string16& value) { // Check if |text| is "yyyy-mm" format first, and check normal month format. - if (!MatchesPattern(value, "^[0-9]{4}-[0-9]{1,2}$")) + if (!MatchesPattern(value, base::UTF8ToUTF16("^[0-9]{4}-[0-9]{1,2}$"))) return; std::vector<base::StringPiece16> year_month = base::SplitStringPiece(
diff --git a/components/autofill/core/browser/credit_card_field.cc b/components/autofill/core/browser/credit_card_field.cc index 9edfef2..3b24ec3 100644 --- a/components/autofill/core/browser/credit_card_field.cc +++ b/components/autofill/core/browser/credit_card_field.cc
@@ -11,6 +11,7 @@ #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_regex_constants.h" @@ -30,7 +31,7 @@ // Look for the vector |regex_needles| in |haystack|. Returns true if a // consecutive section of |haystack| matches |regex_needles|. -bool FindConsecutiveStrings(const std::vector<std::string>& regex_needles, +bool FindConsecutiveStrings(const std::vector<base::string16>& regex_needles, const std::vector<base::string16>& haystack) { if (regex_needles.empty() || haystack.empty() || @@ -90,7 +91,9 @@ break; if (!credit_card_field->cardholder_) { - if (ParseField(scanner, kNameOnCardRe, &credit_card_field->cardholder_)) { + if (ParseField(scanner, + base::UTF8ToUTF16(kNameOnCardRe), + &credit_card_field->cardholder_)) { continue; } @@ -100,8 +103,10 @@ // fields. So we search for "name" only when we've already parsed at // least one other credit card field and haven't yet parsed the // expiration date (which usually appears at the end). - if (fields > 0 && !credit_card_field->expiration_month_ && - ParseField(scanner, kNameOnCardContextualRe, + if (fields > 0 && + !credit_card_field->expiration_month_ && + ParseField(scanner, + base::UTF8ToUTF16(kNameOnCardContextualRe), &credit_card_field->cardholder_)) { continue; } @@ -124,7 +129,7 @@ const int kMatchNumAndTel = MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE; if (!credit_card_field->verification_ && ParseFieldSpecifics(scanner, - kCardCvcRe, + base::UTF8ToUTF16(kCardCvcRe), kMatchNumAndTel | MATCH_PASSWORD, &credit_card_field->verification_)) { continue; @@ -132,7 +137,7 @@ AutofillField* current_number_field; if (ParseFieldSpecifics(scanner, - kCardNumberRe, + base::UTF8ToUTF16(kCardNumberRe), kMatchNumAndTel, ¤t_number_field)) { // Avoid autofilling any credit card number field having very low or high @@ -209,7 +214,8 @@ return false; // Filter out years. - const char kNumericalYearRe[] = "[1-9][0-9][0-9][0-9]"; + const base::string16 kNumericalYearRe = + base::ASCIIToUTF16("[1-9][0-9][0-9][0-9]"); for (const auto& value : field->option_values) { if (MatchesPattern(value, kNumericalYearRe)) return false; @@ -220,7 +226,7 @@ } // Look for numerical months. - const char kNumericalMonthRe[] = "12"; + const base::string16 kNumericalMonthRe = base::ASCIIToUTF16("12"); if (MatchesPattern(field->option_values.back(), kNumericalMonthRe) || MatchesPattern(field->option_contents.back(), kNumericalMonthRe)) { return true; @@ -246,11 +252,11 @@ time_now.UTCExplode(&time_exploded); const int kYearsToMatch = 3; - std::vector<std::string> years_to_check; + std::vector<base::string16> years_to_check; for (int year = time_exploded.year; year < time_exploded.year + kYearsToMatch; ++year) { - years_to_check.push_back(base::IntToString(year)); + years_to_check.push_back(base::IntToString16(year)); } return (FindConsecutiveStrings(years_to_check, field->option_values) || FindConsecutiveStrings(years_to_check, field->option_contents)); @@ -279,16 +285,16 @@ return false; size_t saved_cursor = scanner->SaveCursor(); - if (ParseField(scanner, kDebitCardRe, nullptr)) { + if (ParseField(scanner, base::UTF8ToUTF16(kDebitCardRe), nullptr)) { scanner->RewindTo(saved_cursor); return false; } - if (ParseField(scanner, kDebitGiftCardRe, nullptr)) { + if (ParseField(scanner, base::UTF8ToUTF16(kDebitGiftCardRe), nullptr)) { scanner->RewindTo(saved_cursor); return false; } - return ParseField(scanner, kGiftCardRe, nullptr); + return ParseField(scanner, base::UTF8ToUTF16(kGiftCardRe), nullptr); } CreditCardField::CreditCardField() @@ -370,11 +376,11 @@ scanner->RewindTo(month_year_saved_cursor); const int kMatchTelAndSelect = MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_SELECT; if (ParseFieldSpecifics(scanner, - kExpirationMonthRe, + base::UTF8ToUTF16(kExpirationMonthRe), kMatchTelAndSelect, &expiration_month_) && ParseFieldSpecifics(scanner, - kExpirationYearRe, + base::UTF8ToUTF16(kExpirationYearRe), kMatchTelAndSelect, &expiration_year_)) { return true; @@ -383,11 +389,11 @@ // If that fails, look for just MM/YY(YY). scanner->RewindTo(month_year_saved_cursor); if (ParseFieldSpecifics(scanner, - "^mm$", + base::ASCIIToUTF16("^mm$"), kMatchTelAndSelect, &expiration_month_) && ParseFieldSpecifics(scanner, - "^(yy|yyyy)$", + base::ASCIIToUTF16("^(yy|yyyy)$"), kMatchTelAndSelect, &expiration_year_)) { return true; @@ -406,7 +412,7 @@ // Try to look for a 2-digit year expiration date. if (ParseFieldSpecifics(scanner, - kExpirationDate2DigitYearRe, + base::UTF8ToUTF16(kExpirationDate2DigitYearRe), kMatchTelAndSelect, &expiration_date_)) { exp_year_type_ = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR; @@ -416,7 +422,7 @@ // Try to look for a generic expiration date field. (2 or 4 digit year) if (ParseFieldSpecifics(scanner, - kExpirationDateRe, + base::UTF8ToUTF16(kExpirationDateRe), kMatchTelAndSelect, &expiration_date_)) { // If such a field exists, but it cannot fit a 4-digit year expiration @@ -434,7 +440,7 @@ if (FieldCanFitDataForFieldType(current_field_max_length, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) && ParseFieldSpecifics(scanner, - kExpirationDate4DigitYearRe, + base::UTF8ToUTF16(kExpirationDate4DigitYearRe), kMatchTelAndSelect, &expiration_date_)) { expiration_month_ = nullptr;
diff --git a/components/autofill/core/browser/email_field.cc b/components/autofill/core/browser/email_field.cc index 98ed462..cfe4a1a 100644 --- a/components/autofill/core/browser/email_field.cc +++ b/components/autofill/core/browser/email_field.cc
@@ -4,6 +4,7 @@ #include "components/autofill/core/browser/email_field.h" +#include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_scanner.h" @@ -12,8 +13,8 @@ // static scoped_ptr<FormField> EmailField::Parse(AutofillScanner* scanner) { AutofillField* field; - if (ParseFieldSpecifics(scanner, kEmailRe, MATCH_DEFAULT | MATCH_EMAIL, - &field)) { + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kEmailRe), + MATCH_DEFAULT | MATCH_EMAIL, &field)) { return make_scoped_ptr(new EmailField(field)); }
diff --git a/components/autofill/core/browser/form_field.cc b/components/autofill/core/browser/form_field.cc index 69a3771..50d7e8a3 100644 --- a/components/autofill/core/browser/form_field.cc +++ b/components/autofill/core/browser/form_field.cc
@@ -12,6 +12,7 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/address_field.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_scanner.h" @@ -83,14 +84,14 @@ // static bool FormField::ParseField(AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, AutofillField** match) { return ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, match); } // static bool FormField::ParseFieldSpecifics(AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, int match_type, AutofillField** match) { if (scanner->IsEnd()) @@ -107,7 +108,7 @@ // static FormField::ParseNameLabelResult FormField::ParseNameAndLabelSeparately( AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, int match_type, AutofillField** match) { if (scanner->IsEnd()) @@ -142,7 +143,7 @@ bool FormField::ParseEmptyLabel(AutofillScanner* scanner, AutofillField** match) { return ParseFieldSpecifics(scanner, - "^$", + base::ASCIIToUTF16("^$"), MATCH_LABEL | MATCH_ALL_INPUTS, match); } @@ -160,7 +161,7 @@ // static. bool FormField::MatchAndAdvance(AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, int match_type, AutofillField** match) { AutofillField* field = scanner->Cursor(); @@ -176,7 +177,7 @@ // static bool FormField::Match(const AutofillField* field, - const std::string& pattern, + const base::string16& pattern, int match_type) { if ((match_type & FormField::MATCH_LABEL) && MatchesPattern(field->label, pattern)) {
diff --git a/components/autofill/core/browser/form_field.h b/components/autofill/core/browser/form_field.h index 1b1248e..c27dc08 100644 --- a/components/autofill/core/browser/form_field.h +++ b/components/autofill/core/browser/form_field.h
@@ -69,7 +69,7 @@ // Attempts to parse a form field with the given pattern. Returns true on // success and fills |match| with a pointer to the field. static bool ParseField(AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, AutofillField** match); // Parses the stream of fields in |scanner| with regular expression |pattern| @@ -78,7 +78,7 @@ // A |true| result is returned in the case of a successful match, false // otherwise. static bool ParseFieldSpecifics(AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, int match_type, AutofillField** match); @@ -89,7 +89,7 @@ // change. static ParseNameLabelResult ParseNameAndLabelSeparately( AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, int match_type, AutofillField** match); @@ -123,14 +123,14 @@ // Returns |true| if a match is found according to |match_type|, and |false| // otherwise. static bool MatchAndAdvance(AutofillScanner* scanner, - const std::string& pattern, + const base::string16& pattern, int match_type, AutofillField** match); // Matches the regular expression |pattern| against the components of |field| // as specified in the |match_type| bit field (see |MatchType|). static bool Match(const AutofillField* field, - const std::string& pattern, + const base::string16& pattern, int match_type); // Perform a "pass" over the |fields| where each pass uses the supplied
diff --git a/components/autofill/core/browser/form_field_unittest.cc b/components/autofill/core/browser/form_field_unittest.cc index aac94d0..f13c1d1 100644 --- a/components/autofill/core/browser/form_field_unittest.cc +++ b/components/autofill/core/browser/form_field_unittest.cc
@@ -2,14 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/autofill/core/browser/form_field.h" - -#include <string> - #include "base/memory/scoped_vector.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/form_field.h" #include "testing/gtest/include/gtest/gtest.h" using base::ASCIIToUTF16; @@ -20,83 +17,108 @@ AutofillField field; // Empty strings match. - EXPECT_TRUE(FormField::Match(&field, std::string(), FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, base::string16(), + FormField::MATCH_LABEL)); // Empty pattern matches non-empty string. field.label = ASCIIToUTF16("a"); - EXPECT_TRUE(FormField::Match(&field, std::string(), FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, base::string16(), + FormField::MATCH_LABEL)); // Strictly empty pattern matches empty string. field.label = base::string16(); - EXPECT_TRUE(FormField::Match(&field, "^$", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^$"), + FormField::MATCH_LABEL)); // Strictly empty pattern does not match non-empty string. field.label = ASCIIToUTF16("a"); - EXPECT_FALSE(FormField::Match(&field, "^$", FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^$"), + FormField::MATCH_LABEL)); // Non-empty pattern doesn't match empty string. field.label = base::string16(); - EXPECT_FALSE(FormField::Match(&field, "a", FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("a"), + FormField::MATCH_LABEL)); // Beginning of line. field.label = ASCIIToUTF16("head_tail"); - EXPECT_TRUE(FormField::Match(&field, "^head", FormField::MATCH_LABEL)); - EXPECT_FALSE(FormField::Match(&field, "^tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head"), + FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail"), + FormField::MATCH_LABEL)); // End of line. field.label = ASCIIToUTF16("head_tail"); - EXPECT_FALSE(FormField::Match(&field, "head$", FormField::MATCH_LABEL)); - EXPECT_TRUE(FormField::Match(&field, "tail$", FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head$"), + FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail$"), + FormField::MATCH_LABEL)); // Exact. field.label = ASCIIToUTF16("head_tail"); - EXPECT_FALSE(FormField::Match(&field, "^head$", FormField::MATCH_LABEL)); - EXPECT_FALSE(FormField::Match(&field, "^tail$", FormField::MATCH_LABEL)); - EXPECT_TRUE(FormField::Match(&field, "^head_tail$", FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^head$"), + FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail$"), + FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head_tail$"), + FormField::MATCH_LABEL)); // Escaped dots. field.label = ASCIIToUTF16("m.i."); // Note: This pattern is misleading as the "." characters are wild cards. - EXPECT_TRUE(FormField::Match(&field, "m.i.", FormField::MATCH_LABEL)); - EXPECT_TRUE(FormField::Match(&field, "m\\.i\\.", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."), + FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."), + FormField::MATCH_LABEL)); field.label = ASCIIToUTF16("mXiX"); - EXPECT_TRUE(FormField::Match(&field, "m.i.", FormField::MATCH_LABEL)); - EXPECT_FALSE(FormField::Match(&field, "m\\.i\\.", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."), + FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."), + FormField::MATCH_LABEL)); // Repetition. field.label = ASCIIToUTF16("headtail"); - EXPECT_TRUE(FormField::Match(&field, "head.*tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"), + FormField::MATCH_LABEL)); field.label = ASCIIToUTF16("headXtail"); - EXPECT_TRUE(FormField::Match(&field, "head.*tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"), + FormField::MATCH_LABEL)); field.label = ASCIIToUTF16("headXXXtail"); - EXPECT_TRUE(FormField::Match(&field, "head.*tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"), + FormField::MATCH_LABEL)); field.label = ASCIIToUTF16("headtail"); - EXPECT_FALSE(FormField::Match(&field, "head.+tail", FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head.+tail"), + FormField::MATCH_LABEL)); field.label = ASCIIToUTF16("headXtail"); - EXPECT_TRUE(FormField::Match(&field, "head.+tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.+tail"), + FormField::MATCH_LABEL)); field.label = ASCIIToUTF16("headXXXtail"); - EXPECT_TRUE(FormField::Match(&field, "head.+tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.+tail"), + FormField::MATCH_LABEL)); // Alternation. field.label = ASCIIToUTF16("head_tail"); - EXPECT_TRUE(FormField::Match(&field, "head|other", FormField::MATCH_LABEL)); - EXPECT_TRUE(FormField::Match(&field, "tail|other", FormField::MATCH_LABEL)); - EXPECT_FALSE(FormField::Match(&field, "bad|good", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head|other"), + FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail|other"), + FormField::MATCH_LABEL)); + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("bad|good"), + FormField::MATCH_LABEL)); // Case sensitivity. field.label = ASCIIToUTF16("xxxHeAd_tAiLxxx"); - EXPECT_TRUE(FormField::Match(&field, "head_tail", FormField::MATCH_LABEL)); + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head_tail"), + FormField::MATCH_LABEL)); // Word boundaries. - const std::string kWordBoundary = "(\\A|\\z|\\PL)"; field.label = ASCIIToUTF16("contains word:"); - EXPECT_TRUE(FormField::Match(&field, kWordBoundary + "word" + kWordBoundary, + EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("\\bword\\b"), FormField::MATCH_LABEL)); - EXPECT_FALSE(FormField::Match(&field, kWordBoundary + "con" + kWordBoundary, + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcon\\b"), FormField::MATCH_LABEL)); // Make sure the circumflex in 'crepe' is not treated as a word boundary. field.label = base::UTF8ToUTF16("cr" "\xC3\xAA" "pe"); - EXPECT_FALSE(FormField::Match(&field, kWordBoundary + "cr" + kWordBoundary, + EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcr\\b"), FormField::MATCH_LABEL)); }
diff --git a/components/autofill/core/browser/name_field.cc b/components/autofill/core/browser/name_field.cc index e1e09346..8532de1 100644 --- a/components/autofill/core/browser/name_field.cc +++ b/components/autofill/core/browser/name_field.cc
@@ -6,10 +6,13 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_scanner.h" #include "components/autofill/core/browser/autofill_type.h" +using base::UTF8ToUTF16; + namespace autofill { namespace { @@ -77,7 +80,7 @@ scoped_ptr<FullNameField> FullNameField::Parse(AutofillScanner* scanner) { // Exclude e.g. "username" or "nickname" fields. scanner->SaveCursor(); - bool should_ignore = ParseField(scanner, kNameIgnoredRe, NULL); + bool should_ignore = ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), NULL); scanner->Rewind(); if (should_ignore) return NULL; @@ -86,7 +89,7 @@ // for example, Travelocity_Edit travel profile.html contains a field // "Travel Profile Name". AutofillField* field = NULL; - if (ParseField(scanner, kNameRe, &field)) + if (ParseField(scanner, UTF8ToUTF16(kNameRe), &field)) return make_scoped_ptr(new FullNameField(field)); return NULL; @@ -107,7 +110,7 @@ scanner->SaveCursor(); AutofillField* next = NULL; - if (ParseField(scanner, kNameSpecificRe, &v->first_name_) && + if (ParseField(scanner, UTF8ToUTF16(kNameSpecificRe), &v->first_name_) && ParseEmptyLabel(scanner, &next)) { if (ParseEmptyLabel(scanner, &v->last_name_)) { // There are three name fields; assume that the middle one is a @@ -144,12 +147,13 @@ // Allow name fields to appear in any order. while (!scanner->IsEnd()) { // Skip over any unrelated fields, e.g. "username" or "nickname". - if (ParseFieldSpecifics(scanner, kNameIgnoredRe, + if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kNameIgnoredRe), MATCH_DEFAULT | MATCH_SELECT, NULL)) { continue; } - if (!v->first_name_ && ParseField(scanner, kFirstNameRe, &v->first_name_)) { + if (!v->first_name_ && + ParseField(scanner, UTF8ToUTF16(kFirstNameRe), &v->first_name_)) { continue; } @@ -159,17 +163,18 @@ // "txtmiddlename"); such a field probably actually represents a // middle initial. if (!v->middle_name_ && - ParseField(scanner, kMiddleInitialRe, &v->middle_name_)) { + ParseField(scanner, UTF8ToUTF16(kMiddleInitialRe), &v->middle_name_)) { v->middle_initial_ = true; continue; } if (!v->middle_name_ && - ParseField(scanner, kMiddleNameRe, &v->middle_name_)) { + ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), &v->middle_name_)) { continue; } - if (!v->last_name_ && ParseField(scanner, kLastNameRe, &v->last_name_)) { + if (!v->last_name_ && + ParseField(scanner, UTF8ToUTF16(kLastNameRe), &v->last_name_)) { continue; }
diff --git a/components/autofill/core/browser/phone_field.cc b/components/autofill/core/browser/phone_field.cc index 74f93a4..77a8d9f0 100644 --- a/components/autofill/core/browser/phone_field.cc +++ b/components/autofill/core/browser/phone_field.cc
@@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_scanner.h" @@ -275,8 +276,10 @@ bool PhoneField::ParsePhoneField(AutofillScanner* scanner, const std::string& regex, AutofillField** field) { - return ParseFieldSpecifics( - scanner, regex, MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_NUMBER, field); + return ParseFieldSpecifics(scanner, + base::UTF8ToUTF16(regex), + MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_NUMBER, + field); } } // namespace autofill
diff --git a/components/autofill/core/browser/validation.cc b/components/autofill/core/browser/validation.cc index f2f1a88..b4c95674 100644 --- a/components/autofill/core/browser/validation.cc +++ b/components/autofill/core/browser/validation.cc
@@ -127,9 +127,9 @@ bool IsValidEmailAddress(const base::string16& text) { // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state) - const char kEmailPattern[] = + const base::string16 kEmailPattern = base::ASCIIToUTF16( "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@" - "[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"; + "[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"); return MatchesPattern(text, kEmailPattern); } @@ -139,7 +139,7 @@ } bool IsValidZip(const base::string16& text) { - const char kZipPattern[] = "^\\d{5}(-\\d{4})?$"; + const base::string16 kZipPattern = base::ASCIIToUTF16("^\\d{5}(-\\d{4})?$"); return MatchesPattern(text, kZipPattern); }
diff --git a/components/autofill/core/common/BUILD.gn b/components/autofill/core/common/BUILD.gn index 6678086..05ef7bd 100644 --- a/components/autofill/core/common/BUILD.gn +++ b/components/autofill/core/common/BUILD.gn
@@ -39,7 +39,6 @@ deps = [ "//base", "//base:i18n", - "//third_party/re2", "//ui/base", "//ui/gfx", "//url",
diff --git a/components/autofill/core/common/DEPS b/components/autofill/core/common/DEPS deleted file mode 100644 index 0de07bb..0000000 --- a/components/autofill/core/common/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+third_party/re2", -]
diff --git a/components/autofill/core/common/autofill_l10n_util.cc b/components/autofill/core/common/autofill_l10n_util.cc index c29b0a2..8d2ff801 100644 --- a/components/autofill/core/common/autofill_l10n_util.cc +++ b/components/autofill/core/common/autofill_l10n_util.cc
@@ -11,34 +11,40 @@ namespace autofill { namespace l10n { -CaseInsensitiveCompare::CaseInsensitiveCompare() - : CaseInsensitiveCompare(icu::Locale::getDefault()) {} - -CaseInsensitiveCompare::CaseInsensitiveCompare(const icu::Locale& locale) { - UErrorCode error = U_ZERO_ERROR; - collator_.reset(icu::Collator::createInstance(locale, error)); - if (!collator_) { +icu::Collator* GetCollatorForLocale(const icu::Locale& locale) { + UErrorCode ignored = U_ZERO_ERROR; + icu::Collator* collator(icu::Collator::createInstance(locale, ignored)); + if (!collator) { // On some systems, the default locale is invalid to the eyes of the ICU // library. This could be due to a device-specific issue (has been seen in - // the wild on Android devices). In the failure case, |collator_| will be - // null. See http://crbug.com/558625. + // the wild on Android and iOS devices). In the failure case, |collator| + // will be null. See http://crbug.com/558625. icu::UnicodeString name; std::string locale_name; locale.getDisplayName(name).toUTF8String(locale_name); - LOG(ERROR) << "Failed to initialize the ICU Collator for " - << "CaseInsensitiveCompare with locale" << locale_name; + LOG(ERROR) << "Failed to initialize the ICU Collator with locale " + << locale_name; + // Attempt to load the English locale. - collator_.reset(icu::Collator::createInstance(icu::Locale::getEnglish(), - error)); + collator = + icu::Collator::createInstance(icu::Locale::getEnglish(), ignored); + if (!collator) { + LOG(ERROR) << "Failed to initialize the ICU Collator with the English " + << "locale."; + } } - if (collator_) { + UMA_HISTOGRAM_BOOLEAN("Autofill.IcuCollatorCreationSuccess", !!collator); + return collator; +} + +CaseInsensitiveCompare::CaseInsensitiveCompare() + : CaseInsensitiveCompare(icu::Locale::getDefault()) {} + +CaseInsensitiveCompare::CaseInsensitiveCompare(const icu::Locale& locale) + : collator_(GetCollatorForLocale(locale)) { + if (collator_) collator_->setStrength(icu::Collator::PRIMARY); - } else { - LOG(ERROR) << "Failed to initialize the ICU Collator for " - << "CaseInsensitiveCompare with the English locale."; - } - UMA_HISTOGRAM_BOOLEAN("Autofill.IcuCollatorCreationSuccess", !!collator_); } CaseInsensitiveCompare::~CaseInsensitiveCompare() {
diff --git a/components/autofill/core/common/autofill_l10n_util.h b/components/autofill/core/common/autofill_l10n_util.h index 644c4e1e..9cc2ec9 100644 --- a/components/autofill/core/common/autofill_l10n_util.h +++ b/components/autofill/core/common/autofill_l10n_util.h
@@ -10,6 +10,10 @@ namespace autofill { namespace l10n { +// Obtains the ICU Collator for this locale. If unsuccessful, attempts to return +// the ICU collator for the English locale. If unsuccessful, returns null. +icu::Collator* GetCollatorForLocale(const icu::Locale& locale); + // Assists with locale-aware case insensitive string comparisons. class CaseInsensitiveCompare { public:
diff --git a/components/autofill/core/common/autofill_regexes.cc b/components/autofill/core/common/autofill_regexes.cc index 7f914b931..cdea63c 100644 --- a/components/autofill/core/common/autofill_regexes.cc +++ b/components/autofill/core/common/autofill_regexes.cc
@@ -8,8 +8,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/re2/re2/re2.h" +#include "base/strings/string16.h" +#include "third_party/icu/source/i18n/unicode/regex.h" namespace { @@ -19,7 +19,7 @@ static AutofillRegexes* GetInstance(); // Returns the compiled regex matcher corresponding to |pattern|. - re2::RE2* GetMatcher(const std::string& pattern); + icu::RegexMatcher* GetMatcher(const base::string16& pattern); private: AutofillRegexes(); @@ -27,7 +27,8 @@ friend struct base::DefaultSingletonTraits<AutofillRegexes>; // Maps patterns to their corresponding regex matchers. - base::ScopedPtrHashMap<std::string, scoped_ptr<re2::RE2>> matchers_; + base::ScopedPtrHashMap<base::string16, scoped_ptr<icu::RegexMatcher>> + matchers_; DISALLOW_COPY_AND_ASSIGN(AutofillRegexes); }; @@ -43,13 +44,16 @@ AutofillRegexes::~AutofillRegexes() { } -re2::RE2* AutofillRegexes::GetMatcher(const std::string& pattern) { +icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) { auto it = matchers_.find(pattern); if (it == matchers_.end()) { - re2::RE2::Options options; - options.set_case_sensitive(false); - scoped_ptr<re2::RE2> matcher(new re2::RE2(pattern, options)); - DCHECK(matcher->ok()); + const icu::UnicodeString icu_pattern(pattern.data(), pattern.length()); + + UErrorCode status = U_ZERO_ERROR; + scoped_ptr<icu::RegexMatcher> matcher( + new icu::RegexMatcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status)); + DCHECK(U_SUCCESS(status)); + auto result = matchers_.add(pattern, matcher.Pass()); DCHECK(result.second); it = result.first; @@ -61,12 +65,17 @@ namespace autofill { -bool MatchesPattern(const base::string16& input, const std::string& pattern) { - // TODO(isherman): Run performance tests to determine whether caching regex - // matchers is still useful now that we've switched from ICU to RE2. - // http://crbug.com/470065 - re2::RE2* matcher = AutofillRegexes::GetInstance()->GetMatcher(pattern); - return re2::RE2::PartialMatch(base::UTF16ToUTF8(input), *matcher); +bool MatchesPattern(const base::string16& input, + const base::string16& pattern) { + icu::RegexMatcher* matcher = + AutofillRegexes::GetInstance()->GetMatcher(pattern); + icu::UnicodeString icu_input(input.data(), input.length()); + matcher->reset(icu_input); + + UErrorCode status = U_ZERO_ERROR; + UBool match = matcher->find(0, status); + DCHECK(U_SUCCESS(status)); + return match == TRUE; } } // namespace autofill
diff --git a/components/autofill/core/common/autofill_regexes.h b/components/autofill/core/common/autofill_regexes.h index e5024ce9f..1bf1aa7 100644 --- a/components/autofill/core/common/autofill_regexes.h +++ b/components/autofill/core/common/autofill_regexes.h
@@ -5,8 +5,6 @@ #ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEXES_H_ #define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEXES_H_ -#include <string> - #include "base/strings/string16.h" // Parsing utilities. @@ -15,7 +13,7 @@ // Case-insensitive regular expression matching. // Returns true if |pattern| is found in |input|. bool MatchesPattern(const base::string16& input, - const std::string& pattern); + const base::string16& pattern); } // namespace autofill
diff --git a/components/autofill/core/common/autofill_regexes_unittest.cc b/components/autofill/core/common/autofill_regexes_unittest.cc index a4a3ebe..05895c7 100644 --- a/components/autofill/core/common/autofill_regexes_unittest.cc +++ b/components/autofill/core/common/autofill_regexes_unittest.cc
@@ -38,8 +38,8 @@ const TestCase& test_case = kPositiveCases[i]; SCOPED_TRACE(test_case.input); SCOPED_TRACE(test_case.pattern); - EXPECT_TRUE( - MatchesPattern(ASCIIToUTF16(test_case.input), test_case.pattern)); + EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), + ASCIIToUTF16(test_case.pattern))); } const TestCase kNegativeCases[] = { @@ -58,8 +58,8 @@ const TestCase& test_case = kNegativeCases[i]; SCOPED_TRACE(test_case.input); SCOPED_TRACE(test_case.pattern); - EXPECT_FALSE( - MatchesPattern(ASCIIToUTF16(test_case.input), test_case.pattern)); + EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), + ASCIIToUTF16(test_case.pattern))); } }
diff --git a/components/domain_reliability/context_unittest.cc b/components/domain_reliability/context_unittest.cc index 5b42d21..fd2ab1cb 100644 --- a/components/domain_reliability/context_unittest.cc +++ b/components/domain_reliability/context_unittest.cc
@@ -8,6 +8,7 @@ #include <string> #include "base/bind.h" +#include "base/json/json_reader.h" #include "base/memory/scoped_ptr.h" #include "components/domain_reliability/beacon.h" #include "components/domain_reliability/dispatcher.h" @@ -21,6 +22,10 @@ namespace domain_reliability { namespace { +using base::DictionaryValue; +using base::ListValue; +using base::Value; + typedef std::vector<const DomainReliabilityBeacon*> BeaconVector; scoped_ptr<DomainReliabilityBeacon> MakeBeacon(MockableTime* time) { @@ -39,6 +44,38 @@ return beacon.Pass(); } +template <typename ValueType, + bool (DictionaryValue::* GetValueType)(const std::string&, + ValueType*) const> +struct HasValue { + bool operator()(const DictionaryValue& dict, + const std::string& key, + ValueType expected_value) { + ValueType actual_value; + bool got_value = (dict.*GetValueType)(key, &actual_value); + EXPECT_TRUE(got_value); + if (got_value) + EXPECT_EQ(expected_value, actual_value); + return got_value && (expected_value == actual_value); + } +}; + +HasValue<std::string, &DictionaryValue::GetString> HasStringValue; +HasValue<int, &DictionaryValue::GetInteger> HasIntegerValue; +HasValue<bool, &DictionaryValue::GetBoolean> HasBooleanValue; + +bool GetEntryFromReport(const Value* report, + size_t index, + const DictionaryValue** entry_out) { + const DictionaryValue* report_dict; + const ListValue* entries; + + return report && + report->GetAsDictionary(&report_dict) && + report_dict->GetList("entries", &entries) && + entries->GetDictionary(index, entry_out); +} + class DomainReliabilityContextTest : public testing::Test { protected: DomainReliabilityContextTest() @@ -208,23 +245,27 @@ context_.GetQueuedBeaconsForTesting(&beacons); EXPECT_EQ(1u, beacons.size()); - // N.B.: Assumes max_delay is 5 minutes. - const char* kExpectedReport = "{" - "\"entries\":[" - "{\"failure_data\":{\"custom_error\":\"net::ERR_CONNECTION_RESET\"}," - "\"network_changed\":false,\"protocol\":\"HTTP\"," - "\"quic_broken\":true,\"request_age_ms\":300250," - "\"request_elapsed_ms\":250," - "\"server_ip\":\"127.0.0.1\",\"status\":\"tcp.connection_reset\"," - "\"url\":\"https://localhost/\"," - "\"was_proxied\":false}],\"reporter\":\"test-reporter\"}"; - time_.Advance(max_delay()); EXPECT_TRUE(upload_pending()); - EXPECT_EQ(kExpectedReport, upload_report()); EXPECT_EQ(0, upload_max_depth()); EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + scoped_ptr<Value> value = base::JSONReader::Read(upload_report()); + const DictionaryValue* entry; + ASSERT_TRUE(GetEntryFromReport(value.get(), 0, &entry)); + EXPECT_TRUE(HasStringValue(*entry, "failure_data.custom_error", + "net::ERR_CONNECTION_RESET")); + EXPECT_TRUE(HasBooleanValue(*entry, "network_changed", false)); + EXPECT_TRUE(HasStringValue(*entry, "protocol", "HTTP")); + EXPECT_TRUE(HasBooleanValue(*entry, "quic_broken", true)); + // N.B.: Assumes max_delay is 5 minutes. + EXPECT_TRUE(HasIntegerValue(*entry, "request_age_ms", 300250)); + EXPECT_TRUE(HasIntegerValue(*entry, "request_elapsed_ms", 250)); + EXPECT_TRUE(HasStringValue(*entry, "server_ip", "127.0.0.1")); + EXPECT_TRUE(HasStringValue(*entry, "status", "tcp.connection_reset")); + EXPECT_TRUE(HasStringValue(*entry, "url", "https://localhost/")); + EXPECT_TRUE(HasBooleanValue(*entry, "was_proxied", false)); + DomainReliabilityUploader::UploadResult result; result.status = DomainReliabilityUploader::UploadResult::SUCCESS; CallUploadCallback(result); @@ -232,32 +273,25 @@ EXPECT_TRUE(CheckNoBeacons()); } -TEST_F(DomainReliabilityContextTest, Upload_NetworkChanged) { +TEST_F(DomainReliabilityContextTest, NetworkChanged) { context_.OnBeacon(MakeBeacon(&time_)); BeaconVector beacons; context_.GetQueuedBeaconsForTesting(&beacons); EXPECT_EQ(1u, beacons.size()); - // N.B.: Assumes max_delay is 5 minutes. - const char* kExpectedReport = "{" - "\"entries\":[" - "{\"failure_data\":{\"custom_error\":\"net::ERR_CONNECTION_RESET\"}," - "\"network_changed\":true,\"protocol\":\"HTTP\"," - "\"quic_broken\":true,\"request_age_ms\":300250," - "\"request_elapsed_ms\":250," - "\"server_ip\":\"127.0.0.1\",\"status\":\"tcp.connection_reset\"," - "\"url\":\"https://localhost/\"," - "\"was_proxied\":false}],\"reporter\":\"test-reporter\"}"; - // Simulate a network change after the request but before the upload. last_network_change_time_ = time_.NowTicks(); time_.Advance(max_delay()); EXPECT_TRUE(upload_pending()); - EXPECT_EQ(kExpectedReport, upload_report()); EXPECT_EQ(0, upload_max_depth()); EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); + scoped_ptr<Value> value = base::JSONReader::Read(upload_report()); + const DictionaryValue* entry; + ASSERT_TRUE(GetEntryFromReport(value.get(), 0, &entry)); + EXPECT_TRUE(HasBooleanValue(*entry, "network_changed", true)); + DomainReliabilityUploader::UploadResult result; result.status = DomainReliabilityUploader::UploadResult::SUCCESS; CallUploadCallback(result);
diff --git a/components/metrics/histogram_encoder_unittest.cc b/components/metrics/histogram_encoder_unittest.cc index fb1d47d2..2d50acd 100644 --- a/components/metrics/histogram_encoder_unittest.cc +++ b/components/metrics/histogram_encoder_unittest.cc
@@ -25,7 +25,7 @@ ranges.set_range(6, 11); ranges.set_range(7, 12); - base::SampleVector samples(&ranges); + base::SampleVector samples(1, &ranges); samples.Accumulate(3, 1); // Bucket 1-5. samples.Accumulate(6, 1); // Bucket 5-7. samples.Accumulate(8, 1); // Bucket 8-9. (7-8 skipped)
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc index 10663b3..bbdbbe5 100644 --- a/components/metrics/metrics_log_unittest.cc +++ b/components/metrics/metrics_log_unittest.cc
@@ -192,7 +192,7 @@ ranges.set_range(6, 11); ranges.set_range(7, 12); - base::SampleVector samples(&ranges); + base::SampleVector samples(1, &ranges); samples.Accumulate(3, 1); // Bucket 1-5. samples.Accumulate(6, 1); // Bucket 5-7. samples.Accumulate(8, 1); // Bucket 8-9. (7-8 skipped)
diff --git a/components/nacl/renderer/plugin/service_runtime.cc b/components/nacl/renderer/plugin/service_runtime.cc index 016c6bf..9c4e2b4 100644 --- a/components/nacl/renderer/plugin/service_runtime.cc +++ b/components/nacl/renderer/plugin/service_runtime.cc
@@ -4,8 +4,6 @@ * found in the LICENSE file. */ -#define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" - #include "components/nacl/renderer/plugin/service_runtime.h" #include <string.h> @@ -13,6 +11,7 @@ #include <utility> #include "base/compiler_specific.h" +#include "base/logging.h" #include "components/nacl/renderer/plugin/plugin.h" #include "components/nacl/renderer/plugin/plugin_error.h" #include "components/nacl/renderer/plugin/sel_ldr_launcher_chrome.h" @@ -23,7 +22,6 @@ #include "native_client/src/include/portability_io.h" #include "native_client/src/include/portability_string.h" #include "native_client/src/public/imc_types.h" -#include "native_client/src/shared/platform/nacl_log.h" #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" #include "native_client/src/trusted/service_runtime/nacl_error_code.h" #include "ppapi/c/pp_errors.h" @@ -44,9 +42,6 @@ } bool ServiceRuntime::SetupCommandChannel() { - NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", - static_cast<void*>(this), - static_cast<void*>(subprocess_.get())); // Set up the bootstrap channel in our subprocess so that we can establish // SRPC. subprocess_->set_channel(bootstrap_channel_); @@ -75,12 +70,10 @@ void ServiceRuntime::StartSelLdr(const SelLdrStartParams& params, pp::CompletionCallback callback) { - NaClLog(4, "ServiceRuntime::Start\n"); - nacl::scoped_ptr<SelLdrLauncherChrome> tmp_subprocess(new SelLdrLauncherChrome()); if (NULL == tmp_subprocess.get()) { - NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n"); + LOG(ERROR) << "ServiceRuntime::Start (subprocess create failed)"; ErrorInfo error_info; error_info.SetReport( PP_NACL_ERROR_SEL_LDR_CREATE_LAUNCHER, @@ -103,10 +96,7 @@ } void ServiceRuntime::StartNexe() { - bool ok = SetupCommandChannel(); - if (ok) { - NaClLog(4, "ServiceRuntime::StartNexe (success)\n"); - } + SetupCommandChannel(); } void ServiceRuntime::ReportLoadError(const ErrorInfo& error_info) { @@ -116,18 +106,12 @@ } SrpcClient* ServiceRuntime::SetupAppChannel() { - NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", - reinterpret_cast<void*>(subprocess_.get())); nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); if (NULL == connect_desc) { - NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); + LOG(ERROR) << "ServiceRuntime::SetupAppChannel (connect failed)"; return NULL; } else { - NaClLog(4, "ServiceRuntime::SetupAppChannel (connect_desc=%p)\n", - static_cast<void*>(connect_desc)); SrpcClient* srpc_client = SrpcClient::New(connect_desc); - NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", - static_cast<void*>(srpc_client)); delete connect_desc; return srpc_client; } @@ -146,8 +130,6 @@ } ServiceRuntime::~ServiceRuntime() { - NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n", - static_cast<void*>(this)); // We do this just in case Shutdown() was not called. subprocess_.reset(NULL); }
diff --git a/components/page_load_metrics/browser/page_load_metrics_util.cc b/components/page_load_metrics/browser/page_load_metrics_util.cc index 9b641e5..19436f33 100644 --- a/components/page_load_metrics/browser/page_load_metrics_util.cc +++ b/components/page_load_metrics/browser/page_load_metrics_util.cc
@@ -6,6 +6,7 @@ #include <algorithm> +#include "components/page_load_metrics/browser/page_load_metrics_observer.h" #include "components/page_load_metrics/common/page_load_timing.h" namespace page_load_metrics { @@ -18,5 +19,11 @@ return std::min(timing.first_text_paint, timing.first_image_paint); } -} // namespace page_load_metrics +bool EventOccurredInForeground(const base::TimeDelta& event, + const PageLoadExtraInfo& info) { + return info.started_in_foreground && !event.is_zero() && + (info.first_background_time.is_zero() || + event < info.first_background_time); +} +} // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_util.h b/components/page_load_metrics/browser/page_load_metrics_util.h index b5a56986..0865bfa 100644 --- a/components/page_load_metrics/browser/page_load_metrics_util.h +++ b/components/page_load_metrics/browser/page_load_metrics_util.h
@@ -15,6 +15,7 @@ namespace page_load_metrics { +struct PageLoadExtraInfo; struct PageLoadTiming; // Get the time of the first 'contentful' paint. A contentful paint is a paint @@ -22,6 +23,14 @@ // Painting of a background color is not considered 'contentful'. base::TimeDelta GetFirstContentfulPaint(const PageLoadTiming& timing); +// Returns false for events for which we have no timing information, and events +// that happened on a page that had been in the background. When a page is +// backgrounded, some events (e.g. paint) are delayed. Since these data points +// can skew the mean, they should not be mixed with timing events that occurred +// in the foreground. +bool EventOccurredInForeground(const base::TimeDelta& event, + const PageLoadExtraInfo& info); + } // namespace page_load_metrics #endif // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_UTIL_H_
diff --git a/components/startup_metric_utils.gypi b/components/startup_metric_utils.gypi index e24c83d..452de896 100644 --- a/components/startup_metric_utils.gypi +++ b/components/startup_metric_utils.gypi
@@ -14,6 +14,7 @@ 'type': 'static_library', 'dependencies': [ '../base/base.gyp:base', + 'components.gyp:variations', ], 'include_dirs': [ '..',
diff --git a/components/startup_metric_utils/browser/BUILD.gn b/components/startup_metric_utils/browser/BUILD.gn index bbd027d2..1bd6c3b 100644 --- a/components/startup_metric_utils/browser/BUILD.gn +++ b/components/startup_metric_utils/browser/BUILD.gn
@@ -25,6 +25,7 @@ deps = [ "//base", + "//components/variations", ] }
diff --git a/components/startup_metric_utils/browser/DEPS b/components/startup_metric_utils/browser/DEPS index 1c35d9c..0dcfec3a 100644 --- a/components/startup_metric_utils/browser/DEPS +++ b/components/startup_metric_utils/browser/DEPS
@@ -1,3 +1,4 @@ include_rules = [ + "+components/variations", "+content/public/browser", ]
diff --git a/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.cc b/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.cc index dcf33d4..b343535 100644 --- a/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.cc +++ b/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.cc
@@ -10,6 +10,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/win/registry.h" +#include "components/variations/variations_associated_data.h" namespace startup_metric_utils { @@ -24,10 +25,12 @@ // annotated with the pre-read group that is actually used during this startup. const char kPreReadSyntheticFieldTrialName[] = "SyntheticPreRead"; -// Group names for the PreRead field trial. -const base::char16 kPreReadDisabledGroupName[] = L"Disabled"; -const base::char16 kPreReadEnabledHighPriorityGroupName[] = - L"EnabledHighPriority"; +// Variation names for the PreRead field trial. +const base::char16 kNoPreReadVariationName[] = L"NoPreRead"; +const base::char16 kHighPriorityVariationName[] = L"HighPriority"; +const base::char16 kOnlyIfColdVariationName[] = L"OnlyIfCold"; +const base::char16 kPrefetchVirtualMemoryVariationName[] = + L"PrefetchVirtualMemory"; // Registry key in which the PreRead field trial group is stored. const base::char16 kPreReadFieldTrialRegistryKey[] = L"\\PreReadFieldTrial"; @@ -38,43 +41,42 @@ return product_registry_path + kPreReadFieldTrialRegistryKey; } -// Returns the PreRead group stored in the registry, or an empty string if no -// group is stored in the registry. -base::string16 GetPreReadGroup(const base::string16& product_registry_path) { - const base::string16 registry_path = - GetPreReadRegistryPath(product_registry_path); - const base::win::RegKey registry_key(HKEY_CURRENT_USER, registry_path.c_str(), - KEY_QUERY_VALUE); - base::string16 group; - registry_key.ReadValue(L"", &group); - return group; -} - } // namespace void GetPreReadOptions(const base::string16& product_registry_path, - bool* should_pre_read, - bool* should_pre_read_high_priority) { + bool* no_pre_read, + bool* high_priority, + bool* only_if_cold, + bool* prefetch_virtual_memory) { DCHECK(!product_registry_path.empty()); - DCHECK(should_pre_read); - DCHECK(should_pre_read_high_priority); + DCHECK(no_pre_read); + DCHECK(high_priority); + DCHECK(only_if_cold); + DCHECK(prefetch_virtual_memory); - // Read the pre-read group from the registry. - const base::string16 group = GetPreReadGroup(product_registry_path); + // Open the PreRead field trial's registry key. + const base::string16 registry_path = + GetPreReadRegistryPath(product_registry_path); + const base::win::RegKey key(HKEY_CURRENT_USER, registry_path.c_str(), + KEY_QUERY_VALUE); - // Set pre-read options according to the group. - if (base::StartsWith(group, kPreReadDisabledGroupName, - base::CompareCase::SENSITIVE)) { - *should_pre_read = false; - *should_pre_read_high_priority = false; - } else if (base::StartsWith(group, kPreReadEnabledHighPriorityGroupName, - base::CompareCase::SENSITIVE)) { - *should_pre_read = true; - *should_pre_read_high_priority = true; - } else { - // Default behavior. - *should_pre_read = true; - *should_pre_read_high_priority = false; + // Set the PreRead field trial's options. + struct VariationMapping { + const base::char16* name; + bool* variable; + } const variations_mappings[] = { + {kNoPreReadVariationName, no_pre_read}, + {kHighPriorityVariationName, high_priority}, + {kOnlyIfColdVariationName, only_if_cold}, + {kPrefetchVirtualMemoryVariationName, prefetch_virtual_memory}, + }; + + for (const auto& mapping : variations_mappings) { + // Set the option variable to true if the corresponding value is found in + // the registry. Set to false otherwise (default behavior). + DWORD value = 0; + *mapping.variable = key.ReadValueDW(mapping.name, &value) == ERROR_SUCCESS; + DCHECK(!*mapping.variable || value == 1); } } @@ -83,18 +85,42 @@ const base::string16 registry_path = GetPreReadRegistryPath(product_registry_path); - const std::string group = - base::FieldTrialList::FindFullName(kPreReadFieldTrialName); - if (group.empty()) { - // Clear the registry key. That way, when the trial is shut down, the - // registry will be cleaned automatically. - base::win::RegKey(HKEY_CURRENT_USER, registry_path.c_str(), KEY_SET_VALUE) - .DeleteKey(L""); - } else { - // Write the group in the registry. - base::win::RegKey(HKEY_CURRENT_USER, registry_path.c_str(), KEY_SET_VALUE) - .WriteValue(L"", base::UTF8ToUTF16(group).c_str()); + + // Clear the experiment key. That way, when the trial is shut down, the + // registry will be cleaned automatically. Also, this prevents variation + // params from the previous group to stay in the registry when the group is + // updated. + base::win::RegKey(HKEY_CURRENT_USER, registry_path.c_str(), KEY_SET_VALUE) + .DeleteKey(L""); + + // Read variation params for the new group. + std::map<std::string, std::string> variation_params; + if (!variations::GetVariationParams(kPreReadFieldTrialName, + &variation_params)) + return; + + // Open the registry key. + base::win::RegKey key(HKEY_CURRENT_USER, registry_path.c_str(), + KEY_SET_VALUE); + + // Write variation params in the registry. + for (const auto& variation_param : variation_params) { + if (!variation_param.second.empty()) { + if (key.WriteValue(base::UTF8ToUTF16(variation_param.first).c_str(), 1) != + ERROR_SUCCESS) { + // Abort on failure to prevent the group name from being written in the + // registry if not all variation params have been written. No synthetic + // field trial is registered when there is no group name in the + // registry. + return; + } + } } + + // Write the new group name in the registry. + key.WriteValue(L"", base::UTF8ToUTF16(base::FieldTrialList::FindFullName( + kPreReadFieldTrialName)) + .c_str()); } void RegisterPreReadSyntheticFieldTrial( @@ -103,9 +129,16 @@ register_synthetic_field_trial) { DCHECK(!product_registry_path.empty()); - register_synthetic_field_trial.Run( - kPreReadSyntheticFieldTrialName, - base::UTF16ToUTF8(GetPreReadGroup(product_registry_path))); + const base::win::RegKey key( + HKEY_CURRENT_USER, GetPreReadRegistryPath(product_registry_path).c_str(), + KEY_QUERY_VALUE); + base::string16 group; + key.ReadValue(L"", &group); + + if (!group.empty()) { + register_synthetic_field_trial.Run(kPreReadSyntheticFieldTrialName, + base::UTF16ToUTF8(group)); + } } } // namespace startup_metric_utils
diff --git a/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.h b/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.h index 89999b3..556d7ab 100644 --- a/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.h +++ b/components/startup_metric_utils/browser/pre_read_field_trial_utils_win.h
@@ -20,13 +20,18 @@ const base::Callback<bool(const std::string&, const std::string&)>; // Get DLL pre-reading options. |product_registry_path| is the registry path -// under which the registry key for this field trial resides. The -// |should_pre_read| option is set if DLLs should be pre-read before being used. -// The |should_pre_read_high_priority| option is set if this pre-reading should -// be done with a high thread priority. +// under which the registry key for this field trial resides. The |no_pre_read| +// option is set if DLLs should not be pre-read. The |high_priority| option is +// set if pre-reading should be done with a high thread priority. The +// |only_if_cold| option is set if only cold DLLs should be pre-read. The +// |prefetch_virtual_memory| option is set if the +// ::PrefetchVirtualMemory function should be used to pre-read DLLs, if +// available. void GetPreReadOptions(const base::string16& product_registry_path, - bool* should_pre_read, - bool* should_pre_read_high_priority); + bool* no_pre_read, + bool* high_priority, + bool* only_if_cold, + bool* prefetch_virtual_memory); // Updates DLL pre-reading options in the registry with the latest info for the // next startup. |product_registry_path| is the registry path under which the
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.cc b/components/startup_metric_utils/browser/startup_metric_utils.cc index c916e50..76491ff 100644 --- a/components/startup_metric_utils/browser/startup_metric_utils.cc +++ b/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -91,76 +91,6 @@ // The signature of the NtQuerySystemInformation function. typedef NTSTATUS (WINAPI *NtQuerySystemInformationPtr)( SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); - -// Gets the hard fault count of the current process, returning it via -// |hard_fault_count|. Returns true on success, false otherwise. Also returns -// whether or not the system call was even possible for the current OS version -// via |has_os_support|. -bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count, - bool* has_os_support) { - DCHECK(hard_fault_count); - DCHECK(has_os_support); - - if (base::win::GetVersion() < base::win::VERSION_WIN7) { - *has_os_support = false; - return false; - } - // At this point the OS supports the required system call. - *has_os_support = true; - - // Get the function pointer. - NtQuerySystemInformationPtr query_sys_info = - reinterpret_cast<NtQuerySystemInformationPtr>( - ::GetProcAddress(GetModuleHandle(L"ntdll.dll"), - "NtQuerySystemInformation")); - if (query_sys_info == nullptr) - return false; - - // The output of this system call depends on the number of threads and - // processes on the entire system, and this can change between calls. Retry - // a small handful of times growing the buffer along the way. - // NOTE: The actual required size depends entirely on the number of processes - // and threads running on the system. The initial guess suffices for - // ~100s of processes and ~1000s of threads. - std::vector<uint8_t> buffer(32 * 1024); - for (size_t tries = 0; tries < 3; ++tries) { - ULONG return_length = 0; - NTSTATUS status = query_sys_info( - SystemProcessInformation, - buffer.data(), - static_cast<ULONG>(buffer.size()), - &return_length); - // Insufficient space in the buffer. - if (return_length > buffer.size()) { - buffer.resize(return_length); - continue; - } - if (NT_SUCCESS(status) && return_length <= buffer.size()) - break; - return false; - } - - // Look for the struct housing information for the current process. - DWORD proc_id = ::GetCurrentProcessId(); - size_t index = 0; - while (index < buffer.size()) { - DCHECK_LE(index + sizeof(SYSTEM_PROCESS_INFORMATION_EX), buffer.size()); - SYSTEM_PROCESS_INFORMATION_EX* proc_info = - reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index); - if (base::win::HandleToUint32(proc_info->UniqueProcessId) == proc_id) { - *hard_fault_count = proc_info->HardFaultCount; - return true; - } - // The list ends when NextEntryOffset is zero. This also prevents busy - // looping if the data is in fact invalid. - if (proc_info->NextEntryOffset <= 0) - return false; - index += proc_info->NextEntryOffset; - } - - return false; -} - #endif // defined(OS_WIN) @@ -210,20 +140,9 @@ void RecordHardFaultHistogram(bool is_first_run) { #if defined(OS_WIN) uint32_t hard_fault_count = 0; - bool has_os_support = false; - bool success = GetHardFaultCountForCurrentProcess( - &hard_fault_count, &has_os_support); - - // Log whether or not the system call was successful, assuming the OS was - // detected to support it. - if (has_os_support) { - UMA_HISTOGRAM_BOOLEAN( - "Startup.BrowserMessageLoopStartHardFaultCount.Success", - success); - } // Don't log a histogram value if unable to get the hard fault count. - if (!success) + if (!GetHardFaultCountForCurrentProcess(&hard_fault_count)) return; // Hard fault counts are expected to be in the thousands range, @@ -378,6 +297,64 @@ } // namespace +#if defined(OS_WIN) +bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count) { + DCHECK(hard_fault_count); + + if (base::win::GetVersion() < base::win::VERSION_WIN7) + return false; + + // Get the function pointer. + static const NtQuerySystemInformationPtr query_sys_info = + reinterpret_cast<NtQuerySystemInformationPtr>(::GetProcAddress( + GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation")); + if (query_sys_info == nullptr) + return false; + + // The output of this system call depends on the number of threads and + // processes on the entire system, and this can change between calls. Retry + // a small handful of times growing the buffer along the way. + // NOTE: The actual required size depends entirely on the number of processes + // and threads running on the system. The initial guess suffices for + // ~100s of processes and ~1000s of threads. + std::vector<uint8_t> buffer(32 * 1024); + for (size_t tries = 0; tries < 3; ++tries) { + ULONG return_length = 0; + NTSTATUS status = + query_sys_info(SystemProcessInformation, buffer.data(), + static_cast<ULONG>(buffer.size()), &return_length); + // Insufficient space in the buffer. + if (return_length > buffer.size()) { + buffer.resize(return_length); + continue; + } + if (NT_SUCCESS(status) && return_length <= buffer.size()) + break; + return false; + } + + // Look for the struct housing information for the current process. + DWORD proc_id = ::GetCurrentProcessId(); + size_t index = 0; + while (index < buffer.size()) { + DCHECK_LE(index + sizeof(SYSTEM_PROCESS_INFORMATION_EX), buffer.size()); + SYSTEM_PROCESS_INFORMATION_EX* proc_info = + reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index); + if (base::win::HandleToUint32(proc_info->UniqueProcessId) == proc_id) { + *hard_fault_count = proc_info->HardFaultCount; + return true; + } + // The list ends when NextEntryOffset is zero. This also prevents busy + // looping if the data is in fact invalid. + if (proc_info->NextEntryOffset <= 0) + return false; + index += proc_info->NextEntryOffset; + } + + return false; +} +#endif // defined(OS_WIN) + bool WasNonBrowserUIDisplayed() { return g_non_browser_ui_displayed; }
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.h b/components/startup_metric_utils/browser/startup_metric_utils.h index 6ebf082..51bae62 100644 --- a/components/startup_metric_utils/browser/startup_metric_utils.h +++ b/components/startup_metric_utils/browser/startup_metric_utils.h
@@ -5,9 +5,11 @@ #ifndef COMPONENTS_STARTUP_METRIC_UTILS_BROWSER_STARTUP_METRIC_UTILS_H_ #define COMPONENTS_STARTUP_METRIC_UTILS_BROWSER_STARTUP_METRIC_UTILS_H_ +#include <stdint.h> #include <string> #include "base/time/time.h" +#include "build/build_config.h" // Utility functions to support metric collection for browser startup. Timings // should use TimeTicks whenever possible. OS-provided timings are still @@ -34,6 +36,12 @@ STARTUP_TEMPERATURE_MAX }; +#if defined(OS_WIN) +// Gets the hard fault count of the current process through |hard_fault_count|. +// Returns true on success. +bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count); +#endif // defined(OS_WIN) + // Returns true if any UI other than the browser window has been displayed // so far. Useful to test if UI has been displayed before the first browser // window was shown, which would invalidate any surrounding timing metrics.
diff --git a/content/app/mojo/mojo_init.cc b/content/app/mojo/mojo_init.cc index 2aee5b4b..b289aa7 100644 --- a/content/app/mojo/mojo_init.cc +++ b/content/app/mojo/mojo_init.cc
@@ -24,13 +24,18 @@ MojoInitializer() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + if (0 && process_type.empty() && !command_line.HasSwitch("use-old-edk")) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + "use-new-edk"); + } + if (command_line.HasSwitch("use-new-edk")) { - bool initialize_as_parent = - command_line.GetSwitchValueASCII(switches::kProcessType).empty(); + bool initialize_as_parent = process_type.empty(); #if defined(MOJO_SHELL_CLIENT) - if (IsRunningInMojoShell()) { + if (IsRunningInMojoShell()) initialize_as_parent = false; - } #endif if (initialize_as_parent) { mojo::embedder::PreInitializeParentProcess();
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc index 33de9d6..0dd72a2 100644 --- a/content/browser/download/download_item_impl.cc +++ b/content/browser/download/download_item_impl.cc
@@ -504,9 +504,8 @@ if (state_ != INTERRUPTED_INTERNAL) return false; - // Downloads that don't have a WebContents should still be resumable, but this - // isn't currently the case. See ResumeInterruptedDownload(). - if (!GetWebContents()) + // We currently only support HTTP(S) requests for download resumption. + if (!GetURL().SchemeIsHTTPOrHTTPS()) return false; ResumeMode resume_mode = GetResumeMode(); @@ -1685,13 +1684,6 @@ if (state_ != INTERRUPTED_INTERNAL) return; - // If we can't get a web contents, we can't resume the download. - // TODO(rdsmith): Find some alternative web contents to use--this - // means we can't restart a download if it's a download imported - // from the history. - if (!GetWebContents()) - return; - // Reset the appropriate state if restarting. ResumeMode mode = GetResumeMode(); if (mode == RESUME_MODE_IMMEDIATE_RESTART || @@ -1702,9 +1694,17 @@ etag_ = ""; } - scoped_ptr<DownloadUrlParameters> download_params( - DownloadUrlParameters::FromWebContents(GetWebContents(), - GetOriginalUrl())); + scoped_ptr<DownloadUrlParameters> download_params; + if (GetWebContents()) { + download_params = DownloadUrlParameters::FromWebContents(GetWebContents(), + GetOriginalUrl()); + } else if (GetBrowserContext()) { + download_params = make_scoped_ptr( + new DownloadUrlParameters(GetOriginalUrl(), -1, -1, -1, + GetBrowserContext()->GetResourceContext())); + } else { + return; + } download_params->set_file_path(GetFullPath()); download_params->set_offset(GetReceivedBytes());
diff --git a/content/browser/download/download_item_impl_unittest.cc b/content/browser/download/download_item_impl_unittest.cc index 786d903..8dde06f 100644 --- a/content/browser/download/download_item_impl_unittest.cc +++ b/content/browser/download/download_item_impl_unittest.cc
@@ -60,7 +60,7 @@ MOCK_METHOD2(MockResumeInterruptedDownload, void(DownloadUrlParameters* params, uint32 id)); - MOCK_CONST_METHOD0(GetBrowserContext, BrowserContext*()); + BrowserContext* GetBrowserContext() const override { return nullptr; } MOCK_METHOD1(UpdatePersistence, void(DownloadItemImpl*)); MOCK_METHOD1(DownloadOpened, void(DownloadItemImpl*)); MOCK_METHOD1(DownloadRemoved, void(DownloadItemImpl*));
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index 95ce596..89c63df 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc
@@ -11,6 +11,7 @@ #include "base/debug/alias.h" #include "base/i18n/case_conversion.h" #include "base/logging.h" +#include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" @@ -28,7 +29,6 @@ #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_manager_delegate.h" @@ -49,8 +49,10 @@ namespace content { namespace { -void BeginDownload(scoped_ptr<DownloadUrlParameters> params, - uint32 download_id) { +scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread> BeginDownload( + scoped_ptr<DownloadUrlParameters> params, + uint32 download_id, + base::WeakPtr<DownloadManagerImpl> download_manager) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so @@ -121,19 +123,22 @@ save_info->prompt_for_save_location = params->prompt(); save_info->file = params->GetFile(); - ResourceDispatcherHost::Get()->BeginDownload( - request.Pass(), - params->referrer(), - params->content_initiated(), - params->resource_context(), - params->render_process_host_id(), - params->render_view_host_routing_id(), - params->render_frame_host_routing_id(), - params->prefer_cache(), - params->do_not_prompt_for_login(), - save_info.Pass(), - download_id, - params->callback()); + if (params->render_process_host_id() != -1) { + ResourceDispatcherHost::Get()->BeginDownload( + request.Pass(), params->referrer(), params->content_initiated(), + params->resource_context(), params->render_process_host_id(), + params->render_view_host_routing_id(), + params->render_frame_host_routing_id(), params->prefer_cache(), + params->do_not_prompt_for_login(), save_info.Pass(), download_id, + params->callback()); + return nullptr; + } + return scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread>( + UrlDownloader::BeginDownload( + download_manager, request.Pass(), params->referrer(), false, + params->prefer_cache(), true, save_info.Pass(), download_id, + params->callback()) + .release()); } class DownloadItemFactoryImpl : public DownloadItemFactory { @@ -513,10 +518,12 @@ scoped_ptr<content::DownloadUrlParameters> params, uint32 id) { RecordDownloadSource(INITIATED_BY_RESUMPTION); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&BeginDownload, base::Passed(¶ms), id)); + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::IO, FROM_HERE, + base::Bind(&BeginDownload, base::Passed(¶ms), id, + weak_factory_.GetWeakPtr()), + base::Bind(&DownloadManagerImpl::AddUrlDownloader, + weak_factory_.GetWeakPtr())); } void DownloadManagerImpl::SetDownloadItemFactoryForTesting( @@ -543,6 +550,22 @@ delete download; } +void DownloadManagerImpl::AddUrlDownloader( + scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread> downloader) { + if (downloader) + url_downloaders_.push_back(downloader.Pass()); +} + +void DownloadManagerImpl::RemoveUrlDownloader(UrlDownloader* downloader) { + for (auto ptr = url_downloaders_.begin(); ptr != url_downloaders_.end(); + ++ptr) { + if (ptr->get() == downloader) { + url_downloaders_.erase(ptr); + return; + } + } +} + namespace { bool RemoveDownloadBetween(base::Time remove_begin, @@ -613,9 +636,12 @@ DCHECK(params->prefer_cache()); DCHECK_EQ("POST", params->method()); } - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( - &BeginDownload, base::Passed(¶ms), - content::DownloadItem::kInvalidId)); + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::IO, FROM_HERE, + base::Bind(&BeginDownload, base::Passed(¶ms), + content::DownloadItem::kInvalidId, weak_factory_.GetWeakPtr()), + base::Bind(&DownloadManagerImpl::AddUrlDownloader, + weak_factory_.GetWeakPtr())); } void DownloadManagerImpl::AddObserver(Observer* observer) {
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h index 372fd58..df650fc 100644 --- a/content/browser/download/download_manager_impl.h +++ b/content/browser/download/download_manager_impl.h
@@ -8,9 +8,11 @@ #include <map> #include <set> #include <string> +#include <vector> #include "base/callback_forward.h" #include "base/containers/hash_tables.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -18,7 +20,9 @@ #include "base/sequenced_task_runner_helpers.h" #include "base/synchronization/lock.h" #include "content/browser/download/download_item_impl_delegate.h" +#include "content/browser/download/url_downloader.h" #include "content/common/content_export.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/download_manager_delegate.h" #include "content/public/browser/download_url_parameters.h" @@ -109,6 +113,8 @@ scoped_ptr<DownloadFileFactory> file_factory); virtual DownloadFileFactory* GetDownloadFileFactoryForTesting(); + void RemoveUrlDownloader(UrlDownloader* downloader); + private: typedef std::set<DownloadItem*> DownloadSet; typedef base::hash_map<uint32, DownloadItemImpl*> DownloadMap; @@ -168,6 +174,9 @@ void ShowDownloadInShell(DownloadItemImpl* download) override; void DownloadRemoved(DownloadItemImpl* download) override; + void AddUrlDownloader( + scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread> downloader); + // Factory for creation of downloads items. scoped_ptr<DownloadItemFactory> item_factory_; @@ -196,6 +205,9 @@ net::NetLog* net_log_; + std::vector<scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread>> + url_downloaders_; + base::WeakPtrFactory<DownloadManagerImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DownloadManagerImpl);
diff --git a/content/browser/download/download_request_core.cc b/content/browser/download/download_request_core.cc new file mode 100644 index 0000000..0de82b1 --- /dev/null +++ b/content/browser/download/download_request_core.cc
@@ -0,0 +1,438 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/download/download_request_core.h" + +#include <string> + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "base/metrics/sparse_histogram.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/stringprintf.h" +#include "base/thread_task_runner_handle.h" +#include "content/browser/byte_stream.h" +#include "content/browser/download/download_create_info.h" +#include "content/browser/download/download_interrupt_reasons_impl.h" +#include "content/browser/download/download_manager_impl.h" +#include "content/browser/download/download_stats.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/download_interrupt_reasons.h" +#include "content/public/browser/download_item.h" +#include "content/public/browser/download_manager_delegate.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/power_save_blocker.h" +#include "content/public/browser/web_contents.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_status_code.h" +#include "net/url_request/url_request_context.h" + +namespace content { + +namespace { + +void CallStartedCBOnUIThread( + const DownloadUrlParameters::OnStartedCallback& started_cb, + DownloadItem* item, + DownloadInterruptReason interrupt_reason) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (started_cb.is_null()) + return; + started_cb.Run(item, interrupt_reason); +} + +// Static function in order to prevent any accidental accesses to +// DownloadRequestCore members from the UI thread. +static void StartOnUIThread( + scoped_ptr<DownloadCreateInfo> info, + scoped_ptr<ByteStreamReader> stream, + base::WeakPtr<DownloadManager> download_manager, + const DownloadUrlParameters::OnStartedCallback& started_cb) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (!download_manager) { + // nullptr in unittests or if the page closed right after starting the + // download. + if (!started_cb.is_null()) + started_cb.Run(nullptr, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); + + // |stream| gets deleted on non-FILE thread, but it's ok since + // we're not using stream_writer_ yet. + + return; + } + + download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb); +} + +} // namespace + +const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; + +DownloadRequestCore::DownloadRequestCore( + uint32 id, + net::URLRequest* request, + const DownloadUrlParameters::OnStartedCallback& started_cb, + scoped_ptr<DownloadSaveInfo> save_info, + base::WeakPtr<DownloadManagerImpl> download_manager) + : request_(request), + download_id_(id), + started_cb_(started_cb), + save_info_(save_info.Pass()), + last_buffer_size_(0), + bytes_read_(0), + pause_count_(0), + was_deferred_(false), + on_response_started_called_(false), + download_manager_(download_manager) { + RecordDownloadCount(UNTHROTTLED_COUNT); + + power_save_blocker_ = PowerSaveBlocker::Create( + PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, + PowerSaveBlocker::kReasonOther, "Download in progress"); +} + +DownloadRequestCore::~DownloadRequestCore() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // This won't do anything if the callback was called before. + // If it goes through, it will likely be because OnWillStart() returned + // false somewhere in the chain of resource handlers. + CallStartedCB(nullptr, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); + + // Remove output stream callback if a stream exists. + if (stream_writer_) + stream_writer_->RegisterCallback(base::Closure()); + + UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", + base::TimeTicks::Now() - download_start_time_); +} + +// Send the download creation information to the download thread. +bool DownloadRequestCore::OnResponseStarted() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // There can be only one (call) + DCHECK(!on_response_started_called_); + on_response_started_called_ = true; + + DVLOG(20) << __FUNCTION__ << "()" << DebugString(); + download_start_time_ = base::TimeTicks::Now(); + + // If it's a download, we don't want to poison the cache with it. + request()->StopCaching(); + + // Lower priority as well, so downloads don't contend for resources + // with main frames. + request()->SetPriority(net::IDLE); + + // If the content-length header is not present (or contains something other + // than numbers), the incoming content_length is -1 (unknown size). + // Set the content length to 0 to indicate unknown size to DownloadManager. + int64 content_length = request()->GetExpectedContentSize() > 0 + ? request()->GetExpectedContentSize() + : 0; + + // Deleted in DownloadManager. + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( + base::Time::Now(), content_length, request()->net_log(), false, + ui::PAGE_TRANSITION_LINK, save_info_.Pass())); + + // Create the ByteStream for sending data to the download sink. + scoped_ptr<ByteStreamReader> stream_reader; + CreateByteStream( + base::ThreadTaskRunnerHandle::Get(), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + kDownloadByteStreamSize, &stream_writer_, &stream_reader); + stream_writer_->RegisterCallback( + base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); + + info->download_id = download_id_; + info->url_chain = request()->url_chain(); + info->referrer_url = GURL(request()->referrer()); + string mime_type; + request()->GetMimeType(&mime_type); + info->mime_type = mime_type; + info->remote_address = request()->GetSocketAddress().host(); + if (request()->response_headers()) { + // Grab the first content-disposition header. There may be more than one, + // though as of this writing, the network stack ensures if there are, they + // are all duplicates. + request()->response_headers()->EnumerateHeader( + nullptr, "content-disposition", &info->content_disposition); + } + RecordDownloadMimeType(info->mime_type); + RecordDownloadContentDisposition(info->content_disposition); + + // Get the last modified time and etag. + const net::HttpResponseHeaders* headers = request()->response_headers(); + if (headers) { + if (headers->HasStrongValidators()) { + // If we don't have strong validators as per RFC 2616 section 13.3.3, then + // we neither store nor use them for range requests. + if (!headers->EnumerateHeader(nullptr, "Last-Modified", + &info->last_modified)) + info->last_modified.clear(); + if (!headers->EnumerateHeader(nullptr, "ETag", &info->etag)) + info->etag.clear(); + } + + int status = headers->response_code(); + if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { + // Success & not range response; if we asked for a range, we didn't + // get it--reset the file pointers to reflect that. + info->save_info->offset = 0; + info->save_info->hash_state = ""; + } + + if (!headers->GetMimeType(&info->original_mime_type)) + info->original_mime_type.clear(); + } + + // Blink verifies that the requester of this download is allowed to set a + // suggested name for the security origin of the downlaod URL. However, this + // assumption doesn't hold if there were cross origin redirects. Therefore, + // clear the suggested_name for such requests. + if (info->url_chain.size() > 1 && + info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) + info->save_info->suggested_name.clear(); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&StartOnUIThread, base::Passed(&info), + base::Passed(&stream_reader), download_manager_, + // Pass to StartOnUIThread so that variable + // access is always on IO thread but function + // is called on UI thread. + started_cb_)); + // Guaranteed to be called in StartOnUIThread + started_cb_.Reset(); + + return true; +} + +void DownloadRequestCore::CallStartedCB( + DownloadItem* item, + DownloadInterruptReason interrupt_reason) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (started_cb_.is_null()) + return; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&CallStartedCBOnUIThread, started_cb_, + item, interrupt_reason)); + started_cb_.Reset(); +} + +// Create a new buffer, which will be handed to the download thread for file +// writing and deletion. +bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf, + int* buf_size, + int min_size) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(buf && buf_size); + DCHECK(!read_buffer_.get()); + + *buf_size = min_size < 0 ? kReadBufSize : min_size; + last_buffer_size_ = *buf_size; + read_buffer_ = new net::IOBuffer(*buf_size); + *buf = read_buffer_.get(); + return true; +} + +// Pass the buffer to the download file writer. +bool DownloadRequestCore::OnReadCompleted(int bytes_read, bool* defer) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(read_buffer_.get()); + + base::TimeTicks now(base::TimeTicks::Now()); + if (!last_read_time_.is_null()) { + double seconds_since_last_read = (now - last_read_time_).InSecondsF(); + if (now == last_read_time_) + // Use 1/10 ms as a "very small number" so that we avoid + // divide-by-zero error and still record a very high potential bandwidth. + seconds_since_last_read = 0.00001; + + double actual_bandwidth = (bytes_read) / seconds_since_last_read; + double potential_bandwidth = last_buffer_size_ / seconds_since_last_read; + RecordBandwidth(actual_bandwidth, potential_bandwidth); + } + last_read_time_ = now; + + if (!bytes_read) + return true; + bytes_read_ += bytes_read; + DCHECK(read_buffer_.get()); + + // Take the data ship it down the stream. If the stream is full, pause the + // request; the stream callback will resume it. + if (!stream_writer_->Write(read_buffer_, bytes_read)) { + PauseRequest(); + *defer = was_deferred_ = true; + last_stream_pause_time_ = now; + } + + read_buffer_ = NULL; // Drop our reference. + + if (pause_count_ > 0) + *defer = was_deferred_ = true; + + return true; +} + +void DownloadRequestCore::OnResponseCompleted( + const net::URLRequestStatus& status) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + int response_code = status.is_success() ? request()->GetResponseCode() : 0; + DVLOG(20) << __FUNCTION__ << "()" << DebugString() + << " status.status() = " << status.status() + << " status.error() = " << status.error() + << " response_code = " << response_code; + + net::Error error_code = net::OK; + if (status.status() == net::URLRequestStatus::FAILED || + // Note cancels as failures too. + status.status() == net::URLRequestStatus::CANCELED) { + error_code = static_cast<net::Error>(status.error()); // Normal case. + // Make sure that at least the fact of failure comes through. + if (error_code == net::OK) + error_code = net::ERR_FAILED; + } + + // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are + // allowed since a number of servers in the wild close the connection too + // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - + // treat downloads as complete in both cases, so we follow their lead. + if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || + error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { + error_code = net::OK; + } + DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( + error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); + + if (status.status() == net::URLRequestStatus::CANCELED && + status.error() == net::ERR_ABORTED) { + // CANCELED + ERR_ABORTED == something outside of the network + // stack cancelled the request. There aren't that many things that + // could do this to a download request (whose lifetime is separated from + // the tab from which it came). We map this to USER_CANCELLED as the + // case we know about (system suspend because of laptop close) corresponds + // to a user action. + // TODO(ahendrickson) -- Find a better set of codes to use here, as + // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. + if (net::IsCertStatusError(request()->ssl_info().cert_status)) + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; + else + reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; + } + + if (status.is_success() && reason == DOWNLOAD_INTERRUPT_REASON_NONE && + request()->response_headers()) { + // Handle server's response codes. + switch (response_code) { + case -1: // Non-HTTP request. + case net::HTTP_OK: + case net::HTTP_CREATED: + case net::HTTP_ACCEPTED: + case net::HTTP_NON_AUTHORITATIVE_INFORMATION: + case net::HTTP_RESET_CONTENT: + case net::HTTP_PARTIAL_CONTENT: + // Expected successful codes. + break; + case net::HTTP_NO_CONTENT: + case net::HTTP_NOT_FOUND: + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + break; + case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: + // Retry by downloading from the start automatically: + // If we haven't received data when we get this error, we won't. + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; + break; + case net::HTTP_UNAUTHORIZED: + // Server didn't authorize this request. + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; + break; + case net::HTTP_FORBIDDEN: + // Server forbids access to this resource. + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; + break; + default: // All other errors. + // Redirection and informational codes should have been handled earlier + // in the stack. + DCHECK_NE(3, response_code / 100); + DCHECK_NE(1, response_code / 100); + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; + break; + } + } + + std::string accept_ranges; + bool has_strong_validators = false; + if (request()->response_headers()) { + request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges", + &accept_ranges); + has_strong_validators = + request()->response_headers()->HasStrongValidators(); + } + RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); + RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, + total_pause_time_); + + CallStartedCB(nullptr, reason); + + // Send the info down the stream. Conditional is in case we get + // OnResponseCompleted without OnResponseStarted. + if (stream_writer_) + stream_writer_->Close(reason); + + // If the error mapped to something unknown, record it so that + // we can drill down. + if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Download.MapErrorNetworkFailed", + std::abs(status.error())); + } + + stream_writer_.reset(); // We no longer need the stream. + read_buffer_ = nullptr; +} + +void DownloadRequestCore::PauseRequest() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ++pause_count_; +} + +void DownloadRequestCore::ResumeRequest() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_LT(0, pause_count_); + + --pause_count_; + + if (!was_deferred_) + return; + if (pause_count_ > 0) + return; + + was_deferred_ = false; + if (!last_stream_pause_time_.is_null()) { + total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); + last_stream_pause_time_ = base::TimeTicks(); + } + + downloader_->ResumeReading(); +} + +std::string DownloadRequestCore::DebugString() const { + return base::StringPrintf( + "{" + " url_ = " + "\"%s\"" + " }", + request() ? request()->url().spec().c_str() : "<NULL request>"); +} + +} // namespace content
diff --git a/content/browser/download/download_request_core.h b/content/browser/download/download_request_core.h new file mode 100644 index 0000000..c01fa98 --- /dev/null +++ b/content/browser/download/download_request_core.h
@@ -0,0 +1,123 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_CORE_H_ +#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_CORE_H_ + +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/loader/resource_handler.h" +#include "content/public/browser/download_interrupt_reasons.h" +#include "content/public/browser/download_save_info.h" +#include "content/public/browser/download_url_parameters.h" + +namespace net { +class URLRequest; +} // namespace net + +namespace content { +class DownloadManagerImpl; +class ByteStreamReader; +class ByteStreamWriter; +class PowerSaveBlocker; +class UrlDownloader; +struct DownloadCreateInfo; + +// Forwards data to the download thread. +class CONTENT_EXPORT DownloadRequestCore + : public base::SupportsWeakPtr<DownloadRequestCore> { + public: + // Size of the buffer used between the DownloadRequestCore and the + // downstream receiver of its output. + static const int kDownloadByteStreamSize; + + // started_cb will be called exactly once on the UI thread. + // |id| should be invalid if the id should be automatically assigned. + DownloadRequestCore( + uint32 id, + net::URLRequest* request, + const DownloadUrlParameters::OnStartedCallback& started_cb, + scoped_ptr<DownloadSaveInfo> save_info, + base::WeakPtr<DownloadManagerImpl> download_manager); + ~DownloadRequestCore(); + + // Send the download creation information to the download thread. + bool OnResponseStarted(); + + // Create a new buffer, which will be handed to the download thread for file + // writing and deletion. + bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, + int* buf_size, + int min_size); + + bool OnReadCompleted(int bytes_read, bool* defer); + + void OnResponseCompleted(const net::URLRequestStatus& status); + + void PauseRequest(); + void ResumeRequest(); + + std::string DebugString() const; + + void set_downloader(UrlDownloader* downloader) { downloader_ = downloader; } + + protected: + net::URLRequest* request() const { return request_; } + + private: + // Arrange for started_cb_ to be called on the UI thread with the + // below values, nulling out started_cb_. Should only be called + // on the IO thread. + void CallStartedCB(DownloadItem* item, + DownloadInterruptReason interrupt_reason); + + net::URLRequest* request_; + uint32 download_id_; + + // This is read only on the IO thread, but may only + // be called on the UI thread. + DownloadUrlParameters::OnStartedCallback started_cb_; + scoped_ptr<DownloadSaveInfo> save_info_; + + // Data flow + scoped_refptr<net::IOBuffer> read_buffer_; // From URLRequest. + scoped_ptr<ByteStreamWriter> stream_writer_; // To rest of system. + + // Keeps the system from sleeping while this is alive. If the + // system enters power saving mode while a request is alive, it can cause the + // request to fail and the associated download will be interrupted. + scoped_ptr<PowerSaveBlocker> power_save_blocker_; + + // The following are used to collect stats. + base::TimeTicks download_start_time_; + base::TimeTicks last_read_time_; + base::TimeTicks last_stream_pause_time_; + base::TimeDelta total_pause_time_; + size_t last_buffer_size_; + int64 bytes_read_; + + int pause_count_; + bool was_deferred_; + + // For DCHECKing + bool on_response_started_called_; + + UrlDownloader* downloader_; + + // DownloadManager passed in by the owner of DownloadRequestCore. + base::WeakPtr<DownloadManagerImpl> download_manager_; + + static const int kReadBufSize = 32768; // bytes + static const int kThrottleTimeMs = 200; // milliseconds + + DISALLOW_COPY_AND_ASSIGN(DownloadRequestCore); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_CORE_H_
diff --git a/content/browser/download/download_request_handle.cc b/content/browser/download/download_request_handle.cc index 6e319d3..89d6d00a 100644 --- a/content/browser/download/download_request_handle.cc +++ b/content/browser/download/download_request_handle.cc
@@ -42,7 +42,7 @@ } WebContents* DownloadRequestHandle::GetWebContents() const { - // PlzNavigate: if a FrameTreeNodeID was provided, use it to return the + // PlzNavigate: if a FrameTreeNodeId was provided, use it to return the // WebContents. // TODO(davidben): This logic should be abstracted within the ResourceLoader // stack. https://crbug.com/376003 @@ -64,7 +64,7 @@ } DownloadManager* DownloadRequestHandle::GetDownloadManager() const { - // PlzNavigate: if a FrameTreeNodeID was provided, use it to return the + // PlzNavigate: if a FrameTreeNodeId was provided, use it to return the // DownloadManager. // TODO(davidben): This logic should be abstracted within the ResourceLoader // stack. https://crbug.com/376003
diff --git a/content/browser/download/url_downloader.cc b/content/browser/download/url_downloader.cc new file mode 100644 index 0000000..ffaea711 --- /dev/null +++ b/content/browser/download/url_downloader.cc
@@ -0,0 +1,210 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/download/url_downloader.h" + +#include "base/location.h" +#include "base/thread_task_runner_handle.h" +#include "content/browser/appcache/appcache_interceptor.h" +#include "content/browser/download/download_manager_impl.h" +#include "content/browser/service_worker/service_worker_request_handler.h" +#include "content/browser/ssl/ssl_policy.h" +#include "content/common/ssl_status_serialization.h" +#include "content/public/browser/cert_store.h" +#include "content/public/browser/download_save_info.h" +#include "content/public/browser/signed_certificate_timestamp_store.h" +#include "content/public/common/process_type.h" +#include "content/public/common/security_style.h" +#include "net/base/io_buffer.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_status_code.h" +#include "ui/base/page_transition_types.h" + +namespace content { + +// static +scoped_ptr<UrlDownloader> UrlDownloader::BeginDownload( + base::WeakPtr<DownloadManagerImpl> download_manager, + scoped_ptr<net::URLRequest> request, + const Referrer& referrer, + bool is_content_initiated, + bool prefer_cache, + bool do_not_prompt_for_login, + scoped_ptr<DownloadSaveInfo> save_info, + uint32 download_id, + const DownloadUrlParameters::OnStartedCallback& started_callback) { + if (!referrer.url.is_valid()) + request->SetReferrer(std::string()); + else + request->SetReferrer(referrer.url.spec()); + + int extra_load_flags = net::LOAD_NORMAL; + if (prefer_cache) { + // If there is upload data attached, only retrieve from cache because there + // is no current mechanism to prompt the user for their consent for a + // re-post. For GETs, try to retrieve data from the cache and skip + // validating the entry if present. + if (request->get_upload() != NULL) + extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; + else + extra_load_flags |= net::LOAD_PREFERRING_CACHE; + } else { + extra_load_flags |= net::LOAD_DISABLE_CACHE; + } + request->SetLoadFlags(request->load_flags() | extra_load_flags); + + if (request->url().SchemeIs(url::kBlobScheme)) + return nullptr; + + scoped_ptr<DownloadRequestCore> handler( + new DownloadRequestCore(download_id, request.get(), started_callback, + save_info.Pass(), download_manager)); + + // From this point forward, the |UrlDownloader| is responsible for + // |started_callback|. + scoped_ptr<UrlDownloader> downloader( + new UrlDownloader(request.Pass(), handler.Pass(), download_manager)); + + downloader->Start(); + + return downloader; +} + +UrlDownloader::UrlDownloader(scoped_ptr<net::URLRequest> request, + scoped_ptr<DownloadRequestCore> handler, + base::WeakPtr<DownloadManagerImpl> manager) + : request_(request.Pass()), + handler_(handler.Pass()), + manager_(manager), + weak_ptr_factory_(this) { + handler_->set_downloader(this); +} + +UrlDownloader::~UrlDownloader() { + handler_.reset(); +} + +void UrlDownloader::Start() { + DCHECK(!request_->is_pending()); + + if (!request_->status().is_success()) + return; + + request_->set_delegate(this); + request_->Start(); +} + +void UrlDownloader::OnReceivedRedirect(net::URLRequest* request, + const net::RedirectInfo& redirect_info, + bool* defer_redirect) { + DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); + request_->CancelWithError(net::ERR_ABORTED); +} + +void UrlDownloader::OnResponseStarted(net::URLRequest* request) { + DVLOG(1) << "OnResponseStarted: " << request_->url().spec(); + + if (!request_->status().is_success()) { + ResponseCompleted(); + return; + } + + handler_->OnResponseStarted(); + + if (request_->status().is_success()) + StartReading(false); // Read the first chunk. + else + ResponseCompleted(); +} + +void UrlDownloader::StartReading(bool is_continuation) { + int bytes_read; + + // Make sure we track the buffer in at least one place. This ensures it gets + // deleted even in the case the request has already finished its job and + // doesn't use the buffer. + scoped_refptr<net::IOBuffer> buf; + int buf_size; + if (!handler_->OnWillRead(&buf, &buf_size, -1)) { + request_->CancelWithError(net::ERR_ABORTED); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&UrlDownloader::ResponseCompleted, + weak_ptr_factory_.GetWeakPtr())); + return; + } + + DCHECK(buf.get()); + DCHECK(buf_size > 0); + + request_->Read(buf.get(), buf_size, &bytes_read); + + // If IO is pending, wait for the URLRequest to call OnReadCompleted. + if (request_->status().is_io_pending()) + return; + + if (!is_continuation || bytes_read <= 0) { + OnReadCompleted(request_.get(), bytes_read); + } else { + // Else, trigger OnReadCompleted asynchronously to avoid starving the IO + // thread in case the URLRequest can provide data synchronously. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&UrlDownloader::OnReadCompleted, + weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read)); + } +} + +void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) { + DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\"" + << " bytes_read = " << bytes_read; + + // bytes_read == -1 always implies an error. + if (bytes_read == -1 || !request_->status().is_success()) { + ResponseCompleted(); + return; + } + + DCHECK(bytes_read >= 0); + DCHECK(request_->status().is_success()); + + bool defer = false; + if (!handler_->OnReadCompleted(bytes_read, &defer)) { + request_->CancelWithError(net::ERR_ABORTED); + return; + } else if (defer) { + return; + } + + if (!request_->status().is_success()) + return; + + if (bytes_read > 0) { + StartReading(true); // Read the next chunk. + } else { + // URLRequest reported an EOF. Call ResponseCompleted. + DCHECK_EQ(0, bytes_read); + ResponseCompleted(); + } +} + +void UrlDownloader::ResponseCompleted() { + DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); + + handler_->OnResponseCompleted(request_->status()); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManagerImpl::RemoveUrlDownloader, manager_, this)); +} + +void UrlDownloader::ResumeReading() { + if (request_->status().is_success()) { + StartReading(false); // Read the next chunk (OK to complete synchronously). + } else { + ResponseCompleted(); + } +} + +} // namespace content
diff --git a/content/browser/download/url_downloader.h b/content/browser/download/url_downloader.h new file mode 100644 index 0000000..c9850920 --- /dev/null +++ b/content/browser/download/url_downloader.h
@@ -0,0 +1,62 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DOWNLOAD_URL_DOWNLOADER_H_ +#define CONTENT_BROWSER_DOWNLOAD_URL_DOWNLOADER_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/download/download_request_core.h" +#include "content/public/browser/download_save_info.h" +#include "content/public/browser/download_url_parameters.h" +#include "content/public/common/referrer.h" +#include "net/url_request/redirect_info.h" +#include "net/url_request/url_request.h" + +namespace content { +class DownloadManagerImpl; +class DownloadRequestCore; + +class UrlDownloader : public net::URLRequest::Delegate { + public: + UrlDownloader(scoped_ptr<net::URLRequest> request, + scoped_ptr<DownloadRequestCore> handler, + base::WeakPtr<DownloadManagerImpl> manager); + ~UrlDownloader() override; + + static scoped_ptr<UrlDownloader> BeginDownload( + base::WeakPtr<DownloadManagerImpl> download_manager, + scoped_ptr<net::URLRequest> request, + const Referrer& referrer, + bool is_content_initiated, + bool prefer_cache, + bool do_not_prompt_for_login, + scoped_ptr<DownloadSaveInfo> save_info, + uint32 download_id, + const DownloadUrlParameters::OnStartedCallback& started_callback); + + // URLRequest::Delegate: + void OnReceivedRedirect(net::URLRequest* request, + const net::RedirectInfo& redirect_info, + bool* defer_redirect) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnReadCompleted(net::URLRequest* request, int bytes_read) override; + + void StartReading(bool is_continuation); + void ResponseCompleted(); + + void Start(); + void ResumeReading(); + + private: + scoped_ptr<net::URLRequest> request_; + scoped_ptr<DownloadRequestCore> handler_; + base::WeakPtr<DownloadManagerImpl> manager_; + + base::WeakPtrFactory<UrlDownloader> weak_ptr_factory_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOWNLOAD_URL_DOWNLOADER_H_
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc index 8270ea29..3e711dd7 100644 --- a/content/browser/frame_host/frame_tree_node.cc +++ b/content/browser/frame_host/frame_tree_node.cc
@@ -25,9 +25,9 @@ // This is a global map between frame_tree_node_ids and pointers to // FrameTreeNodes. -typedef base::hash_map<int, FrameTreeNode*> FrameTreeNodeIDMap; +typedef base::hash_map<int, FrameTreeNode*> FrameTreeNodeIdMap; -base::LazyInstance<FrameTreeNodeIDMap> g_frame_tree_node_id_map = +base::LazyInstance<FrameTreeNodeIdMap> g_frame_tree_node_id_map = LAZY_INSTANCE_INITIALIZER; // These values indicate the loading progress status. The minimum progress @@ -62,8 +62,8 @@ // static FrameTreeNode* FrameTreeNode::GloballyFindByID(int frame_tree_node_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - FrameTreeNodeIDMap* nodes = g_frame_tree_node_id_map.Pointer(); - FrameTreeNodeIDMap::iterator it = nodes->find(frame_tree_node_id); + FrameTreeNodeIdMap* nodes = g_frame_tree_node_id_map.Pointer(); + FrameTreeNodeIdMap::iterator it = nodes->find(frame_tree_node_id); return it == nodes->end() ? nullptr : it->second; } @@ -96,7 +96,7 @@ effective_sandbox_flags_(sandbox_flags), frame_owner_properties_(frame_owner_properties), loading_progress_(kLoadingProgressNotStarted) { - std::pair<FrameTreeNodeIDMap::iterator, bool> result = + std::pair<FrameTreeNodeIdMap::iterator, bool> result = g_frame_tree_node_id_map.Get().insert( std::make_pair(frame_tree_node_id_, this)); CHECK(result.second);
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc index 96d6c1f..87c139ca 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -314,7 +314,7 @@ } bool RenderFrameHostManager::ForInnerDelegate() { - return delegate_->GetOuterDelegateFrameTreeNodeID() != + return delegate_->GetOuterDelegateFrameTreeNodeId() != FrameTreeNode::kFrameTreeNodeInvalidID; } @@ -325,7 +325,7 @@ FrameTreeNode* outer_contents_frame_tree_node = FrameTreeNode::GloballyFindByID( - delegate_->GetOuterDelegateFrameTreeNodeID()); + delegate_->GetOuterDelegateFrameTreeNodeId()); return outer_contents_frame_tree_node->parent() ->current_frame_host() ->render_view_host() @@ -334,7 +334,7 @@ FrameTreeNode* RenderFrameHostManager::GetOuterDelegateNode() { int outer_contents_frame_tree_node_id = - delegate_->GetOuterDelegateFrameTreeNodeID(); + delegate_->GetOuterDelegateFrameTreeNodeId(); return FrameTreeNode::GloballyFindByID(outer_contents_frame_tree_node_id); } @@ -351,7 +351,7 @@ RenderFrameProxyHost* RenderFrameHostManager::GetProxyToOuterDelegate() { int outer_contents_frame_tree_node_id = - delegate_->GetOuterDelegateFrameTreeNodeID(); + delegate_->GetOuterDelegateFrameTreeNodeId(); FrameTreeNode* outer_contents_frame_tree_node = FrameTreeNode::GloballyFindByID(outer_contents_frame_tree_node_id); if (!outer_contents_frame_tree_node || @@ -367,7 +367,7 @@ void RenderFrameHostManager::RemoveOuterDelegateFrame() { FrameTreeNode* outer_delegate_frame_tree_node = FrameTreeNode::GloballyFindByID( - delegate_->GetOuterDelegateFrameTreeNodeID()); + delegate_->GetOuterDelegateFrameTreeNodeId()); DCHECK(outer_delegate_frame_tree_node->parent()); outer_delegate_frame_tree_node->frame_tree()->RemoveFrame( outer_delegate_frame_tree_node);
diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h index 2c6e5d4..9690fe4 100644 --- a/content/browser/frame_host/render_frame_host_manager.h +++ b/content/browser/frame_host/render_frame_host_manager.h
@@ -168,7 +168,7 @@ // FrameTreeNode ID of the frame in the outer WebContents which hosts // the inner WebContents. Returns FrameTreeNode::kFrameTreeNodeInvalidID // if the delegate does not have an outer WebContents. - virtual int GetOuterDelegateFrameTreeNodeID() = 0; + virtual int GetOuterDelegateFrameTreeNodeId() = 0; protected: virtual ~Delegate() {}
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index ad2d5b4f..ba77c56 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -961,6 +961,12 @@ "Net.RequestTime2.Success", request_loading_time); break; case net::ERR_ABORTED: + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.SentBytes", + loader->request()->GetTotalSentBytes(), 1, + 50000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.ReceivedBytes", + loader->request()->GetTotalReceivedBytes(), + 1, 50000000, 50); UMA_HISTOGRAM_LONG_TIMES( "Net.RequestTime2.ErrAborted", request_loading_time); break;
diff --git a/content/browser/media/android/media_session.cc b/content/browser/media/android/media_session.cc index 60360f4..a8d911eb 100644 --- a/content/browser/media/android/media_session.cc +++ b/content/browser/media/android/media_session.cc
@@ -121,14 +121,9 @@ if (audio_focus_state_ != State::ACTIVE) return; - OnSuspendInternal(SuspendType::SYSTEM); - if (!temporary) - SetAudioFocusState(State::INACTIVE); + OnSuspendInternal(SuspendType::SYSTEM, + temporary ? State::SUSPENDED : State::INACTIVE); - uma_helper_.RecordSessionSuspended( - temporary ? MediaSessionSuspendedSource::SystemTransient - : MediaSessionSuspendedSource::SystemPermanent); - UpdateWebContents(); } void MediaSession::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) { @@ -136,7 +131,6 @@ return; OnResumeInternal(SuspendType::SYSTEM); - UpdateWebContents(); } void MediaSession::Resume() { @@ -158,14 +152,14 @@ void MediaSession::Suspend() { DCHECK(!IsSuspended()); - OnSuspendInternal(SuspendType::UI); + OnSuspendInternal(SuspendType::UI, State::SUSPENDED); } void MediaSession::Stop() { DCHECK(audio_focus_state_ != State::INACTIVE); if (audio_focus_state_ != State::SUSPENDED) - OnSuspendInternal(SuspendType::UI); + OnSuspendInternal(SuspendType::UI, State::SUSPENDED); DCHECK(audio_focus_state_ == State::SUSPENDED); players_.clear(); @@ -204,17 +198,35 @@ AbandonSystemAudioFocusIfNeeded(); } -void MediaSession::OnSuspendInternal(SuspendType type) { - // SuspendType::System will handle the UMA recording at the calling point - // because there are more than one type. +void MediaSession::OnSuspendInternal(SuspendType type, State new_state) { + DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE); + // UI suspend cannot use State::INACTIVE. + DCHECK(type == SuspendType::SYSTEM || new_state == State::SUSPENDED); + if (type == SuspendType::UI) uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI); + if (type == SuspendType::SYSTEM) { + switch (new_state) { + case State::SUSPENDED: + uma_helper_.RecordSessionSuspended( + MediaSessionSuspendedSource::SystemTransient); + break; + case State::INACTIVE: + uma_helper_.RecordSessionSuspended( + MediaSessionSuspendedSource::SystemPermanent); + break; + default: + break; + } + } - SetAudioFocusState(State::SUSPENDED); + SetAudioFocusState(new_state); suspend_type_ = type; for (const auto& it : players_) it.observer->OnSuspend(it.player_id); + + UpdateWebContents(); } void MediaSession::OnResumeInternal(SuspendType type) { @@ -225,6 +237,8 @@ for (const auto& it : players_) it.observer->OnResume(it.player_id); + + UpdateWebContents(); } MediaSession::MediaSession(WebContents* web_contents)
diff --git a/content/browser/media/android/media_session.h b/content/browser/media/android/media_session.h index 9ba76c3..b4a8424 100644 --- a/content/browser/media/android/media_session.h +++ b/content/browser/media/android/media_session.h
@@ -136,7 +136,7 @@ // Setup the JNI. void Initialize(); - void OnSuspendInternal(SuspendType type); + void OnSuspendInternal(SuspendType type, State new_state); void OnResumeInternal(SuspendType type); // Requests audio focus to Android using |j_media_session_|.
diff --git a/content/browser/media/android/media_session_browsertest.cc b/content/browser/media/android/media_session_browsertest.cc index 99553b4a..a16ab2f 100644 --- a/content/browser/media/android/media_session_browsertest.cc +++ b/content/browser/media/android/media_session_browsertest.cc
@@ -302,6 +302,17 @@ EXPECT_FALSE(HasAudioFocus()); } +IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, StopGivesAwayAudioFocus) { + scoped_ptr<MockMediaSessionObserver> media_session_observer( + new MockMediaSessionObserver); + + StartNewPlayer(media_session_observer.get(), MediaSession::Type::Content); + + media_session_->Stop(); + + EXPECT_FALSE(HasAudioFocus()); +} + IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ResumeGivesBackAudioFocus) { scoped_ptr<MockMediaSessionObserver> media_session_observer( new MockMediaSessionObserver); @@ -690,6 +701,28 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, + ConstrolsHideWhenSessionStops) { + Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(), + MediaSessionStateChanged(true, false)); + Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(), + MediaSessionStateChanged(true, true)) + .After(showControls); + EXPECT_CALL(*mock_web_contents_observer(), + MediaSessionStateChanged(false, true)) + .After(pauseControls); + + scoped_ptr<MockMediaSessionObserver> media_session_observer( + new MockMediaSessionObserver); + + StartNewPlayer(media_session_observer.get(), MediaSession::Type::Content); + + media_session_->Stop(); + + EXPECT_FALSE(IsControllable()); + EXPECT_TRUE(IsSuspended()); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsHideWhenSessionChangesFromContentToTransient) { Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(true, false)); @@ -763,9 +796,11 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, - ControlsNotUpdatedDueToResumeSessionAction) { - EXPECT_CALL(*mock_web_contents_observer(), + ControlsUpdatedDueToResumeSessionAction) { + Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(), MediaSessionStateChanged(true, false)); + EXPECT_CALL(*mock_web_contents_observer(), + MediaSessionStateChanged(true, true)).After(showControls); scoped_ptr<MockMediaSessionObserver> media_session_observer( new MockMediaSessionObserver); @@ -778,9 +813,15 @@ } IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, - ControlsNotUpdatedDueToSuspendSessionAction) { + ControlsUpdatedDueToSuspendSessionAction) { + Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(), + MediaSessionStateChanged(true, false)); + Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(), + MediaSessionStateChanged(true, true)) + .After(showControls); EXPECT_CALL(*mock_web_contents_observer(), - MediaSessionStateChanged(true, false)); + MediaSessionStateChanged(true, false)) + .After(pauseControls); scoped_ptr<MockMediaSessionObserver> media_session_observer( new MockMediaSessionObserver);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 80fcea5..14c9458 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1455,7 +1455,6 @@ cc::switches::kEnableBeginFrameScheduling, cc::switches::kEnableGpuBenchmarking, cc::switches::kEnableMainFrameBeforeActivation, - cc::switches::kEnableTileCompression, cc::switches::kShowCompositedLayerBorders, cc::switches::kShowLayerAnimationBounds, cc::switches::kShowPropertyChangedRects,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 1ca34e2..3ff2c4d3 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4618,7 +4618,7 @@ return capturer_count_ == 0 && !should_normally_be_visible_; } -int WebContentsImpl::GetOuterDelegateFrameTreeNodeID() { +int WebContentsImpl::GetOuterDelegateFrameTreeNodeId() { if (node_ && node_->outer_web_contents()) return node_->outer_contents_frame_tree_node_id();
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index a46f717..7136cc8 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -643,7 +643,7 @@ bool FocusLocationBarByDefault() override; void SetFocusToLocationBar(bool select_all) override; bool IsHidden() override; - int GetOuterDelegateFrameTreeNodeID() override; + int GetOuterDelegateFrameTreeNodeId() override; // NotificationObserver ------------------------------------------------------
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h index d463ef0f..997a6e8 100644 --- a/content/common/gpu/gpu_messages.h +++ b/content/common/gpu/gpu_messages.h
@@ -227,6 +227,11 @@ IPC_STRUCT_TRAITS_MEMBER(min_resolution) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(gpu::VideoDecodeAcceleratorCapabilities) + IPC_STRUCT_TRAITS_MEMBER(supported_profiles) + IPC_STRUCT_TRAITS_MEMBER(flags) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(gpu::VideoEncodeAcceleratorSupportedProfile) IPC_STRUCT_TRAITS_MEMBER(profile) IPC_STRUCT_TRAITS_MEMBER(max_resolution) @@ -270,7 +275,7 @@ IPC_STRUCT_TRAITS_MEMBER(dx_diagnostics_info_state) IPC_STRUCT_TRAITS_MEMBER(dx_diagnostics) #endif - IPC_STRUCT_TRAITS_MEMBER(video_decode_accelerator_supported_profiles) + IPC_STRUCT_TRAITS_MEMBER(video_decode_accelerator_capabilities) IPC_STRUCT_TRAITS_MEMBER(video_encode_accelerator_supported_profiles) IPC_STRUCT_TRAITS_MEMBER(jpeg_decode_accelerator_supported) IPC_STRUCT_TRAITS_END()
diff --git a/content/common/gpu/media/android_copying_backing_strategy.cc b/content/common/gpu/media/android_copying_backing_strategy.cc index 912ba453..30e4bc1 100644 --- a/content/common/gpu/media/android_copying_backing_strategy.cc +++ b/content/common/gpu/media/android_copying_backing_strategy.cc
@@ -17,13 +17,6 @@ namespace content { -// TODO(liberato): It is unclear if we have an issue with deadlock during -// playback if we lower this. Previously (crbug.com/176036), a deadlock -// could occur during preroll. More recent tests have shown some -// instability with kNumPictureBuffers==2 with similar symptoms -// during playback. crbug.com/:531588 . -enum { kNumPictureBuffers = media::limits::kMaxVideoFrames + 1 }; - const static GLfloat kIdentityMatrix[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; @@ -48,10 +41,6 @@ glDeleteTextures(1, &surface_texture_id_); } -uint32 AndroidCopyingBackingStrategy::GetNumPictureBuffers() const { - return kNumPictureBuffers; -} - uint32 AndroidCopyingBackingStrategy::GetTextureTarget() const { return GL_TEXTURE_2D; }
diff --git a/content/common/gpu/media/android_copying_backing_strategy.h b/content/common/gpu/media/android_copying_backing_strategy.h index 4cb1f62..15be76d8 100644 --- a/content/common/gpu/media/android_copying_backing_strategy.h +++ b/content/common/gpu/media/android_copying_backing_strategy.h
@@ -32,7 +32,6 @@ // AndroidVideoDecodeAccelerator::BackingStrategy void Initialize(AVDAStateProvider*) override; void Cleanup(const AndroidVideoDecodeAccelerator::OutputBufferMap&) override; - uint32 GetNumPictureBuffers() const override; uint32 GetTextureTarget() const override; scoped_refptr<gfx::SurfaceTexture> CreateSurfaceTexture() override; void UseCodecBufferForPictureBuffer(int32 codec_buffer_index, @@ -41,6 +40,11 @@ media::VideoCodecBridge*, const AndroidVideoDecodeAccelerator::OutputBufferMap&) override; + static media::VideoDecodeAccelerator::Capabilities::Flags + GetCapabilitiesFlags() { + return media::VideoDecodeAccelerator::Capabilities::NO_FLAGS; + } + private: // Used for copy the texture from surface texture to picture buffers. scoped_ptr<gpu::CopyTextureCHROMIUMResourceManager> copier_;
diff --git a/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc b/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc index 78472bc..4075c969 100644 --- a/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc +++ b/content/common/gpu/media/android_deferred_rendering_backing_strategy.cc
@@ -18,11 +18,6 @@ namespace content { -// TODO(liberato): This is an entirely made-up number. It depends on how -// many decoded buffers that the MediaCodec is willing to have outstanding -// at any one time. Only one is guaranteed. crbug.com/531606. -enum { kNumPictureBuffers = 3 }; - AndroidDeferredRenderingBackingStrategy:: AndroidDeferredRenderingBackingStrategy() : state_provider_(nullptr), media_codec_(nullptr) {} @@ -47,10 +42,6 @@ } } -uint32 AndroidDeferredRenderingBackingStrategy::GetNumPictureBuffers() const { - return kNumPictureBuffers; -} - uint32 AndroidDeferredRenderingBackingStrategy::GetTextureTarget() const { return GL_TEXTURE_EXTERNAL_OES; }
diff --git a/content/common/gpu/media/android_deferred_rendering_backing_strategy.h b/content/common/gpu/media/android_deferred_rendering_backing_strategy.h index 7fb8d2b..b056e135 100644 --- a/content/common/gpu/media/android_deferred_rendering_backing_strategy.h +++ b/content/common/gpu/media/android_deferred_rendering_backing_strategy.h
@@ -36,7 +36,6 @@ // AndroidVideoDecodeAccelerator::BackingStrategy void Initialize(AVDAStateProvider*) override; void Cleanup(const AndroidVideoDecodeAccelerator::OutputBufferMap&) override; - uint32 GetNumPictureBuffers() const override; uint32 GetTextureTarget() const override; scoped_refptr<gfx::SurfaceTexture> CreateSurfaceTexture() override; void UseCodecBufferForPictureBuffer(int32 codec_buffer_index, @@ -48,6 +47,12 @@ media::VideoCodecBridge*, const AndroidVideoDecodeAccelerator::OutputBufferMap&) override; + static media::VideoDecodeAccelerator::Capabilities::Flags + GetCapabilitiesFlags() { + return media::VideoDecodeAccelerator::Capabilities:: + NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE; + } + private: // Release any codec buffer that is associated with the given picture buffer // back to the codec. It is okay if there is no such buffer.
diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc index 00e1bb6..6b381f3 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.cc +++ b/content/common/gpu/media/android_video_decode_accelerator.cc
@@ -10,6 +10,8 @@ #include "base/metrics/histogram.h" #include "base/trace_event/trace_event.h" #include "content/common/gpu/gpu_channel.h" +#include "content/common/gpu/media/android_copying_backing_strategy.h" +#include "content/common/gpu/media/android_deferred_rendering_backing_strategy.h" #include "content/common/gpu/media/avda_return_on_failure.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "media/base/bitstream_buffer.h" @@ -28,6 +30,13 @@ namespace content { +// TODO(liberato): It is unclear if we have an issue with deadlock during +// playback if we lower this. Previously (crbug.com/176036), a deadlock +// could occur during preroll. More recent tests have shown some +// instability with kNumPictureBuffers==2 with similar symptoms +// during playback. crbug.com/531588 . +enum { kNumPictureBuffers = media::limits::kMaxVideoFrames + 1 }; + // Max number of bitstreams notified to the client with // NotifyEndOfBitstreamBuffer() before getting output from the bitstream. enum { kMaxBitstreamsNotifiedInAdvance = 32 }; @@ -50,6 +59,10 @@ media::H264PROFILE_STEREOHIGH, media::H264PROFILE_MULTIVIEWHIGH }; + +#define BACKING_STRATEGY AndroidDeferredRenderingBackingStrategy +#else +#define BACKING_STRATEGY AndroidCopyingBackingStrategy #endif // Because MediaCodec is thread-hostile (must be poked on a single thread) and @@ -76,8 +89,7 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( const base::WeakPtr<gpu::gles2::GLES2Decoder> decoder, - const base::Callback<bool(void)>& make_context_current, - scoped_ptr<BackingStrategy> strategy) + const base::Callback<bool(void)>& make_context_current) : client_(NULL), make_context_current_(make_context_current), codec_(media::kCodecH264), @@ -85,7 +97,7 @@ state_(NO_ERROR), picturebuffers_requested_(false), gl_decoder_(decoder), - strategy_(strategy.Pass()), + strategy_(new BACKING_STRATEGY()), weak_this_factory_(this) {} AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { @@ -434,7 +446,7 @@ } void AndroidVideoDecodeAccelerator::RequestPictureBuffers() { - client_->ProvidePictureBuffers(strategy_->GetNumPictureBuffers(), size_, + client_->ProvidePictureBuffers(kNumPictureBuffers, size_, strategy_->GetTextureTarget()); } @@ -460,9 +472,8 @@ } TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size()); - RETURN_ON_FAILURE( - this, output_picture_buffers_.size() >= strategy_->GetNumPictureBuffers(), - "Invalid picture buffers were passed.", INVALID_ARGUMENT); + RETURN_ON_FAILURE(this, output_picture_buffers_.size() >= kNumPictureBuffers, + "Invalid picture buffers were passed.", INVALID_ARGUMENT); DoIOTask(); } @@ -634,9 +645,10 @@ } // static -media::VideoDecodeAccelerator::SupportedProfiles -AndroidVideoDecodeAccelerator::GetSupportedProfiles() { - SupportedProfiles profiles; +media::VideoDecodeAccelerator::Capabilities +AndroidVideoDecodeAccelerator::GetCapabilities() { + Capabilities capabilities; + SupportedProfiles& profiles = capabilities.supported_profiles; if (!media::VideoCodecBridge::IsKnownUnaccelerated( media::kCodecVP8, media::MEDIA_CODEC_DECODER)) { @@ -669,7 +681,9 @@ } #endif - return profiles; + capabilities.flags = BACKING_STRATEGY::GetCapabilitiesFlags(); + + return capabilities; } } // namespace content
diff --git a/content/common/gpu/media/android_video_decode_accelerator.h b/content/common/gpu/media/android_video_decode_accelerator.h index 945a4d0a..6221ad9 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.h +++ b/content/common/gpu/media/android_video_decode_accelerator.h
@@ -51,9 +51,6 @@ // the last call that the BackingStrategy receives. virtual void Cleanup(const OutputBufferMap& buffer_map) = 0; - // Return the number of picture buffers that we can support. - virtual uint32 GetNumPictureBuffers() const = 0; - // Return the GL texture target that the PictureBuffer textures use. virtual uint32 GetTextureTarget() const = 0; @@ -89,8 +86,7 @@ AndroidVideoDecodeAccelerator( const base::WeakPtr<gpu::gles2::GLES2Decoder> decoder, - const base::Callback<bool(void)>& make_context_current, - scoped_ptr<BackingStrategy> strategy); + const base::Callback<bool(void)>& make_context_current); ~AndroidVideoDecodeAccelerator() override; @@ -113,8 +109,7 @@ void PostError(const ::tracked_objects::Location& from_here, media::VideoDecodeAccelerator::Error error) override; - static media::VideoDecodeAccelerator::SupportedProfiles - GetSupportedProfiles(); + static media::VideoDecodeAccelerator::Capabilities GetCapabilities(); private: enum State {
diff --git a/content/common/gpu/media/android_video_decode_accelerator_unittest.cc b/content/common/gpu/media/android_video_decode_accelerator_unittest.cc index c1e91f12..400d7cc 100644 --- a/content/common/gpu/media/android_video_decode_accelerator_unittest.cc +++ b/content/common/gpu/media/android_video_decode_accelerator_unittest.cc
@@ -70,8 +70,7 @@ scoped_ptr<MockVideoDecodeAcceleratorClient> client( new MockVideoDecodeAcceleratorClient()); accelerator_.reset(new AndroidVideoDecodeAccelerator( - decoder->AsWeakPtr(), base::Bind(&MockMakeContextCurrent), - make_scoped_ptr(new AndroidCopyingBackingStrategy()))); + decoder->AsWeakPtr(), base::Bind(&MockMakeContextCurrent))); } bool Configure(media::VideoCodec codec) {
diff --git a/content/common/gpu/media/gpu_video_accelerator_util.cc b/content/common/gpu/media/gpu_video_accelerator_util.cc index d23c192..7692fdd 100644 --- a/content/common/gpu/media/gpu_video_accelerator_util.cc +++ b/content/common/gpu/media/gpu_video_accelerator_util.cc
@@ -31,6 +31,17 @@ STATIC_ASSERT_ENUM_MATCH(VIDEO_CODEC_PROFILE_MAX); // static +media::VideoDecodeAccelerator::Capabilities +GpuVideoAcceleratorUtil::ConvertGpuToMediaDecodeCapabilities( + const gpu::VideoDecodeAcceleratorCapabilities& gpu_capabilities) { + media::VideoDecodeAccelerator::Capabilities capabilities; + capabilities.supported_profiles = + ConvertGpuToMediaDecodeProfiles(gpu_capabilities.supported_profiles); + capabilities.flags = gpu_capabilities.flags; + return capabilities; +} + +// static media::VideoDecodeAccelerator::SupportedProfiles GpuVideoAcceleratorUtil::ConvertGpuToMediaDecodeProfiles(const gpu::VideoDecodeAcceleratorSupportedProfiles& gpu_profiles) { @@ -47,6 +58,17 @@ } // static +gpu::VideoDecodeAcceleratorCapabilities +GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeCapabilities( + const media::VideoDecodeAccelerator::Capabilities& media_capabilities) { + gpu::VideoDecodeAcceleratorCapabilities capabilities; + capabilities.supported_profiles = + ConvertMediaToGpuDecodeProfiles(media_capabilities.supported_profiles); + capabilities.flags = media_capabilities.flags; + return capabilities; +} + +// static gpu::VideoDecodeAcceleratorSupportedProfiles GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeProfiles(const media::VideoDecodeAccelerator::SupportedProfiles& media_profiles) {
diff --git a/content/common/gpu/media/gpu_video_accelerator_util.h b/content/common/gpu/media/gpu_video_accelerator_util.h index 28b11d1..e39034e1 100644 --- a/content/common/gpu/media/gpu_video_accelerator_util.h +++ b/content/common/gpu/media/gpu_video_accelerator_util.h
@@ -15,11 +15,21 @@ class GpuVideoAcceleratorUtil { public: + // Convert decoder gpu capabilities to media capabilities. + static media::VideoDecodeAccelerator::Capabilities + ConvertGpuToMediaDecodeCapabilities( + const gpu::VideoDecodeAcceleratorCapabilities& gpu_capabilities); + // Convert decoder gpu profiles to media profiles. static media::VideoDecodeAccelerator::SupportedProfiles ConvertGpuToMediaDecodeProfiles(const gpu::VideoDecodeAcceleratorSupportedProfiles& gpu_profiles); + // Convert decoder media capabilities to gpu capabilities. + static gpu::VideoDecodeAcceleratorCapabilities + ConvertMediaToGpuDecodeCapabilities( + const media::VideoDecodeAccelerator::Capabilities& media_capabilities); + // Convert decoder media profiles to gpu profiles. static gpu::VideoDecodeAcceleratorSupportedProfiles ConvertMediaToGpuDecodeProfiles(const
diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc index 2dfe85b..23c19f1 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.cc +++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc
@@ -46,8 +46,6 @@ #elif defined(USE_OZONE) #include "media/ozone/media_ozone_platform.h" #elif defined(OS_ANDROID) -#include "content/common/gpu/media/android_copying_backing_strategy.h" -#include "content/common/gpu/media/android_deferred_rendering_backing_strategy.h" #include "content/common/gpu/media/android_video_decode_accelerator.h" #endif @@ -152,36 +150,42 @@ } // static -gpu::VideoDecodeAcceleratorSupportedProfiles -GpuVideoDecodeAccelerator::GetSupportedProfiles() { - media::VideoDecodeAccelerator::SupportedProfiles profiles; +gpu::VideoDecodeAcceleratorCapabilities +GpuVideoDecodeAccelerator::GetCapabilities() { + media::VideoDecodeAccelerator::Capabilities capabilities; const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) - return gpu::VideoDecodeAcceleratorSupportedProfiles(); + return gpu::VideoDecodeAcceleratorCapabilities(); // Query supported profiles for each VDA. The order of querying VDAs should // be the same as the order of initializing VDAs. Then the returned profile // can be initialized by corresponding VDA successfully. #if defined(OS_WIN) - profiles = DXVAVideoDecodeAccelerator::GetSupportedProfiles(); + capabilities.supported_profiles = + DXVAVideoDecodeAccelerator::GetSupportedProfiles(); #elif defined(OS_CHROMEOS) media::VideoDecodeAccelerator::SupportedProfiles vda_profiles; #if defined(USE_V4L2_CODEC) vda_profiles = V4L2VideoDecodeAccelerator::GetSupportedProfiles(); - GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles(vda_profiles, &profiles); + GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( + vda_profiles, &capabilities.supported_profiles); vda_profiles = V4L2SliceVideoDecodeAccelerator::GetSupportedProfiles(); - GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles(vda_profiles, &profiles); + GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( + vda_profiles, &capabilities.supported_profiles); #endif #if defined(ARCH_CPU_X86_FAMILY) vda_profiles = VaapiVideoDecodeAccelerator::GetSupportedProfiles(); - GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles(vda_profiles, &profiles); + GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( + vda_profiles, &capabilities.supported_profiles); #endif #elif defined(OS_MACOSX) - profiles = VTVideoDecodeAccelerator::GetSupportedProfiles(); + capabilities.supported_profiles = + VTVideoDecodeAccelerator::GetSupportedProfiles(); #elif defined(OS_ANDROID) - profiles = AndroidVideoDecodeAccelerator::GetSupportedProfiles(); + capabilities = AndroidVideoDecodeAccelerator::GetCapabilities(); #endif - return GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeProfiles(profiles); + return GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeCapabilities( + capabilities); } bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message& msg) { @@ -473,15 +477,8 @@ GpuVideoDecodeAccelerator::CreateAndroidVDA() { scoped_ptr<media::VideoDecodeAccelerator> decoder; #if defined(OS_ANDROID) - decoder.reset(new AndroidVideoDecodeAccelerator( - stub_->decoder()->AsWeakPtr(), make_context_current_, - make_scoped_ptr( -#if defined(ENABLE_MEDIA_PIPELINE_ON_ANDROID) - new AndroidDeferredRenderingBackingStrategy() -#else - new AndroidCopyingBackingStrategy() -#endif - ))); + decoder.reset(new AndroidVideoDecodeAccelerator(stub_->decoder()->AsWeakPtr(), + make_context_current_)); #endif return decoder.Pass(); }
diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.h b/content/common/gpu/media/gpu_video_decode_accelerator.h index b6fba877..ea599f7 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.h +++ b/content/common/gpu/media/gpu_video_decode_accelerator.h
@@ -38,10 +38,10 @@ GpuCommandBufferStub* stub, const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner); - // Static query for supported profiles. This query calls the appropriate - // platform-specific version. The returned supported profiles vector will - // not contain duplicates. - static gpu::VideoDecodeAcceleratorSupportedProfiles GetSupportedProfiles(); + // Static query for the capabilities, which includes the supported profiles. + // This query calls the appropriate platform-specific version. The returned + // capabilities will not contain duplicate supported profile entries. + static gpu::VideoDecodeAcceleratorCapabilities GetCapabilities(); // IPC::Listener implementation. bool OnMessageReceived(const IPC::Message& message) override;
diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 4e803add..d708336 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi
@@ -635,6 +635,8 @@ 'browser/download/download_manager_impl.h', 'browser/download/download_net_log_parameters.cc', 'browser/download/download_net_log_parameters.h', + 'browser/download/download_request_core.cc', + 'browser/download/download_request_core.h', 'browser/download/download_request_handle.cc', 'browser/download/download_request_handle.h', 'browser/download/download_resource_handler.cc', @@ -665,6 +667,8 @@ 'browser/download/save_package.h', 'browser/download/save_types.cc', 'browser/download/save_types.h', + 'browser/download/url_downloader.cc', + 'browser/download/url_downloader.h', 'browser/fileapi/blob_storage_host.cc', 'browser/fileapi/blob_storage_host.h', 'browser/fileapi/browser_file_system_helper.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi index ef88817..516d4b9 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi
@@ -926,6 +926,7 @@ '../storage/storage_browser.gyp:storage', '../storage/storage_common.gyp:storage_common', '../third_party/WebKit/public/blink.gyp:blink', + '../third_party/mojo/mojo_edk.gyp:mojo_common_test_support', '../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', '../ui/compositor/compositor.gyp:compositor_test_support', '../ui/surface/surface.gyp:surface',
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc index 973b0a0..f34d82e 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc
@@ -364,8 +364,8 @@ gpu_info.sandboxed = Sandbox::SandboxIsCurrentlyActive(); #endif - gpu_info.video_decode_accelerator_supported_profiles = - content::GpuVideoDecodeAccelerator::GetSupportedProfiles(); + gpu_info.video_decode_accelerator_capabilities = + content::GpuVideoDecodeAccelerator::GetCapabilities(); gpu_info.video_encode_accelerator_supported_profiles = content::GpuVideoEncodeAccelerator::GetSupportedProfiles(); gpu_info.jpeg_decode_accelerator_supported =
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc index 0cb39a4..c67cb5d2 100644 --- a/content/public/test/render_view_test.cc +++ b/content/public/test/render_view_test.cc
@@ -276,6 +276,12 @@ #if !defined(OS_IOS) InitializeMojo(); + if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { + test_io_thread_.reset(new base::TestIOThread( + base::TestIOThread::kAutoStart)); + ipc_support_.reset(new mojo::test::ScopedIPCSupport( + test_io_thread_->task_runner())); + } #endif // This needs to pass the mock render thread to the view. @@ -317,6 +323,9 @@ platform_.reset(); params_.reset(); command_line_.reset(); + + test_io_thread_.reset(); + ipc_support_.reset(); } void RenderViewTest::onLeakDetectionComplete(const Result& result) {
diff --git a/content/public/test/render_view_test.h b/content/public/test/render_view_test.h index 205ce21..4d6f133 100644 --- a/content/public/test/render_view_test.h +++ b/content/public/test/render_view_test.h
@@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string16.h" +#include "base/test/test_io_thread.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/common/main_function_params.h" #include "content/public/common/page_state.h" @@ -19,6 +20,7 @@ #include "third_party/WebKit/public/platform/Platform.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebLeakDetector.h" +#include "third_party/mojo/src/mojo/edk/test/scoped_ipc_support.h" struct ViewMsg_Resize_Params; @@ -194,6 +196,10 @@ scoped_ptr<MainFunctionParams> params_; scoped_ptr<base::CommandLine> command_line_; + // For Mojo. + scoped_ptr<base::TestIOThread> test_io_thread_; + scoped_ptr<mojo::test::ScopedIPCSupport> ipc_support_; + #if defined(OS_MACOSX) scoped_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool_; #endif
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 6c2b12ba..74aae79 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -405,16 +405,14 @@ // low end, so always use default policy. bool use_low_memory_policy = base::SysInfo::IsLowEndDevice() && !using_synchronous_compositor; + // RGBA_4444 textures are only enabled by default for low end devices + // and are disabled for Android WebView as it doesn't support the format. + settings.renderer_settings.use_rgba_4444_textures = use_low_memory_policy; if (use_low_memory_policy) { // On low-end we want to be very carefull about killing other // apps. So initially we use 50% more memory to avoid flickering // or raster-on-demand. settings.max_memory_for_prepaint_percentage = 67; - - // RGBA_4444 textures are only enabled by default for low end devices - // and are disabled for Android WebView as it doesn't support the format. - if (!cmd->HasSwitch(switches::kDisableRGBA4444Textures)) - settings.renderer_settings.preferred_tile_format = cc::RGBA_4444; } else { // On other devices we have increased memory excessively to avoid // raster-on-demand already, so now we reserve 50% _only_ to avoid @@ -450,14 +448,10 @@ if (cmd->HasSwitch(cc::switches::kEnableBeginFrameScheduling)) settings.use_external_begin_frame_source = true; - if (cmd->HasSwitch(switches::kEnableRGBA4444Textures) && - !cmd->HasSwitch(switches::kDisableRGBA4444Textures)) { - settings.renderer_settings.preferred_tile_format = cc::RGBA_4444; - } - - if (cmd->HasSwitch(cc::switches::kEnableTileCompression)) { - settings.renderer_settings.preferred_tile_format = cc::ETC1; - } + settings.renderer_settings.use_rgba_4444_textures |= + cmd->HasSwitch(switches::kEnableRGBA4444Textures); + settings.renderer_settings.use_rgba_4444_textures &= + !cmd->HasSwitch(switches::kDisableRGBA4444Textures); if (widget_->for_oopif()) { // TODO(simonhong): Apply BeginFrame scheduling for OOPIF.
diff --git a/content/renderer/media/media_recorder_handler.cc b/content/renderer/media/media_recorder_handler.cc index 158f3df..dec9cbc 100644 --- a/content/renderer/media/media_recorder_handler.cc +++ b/content/renderer/media/media_recorder_handler.cc
@@ -10,6 +10,7 @@ #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "content/renderer/media/audio_track_recorder.h" +#include "content/renderer/media/media_stream_track.h" #include "content/renderer/media/video_track_recorder.h" #include "content/renderer/media/webrtc_uma_histograms.h" #include "media/audio/audio_parameters.h" @@ -113,9 +114,15 @@ return false; } + // We cannot add ourselves as sink to a remote audio track, see + // http://crbug.com/121673 and MediaStreamAudioSink::AddToAudioTrack(); + const bool use_audio_tracks = !audio_tracks.isEmpty() && + MediaStreamTrack::GetTrack(audio_tracks[0]) && + MediaStreamTrack::GetTrack(audio_tracks[0])->is_local_track(); + webm_muxer_.reset(new media::WebmMuxer( use_vp9_ ? media::kCodecVP9 : media::kCodecVP8, - video_tracks.size() > 0, audio_tracks.size() > 0, + video_tracks.size() > 0, use_audio_tracks, base::Bind(&MediaRecorderHandler::WriteData, weak_factory_.GetWeakPtr()))); @@ -137,7 +144,7 @@ new VideoTrackRecorder(use_vp9_, video_track, on_encoded_video_cb)); } - if (!audio_tracks.isEmpty()) { + if (use_audio_tracks) { // TODO(ajose): The muxer API supports only one audio track. Extend it to // several tracks. LOG_IF(WARNING, audio_tracks.size() > 1u)
diff --git a/content/renderer/media/media_stream_renderer_factory_impl.cc b/content/renderer/media/media_stream_renderer_factory_impl.cc index 929519e..3163953 100644 --- a/content/renderer/media/media_stream_renderer_factory_impl.cc +++ b/content/renderer/media/media_stream_renderer_factory_impl.cc
@@ -141,7 +141,15 @@ // and mixes audio from all the tracks that belong to the media stream. // For now, we have separate renderers depending on if the first audio track // in the stream is local or remote. - if (MediaStreamTrack::GetTrack(audio_tracks[0])->is_local_track()) { + MediaStreamTrack* audio_track = MediaStreamTrack::GetTrack(audio_tracks[0]); + if (!audio_track) { + // This can happen if the track was cloned. + // TODO(tommi, perkj): Fix cloning of tracks to handle extra data too. + LOG(ERROR) << "No native track for WebMediaStreamTrack."; + return nullptr; + } + + if (audio_track->is_local_track()) { // TODO(xians): Add support for the case where the media stream contains // multiple audio tracks. return CreateLocalAudioRenderer(audio_tracks[0], render_frame_id, device_id,
diff --git a/content/renderer/media/renderer_gpu_video_accelerator_factories.cc b/content/renderer/media/renderer_gpu_video_accelerator_factories.cc index 934771c..8c403c6c 100644 --- a/content/renderer/media/renderer_gpu_video_accelerator_factories.cc +++ b/content/renderer/media/renderer_gpu_video_accelerator_factories.cc
@@ -246,12 +246,10 @@ return task_runner_; } -media::VideoDecodeAccelerator::SupportedProfiles -RendererGpuVideoAcceleratorFactories:: - GetVideoDecodeAcceleratorSupportedProfiles() { - return GpuVideoAcceleratorUtil::ConvertGpuToMediaDecodeProfiles( - gpu_channel_host_->gpu_info() - .video_decode_accelerator_supported_profiles); +media::VideoDecodeAccelerator::Capabilities +RendererGpuVideoAcceleratorFactories::GetVideoDecodeAcceleratorCapabilities() { + return GpuVideoAcceleratorUtil::ConvertGpuToMediaDecodeCapabilities( + gpu_channel_host_->gpu_info().video_decode_accelerator_capabilities); } media::VideoEncodeAccelerator::SupportedProfiles
diff --git a/content/renderer/media/renderer_gpu_video_accelerator_factories.h b/content/renderer/media/renderer_gpu_video_accelerator_factories.h index dea8c20..5f68546 100644 --- a/content/renderer/media/renderer_gpu_video_accelerator_factories.h +++ b/content/renderer/media/renderer_gpu_video_accelerator_factories.h
@@ -81,8 +81,8 @@ scoped_ptr<base::SharedMemory> CreateSharedMemory(size_t size) override; scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override; - std::vector<media::VideoDecodeAccelerator::SupportedProfile> - GetVideoDecodeAcceleratorSupportedProfiles() override; + media::VideoDecodeAccelerator::Capabilities + GetVideoDecodeAcceleratorCapabilities() override; std::vector<media::VideoEncodeAccelerator::SupportedProfile> GetVideoEncodeAcceleratorSupportedProfiles() override;
diff --git a/content/renderer/media/rtc_video_decoder.cc b/content/renderer/media/rtc_video_decoder.cc index 1f43ba24..86ac8729 100644 --- a/content/renderer/media/rtc_video_decoder.cc +++ b/content/renderer/media/rtc_video_decoder.cc
@@ -652,10 +652,10 @@ bool RTCVideoDecoder::IsProfileSupported(media::VideoCodecProfile profile) { DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); - media::VideoDecodeAccelerator::SupportedProfiles supported_profiles = - factories_->GetVideoDecodeAcceleratorSupportedProfiles(); + media::VideoDecodeAccelerator::Capabilities capabilities = + factories_->GetVideoDecodeAcceleratorCapabilities(); - for (const auto& supported_profile : supported_profiles) { + for (const auto& supported_profile : capabilities.supported_profiles) { if (profile == supported_profile.profile) { min_resolution_ = supported_profile.min_resolution; max_resolution_ = supported_profile.max_resolution;
diff --git a/content/renderer/media/rtc_video_decoder_unittest.cc b/content/renderer/media/rtc_video_decoder_unittest.cc index 49cb982..1ce30d8 100644 --- a/content/renderer/media/rtc_video_decoder_unittest.cc +++ b/content/renderer/media/rtc_video_decoder_unittest.cc
@@ -43,15 +43,15 @@ supported_profile.min_resolution.SetSize(16, 16); supported_profile.max_resolution.SetSize(1920, 1088); supported_profile.profile = media::H264PROFILE_MAIN; - supported_profiles_.push_back(supported_profile); + capabilities_.supported_profiles.push_back(supported_profile); supported_profile.profile = media::VP8PROFILE_ANY; - supported_profiles_.push_back(supported_profile); + capabilities_.supported_profiles.push_back(supported_profile); EXPECT_CALL(*mock_gpu_factories_.get(), GetTaskRunner()) .WillRepeatedly(Return(vda_task_runner_)); EXPECT_CALL(*mock_gpu_factories_.get(), - GetVideoDecodeAcceleratorSupportedProfiles()) - .WillRepeatedly(Return(supported_profiles_)); + GetVideoDecodeAcceleratorCapabilities()) + .WillRepeatedly(Return(capabilities_)); EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoDecodeAccelerator()) .WillRepeatedly(Return(mock_vda_)); EXPECT_CALL(*mock_vda_, Initialize(_, _)) @@ -112,7 +112,7 @@ scoped_ptr<RTCVideoDecoder> rtc_decoder_; webrtc::VideoCodec codec_; base::Thread vda_thread_; - media::VideoDecodeAccelerator::SupportedProfiles supported_profiles_; + media::VideoDecodeAccelerator::Capabilities capabilities_; private: scoped_refptr<base::SingleThreadTaskRunner> vda_task_runner_;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 32828ed..2c47593 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -96,6 +96,7 @@ "//mojo/environment:chromium", "//storage/browser", "//storage/common", + "//third_party/mojo/src/mojo/edk/test:test_support", "//ui/compositor:test_support", "//ui/surface", "//v8", @@ -245,6 +246,7 @@ "//components/test_runner:test_runner", "//content/public/common", "//skia", + "//third_party/mojo/src/mojo/edk/test:test_support", "//ui/accessibility:ax_gen", "//v8", ]
diff --git a/content/test/ct/run_ct_dm.py b/content/test/ct/run_ct_dm.py deleted file mode 100755 index 3b1e57e..0000000 --- a/content/test/ct/run_ct_dm.py +++ /dev/null
@@ -1,47 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""This script is meant to be run on a Swarming bot.""" - -import argparse -import os -import subprocess -import sys - - -PARENT_DIR = os.path.dirname(os.path.realpath(__file__)) - -REPOS_BASE_DIR = os.path.normpath(os.path.join( - PARENT_DIR, os.pardir, os.pardir, os.pardir, os.pardir)) - -SKIA_SRC_DIR = os.path.join(REPOS_BASE_DIR, 'skia') - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--slave_num', required=True, type=int, - help='The slave num of this CT run.') - args = parser.parse_args() - - dm_path = os.path.join(SKIA_SRC_DIR, 'out', 'Debug', 'dm') - skps_dir = os.path.join(REPOS_BASE_DIR, 'skps', 'slave%d' % args.slave_num) - resource_path = os.path.join(SKIA_SRC_DIR, 'resources') - - # TODO(rmistry): Double check the below DM configuration with mtklein@. We - # need a basic configuration that can help catch bugs like the ones listed in - # skbug.com/4416 - cmd = [ - dm_path, - '--src', 'skp', - '--skps', skps_dir, - '--resourcePath', resource_path, - '--config', '8888', - '--verbose', - ] - return subprocess.call(cmd) - - -if __name__ == '__main__': - sys.exit(main())
diff --git a/content/test/ct/run_ct_skps.py b/content/test/ct/run_ct_skps.py new file mode 100755 index 0000000..f4234cf5 --- /dev/null +++ b/content/test/ct/run_ct_skps.py
@@ -0,0 +1,70 @@ +#!/usr/bin/env python +# Copyright (c) 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""This script is meant to be run on a Swarming bot.""" + +import argparse +import os +import subprocess +import sys + + +PARENT_DIR = os.path.dirname(os.path.realpath(__file__)) + +REPOS_BASE_DIR = os.path.normpath(os.path.join( + PARENT_DIR, os.pardir, os.pardir, os.pardir, os.pardir)) + +SKIA_SRC_DIR = os.path.join(REPOS_BASE_DIR, 'skia') + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--slave_num', required=True, type=int, + help='The slave num of this CT run.') + parser.add_argument('-t', '--tool', required=True, + choices=['dm', 'nanobench'], + help='The tool to run on the SKPs.') + parser.add_argument('-g', '--git_hash', required=True, + help='The Skia hash the tool was built at.') + parser.add_argument('-i', '--isolated_outdir', required=True, + help='Swarming will automatically upload to ' + 'isolateserver all artifacts in this dir.') + args = parser.parse_args() + + tool_path = os.path.join(SKIA_SRC_DIR, 'out', 'Debug', args.tool) + skps_dir = os.path.join(REPOS_BASE_DIR, 'skps', 'slave%d' % args.slave_num) + resource_path = os.path.join(SKIA_SRC_DIR, 'resources') + + cmd = [tool_path] + if args.tool == 'dm': + # Add DM specific arguments. + cmd.extend([ + '--src', 'skp', + '--skps', skps_dir, + '--resourcePath', resource_path, + '--config', '8888', + '--verbose', + ]) + elif args.tool == 'nanobench': + # Add Nanobench specific arguments. + out_results_file = os.path.join( + args.isolated_outdir, 'nanobench_%s_slave%d.json' % (args.git_hash, + args.slave_num)) + cmd.extend([ + '--skps', skps_dir, + '--resourcePath', resource_path, + '--config', '8888', + '--outResultsFile', out_results_file, + '--properties', 'gitHash', args.git_hash, + '--key', 'arch', 'x86_64', 'compiler', 'GCC', 'cpu_or_gpu', 'CPU', + 'cpu_or_gpu_value', 'AVX2', 'model', 'SWARM', 'os', 'Ubuntu', + '--verbose', + ]) + + return subprocess.call(cmd) + + +if __name__ == '__main__': + sys.exit(main())
diff --git a/content/test/run_all_unittests.cc b/content/test/run_all_unittests.cc index d4fbd200..c9b257a1 100644 --- a/content/test/run_all_unittests.cc +++ b/content/test/run_all_unittests.cc
@@ -3,10 +3,13 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/command_line.h" #include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_io_thread.h" #include "content/app/mojo/mojo_init.h" #include "content/public/test/unittest_test_suite.h" #include "content/test/content_test_suite.h" +#include "third_party/mojo/src/mojo/edk/test/scoped_ipc_support.h" #if defined(OS_ANDROID) #include "base/android/jni_android.h" @@ -23,6 +26,12 @@ new content::ContentTestSuite(argc, argv)); #if !defined(OS_IOS) content::InitializeMojo(); + base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart); + scoped_ptr<mojo::test::ScopedIPCSupport> ipc_support; + if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { + ipc_support.reset(new mojo::test::ScopedIPCSupport( + test_io_thread.task_runner())); + } #endif return base::LaunchUnitTests(
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc index 5367f4d1..8b547fa 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.cc +++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -509,156 +509,67 @@ return bytes_per_element * elements_per_group; } -bool GLES2Util::ComputeImageRowSizeHelper( - int width, uint32 bytes_per_group, int alignment, - uint32* rt_unpadded_row_size, uint32* rt_padded_row_size) { - DCHECK(alignment == 1 || alignment == 2 || - alignment == 4 || alignment == 8); +bool GLES2Util::ComputeImagePaddedRowSize( + int width, int format, int type, int unpack_alignment, + uint32* padded_row_size) { + DCHECK(unpack_alignment == 1 || unpack_alignment == 2 || + unpack_alignment == 4 || unpack_alignment == 8); + uint32 bytes_per_group = ComputeImageGroupSize(format, type); uint32 unpadded_row_size; if (!SafeMultiplyUint32(width, bytes_per_group, &unpadded_row_size)) { return false; } uint32 temp; - if (!SafeAddUint32(unpadded_row_size, alignment - 1, &temp)) { - return false; + if (!SafeAddUint32(unpadded_row_size, unpack_alignment - 1, &temp)) { + return false; } - uint32 padded_row_size = (temp / alignment) * alignment; - if (rt_unpadded_row_size) - *rt_unpadded_row_size = unpadded_row_size; - if (rt_padded_row_size) - *rt_padded_row_size = padded_row_size; + *padded_row_size = (temp / unpack_alignment) * unpack_alignment; return true; } -bool GLES2Util::ComputeImagePaddedRowSize( - int width, int format, int type, int alignment, uint32* padded_row_size) { - uint32 bytes_per_group = ComputeImageGroupSize(format, type); - return ComputeImageRowSizeHelper( - width, bytes_per_group, alignment, nullptr, padded_row_size); -} - // Returns the amount of data glTexImage*D or glTexSubImage*D will access. bool GLES2Util::ComputeImageDataSizes( int width, int height, int depth, int format, int type, - int alignment, uint32* size, uint32* opt_unpadded_row_size, - uint32* opt_padded_row_size) { - DCHECK(width >= 0 && height >= 0 && height >=0); - if (width == 0 || height == 0 || depth == 0) { - *size = 0; - return true; - } - + int unpack_alignment, uint32* size, uint32* ret_unpadded_row_size, + uint32* ret_padded_row_size) { + DCHECK(unpack_alignment == 1 || unpack_alignment == 2 || + unpack_alignment == 4 || unpack_alignment == 8); uint32 bytes_per_group = ComputeImageGroupSize(format, type); - - uint32 unpadded_row_size; - uint32 padded_row_size; - if (!ComputeImageRowSizeHelper(width, bytes_per_group, alignment, - &unpadded_row_size, &padded_row_size)) { + uint32 row_size; + if (!SafeMultiplyUint32(width, bytes_per_group, &row_size)) { return false; } uint32 num_of_rows; if (!SafeMultiplyUint32(height, depth, &num_of_rows)) { return false; } - DCHECK(num_of_rows > 0); - uint32 size_of_all_but_last_row; - if (!SafeMultiplyUint32((num_of_rows - 1), padded_row_size, - &size_of_all_but_last_row)) { - return false; - } - if (!SafeAddUint32(size_of_all_but_last_row, unpadded_row_size, size)) { - return false; - } - if (opt_padded_row_size) { - if (num_of_rows > 1) - *opt_padded_row_size = padded_row_size; - else - *opt_padded_row_size = unpadded_row_size; - } - if (opt_unpadded_row_size) { - *opt_unpadded_row_size = unpadded_row_size; - } - - return true; -} - -bool GLES2Util::ComputeImageDataSizesES3( - int width, int height, int depth, int format, int type, - const PixelStoreParams& params, - uint32_t* size, uint32_t* opt_unpadded_row_size, - uint32_t* opt_padded_row_size, uint32_t* opt_skip_size) { - DCHECK(width >= 0 && height >= 0 && height >=0); - if (width == 0 || height == 0 || depth == 0) { - *size = 0; - return true; - } - - uint32 bytes_per_group = ComputeImageGroupSize(format, type); - - uint32 unpadded_row_size; - uint32 padded_row_size; - if (!ComputeImageRowSizeHelper(width, bytes_per_group, params.alignment, - &unpadded_row_size, &padded_row_size)) { - return false; - } - if (params.row_length > 0 && - !ComputeImageRowSizeHelper(params.row_length, bytes_per_group, - params.alignment, nullptr, &padded_row_size)) { - // Here we re-compute the padded_row_size, but the unpadded_row_size - // isn't affected. That is, the last row isn't affected by ROW_LENGTH. - return false; - } - - int image_height = params.image_height > 0 ? params.image_height : height; - uint32 num_of_rows; - if (!SafeMultiplyUint32(image_height, depth - 1, &num_of_rows) || - !SafeAddUint32(num_of_rows, height, &num_of_rows)) { - return false; - } - DCHECK(num_of_rows > 0); - uint32 size_of_all_but_last_row; - if (!SafeMultiplyUint32((num_of_rows - 1), padded_row_size, - &size_of_all_but_last_row)) { - return false; - } - if (!SafeAddUint32(size_of_all_but_last_row, unpadded_row_size, size)) { - return false; - } - - uint32 skip_size = 0; - if (params.skip_images > 0) { - uint32 image_size; - if (!SafeMultiplyUint32(image_height, padded_row_size, &image_size)) - return false; - if (!SafeMultiplyUint32(image_size, params.skip_images, &skip_size)) - return false; - } - if (params.skip_rows > 0) { + if (num_of_rows > 1) { uint32 temp; - if (!SafeMultiplyUint32(padded_row_size, params.skip_rows, &temp)) + if (!SafeAddUint32(row_size, unpack_alignment - 1, &temp)) { return false; - if (!SafeAddUint32(skip_size, temp, &skip_size)) + } + uint32 padded_row_size = (temp / unpack_alignment) * unpack_alignment; + uint32 size_of_all_but_last_row; + if (!SafeMultiplyUint32((num_of_rows - 1), padded_row_size, + &size_of_all_but_last_row)) { return false; + } + if (!SafeAddUint32(size_of_all_but_last_row, row_size, size)) { + return false; + } + if (ret_padded_row_size) { + *ret_padded_row_size = padded_row_size; + } + } else { + *size = row_size; + if (ret_padded_row_size) { + *ret_padded_row_size = row_size; + } } - if (params.skip_pixels > 0) { - uint32 temp; - if (!SafeMultiplyUint32(bytes_per_group, params.skip_pixels, &temp)) - return false; - if (!SafeAddUint32(skip_size, temp, &skip_size)) - return false; + if (ret_unpadded_row_size) { + *ret_unpadded_row_size = row_size; } - uint32 total_size; - if (!SafeAddUint32(*size, skip_size, &total_size)) - return false; - if (opt_padded_row_size) { - *opt_padded_row_size = padded_row_size; - } - if (opt_unpadded_row_size) { - *opt_unpadded_row_size = unpadded_row_size; - } - if (opt_skip_size) - *opt_skip_size = skip_size; return true; }
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h index e3b82b8..3aa0299 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.h +++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -52,24 +52,6 @@ return checked.IsValid(); } -struct GLES2_UTILS_EXPORT PixelStoreParams { - PixelStoreParams() - : alignment(4), - row_length(0), - image_height(0), - skip_pixels(0), - skip_rows(0), - skip_images(0) { - } - - int32_t alignment; - int32_t row_length; - int32_t image_height; - int32_t skip_pixels; - int32_t skip_rows; - int32_t skip_images; -}; - // Utilties for GLES2 support. class GLES2_UTILS_EXPORT GLES2Util { public: @@ -123,26 +105,17 @@ // Computes the size of an image row including alignment padding static bool ComputeImagePaddedRowSize( - int width, int format, int type, int alignment, + int width, int format, int type, int unpack_alignment, uint32_t* padded_row_size); // Computes the size of image data for TexImage2D and TexSubImage2D. - // Optionally the unpadded and padded row sizes can be returned. + // Optionally the unpadded and padded row sizes can be returned. If height < 2 + // then the padded_row_size will be the same as the unpadded_row_size since + // padding is not necessary. static bool ComputeImageDataSizes( int width, int height, int depth, int format, int type, - int alignment, uint32_t* size, uint32_t* opt_unpadded_row_size, - uint32_t* opt_padded_row_size); - - // Similar to the above function, but taking into consideration all ES3 - // pixel pack/unpack parameters. - // Optionally the skipped bytes in the beginning can be returned. - // Note the returned |size| does NOT include |skip_size|. - // TODO(zmo): merging ComputeImageDataSize and ComputeImageDataSizeES3. - static bool ComputeImageDataSizesES3( - int width, int height, int depth, int format, int type, - const PixelStoreParams& params, - uint32_t* size, uint32_t* opt_unpadded_row_size, - uint32_t* opt_padded_row_size, uint32_t* opt_skip_size); + int unpack_alignment, uint32_t* size, uint32_t* unpadded_row_size, + uint32_t* padded_row_size); static size_t RenderbufferBytesPerPixel(int format); @@ -219,10 +192,6 @@ static std::string GetQualifiedEnumString( const EnumToString* table, size_t count, uint32_t value); - static bool ComputeImageRowSizeHelper( - int width, uint32 bytes_per_group, int alignment, - uint32* rt_unpadded_row_size, uint32* rt_padded_row_size); - static const EnumToString* const enum_to_string_table_; static const size_t enum_to_string_table_len_;
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc b/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc index a435f28a..3eb2a38 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc +++ b/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc
@@ -320,123 +320,6 @@ EXPECT_EQ(kWidth * 3 + 7, padded_row_size); } -TEST_F(GLES2UtilTest, ComputeImageDataSizePixelStoreParams) { - const uint32_t kWidth = 3; - const uint32_t kHeight = 3; - const uint32_t kDepth = 3; - uint32_t size; - uint32_t unpadded_row_size; - uint32_t padded_row_size; - uint32_t skip_size; - - { // Default - PixelStoreParams params; - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGB, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(kWidth * 3, unpadded_row_size); - EXPECT_EQ(kWidth * 3 + 3, padded_row_size); - EXPECT_EQ(padded_row_size * (kHeight * kDepth - 1) + unpadded_row_size, - size); - EXPECT_EQ(0u, skip_size); - } - - { // row_length > width - PixelStoreParams params; - params.row_length = kWidth + 2; - uint32_t kPadding = 1; // 5 * 3 = 15 -> 16 - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGB, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(static_cast<uint32_t>(kWidth * 3), unpadded_row_size); - EXPECT_EQ(static_cast<uint32_t>(params.row_length * 3 + kPadding), - padded_row_size); - EXPECT_EQ(padded_row_size * (kHeight * kDepth - 1) + unpadded_row_size, - size); - EXPECT_EQ(0u, skip_size); - } - - { // row_length < width - PixelStoreParams params; - params.row_length = kWidth - 1; - uint32_t kPadding = 2; // 2 * 3 = 6 -> 8 - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGB, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(static_cast<uint32_t>(kWidth * 3), unpadded_row_size); - EXPECT_EQ(static_cast<uint32_t>(params.row_length * 3 + kPadding), - padded_row_size); - EXPECT_EQ(padded_row_size * (kHeight * kDepth - 1) + unpadded_row_size, - size); - EXPECT_EQ(0u, skip_size); - } - - { // image_height > height - PixelStoreParams params; - params.image_height = kHeight + 1; - uint32_t kPadding = 3; // 3 * 3 = 9 -> 21 - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGB, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(kWidth * 3, unpadded_row_size); - EXPECT_EQ(kWidth * 3 + kPadding, padded_row_size); - EXPECT_EQ((params.image_height * (kDepth - 1) + kHeight - 1) * - padded_row_size + unpadded_row_size, size); - EXPECT_EQ(0u, skip_size); - } - - { // image_height < height - PixelStoreParams params; - params.image_height = kHeight - 1; - uint32_t kPadding = 3; // 3 * 3 = 9 -> 12 - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGB, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(kWidth * 3, unpadded_row_size); - EXPECT_EQ(kWidth * 3 + kPadding, padded_row_size); - EXPECT_EQ((params.image_height * (kDepth - 1) + kHeight - 1) * - padded_row_size + unpadded_row_size, size); - EXPECT_EQ(0u, skip_size); - } - - { // skip_pixels, skip_rows, skip_images, alignment = 4, RGB - PixelStoreParams params; - params.skip_pixels = 1; - params.skip_rows = 10; - params.skip_images = 2; - uint32_t kPadding = 3; // 3 * 3 = 9 -> 12 - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGB, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(kWidth * 3, unpadded_row_size); - EXPECT_EQ(kWidth * 3 + kPadding, padded_row_size); - EXPECT_EQ(padded_row_size * kHeight * params.skip_images + - padded_row_size * params.skip_rows + 3 * params.skip_pixels, - skip_size); - EXPECT_EQ(padded_row_size * (kWidth * kDepth - 1) + unpadded_row_size, - size); - } - - { // skip_pixels, skip_rows, skip_images, alignment = 8, RGBA - PixelStoreParams params; - params.skip_pixels = 1; - params.skip_rows = 10; - params.skip_images = 2; - params.alignment = 8; - uint32_t kPadding = 4; // 3 * 4 = 12 -> 16 - EXPECT_TRUE(GLES2Util::ComputeImageDataSizesES3( - kWidth, kHeight, kDepth, GL_RGBA, GL_UNSIGNED_BYTE, params, - &size, &unpadded_row_size, &padded_row_size, &skip_size)); - EXPECT_EQ(kWidth * 4, unpadded_row_size); - EXPECT_EQ(kWidth * 4 + kPadding, padded_row_size); - EXPECT_EQ(padded_row_size * kHeight * params.skip_images + - padded_row_size * params.skip_rows + 4 * params.skip_pixels, - skip_size); - EXPECT_EQ(padded_row_size * (kWidth * kDepth - 1) + unpadded_row_size, - size); - } -} - TEST_F(GLES2UtilTest, RenderbufferBytesPerPixel) { EXPECT_EQ(1u, GLES2Util::RenderbufferBytesPerPixel(GL_STENCIL_INDEX8)); EXPECT_EQ(2u, GLES2Util::RenderbufferBytesPerPixel(GL_RGBA4));
diff --git a/gpu/command_buffer/service/context_state.cc b/gpu/command_buffer/service/context_state.cc index 4cbf2941..b93a372 100644 --- a/gpu/command_buffer/service/context_state.cc +++ b/gpu/command_buffer/service/context_state.cc
@@ -594,28 +594,6 @@ } } -PixelStoreParams ContextState::GetPackParams() { - PixelStoreParams params; - params.alignment = pack_alignment; - params.row_length = pack_row_length; - params.skip_pixels = pack_skip_pixels; - params.skip_rows = pack_skip_rows; - return params; -} - -PixelStoreParams ContextState::GetUnpackParams(Dimension dimension) { - PixelStoreParams params; - params.alignment = unpack_alignment; - params.row_length = unpack_row_length; - params.skip_pixels = unpack_skip_pixels; - params.skip_rows = unpack_skip_rows; - if (dimension == k3D) { - params.image_height = unpack_image_height; - params.skip_images = unpack_skip_images; - } - return params; -} - // Include the auto-generated part of this file. We split this because it means // we can easily edit the non-auto generated parts right here in this file // instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/context_state.h b/gpu/command_buffer/service/context_state.h index e1fa4a4..5f154c2 100644 --- a/gpu/command_buffer/service/context_state.h +++ b/gpu/command_buffer/service/context_state.h
@@ -138,11 +138,6 @@ GPU_EXPORT void Vec4::SetValues<GLuint>(const GLuint* values); struct GPU_EXPORT ContextState { - enum Dimension { - k2D, - k3D - }; - ContextState(FeatureInfo* feature_info, ErrorStateClient* error_state_client, Logger* logger); @@ -225,9 +220,6 @@ void UnbindTexture(TextureRef* texture); void UnbindSampler(Sampler* sampler); - PixelStoreParams GetPackParams(); - PixelStoreParams GetUnpackParams(Dimension dimension); - #include "gpu/command_buffer/service/context_state_autogen.h" EnableFlags enable_flags;
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc index 23d5216..3a753e5 100644 --- a/gpu/config/gpu_info.cc +++ b/gpu/config/gpu_info.cc
@@ -46,6 +46,11 @@ namespace gpu { +VideoDecodeAcceleratorCapabilities::VideoDecodeAcceleratorCapabilities() + : flags(0) {} + +VideoDecodeAcceleratorCapabilities::~VideoDecodeAcceleratorCapabilities() {} + GPUInfo::GPUDevice::GPUDevice() : vendor_id(0), device_id(0), @@ -114,8 +119,7 @@ CollectInfoResult dx_diagnostics_info_state; DxDiagNode dx_diagnostics; #endif - VideoDecodeAcceleratorSupportedProfiles - video_decode_accelerator_supported_profiles; + VideoDecodeAcceleratorCapabilities video_decode_accelerator_capabilities; VideoEncodeAcceleratorSupportedProfiles video_encode_accelerator_supported_profiles; bool jpeg_decode_accelerator_supported; @@ -175,7 +179,10 @@ enumerator->AddInt("DxDiagnosticsInfoState", dx_diagnostics_info_state); #endif // TODO(kbr): add dx_diagnostics on Windows. - for (const auto& profile : video_decode_accelerator_supported_profiles) + enumerator->AddInt("videoDecodeAcceleratorFlags", + video_decode_accelerator_capabilities.flags); + for (const auto& profile : + video_decode_accelerator_capabilities.supported_profiles) EnumerateVideoDecodeAcceleratorSupportedProfile(profile, enumerator); for (const auto& profile : video_encode_accelerator_supported_profiles) EnumerateVideoEncodeAcceleratorSupportedProfile(profile, enumerator);
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h index d6f61fd..d803cc39 100644 --- a/gpu/config/gpu_info.h +++ b/gpu/config/gpu_info.h
@@ -60,9 +60,17 @@ gfx::Size max_resolution; gfx::Size min_resolution; }; + using VideoDecodeAcceleratorSupportedProfiles = std::vector<VideoDecodeAcceleratorSupportedProfile>; +struct GPU_EXPORT VideoDecodeAcceleratorCapabilities { + VideoDecodeAcceleratorCapabilities(); + ~VideoDecodeAcceleratorCapabilities(); + VideoDecodeAcceleratorSupportedProfiles supported_profiles; + uint32_t flags; +}; + // Specification of an encoding profile supported by a hardware encoder. struct GPU_EXPORT VideoEncodeAcceleratorSupportedProfile { VideoCodecProfile profile; @@ -220,8 +228,7 @@ DxDiagNode dx_diagnostics; #endif - VideoDecodeAcceleratorSupportedProfiles - video_decode_accelerator_supported_profiles; + VideoDecodeAcceleratorCapabilities video_decode_accelerator_capabilities; VideoEncodeAcceleratorSupportedProfiles video_encode_accelerator_supported_profiles; bool jpeg_decode_accelerator_supported;
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc index 20630c6..1709da1 100644 --- a/gpu/config/gpu_info_collector.cc +++ b/gpu/config/gpu_info_collector.cc
@@ -202,8 +202,8 @@ basic_gpu_info->in_process_gpu = context_gpu_info.in_process_gpu; basic_gpu_info->context_info_state = context_gpu_info.context_info_state; basic_gpu_info->initialization_time = context_gpu_info.initialization_time; - basic_gpu_info->video_decode_accelerator_supported_profiles = - context_gpu_info.video_decode_accelerator_supported_profiles; + basic_gpu_info->video_decode_accelerator_capabilities = + context_gpu_info.video_decode_accelerator_capabilities; basic_gpu_info->video_encode_accelerator_supported_profiles = context_gpu_info.video_encode_accelerator_supported_profiles; basic_gpu_info->jpeg_decode_accelerator_supported =
diff --git a/gpu/config/gpu_info_unittest.cc b/gpu/config/gpu_info_unittest.cc index 21dd4ff..6e09cb3 100644 --- a/gpu/config/gpu_info_unittest.cc +++ b/gpu/config/gpu_info_unittest.cc
@@ -33,7 +33,10 @@ #if defined(OS_WIN) EXPECT_EQ(gpu_info.dx_diagnostics_info_state, kCollectInfoNone); #endif - EXPECT_EQ(gpu_info.video_decode_accelerator_supported_profiles.size(), 0u); + EXPECT_EQ(gpu_info.video_decode_accelerator_capabilities.flags, 0u); + EXPECT_EQ( + gpu_info.video_decode_accelerator_capabilities.supported_profiles.size(), + 0u); EXPECT_EQ(gpu_info.video_encode_accelerator_supported_profiles.size(), 0u); }
diff --git a/chrome/app/theme/default_100_percent/common/infobar_autologin.png b/ios/chrome/app/theme/default_100_percent/infobar_autologin.png similarity index 100% rename from chrome/app/theme/default_100_percent/common/infobar_autologin.png rename to ios/chrome/app/theme/default_100_percent/infobar_autologin.png Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/infobar_autologin.png b/ios/chrome/app/theme/default_200_percent/infobar_autologin.png similarity index 100% rename from chrome/app/theme/default_200_percent/common/infobar_autologin.png rename to ios/chrome/app/theme/default_200_percent/infobar_autologin.png Binary files differ
diff --git a/ios/chrome/app/theme/ios_theme_resources.grd b/ios/chrome/app/theme/ios_theme_resources.grd index 7cb092d..11d9a15 100644 --- a/ios/chrome/app/theme/ios_theme_resources.grd +++ b/ios/chrome/app/theme/ios_theme_resources.grd
@@ -25,7 +25,8 @@ <!-- KEEP THESE IN ALPHABETICAL ORDER! DO NOT ADD TO RANDOM PLACES JUST BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE SAME CONDITIONALS. --> - <structure type="chrome_scaled_image" name="IDR_INFOBAR_TRANSLATE_IOS" file="infobar_translate.png" /> + <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_TRANSLATE" file="infobar_translate.png" /> + <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_AUTOLOGIN" file="infobar_autologin.png" /> </structures> </release> </grit>
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn index c16f90f..e096b28 100644 --- a/ios/chrome/browser/BUILD.gn +++ b/ios/chrome/browser/BUILD.gn
@@ -68,6 +68,12 @@ "browser_state/browser_state_keyed_service_factories.mm", "browser_state/browser_state_otr_helper.cc", "browser_state/browser_state_otr_helper.h", + "browser_state/chrome_browser_state_impl_io_data.cc", + "browser_state/chrome_browser_state_impl_io_data.h", + "browser_state/chrome_browser_state_io_data.cc", + "browser_state/chrome_browser_state_io_data.h", + "browser_state/off_the_record_chrome_browser_state_io_data.h", + "browser_state/off_the_record_chrome_browser_state_io_data.mm", "browser_state_metrics/browser_state_metrics.cc", "browser_state_metrics/browser_state_metrics.h", "browsing_data_change_listening.h", @@ -227,6 +233,8 @@ "net/ios_chrome_http_user_agent_settings.h", "net/ios_chrome_network_delegate.cc", "net/ios_chrome_network_delegate.h", + "net/ios_chrome_url_request_context_getter.cc", + "net/ios_chrome_url_request_context_getter.h", "net/metrics_network_client.h", "net/metrics_network_client.mm", "net/metrics_network_client_manager.h", @@ -455,6 +463,7 @@ "//base", "//base:prefs", "//breakpad:client", + "//components/about_handler", "//components/autofill/core/browser", "//components/autofill/core/common", "//components/autofill/ios/browser", @@ -464,6 +473,7 @@ "//components/browser_sync/common", "//components/component_updater", "//components/content_settings/core/browser", + "//components/cookie_config", "//components/crash/core/browser", "//components/crash/core/common", "//components/data_reduction_proxy/core/common",
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS index c7e2072..20716e4 100644 --- a/ios/chrome/browser/DEPS +++ b/ios/chrome/browser/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+breakpad/src/client/ios", "+breakpad/src/common", + "+components/about_handler", "+components/autofill/core/browser", "+components/autofill/core/common", "+components/autofill/ios/browser", @@ -9,6 +10,7 @@ "+components/browser_sync/common", "+components/component_updater", "+components/content_settings/core", + "+components/cookie_config", "+components/crash/core/browser", "+components/crash/core/common", "+components/data_reduction_proxy/core/browser",
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.cc b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.cc new file mode 100644 index 0000000..c0096116 --- /dev/null +++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.cc
@@ -0,0 +1,412 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h" + +#include <set> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/json_pref_store.h" +#include "base/prefs/pref_filter.h" +#include "base/prefs/pref_service.h" +#include "base/sequenced_task_runner.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/worker_pool.h" +#include "components/cookie_config/cookie_store_util.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h" +#include "components/data_reduction_proxy/core/browser/data_store_impl.h" +#include "components/domain_reliability/monitor.h" +#include "components/net_log/chrome_net_log.h" +#include "ios/chrome/browser/application_context.h" +#include "ios/chrome/browser/chrome_constants.h" +#include "ios/chrome/browser/data_reduction_proxy/ios_chrome_data_reduction_proxy_io_data.h" +#include "ios/chrome/browser/data_reduction_proxy/ios_chrome_data_reduction_proxy_settings.h" +#include "ios/chrome/browser/data_reduction_proxy/ios_chrome_data_reduction_proxy_settings_factory.h" +#include "ios/chrome/browser/ios_chrome_io_thread.h" +#include "ios/chrome/browser/net/cookie_util.h" +#include "ios/chrome/browser/net/http_server_properties_manager_factory.h" +#include "ios/chrome/browser/net/ios_chrome_network_delegate.h" +#include "ios/chrome/browser/net/ios_chrome_url_request_context_getter.h" +#include "ios/chrome/browser/pref_names.h" +#include "ios/net/cookies/cookie_store_ios.h" +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/web/public/web_thread.h" +#include "net/base/cache_type.h" +#include "net/base/sdch_manager.h" +#include "net/extras/sqlite/sqlite_channel_id_store.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_session.h" +#include "net/http/http_server_properties_manager.h" +#include "net/sdch/sdch_owner.h" +#include "net/ssl/channel_id_service.h" +#include "net/ssl/default_channel_id_store.h" +#include "net/url_request/url_request_intercepting_job_factory.h" +#include "net/url_request/url_request_job_factory_impl.h" + +ChromeBrowserStateImplIOData::Handle::Handle( + ios::ChromeBrowserState* browser_state) + : io_data_(new ChromeBrowserStateImplIOData), + browser_state_(browser_state), + initialized_(false) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + DCHECK(browser_state); +} + +ChromeBrowserStateImplIOData::Handle::~Handle() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + if (io_data_->http_server_properties_manager_) + io_data_->http_server_properties_manager_->ShutdownOnPrefThread(); + + // io_data_->data_reduction_proxy_io_data() might be NULL if Init() was + // never called. + if (io_data_->data_reduction_proxy_io_data()) + io_data_->data_reduction_proxy_io_data()->ShutdownOnUIThread(); + + io_data_->ShutdownOnUIThread(GetAllContextGetters().Pass()); +} + +void ChromeBrowserStateImplIOData::Handle::Init( + const base::FilePath& cookie_path, + const base::FilePath& channel_id_path, + const base::FilePath& cache_path, + int cache_max_size, + const base::FilePath& profile_path) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + DCHECK(!io_data_->lazy_params_); + + LazyParams* lazy_params = new LazyParams(); + + lazy_params->cookie_path = cookie_path; + lazy_params->channel_id_path = channel_id_path; + lazy_params->cache_path = cache_path; + lazy_params->cache_max_size = cache_max_size; + io_data_->lazy_params_.reset(lazy_params); + + // Keep track of profile path and cache sizes separately so we can use them + // on demand when creating storage isolated URLRequestContextGetters. + io_data_->profile_path_ = profile_path; + io_data_->app_cache_max_size_ = cache_max_size; + + io_data_->InitializeMetricsEnabledStateOnUIThread(); + + // TODO(tbansal): Move this to IO thread once the data reduction proxy + // params are unified into a single object. + bool enable_quic_for_data_reduction_proxy = + IOSChromeIOThread::ShouldEnableQuicForDataReductionProxy(); + + io_data_->set_data_reduction_proxy_io_data( + CreateIOSChromeDataReductionProxyIOData( + GetApplicationContext()->GetIOSChromeIOThread()->net_log(), + browser_state_->GetPrefs(), + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO), + web::WebThread::GetTaskRunnerForThread(web::WebThread::UI), + enable_quic_for_data_reduction_proxy) + .Pass()); + + base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); + scoped_refptr<base::SequencedTaskRunner> db_task_runner = + pool->GetSequencedTaskRunnerWithShutdownBehavior( + pool->GetSequenceToken(), + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); + scoped_ptr<data_reduction_proxy::DataStore> store( + new data_reduction_proxy::DataStoreImpl(profile_path)); + IOSChromeDataReductionProxySettingsFactory::GetForBrowserState(browser_state_) + ->InitDataReductionProxySettings( + io_data_->data_reduction_proxy_io_data(), browser_state_->GetPrefs(), + browser_state_->GetRequestContext(), store.Pass(), + web::WebThread::GetTaskRunnerForThread(web::WebThread::UI), + db_task_runner); +} + +scoped_refptr<IOSChromeURLRequestContextGetter> +ChromeBrowserStateImplIOData::Handle::CreateMainRequestContextGetter( + ProtocolHandlerMap* protocol_handlers, + PrefService* local_state, + IOSChromeIOThread* io_thread) const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + LazyInitialize(); + DCHECK(!main_request_context_getter_.get()); + main_request_context_getter_ = + IOSChromeURLRequestContextGetter::Create(io_data_, protocol_handlers); + + return main_request_context_getter_; +} + +scoped_refptr<IOSChromeURLRequestContextGetter> +ChromeBrowserStateImplIOData::Handle::CreateIsolatedAppRequestContextGetter( + const base::FilePath& partition_path) const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + // Check that the partition_path is not the same as the base profile path. We + // expect isolated partition, which will never go to the default profile path. + CHECK(partition_path != browser_state_->GetStatePath()); + LazyInitialize(); + + // Keep a map of request context getters, one per requested storage partition. + IOSChromeURLRequestContextGetterMap::iterator iter = + app_request_context_getter_map_.find(partition_path); + if (iter != app_request_context_getter_map_.end()) + return iter->second; + + IOSChromeURLRequestContextGetter* context = + IOSChromeURLRequestContextGetter::CreateForIsolatedApp( + browser_state_->GetRequestContext(), io_data_, partition_path); + app_request_context_getter_map_[partition_path] = context; + return context; +} + +ChromeBrowserStateIOData* ChromeBrowserStateImplIOData::Handle::io_data() + const { + LazyInitialize(); + return io_data_; +} + +void ChromeBrowserStateImplIOData::Handle::ClearNetworkingHistorySince( + base::Time time, + const base::Closure& completion) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + LazyInitialize(); + + web::WebThread::PostTask( + web::WebThread::IO, FROM_HERE, + base::Bind( + &ChromeBrowserStateImplIOData::ClearNetworkingHistorySinceOnIOThread, + base::Unretained(io_data_), time, completion)); +} + +void ChromeBrowserStateImplIOData::Handle::LazyInitialize() const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + if (initialized_) + return; + + // Set initialized_ to true at the beginning in case any of the objects + // below try to get the ResourceContext pointer. + initialized_ = true; + PrefService* pref_service = browser_state_->GetPrefs(); + io_data_->http_server_properties_manager_ = + HttpServerPropertiesManagerFactory::CreateManager(pref_service); + io_data_->set_http_server_properties(scoped_ptr<net::HttpServerProperties>( + io_data_->http_server_properties_manager_)); + io_data_->safe_browsing_enabled()->Init(prefs::kSafeBrowsingEnabled, + pref_service); + io_data_->safe_browsing_enabled()->MoveToThread( + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)); + io_data_->InitializeOnUIThread(browser_state_); +} + +scoped_ptr<ChromeBrowserStateIOData::IOSChromeURLRequestContextGetterVector> +ChromeBrowserStateImplIOData::Handle::GetAllContextGetters() { + IOSChromeURLRequestContextGetterMap::iterator iter; + scoped_ptr<IOSChromeURLRequestContextGetterVector> context_getters( + new IOSChromeURLRequestContextGetterVector()); + + iter = app_request_context_getter_map_.begin(); + for (; iter != app_request_context_getter_map_.end(); ++iter) + context_getters->push_back(iter->second); + + if (main_request_context_getter_.get()) + context_getters->push_back(main_request_context_getter_); + + return context_getters.Pass(); +} + +ChromeBrowserStateImplIOData::LazyParams::LazyParams() : cache_max_size(0) {} + +ChromeBrowserStateImplIOData::LazyParams::~LazyParams() {} + +ChromeBrowserStateImplIOData::ChromeBrowserStateImplIOData() + : ChromeBrowserStateIOData( + ios::ChromeBrowserStateType::REGULAR_BROWSER_STATE), + http_server_properties_manager_(nullptr), + app_cache_max_size_(0) {} + +ChromeBrowserStateImplIOData::~ChromeBrowserStateImplIOData() {} + +void ChromeBrowserStateImplIOData::InitializeInternal( + scoped_ptr<IOSChromeNetworkDelegate> chrome_network_delegate, + ProfileParams* profile_params, + ProtocolHandlerMap* protocol_handlers) const { + // Set up a persistent store for use by the network stack on the IO thread. + base::FilePath network_json_store_filepath( + profile_path_.Append(kIOSChromeNetworkPersistentStateFilename)); + network_json_store_ = new JsonPrefStore( + network_json_store_filepath, + JsonPrefStore::GetTaskRunnerForFile(network_json_store_filepath, + web::WebThread::GetBlockingPool()), + scoped_ptr<PrefFilter>()); + network_json_store_->ReadPrefsAsync(nullptr); + + net::URLRequestContext* main_context = main_request_context(); + + IOSChromeIOThread* const io_thread = profile_params->io_thread; + IOSChromeIOThread::Globals* const io_thread_globals = io_thread->globals(); + + ApplyProfileParamsToContext(main_context); + + if (http_server_properties_manager_) + http_server_properties_manager_->InitializeOnNetworkThread(); + + main_context->set_transport_security_state(transport_security_state()); + + main_context->set_net_log(io_thread->net_log()); + + network_delegate_ = + data_reduction_proxy_io_data() + ->CreateNetworkDelegate(chrome_network_delegate.Pass(), true) + .Pass(); + + main_context->set_network_delegate(network_delegate_.get()); + + main_context->set_http_server_properties(http_server_properties()); + + main_context->set_host_resolver(io_thread_globals->host_resolver.get()); + + main_context->set_http_auth_handler_factory( + io_thread_globals->http_auth_handler_factory.get()); + + main_context->set_proxy_service(proxy_service()); + main_context->set_backoff_manager( + io_thread_globals->url_request_backoff_manager.get()); + + scoped_refptr<net::CookieStore> cookie_store = NULL; + net::ChannelIDService* channel_id_service = NULL; + + // Set up cookie store. + if (!cookie_store.get()) { + DCHECK(!lazy_params_->cookie_path.empty()); + cookie_util::CookieStoreConfig ios_cookie_config( + lazy_params_->cookie_path, + cookie_util::CookieStoreConfig::RESTORED_SESSION_COOKIES, + cookie_util::CookieStoreConfig::COOKIE_STORE_IOS, + cookie_config::GetCookieCryptoDelegate()); + cookie_store = cookie_util::CreateCookieStore(ios_cookie_config); + + if (profile_params->path.BaseName().value() == + kIOSChromeInitialBrowserState) { + // Enable metrics on the default profile, not secondary profiles. + static_cast<net::CookieStoreIOS*>(cookie_store.get()) + ->SetMetricsEnabled(); + } + } + + main_context->set_cookie_store(cookie_store.get()); + + // Set up server bound cert service. + if (!channel_id_service) { + DCHECK(!lazy_params_->channel_id_path.empty()); + + scoped_refptr<net::SQLiteChannelIDStore> channel_id_db = + new net::SQLiteChannelIDStore( + lazy_params_->channel_id_path, + web::WebThread::GetBlockingPool()->GetSequencedTaskRunner( + web::WebThread::GetBlockingPool()->GetSequenceToken())); + channel_id_service = new net::ChannelIDService( + new net::DefaultChannelIDStore(channel_id_db.get()), + base::WorkerPool::GetTaskRunner(true)); + } + + set_channel_id_service(channel_id_service); + main_context->set_channel_id_service(channel_id_service); + + scoped_ptr<net::HttpCache::BackendFactory> main_backend( + new net::HttpCache::DefaultBackend( + net::DISK_CACHE, net::CACHE_BACKEND_BLOCKFILE, + lazy_params_->cache_path, lazy_params_->cache_max_size, + web::WebThread::GetTaskRunnerForThread(web::WebThread::CACHE))); + http_network_session_ = CreateHttpNetworkSession(*profile_params); + main_http_factory_ = + CreateMainHttpFactory(http_network_session_.get(), main_backend.Pass()); + main_context->set_http_transaction_factory(main_http_factory_.get()); + + scoped_ptr<net::URLRequestJobFactoryImpl> main_job_factory( + new net::URLRequestJobFactoryImpl()); + InstallProtocolHandlers(main_job_factory.get(), protocol_handlers); + + // The data reduction proxy interceptor should be as close to the network as + // possible. + URLRequestInterceptorScopedVector request_interceptors; + request_interceptors.insert( + request_interceptors.begin(), + data_reduction_proxy_io_data()->CreateInterceptor().release()); + main_job_factory_ = SetUpJobFactoryDefaults(main_job_factory.Pass(), + request_interceptors.Pass(), + main_context->network_delegate()); + main_context->set_job_factory(main_job_factory_.get()); + main_context->set_network_quality_estimator( + io_thread_globals->network_quality_estimator.get()); + + // Setup SDCH for this profile. + sdch_manager_.reset(new net::SdchManager); + sdch_policy_.reset(new net::SdchOwner(sdch_manager_.get(), main_context)); + main_context->set_sdch_manager(sdch_manager_.get()); + sdch_policy_->EnablePersistentStorage(network_json_store_.get()); + + lazy_params_.reset(); +} + +net::URLRequestContext* +ChromeBrowserStateImplIOData::InitializeAppRequestContext( + net::URLRequestContext* main_context) const { + // Copy most state from the main context. + AppRequestContext* context = new AppRequestContext(); + context->CopyFrom(main_context); + + // Use a separate HTTP disk cache for isolated apps. + scoped_ptr<net::HttpCache::BackendFactory> app_backend = + net::HttpCache::DefaultBackend::InMemory(0); + scoped_ptr<net::HttpCache> app_http_cache = + CreateHttpFactory(http_network_session_.get(), app_backend.Pass()); + + cookie_util::CookieStoreConfig ios_cookie_config( + base::FilePath(), + cookie_util::CookieStoreConfig::EPHEMERAL_SESSION_COOKIES, + cookie_util::CookieStoreConfig::COOKIE_STORE_IOS, nullptr); + scoped_refptr<net::CookieStore> cookie_store = + cookie_util::CreateCookieStore(ios_cookie_config); + + // Transfer ownership of the cookies and cache to AppRequestContext. + context->SetCookieStore(cookie_store.get()); + context->SetHttpTransactionFactory(app_http_cache.Pass()); + + scoped_ptr<net::URLRequestJobFactoryImpl> job_factory( + new net::URLRequestJobFactoryImpl()); + // The data reduction proxy interceptor should be as close to the network as + // possible. + URLRequestInterceptorScopedVector request_interceptors; + request_interceptors.insert( + request_interceptors.begin(), + data_reduction_proxy_io_data()->CreateInterceptor().release()); + scoped_ptr<net::URLRequestJobFactory> top_job_factory( + SetUpJobFactoryDefaults(job_factory.Pass(), request_interceptors.Pass(), + main_context->network_delegate())); + context->SetJobFactory(top_job_factory.Pass()); + + return context; +} + +net::URLRequestContext* +ChromeBrowserStateImplIOData::AcquireIsolatedAppRequestContext( + net::URLRequestContext* main_context) const { + // We create per-app contexts on demand, unlike the others above. + net::URLRequestContext* app_request_context = + InitializeAppRequestContext(main_context); + DCHECK(app_request_context); + return app_request_context; +} + +void ChromeBrowserStateImplIOData::ClearNetworkingHistorySinceOnIOThread( + base::Time time, + const base::Closure& completion) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + DCHECK(initialized()); + + DCHECK(transport_security_state()); + // Completes synchronously. + transport_security_state()->DeleteAllDynamicDataSince(time); + DCHECK(http_server_properties_manager_); + http_server_properties_manager_->Clear(completion); +}
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h new file mode 100644 index 0000000..e374a43 --- /dev/null +++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
@@ -0,0 +1,161 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IMPL_IO_DATA_H_ +#define IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IMPL_IO_DATA_H_ + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "base/prefs/pref_store.h" +#include "ios/chrome/browser/browser_state/chrome_browser_state_io_data.h" +#include "ios/chrome/browser/net/net_types.h" + +class JsonPrefStore; + +namespace data_reduction_proxy { +class DataReductionProxyNetworkDelegate; +} + +namespace ios { +class ChromeBrowserState; +} + +namespace net { +class HttpNetworkSession; +class HttpServerPropertiesManager; +class HttpTransactionFactory; +class SdchManager; +class SdchOwner; +} // namespace net + +class ChromeBrowserStateImplIOData : public ChromeBrowserStateIOData { + public: + class Handle { + public: + explicit Handle(ios::ChromeBrowserState* browser_state); + ~Handle(); + + // Init() must be called before ~Handle(). It records most of the + // parameters needed to construct a ChromeURLRequestContextGetter. + void Init(const base::FilePath& cookie_path, + const base::FilePath& channel_id_path, + const base::FilePath& cache_path, + int cache_max_size, + const base::FilePath& profile_path); + + // These Create*ContextGetter() functions are only exposed because the + // circular relationship between ChromeBrowserState, + // ChromeBrowserStateIOData::Handle, and the + // IOSChromeURLRequestContextGetter factories requires ChromeBrowserState be + // able to call these functions. + scoped_refptr<IOSChromeURLRequestContextGetter> + CreateMainRequestContextGetter(ProtocolHandlerMap* protocol_handlers, + PrefService* local_state, + IOSChromeIOThread* io_thread) const; + scoped_refptr<IOSChromeURLRequestContextGetter> + CreateIsolatedAppRequestContextGetter( + const base::FilePath& partition_path) const; + + ChromeBrowserStateIOData* io_data() const; + + // Deletes all network related data since |time|. It deletes transport + // security state since |time| and also deletes HttpServerProperties data. + // Works asynchronously, however if the |completion| callback is non-null, + // it will be posted on the UI thread once the removal process completes. + void ClearNetworkingHistorySince(base::Time time, + const base::Closure& completion); + + private: + typedef std::map<base::FilePath, + scoped_refptr<IOSChromeURLRequestContextGetter>> + IOSChromeURLRequestContextGetterMap; + + // Lazily initialize ProfileParams. We do this on the calls to + // Get*RequestContextGetter(), so we only initialize ProfileParams right + // before posting a task to the IO thread to start using them. This prevents + // objects that are supposed to be deleted on the IO thread, but are created + // on the UI thread from being unnecessarily initialized. + void LazyInitialize() const; + + // Collect references to context getters in reverse order, i.e. last item + // will be main request getter. This list is passed to |io_data_| + // for invalidation on IO thread. + scoped_ptr<IOSChromeURLRequestContextGetterVector> GetAllContextGetters(); + + // The getters will be invalidated on the IO thread before + // ChromeBrowserStateIOData instance is deleted. + mutable scoped_refptr<IOSChromeURLRequestContextGetter> + main_request_context_getter_; + mutable IOSChromeURLRequestContextGetterMap app_request_context_getter_map_; + ChromeBrowserStateImplIOData* const io_data_; + + ios::ChromeBrowserState* const browser_state_; + + mutable bool initialized_; + + DISALLOW_COPY_AND_ASSIGN(Handle); + }; + + private: + friend class base::RefCountedThreadSafe<ChromeBrowserStateImplIOData>; + + struct LazyParams { + LazyParams(); + ~LazyParams(); + + // All of these parameters are intended to be read on the IO thread. + base::FilePath cookie_path; + base::FilePath channel_id_path; + base::FilePath cache_path; + int cache_max_size; + }; + + ChromeBrowserStateImplIOData(); + ~ChromeBrowserStateImplIOData() override; + + void InitializeInternal( + scoped_ptr<IOSChromeNetworkDelegate> chrome_network_delegate, + ProfileParams* profile_params, + ProtocolHandlerMap* protocol_handlers) const override; + net::URLRequestContext* InitializeAppRequestContext( + net::URLRequestContext* main_context) const override; + net::URLRequestContext* AcquireIsolatedAppRequestContext( + net::URLRequestContext* main_context) const override; + + // Deletes all network related data since |time|. It deletes transport + // security state since |time| and also deletes HttpServerProperties data. + // Works asynchronously, however if the |completion| callback is non-null, + // it will be posted on the UI thread once the removal process completes. + void ClearNetworkingHistorySinceOnIOThread(base::Time time, + const base::Closure& completion); + + mutable scoped_ptr<data_reduction_proxy::DataReductionProxyNetworkDelegate> + network_delegate_; + + // Lazy initialization params. + mutable scoped_ptr<LazyParams> lazy_params_; + + mutable scoped_refptr<JsonPrefStore> network_json_store_; + + mutable scoped_ptr<net::HttpNetworkSession> http_network_session_; + mutable scoped_ptr<net::HttpTransactionFactory> main_http_factory_; + + // Same as |ChromeBrowserState::http_server_properties_|, owned there to + // maintain destruction ordering. + mutable net::HttpServerPropertiesManager* http_server_properties_manager_; + + mutable scoped_ptr<net::URLRequestJobFactory> main_job_factory_; + + mutable scoped_ptr<net::SdchManager> sdch_manager_; + mutable scoped_ptr<net::SdchOwner> sdch_policy_; + + // Parameters needed for isolated apps. + base::FilePath profile_path_; + int app_cache_max_size_; + + DISALLOW_COPY_AND_ASSIGN(ChromeBrowserStateImplIOData); +}; + +#endif // IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IMPL_IO_DATA_H_
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc new file mode 100644 index 0000000..0706b780 --- /dev/null +++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.cc
@@ -0,0 +1,488 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios/chrome/browser/browser_state/chrome_browser_state_io_data.h" + +#include <string> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/debug/alias.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/prefs/pref_service.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/sequenced_worker_pool.h" +#include "components/about_handler/about_protocol_handler.h" +#include "components/content_settings/core/browser/content_settings_provider.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" +#include "components/metrics/metrics_pref_names.h" +#include "components/net_log/chrome_net_log.h" +#include "components/signin/core/common/signin_pref_names.h" +#include "components/sync_driver/pref_names.h" +#include "ios/chrome/browser/application_context.h" +#include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/content_settings/cookie_settings_factory.h" +#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "ios/chrome/browser/ios_chrome_io_thread.h" +#include "ios/chrome/browser/net/ios_chrome_http_user_agent_settings.h" +#include "ios/chrome/browser/net/ios_chrome_network_delegate.h" +#include "ios/chrome/browser/net/ios_chrome_url_request_context_getter.h" +#include "ios/chrome/browser/net/proxy_service_factory.h" +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/web/public/web_thread.h" +#include "net/base/keygen_handler.h" +#include "net/base/network_quality_estimator.h" +#include "net/cert/cert_verifier.h" +#include "net/cert/multi_log_ct_verifier.h" +#include "net/cookies/canonical_cookie.h" +#include "net/http/http_network_session.h" +#include "net/http/http_transaction_factory.h" +#include "net/http/http_util.h" +#include "net/http/transport_security_persister.h" +#include "net/proxy/proxy_config_service_fixed.h" +#include "net/proxy/proxy_script_fetcher_impl.h" +#include "net/proxy/proxy_service.h" +#include "net/ssl/channel_id_service.h" +#include "net/url_request/certificate_report_sender.h" +#include "net/url_request/data_protocol_handler.h" +#include "net/url_request/file_protocol_handler.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_intercepting_job_factory.h" +#include "net/url_request/url_request_interceptor.h" +#include "net/url_request/url_request_job_factory_impl.h" + +namespace { + +// For safe shutdown, must be called before the ChromeBrowserStateIOData is +// destroyed. +void NotifyContextGettersOfShutdownOnIO( + scoped_ptr<ChromeBrowserStateIOData::IOSChromeURLRequestContextGetterVector> + getters) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + ChromeBrowserStateIOData::IOSChromeURLRequestContextGetterVector::iterator + iter; + for (auto& chrome_context_getter : *getters) + chrome_context_getter->NotifyContextShuttingDown(); +} + +} // namespace + +void ChromeBrowserStateIOData::InitializeOnUIThread( + ios::ChromeBrowserState* browser_state) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + PrefService* pref_service = browser_state->GetPrefs(); + scoped_ptr<ProfileParams> params(new ProfileParams); + params->path = browser_state->GetOriginalChromeBrowserState()->GetStatePath(); + + params->io_thread = GetApplicationContext()->GetIOSChromeIOThread(); + + params->cookie_settings = + ios::CookieSettingsFactory::GetForBrowserState(browser_state); + params->host_content_settings_map = + ios::HostContentSettingsMapFactory::GetForBrowserState(browser_state); + params->ssl_config_service = browser_state->GetSSLConfigService(); + + params->proxy_config_service = + ios::ProxyServiceFactory::CreateProxyConfigService( + browser_state->GetProxyConfigTracker()); + + params->browser_state = browser_state; + profile_params_.reset(params.release()); + + IOSChromeNetworkDelegate::InitializePrefsOnUIThread(&enable_do_not_track_, + pref_service); + + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO); + + chrome_http_user_agent_settings_.reset( + new IOSChromeHttpUserAgentSettings(pref_service)); + + // These members are used only for sign in, which is not enabled + // in incognito mode. So no need to initialize them. + if (!IsOffTheRecord()) { + google_services_user_account_id_.Init(prefs::kGoogleServicesUserAccountId, + pref_service); + google_services_user_account_id_.MoveToThread(io_task_runner); + + sync_disabled_.Init(sync_driver::prefs::kSyncManaged, pref_service); + sync_disabled_.MoveToThread(io_task_runner); + + signin_allowed_.Init(prefs::kSigninAllowed, pref_service); + signin_allowed_.MoveToThread(io_task_runner); + } + + initialized_on_UI_thread_ = true; +} + +ChromeBrowserStateIOData::AppRequestContext::AppRequestContext() {} + +void ChromeBrowserStateIOData::AppRequestContext::SetCookieStore( + net::CookieStore* cookie_store) { + cookie_store_ = cookie_store; + set_cookie_store(cookie_store); +} + +void ChromeBrowserStateIOData::AppRequestContext::SetHttpTransactionFactory( + scoped_ptr<net::HttpTransactionFactory> http_factory) { + http_factory_ = http_factory.Pass(); + set_http_transaction_factory(http_factory_.get()); +} + +void ChromeBrowserStateIOData::AppRequestContext::SetJobFactory( + scoped_ptr<net::URLRequestJobFactory> job_factory) { + job_factory_ = job_factory.Pass(); + set_job_factory(job_factory_.get()); +} + +ChromeBrowserStateIOData::AppRequestContext::~AppRequestContext() { + AssertNoURLRequests(); +} + +ChromeBrowserStateIOData::ProfileParams::ProfileParams() + : io_thread(nullptr), browser_state(nullptr) {} + +ChromeBrowserStateIOData::ProfileParams::~ProfileParams() {} + +ChromeBrowserStateIOData::ChromeBrowserStateIOData( + ios::ChromeBrowserStateType browser_state_type) + : initialized_(false), + initialized_on_UI_thread_(false), + browser_state_type_(browser_state_type) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); +} + +ChromeBrowserStateIOData::~ChromeBrowserStateIOData() { + if (web::WebThread::IsMessageLoopValid(web::WebThread::IO)) + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + // Pull the contents of the request context maps onto the stack for sanity + // checking of values in a minidump. http://crbug.com/260425 + size_t num_app_contexts = app_request_context_map_.size(); + size_t current_context = 0; + static const size_t kMaxCachedContexts = 20; + net::URLRequestContext* app_context_cache[kMaxCachedContexts] = {0}; + void* app_context_vtable_cache[kMaxCachedContexts] = {0}; + void* tmp_vtable = nullptr; + base::debug::Alias(&num_app_contexts); + base::debug::Alias(¤t_context); + base::debug::Alias(app_context_cache); + base::debug::Alias(app_context_vtable_cache); + base::debug::Alias(&tmp_vtable); + + current_context = 0; + for (URLRequestContextMap::const_iterator + it = app_request_context_map_.begin(); + current_context < kMaxCachedContexts && + it != app_request_context_map_.end(); + ++it, ++current_context) { + app_context_cache[current_context] = it->second; + memcpy(&app_context_vtable_cache[current_context], + static_cast<void*>(it->second), sizeof(void*)); + } + + // Destroy certificate_report_sender_ before main_request_context_, + // since the former has a reference to the latter. + if (transport_security_state_) + transport_security_state_->SetReportSender(nullptr); + certificate_report_sender_.reset(); + + // TODO(ajwong): These AssertNoURLRequests() calls are unnecessary since they + // are already done in the URLRequestContext destructor. + if (main_request_context_) + main_request_context_->AssertNoURLRequests(); + + current_context = 0; + for (URLRequestContextMap::iterator it = app_request_context_map_.begin(); + it != app_request_context_map_.end(); ++it) { + if (current_context < kMaxCachedContexts) { + CHECK_EQ(app_context_cache[current_context], it->second); + memcpy(&tmp_vtable, static_cast<void*>(it->second), sizeof(void*)); + CHECK_EQ(app_context_vtable_cache[current_context], tmp_vtable); + } + it->second->AssertNoURLRequests(); + delete it->second; + current_context++; + } +} + +// static +bool ChromeBrowserStateIOData::IsHandledProtocol(const std::string& scheme) { + DCHECK_EQ(scheme, base::ToLowerASCII(scheme)); + static const char* const kProtocolList[] = { + url::kFileScheme, kChromeUIScheme, url::kDataScheme, url::kAboutScheme, + }; + for (size_t i = 0; i < arraysize(kProtocolList); ++i) { + if (scheme == kProtocolList[i]) + return true; + } + return net::URLRequest::IsHandledProtocol(scheme); +} + +// static +void ChromeBrowserStateIOData::InstallProtocolHandlers( + net::URLRequestJobFactoryImpl* job_factory, + ProtocolHandlerMap* protocol_handlers) { + for (ProtocolHandlerMap::iterator it = protocol_handlers->begin(); + it != protocol_handlers->end(); ++it) { + bool set_protocol = job_factory->SetProtocolHandler( + it->first, make_scoped_ptr(it->second.release())); + DCHECK(set_protocol); + } + protocol_handlers->clear(); +} + +net::URLRequestContext* ChromeBrowserStateIOData::GetMainRequestContext() + const { + DCHECK(initialized_); + return main_request_context_.get(); +} + +net::URLRequestContext* ChromeBrowserStateIOData::GetIsolatedAppRequestContext( + net::URLRequestContext* main_context, + const base::FilePath& partition_path) const { + DCHECK(initialized_); + net::URLRequestContext* context = nullptr; + if (ContainsKey(app_request_context_map_, partition_path)) { + context = app_request_context_map_[partition_path]; + } else { + context = AcquireIsolatedAppRequestContext(main_context); + app_request_context_map_[partition_path] = context; + } + DCHECK(context); + return context; +} + +content_settings::CookieSettings* ChromeBrowserStateIOData::GetCookieSettings() + const { + DCHECK(initialized_); + return cookie_settings_.get(); +} + +HostContentSettingsMap* ChromeBrowserStateIOData::GetHostContentSettingsMap() + const { + DCHECK(initialized_); + return host_content_settings_map_.get(); +} + +bool ChromeBrowserStateIOData::IsOffTheRecord() const { + return browser_state_type() == + ios::ChromeBrowserStateType::INCOGNITO_BROWSER_STATE; +} + +void ChromeBrowserStateIOData::InitializeMetricsEnabledStateOnUIThread() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + // Prep the PrefMember and send it to the IO thread, since this value will be + // read from there. + enable_metrics_.Init(metrics::prefs::kMetricsReportingEnabled, + GetApplicationContext()->GetLocalState()); + enable_metrics_.MoveToThread( + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)); +} + +bool ChromeBrowserStateIOData::GetMetricsEnabledStateOnIOThread() const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + return enable_metrics_.GetValue(); +} + +bool ChromeBrowserStateIOData::IsDataReductionProxyEnabled() const { + return data_reduction_proxy_io_data() && + data_reduction_proxy_io_data()->IsEnabled(); +} + +void ChromeBrowserStateIOData::set_data_reduction_proxy_io_data( + scoped_ptr<data_reduction_proxy::DataReductionProxyIOData> + data_reduction_proxy_io_data) const { + data_reduction_proxy_io_data_ = data_reduction_proxy_io_data.Pass(); +} + +base::WeakPtr<net::HttpServerProperties> +ChromeBrowserStateIOData::http_server_properties() const { + return http_server_properties_->GetWeakPtr(); +} + +void ChromeBrowserStateIOData::set_http_server_properties( + scoped_ptr<net::HttpServerProperties> http_server_properties) const { + http_server_properties_ = http_server_properties.Pass(); +} + +void ChromeBrowserStateIOData::Init( + ProtocolHandlerMap* protocol_handlers) const { + // The basic logic is implemented here. The specific initialization + // is done in InitializeInternal(), implemented by subtypes. Static helper + // functions have been provided to assist in common operations. + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + DCHECK(!initialized_); + + // TODO(jhawkins): Remove once crbug.com/102004 is fixed. + CHECK(initialized_on_UI_thread_); + + // TODO(jhawkins): Return to DCHECK once crbug.com/102004 is fixed. + CHECK(profile_params_.get()); + + IOSChromeIOThread* const io_thread = profile_params_->io_thread; + IOSChromeIOThread::Globals* const io_thread_globals = io_thread->globals(); + + // Create the common request contexts. + main_request_context_.reset(new net::URLRequestContext()); + + scoped_ptr<IOSChromeNetworkDelegate> network_delegate( + new IOSChromeNetworkDelegate()); + + network_delegate->set_cookie_settings(profile_params_->cookie_settings.get()); + network_delegate->set_enable_do_not_track(&enable_do_not_track_); + + // NOTE: Proxy service uses the default io thread network delegate, not the + // delegate just created. + proxy_service_ = ios::ProxyServiceFactory::CreateProxyService( + io_thread->net_log(), nullptr, + io_thread_globals->system_network_delegate.get(), + profile_params_->proxy_config_service.Pass(), + true /* quick_check_enabled */); + transport_security_state_.reset(new net::TransportSecurityState()); + base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); + transport_security_persister_.reset(new net::TransportSecurityPersister( + transport_security_state_.get(), profile_params_->path, + pool->GetSequencedTaskRunnerWithShutdownBehavior( + pool->GetSequenceToken(), base::SequencedWorkerPool::BLOCK_SHUTDOWN), + IsOffTheRecord())); + + certificate_report_sender_.reset(new net::CertificateReportSender( + main_request_context_.get(), + net::CertificateReportSender::DO_NOT_SEND_COOKIES)); + transport_security_state_->SetReportSender(certificate_report_sender_.get()); + + // Take ownership over these parameters. + cookie_settings_ = profile_params_->cookie_settings; + host_content_settings_map_ = profile_params_->host_content_settings_map; + + main_request_context_->set_cert_verifier( + io_thread_globals->cert_verifier.get()); + + InitializeInternal(network_delegate.Pass(), profile_params_.get(), + protocol_handlers); + + profile_params_.reset(); + initialized_ = true; +} + +void ChromeBrowserStateIOData::ApplyProfileParamsToContext( + net::URLRequestContext* context) const { + context->set_http_user_agent_settings(chrome_http_user_agent_settings_.get()); + context->set_ssl_config_service(profile_params_->ssl_config_service.get()); +} + +scoped_ptr<net::URLRequestJobFactory> +ChromeBrowserStateIOData::SetUpJobFactoryDefaults( + scoped_ptr<net::URLRequestJobFactoryImpl> job_factory, + URLRequestInterceptorScopedVector request_interceptors, + net::NetworkDelegate* network_delegate) const { + // NOTE(willchan): Keep these protocol handlers in sync with + // ChromeBrowserStateIOData::IsHandledProtocol(). + bool set_protocol = job_factory->SetProtocolHandler( + url::kFileScheme, + make_scoped_ptr(new net::FileProtocolHandler( + web::WebThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)))); + DCHECK(set_protocol); + + set_protocol = job_factory->SetProtocolHandler( + url::kDataScheme, make_scoped_ptr(new net::DataProtocolHandler())); + DCHECK(set_protocol); + + job_factory->SetProtocolHandler( + url::kAboutScheme, + make_scoped_ptr(new about_handler::AboutProtocolHandler())); + + // Set up interceptors in the reverse order. + scoped_ptr<net::URLRequestJobFactory> top_job_factory = job_factory.Pass(); + for (URLRequestInterceptorScopedVector::reverse_iterator i = + request_interceptors.rbegin(); + i != request_interceptors.rend(); ++i) { + top_job_factory.reset(new net::URLRequestInterceptingJobFactory( + top_job_factory.Pass(), make_scoped_ptr(*i))); + } + request_interceptors.weak_clear(); + return top_job_factory.Pass(); +} + +void ChromeBrowserStateIOData::ShutdownOnUIThread( + scoped_ptr<IOSChromeURLRequestContextGetterVector> context_getters) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + + google_services_user_account_id_.Destroy(); + enable_referrers_.Destroy(); + enable_do_not_track_.Destroy(); + enable_metrics_.Destroy(); + safe_browsing_enabled_.Destroy(); + sync_disabled_.Destroy(); + signin_allowed_.Destroy(); + if (chrome_http_user_agent_settings_) + chrome_http_user_agent_settings_->CleanupOnUIThread(); + + if (!context_getters->empty()) { + if (web::WebThread::IsMessageLoopValid(web::WebThread::IO)) { + web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, + base::Bind(&NotifyContextGettersOfShutdownOnIO, + base::Passed(&context_getters))); + } + } + + bool posted = web::WebThread::DeleteSoon(web::WebThread::IO, FROM_HERE, this); + if (!posted) + delete this; +} + +void ChromeBrowserStateIOData::set_channel_id_service( + net::ChannelIDService* channel_id_service) const { + channel_id_service_.reset(channel_id_service); +} + +scoped_ptr<net::HttpNetworkSession> +ChromeBrowserStateIOData::CreateHttpNetworkSession( + const ProfileParams& profile_params) const { + net::HttpNetworkSession::Params params; + net::URLRequestContext* context = main_request_context(); + + IOSChromeIOThread* const io_thread = profile_params.io_thread; + + io_thread->InitializeNetworkSessionParams(¶ms); + net::URLRequestContextBuilder::SetHttpNetworkSessionComponents(context, + ¶ms); + if (!IsOffTheRecord()) { + params.socket_performance_watcher_factory = + io_thread->globals()->network_quality_estimator.get(); + } + if (data_reduction_proxy_io_data_.get()) + params.proxy_delegate = data_reduction_proxy_io_data_->proxy_delegate(); + + return scoped_ptr<net::HttpNetworkSession>( + new net::HttpNetworkSession(params)); +} + +scoped_ptr<net::HttpCache> ChromeBrowserStateIOData::CreateMainHttpFactory( + net::HttpNetworkSession* session, + scoped_ptr<net::HttpCache::BackendFactory> main_backend) const { + return scoped_ptr<net::HttpCache>( + new net::HttpCache(session, main_backend.Pass(), true)); +} + +scoped_ptr<net::HttpCache> ChromeBrowserStateIOData::CreateHttpFactory( + net::HttpNetworkSession* shared_session, + scoped_ptr<net::HttpCache::BackendFactory> backend) const { + return scoped_ptr<net::HttpCache>( + new net::HttpCache(shared_session, backend.Pass(), true)); +}
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h new file mode 100644 index 0000000..26f0017 --- /dev/null +++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
@@ -0,0 +1,328 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IO_DATA_H_ +#define IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IO_DATA_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/prefs/pref_member.h" +#include "components/content_settings/core/common/content_settings_types.h" +#include "ios/chrome/browser/ios_chrome_io_thread.h" +#include "ios/chrome/browser/net/net_types.h" +#include "net/cert/ct_verifier.h" +#include "net/cookies/cookie_monster.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_session.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_interceptor.h" +#include "net/url_request/url_request_job_factory.h" + +class HostContentSettingsMap; +class IOSChromeHttpUserAgentSettings; +class IOSChromeNetworkDelegate; +class IOSChromeURLRequestContextGetter; + +namespace content_settings { +class CookieSettings; +} + +namespace data_reduction_proxy { +class DataReductionProxyIOData; +} + +namespace ios { +class ChromeBrowserState; +enum class ChromeBrowserStateType; +} + +namespace net { +class CertificateReportSender; +class CertVerifier; +class ChannelIDService; +class CookieStore; +class HttpServerProperties; +class HttpTransactionFactory; +class ProxyConfigService; +class ProxyService; +class ServerBoundCertService; +class SSLConfigService; +class TransportSecurityPersister; +class TransportSecurityState; +class URLRequestJobFactoryImpl; +} // namespace net + +// Conceptually speaking, the ChromeBrowserStateIOData represents data that +// lives on the IO thread that is owned by a ChromeBrowserState, such as, but +// not limited to, network objects like CookieMonster, HttpTransactionFactory, +// etc. +// ChromeBrowserState owns ChromeBrowserStateIOData, but will make sure to +// delete it on the IO thread. +class ChromeBrowserStateIOData { + public: + typedef std::vector<scoped_refptr<IOSChromeURLRequestContextGetter>> + IOSChromeURLRequestContextGetterVector; + + virtual ~ChromeBrowserStateIOData(); + + // Returns true if |scheme| is handled in Chrome, or by default handlers in + // net::URLRequest. + static bool IsHandledProtocol(const std::string& scheme); + + // Utility to install additional WebUI handlers into the |job_factory|. + // Ownership of the handlers is transfered from |protocol_handlers| + // to the |job_factory|. + static void InstallProtocolHandlers( + net::URLRequestJobFactoryImpl* job_factory, + ProtocolHandlerMap* protocol_handlers); + + // Initializes the ChromeBrowserStateIOData object and primes the + // RequestContext generation. Must be called prior to any of the Get*() + // methods other than GetResouceContext or GetMetricsEnabledStateOnIOThread. + void Init(ProtocolHandlerMap* protocol_handlers) const; + + net::URLRequestContext* GetMainRequestContext() const; + net::URLRequestContext* GetIsolatedAppRequestContext( + net::URLRequestContext* main_context, + const base::FilePath& partition_path) const; + + // These are useful when the Chrome layer is called from the content layer + // with a content::ResourceContext, and they want access to Chrome data for + // that browser state. + content_settings::CookieSettings* GetCookieSettings() const; + HostContentSettingsMap* GetHostContentSettingsMap() const; + + StringPrefMember* google_services_account_id() const { + return &google_services_user_account_id_; + } + + BooleanPrefMember* safe_browsing_enabled() const { + return &safe_browsing_enabled_; + } + + net::TransportSecurityState* transport_security_state() const { + return transport_security_state_.get(); + } + + ios::ChromeBrowserStateType browser_state_type() const { + return browser_state_type_; + } + + bool IsOffTheRecord() const; + + // Initialize the member needed to track the metrics enabled state. This is + // only to be called on the UI thread. + void InitializeMetricsEnabledStateOnUIThread(); + + // Returns whether or not metrics reporting is enabled in the browser instance + // on which this browser state resides. This is safe for use from the IO + // thread, and should only be called from there. + bool GetMetricsEnabledStateOnIOThread() const; + + bool IsDataReductionProxyEnabled() const; + + data_reduction_proxy::DataReductionProxyIOData* data_reduction_proxy_io_data() + const { + return data_reduction_proxy_io_data_.get(); + } + + protected: + // A URLRequestContext for apps that owns its cookie store and HTTP factory, + // to ensure they are deleted. + class AppRequestContext : public net::URLRequestContext { + public: + AppRequestContext(); + + void SetCookieStore(net::CookieStore* cookie_store); + void SetHttpTransactionFactory( + scoped_ptr<net::HttpTransactionFactory> http_factory); + void SetJobFactory(scoped_ptr<net::URLRequestJobFactory> job_factory); + + private: + ~AppRequestContext() override; + + scoped_refptr<net::CookieStore> cookie_store_; + scoped_ptr<net::HttpTransactionFactory> http_factory_; + scoped_ptr<net::URLRequestJobFactory> job_factory_; + }; + + // Created on the UI thread, read on the IO thread during + // ChromeBrowserStateIOData lazy initialization. + struct ProfileParams { + ProfileParams(); + ~ProfileParams(); + + base::FilePath path; + IOSChromeIOThread* io_thread; + scoped_refptr<content_settings::CookieSettings> cookie_settings; + scoped_refptr<HostContentSettingsMap> host_content_settings_map; + scoped_refptr<net::SSLConfigService> ssl_config_service; + scoped_refptr<net::CookieMonsterDelegate> cookie_monster_delegate; + + // We need to initialize the ProxyConfigService from the UI thread + // because on linux it relies on initializing things through gconf, + // and needs to be on the main thread. + scoped_ptr<net::ProxyConfigService> proxy_config_service; + + // The browser state this struct was populated from. It's passed as a void* + // to ensure it's not accidently used on the IO thread. + void* browser_state; + }; + + explicit ChromeBrowserStateIOData( + ios::ChromeBrowserStateType browser_state_type); + + void InitializeOnUIThread(ios::ChromeBrowserState* browser_state); + void ApplyProfileParamsToContext(net::URLRequestContext* context) const; + + scoped_ptr<net::URLRequestJobFactory> SetUpJobFactoryDefaults( + scoped_ptr<net::URLRequestJobFactoryImpl> job_factory, + URLRequestInterceptorScopedVector request_interceptors, + net::NetworkDelegate* network_delegate) const; + + // Called when the ChromeBrowserState is destroyed. |context_getters| must + // include all URLRequestContextGetters that refer to the + // ChromeBrowserStateIOData's URLRequestContexts. Triggers destruction of the + // ChromeBrowserStateIOData and shuts down |context_getters| safely on the IO + // thread. + // TODO(mmenke): Passing all those URLRequestContextGetters around like this + // is really silly. Can we do something cleaner? + void ShutdownOnUIThread( + scoped_ptr<IOSChromeURLRequestContextGetterVector> context_getters); + + // A ChannelIDService object is created by a derived class of + // ChromeBrowserStateIOData, and the derived class calls this method to set + // the channel_id_service_ member and transfers ownership to the base class. + void set_channel_id_service(net::ChannelIDService* channel_id_service) const; + + void set_data_reduction_proxy_io_data( + scoped_ptr<data_reduction_proxy::DataReductionProxyIOData> + data_reduction_proxy_io_data) const; + + net::ProxyService* proxy_service() const { return proxy_service_.get(); } + + base::WeakPtr<net::HttpServerProperties> http_server_properties() const; + + void set_http_server_properties( + scoped_ptr<net::HttpServerProperties> http_server_properties) const; + + net::URLRequestContext* main_request_context() const { + return main_request_context_.get(); + } + + bool initialized() const { return initialized_; } + + scoped_ptr<net::HttpNetworkSession> CreateHttpNetworkSession( + const ProfileParams& profile_params) const; + + // Creates main network transaction factory. + scoped_ptr<net::HttpCache> CreateMainHttpFactory( + net::HttpNetworkSession* session, + scoped_ptr<net::HttpCache::BackendFactory> main_backend) const; + + // Creates network transaction factory. + scoped_ptr<net::HttpCache> CreateHttpFactory( + net::HttpNetworkSession* shared_session, + scoped_ptr<net::HttpCache::BackendFactory> backend) const; + + private: + typedef std::map<base::FilePath, net::URLRequestContext*> + URLRequestContextMap; + + // -------------------------------------------- + // Virtual interface for subtypes to implement: + // -------------------------------------------- + + // Does the actual initialization of the ChromeBrowserStateIOData subtype. + // Subtypes should use the static helper functions above to implement this. + virtual void InitializeInternal( + scoped_ptr<IOSChromeNetworkDelegate> chrome_network_delegate, + ProfileParams* profile_params, + ProtocolHandlerMap* protocol_handlers) const = 0; + + // Does an on-demand initialization of a RequestContext for the given + // isolated app. + virtual net::URLRequestContext* InitializeAppRequestContext( + net::URLRequestContext* main_context) const = 0; + + // These functions are used to transfer ownership of the lazily initialized + // context from ChromeBrowserStateIOData to the URLRequestContextGetter. + virtual net::URLRequestContext* AcquireIsolatedAppRequestContext( + net::URLRequestContext* main_context) const = 0; + + // The order *DOES* matter for the majority of these member variables, so + // don't move them around unless you know what you're doing! + // General rules: + // * ResourceContext references the URLRequestContexts, so + // URLRequestContexts must outlive ResourceContext, hence ResourceContext + // should be destroyed first. + // * URLRequestContexts reference a whole bunch of members, so + // URLRequestContext needs to be destroyed before them. + // * Therefore, ResourceContext should be listed last, and then the + // URLRequestContexts, and then the URLRequestContext members. + // * Note that URLRequestContext members have a directed dependency graph + // too, so they must themselves be ordered correctly. + + // Tracks whether or not we've been lazily initialized. + mutable bool initialized_; + + // Data from the UI thread from the ChromeBrowserState, used to initialize + // ChromeBrowserStateIOData. Deleted after lazy initialization. + mutable scoped_ptr<ProfileParams> profile_params_; + + mutable StringPrefMember google_services_user_account_id_; + + // Member variables which are pointed to by the various context objects. + mutable BooleanPrefMember enable_referrers_; + mutable BooleanPrefMember enable_do_not_track_; + mutable BooleanPrefMember safe_browsing_enabled_; + mutable BooleanPrefMember sync_disabled_; + mutable BooleanPrefMember signin_allowed_; + + BooleanPrefMember enable_metrics_; + + // Pointed to by URLRequestContext. + mutable scoped_ptr<net::ChannelIDService> channel_id_service_; + + mutable scoped_ptr<data_reduction_proxy::DataReductionProxyIOData> + data_reduction_proxy_io_data_; + + mutable scoped_ptr<net::ProxyService> proxy_service_; + mutable scoped_ptr<net::TransportSecurityState> transport_security_state_; + mutable scoped_ptr<net::CTVerifier> cert_transparency_verifier_; + mutable scoped_ptr<net::HttpServerProperties> http_server_properties_; + mutable scoped_ptr<net::TransportSecurityPersister> + transport_security_persister_; + mutable scoped_ptr<net::CertificateReportSender> certificate_report_sender_; + + // These are only valid in between LazyInitialize() and their accessor being + // called. + mutable scoped_ptr<net::URLRequestContext> main_request_context_; + // One URLRequestContext per isolated app for main and media requests. + mutable URLRequestContextMap app_request_context_map_; + + mutable scoped_refptr<content_settings::CookieSettings> cookie_settings_; + + mutable scoped_refptr<HostContentSettingsMap> host_content_settings_map_; + + mutable scoped_ptr<IOSChromeHttpUserAgentSettings> + chrome_http_user_agent_settings_; + + // TODO(jhawkins): Remove once crbug.com/102004 is fixed. + bool initialized_on_UI_thread_; + + const ios::ChromeBrowserStateType browser_state_type_; + + DISALLOW_COPY_AND_ASSIGN(ChromeBrowserStateIOData); +}; + +#endif // IOS_CHROME_BROWSER_BROWSER_STATE_CHROME_BROWSER_STATE_IO_DATA_H_
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h new file mode 100644 index 0000000..3f4dc41 --- /dev/null +++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
@@ -0,0 +1,119 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_BROWSER_STATE_OFF_THE_RECORD_CHROME_BROWSER_STATE_IO_DATA_H_ +#define IOS_CHROME_BROWSER_BROWSER_STATE_OFF_THE_RECORD_CHROME_BROWSER_STATE_IO_DATA_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/containers/hash_tables.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "ios/chrome/browser/browser_state/chrome_browser_state_io_data.h" +#include "ios/chrome/browser/net/net_types.h" + +class IOSChromeURLRequestContextGetter; + +namespace ios { +class ChromeBrowserState; +} + +namespace net { +class HttpNetworkSession; +class HttpTransactionFactory; +class SdchManager; +class SdchOwner; +class URLRequestContext; +} // namespace net + +// OffTheRecordChromeBrowserState owns a +// OffTheRecordChromeBrowserStateIOData::Handle, which holds a reference to the +// OffTheRecordChromeBrowserStateIOData. +// OffTheRecordChromeBrowserStateIOData is intended to own all the objects owned +// by OffTheRecordChromeBrowserState which live on the IO thread, such as, but +// not limited to, network objects like CookieMonster, HttpTransactionFactory, +// etc. +// OffTheRecordChromeBrowserStateIOData is owned by the +// OffTheRecordChromeBrowserState and OffTheRecordChromeBrowserStateIOData's +// IOSChromeURLRequestContexts. When all of them go away, then +// ChromeBrowserStateIOData will be deleted. Note that the +// OffTheRecordChromeBrowserStateIOData will typically outlive the browser state +// it is "owned" by, so it's important for OffTheRecordChromeBrowserStateIOData +// not to hold any references to the browser state beyond what's used by +// LazyParams (which should be deleted after lazy initialization). +class OffTheRecordChromeBrowserStateIOData : public ChromeBrowserStateIOData { + public: + class Handle { + public: + explicit Handle(ios::ChromeBrowserState* browser_state); + ~Handle(); + + scoped_refptr<IOSChromeURLRequestContextGetter> + CreateMainRequestContextGetter(ProtocolHandlerMap* protocol_handlers) const; + + // Clears the HTTP cache associated with the incognito browser state. + void DoomIncognitoCache(); + + ChromeBrowserStateIOData* io_data() const; + + private: + // Lazily initialize ProfileParams. We do this on the calls to + // Get*RequestContextGetter(), so we only initialize ProfileParams right + // before posting a task to the IO thread to start using them. This prevents + // objects that are supposed to be deleted on the IO thread, but are created + // on the UI thread from being unnecessarily initialized. + void LazyInitialize() const; + + // Collect references to context getters in reverse order, i.e. last item + // will be main request getter. This list is passed to |io_data_| + // for invalidation on IO thread. + scoped_ptr<IOSChromeURLRequestContextGetterVector> GetAllContextGetters(); + + // The getters will be invalidated on the IO thread before + // ProfileIOData instance is deleted. + mutable scoped_refptr<IOSChromeURLRequestContextGetter> + main_request_context_getter_; + OffTheRecordChromeBrowserStateIOData* const io_data_; + + ios::ChromeBrowserState* const browser_state_; + + mutable bool initialized_; + + DISALLOW_COPY_AND_ASSIGN(Handle); + }; + + private: + friend class base::RefCountedThreadSafe<OffTheRecordChromeBrowserStateIOData>; + + OffTheRecordChromeBrowserStateIOData(); + ~OffTheRecordChromeBrowserStateIOData() override; + + void InitializeInternal( + scoped_ptr<IOSChromeNetworkDelegate> chrome_network_delegate, + ProfileParams* profile_params, + ProtocolHandlerMap* protocol_handlers) const override; + net::URLRequestContext* InitializeAppRequestContext( + net::URLRequestContext* main_context) const override; + net::URLRequestContext* AcquireIsolatedAppRequestContext( + net::URLRequestContext* main_context) const override; + + mutable scoped_ptr<IOSChromeNetworkDelegate> network_delegate_; + + mutable scoped_ptr<net::HttpNetworkSession> http_network_session_; + mutable scoped_ptr<net::HttpTransactionFactory> main_http_factory_; + + mutable scoped_ptr<net::URLRequestJobFactory> main_job_factory_; + + // Server bound certificates and cookies are persisted to the disk on iOS. + base::FilePath cookie_path_; + base::FilePath channel_id_path_; + + mutable scoped_ptr<net::SdchManager> sdch_manager_; + mutable scoped_ptr<net::SdchOwner> sdch_policy_; + + DISALLOW_COPY_AND_ASSIGN(OffTheRecordChromeBrowserStateIOData); +}; + +#endif // IOS_CHROME_BROWSER_BROWSER_STATE_OFF_THE_RECORD_CHROME_BROWSER_STATE_IO_DATA_H_
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm new file mode 100644 index 0000000..086f950 --- /dev/null +++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm
@@ -0,0 +1,237 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h" + +#import <UIKit/UIkit.h> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/mac/bind_objc_block.h" +#include "base/prefs/pref_service.h" +#include "base/stl_util.h" +#include "base/threading/worker_pool.h" +#include "components/net_log/chrome_net_log.h" +#include "ios/chrome/browser/chrome_constants.h" +#include "ios/chrome/browser/ios_chrome_io_thread.h" +#include "ios/chrome/browser/net/cookie_util.h" +#include "ios/chrome/browser/net/ios_chrome_network_delegate.h" +#include "ios/chrome/browser/net/ios_chrome_url_request_context_getter.h" +#include "ios/chrome/browser/pref_names.h" +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/web/public/web_thread.h" +#include "net/base/sdch_manager.h" +#include "net/disk_cache/disk_cache.h" +#include "net/extras/sqlite/sqlite_channel_id_store.h" +#include "net/ftp/ftp_network_layer.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_session.h" +#include "net/http/http_server_properties_impl.h" +#include "net/sdch/sdch_owner.h" +#include "net/ssl/channel_id_service.h" +#include "net/ssl/default_channel_id_store.h" +#include "net/url_request/url_request_job_factory_impl.h" + +namespace { + +// Callback doing nothing, called by DoomIncognitoCache() below. +void DoNothing(int rv) {} + +// Called by the notification center on memory warnings. +void OnMemoryWarningReceived(CFNotificationCenterRef center, + void* observer, + CFStringRef name, + const void* object, + CFDictionaryRef userInfo) { + OffTheRecordChromeBrowserStateIOData::Handle* handle = + (OffTheRecordChromeBrowserStateIOData::Handle*)observer; + handle->DoomIncognitoCache(); +} + +} // namespace + +void OffTheRecordChromeBrowserStateIOData::Handle::DoomIncognitoCache() { + // The cache for the incognito profile is in RAM. + scoped_refptr<net::URLRequestContextGetter> getter = + main_request_context_getter_; + web::WebThread::PostTask( + web::WebThread::IO, FROM_HERE, base::BindBlock(^{ + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + net::HttpCache* cache = getter->GetURLRequestContext() + ->http_transaction_factory() + ->GetCache(); + if (!cache->GetCurrentBackend()) + return; + cache->GetCurrentBackend()->DoomAllEntries(base::Bind(&DoNothing)); + })); +} + +OffTheRecordChromeBrowserStateIOData::Handle::Handle( + ios::ChromeBrowserState* browser_state) + : io_data_(new OffTheRecordChromeBrowserStateIOData()), + browser_state_(browser_state), + initialized_(false) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + DCHECK(browser_state); + io_data_->cookie_path_ = + browser_state->GetStatePath().Append(kIOSChromeCookieFilename); + io_data_->channel_id_path_ = + browser_state->GetStatePath().Append(kIOSChromeChannelIDFilename); +} + +OffTheRecordChromeBrowserStateIOData::Handle::~Handle() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + + // Stop listening to notifications. + CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, + nullptr, nullptr); + + io_data_->ShutdownOnUIThread(GetAllContextGetters().Pass()); +} + +scoped_refptr<IOSChromeURLRequestContextGetter> +OffTheRecordChromeBrowserStateIOData::Handle::CreateMainRequestContextGetter( + ProtocolHandlerMap* protocol_handlers) const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + LazyInitialize(); + DCHECK(!main_request_context_getter_.get()); + main_request_context_getter_ = + IOSChromeURLRequestContextGetter::Create(io_data_, protocol_handlers); + return main_request_context_getter_; +} + +ChromeBrowserStateIOData* +OffTheRecordChromeBrowserStateIOData::Handle::io_data() const { + LazyInitialize(); + return io_data_; +} + +void OffTheRecordChromeBrowserStateIOData::Handle::LazyInitialize() const { + if (initialized_) + return; + + // Set initialized_ to true at the beginning in case any of the objects + // below try to get the ResourceContext pointer. + initialized_ = true; + io_data_->safe_browsing_enabled()->Init(prefs::kSafeBrowsingEnabled, + browser_state_->GetPrefs()); + io_data_->safe_browsing_enabled()->MoveToThread( + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)); + io_data_->InitializeOnUIThread(browser_state_); + + // Once initialized, listen to memory warnings. + CFNotificationCenterAddObserver( + CFNotificationCenterGetLocalCenter(), this, &OnMemoryWarningReceived, + static_cast<CFStringRef>( + UIApplicationDidReceiveMemoryWarningNotification), + nullptr, CFNotificationSuspensionBehaviorCoalesce); +} + +scoped_ptr<ChromeBrowserStateIOData::IOSChromeURLRequestContextGetterVector> +OffTheRecordChromeBrowserStateIOData::Handle::GetAllContextGetters() { + scoped_ptr<IOSChromeURLRequestContextGetterVector> context_getters( + new IOSChromeURLRequestContextGetterVector()); + if (main_request_context_getter_.get()) + context_getters->push_back(main_request_context_getter_); + + return context_getters.Pass(); +} + +OffTheRecordChromeBrowserStateIOData::OffTheRecordChromeBrowserStateIOData() + : ChromeBrowserStateIOData( + ios::ChromeBrowserStateType::INCOGNITO_BROWSER_STATE) {} + +OffTheRecordChromeBrowserStateIOData::~OffTheRecordChromeBrowserStateIOData() {} + +void OffTheRecordChromeBrowserStateIOData::InitializeInternal( + scoped_ptr<IOSChromeNetworkDelegate> chrome_network_delegate, + ProfileParams* profile_params, + ProtocolHandlerMap* protocol_handlers) const { + net::URLRequestContext* main_context = main_request_context(); + + IOSChromeIOThread* const io_thread = profile_params->io_thread; + IOSChromeIOThread::Globals* const io_thread_globals = io_thread->globals(); + + ApplyProfileParamsToContext(main_context); + + main_context->set_transport_security_state(transport_security_state()); + + main_context->set_net_log(io_thread->net_log()); + + main_context->set_network_delegate(chrome_network_delegate.get()); + + network_delegate_ = chrome_network_delegate.Pass(); + + main_context->set_host_resolver(io_thread_globals->host_resolver.get()); + main_context->set_http_auth_handler_factory( + io_thread_globals->http_auth_handler_factory.get()); + main_context->set_proxy_service(proxy_service()); + + main_context->set_cert_transparency_verifier( + io_thread_globals->cert_transparency_verifier.get()); + main_context->set_backoff_manager( + io_thread_globals->url_request_backoff_manager.get()); + + // For incognito, we use the default non-persistent HttpServerPropertiesImpl. + set_http_server_properties(scoped_ptr<net::HttpServerProperties>( + new net::HttpServerPropertiesImpl())); + main_context->set_http_server_properties(http_server_properties()); + + // For incognito, we use a non-persistent channel ID store. + scoped_refptr<net::DefaultChannelIDStore::PersistentStore> channel_id_store; + + // On iOS, certificates are persisted to the disk in incognito. + DCHECK(!channel_id_path_.empty()); + channel_id_store = new net::SQLiteChannelIDStore( + channel_id_path_, + web::WebThread::GetTaskRunnerForThread(web::WebThread::DB)); + + net::ChannelIDService* channel_id_service = new net::ChannelIDService( + new net::DefaultChannelIDStore(channel_id_store.get()), + base::WorkerPool::GetTaskRunner(true)); + set_channel_id_service(channel_id_service); + main_context->set_channel_id_service(channel_id_service); + + main_context->set_cookie_store( + cookie_util::CreateCookieStore(cookie_util::CookieStoreConfig( + cookie_path_, + cookie_util::CookieStoreConfig::RESTORED_SESSION_COOKIES, + cookie_util::CookieStoreConfig::COOKIE_STORE_IOS, nullptr))); + + http_network_session_ = CreateHttpNetworkSession(*profile_params); + main_http_factory_ = CreateMainHttpFactory( + http_network_session_.get(), net::HttpCache::DefaultBackend::InMemory(0)); + + main_context->set_http_transaction_factory(main_http_factory_.get()); + + scoped_ptr<net::URLRequestJobFactoryImpl> main_job_factory( + new net::URLRequestJobFactoryImpl()); + + InstallProtocolHandlers(main_job_factory.get(), protocol_handlers); + URLRequestInterceptorScopedVector empty_interceptors; + main_job_factory_ = SetUpJobFactoryDefaults(main_job_factory.Pass(), + empty_interceptors.Pass(), + main_context->network_delegate()); + main_context->set_job_factory(main_job_factory_.get()); + + // Setup SDCH for this profile. + sdch_manager_.reset(new net::SdchManager); + sdch_policy_.reset(new net::SdchOwner(sdch_manager_.get(), main_context)); + main_context->set_sdch_manager(sdch_manager_.get()); +} + +net::URLRequestContext* +OffTheRecordChromeBrowserStateIOData::InitializeAppRequestContext( + net::URLRequestContext* main_context) const { + NOTREACHED(); + return nullptr; +} + +net::URLRequestContext* +OffTheRecordChromeBrowserStateIOData::AcquireIsolatedAppRequestContext( + net::URLRequestContext* main_context) const { + NOTREACHED(); + return nullptr; +}
diff --git a/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc new file mode 100644 index 0000000..dd2f88c --- /dev/null +++ b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc
@@ -0,0 +1,139 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ios/chrome/browser/net/ios_chrome_url_request_context_getter.h" + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "ios/chrome/browser/browser_state/chrome_browser_state_io_data.h" +#include "ios/chrome/browser/ios_chrome_io_thread.h" +#include "ios/web/public/web_thread.h" +#include "net/cookies/cookie_store.h" + +class IOSChromeURLRequestContextFactory { + public: + IOSChromeURLRequestContextFactory() {} + virtual ~IOSChromeURLRequestContextFactory() {} + + // Called to create a new instance (will only be called once). + virtual net::URLRequestContext* Create() = 0; + + protected: + DISALLOW_COPY_AND_ASSIGN(IOSChromeURLRequestContextFactory); +}; + +namespace { + +// ---------------------------------------------------------------------------- +// Helper factories +// ---------------------------------------------------------------------------- + +// Factory that creates the main URLRequestContext. +class FactoryForMain : public IOSChromeURLRequestContextFactory { + public: + FactoryForMain(const ChromeBrowserStateIOData* io_data, + ProtocolHandlerMap* protocol_handlers) + : io_data_(io_data) { + std::swap(protocol_handlers_, *protocol_handlers); + } + + net::URLRequestContext* Create() override { + io_data_->Init(&protocol_handlers_); + return io_data_->GetMainRequestContext(); + } + + private: + const ChromeBrowserStateIOData* const io_data_; + ProtocolHandlerMap protocol_handlers_; +}; + +// Factory that creates the URLRequestContext for a given isolated app. +class FactoryForIsolatedApp : public IOSChromeURLRequestContextFactory { + public: + FactoryForIsolatedApp(const ChromeBrowserStateIOData* io_data, + const base::FilePath& partition_path, + net::URLRequestContextGetter* main_context) + : io_data_(io_data), + partition_path_(partition_path), + main_request_context_getter_(main_context) {} + + net::URLRequestContext* Create() override { + // We will copy most of the state from the main request context. + // + // Note that this factory is one-shot. After Create() is called once, the + // factory is actually destroyed. Thus it is safe to destructively pass + // state onwards. + return io_data_->GetIsolatedAppRequestContext( + main_request_context_getter_->GetURLRequestContext(), partition_path_); + } + + private: + const ChromeBrowserStateIOData* const io_data_; + const base::FilePath partition_path_; + scoped_refptr<net::URLRequestContextGetter> main_request_context_getter_; +}; + +} // namespace + +// ---------------------------------------------------------------------------- +// IOSChromeURLRequestContextGetter +// ---------------------------------------------------------------------------- + +IOSChromeURLRequestContextGetter::IOSChromeURLRequestContextGetter( + scoped_ptr<IOSChromeURLRequestContextFactory> factory) + : factory_(std::move(factory)), url_request_context_(nullptr) { + DCHECK(factory_); +} + +IOSChromeURLRequestContextGetter::~IOSChromeURLRequestContextGetter() { + // NotifyContextShuttingDown() must have been called. + DCHECK(!factory_.get()); + DCHECK(!url_request_context_); +} + +// Lazily create a URLRequestContext using our factory. +net::URLRequestContext* +IOSChromeURLRequestContextGetter::GetURLRequestContext() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + if (factory_.get()) { + DCHECK(!url_request_context_); + url_request_context_ = factory_->Create(); + factory_.reset(); + } + + return url_request_context_; +} + +void IOSChromeURLRequestContextGetter::NotifyContextShuttingDown() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + factory_.reset(); + url_request_context_ = nullptr; + URLRequestContextGetter::NotifyContextShuttingDown(); +} + +scoped_refptr<base::SingleThreadTaskRunner> +IOSChromeURLRequestContextGetter::GetNetworkTaskRunner() const { + return web::WebThread::GetTaskRunnerForThread(web::WebThread::IO); +} + +// static +IOSChromeURLRequestContextGetter* IOSChromeURLRequestContextGetter::Create( + const ChromeBrowserStateIOData* io_data, + ProtocolHandlerMap* protocol_handlers) { + return new IOSChromeURLRequestContextGetter( + make_scoped_ptr(new FactoryForMain(io_data, protocol_handlers))); +} + +// static +IOSChromeURLRequestContextGetter* +IOSChromeURLRequestContextGetter::CreateForIsolatedApp( + net::URLRequestContextGetter* main_context, + const ChromeBrowserStateIOData* io_data, + const base::FilePath& partition_path) { + return new IOSChromeURLRequestContextGetter(make_scoped_ptr( + new FactoryForIsolatedApp(io_data, partition_path, main_context))); +}
diff --git a/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h new file mode 100644 index 0000000..531490d9 --- /dev/null +++ b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h
@@ -0,0 +1,72 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_NET_IOS_CHROME_URL_REQUEST_CONTEXT_GETTER_H_ +#define IOS_CHROME_BROWSER_NET_IOS_CHROME_URL_REQUEST_CONTEXT_GETTER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ios/chrome/browser/net/net_types.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" + +class ChromeBrowserStateIOData; +class IOSChromeURLRequestContextFactory; + +// A net::URLRequestContextGetter subclass used by the browser. This returns a +// subclass of net::URLRequestContext which can be used to store extra +// information about requests. +// +// Most methods are expected to be called on the UI thread, except for +// the destructor and GetURLRequestContext(). +class IOSChromeURLRequestContextGetter : public net::URLRequestContextGetter { + public: + // Constructs a ChromeURLRequestContextGetter that will use |factory| to + // create the URLRequestContext. + explicit IOSChromeURLRequestContextGetter( + scoped_ptr<IOSChromeURLRequestContextFactory> factory); + + // Note that GetURLRequestContext() can only be called from the IO + // thread (it will assert otherwise). + // GetIOMessageLoopProxy however can be called from any thread. + // + // net::URLRequestContextGetter implementation. + net::URLRequestContext* GetURLRequestContext() override; + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() + const override; + + // Create an instance for use with an 'original' (non-OTR) profile. This is + // expected to get called on the UI thread. + static IOSChromeURLRequestContextGetter* Create( + const ChromeBrowserStateIOData* io_data, + ProtocolHandlerMap* protocol_handlers); + + // Create an instance for an original profile for an app with isolated + // storage. This is expected to get called on UI thread. + static IOSChromeURLRequestContextGetter* CreateForIsolatedApp( + net::URLRequestContextGetter* main_context, + const ChromeBrowserStateIOData* io_data, + const base::FilePath& partition_path); + + // Discard reference to URLRequestContext and inform observers of shutdown. + // Must be called before destruction. May only be called on IO thread. + void NotifyContextShuttingDown(); + + private: + ~IOSChromeURLRequestContextGetter() override; + + // Deferred logic for creating a URLRequestContext. + // Access only from the IO thread. + scoped_ptr<IOSChromeURLRequestContextFactory> factory_; + + // NULL before initialization and after invalidation. + // Otherwise, it is the URLRequestContext instance that + // was lazily created by GetURLRequestContext(). + // Access only from the IO thread. + net::URLRequestContext* url_request_context_; + + DISALLOW_COPY_AND_ASSIGN(IOSChromeURLRequestContextGetter); +}; + +#endif // IOS_CHROME_BROWSER_NET_IOS_CHROME_URL_REQUEST_CONTEXT_GETTER_H_
diff --git a/ios/chrome/browser/net/net_types.h b/ios/chrome/browser/net/net_types.h new file mode 100644 index 0000000..7cab4c6 --- /dev/null +++ b/ios/chrome/browser/net/net_types.h
@@ -0,0 +1,29 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_NET_NET_TYPES_H_ +#define IOS_CHROME_BROWSER_NET_NET_TYPES_H_ + +#include <map> +#include <string> + +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_vector.h" +#include "net/url_request/url_request_job_factory.h" + +namespace net { +class URLRequestInterceptor; +} + +// A mapping from the scheme name to the protocol handler that services its +// content. +typedef std::map<std::string, + linked_ptr<net::URLRequestJobFactory::ProtocolHandler>> + ProtocolHandlerMap; + +// A scoped vector of protocol interceptors. +typedef ScopedVector<net::URLRequestInterceptor> + URLRequestInterceptorScopedVector; + +#endif // IOS_CHROME_BROWSER_NET_NET_TYPES_H_
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.mm b/ios/chrome/browser/translate/chrome_ios_translate_client.mm index d5af3af..bc15df24 100644 --- a/ios/chrome/browser/translate/chrome_ios_translate_client.mm +++ b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
@@ -135,7 +135,7 @@ } int ChromeIOSTranslateClient::GetInfobarIconID() const { - return IDR_INFOBAR_TRANSLATE_IOS; + return IDR_IOS_INFOBAR_TRANSLATE; } bool ChromeIOSTranslateClient::IsTranslatableURL(const GURL& url) {
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp index 5aa1b71..5f744bb 100644 --- a/ios/chrome/ios_chrome.gyp +++ b/ios/chrome/ios_chrome.gyp
@@ -46,6 +46,7 @@ '../../base/base.gyp:base', '../../base/base.gyp:base_prefs', '../../breakpad/breakpad.gyp:breakpad_client', + '../../components/components.gyp:about_handler', '../../components/components.gyp:autofill_core_browser', '../../components/components.gyp:autofill_core_common', '../../components/components.gyp:autofill_ios_browser', @@ -55,6 +56,7 @@ '../../components/components.gyp:browser_sync_common', '../../components/components.gyp:component_updater', '../../components/components.gyp:content_settings_core_browser', + '../../components/components.gyp:cookie_config', '../../components/components.gyp:crash_core_browser', '../../components/components.gyp:crash_core_common', '../../components/components.gyp:data_reduction_proxy_core_browser', @@ -190,6 +192,12 @@ 'browser/browser_state/browser_state_keyed_service_factories.mm', 'browser/browser_state/browser_state_otr_helper.cc', 'browser/browser_state/browser_state_otr_helper.h', + 'browser/browser_state/chrome_browser_state_impl_io_data.cc', + 'browser/browser_state/chrome_browser_state_impl_io_data.h', + 'browser/browser_state/chrome_browser_state_io_data.cc', + 'browser/browser_state/chrome_browser_state_io_data.h', + 'browser/browser_state/off_the_record_chrome_browser_state_io_data.h', + 'browser/browser_state/off_the_record_chrome_browser_state_io_data.mm', 'browser/browser_state_metrics/browser_state_metrics.cc', 'browser/browser_state_metrics/browser_state_metrics.h', 'browser/browsing_data/ios_chrome_browsing_data_remover.cc', @@ -355,10 +363,13 @@ 'browser/net/ios_chrome_http_user_agent_settings.h', 'browser/net/ios_chrome_network_delegate.cc', 'browser/net/ios_chrome_network_delegate.h', + 'browser/net/ios_chrome_url_request_context_getter.cc', + 'browser/net/ios_chrome_url_request_context_getter.h', 'browser/net/metrics_network_client.h', 'browser/net/metrics_network_client.mm', 'browser/net/metrics_network_client_manager.h', 'browser/net/metrics_network_client_manager.mm', + 'browser/net/net_types.h', 'browser/net/proxy_service_factory.cc', 'browser/net/proxy_service_factory.h', 'browser/net/retryable_url_fetcher.h',
diff --git a/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h b/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h index ab7be32..bdb13749 100644 --- a/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h +++ b/ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h
@@ -13,6 +13,7 @@ #include "base/memory/ref_counted.h" #include "ios/web/public/browser_state.h" +class PrefProxyConfigTracker; class PrefService; namespace base { @@ -20,6 +21,10 @@ class Time; } +namespace net { +class SSLConfigService; +} + namespace syncable_prefs { class PrefServiceSyncable; } @@ -30,6 +35,11 @@ namespace ios { +enum class ChromeBrowserStateType { + REGULAR_BROWSER_STATE, + INCOGNITO_BROWSER_STATE, +}; + // This class is a Chrome-specific extension of the BrowserState interface. class ChromeBrowserState : public web::BrowserState { public: @@ -84,6 +94,13 @@ // Returns an identifier of the browser state for debugging. std::string GetDebugName(); + // Returns the helper object that provides the proxy configuration service + // access to the the proxy configuration possibly defined by preferences. + virtual PrefProxyConfigTracker* GetProxyConfigTracker() = 0; + + // Returns the SSLConfigService for this browser state. + virtual net::SSLConfigService* GetSSLConfigService() = 0; + protected: ChromeBrowserState() {}
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc index e907bf60..c4734c8 100644 --- a/media/capture/video/video_capture_device_unittest.cc +++ b/media/capture/video/video_capture_device_unittest.cc
@@ -297,7 +297,7 @@ TEST_P(VideoCaptureDeviceTest, CaptureWithSize) { names_ = EnumerateDevices(); if (names_->empty()) { - DVLOG(1) << "No camera available. Exiting test."; + VLOG(1) << "No camera available. Exiting test."; return; } @@ -338,7 +338,7 @@ TEST_F(VideoCaptureDeviceTest, MAYBE_AllocateBadSize) { names_ = EnumerateDevices(); if (names_->empty()) { - DVLOG(1) << "No camera available. Exiting test."; + VLOG(1) << "No camera available. Exiting test."; return; } scoped_ptr<VideoCaptureDevice> device( @@ -366,7 +366,7 @@ TEST_F(VideoCaptureDeviceTest, DISABLED_ReAllocateCamera) { names_ = EnumerateDevices(); if (names_->empty()) { - DVLOG(1) << "No camera available. Exiting test."; + VLOG(1) << "No camera available. Exiting test."; return; } @@ -410,7 +410,7 @@ TEST_F(VideoCaptureDeviceTest, DeAllocateCameraWhileRunning) { names_ = EnumerateDevices(); if (names_->empty()) { - DVLOG(1) << "No camera available. Exiting test."; + VLOG(1) << "No camera available. Exiting test."; return; } scoped_ptr<VideoCaptureDevice> device( @@ -437,7 +437,7 @@ scoped_ptr<VideoCaptureDevice::Name> name = GetFirstDeviceNameSupportingPixelFormat(PIXEL_FORMAT_MJPEG); if (!name) { - DVLOG(1) << "No camera supports MJPEG format. Exiting test."; + VLOG(1) << "No camera supports MJPEG format. Exiting test."; return; } scoped_ptr<VideoCaptureDevice> device(
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index 1185b967..c413a86 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc
@@ -68,6 +68,7 @@ next_picture_buffer_id_(0), next_bitstream_buffer_id_(0), available_pictures_(0), + needs_all_picture_buffers_to_decode_(false), weak_factory_(this) { DCHECK(factories_); } @@ -154,7 +155,10 @@ return; } - if (!IsProfileSupported(config.profile(), config.coded_size())) { + VideoDecodeAccelerator::Capabilities capabilities = + factories_->GetVideoDecodeAcceleratorCapabilities(); + if (!IsProfileSupported(capabilities, config.profile(), + config.coded_size())) { DVLOG(1) << "Profile " << config.profile() << " or coded size " << config.coded_size().ToString() << " not supported."; bound_init_cb.Run(false); @@ -162,6 +166,9 @@ } config_ = config; + needs_all_picture_buffers_to_decode_ = + capabilities.flags & + VideoDecodeAccelerator::Capabilities::NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE; needs_bitstream_conversion_ = (config.codec() == kCodecH264); output_cb_ = BindToCurrentLoop(output_cb); @@ -354,9 +361,11 @@ bool GpuVideoDecoder::CanReadWithoutStalling() const { DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); - return - next_picture_buffer_id_ == 0 || // Decode() will ProvidePictureBuffers(). - available_pictures_ > 0; + return next_picture_buffer_id_ == + 0 || // Decode() will ProvidePictureBuffers(). + (!needs_all_picture_buffers_to_decode_ && available_pictures_ > 0) || + available_pictures_ == + static_cast<int>(assigned_picture_buffers_.size()); } int GpuVideoDecoder::GetMaxDecodeRequests() const { @@ -645,12 +654,12 @@ DestroyVDA(); } -bool GpuVideoDecoder::IsProfileSupported(VideoCodecProfile profile, - const gfx::Size& coded_size) { +bool GpuVideoDecoder::IsProfileSupported( + const VideoDecodeAccelerator::Capabilities& capabilities, + VideoCodecProfile profile, + const gfx::Size& coded_size) { DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); - VideoDecodeAccelerator::SupportedProfiles supported_profiles = - factories_->GetVideoDecodeAcceleratorSupportedProfiles(); - for (const auto& supported_profile : supported_profiles) { + for (const auto& supported_profile : capabilities.supported_profiles) { if (profile == supported_profile.profile) { return IsCodedSizeSupported(coded_size, supported_profile.min_resolution,
diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h index 065ac38d..81c7d6c 100644 --- a/media/filters/gpu_video_decoder.h +++ b/media/filters/gpu_video_decoder.h
@@ -135,9 +135,12 @@ // Destroy all PictureBuffers in |buffers|, and delete their textures. void DestroyPictureBuffers(PictureBufferMap* buffers); - // Returns true if the video decoder can support |profile| and |coded_size|. - bool IsProfileSupported(VideoCodecProfile profile, - const gfx::Size& coded_size); + // Returns true if the video decoder with |capabilities| can support + // |profile| and |coded_size|. + bool IsProfileSupported( + const VideoDecodeAccelerator::Capabilities& capabilities, + VideoCodecProfile profile, + const gfx::Size& coded_size); // Assert the contract that this class is operated on the right thread. void DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent() const; @@ -203,6 +206,12 @@ // HasAvailableOutputFrames(). int available_pictures_; + // If true, the client cannot expect the VDA to produce any new decoded + // frames, until it returns all PictureBuffers it may be holding back to the + // VDA. In other words, the VDA may require all PictureBuffers to be able to + // proceed with decoding the next frame. + bool needs_all_picture_buffers_to_decode_; + // Bound to factories_->GetMessageLoop(). // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<GpuVideoDecoder> weak_factory_;
diff --git a/media/renderers/gpu_video_accelerator_factories.h b/media/renderers/gpu_video_accelerator_factories.h index 329f4b4..c9ac36f 100644 --- a/media/renderers/gpu_video_accelerator_factories.h +++ b/media/renderers/gpu_video_accelerator_factories.h
@@ -97,9 +97,10 @@ // Returns the task runner the video accelerator runs on. virtual scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() = 0; - // Returns the supported codec profiles of video decode accelerator. - virtual VideoDecodeAccelerator::SupportedProfiles - GetVideoDecodeAcceleratorSupportedProfiles() = 0; + // Return the capabilities of video decode accelerator, which includes the + // supported codec profiles. + virtual VideoDecodeAccelerator::Capabilities + GetVideoDecodeAcceleratorCapabilities() = 0; // Returns the supported codec profiles of video encode accelerator. virtual VideoEncodeAccelerator::SupportedProfiles
diff --git a/media/renderers/mock_gpu_video_accelerator_factories.h b/media/renderers/mock_gpu_video_accelerator_factories.h index 57a73e1..3a206dbc 100644 --- a/media/renderers/mock_gpu_video_accelerator_factories.h +++ b/media/renderers/mock_gpu_video_accelerator_factories.h
@@ -39,8 +39,8 @@ MOCK_METHOD1(DeleteTexture, void(uint32 texture_id)); MOCK_METHOD1(WaitSyncToken, void(const gpu::SyncToken& sync_token)); MOCK_METHOD0(GetTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>()); - MOCK_METHOD0(GetVideoDecodeAcceleratorSupportedProfiles, - VideoDecodeAccelerator::SupportedProfiles()); + MOCK_METHOD0(GetVideoDecodeAcceleratorCapabilities, + VideoDecodeAccelerator::Capabilities()); MOCK_METHOD0(GetVideoEncodeAcceleratorSupportedProfiles, VideoEncodeAccelerator::SupportedProfiles());
diff --git a/media/video/video_decode_accelerator.cc b/media/video/video_decode_accelerator.cc index d1df010..3b13477 100644 --- a/media/video/video_decode_accelerator.cc +++ b/media/video/video_decode_accelerator.cc
@@ -38,11 +38,13 @@ } VideoDecodeAccelerator::SupportedProfile::SupportedProfile() - : profile(media::VIDEO_CODEC_PROFILE_UNKNOWN) { -} + : profile(media::VIDEO_CODEC_PROFILE_UNKNOWN) {} -VideoDecodeAccelerator::SupportedProfile::~SupportedProfile() { -} +VideoDecodeAccelerator::SupportedProfile::~SupportedProfile() {} + +VideoDecodeAccelerator::Capabilities::Capabilities() : flags(NO_FLAGS) {} + +VideoDecodeAccelerator::Capabilities::~Capabilities() {} } // namespace media
diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h index da184ee..81f3794 100644 --- a/media/video/video_decode_accelerator.h +++ b/media/video/video_decode_accelerator.h
@@ -34,6 +34,29 @@ }; using SupportedProfiles = std::vector<SupportedProfile>; + struct MEDIA_EXPORT Capabilities { + Capabilities(); + ~Capabilities(); + // Flags that can be associated with a VDA. + enum Flags { + NO_FLAGS = 0, + + // Normally, the VDA is required to be able to provide all PictureBuffers + // to the client via PictureReady(), even if the client does not return + // any of them via ReusePictureBuffer(). The client is only required to + // return PictureBuffers when it holds all of them, if it wants to get + // more decoded output. See VideoDecoder::CanReadWithoutStalling for + // more context. + // If this flag is set, then the VDA does not make this guarantee. The + // client must return PictureBuffers to be sure that new frames will be + // provided via PictureReady. + NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE = 1 << 0, + }; + + SupportedProfiles supported_profiles; + uint32_t flags; + }; + // Enumeration of potential errors generated by the API. // Note: Keep these in sync with PP_VideoDecodeError_Dev. Also do not // rearrange, reuse or remove values as they are used for gathering UMA
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc index 35e55ba..b5363f3 100644 --- a/mojo/edk/embedder/embedder.cc +++ b/mojo/edk/embedder/embedder.cc
@@ -28,14 +28,6 @@ namespace mojo { namespace edk { -namespace { - -// Note: Called on the I/O thread. -void ShutdownIPCSupportHelper() { -} - -} // namespace - namespace internal { // Declared in embedder_internal.h. @@ -150,11 +142,8 @@ } void ShutdownIPCSupport() { - internal::g_io_thread_task_runner->PostTaskAndReply( - FROM_HERE, - base::Bind(&ShutdownIPCSupportHelper), - base::Bind(&ProcessDelegate::OnShutdownComplete, - base::Unretained(internal::g_process_delegate))); + // TODO(jam): remove ProcessDelegate from new EDK once the old EDK is gone. + internal::g_process_delegate->OnShutdownComplete(); } ScopedMessagePipeHandle CreateMessagePipe(
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc index 2a53d642..eb9eaf6 100644 --- a/mojo/edk/system/message_pipe_dispatcher.cc +++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -418,10 +418,16 @@ // exception is if they posted a task to run CloseOnIO but the IO thread shut // down and so when it was deleting pending tasks it caused the last reference // to destruct this object. In that case, safe to destroy the channel. - if (channel_ && internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()) - channel_->Shutdown(); - else + if (channel_ && + internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()) { + if (transferable_) { + channel_->Shutdown(); + } else { + internal::g_broker->CloseMessagePipe(pipe_id_, this); + } + } else { DCHECK(!channel_); + } #if defined(OS_POSIX) ClosePlatformHandles(&serialized_fds_); #endif
diff --git a/mojo/message_pump/handle_watcher.cc b/mojo/message_pump/handle_watcher.cc index dc3b9b9..73c423e 100644 --- a/mojo/message_pump/handle_watcher.cc +++ b/mojo/message_pump/handle_watcher.cc
@@ -271,6 +271,12 @@ if (!was_empty) return; } + + // With the new Mojo EDK, the MojoMessagePump will destruct when it sees that + // the IO thread has gone away. So at shutdown this can be null. + if (!thread_.task_runner()) + return; + // We outlive |thread_|, so it's safe to use Unretained() here. thread_.task_runner()->PostTask( FROM_HERE,
diff --git a/mojo/runner/context.cc b/mojo/runner/context.cc index 3e47f6c..db00a7d 100644 --- a/mojo/runner/context.cc +++ b/mojo/runner/context.cc
@@ -287,7 +287,9 @@ TRACE_EVENT0("mojo_shell", "Context::Shutdown"); DCHECK_EQ(base::MessageLoop::current()->task_runner(), task_runners_->shell_runner()); - embedder::ShutdownIPCSupport(); + // Post a task in case OnShutdownComplete is called synchronously. + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(embedder::ShutdownIPCSupport)); // We'll quit when we get OnShutdownComplete(). base::MessageLoop::current()->Run(); }
diff --git a/mojo/runner/host/in_process_native_runner.cc b/mojo/runner/host/in_process_native_runner.cc index 1182fb6d..cb5b2b6f 100644 --- a/mojo/runner/host/in_process_native_runner.cc +++ b/mojo/runner/host/in_process_native_runner.cc
@@ -64,7 +64,11 @@ // TODO(vtl): ScopedNativeLibrary doesn't have a .get() method! base::NativeLibrary app_library = LoadNativeApplication(app_path_); app_library_.Reset(app_library); + // This hangs on Windows in the component build, so skip it since it's + // unnecessary. +#if !(defined(COMPONENT_BUILD) && defined(OS_WIN)) CallLibraryEarlyInitialization(app_library); +#endif RunNativeApplication(app_library, application_request_.Pass()); app_completed_callback_runner_.Run(); app_completed_callback_runner_.Reset();
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc index 36811805..aa379a03 100644 --- a/net/dns/host_resolver_impl_unittest.cc +++ b/net/dns/host_resolver_impl_unittest.cc
@@ -7,12 +7,13 @@ #include <algorithm> #include <string> #include <tuple> +#include <vector> #include "base/bind.h" #include "base/bind_helpers.h" #include "base/location.h" #include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" @@ -481,7 +482,7 @@ Request* CreateRequest(const std::string& hostname) { return test->CreateRequest(hostname); } - ScopedVector<Request>& requests() { return test->requests_; } + std::vector<scoped_ptr<Request>>& requests() { return test->requests_; } void DeleteResolver() { test->resolver_.reset(); } @@ -510,10 +511,9 @@ // not start until released by |proc_->SignalXXX|. Request* CreateRequest(const HostResolver::RequestInfo& info, RequestPriority priority) { - Request* req = new Request( - info, priority, requests_.size(), resolver_.get(), handler_.get()); - requests_.push_back(req); - return req; + requests_.push_back(make_scoped_ptr(new Request( + info, priority, requests_.size(), resolver_.get(), handler_.get()))); + return requests_.back().get(); } Request* CreateRequest(const std::string& hostname, @@ -565,7 +565,7 @@ scoped_refptr<MockHostResolverProc> proc_; scoped_ptr<HostResolverImpl> resolver_; - ScopedVector<Request> requests_; + std::vector<scoped_ptr<Request>> requests_; scoped_ptr<Handler> handler_; };
diff --git a/net/sdch/README.md b/net/sdch/README.md index 8200ee5..7735197 100644 --- a/net/sdch/README.md +++ b/net/sdch/README.md
@@ -63,9 +63,8 @@ Note the layering of the above classes: -1. The SdchManager and SdchOwner classes have no knowledge of - URLRequests. URLRequest is dependent on those classes, not the - reverse. +1. The SdchManager class has no knowledge of URLRequests. URLRequest + is dependent on that class, not the reverse. 2. SdchDictionaryFetcher is dependent on URLRequest, but is still a utility class exported by the net/ library for use by higher levels. 3. SdchOwner manages the entire system on behalf of the embedder. The
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn index bb083d75..54224e8 100644 --- a/remoting/protocol/BUILD.gn +++ b/remoting/protocol/BUILD.gn
@@ -46,8 +46,10 @@ ] } else { sources -= [ + "webrtc_connection_to_client.cc", "webrtc_transport.cc", "webrtc_video_capturer_adapter.cc", + "webrtc_video_stream.cc", ] } } @@ -116,6 +118,7 @@ "third_party_authenticator_unittest.cc", "v2_authenticator_unittest.cc", "video_frame_pump_unittest.cc", + "webrtc_connection_to_client_unittest.cc", "webrtc_transport_unittest.cc", ]
diff --git a/remoting/protocol/ice_connection_to_client.h b/remoting/protocol/ice_connection_to_client.h index 8387b94e..72d76912e 100644 --- a/remoting/protocol/ice_connection_to_client.h +++ b/remoting/protocol/ice_connection_to_client.h
@@ -73,7 +73,6 @@ // Event handler for handling events sent from this object. ConnectionToClient::EventHandler* event_handler_; - // The libjingle channel used to send and receive data from the remote client. scoped_ptr<Session> session_; scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner_;
diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc index 92b56c8..47eab67 100644 --- a/remoting/protocol/jingle_session.cc +++ b/remoting/protocol/jingle_session.cc
@@ -409,7 +409,8 @@ break; case JingleMessage::TRANSPORT_INFO: - if (transport_->ProcessTransportInfo(message.transport_info.get())) { + if (message.transport_info && + transport_->ProcessTransportInfo(message.transport_info.get())) { reply_callback.Run(JingleMessageReply::NONE); } else { reply_callback.Run(JingleMessageReply::BAD_REQUEST);
diff --git a/remoting/protocol/transport.cc b/remoting/protocol/transport.cc index 13ed29f..41eef833 100644 --- a/remoting/protocol/transport.cc +++ b/remoting/protocol/transport.cc
@@ -23,10 +23,11 @@ return std::string(); } -TransportRoute::TransportRoute() : type(DIRECT) { -} +TransportRoute::TransportRoute() : type(DIRECT) {} +TransportRoute::~TransportRoute() {} -TransportRoute::~TransportRoute() { +WebrtcTransport* Transport::AsWebrtcTransport() { + return nullptr; } } // namespace protocol
diff --git a/remoting/protocol/transport.h b/remoting/protocol/transport.h index b037c50..055381a 100644 --- a/remoting/protocol/transport.h +++ b/remoting/protocol/transport.h
@@ -22,6 +22,10 @@ class XmlElement; } // namespace buzz +namespace webrtc { +class PeerConnectionInterface; +} // namespace webrtc + namespace remoting { namespace protocol { @@ -29,6 +33,7 @@ class DatagramChannelFactory; class P2PDatagramSocket; class StreamChannelFactory; +class WebrtcTransport; enum class TransportRole { SERVER, @@ -92,6 +97,15 @@ // channel. virtual StreamChannelFactory* GetMultiplexedChannelFactory() = 0; + // Returns the transport as WebrtcTransport or nullptr if this is not a + // WebrtcTransport. + // + // TODO(sergeyu): Move creation and ownership of Transport objects to the + // Connection classes. That way the Connection classes will be able to ensure + // that correct transport implementation is used for the connection and this + // method will not be necessary. + virtual WebrtcTransport* AsWebrtcTransport(); + private: DISALLOW_COPY_AND_ASSIGN(Transport); };
diff --git a/remoting/protocol/video_stream.h b/remoting/protocol/video_stream.h index 000a1f28..31da0f7 100644 --- a/remoting/protocol/video_stream.h +++ b/remoting/protocol/video_stream.h
@@ -5,6 +5,8 @@ #ifndef REMOTING_PROTOCOL_VIDEO_STREAM_H_ #define REMOTING_PROTOCOL_VIDEO_STREAM_H_ +#include <cstdint> + #include "base/callback_forward.h" namespace webrtc {
diff --git a/remoting/protocol/webrtc_connection_to_client.cc b/remoting/protocol/webrtc_connection_to_client.cc new file mode 100644 index 0000000..d36ac24 --- /dev/null +++ b/remoting/protocol/webrtc_connection_to_client.cc
@@ -0,0 +1,203 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/protocol/webrtc_connection_to_client.h" + +#include "base/bind.h" +#include "base/location.h" +#include "net/base/io_buffer.h" +#include "remoting/codec/video_encoder.h" +#include "remoting/codec/video_encoder_verbatim.h" +#include "remoting/codec/video_encoder_vpx.h" +#include "remoting/protocol/audio_writer.h" +#include "remoting/protocol/clipboard_stub.h" +#include "remoting/protocol/host_control_dispatcher.h" +#include "remoting/protocol/host_event_dispatcher.h" +#include "remoting/protocol/host_stub.h" +#include "remoting/protocol/input_stub.h" +#include "remoting/protocol/webrtc_transport.h" +#include "remoting/protocol/webrtc_video_capturer_adapter.h" +#include "remoting/protocol/webrtc_video_stream.h" +#include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h" +#include "third_party/libjingle/source/talk/app/webrtc/test/fakeconstraints.h" +#include "third_party/libjingle/source/talk/app/webrtc/videosourceinterface.h" + +namespace remoting { +namespace protocol { + +const char kStreamLabel[] = "screen_stream"; +const char kVideoLabel[] = "screen_video"; + +WebrtcConnectionToClient::WebrtcConnectionToClient( + scoped_ptr<protocol::Session> session) + : session_(session.Pass()) { + session_->SetEventHandler(this); +} + +WebrtcConnectionToClient::~WebrtcConnectionToClient() {} + +void WebrtcConnectionToClient::SetEventHandler( + ConnectionToClient::EventHandler* event_handler) { + DCHECK(thread_checker_.CalledOnValidThread()); + event_handler_ = event_handler; +} + +protocol::Session* WebrtcConnectionToClient::session() { + DCHECK(thread_checker_.CalledOnValidThread()); + return session_.get(); +} + +void WebrtcConnectionToClient::Disconnect(ErrorCode error) { + DCHECK(thread_checker_.CalledOnValidThread()); + + control_dispatcher_.reset(); + event_dispatcher_.reset(); + + // This should trigger OnConnectionClosed() event and this object + // may be destroyed as the result. + session_->Close(error); +} + +void WebrtcConnectionToClient::OnInputEventReceived(int64_t timestamp) { + DCHECK(thread_checker_.CalledOnValidThread()); + event_handler_->OnInputEventReceived(this, timestamp); +} + +scoped_ptr<VideoStream> WebrtcConnectionToClient::StartVideoStream( + scoped_ptr<webrtc::DesktopCapturer> desktop_capturer) { + // TODO(sergeyu): Reconsider Transport interface and how it's used here. + WebrtcTransport* transport = session_->GetTransport()->AsWebrtcTransport(); + CHECK(transport); + + scoped_ptr<WebrtcVideoCapturerAdapter> video_capturer_adapter( + new WebrtcVideoCapturerAdapter(desktop_capturer.Pass())); + + // Set video stream constraints. + webrtc::FakeConstraints video_constraints; + video_constraints.AddMandatory( + webrtc::MediaConstraintsInterface::kMinFrameRate, 5); + + rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track = + transport->peer_connection_factory()->CreateVideoTrack( + kVideoLabel, + transport->peer_connection_factory()->CreateVideoSource( + video_capturer_adapter.release(), &video_constraints)); + + rtc::scoped_refptr<webrtc::MediaStreamInterface> video_stream = + transport->peer_connection_factory()->CreateLocalMediaStream( + kStreamLabel); + + if (!video_stream->AddTrack(video_track) || + !transport->peer_connection()->AddStream(video_stream)) { + return nullptr; + } + + scoped_ptr<VideoStream> result( + new WebrtcVideoStream(transport->peer_connection(), video_stream)); + return result.Pass(); +} + +AudioStub* WebrtcConnectionToClient::audio_stub() { + DCHECK(thread_checker_.CalledOnValidThread()); + return nullptr; +} + +// Return pointer to ClientStub. +ClientStub* WebrtcConnectionToClient::client_stub() { + DCHECK(thread_checker_.CalledOnValidThread()); + return control_dispatcher_.get(); +} + +void WebrtcConnectionToClient::set_clipboard_stub( + protocol::ClipboardStub* clipboard_stub) { + DCHECK(thread_checker_.CalledOnValidThread()); + control_dispatcher_->set_clipboard_stub(clipboard_stub); +} + +void WebrtcConnectionToClient::set_host_stub(protocol::HostStub* host_stub) { + DCHECK(thread_checker_.CalledOnValidThread()); + control_dispatcher_->set_host_stub(host_stub); +} + +void WebrtcConnectionToClient::set_input_stub(protocol::InputStub* input_stub) { + DCHECK(thread_checker_.CalledOnValidThread()); + event_dispatcher_->set_input_stub(input_stub); +} + +void WebrtcConnectionToClient::OnSessionStateChange(Session::State state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + DCHECK(event_handler_); + switch(state) { + case Session::INITIALIZING: + case Session::CONNECTING: + case Session::ACCEPTING: + case Session::ACCEPTED: + // Don't care about these events. + break; + case Session::AUTHENTICATING: + event_handler_->OnConnectionAuthenticating(this); + break; + case Session::AUTHENTICATED: { + // Initialize channels. + control_dispatcher_.reset(new HostControlDispatcher()); + control_dispatcher_->Init( + session_.get(), + ChannelConfig(ChannelConfig::TRANSPORT_STREAM, kDefaultStreamVersion, + ChannelConfig::CODEC_UNDEFINED), + this); + + event_dispatcher_.reset(new HostEventDispatcher()); + event_dispatcher_->Init( + session_.get(), + ChannelConfig(ChannelConfig::TRANSPORT_STREAM, kDefaultStreamVersion, + ChannelConfig::CODEC_UNDEFINED), + this); + event_dispatcher_->set_on_input_event_callback(base::Bind( + &ConnectionToClient::OnInputEventReceived, base::Unretained(this))); + + // Notify the handler after initializing the channels, so that + // ClientSession can get a client clipboard stub. + event_handler_->OnConnectionAuthenticated(this); + break; + } + + case Session::CONNECTED: + event_handler_->OnConnectionChannelsConnected(this); + break; + + case Session::CLOSED: + case Session::FAILED: + control_dispatcher_.reset(); + event_dispatcher_.reset(); + event_handler_->OnConnectionClosed( + this, state == Session::CLOSED ? OK : session_->error()); + break; + } +} + +void WebrtcConnectionToClient::OnSessionRouteChange( + const std::string& channel_name, + const TransportRoute& route) { + event_handler_->OnRouteChange(this, channel_name, route); +} + +void WebrtcConnectionToClient::OnChannelInitialized( + ChannelDispatcherBase* channel_dispatcher) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void WebrtcConnectionToClient::OnChannelError( + ChannelDispatcherBase* channel_dispatcher, + ErrorCode error) { + DCHECK(thread_checker_.CalledOnValidThread()); + + LOG(ERROR) << "Failed to connect channel " + << channel_dispatcher->channel_name(); + session_->Close(CHANNEL_CONNECTION_ERROR); +} + +} // namespace protocol +} // namespace remoting
diff --git a/remoting/protocol/webrtc_connection_to_client.h b/remoting/protocol/webrtc_connection_to_client.h new file mode 100644 index 0000000..02919db6 --- /dev/null +++ b/remoting/protocol/webrtc_connection_to_client.h
@@ -0,0 +1,71 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_PROTOCOL_WEBRTC_CONNECTION_TO_CLIENT_H_ +#define REMOTING_PROTOCOL_WEBRTC_CONNECTION_TO_CLIENT_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "remoting/protocol/channel_dispatcher_base.h" +#include "remoting/protocol/connection_to_client.h" +#include "remoting/protocol/session.h" + +namespace remoting { +namespace protocol { + +class HostControlDispatcher; +class HostEventDispatcher; + +class WebrtcConnectionToClient : public ConnectionToClient, + public Session::EventHandler, + public ChannelDispatcherBase::EventHandler { + public: + explicit WebrtcConnectionToClient(scoped_ptr<Session> session); + ~WebrtcConnectionToClient() override; + + // ConnectionToClient interface. + void SetEventHandler( + ConnectionToClient::EventHandler* event_handler) override; + Session* session() override; + void Disconnect(ErrorCode error) override; + void OnInputEventReceived(int64_t timestamp) override; + scoped_ptr<VideoStream> StartVideoStream( + scoped_ptr<webrtc::DesktopCapturer> desktop_capturer) override; + AudioStub* audio_stub() override; + ClientStub* client_stub() override; + void set_clipboard_stub(ClipboardStub* clipboard_stub) override; + void set_host_stub(HostStub* host_stub) override; + void set_input_stub(InputStub* input_stub) override; + + // Session::EventHandler interface. + void OnSessionStateChange(Session::State state) override; + void OnSessionRouteChange(const std::string& channel_name, + const TransportRoute& route) override; + + // ChannelDispatcherBase::EventHandler interface. + void OnChannelInitialized(ChannelDispatcherBase* channel_dispatcher) override; + void OnChannelError(ChannelDispatcherBase* channel_dispatcher, + ErrorCode error) override; + + private: + base::ThreadChecker thread_checker_; + + // Event handler for handling events sent from this object. + ConnectionToClient::EventHandler* event_handler_ = nullptr; + + scoped_ptr<Session> session_; + + scoped_ptr<HostControlDispatcher> control_dispatcher_; + scoped_ptr<HostEventDispatcher> event_dispatcher_; + + DISALLOW_COPY_AND_ASSIGN(WebrtcConnectionToClient); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_WEBRTC_CONNECTION_TO_CLIENT_H_
diff --git a/remoting/protocol/webrtc_connection_to_client_unittest.cc b/remoting/protocol/webrtc_connection_to_client_unittest.cc new file mode 100644 index 0000000..83232ce6 --- /dev/null +++ b/remoting/protocol/webrtc_connection_to_client_unittest.cc
@@ -0,0 +1,95 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/protocol/webrtc_connection_to_client.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "remoting/base/constants.h" +#include "remoting/protocol/fake_session.h" +#include "remoting/protocol/message_serialization.h" +#include "remoting/protocol/protocol_mock_objects.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { +namespace protocol { + +class WebrtcConnectionToClientTest : public testing::Test { + public: + WebrtcConnectionToClientTest() {} + + protected: + void SetUp() override { + session_ = new FakeSession(); + + // Allocate a ClientConnection object with the mock objects. + connection_.reset(new WebrtcConnectionToClient(make_scoped_ptr(session_))); + connection_->SetEventHandler(&handler_); + EXPECT_CALL(handler_, OnConnectionAuthenticated(connection_.get())) + .WillOnce(InvokeWithoutArgs( + this, &WebrtcConnectionToClientTest::ConnectStubs)); + EXPECT_CALL(handler_, OnConnectionChannelsConnected(connection_.get())); + session_->event_handler()->OnSessionStateChange(Session::ACCEPTED); + session_->event_handler()->OnSessionStateChange(Session::AUTHENTICATED); + session_->event_handler()->OnSessionStateChange(Session::CONNECTED); + base::RunLoop().RunUntilIdle(); + } + + void TearDown() override { + connection_.reset(); + base::RunLoop().RunUntilIdle(); + } + + void ConnectStubs() { + connection_->set_clipboard_stub(&clipboard_stub_); + connection_->set_host_stub(&host_stub_); + connection_->set_input_stub(&input_stub_); + } + + base::MessageLoop message_loop_; + MockConnectionToClientEventHandler handler_; + MockClipboardStub clipboard_stub_; + MockHostStub host_stub_; + MockInputStub input_stub_; + scoped_ptr<ConnectionToClient> connection_; + + FakeSession* session_; + + private: + DISALLOW_COPY_AND_ASSIGN(WebrtcConnectionToClientTest); +}; + +TEST_F(WebrtcConnectionToClientTest, SendCapabilitesMessage) { + Capabilities capabilities; + connection_->client_stub()->SetCapabilities(capabilities); + + base::RunLoop().RunUntilIdle(); + + // Verify that something has been written. + // TODO(sergeyu): Verify that the correct data has been written. + FakeStreamSocket* channel = + session_->GetTransport()->GetStreamChannelFactory()->GetFakeChannel( + kControlChannelName); + ASSERT_TRUE(channel); + EXPECT_FALSE(channel->written_data().empty()); + + ControlMessage message; + message.mutable_capabilities()->CopyFrom(capabilities); + scoped_refptr<net::IOBufferWithSize> expected_message = + SerializeAndFrameMessage(message); + EXPECT_EQ(std::string(expected_message->data(), expected_message->size()), + channel->written_data()); + + // And then close the connection to ConnectionToClient. + connection_->Disconnect(protocol::OK); + + base::RunLoop().RunUntilIdle(); +} + +// TODO(sergeyu): Add more tests here after Session is refactored not to own +// Transport, which will allow to use real WebrtcTransport with FakeSession. + +} // namespace protocol +} // namespace remoting
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc index 6dcbb972..253293eb 100644 --- a/remoting/protocol/webrtc_transport.cc +++ b/remoting/protocol/webrtc_transport.cc
@@ -244,6 +244,10 @@ return GetStreamChannelFactory(); } +WebrtcTransport* WebrtcTransport::AsWebrtcTransport() { + return this; +} + void WebrtcTransport::OnLocalSessionDescriptionCreated( scoped_ptr<webrtc::SessionDescriptionInterface> description, const std::string& error) {
diff --git a/remoting/protocol/webrtc_transport.h b/remoting/protocol/webrtc_transport.h index ae792f2..afba35e 100644 --- a/remoting/protocol/webrtc_transport.h +++ b/remoting/protocol/webrtc_transport.h
@@ -45,6 +45,7 @@ bool ProcessTransportInfo(buzz::XmlElement* transport_info) override; StreamChannelFactory* GetStreamChannelFactory() override; StreamChannelFactory* GetMultiplexedChannelFactory() override; + WebrtcTransport* AsWebrtcTransport() override; private: void OnLocalSessionDescriptionCreated(
diff --git a/remoting/protocol/webrtc_video_capturer_adapter.cc b/remoting/protocol/webrtc_video_capturer_adapter.cc index 9bf51ed6..42442b3 100644 --- a/remoting/protocol/webrtc_video_capturer_adapter.cc +++ b/remoting/protocol/webrtc_video_capturer_adapter.cc
@@ -9,7 +9,7 @@ namespace remoting { // Number of frames to be captured per second. -const int kFramesPerSec = 10; +const int kFramesPerSec = 30; WebrtcVideoCapturerAdapter::WebrtcVideoCapturerAdapter( scoped_ptr<webrtc::DesktopCapturer> capturer) @@ -157,6 +157,7 @@ DCHECK_NE(capture_state(), cricket::CS_STOPPED); capture_timer_.reset(); + desktop_capturer_.reset(); SetCaptureFormat(nullptr); SetCaptureState(cricket::CS_STOPPED); @@ -164,7 +165,6 @@ VLOG(1) << "WebrtcVideoCapturerAdapter stopped."; } - bool WebrtcVideoCapturerAdapter::IsRunning() { DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc new file mode 100644 index 0000000..50c6ddd --- /dev/null +++ b/remoting/protocol/webrtc_video_stream.cc
@@ -0,0 +1,49 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/protocol/webrtc_video_stream.h" + +#include "base/logging.h" +#include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" +#include "third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h" +#include "third_party/libjingle/source/talk/app/webrtc/videosourceinterface.h" + +namespace remoting { +namespace protocol { + +WebrtcVideoStream::WebrtcVideoStream( + rtc::scoped_refptr<webrtc::PeerConnectionInterface> connection, + rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) + : connection_(connection), stream_(stream) {} + +WebrtcVideoStream::~WebrtcVideoStream() { + for (const auto& track : stream_->GetVideoTracks()) { + track->GetSource()->Stop(); + stream_->RemoveTrack(track.get()); + } + connection_->RemoveStream(stream_.get()); +} + +void WebrtcVideoStream::Pause(bool pause) { + NOTIMPLEMENTED(); +} + +void WebrtcVideoStream::OnInputEventReceived(int64_t event_timestamp) { + NOTIMPLEMENTED(); +} + +void WebrtcVideoStream::SetLosslessEncode(bool want_lossless) { + NOTIMPLEMENTED(); +} + +void WebrtcVideoStream::SetLosslessColor(bool want_lossless) { + NOTIMPLEMENTED(); +} + +void WebrtcVideoStream::SetSizeCallback(const SizeCallback& size_callback) { + NOTIMPLEMENTED(); +} + +} // namespace protocol +} // namespace remoting
diff --git a/remoting/protocol/webrtc_video_stream.h b/remoting/protocol/webrtc_video_stream.h new file mode 100644 index 0000000..ffb0791c --- /dev/null +++ b/remoting/protocol/webrtc_video_stream.h
@@ -0,0 +1,44 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_PROTOCOL_WEBRTC_VIDEO_STREAM_H_ +#define REMOTING_PROTOCOL_WEBRTC_VIDEO_STREAM_H_ + +#include "base/macros.h" +#include "remoting/protocol/video_stream.h" +#include "third_party/webrtc/base/scoped_ref_ptr.h" + +namespace webrtc { +class MediaStreamInterface; +class PeerConnectionInterface; +} // namespace webrtc + +namespace remoting { +namespace protocol { + +class WebrtcVideoStream : public VideoStream { + public: + WebrtcVideoStream( + rtc::scoped_refptr<webrtc::PeerConnectionInterface> connection, + rtc::scoped_refptr<webrtc::MediaStreamInterface> stream); + ~WebrtcVideoStream() override; + + // VideoStream interface. + void Pause(bool pause) override; + void OnInputEventReceived(int64_t event_timestamp) override; + void SetLosslessEncode(bool want_lossless) override; + void SetLosslessColor(bool want_lossless) override; + void SetSizeCallback(const SizeCallback& size_callback) override; + + private: + rtc::scoped_refptr<webrtc::PeerConnectionInterface> connection_; + rtc::scoped_refptr<webrtc::MediaStreamInterface> stream_; + + DISALLOW_COPY_AND_ASSIGN(WebrtcVideoStream); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_WEBRTC_VIDEO_STREAM_H_
diff --git a/remoting/remoting_srcs.gypi b/remoting/remoting_srcs.gypi index 38bafcf7..cbd416fb 100644 --- a/remoting/remoting_srcs.gypi +++ b/remoting/remoting_srcs.gypi
@@ -212,12 +212,16 @@ 'protocol/ice_connection_to_client.h', 'protocol/video_frame_pump.cc', 'protocol/video_frame_pump.h', + 'protocol/webrtc_connection_to_client.cc', + 'protocol/webrtc_connection_to_client.h', 'protocol/webrtc_data_stream_adapter.cc', 'protocol/webrtc_data_stream_adapter.h', 'protocol/webrtc_transport.cc', 'protocol/webrtc_transport.h', 'protocol/webrtc_video_capturer_adapter.cc', 'protocol/webrtc_video_capturer_adapter.h', + 'protocol/webrtc_video_stream.cc', + 'protocol/webrtc_video_stream.h', ], 'remoting_signaling_sources': [
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi index aab6f2c..d0f156b2 100644 --- a/remoting/remoting_test.gypi +++ b/remoting/remoting_test.gypi
@@ -332,6 +332,7 @@ 'protocol/third_party_authenticator_unittest.cc', 'protocol/v2_authenticator_unittest.cc', 'protocol/video_frame_pump_unittest.cc', + 'protocol/webrtc_connection_to_client_unittest.cc', 'protocol/webrtc_transport_unittest.cc', 'signaling/iq_sender_unittest.cc', 'signaling/jid_util_unittest.cc',
diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java index 0a12849..b209b3c 100644 --- a/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java +++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java
@@ -76,6 +76,9 @@ private static final String TAG = "InvalidationPreferences"; + // Only one commit call can be in progress at a time. + private static final Object sCommitLock = new Object(); + private final Context mContext; public InvalidationPreferences(Context context) { @@ -96,11 +99,13 @@ * NOTE: this method performs blocking I/O and must not be called from the UI thread. */ public boolean commit(EditContext editContext) { - if (!editContext.mEditor.commit()) { - Log.w(TAG, "Failed to commit invalidation preferences"); - return false; + synchronized (sCommitLock) { + if (!editContext.mEditor.commit()) { + Log.w(TAG, "Failed to commit invalidation preferences"); + return false; + } + return true; } - return true; } /** Returns the saved sync types, or {@code null} if none exist. */
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 001aff2..9409293 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1308,3 +1308,24 @@ crbug.com/568157 [ XP ] virtual/syncpaint/fast/repaint/details-open-repaint.html [ Failure ] crbug.com/568157 [ XP ] virtual/syncpaint/fast/repaint/overflow-scroll-body-appear.html [ Failure ] crbug.com/568157 [ XP ] virtual/syncpaint/fast/repaint/resize-scrollable-iframe.html [ Failure ] + +crbug.com/568678 [ XP ] ietestcenter/css3/text/textshadow-001.htm [ NeedsRebaseline ] +crbug.com/568678 [ XP ] ietestcenter/css3/text/textshadow-002.htm [ NeedsRebaseline ] +crbug.com/568678 [ XP ] ietestcenter/css3/text/textshadow-003.htm [ NeedsRebaseline ] +crbug.com/568678 [ XP ] ietestcenter/css3/text/textshadow-004.htm [ NeedsRebaseline ] +crbug.com/568678 [ XP ] ietestcenter/css3/text/textshadow-010.htm [ NeedsRebaseline ] +crbug.com/568678 [ XP ] paint/roundedrects/circle-with-shadow.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] paint/roundedrects/input-with-rounded-rect-and-shadow.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] svg/css/text-gradient-shadow.svg [ NeedsRebaseline ] +crbug.com/568678 [ XP ] svg/css/text-shadow-multiple.xhtml [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/display_list_2d_canvas/fast/canvas/canvas-composite-shadow.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/display_list_2d_canvas/fast/canvas/canvas-incremental-repaint.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/display_list_2d_canvas/fast/canvas/canvas-shadow-source-in.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/gpu/fast/canvas/canvas-composite-shadow.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/gpu/fast/canvas/canvas-incremental-repaint.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/gpu/fast/canvas/canvas-shadow-source-in.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/syncpaint/fast/repaint/shadow-multiple.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/syncpaint/paint/roundedrects/circle-with-shadow.html [ NeedsRebaseline ] +crbug.com/568678 [ XP ] virtual/syncpaint/paint/roundedrects/input-with-rounded-rect-and-shadow.html [ NeedsRebaseline ] + +crbug.com/568678 [ XP ] virtual/gpu/fast/canvas/canvas-scale-strokePath-shadow.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/animations/responsive/resources/responsive-test.js b/third_party/WebKit/LayoutTests/animations/responsive/resources/responsive-test.js index bbf7a1a..59dcba8 100644 --- a/third_party/WebKit/LayoutTests/animations/responsive/resources/responsive-test.js +++ b/third_party/WebKit/LayoutTests/animations/responsive/resources/responsive-test.js
@@ -207,19 +207,6 @@ setState(bindings, targets, property, before.state); var animations = createPausedAnimations(targets, keyframes, after.expect.map(expectation => expectation.at)); stateTransitionTests.push({ - // TODO(alancutter): Make SVG animations responsive when their interpolation fractions haven't changed. - wiggleFractionHack() { - return new Promise(resolve => { - animations.forEach(animation => { - var currentTime = animation.currentTime; - animation.currentTime = 1; - requestAnimationFrame(() => { - animation.currentTime = currentTime; - }); - }); - requestAnimationFrame(resolve); - }); - }, applyStateTransition() { setState(bindings, targets, property, after.state); }, @@ -244,14 +231,12 @@ stateTransitionTest.applyStateTransition(); } - Promise.all(stateTransitionTests.map(stateTransitionTest => stateTransitionTest.wiggleFractionHack())).then(() => { - requestAnimationFrame(() => { - for (var stateTransitionTest of stateTransitionTests) { - stateTransitionTest.assert(); - } - resolve(); - }); - }) + requestAnimationFrame(() => { + for (var stateTransitionTest of stateTransitionTests) { + stateTransitionTest.assert(); + } + resolve(); + }); }); }); }
diff --git a/third_party/WebKit/LayoutTests/animations/svg-responsive-to-timing-updates.html b/third_party/WebKit/LayoutTests/animations/svg-responsive-to-timing-updates.html new file mode 100644 index 0000000..2a684e0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/animations/svg-responsive-to-timing-updates.html
@@ -0,0 +1,18 @@ +<!DOCTYPE html> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<svg> + <stop id="target" offset="0"/> +</svg> +<script> +test(() => { + var animation = target.animate({'svg-offset': '1'}, 1); + assert_equals(target.offset.animVal, 0); + animation.currentTime = 0.5; + assert_equals(target.offset.animVal, 0.5); + animation.currentTime = 0.75; + assert_equals(target.offset.animVal, 0.75); + animation.cancel(); + assert_equals(target.offset.animVal, 0); +}, 'SVG Web Animations should be responsive to changes in animation timing'); +</script>
diff --git a/third_party/WebKit/LayoutTests/animations/svg-responsive-to-updated-baseval.html b/third_party/WebKit/LayoutTests/animations/svg-responsive-to-updated-baseval.html new file mode 100644 index 0000000..e0819e1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/animations/svg-responsive-to-updated-baseval.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<title>SVG Web Animations should be responsive to changes in the underlying value</title> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<script> +// offset has a primitive animVal type. +function createOffsetTestTarget() { + var target = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + target.setAttribute('offset', '0'); + var animation = target.animate({'svg-offset': '1'}, 1); + animation.pause(); + animation.currentTime = 0.5; + assert_equals(target.getAttribute('offset'), '0.5', 'Initial getAttribute()'); + assert_equals(target.offset.animVal, 0.5, 'Initial get animVal'); + return target; +} + +test(() => { + var target = createOffsetTestTarget(); + target.setAttribute('offset', '0.5'); + assert_equals(target.getAttribute('offset'), '0.75'); +}, document.title + ': setAttribute() -> getAttribute()'); + +test(() => { + var target = createOffsetTestTarget(); + target.setAttribute('offset', '0.5'); + assert_equals(target.offset.animVal, 0.75); +}, document.title + ': setAttribute() -> get primitive animVal'); + +test(() => { + var target = createOffsetTestTarget(); + target.offset.baseVal = '0.5'; + assert_equals(target.getAttribute('offset'), '0.75'); +}, document.title + ': set baseVal -> getAttribute()'); + +test(() => { + var target = createOffsetTestTarget(); + target.offset.baseVal = '0.5'; + assert_equals(target.offset.animVal, 0.75); +}, document.title + ': set baseVal -> get primitive animVal'); + + +function serializeRect(rect) { + return [rect.x, rect.y, rect.width, rect.height].join(' '); +} + +// viewBox has a tear-off animVal type. +function createViewBoxTestTarget() { + var target = document.createElementNS('http://www.w3.org/2000/svg', 'marker'); + target.setAttribute('viewBox', '0 0 0 0'); + var animation = target.animate({'svg-viewBox': '1 1 1 1'}, 1); + animation.pause(); + animation.currentTime = 0.5; + assert_equals(target.getAttribute('viewBox'), '0.5 0.5 0.5 0.5', 'Initial getAttribute()'); + assert_equals(serializeRect(target.viewBox.animVal), '0.5 0.5 0.5 0.5', 'Initial get animVal'); + return target; +} + +test(() => { + var target = createViewBoxTestTarget(); + var animVal = target.viewBox.animVal; + target.setAttribute('viewBox', '0.5 0.5 0.5 0.5'); + assert_equals(serializeRect(animVal), '0.75 0.75 0.75 0.75'); +}, document.title + ': setAttribute() -> read tear-off animVal'); + +test(() => { + var target = createViewBoxTestTarget(); + var animVal = target.viewBox.animVal; + var baseVal = target.viewBox.baseVal; + baseVal.x = 0.5; + baseVal.y = 0.5; + baseVal.width = 0.5; + baseVal.height = 0.5; + assert_equals(serializeRect(animVal), '0.75 0.75 0.75 0.75'); +}, document.title + ': set baseVal -> read tear-off animVal'); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt index 9ebc276..f7628e6 100644 --- a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt
@@ -1,8 +1,8 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 897 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 898 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x897 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x897.31 - LayoutBlockFlow {BODY} at (8,8) size 769x879.31 +layer at (0,0) size 785x898 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x898.31 + LayoutBlockFlow {BODY} at (8,8) size 769x880.31 LayoutFieldset {FIELDSET} at (2,0) size 140x35.59 [border: (8px double #000000)] LayoutBlockFlow {LEGEND} at (20,0) size 54x12 [bgcolor=#0080004C] LayoutFieldset {FIELDSET} at (2,45.59) size 140x35.59 [border: (8px double #000000)] @@ -29,11 +29,11 @@ LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)] LayoutFieldset {FIELDSET} at (2,541.13) size 184x59.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (44,0) size 96x12 [border: (1px solid #0000FF)] - LayoutBlockFlow (anonymous) at (0,810.72) size 769x39 + LayoutBlockFlow (anonymous) at (0,810.72) size 769x40 LayoutFieldset {FIELDSET} at (2,0) size 184x29.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (2,849.72) size 128x29.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (2,850.72) size 128x29.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)] layer at (8,619) size 100x40 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 LayoutBlockFlow (relative positioned) {DIV} at (0,610.72) size 100x40
diff --git a/third_party/WebKit/LayoutTests/fast/block/positioning/replaced-inside-fixed-top-bottom-expected.txt b/third_party/WebKit/LayoutTests/fast/block/positioning/replaced-inside-fixed-top-bottom-expected.txt index 7f589e2..88d9463 100644 --- a/third_party/WebKit/LayoutTests/fast/block/positioning/replaced-inside-fixed-top-bottom-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/block/positioning/replaced-inside-fixed-top-bottom-expected.txt
@@ -7,6 +7,6 @@ LayoutBlockFlow (positioned) {DIV} at (50,100) size 700x400 [border: (1px solid #808080)] layer at (51,141) size 560x320 LayoutBlockFlow (positioned) {DIV} at (1,40.80) size 560.39x320.39 [border: (1px solid #FF0000)] - LayoutImage {IMG} at (1,1) size 393x318.39 + LayoutImage {IMG} at (1,1) size 393.41x318.39 LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png index ecd593d..eacd0f6 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png +++ b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt index 8b551cf6..c46c845 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt
@@ -31,16 +31,16 @@ LayoutBlockFlow {LEGEND} at (135,0) size 54x12 [bgcolor=#0080004C] LayoutFieldset {FIELDSET} at (2,307.97) size 140x51.59 [border: (8px double #000000)] LayoutBlockFlow {LEGEND} at (140,0) size 54x12 [bgcolor=#0080004C] - LayoutBlockFlow {DIV} at (450,0) size 334x387.78 + LayoutBlockFlow {DIV} at (450,0) size 334x388.78 LayoutFieldset {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)] LayoutFieldset {FIELDSET} at (2,49.59) size 184x39.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (44,0) size 96x12 [border: (1px solid #0000FF)] - LayoutBlockFlow (anonymous) at (0,299.19) size 334x49 + LayoutBlockFlow (anonymous) at (0,299.19) size 334x50 LayoutFieldset {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (2,348.19) size 128x39.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (2,349.19) size 128x39.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)] layer at (458,107) size 100x40 LayoutBlockFlow (relative positioned) {DIV} at (0,99.19) size 100x40
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-toBlob-invalid-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-toBlob-invalid-expected.txt new file mode 100644 index 0000000..cbe19275 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-toBlob-invalid-expected.txt
@@ -0,0 +1,14 @@ +Test the handling of invalid arguments in canvas toBlob(). + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS canvas.toBlob(); threw exception TypeError: Failed to execute 'toBlob' on 'HTMLCanvasElement': 1 argument required, but only 0 present.. +PASS canvas.toBlob(null); threw exception TypeError: Failed to execute 'toBlob' on 'HTMLCanvasElement': The callback provided as parameter 1 is not a function.. +PASS canvas.toBlob(undefined); threw exception TypeError: Failed to execute 'toBlob' on 'HTMLCanvasElement': The callback provided as parameter 1 is not a function.. +PASS canvas.toBlob(function() {}); did not throw exception. +PASS canvas.toBlob(function(blob) {}, 'image/jpeg', 500) did not throw exception. +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-toBlob-invalid.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-toBlob-invalid.html new file mode 100644 index 0000000..c4633f2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-toBlob-invalid.html
@@ -0,0 +1,18 @@ +<script src="../../resources/js-test.js"></script> +<script type='text/javascript'> +description("Test the handling of invalid arguments in canvas toBlob()."); + +var canvas = document.createElement('canvas'); +var ctx = canvas.getContext("2d"); +ctx.strokeStyle = "red"; +ctx.strokeRect(0, 0, 50, 50); + +shouldThrow("canvas.toBlob();"); +shouldThrow("canvas.toBlob(null);"); +shouldThrow("canvas.toBlob(undefined);"); +// Passing the callback argument without blob handle silently fails. +shouldNotThrow("canvas.toBlob(function() {});"); + +// Invalid quality argument will fall back to default value +shouldNotThrow("canvas.toBlob(function(blob) {}, 'image/jpeg', 500)"); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lost-gpu-context.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lost-gpu-context.js index 363045a..3ca6941 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lost-gpu-context.js +++ b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lost-gpu-context.js
@@ -13,8 +13,12 @@ document.body.appendChild(ctx.canvas); verifyContextLost(false); window.internals.loseSharedGraphicsContext3D(); - // for the canvas to realize it Graphics context was lost we must try to use the canvas + // for the canvas to realize its Graphics context was lost we must try to + // use the contents of the canvas -- that is, we must either try to present + // the canvas or read from it (e.g. by calling getImageData) ctx.fillRect(0, 0, 1, 1); + shouldBeFalse('ctx.isContextLost()'); + var imageData = ctx.getImageData(0, 0, 1, 1); if (!ctx.isContextLost()) { debug('<span>Aborting test: Graphics context loss did not destroy canvas contents. This is expected if canvas is not accelerated.</span>'); } else {
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Selection/use-counters.html b/third_party/WebKit/LayoutTests/fast/dom/Selection/use-counters.html new file mode 100644 index 0000000..9682116 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/dom/Selection/use-counters.html
@@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>Selection UseCounters</title> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script> +test(function() { + var SelectionCollapseNull = 1083; // From UseCounter.h + assert_false(internals.isUseCounted(document, SelectionCollapseNull)); + document.getSelection().collapse(null, -1); + assert_true(internals.isUseCounted(document, SelectionCollapseNull)); +}, 'selection.collapse(null, -1)'); + +test(function() { + var SelectionSetBaseAndExtentNull = 1084; // From UseCounter.h + assert_false(internals.isUseCounted(document, SelectionSetBaseAndExtentNull)); + assert_throws('IndexSizeError', function() { + document.getSelection().setBaseAndExtent(null, -1, null, -1); + }); + assert_false(internals.isUseCounted(document, SelectionSetBaseAndExtentNull)); + document.getSelection().setBaseAndExtent(null, 0, null, 0) + assert_true(internals.isUseCounted(document, SelectionSetBaseAndExtentNull)); +}, 'selection.setBaseAndExtent(null, 0, null, 0)'); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/video-mute-repaint-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/video-mute-repaint-expected.txt index 411170e..78a071f0 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/video-mute-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/repaint/video-mute-repaint-expected.txt
@@ -23,11 +23,11 @@ "bounds": [700, 520], "drawsContent": true, "repaintRects": [ - [628, 493, 24, 24], - [570, 493, 89, 24], - [570, 493, 89, 24], - [570, 493, 24, 24], - [535, 490, 35, 30] + [628, 492, 24, 25], + [570, 492, 89, 25], + [570, 492, 89, 25], + [570, 492, 24, 25], + [535, 489, 35, 31] ], "paintInvalidationClients": [ "LayoutButton INPUT",
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/video-unmute-repaint-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/video-unmute-repaint-expected.txt index d45b4b8..5c3ac11 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/video-unmute-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/repaint/video-unmute-repaint-expected.txt
@@ -23,11 +23,11 @@ "bounds": [700, 520], "drawsContent": true, "repaintRects": [ - [599, 493, 24, 24], - [570, 493, 89, 24], - [570, 493, 89, 24], - [570, 493, 24, 24], - [535, 490, 35, 30] + [599, 492, 24, 25], + [570, 492, 89, 25], + [570, 492, 89, 25], + [570, 492, 24, 25], + [535, 489, 35, 31] ], "paintInvalidationClients": [ "LayoutButton INPUT",
diff --git a/third_party/WebKit/LayoutTests/fast/writing-mode/block-level-images-expected.txt b/third_party/WebKit/LayoutTests/fast/writing-mode/block-level-images-expected.txt index 608f990..e9743d6 100644 --- a/third_party/WebKit/LayoutTests/fast/writing-mode/block-level-images-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/writing-mode/block-level-images-expected.txt
@@ -3,13 +3,13 @@ layer at (0,0) size 800x316 LayoutBlockFlow {HTML} at (0,0) size 800x316 LayoutBlockFlow {BODY} at (8,8) size 784x0 - LayoutBlockFlow (floating) {DIV} at (2,2) size 264x304 [border: (2px solid #000000)] + LayoutBlockFlow (floating) {DIV} at (2,2) size 264.05x304 [border: (2px solid #000000)] LayoutBlockFlow {DIV} at (2,2) size 25x300 [bgcolor=#008000] LayoutImage {IMG} at (27,2) size 154x156 [border: (1px solid #800080) (2px solid #800000) (5px dashed #808080) (2px solid #800000)] - LayoutImage {IMG} at (181,42) size 56x50 - LayoutBlockFlow {DIV} at (237,2) size 25x300 [bgcolor=#008000] - LayoutBlockFlow (floating) {DIV} at (270,2) size 264x304 [border: (2px solid #000000)] + LayoutImage {IMG} at (181,42) size 56.05x50 + LayoutBlockFlow {DIV} at (237.05,2) size 25x300 [bgcolor=#008000] + LayoutBlockFlow (floating) {DIV} at (270.05,2) size 264.05x304 [border: (2px solid #000000)] LayoutBlockFlow {DIV} at (2,2) size 25x300 [bgcolor=#008000] LayoutImage {IMG} at (27,146) size 154x156 [border: (5px dashed #808080) (2px solid #800000) (1px solid #800080) (2px solid #800000)] - LayoutImage {IMG} at (181,212) size 56x50 - LayoutBlockFlow {DIV} at (237,2) size 25x300 [bgcolor=#008000] + LayoutImage {IMG} at (181,212) size 56.05x50 + LayoutBlockFlow {DIV} at (237.05,2) size 25x300 [bgcolor=#008000]
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-xml-document-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-xml-document-expected.txt index a0aee77..c2ffd09d 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-xml-document-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/console/console-xml-document-expected.txt
@@ -1,5 +1,7 @@ CONSOLE MESSAGE: line 9: [object XMLDocument] +CONSOLE MESSAGE: line 11: [object Element] Tests that XML document contents are logged using the correct case in the console. console-xml-document.html:4 #document<MixedCase> Test </MixedCase> +console-xml-document.html:6 <Book></Book>
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-xml-document.html b/third_party/WebKit/LayoutTests/inspector/console/console-xml-document.html index 2cde53d..6913c2b 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-xml-document.html +++ b/third_party/WebKit/LayoutTests/inspector/console/console-xml-document.html
@@ -7,6 +7,8 @@ function onload() { console.dirxml((new DOMParser()).parseFromString("<MixedCase> Test </MixedCase>", "text/xml")); + var danglingNode = document.implementation.createDocument("", "books"); + console.dirxml(danglingNode.createElement("Book")); runTest(); } //# sourceURL=console-xml-document.html
diff --git a/third_party/WebKit/LayoutTests/platform/android/css2.1/20110323/inline-block-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/android/css2.1/20110323/inline-block-replaced-height-008-expected.txt new file mode 100644 index 0000000..b8ce52a8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/css2.1/20110323/inline-block-replaced-height-008-expected.txt
@@ -0,0 +1,38 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x202 + LayoutBlockFlow {HTML} at (0,0) size 800x202 + LayoutBlockFlow {BODY} at (8,16) size 784x178 + LayoutBlockFlow {P} at (0,0) size 784x40 + LayoutText {#text} at (0,0) size 101x19 + text run at (0,0) width 101: "There should be " + LayoutInline {STRONG} at (0,0) size 143x19 + LayoutText {#text} at (101,0) size 143x19 + text run at (101,0) width 143: "5 filled green squares" + LayoutText {#text} at (243,0) size 176x19 + text run at (243,0) width 176: " with the same width and the " + LayoutInline {STRONG} at (0,0) size 81x19 + LayoutText {#text} at (418,0) size 81x19 + text run at (418,0) width 81: "same height" + LayoutText {#text} at (498,0) size 229x19 + text run at (498,0) width 229: ". The 5 filled green squares should be " + LayoutInline {STRONG} at (0,0) size 57x19 + LayoutText {#text} at (726,0) size 57x19 + text run at (726,0) width 57: "identical" + LayoutText {#text} at (0,20) size 417x19 + text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." + LayoutBlockFlow {DIV} at (0,56) size 784x122 + LayoutImage {OBJECT} at (0,0) size 117.59x117.59 + LayoutText {#text} at (117,102) size 5x19 + text run at (117,102) width 5: " " + LayoutImage {OBJECT} at (121.59,0) size 117.59x117.59 + LayoutText {#text} at (239,102) size 5x19 + text run at (239,102) width 5: " " + LayoutImage {OBJECT} at (243.19,0) size 117.59x117.59 + LayoutText {#text} at (360,102) size 5x19 + text run at (360,102) width 5: " " + LayoutImage {OBJECT} at (364.78,0) size 117.59x117.59 + LayoutText {#text} at (482,102) size 5x19 + text run at (482,102) width 5: " " + LayoutImage {OBJECT} at (486.38,0) size 117.59x117.59 + LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/android/css2.1/20110323/inline-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/android/css2.1/20110323/inline-replaced-height-008-expected.txt new file mode 100644 index 0000000..6c8b046 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/css2.1/20110323/inline-replaced-height-008-expected.txt
@@ -0,0 +1,38 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x202 + LayoutBlockFlow {HTML} at (0,0) size 800x202 + LayoutBlockFlow {BODY} at (8,16) size 784x178 + LayoutBlockFlow {P} at (0,0) size 784x40 + LayoutText {#text} at (0,0) size 101x19 + text run at (0,0) width 101: "There should be " + LayoutInline {STRONG} at (0,0) size 143x19 + LayoutText {#text} at (101,0) size 143x19 + text run at (101,0) width 143: "5 filled green squares" + LayoutText {#text} at (243,0) size 176x19 + text run at (243,0) width 176: " with the same width and the " + LayoutInline {STRONG} at (0,0) size 81x19 + LayoutText {#text} at (418,0) size 81x19 + text run at (418,0) width 81: "same height" + LayoutText {#text} at (498,0) size 229x19 + text run at (498,0) width 229: ". The 5 filled green squares should be " + LayoutInline {STRONG} at (0,0) size 57x19 + LayoutText {#text} at (726,0) size 57x19 + text run at (726,0) width 57: "identical" + LayoutText {#text} at (0,20) size 417x19 + text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." + LayoutBlockFlow {DIV} at (0,56) size 784x122 + LayoutImage {IMG} at (0,0) size 117.59x117.59 + LayoutText {#text} at (117,102) size 5x19 + text run at (117,102) width 5: " " + LayoutImage {IMG} at (121.59,0) size 117.59x117.59 + LayoutText {#text} at (239,102) size 5x19 + text run at (239,102) width 5: " " + LayoutImage {IMG} at (243.19,0) size 117.59x117.59 + LayoutText {#text} at (360,102) size 5x19 + text run at (360,102) width 5: " " + LayoutImage {IMG} at (364.78,0) size 117.59x117.59 + LayoutText {#text} at (482,102) size 5x19 + text run at (482,102) width 5: " " + LayoutImage {IMG} at (486.38,0) size 117.59x117.59 + LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/gradients/list-item-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/android/fast/gradients/list-item-gradient-expected.png new file mode 100644 index 0000000..a5bc3d9d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/fast/gradients/list-item-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/gradients/unprefixed-list-item-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/android/fast/gradients/unprefixed-list-item-gradient-expected.png new file mode 100644 index 0000000..ec484036 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/fast/gradients/unprefixed-list-item-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt new file mode 100644 index 0000000..af069c7b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt
@@ -0,0 +1,256 @@ +88 +finished layout: LayoutImage logicalHeight: 34.7188 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 34.7188 +finished layout: LayoutImage logicalHeight: 34.7188 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +0x2cd10882cc10 pre logicalHeight: 35 +0x2cd10882cc10 logicalHeight: 29 +0x2cd10882cc10 final logicalHeight: 29 +0x2cd10882cd90 pre logicalHeight: 35 +0x2cd10882cd90 logicalHeight: 29 +0x2cd10882cd90 final logicalHeight: 29 +0x2cd10882cf10 pre logicalHeight: 35 +0x2cd10882cf10 logicalHeight: 29 +0x2cd10882cf10 final logicalHeight: 29 +0x2cd10882d090 pre logicalHeight: 35 +0x2cd10882d090 logicalHeight: 29 +0x2cd10882d090 final logicalHeight: 29 +0x2cd10882d210 pre logicalHeight: 35 +0x2cd10882d210 logicalHeight: 29 +0x2cd10882d210 final logicalHeight: 29 +0x2cd10882d390 pre logicalHeight: 35 +0x2cd10882d390 logicalHeight: 29 +0x2cd10882d390 final logicalHeight: 29 +0x2cd10882d510 pre logicalHeight: 35 +0x2cd10882d510 logicalHeight: 29 +0x2cd10882d510 final logicalHeight: 29 +0x2cd10882d690 pre logicalHeight: 35 +0x2cd10882d690 logicalHeight: 29 +0x2cd10882d690 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882cc10 pre logicalHeight: 29 +0x2cd10882cc10 logicalHeight: 29 +0x2cd10882cc10 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882cd90 pre logicalHeight: 29 +0x2cd10882cd90 logicalHeight: 29 +0x2cd10882cd90 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882cf10 pre logicalHeight: 29 +0x2cd10882cf10 logicalHeight: 29 +0x2cd10882cf10 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882d090 pre logicalHeight: 29 +0x2cd10882d090 logicalHeight: 29 +0x2cd10882d090 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882d210 pre logicalHeight: 29 +0x2cd10882d210 logicalHeight: 29 +0x2cd10882d210 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882d390 pre logicalHeight: 29 +0x2cd10882d390 logicalHeight: 29 +0x2cd10882d390 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882d510 pre logicalHeight: 29 +0x2cd10882d510 logicalHeight: 29 +0x2cd10882d510 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 34.7188 +finished layout: LayoutEmbeddedObject logicalHeight: 34.7188 +0x2cd10882d690 pre logicalHeight: 29 +0x2cd10882d690 logicalHeight: 29 +0x2cd10882d690 final logicalHeight: 29 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutImage, linebox height: 50 +finished layout: LayoutImage logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 50 +finished layout: LayoutEmbeddedObject logicalHeight: 50 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +0x2cd10882cc10 pre logicalHeight: 29 +0x2cd10882cc10 logicalHeight: 42 +0x2cd10882cc10 final logicalHeight: 42 +0x2cd10882cd90 pre logicalHeight: 29 +0x2cd10882cd90 logicalHeight: 42 +0x2cd10882cd90 final logicalHeight: 42 +0x2cd10882cf10 pre logicalHeight: 29 +0x2cd10882cf10 logicalHeight: 42 +0x2cd10882cf10 final logicalHeight: 42 +0x2cd10882d090 pre logicalHeight: 29 +0x2cd10882d090 logicalHeight: 42 +0x2cd10882d090 final logicalHeight: 42 +0x2cd10882d210 pre logicalHeight: 29 +0x2cd10882d210 logicalHeight: 42 +0x2cd10882d210 final logicalHeight: 42 +0x2cd10882d390 pre logicalHeight: 29 +0x2cd10882d390 logicalHeight: 42 +0x2cd10882d390 final logicalHeight: 42 +0x2cd10882d510 pre logicalHeight: 29 +0x2cd10882d510 logicalHeight: 42 +0x2cd10882d510 final logicalHeight: 42 +0x2cd10882d690 pre logicalHeight: 29 +0x2cd10882d690 logicalHeight: 42 +0x2cd10882d690 final logicalHeight: 42 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 160 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 160 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 160 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 160 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 160 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 160 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +0x2cd10882d690 pre logicalHeight: 0 +0x2cd10882d690 logicalHeight: 220 +0x2cd10882d690 final logicalHeight: 220 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +0x2cd10882d510 pre logicalHeight: 0 +0x2cd10882d510 logicalHeight: 220 +0x2cd10882d510 final logicalHeight: 220 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +0x2cd10882d690 pre logicalHeight: 220 +0x2cd10882d690 logicalHeight: 220 +0x2cd10882d690 final logicalHeight: 220 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +placeBoxesInBlockDirection object: LayoutText, linebox height: 19 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +finished layout: LayoutEmbeddedObject logicalHeight: 230 +0x2cd10882d510 pre logicalHeight: 220 +0x2cd10882d510 logicalHeight: 220 +0x2cd10882d510 final logicalHeight: 220 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 190.328 +placeBoxesInBlockDirection object: LayoutText, linebox height: 15 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 190.328 +finished layout: LayoutEmbeddedObject logicalHeight: 190.328 +finished layout: LayoutEmbeddedObject logicalHeight: 190.328 +0x2cd10882d690 pre logicalHeight: 220 +0x2cd10882d690 logicalHeight: 183 +0x2cd10882d690 final logicalHeight: 183 +0x2cd10882d510 pre logicalHeight: 220 +0x2cd10882d510 logicalHeight: 183 +0x2cd10882d510 final logicalHeight: 183 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 190.328 +placeBoxesInBlockDirection object: LayoutText, linebox height: 15 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 190.328 +finished layout: LayoutEmbeddedObject logicalHeight: 190.328 +finished layout: LayoutEmbeddedObject logicalHeight: 190.328 +0x2cd10882d690 pre logicalHeight: 183 +0x2cd10882d690 logicalHeight: 183 +0x2cd10882d690 final logicalHeight: 183 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 190.328 +placeBoxesInBlockDirection object: LayoutText, linebox height: 15 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 190.328 +finished layout: LayoutEmbeddedObject logicalHeight: 190.328 +finished layout: LayoutEmbeddedObject logicalHeight: 190.328 +0x2cd10882d510 pre logicalHeight: 183 +0x2cd10882d510 logicalHeight: 183 +0x2cd10882d510 final logicalHeight: 183 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 158.922 +placeBoxesInBlockDirection object: LayoutText, linebox height: 14 +placeBoxesInBlockDirection object: LayoutEmbeddedObject, linebox height: 158.922 +finished layout: Content-Type: text/plain +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x173 + LayoutBlockFlow {HTML} at (0,0) size 800x173.09 + LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x162 + LayoutText {#text} at (158,148) size 4x14 + text run at (158,148) width 4: " " + LayoutText {#text} at (0,0) size 0x0 + LayoutText {#text} at (0,0) size 0x0 + LayoutText {#text} at (0,0) size 0x0 + LayoutText {#text} at (0,0) size 0x0 + LayoutText {#text} at (0,0) size 0x0 +layer at (6,6) size 159x159 + LayoutEmbeddedObject {OBJECT} at (0,0) size 158.92x158.92 [border: (1px dashed #800000)] + layer at (0,0) size 153x153 + LayoutView at (0,0) size 153x153 + layer at (0,0) size 153x153 + LayoutSVGRoot {svg} at (0,0) size 153x153 + LayoutSVGEllipse {circle} at (0,0) size 153x153 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00] +layer at (167,6) size 159x159 + LayoutEmbeddedObject {OBJECT} at (161.92,0) size 158.92x158.92 [border: (1px dashed #800000)] + layer at (0,0) size 153x153 + LayoutView at (0,0) size 153x153 + layer at (0,0) size 153x153 + LayoutSVGRoot {svg} at (0,0) size 153x153 + LayoutSVGEllipse {circle} at (0,0) size 153x153 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00]
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/perpendicular-layer-sorting-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/perpendicular-layer-sorting-expected.png index b45d024..5ff3e6b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/compositing/perpendicular-layer-sorting-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/perpendicular-layer-sorting-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.png index 12530b88..a7ece17 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.txt index ba32ef09..16fefb4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/video-frame-size-change-expected.txt
@@ -12,16 +12,16 @@ LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 layer at (8,52) size 320x180 - LayoutVideo {VIDEO} at (0,0) size 320x180 + LayoutVideo {VIDEO} at (0,0) size 320x179.98 layer at (332,52) size 320x180 - LayoutVideo {VIDEO} at (324,0) size 320x180 + LayoutVideo {VIDEO} at (324,0) size 320x179.98 layer at (8,52) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (8,52) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98 layer at (332,52) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (332,52) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.png index fdfb709..c9883bd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.txt index b7e46f5..a50b2f2 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css1/box_properties/width-expected.txt
@@ -22,7 +22,7 @@ LayoutText {#text} at (0,0) size 271x19 text run at (0,0) width 271: "The square above should be fifty pixels wide." LayoutBlockFlow (anonymous) at (0,209) size 769x385 - LayoutImage {IMG} at (0,0) size 384.50x385 + LayoutImage {IMG} at (0,0) size 384.50x384.50 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,610) size 769x20 LayoutText {#text} at (0,0) size 641x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.png index 2d44440..1b02773 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.txt index 632b0ff..3c38ba40 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css1/formatting_model/replaced_elements-expected.txt
@@ -1,8 +1,8 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2379 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2377 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x2379 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x2379 - LayoutBlockFlow {BODY} at (8,8) size 769x2363 [bgcolor=#CCCCCC] +layer at (0,0) size 785x2377 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x2377 + LayoutBlockFlow {BODY} at (8,8) size 769x2361 [bgcolor=#CCCCCC] LayoutBlockFlow {P} at (0,0) size 769x20 LayoutText {#text} at (0,0) size 337x19 text run at (0,0) width 337: "The style declarations which apply to the text below are:" @@ -37,30 +37,30 @@ LayoutBlockFlow {P} at (0,329) size 769x20 LayoutText {#text} at (0,0) size 383x19 text run at (0,0) width 383: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (192.25,365) size 384.50x385 - LayoutBlockFlow {P} at (0,766) size 769x40 + LayoutImage {IMG} at (192.25,365) size 384.50x384.50 + LayoutBlockFlow {P} at (0,765.50) size 769x40 LayoutText {#text} at (0,0) size 750x39 text run at (0,0) width 750: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally within the" text run at (0,20) width 95: "parent element: " text run at (95,20) width 375: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (384.50,822) size 384.50x385 - LayoutBlockFlow {P} at (0,1223) size 769x40 + LayoutImage {IMG} at (384.50,821.50) size 384.50x384.50 + LayoutBlockFlow {P} at (0,1222) size 769x40 LayoutText {#text} at (0,0) size 765x39 text run at (0,0) width 765: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge of the parent" text run at (0,20) width 53: "element: " text run at (53,20) width 375: "the document body in the first half, and the table in the second." - LayoutTable {TABLE} at (0,1279) size 769x1084 [border: (1px outset #808080)] - LayoutTableSection {TBODY} at (1,1) size 767x1082 + LayoutTable {TABLE} at (0,1278) size 769x1083 [border: (1px outset #808080)] + LayoutTableSection {TBODY} at (1,1) size 767x1081 LayoutTableRow {TR} at (0,0) size 767x28 LayoutTableCell {TD} at (0,0) size 767x28 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] LayoutInline {STRONG} at (0,0) size 157x19 LayoutText {#text} at (4,4) size 157x19 text run at (4,4) width 157: "TABLE Testing Section" - LayoutTableRow {TR} at (0,28) size 767x1054 - LayoutTableCell {TD} at (0,541) size 12x28 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] + LayoutTableRow {TR} at (0,28) size 767x1053 + LayoutTableCell {TD} at (0,540) size 12x28 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (4,4) size 4x19 text run at (4,4) width 4: " " - LayoutTableCell {TD} at (12,28) size 755x1054 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (12,28) size 755x1053 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] LayoutBlockFlow {P} at (4,4) size 747x20 LayoutImage {IMG} at (0,0) size 15x15 LayoutText {#text} at (15,0) size 411x19 @@ -73,14 +73,14 @@ LayoutBlockFlow {P} at (4,138) size 747x20 LayoutText {#text} at (0,0) size 383x19 text run at (0,0) width 383: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (190.75,174) size 373.50x374 - LayoutBlockFlow {P} at (4,564) size 747x40 + LayoutImage {IMG} at (190.75,174) size 373.50x373.50 + LayoutBlockFlow {P} at (4,563.50) size 747x40 LayoutText {#text} at (0,0) size 728x39 text run at (0,0) width 728: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally within" text run at (0,20) width 117: "the parent element: " text run at (117,20) width 375: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (377.50,620) size 373.50x374 - LayoutBlockFlow {P} at (4,1010) size 747x40 + LayoutImage {IMG} at (377.50,619.50) size 373.50x373.50 + LayoutBlockFlow {P} at (4,1009) size 747x40 LayoutText {#text} at (0,0) size 723x39 text run at (0,0) width 723: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge of the" text run at (0,20) width 95: "parent element: "
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.png index 5ecb867c..dabc8ae 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.txt index e15604b..9e0b192 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css1/text_properties/vertical_align-expected.txt
@@ -174,26 +174,26 @@ LayoutImage {IMG} at (44,125) size 6x20 LayoutText {#text} at (50,125) size 96x21 text run at (50,125) width 96: " all of which " - LayoutImage {IMG} at (146,125) size 20x65 - LayoutText {#text} at (166,125) size 5x21 - text run at (166,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 254x42 - LayoutText {#text} at (171,108) size 254x42 - text run at (171,108) width 254: "should be aligned" - LayoutText {#text} at (425,125) size 5x21 - text run at (425,125) width 5: " " - LayoutImage {IMG} at (430,125) size 11x35 - LayoutText {#text} at (441,125) size 119x21 - text run at (441,125) width 119: " with the top of " - LayoutImage {IMG} at (560,125) size 9x30 - LayoutText {#text} at (569,125) size 5x21 - text run at (569,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 686x146 - LayoutText {#text} at (574,115) size 19x33 - text run at (574,115) width 19: "a " - LayoutInline {SPAN} at (0,0) size 686x193 - LayoutText {#text} at (593,78) size 686x193 - text run at (593,78) width 93: "14-" + LayoutImage {IMG} at (146,125) size 19.50x65 + LayoutText {#text} at (165,125) size 6x21 + text run at (165,125) width 6: " " + LayoutInline {SPAN} at (0,0) size 255x42 + LayoutText {#text} at (170,108) size 255x42 + text run at (170,108) width 255: "should be aligned" + LayoutText {#text} at (424,125) size 6x21 + text run at (424,125) width 6: " " + LayoutImage {IMG} at (429.50,125) size 10.50x35 + LayoutText {#text} at (440,125) size 119x21 + text run at (440,125) width 119: " with the top of " + LayoutImage {IMG} at (559,125) size 9x30 + LayoutText {#text} at (568,125) size 5x21 + text run at (568,125) width 5: " " + LayoutInline {SPAN} at (0,0) size 685x146 + LayoutText {#text} at (573,115) size 19x33 + text run at (573,115) width 19: "a " + LayoutInline {SPAN} at (0,0) size 685x193 + LayoutText {#text} at (592,78) size 685x193 + text run at (592,78) width 93: "14-" text run at (0,191) width 142: "point" LayoutText {#text} at (142,228) size 142x33 text run at (142,228) width 142: " text element" @@ -207,15 +207,15 @@ text run at (309,239) width 176: "regardless of the line in which" LayoutText {#text} at (485,238) size 5x21 text run at (485,238) width 5: " " - LayoutImage {IMG} at (490,238) size 5x15 - LayoutText {#text} at (495,238) size 5x21 - text run at (495,238) width 5: " " - LayoutInline {BIG} at (0,0) size 151x23 - LayoutText {#text} at (500,236) size 151x23 - text run at (500,236) width 151: "the images appear." + LayoutImage {IMG} at (490,238) size 4.50x15 + LayoutText {#text} at (494,238) size 6x21 + text run at (494,238) width 6: " " + LayoutInline {BIG} at (0,0) size 152x23 + LayoutText {#text} at (499,236) size 152x23 + text run at (499,236) width 152: "the images appear." LayoutText {#text} at (650,238) size 6x21 text run at (650,238) width 6: " " - LayoutImage {IMG} at (655.81,238) size 27x90 + LayoutImage {IMG} at (655.31,238) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1829.31) size 769x40 LayoutText {#text} at (0,0) size 751x39 @@ -246,46 +246,46 @@ LayoutImage {IMG} at (666,24) size 6x20 LayoutText {#text} at (672,22) size 76x19 text run at (672,22) width 76: " all of which " - LayoutImage {IMG} at (748,1) size 20x65 + LayoutImage {IMG} at (748,1) size 19.50x65 LayoutText {#text} at (0,106) size 108x19 text run at (0,106) width 108: "should be aligned " - LayoutImage {IMG} at (108,100) size 11x35 - LayoutText {#text} at (119,106) size 4x19 - text run at (119,106) width 4: " " - LayoutInline {SPAN} at (0,0) size 231x36 - LayoutText {#text} at (123,93) size 231x36 - text run at (123,93) width 231: "with the middle of" - LayoutText {#text} at (354,106) size 4x19 - text run at (354,106) width 4: " " - LayoutImage {IMG} at (358,93) size 15x50 - LayoutText {#text} at (373,106) size 4x19 - text run at (373,106) width 4: " " - LayoutInline {SPAN} at (0,0) size 345x27 - LayoutText {#text} at (377,100) size 17x27 - text run at (377,100) width 17: "a " - LayoutInline {SPAN} at (0,0) size 203x68 - LayoutText {#text} at (394,67) size 203x68 - text run at (394,67) width 203: "14-point" - LayoutText {#text} at (597,100) size 125x27 - text run at (597,100) width 125: " text element" - LayoutText {#text} at (722,106) size 4x19 - text run at (722,106) width 4: " " - LayoutImage {IMG} at (726,93) size 15x50 + LayoutImage {IMG} at (108,100) size 10.50x35 + LayoutText {#text} at (118,106) size 5x19 + text run at (118,106) width 5: " " + LayoutInline {SPAN} at (0,0) size 232x36 + LayoutText {#text} at (122,93) size 232x36 + text run at (122,93) width 232: "with the middle of" + LayoutText {#text} at (353,106) size 5x19 + text run at (353,106) width 5: " " + LayoutImage {IMG} at (357.50,93) size 15x50 + LayoutText {#text} at (372,106) size 5x19 + text run at (372,106) width 5: " " + LayoutInline {SPAN} at (0,0) size 346x27 + LayoutText {#text} at (376,100) size 18x27 + text run at (376,100) width 18: "a " + LayoutInline {SPAN} at (0,0) size 204x68 + LayoutText {#text} at (393,67) size 204x68 + text run at (393,67) width 204: "14-point" + LayoutText {#text} at (596,100) size 126x27 + text run at (596,100) width 126: " text element" + LayoutText {#text} at (721,106) size 5x19 + text run at (721,106) width 5: " " + LayoutImage {IMG} at (725.50,93) size 15x50 LayoutText {#text} at (0,0) size 0x0 LayoutInline {SMALL} at (0,0) size 176x19 LayoutText {#text} at (0,176) size 176x19 text run at (0,176) width 176: "regardless of the line in which" LayoutText {#text} at (176,176) size 4x19 text run at (176,176) width 4: " " - LayoutImage {IMG} at (180,180) size 5x15 - LayoutText {#text} at (185,176) size 4x19 - text run at (185,176) width 4: " " - LayoutInline {BIG} at (0,0) size 151x23 - LayoutText {#text} at (189,173) size 151x23 - text run at (189,173) width 151: "the images appear." + LayoutImage {IMG} at (180,180) size 4.50x15 + LayoutText {#text} at (184,176) size 5x19 + text run at (184,176) width 5: " " + LayoutInline {BIG} at (0,0) size 152x23 + LayoutText {#text} at (188,173) size 152x23 + text run at (188,173) width 152: "the images appear." LayoutText {#text} at (339,176) size 5x19 text run at (339,176) width 5: " " - LayoutImage {IMG} at (343.81,143) size 27x90 + LayoutImage {IMG} at (343.31,143) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,2134.31) size 769x40 LayoutText {#text} at (0,0) size 753x39 @@ -338,36 +338,36 @@ text run at (10,48) width 68: "all of which" LayoutText {#text} at (78,48) size 4x19 text run at (78,48) width 4: " " - LayoutImage {IMG} at (82,48) size 20x65 - LayoutText {#text} at (102,48) size 4x19 - text run at (102,48) width 4: " " - LayoutInline {SPAN} at (0,0) size 104x19 - LayoutText {#text} at (106,48) size 104x19 - text run at (106,48) width 104: "should be aligned" - LayoutText {#text} at (210,48) size 4x19 - text run at (210,48) width 4: " " - LayoutImage {IMG} at (214,48) size 11x35 - LayoutText {#text} at (225,48) size 4x19 - text run at (225,48) width 4: " " + LayoutImage {IMG} at (82,48) size 19.50x65 + LayoutText {#text} at (101,48) size 5x19 + text run at (101,48) width 5: " " + LayoutInline {SPAN} at (0,0) size 105x19 + LayoutText {#text} at (105,48) size 105x19 + text run at (105,48) width 105: "should be aligned" + LayoutText {#text} at (209,48) size 5x19 + text run at (209,48) width 5: " " + LayoutImage {IMG} at (213.50,48) size 10.50x35 + LayoutText {#text} at (224,48) size 4x19 + text run at (224,48) width 4: " " LayoutInline {SPAN} at (0,0) size 185x36 - LayoutText {#text} at (229,48) size 185x36 - text run at (229,48) width 185: "with the top of" - LayoutText {#text} at (414,48) size 4x19 - text run at (414,48) width 4: " " - LayoutImage {IMG} at (418,48) size 15x50 - LayoutText {#text} at (433,48) size 4x19 - text run at (433,48) width 4: " " + LayoutText {#text} at (228,48) size 185x36 + text run at (228,48) width 185: "with the top of" + LayoutText {#text} at (413,48) size 4x19 + text run at (413,48) width 4: " " + LayoutImage {IMG} at (417,48) size 15x50 + LayoutText {#text} at (432,48) size 4x19 + text run at (432,48) width 4: " " LayoutInline {SPAN} at (0,0) size 120x19 - LayoutText {#text} at (437,48) size 120x19 - text run at (437,48) width 120: "the tallest element in" - LayoutText {#text} at (557,48) size 4x19 - text run at (557,48) width 4: " " - LayoutImage {IMG} at (561,48) size 5x15 - LayoutText {#text} at (566,48) size 4x19 - text run at (566,48) width 4: " " - LayoutInline {BIG} at (0,0) size 721x88 - LayoutText {#text} at (570,48) size 721x88 - text run at (570,48) width 151: "whichever line the" + LayoutText {#text} at (436,48) size 120x19 + text run at (436,48) width 120: "the tallest element in" + LayoutText {#text} at (556,48) size 4x19 + text run at (556,48) width 4: " " + LayoutImage {IMG} at (560,48) size 4.50x15 + LayoutText {#text} at (564,48) size 5x19 + text run at (564,48) width 5: " " + LayoutInline {BIG} at (0,0) size 720x88 + LayoutText {#text} at (568,48) size 720x88 + text run at (568,48) width 152: "whichever line the" text run at (0,113) width 136: "elements appear." LayoutText {#text} at (135,113) size 5x19 text run at (135,113) width 5: " " @@ -514,26 +514,26 @@ LayoutImage {IMG} at (70,125) size 6x20 LayoutText {#text} at (76,125) size 96x21 text run at (76,125) width 96: " all of which " - LayoutImage {IMG} at (172,125) size 20x65 - LayoutText {#text} at (192,125) size 5x21 - text run at (192,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 254x42 - LayoutText {#text} at (197,108) size 254x42 - text run at (197,108) width 254: "should be aligned" - LayoutText {#text} at (451,125) size 5x21 - text run at (451,125) width 5: " " - LayoutImage {IMG} at (456,125) size 11x35 - LayoutText {#text} at (467,125) size 119x21 - text run at (467,125) width 119: " with the top of " - LayoutImage {IMG} at (586,125) size 9x30 - LayoutText {#text} at (595,125) size 5x21 - text run at (595,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 712x146 - LayoutText {#text} at (600,115) size 19x33 - text run at (600,115) width 19: "a " - LayoutInline {SPAN} at (0,0) size 712x193 - LayoutText {#text} at (619,78) size 712x193 - text run at (619,78) width 93: "14-" + LayoutImage {IMG} at (172,125) size 19.50x65 + LayoutText {#text} at (191,125) size 6x21 + text run at (191,125) width 6: " " + LayoutInline {SPAN} at (0,0) size 255x42 + LayoutText {#text} at (196,108) size 255x42 + text run at (196,108) width 255: "should be aligned" + LayoutText {#text} at (450,125) size 6x21 + text run at (450,125) width 6: " " + LayoutImage {IMG} at (455.50,125) size 10.50x35 + LayoutText {#text} at (466,125) size 119x21 + text run at (466,125) width 119: " with the top of " + LayoutImage {IMG} at (585,125) size 9x30 + LayoutText {#text} at (594,125) size 5x21 + text run at (594,125) width 5: " " + LayoutInline {SPAN} at (0,0) size 711x146 + LayoutText {#text} at (599,115) size 19x33 + text run at (599,115) width 19: "a " + LayoutInline {SPAN} at (0,0) size 711x193 + LayoutText {#text} at (618,78) size 711x193 + text run at (618,78) width 93: "14-" text run at (0,191) width 142: "point" LayoutText {#text} at (142,228) size 142x33 text run at (142,228) width 142: " text element" @@ -547,15 +547,15 @@ text run at (309,239) width 176: "regardless of the line in which" LayoutText {#text} at (485,238) size 5x21 text run at (485,238) width 5: " " - LayoutImage {IMG} at (490,238) size 5x15 - LayoutText {#text} at (495,238) size 5x21 - text run at (495,238) width 5: " " - LayoutInline {BIG} at (0,0) size 151x23 - LayoutText {#text} at (500,236) size 151x23 - text run at (500,236) width 151: "the images appear." + LayoutImage {IMG} at (490,238) size 4.50x15 + LayoutText {#text} at (494,238) size 6x21 + text run at (494,238) width 6: " " + LayoutInline {BIG} at (0,0) size 152x23 + LayoutText {#text} at (499,236) size 152x23 + text run at (499,236) width 152: "the images appear." LayoutText {#text} at (650,238) size 6x21 text run at (650,238) width 6: " " - LayoutImage {IMG} at (655.81,238) size 27x90 + LayoutImage {IMG} at (655.31,238) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1478.31) size 747x40 LayoutText {#text} at (0,0) size 705x39 @@ -586,28 +586,28 @@ LayoutImage {IMG} at (666,24) size 6x20 LayoutText {#text} at (672,22) size 72x19 text run at (672,22) width 72: " all of which" - LayoutImage {IMG} at (0,78) size 20x65 - LayoutText {#text} at (20,99) size 112x19 - text run at (20,99) width 112: " should be aligned " - LayoutImage {IMG} at (132,93) size 11x35 - LayoutText {#text} at (143,99) size 4x19 - text run at (143,99) width 4: " " + LayoutImage {IMG} at (0,78) size 19.50x65 + LayoutText {#text} at (19,99) size 113x19 + text run at (19,99) width 113: " should be aligned " + LayoutImage {IMG} at (131.50,93) size 10.50x35 + LayoutText {#text} at (142,99) size 4x19 + text run at (142,99) width 4: " " LayoutInline {SPAN} at (0,0) size 231x36 - LayoutText {#text} at (147,86) size 231x36 - text run at (147,86) width 231: "with the middle of" - LayoutText {#text} at (378,99) size 4x19 - text run at (378,99) width 4: " " - LayoutImage {IMG} at (382,86) size 15x50 - LayoutText {#text} at (397,99) size 4x19 - text run at (397,99) width 4: " " + LayoutText {#text} at (146,86) size 231x36 + text run at (146,86) width 231: "with the middle of" + LayoutText {#text} at (377,99) size 4x19 + text run at (377,99) width 4: " " + LayoutImage {IMG} at (381,86) size 15x50 + LayoutText {#text} at (396,99) size 4x19 + text run at (396,99) width 4: " " LayoutInline {SPAN} at (0,0) size 345x27 - LayoutText {#text} at (401,93) size 17x27 - text run at (401,93) width 17: "a " + LayoutText {#text} at (400,93) size 17x27 + text run at (400,93) width 17: "a " LayoutInline {SPAN} at (0,0) size 203x68 - LayoutText {#text} at (418,60) size 203x68 - text run at (418,60) width 203: "14-point" - LayoutText {#text} at (621,93) size 125x27 - text run at (621,93) width 125: " text element" + LayoutText {#text} at (417,60) size 203x68 + text run at (417,60) width 203: "14-point" + LayoutText {#text} at (620,93) size 125x27 + text run at (620,93) width 125: " text element" LayoutText {#text} at (0,0) size 0x0 LayoutImage {IMG} at (0,163) size 15x50 LayoutText {#text} at (15,176) size 4x19 @@ -617,15 +617,15 @@ text run at (19,176) width 176: "regardless of the line in which" LayoutText {#text} at (195,176) size 4x19 text run at (195,176) width 4: " " - LayoutImage {IMG} at (199,180) size 5x15 - LayoutText {#text} at (204,176) size 4x19 - text run at (204,176) width 4: " " - LayoutInline {BIG} at (0,0) size 151x23 - LayoutText {#text} at (208,173) size 151x23 - text run at (208,173) width 151: "the images appear." + LayoutImage {IMG} at (199,180) size 4.50x15 + LayoutText {#text} at (203,176) size 5x19 + text run at (203,176) width 5: " " + LayoutInline {BIG} at (0,0) size 152x23 + LayoutText {#text} at (207,173) size 152x23 + text run at (207,173) width 152: "the images appear." LayoutText {#text} at (358,176) size 5x19 text run at (358,176) width 5: " " - LayoutImage {IMG} at (362.81,143) size 27x90 + LayoutImage {IMG} at (362.31,143) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1783.31) size 747x40 LayoutText {#text} at (0,0) size 740x39 @@ -679,36 +679,36 @@ text run at (69,48) width 68: "all of which" LayoutText {#text} at (137,48) size 4x19 text run at (137,48) width 4: " " - LayoutImage {IMG} at (141,48) size 20x65 - LayoutText {#text} at (161,48) size 4x19 - text run at (161,48) width 4: " " - LayoutInline {SPAN} at (0,0) size 104x19 - LayoutText {#text} at (165,48) size 104x19 - text run at (165,48) width 104: "should be aligned" - LayoutText {#text} at (269,48) size 4x19 - text run at (269,48) width 4: " " - LayoutImage {IMG} at (273,48) size 11x35 - LayoutText {#text} at (284,48) size 4x19 - text run at (284,48) width 4: " " + LayoutImage {IMG} at (141,48) size 19.50x65 + LayoutText {#text} at (160,48) size 5x19 + text run at (160,48) width 5: " " + LayoutInline {SPAN} at (0,0) size 105x19 + LayoutText {#text} at (164,48) size 105x19 + text run at (164,48) width 105: "should be aligned" + LayoutText {#text} at (268,48) size 5x19 + text run at (268,48) width 5: " " + LayoutImage {IMG} at (272.50,48) size 10.50x35 + LayoutText {#text} at (283,48) size 4x19 + text run at (283,48) width 4: " " LayoutInline {SPAN} at (0,0) size 185x36 - LayoutText {#text} at (288,48) size 185x36 - text run at (288,48) width 185: "with the top of" - LayoutText {#text} at (473,48) size 4x19 - text run at (473,48) width 4: " " - LayoutImage {IMG} at (477,48) size 15x50 - LayoutText {#text} at (492,48) size 4x19 - text run at (492,48) width 4: " " + LayoutText {#text} at (287,48) size 185x36 + text run at (287,48) width 185: "with the top of" + LayoutText {#text} at (472,48) size 4x19 + text run at (472,48) width 4: " " + LayoutImage {IMG} at (476,48) size 15x50 + LayoutText {#text} at (491,48) size 4x19 + text run at (491,48) width 4: " " LayoutInline {SPAN} at (0,0) size 120x19 - LayoutText {#text} at (496,48) size 120x19 - text run at (496,48) width 120: "the tallest element in" - LayoutText {#text} at (616,48) size 4x19 - text run at (616,48) width 4: " " - LayoutImage {IMG} at (620,48) size 5x15 - LayoutText {#text} at (625,48) size 4x19 - text run at (625,48) width 4: " " - LayoutInline {BIG} at (0,0) size 714x88 - LayoutText {#text} at (629,48) size 714x88 - text run at (629,48) width 85: "whichever" + LayoutText {#text} at (495,48) size 120x19 + text run at (495,48) width 120: "the tallest element in" + LayoutText {#text} at (615,48) size 4x19 + text run at (615,48) width 4: " " + LayoutImage {IMG} at (619,48) size 4.50x15 + LayoutText {#text} at (623,48) size 5x19 + text run at (623,48) width 5: " " + LayoutInline {BIG} at (0,0) size 713x88 + LayoutText {#text} at (627,48) size 713x88 + text run at (627,48) width 86: "whichever" text run at (0,113) width 202: "line the elements appear." LayoutText {#text} at (201,113) size 5x19 text run at (201,113) width 5: " "
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/floating-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/floating-replaced-height-008-expected.txt index c1d67f8..217278b6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/floating-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/floating-replaced-height-008-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x190 - LayoutBlockFlow {HTML} at (0,0) size 800x190 + LayoutBlockFlow {HTML} at (0,0) size 800x189.59 LayoutBlockFlow {BODY} at (8,16) size 784x40 LayoutBlockFlow {P} at (0,0) size 784x40 LayoutText {#text} at (0,0) size 101x19 @@ -22,12 +22,12 @@ LayoutText {#text} at (0,20) size 417x19 text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,56) size 784x0 - LayoutImage (floating) {IMG} at (0,0) size 117.59x118 + LayoutImage (floating) {IMG} at (0,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (117.59,0) size 5x5 - LayoutImage (floating) {IMG} at (122.59,0) size 117.59x118 + LayoutImage (floating) {IMG} at (122.59,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (240.19,0) size 5x5 - LayoutImage (floating) {IMG} at (245.19,0) size 117.59x118 + LayoutImage (floating) {IMG} at (245.19,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (362.78,0) size 5x5 - LayoutImage (floating) {IMG} at (367.78,0) size 117.59x118 + LayoutImage (floating) {IMG} at (367.78,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (485.38,0) size 5x5 - LayoutImage (floating) {IMG} at (490.38,0) size 117.59x118 + LayoutImage (floating) {IMG} at (490.38,0) size 117.59x117.59
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-block-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-block-replaced-height-008-expected.txt index f39d8c03..fdfcce4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-block-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-block-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (0,20) size 417x19 text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,56) size 784x123 - LayoutImage {OBJECT} at (0,0) size 117.59x118 + LayoutImage {OBJECT} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,103) size 5x19 text run at (117,103) width 5: " " - LayoutImage {OBJECT} at (121.59,0) size 117.59x118 + LayoutImage {OBJECT} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,103) size 5x19 text run at (239,103) width 5: " " - LayoutImage {OBJECT} at (243.19,0) size 117.59x118 + LayoutImage {OBJECT} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,103) size 5x19 text run at (360,103) width 5: " " - LayoutImage {OBJECT} at (364.78,0) size 117.59x118 + LayoutImage {OBJECT} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,103) size 5x19 text run at (482,103) width 5: " " - LayoutImage {OBJECT} at (486.38,0) size 117.59x118 + LayoutImage {OBJECT} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-replaced-height-008-expected.txt index 32dbf16..82e798f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/inline-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (0,20) size 417x19 text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,56) size 784x123 - LayoutImage {IMG} at (0,0) size 117.59x118 + LayoutImage {IMG} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,103) size 5x19 text run at (117,103) width 5: " " - LayoutImage {IMG} at (121.59,0) size 117.59x118 + LayoutImage {IMG} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,103) size 5x19 text run at (239,103) width 5: " " - LayoutImage {IMG} at (243.19,0) size 117.59x118 + LayoutImage {IMG} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,103) size 5x19 text run at (360,103) width 5: " " - LayoutImage {IMG} at (364.78,0) size 117.59x118 + LayoutImage {IMG} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,103) size 5x19 text run at (482,103) width 5: " " - LayoutImage {IMG} at (486.38,0) size 117.59x118 + LayoutImage {IMG} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt index 373f95c..a0ed58b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt
@@ -10,6 +10,6 @@ LayoutText {#text} at (32,0) size 36x19 text run at (32,0) width 36: "TEST" LayoutBlockFlow {DIV} at (16,72) size 192x20 [color=#FFFF00] [bgcolor=#000080] - LayoutImage {IMG} at (0,14) size 32x1.59 + LayoutImage {IMG} at (0,13) size 32x1.59 LayoutText {#text} at (32,0) size 36x19 text run at (32,0) width 36: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css2.1/t090501-c414-flt-03-b-g-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css2.1/t090501-c414-flt-03-b-g-expected.txt index d83bf58b..a5f7c11 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css2.1/t090501-c414-flt-03-b-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/css2.1/t090501-c414-flt-03-b-g-expected.txt
@@ -7,13 +7,13 @@ LayoutText {#text} at (0,0) size 376x19 text run at (0,0) width 233: "In the following test, the purple square " text run at (233,0) width 143: "should be on the left (\x{21E6}" - LayoutImage {IMG} at (376,0) size 19x19.19 - LayoutText {#text} at (395,0) size 206x19 - text run at (395,0) width 206: "), and the teal square on the right (" - LayoutImage {IMG} at (601,0) size 19x19.19 - LayoutText {#text} at (620,0) size 147x19 - text run at (620,0) width 89: "\x{21E8}) of the blue " - text run at (709,0) width 58: "rectangle." + LayoutImage {IMG} at (376,0) size 19.19x19.19 + LayoutText {#text} at (395,0) size 207x19 + text run at (395,0) width 207: "), and the teal square on the right (" + LayoutImage {IMG} at (601.19,0) size 19.19x19.19 + LayoutText {#text} at (620,0) size 148x19 + text run at (620,0) width 90: "\x{21E8}) of the blue " + text run at (709,0) width 59: "rectangle." LayoutBlockFlow {DIV} at (0,36) size 784x76 LayoutBlockFlow {DIV} at (16,0) size 752x76 [color=#0000FF] [bgcolor=#000080] LayoutImage (floating) {IMG} at (8,8) size 160x160
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index 2ba6472..9bde6d0 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.png index d43292f6..dc3cc896 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.txt index 0477173..b8587f3a 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/list-item-gradient-expected.txt
@@ -5,14 +5,14 @@ LayoutBlockFlow {BODY} at (8,8) size 784x576 LayoutBlockFlow {UL} at (0,0) size 784x60 LayoutListItem {LI} at (40,0) size 744x20 - LayoutListMarker (anonymous) at (-14.50,8) size 7.50x7.50 + LayoutListMarker (anonymous) at (-14.50,7) size 7.50x7.50 LayoutText {#text} at (0,0) size 57x19 text run at (0,0) width 57: "Item One" LayoutListItem {LI} at (40,20) size 744x20 - LayoutListMarker (anonymous) at (-14.50,8) size 7.50x7.50 + LayoutListMarker (anonymous) at (-14.50,7) size 7.50x7.50 LayoutText {#text} at (0,0) size 58x19 text run at (0,0) width 58: "Item Two" LayoutListItem {LI} at (40,40) size 744x20 - LayoutListMarker (anonymous) at (-14.50,8) size 7.50x7.50 + LayoutListMarker (anonymous) at (-14.50,7) size 7.50x7.50 LayoutText {#text} at (0,0) size 66x19 text run at (0,0) width 66: "Item Three"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/unprefixed-list-item-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/unprefixed-list-item-gradient-expected.png index ec484036..70f83af 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/unprefixed-list-item-gradient-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/gradients/unprefixed-list-item-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.png index 689d087..d0f001df 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.txt index d97605e..a6dcf45 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.png index 689d087..d0f001df 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.txt index 014f73f..edd1db2 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.png index 689d087..d0f001df 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.txt index d97605e..a6dcf45 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.png index 689d087..d0f001df 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.txt index d8e4bd45e..b513846 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-details-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.png index 5caf2ea..3dcffd19 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.txt index 947f9ba0..bc2c7db 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.png index 1f58dd17..45dc6da 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.txt index fef6197a..0fef6534 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.png index 9a215d5..1dcbe74 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.txt index e9c20549..5463aad 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.png index 70b26db..432443d 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.txt index 0d18691..01273e2 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-10-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.png index 2cb0fa13..7824e07 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.txt index 7859698f..1017e2f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.png index 1f58dd17..45dc6da 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.txt index fef6197a..0fef6534 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.png index b61de2c..9624e29 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.txt index fe8063b..e271c1fd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.png index 8c48cd5..1e0c5ee0 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.txt index 470e6cd..0890fa53 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.png index c4f3eeaf..f521bcf8 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.txt index b9d56d8..90b81a5 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.png index 6df1c1c4..950d7f09 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-4-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.png index d945369..b09e66e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.txt index 063bafd..9d2f00c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.png index 1f58dd17..45dc6da 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.txt index fef6197a..0fef6534 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.png index 9a215d5..1dcbe74 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.txt index d55cccb..a842f6ec6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.png index 7f44a86b..d7a5269 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.txt index 6f310fcf..d61aef54 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.png index 9a215d5..1dcbe74 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.txt index d55cccb..a842f6ec6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.png index 898d6cf..11667af 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.txt index 5367ff0e..cd575c5 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-7-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.png index 1d31a36..3fcc04a 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.txt index c393bc0b..5d675c9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.png index 7680ffd..d0676a9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.txt index 811af93..ef6e8cb 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-8-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.png index bb93a57d..ebcf011 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.txt index e85d5694..febd913 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.png index a902636..c7cfe6c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.txt index 5449c51..f62365c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-9-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.png index b913b89..ffc1b54 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.txt index 5486f4c..5da04e8 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutInline {B} at (0,0) size 141x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.png index b913b89..ffc1b54 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.txt index 652e921..0db91fd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-add-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutInline {SPAN} at (0,0) size 141x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.png index 63aa19de..f1bfe161 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.txt index 36c7cb7..05e2753 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-marker-style-expected.txt
@@ -1,27 +1,27 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x470 - LayoutBlockFlow {HTML} at (0,0) size 800x470.05 - LayoutBlockFlow {BODY} at (8,8) size 784x454.05 - LayoutBlockFlow {DIV} at (0,0) size 784x86 - LayoutBlockFlow {DETAILS} at (0,0) size 784x86 - LayoutBlockFlow {SUMMARY} at (0,0) size 784x86 +layer at (0,0) size 800x471 + LayoutBlockFlow {HTML} at (0,0) size 800x471.05 + LayoutBlockFlow {BODY} at (8,8) size 784x455.05 + LayoutBlockFlow {DIV} at (0,0) size 784x87 + LayoutBlockFlow {DETAILS} at (0,0) size 784x87 + LayoutBlockFlow {SUMMARY} at (0,0) size 784x87 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: right - LayoutText {#text} at (121,58) size 92x27 - text run at (121,58) width 92: "Summary" - LayoutBlockFlow {DIV} at (0,86) size 118x180.44 - LayoutBlockFlow {DETAILS} at (0,0) size 118x180.44 - LayoutBlockFlow {SUMMARY} at (0,0) size 118x180.44 + LayoutText {#text} at (121,59) size 92x27 + text run at (121,59) width 92: "Summary" + LayoutBlockFlow {DIV} at (0,87) size 119x180.44 + LayoutBlockFlow {DETAILS} at (0,0) size 119x180.44 + LayoutBlockFlow {SUMMARY} at (0,0) size 119x180.44 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: down - LayoutText {#text} at (90,89) size 27x92 - text run at (90,89) width 91: "Summary" - LayoutBlockFlow {DIV} at (0,266.44) size 784x47 + LayoutText {#text} at (91,89) size 27x92 + text run at (91,89) width 91: "Summary" + LayoutBlockFlow {DIV} at (0,267.44) size 784x47 LayoutBlockFlow {DETAILS} at (0,0) size 784x47 LayoutBlockFlow {SUMMARY} at (0,0) size 784x47 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: right LayoutText {#text} at (73,19) size 92x27 text run at (73,19) width 92: "Summary" - LayoutBlockFlow {DIV} at (0,313.44) size 71x140.61 + LayoutBlockFlow {DIV} at (0,314.44) size 71x140.61 LayoutBlockFlow {DETAILS} at (0,0) size 71x140.61 LayoutBlockFlow {SUMMARY} at (0,0) size 71x140.61 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: down
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.png index 67cb39e..39949e1 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.txt index 7a3b372..e556859 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-1-expected.txt
@@ -6,13 +6,13 @@ LayoutBlockFlow {DETAILS} at (0,0) size 784x144 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x108 [border: (8px solid #9999CC)] LayoutBlockFlow (anonymous) at (8,8) size 752x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 5: " " text run at (20,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (8,28) size 752x72 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 736x36 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,13) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down LayoutText {#text} at (24,8) size 270x19 text run at (24,8) width 5: " " text run at (28,8) width 266: "nested summary (summary-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.png index ae4dcd1..24b4e74 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.txt index ee6d410..d5eb49b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-nested-2-expected.txt
@@ -5,14 +5,14 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x144 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x36 [border: (8px solid #9999CC)] - LayoutDetailsMarker {DIV} at (8,13) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down LayoutText {#text} at (24,8) size 59x19 text run at (24,8) width 5: " " text run at (28,8) width 55: "summary" LayoutBlockFlow {DIV} at (8,44) size 768x92 LayoutBlockFlow {DETAILS} at (0,0) size 768x72 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 752x36 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,13) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down LayoutText {#text} at (24,8) size 254x19 text run at (24,8) width 5: " " text run at (28,8) width 250: "nested summary (details-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.png index 4142cbd..d6c9cbd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.txt index ebae16a..db4b99f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.png index f45c574..9923cacd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.txt index d83bb910..074f8ab 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.png index 4142cbd..d6c9cbd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.txt index ebae16a..db4b99f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.png index aacfc1a..c3dc5cf7 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.txt index 13869c7..5b914d9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-no-summary4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.png index 209c747..57d608e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.txt index bd27cd67..9c730dd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open-javascript-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x19 text run at (16,0) width 47: "details1" LayoutBlockFlow {DIV} at (0,20) size 784x22 @@ -13,7 +13,7 @@ LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {DETAILS} at (0,42) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x19 text run at (16,0) width 47: "details2" layer at (10,31) size 150x16
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.png index 6df1c1c4..950d7f09 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.png index c02f5ec..d993ee5 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.txt index 67b6436..099a3c9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.png index 6df1c1c4..950d7f09 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.png index c02f5ec..d993ee5 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.txt index 9461401..53f92d6e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.png index 6df1c1c4..950d7f09 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.png index ea7652de..1a42c2b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.txt index dde239e..82add38 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-open6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.png index 080c8304..c9176b4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.txt index 80eafd73..b6ce9cf 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-position-expected.txt
@@ -8,16 +8,16 @@ LayoutBlockFlow {DETAILS} at (0,20) size 784x0 layer at (50,150) size 46x20 LayoutBlockFlow (positioned) {SUMMARY} at (50,150) size 45.95x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 30x19 text run at (16,0) width 30: "fixed" layer at (158,158) size 784x20 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutBlockFlow (relative positioned) {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 44x19 text run at (16,0) width 44: "relative" layer at (250,150) size 67x20 LayoutBlockFlow (positioned) {SUMMARY} at (250,150) size 66.95x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 51x19 text run at (16,0) width 51: "absolute"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.png index 0468096..cab03e9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.txt index 62cbf8cb..16b0a83f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.png index 0468096..cab03e9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.txt index d1dcdbe..293395c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.png index 8090b65..75f52e0 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.txt index 670103c..9ce6057 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.png index 4142cbd..d6c9cbd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.txt index ebae16a..db4b99f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.png index b151ade6..0687cf6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.txt index fb59ecc1..06a7c485 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.png index fced46f..4767efa 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.txt index 143c300..668d75d8 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.png index 239025d..3a73d57 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.txt index 9617cbbf..facbfd6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.png index 42b190b..116a6ae 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.txt index 107ac8c..4a5efdf 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.png index 03c69b7..4bda1cd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.txt index ca3b0d98..15d0c49d 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 0 {CONTENT} of {#document-fragment} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.png index f45c574..9923cacd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.txt index d83bb910..074f8ab 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.png index 245cce40..504c931 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.txt index 120480d..851d781a 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 2 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.png index db5b167a..a331e87f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.txt index 4da4d648..7587091 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-5-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.png index a3d292a..ad0d676 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.txt index b8fc494e..74b923e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.png index c26ac6e6..279f503 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.txt index b1e5b221..a4d5bfa 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.png index aa8c86a..b8c6d7c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.txt index 77c7bd7..48eded2c9 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutText {#text} at (74,0) size 176x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.png index d022754f..b41d7e7b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.txt index 6b056d6d..793fd09 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-remove-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutInline {SPAN} at (0,0) size 172x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.png index a53d71a5..57d06e4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.txt index 39d4ebe1..03fb0342 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-summary-child-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 5x19 text run at (16,0) width 5: " " LayoutBlockFlow {SPAN} at (20.94,3) size 64x16
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.png index 7204398..a8abee2e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.txt index 2c86397..b02c1eaa 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-replace-text-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 58x19 text run at (16,0) width 58: "Summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.png index 5d308bd..c7d0e30 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.txt index c06291f..d5de2c4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-center-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (24.53,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (24.53,4) size 10.55x10.55: right LayoutText {#text} at (41,0) size 55x19 text run at (41,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (24.53,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (24.53,4) size 10.55x10.55: down LayoutText {#text} at (41,0) size 55x19 text run at (41,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,24.53) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,24.53) size 10.55x10.55: down LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,24.53) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,24.53) size 10.55x10.55: right LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,24.53) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,24.53) size 10.55x10.55: down LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,24.53) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,24.53) size 10.55x10.55: left LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (84.92,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (84.92,4) size 10.55x10.55: left LayoutText {#text} at (24,0) size 55x19 text run at (24,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (84.92,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (84.92,4) size 10.55x10.55: down LayoutText {#text} at (24,0) size 55x19 text run at (24,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,84.92) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,84.92) size 10.55x10.55: up LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,84.92) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,84.92) size 10.55x10.55: right LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,84.92) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,84.92) size 10.55x10.55: up LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,84.92) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,84.92) size 10.55x10.55: left LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.png index 17bcdc6..92ec14de 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.txt index e2029882..d05b0cdb 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-left-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (60.39,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (60.39,4) size 10.55x10.55: left LayoutText {#text} at (0,0) size 54x19 text run at (0,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (60.39,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (60.39,4) size 10.55x10.55: down LayoutText {#text} at (0,0) size 54x19 text run at (0,0) width 54: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,60.39) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,60.39) size 10.55x10.55: up LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,60.39) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,60.39) size 10.55x10.55: right LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,60.39) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,60.39) size 10.55x10.55: up LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,60.39) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,60.39) size 10.55x10.55: left LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.png index 1f2e35ea..2847b37 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.txt index 871500a2..bba7965 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-align-right-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (49.06,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (49.06,4) size 10.55x10.55: right LayoutText {#text} at (66,0) size 54x19 text run at (66,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (49.06,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (49.06,4) size 10.55x10.55: down LayoutText {#text} at (66,0) size 54x19 text run at (66,0) width 54: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,49.06) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,49.06) size 10.55x10.55: down LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,49.06) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,49.06) size 10.55x10.55: right LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,49.06) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,49.06) size 10.55x10.55: down LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,49.06) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,49.06) size 10.55x10.55: left LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.png index afce3e0..7339f50 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.txt index 1535aae3..6dbad95 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/html/details-writing-mode-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/details-open-repaint-expected.txt index 8df0e94..329509ad 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [10, 75, 150, 16], [8, 72, 784, 22], [8, 72, 154, 22], - [8, 57, 11, 11] + [8, 56, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/width100percent-image-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/width100percent-image-expected.txt index 82ec6418..5bb86231 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/width100percent-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/replaced/width100percent-image-expected.txt
@@ -27,11 +27,11 @@ LayoutTableSection {TBODY} at (0,0) size 784x279 LayoutTableRow {TR} at (0,1) size 784x277 LayoutTableCell {TD} at (1,1) size 216x277 [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (218,1) size 216x277 [r=0 c=1 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (435,1) size 216x277 [r=0 c=2 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (652,128) size 131x22 [r=0 c=3 rs=1 cs=1] LayoutText {#text} at (1,1) size 4x19 text run at (1,1) width 4: " "
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.png index 8be748f..fb1380c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt index cfeaab0..da7921d 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt
@@ -1,77 +1,77 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1250 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1254 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1250 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x1250 - LayoutBlockFlow {BODY} at (8,8) size 769x1234 +layer at (0,0) size 785x1254 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x1254 + LayoutBlockFlow {BODY} at (8,8) size 769x1238 LayoutText {#text} at (0,0) size 261x19 text run at (0,0) width 261: "LTR fieldset with left/center/right text-align: " LayoutBR {BR} at (260,15) size 1x0 LayoutFieldset {FIELDSET} at (16,36) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (34,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,156) size 4x19 - text run at (260,156) width 4: " " + LayoutText {#text} at (260,157) size 4x19 + text run at (260,157) width 4: " " LayoutFieldset {FIELDSET} at (280,36) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (110,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,192) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,193) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,312) size 4x19 - text run at (260,312) width 4: " " + LayoutText {#text} at (260,314) size 4x19 + text run at (260,314) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,332) size 262x19 - text run at (0,332) width 262: "RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (261,347) size 1x0 - LayoutFieldset {FIELDSET} at (16,368) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,334) size 262x19 + text run at (0,334) width 262: "RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (261,349) size 1x0 + LayoutFieldset {FIELDSET} at (16,370) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,488) size 4x19 - text run at (260,488) width 4: " " - LayoutFieldset {FIELDSET} at (280,368) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (260,491) size 4x19 + text run at (260,491) width 4: " " + LayoutFieldset {FIELDSET} at (280,370) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (90,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,524) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,527) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,644) size 4x19 - text run at (260,644) width 4: " " + LayoutText {#text} at (260,648) size 4x19 + text run at (260,648) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,664) size 310x19 - text run at (0,664) width 310: "Vertical LTR fieldset with left/center/right text-align: " - LayoutBR {BR} at (309,679) size 1x0 - LayoutFieldset {FIELDSET} at (16,700) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,668) size 310x19 + text run at (0,668) width 310: "Vertical LTR fieldset with left/center/right text-align: " + LayoutBR {BR} at (309,683) size 1x0 + LayoutFieldset {FIELDSET} at (16,704) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,34) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,929) size 5x19 - text run at (151,929) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,700) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,933) size 5x19 + text run at (151,933) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,704) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,110) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,929) size 5x19 - text run at (307,929) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,700) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,933) size 5x19 + text run at (307,933) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,704) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (462,929) size 5x19 - text run at (462,929) width 5: " " + LayoutText {#text} at (462,933) size 5x19 + text run at (462,933) width 5: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,949) size 311x19 - text run at (0,949) width 311: "Vertical RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (310,964) size 1x0 - LayoutFieldset {FIELDSET} at (16,985) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,953) size 311x19 + text run at (0,953) width 311: "Vertical RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (310,968) size 1x0 + LayoutFieldset {FIELDSET} at (16,989) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,14) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,1214) size 5x19 - text run at (151,1214) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,985) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,1218) size 5x19 + text run at (151,1218) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,989) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,90) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,1214) size 5x19 - text run at (307,1214) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,985) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,1218) size 5x19 + text run at (307,1218) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,989) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.png index d748752..a4aedb7 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.png index da165ff7..7a72598 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.txt index 7f998da..4fccfb7c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-aspect-ratio-expected.txt
@@ -18,13 +18,13 @@ layer at (8,284) size 160x120 LayoutVideo {VIDEO} at (0,240) size 160x120 layer at (168,284) size 160x120 - LayoutVideo {VIDEO} at (0,0) size 160x120 + LayoutVideo {VIDEO} at (0,0) size 160x119.98 layer at (328,284) size 160x120 LayoutVideo {VIDEO} at (160,0) size 160x120 layer at (8,404) size 160x120 LayoutVideo {VIDEO} at (0,0) size 160x120 layer at (168,404) size 160x120 - LayoutVideo {VIDEO} at (160,0) size 160x120 + LayoutVideo {VIDEO} at (160,0) size 160x119.98 layer at (8,44) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -36,10 +36,10 @@ layer at (8,284) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (168,284) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (168,284) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98 layer at (328,284) size 160x120 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 LayoutBlockFlow {DIV} at (0,85) size 160x35 @@ -51,10 +51,10 @@ layer at (8,404) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (168,404) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (168,404) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98 layer at (328,404) size 320x120 LayoutBlockFlow (relative positioned) {DIV} at (320,360) size 320x120 layer at (328,404) size 160x120 @@ -65,9 +65,9 @@ layer at (328,404) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (488,404) size 160x120 - LayoutVideo (positioned) {VIDEO} at (160,0) size 160x120 + LayoutVideo (positioned) {VIDEO} at (160,0) size 160x119.98 layer at (488,404) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (488,404) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.png index 06d69b5c..eb54ecc0 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.txt index f210fbf..01ea82b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv420-expected.txt
@@ -8,9 +8,9 @@ text run at (0,0) width 317: "Test correct colorspace for yuv420, i.e. YU12 video" LayoutBlockFlow (anonymous) at (0,36) size 784x156 layer at (8,44) size 206x156 - LayoutVideo {VIDEO} at (0,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (0,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (11,47) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (11,47) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.png index 5222cfd..81b2b59 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.txt index 502f7ae..6c5fa26 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-colorspace-yuv422-expected.txt
@@ -8,9 +8,9 @@ text run at (0,0) width 317: "Test correct colorspace for yuv422, i.e. YU16 video" LayoutBlockFlow (anonymous) at (0,36) size 784x156 layer at (8,44) size 206x156 - LayoutVideo {VIDEO} at (0,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (0,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (11,47) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (11,47) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-rendering-expected.txt index 2dd0a57..fd0a1a67 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,44) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,284) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,44) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -38,10 +38,10 @@ LayoutBlockFlow {DIV} at (12.42,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,284) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,284) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,489) size 310x30 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30 @@ -60,12 +60,12 @@ LayoutBlockFlow {DIV} at (12.42,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,524) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,524) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,524) size 320x239.98 layer at (8,524) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,524) size 320x205 backgroundClip at (8,524) size 320x76 clip at (8,524) size 320x76 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,729) size 310x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.png index 236c699..096e73b4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.txt index d0bf892..9e70781 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-controls-with-cast-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,52) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,297) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,52) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -39,10 +39,10 @@ LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,297) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,297) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,502) size 310x30 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30 @@ -62,12 +62,12 @@ LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,542) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,542) size 320x239.98 layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,542) size 320x205 backgroundClip at (8,542) size 320x58 clip at (8,542) size 320x58 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,747) size 310x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-layer-crash-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-layer-crash-expected.txt index 613f2c2f1..21f9ddb 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-layer-crash-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-layer-crash-expected.txt
@@ -17,16 +17,16 @@ text run at (0,322) width 4: " " LayoutBR {BR} at (210,337) size 0x0 layer at (12,64) size 206x156 - LayoutVideo {VIDEO} at (4,20) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,20) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,67) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,67) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,225) size 206x156 - LayoutVideo {VIDEO} at (4,181) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,181) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,228) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,228) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.png index bb0490f..5b2402c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.txt index d881552..c188edc 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-dark-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,52) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,297) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,52) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -23,18 +23,18 @@ layer at (24,62) size 30x30 LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 layer at (8,297) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,297) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (24,307) size 30x30 - LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 + LayoutButton (positioned) {INPUT} at (16,10.23) size 30x30 layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,542) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,542) size 320x239.98 layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,542) size 320x205 backgroundClip at (8,542) size 320x58 clip at (8,542) size 320x58 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (24,552) size 30x30 - LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 + LayoutButton (positioned) {INPUT} at (16,10.23) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.png index cebc6ce..f8a2d3e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.txt index 8db6fdd..e3ecbde 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-overlay-cast-light-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,52) size 352x288 LayoutVideo {VIDEO} at (0,0) size 352x288 layer at (8,345) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo {VIDEO} at (0,0) size 320x262 + LayoutVideo {VIDEO} at (0,0) size 320x261.81 layer at (8,52) size 352x288 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 352x288 LayoutBlockFlow {DIV} at (0,253) size 352x35 @@ -23,16 +23,16 @@ layer at (26,65) size 30x30 LayoutButton (positioned) {INPUT} at (17.59,12.64) size 30x30 layer at (8,345) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 - LayoutBlockFlow {DIV} at (0,227) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x261.81 + LayoutBlockFlow {DIV} at (0,226.81) size 320x35 layer at (8,345) size 320x227 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x226.81 layer at (24,356) size 30x30 - LayoutButton (positioned) {INPUT} at (16,11.34) size 30x30 + LayoutButton (positioned) {INPUT} at (16,11.33) size 30x30 layer at (8,612) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,612) size 320x262 + LayoutVideo (positioned) {VIDEO} at (8,612) size 320x261.81 layer at (8,612) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 - LayoutBlockFlow {DIV} at (0,227) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x261.81 + LayoutBlockFlow {DIV} at (0,226.81) size 320x35 layer at (8,612) size 320x227 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x226.81
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/video-transformed-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/media/video-transformed-expected.txt index 33cf546..731b9cf 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/media/video-transformed-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/media/video-transformed-expected.txt
@@ -17,23 +17,23 @@ text run at (0,463) width 4: " " LayoutBR {BR} at (210,478) size 0x0 layer at (12,44) size 206x156 - LayoutVideo {VIDEO} at (4,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,47) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,47) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,205) size 206x156 - LayoutVideo {VIDEO} at (4,161) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,161) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,208) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,208) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,366) size 206x156 - LayoutVideo {VIDEO} at (4,322) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,322) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,369) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,369) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.png index 877859c0..d2e9603c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.txt index f65d70b..1269414d 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/rightsizing-grid-expected.txt
@@ -1,9 +1,9 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1449 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1378 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {html} at (0,0) size 785x1449.14 +layer at (0,0) size 785x1378 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {html} at (0,0) size 785x1378.38 LayoutBlockFlow {body} at (8,8) size 769x0 - LayoutBlockFlow (floating) {div} at (0,0) size 769x1441.14 + LayoutBlockFlow (floating) {div} at (0,0) size 769x1370.38 LayoutBlockFlow {h1} at (0,21.44) size 769x37 LayoutText {#text} at (0,0) size 443x36 text run at (0,0) width 443: "SVG grid with percentage width" @@ -11,51 +11,48 @@ LayoutText {#text} at (0,0) size 146x19 text run at (0,0) width 146: "WICD Core 1.0 #20-3" LayoutBlockFlow {p} at (0,121.14) size 769x0 - LayoutBlockFlow (floating) {div} at (256.30,128) size 256.30x448 + LayoutBlockFlow (floating) {div} at (256.30,128.16) size 256.30x448.52 LayoutBR {br} at (256,0) size 1x19 - LayoutBlockFlow (floating) {div} at (512.59,128) size 256.30x448 - LayoutBlockFlow (floating) {div} at (384.50,704) size 384.50x192 - LayoutBlockFlow (floating) {div} at (0,896) size 384.50x192 - LayoutBlockFlow (floating) {div} at (384.50,1024) size 384.50x128 - LayoutBlockFlow {p} at (0,121.14) size 769x596 [color=#FFFFFF] + LayoutBlockFlow (floating) {div} at (512.59,128.16) size 256.30x448.52 + LayoutBlockFlow (floating) {div} at (0,704.83) size 384.50x192.25 + LayoutBlockFlow (floating) {div} at (384.50,704.83) size 384.50x192.25 + LayoutBlockFlow (floating) {div} at (384.50,897.08) size 384.50x128.16 + LayoutBlockFlow {p} at (0,121.14) size 769x1045.23 [color=#FFFFFF] LayoutBR {br} at (769,0) size 0x19 - LayoutText {#text} at (640,576) size 9x19 - text run at (640,576) width 9: ".." - LayoutBlockFlow {p} at (0,733.14) size 769x580 - LayoutText {#text} at (640,0) size 760x579 - text run at (640,0) width 115: "Above, you should" - text run at (640,20) width 95: "see a grid of 17" - text run at (640,40) width 120: "SVG child elements" - text run at (640,60) width 113: "sticked together to" - text run at (0,540) width 148: "build one rectangle grid. " - text run at (148,540) width 583: "You should be able to resize your browser window and the grid rendering should adjust to it. The" - text run at (0,560) width 327: "outcome should look like in these sample screenshots: " + LayoutText {#text} at (0,1025) size 8x19 + text run at (0,1025) width 8: ".." + LayoutBlockFlow {p} at (0,1182.38) size 769x60 + LayoutText {#text} at (0,0) size 735x59 + text run at (0,0) width 603: "Above, you should see a grid of 17 SVG child elements sticked together to build one rectangle grid. " + text run at (603,0) width 132: "You should be able to" + text run at (0,20) width 697: "resize your browser window and the grid rendering should adjust to it. The outcome should look like in these sample" + text run at (0,40) width 77: "screenshots: " LayoutInline {a} at (0,0) size 30x19 [color=#0000EE] - LayoutText {#text} at (327,560) size 30x19 - text run at (327,560) width 30: "small" - LayoutText {#text} at (357,560) size 8x19 - text run at (357,560) width 8: ", " + LayoutText {#text} at (77,40) size 30x19 + text run at (77,40) width 30: "small" + LayoutText {#text} at (107,40) size 8x19 + text run at (107,40) width 8: ", " LayoutInline {a} at (0,0) size 37x19 [color=#0000EE] - LayoutText {#text} at (365,560) size 37x19 - text run at (365,560) width 37: "bigger" - LayoutText {#text} at (402,560) size 30x19 - text run at (402,560) width 30: " and " + LayoutText {#text} at (115,40) size 37x19 + text run at (115,40) width 37: "bigger" + LayoutText {#text} at (152,40) size 30x19 + text run at (152,40) width 30: " and " LayoutInline {a} at (0,0) size 18x19 [color=#0000EE] - LayoutText {#text} at (432,560) size 18x19 - text run at (432,560) width 18: "big" - LayoutText {#text} at (450,560) size 4x19 - text run at (450,560) width 4: "." - LayoutBlockFlow {p} at (0,1329.14) size 769x40 + LayoutText {#text} at (182,40) size 18x19 + text run at (182,40) width 18: "big" + LayoutText {#text} at (200,40) size 4x19 + text run at (200,40) width 4: "." + LayoutBlockFlow {p} at (0,1258.38) size 769x40 LayoutText {#text} at (0,0) size 754x39 text run at (0,0) width 754: "The test is successful, if all SVG elements resize exacly dependend on the width of the browser window, but keep their aspect" text run at (0,20) width 553: "ratio and relative position. The complete grid should always show a perfect rectangle object." - LayoutBlockFlow {p} at (0,1385.14) size 769x40 + LayoutBlockFlow {p} at (0,1314.38) size 769x40 LayoutBR {br} at (0,0) size 0x19 LayoutInline {a} at (0,0) size 32x19 [color=#0000EE] LayoutText {#text} at (0,20) size 32x19 text run at (0,20) width 32: "Back" layer at (8,129) size 385x128 - LayoutEmbeddedObject (floating) {object} at (0,0) size 384.50x128 + LayoutEmbeddedObject (floating) {object} at (0,0) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -69,7 +66,7 @@ LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (52.97,27.00) startOffset 0 endOffset 1 width 14.06: "A" layer at (393,129) size 384x128 - LayoutEmbeddedObject (floating) {object} at (384.50,0) size 384.50x128 + LayoutEmbeddedObject (floating) {object} at (384.50,0) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -83,7 +80,7 @@ LayoutSVGInlineText {#text} at (0,0) size 12x23 chunk 1 (middle anchor) text run 1 at (54.06,27.00) startOffset 0 endOffset 1 width 11.88: "L" layer at (8,257) size 256x449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,128) size 256.30x449 + LayoutEmbeddedObject (floating) {object} at (0,128.16) size 256.30x448.52 layer at (0,0) size 256x449 LayoutView at (0,0) size 256x449 layer at (0,0) size 256x449 @@ -117,7 +114,7 @@ LayoutSVGInlineText {#text} at (0,0) size 13x23 chunk 1 (middle anchor) text run 1 at (53.91,127.00) startOffset 0 endOffset 1 width 12.19: "E" layer at (264,257) size 256x256 - LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256 + LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256.30 layer at (0,0) size 256x256 LayoutView at (0,0) size 256x256 layer at (0,0) size 256x256 @@ -130,8 +127,8 @@ LayoutSVGText {text} at (32,28) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (32.81,47.00) startOffset 0 endOffset 1 width 14.38: "K" -layer at (264,513) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 +layer at (264,514) size 256x193 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,256.30) size 256.30x192.22 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 layer at (0,0) size 256x192 @@ -145,7 +142,7 @@ LayoutSVGInlineText {#text} at (0,0) size 8x23 chunk 1 (middle anchor) text run 1 at (36.09,37.00) startOffset 0 endOffset 1 width 7.81: "J" layer at (521,257) size 256x256 - LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256 + LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256.30 layer at (0,0) size 256x256 LayoutView at (0,0) size 256x256 layer at (0,0) size 256x256 @@ -174,8 +171,8 @@ LayoutSVGText {text} at (52,48) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (52.81,67.00) startOffset 0 endOffset 1 width 14.38: "O" -layer at (521,513) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 +layer at (521,514) size 256x193 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,256.30) size 256.30x192.22 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 layer at (0,0) size 256x192 @@ -188,8 +185,8 @@ LayoutSVGText {text} at (32,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (32.81,37.00) startOffset 0 endOffset 1 width 14.38: "Q" -layer at (264,705) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (256.30,576) size 384.50x128 +layer at (8,706) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,576.67) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -202,8 +199,8 @@ LayoutSVGText {text} at (54,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 11x23 chunk 1 (middle anchor) text run 1 at (54.53,27.00) startOffset 0 endOffset 1 width 10.94: "F" -layer at (8,833) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,704) size 384.50x128 +layer at (393,706) size 384x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (384.50,576.67) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -216,8 +213,8 @@ LayoutSVGText {text} at (56,8) size 8x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 7x23 chunk 1 (middle anchor) text run 1 at (56.88,27.00) startOffset 0 endOffset 1 width 6.25: "I" -layer at (393,833) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192 +layer at (8,834) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192.25 layer at (0,0) size 96x192 LayoutView at (0,0) size 96x192 layer at (0,0) size 96x192 @@ -227,8 +224,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGRect {rect} at (0,0) size 96x192 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [x=1.00] [y=1.00] [width=28.00] [height=58.00] -layer at (489,833) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192 +layer at (104,834) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192.25 layer at (0,0) size 288x192 LayoutView at (0,0) size 288x192 layer at (0,0) size 288x192 @@ -241,8 +238,8 @@ LayoutSVGText {text} at (37,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (37.81,37.00) startOffset 0 endOffset 1 width 14.38: "G" -layer at (8,1025) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192 +layer at (393,834) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192.25 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 layer at (0,0) size 192x192 @@ -255,8 +252,8 @@ LayoutSVGText {text} at (22,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (22.81,37.00) startOffset 0 endOffset 1 width 14.38: "H" -layer at (200,1025) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192 +layer at (585,834) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192.25 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 layer at (0,0) size 192x192 @@ -269,8 +266,8 @@ LayoutSVGText {text} at (23,18) size 14x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 14x23 chunk 1 (middle anchor) text run 1 at (23.28,37.00) startOffset 0 endOffset 1 width 13.44: "R" -layer at (393,1025) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (384.50,896) size 192.25x128 +layer at (8,1026) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,897.08) size 192.25x128.16 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 layer at (0,0) size 192x128 @@ -280,8 +277,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (585,1025) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (576.75,896) size 192.25x128 +layer at (200,1026) size 193x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (192.25,897.08) size 192.25x128.16 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 layer at (0,0) size 192x128 @@ -291,8 +288,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (393,1153) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128 +layer at (393,1026) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128.16 layer at (0,0) size 288x128 LayoutView at (0,0) size 288x128 layer at (0,0) size 288x128 @@ -305,8 +302,8 @@ LayoutSVGText {text} at (39,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x23 chunk 1 (middle anchor) text run 1 at (39.38,27.00) startOffset 0 endOffset 1 width 11.25: "S" -layer at (681,1153) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128 +layer at (681,1026) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128.16 layer at (0,0) size 96x128 LayoutView at (0,0) size 96x128 layer at (0,0) size 96x128
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/sizing-flakiness-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/sizing-flakiness-expected.txt index be98781..b4ff5b63 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/sizing-flakiness-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/sizing-flakiness-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x0 LayoutBlockFlow (floating) {DIV} at (0,0) size 402x402 [border: (1px solid #FF0000)] layer at (9,9) size 200x67 - LayoutEmbeddedObject (floating) {OBJECT} at (1,1) size 200x67 + LayoutEmbeddedObject (floating) {OBJECT} at (1,1) size 200x66.66 layer at (0,0) size 200x67 LayoutView at (0,0) size 200x67 layer at (0,0) size 200x67
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-rightsizing-a-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-rightsizing-a-expected.txt index 6f98460..91c7979 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-rightsizing-a-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-rightsizing-a-expected.txt
@@ -22,7 +22,7 @@ text run at (0,0) width 675: "Above there must be a GIF- and a SVG-image visible. Both are referenced by an object element (width:100%," text run at (0,17) width 506: "no defined height) and each nested into a div element (width:176px, height:62px)." LayoutBlockFlow {div} at (0,251.77) size 176x62 [bgcolor=#FF0000] - LayoutImage {object} at (0,0) size 176x62 + LayoutImage {object} at (0,0) size 175.98x62 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow (anonymous) at (0,313.77) size 752x19 LayoutBR {br} at (0,0) size 0x18
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.png index 4d8b172b..409b9bb 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.txt index 772a5f2..253fd840 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-hixie-mixed-009-expected.txt
@@ -1,12 +1,12 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x436 - LayoutBlockFlow {html} at (0,0) size 800x436.36 - LayoutBlockFlow {body} at (11.52,14.39) size 776.97x407.58 +layer at (0,0) size 800x437 + LayoutBlockFlow {html} at (0,0) size 800x437.36 + LayoutBlockFlow {body} at (11.52,14.39) size 776.97x408.58 LayoutBlockFlow {p} at (0,0) size 776.97x27 [color=#000080] LayoutText {#text} at (0,0) size 662x26 text run at (0,0) width 662: "The word \"TEST \" should appear twice below, the same size each time." - LayoutBlockFlow (anonymous) at (0,41.39) size 776.97x179 + LayoutBlockFlow (anonymous) at (0,41.39) size 776.97x180 LayoutSVGRoot {svg} at (11,55) size 577x174 LayoutSVGRect {rect} at (11,55) size 577x174 [transform={m=((10.00,0.00)(0.00,10.00)) t=(0.00,0.00)}] [fill={[type=SOLID] [color=#EEEEEE]}] [x=0.00] [y=0.00] [width=60.00] [height=12.00] LayoutSVGForeignObject {foreignObject} at (0,0) size 60x10 @@ -14,6 +14,6 @@ LayoutText {#text} at (0,0) size 26x12 text run at (0,0) width 26: "TEST" LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {div} at (0,234.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] + LayoutBlockFlow {div} at (0,235.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] LayoutText {#text} at (0,3) size 344x164 text run at (0,3) width 344: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png index 2167cf4..e9dd03d6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png index c1cf580c..8344a0f8 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png index 7e3c9bb..5aa8020 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.png index a847a8e..ead934b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt index c5e6309..d50e4b4c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt
@@ -1,14 +1,14 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x379 - LayoutBlockFlow {html} at (0,0) size 800x379.09 - LayoutBlockFlow {body} at (5.55,5.55) size 788.91x368 - LayoutTable {table} at (41.45,0) size 706x368 - LayoutTableSection (anonymous) at (0,0) size 706x368 - LayoutTableRow {tr} at (0,0) size 706x368 - LayoutTableCell {td} at (0,0) size 706x368 [r=0 c=0 rs=1 cs=3] - LayoutTable {table} at (7.44,6) size 693x356 - LayoutTableSection (anonymous) at (0,0) size 693x356 +layer at (0,0) size 800x380 + LayoutBlockFlow {html} at (0,0) size 800x380.09 + LayoutBlockFlow {body} at (5.55,5.55) size 788.91x369 + LayoutTable {table} at (41.45,0) size 706x369 + LayoutTableSection (anonymous) at (0,0) size 706x369 + LayoutTableRow {tr} at (0,0) size 706x369 + LayoutTableCell {td} at (0,0) size 706x369 [r=0 c=0 rs=1 cs=3] + LayoutTable {table} at (7.44,6) size 693x357 + LayoutTableSection (anonymous) at (0,0) size 693x357 LayoutTableRow {tr} at (0,1) size 693x66 LayoutTableCell {td} at (1,1) size 691x65.75 [r=0 c=0 rs=1 cs=2] LayoutBlockFlow {h1} at (5.55,19.88) size 681x26 @@ -21,10 +21,10 @@ LayoutTableCell {td} at (347,68) size 345x24 [r=1 c=1 rs=1 cs=1] LayoutText {#text} at (146,5) size 53x14 text run at (146,5) width 53: "PNG Image" - LayoutTableRow {tr} at (0,93) size 693x262 - LayoutTableCell {td} at (1,93) size 345x262 [r=2 c=0 rs=1 cs=1] + LayoutTableRow {tr} at (0,93) size 693x263 + LayoutTableCell {td} at (1,93) size 345x263 [r=2 c=0 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {td} at (347,93) size 345x262 [r=2 c=1 rs=1 cs=1] + LayoutTableCell {td} at (347,93) size 345x263 [r=2 c=1 rs=1 cs=1] LayoutImage {img} at (5,5) size 333.33x249.97 LayoutText {#text} at (0,0) size 0x0 layer at (62,110) size 333x250
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png index 11fac0a..9726d67 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.txt index 91e57cd..57c42e4 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug101674-expected.txt
@@ -7,11 +7,11 @@ LayoutTableSection {TBODY} at (1,1) size 767x73 LayoutTableRow {TR} at (0,2) size 767x69 LayoutTableCell {TD} at (2,2) size 444x69 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 - LayoutImage {IMG} at (90,2) size 88x65 - LayoutImage {IMG} at (178,2) size 88x65 - LayoutImage {IMG} at (266,2) size 88x65 - LayoutImage {IMG} at (354,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 + LayoutImage {IMG} at (90,2) size 88x64.53 + LayoutImage {IMG} at (178,2) size 88x64.53 + LayoutImage {IMG} at (266,2) size 88x64.53 + LayoutImage {IMG} at (354,2) size 88x64.53 LayoutTableCell {TD} at (448,24) size 317x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 148x19 text run at (2,2) width 148: "Nothing between images" @@ -19,15 +19,15 @@ LayoutTableSection {TBODY} at (1,1) size 767x333 LayoutTableRow {TR} at (0,2) size 767x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,67) size 88x65 + LayoutImage {IMG} at (2,67) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,132) size 88x65 + LayoutImage {IMG} at (2,132) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,197) size 88x65 + LayoutImage {IMG} at (2,197) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,262) size 88x65 + LayoutImage {IMG} at (2,262) size 88x64.53 LayoutTableCell {TD} at (96,154) size 669x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 144x19 text run at (2,2) width 144: "Spaces between images" @@ -35,15 +35,15 @@ LayoutTableSection {TBODY} at (1,1) size 767x333 LayoutTableRow {TR} at (0,2) size 767x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,67) size 88x65 + LayoutImage {IMG} at (2,67) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,132) size 88x65 + LayoutImage {IMG} at (2,132) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,197) size 88x65 + LayoutImage {IMG} at (2,197) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,262) size 88x65 + LayoutImage {IMG} at (2,262) size 88x64.53 LayoutTableCell {TD} at (96,154) size 669x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 156x19 text run at (2,2) width 156: "Newlines between images" @@ -53,19 +53,19 @@ LayoutTableCell {TD} at (2,2) size 587x74 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,52) size 26x19 text run at (2,52) width 26: "One" - LayoutImage {IMG} at (28,2) size 88x65 + LayoutImage {IMG} at (28,2) size 88x64.53 LayoutText {#text} at (116,52) size 27x19 text run at (116,52) width 27: "Two" - LayoutImage {IMG} at (142.88,2) size 88x65 + LayoutImage {IMG} at (142.88,2) size 88x64.53 LayoutText {#text} at (230,52) size 36x19 text run at (230,52) width 36: "Three" - LayoutImage {IMG} at (265.88,2) size 88x65 + LayoutImage {IMG} at (265.88,2) size 88x64.53 LayoutText {#text} at (353,52) size 30x19 text run at (353,52) width 30: "Four" - LayoutImage {IMG} at (382.88,2) size 88x65 + LayoutImage {IMG} at (382.88,2) size 88x64.53 LayoutText {#text} at (470,52) size 27x19 text run at (470,52) width 27: "Five" - LayoutImage {IMG} at (496.88,2) size 88x65 + LayoutImage {IMG} at (496.88,2) size 88x64.53 LayoutTableCell {TD} at (591,27) size 174x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 126x19 text run at (2,2) width 126: "Text between images" @@ -75,19 +75,19 @@ LayoutTableCell {TD} at (2,2) size 127x369 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 26x19 text run at (2,2) width 26: "One" - LayoutImage {IMG} at (2,22) size 88x65 + LayoutImage {IMG} at (2,22) size 88x64.53 LayoutText {#text} at (90,72) size 27x19 text run at (90,72) width 27: "Two" - LayoutImage {IMG} at (2,92) size 88x65 + LayoutImage {IMG} at (2,92) size 88x64.53 LayoutText {#text} at (90,142) size 35x19 text run at (90,142) width 35: "Three" - LayoutImage {IMG} at (2,162) size 88x65 + LayoutImage {IMG} at (2,162) size 88x64.53 LayoutText {#text} at (90,212) size 29x19 text run at (90,212) width 29: "Four" - LayoutImage {IMG} at (2,232) size 88x65 + LayoutImage {IMG} at (2,232) size 88x64.53 LayoutText {#text} at (90,282) size 26x19 text run at (90,282) width 26: "Five" - LayoutImage {IMG} at (2,302) size 88x65 + LayoutImage {IMG} at (2,302) size 88x64.53 LayoutTableCell {TD} at (131,174) size 634x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 200x19 text run at (2,2) width 200: "Text with spaces between images" @@ -98,23 +98,23 @@ LayoutText {#text} at (2,2) size 26x89 text run at (2,2) width 26: "One" text run at (2,72) width 11: "A" - LayoutImage {IMG} at (13,22) size 88x65 + LayoutImage {IMG} at (13,22) size 88x64.53 LayoutText {#text} at (101,72) size 126x89 text run at (101,72) width 27: "Two" text run at (2,142) width 8: "b" - LayoutImage {IMG} at (10,92) size 88x65 + LayoutImage {IMG} at (10,92) size 88x64.53 LayoutText {#text} at (98,142) size 131x89 text run at (98,142) width 35: "Three" text run at (2,212) width 7: "c" - LayoutImage {IMG} at (9,162) size 88x65 + LayoutImage {IMG} at (9,162) size 88x64.53 LayoutText {#text} at (97,212) size 124x89 text run at (97,212) width 29: "Four" text run at (2,282) width 8: "d" - LayoutImage {IMG} at (10,232) size 88x65 + LayoutImage {IMG} at (10,232) size 88x64.53 LayoutText {#text} at (98,282) size 122x89 text run at (98,282) width 26: "Five" text run at (2,352) width 7: "e" - LayoutImage {IMG} at (9,302) size 88x65 + LayoutImage {IMG} at (9,302) size 88x64.53 LayoutTableCell {TD} at (139,177) size 626x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 287x19 text run at (2,2) width 287: "Text with spaces and more text between images"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.png index 4cd896f..8fe9bc51 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.txt index bcd63f1..ab17c2af 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug14929-expected.txt
@@ -28,7 +28,7 @@ LayoutTableSection {TBODY} at (1,1) size 638x52 LayoutTableRow {TR} at (0,2) size 638x22 LayoutTableCell {TD} at (2,2) size 634x22 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] - LayoutImage {IMG} at (2,2) size 190x18 + LayoutImage {IMG} at (2,2) size 190x18.36 LayoutTableRow {TR} at (0,26) size 638x24 LayoutTableCell {TD} at (2,26) size 127x24 [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 55x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.png index 45a25de..19aa9715 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.txt index f92a211..0e98bb3 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug86708-expected.txt
@@ -8,7 +8,7 @@ LayoutTableRow {TR} at (0,0) size 746x212 LayoutTableCell {TD} at (0,0) size 477x212 [bgcolor=#FF0000] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutInline {A} at (0,0) size 286x19 [color=#0000EE] - LayoutImage {IMG} at (1,1) size 286x210 + LayoutImage {IMG} at (1,1) size 286x209.72 LayoutTableCell {TD} at (477,0) size 269x22 [bgcolor=#FFFF00] [border: (1px inset #808080)] [r=0 c=1 rs=2 cs=1] LayoutText {#text} at (1,1) size 160x19 text run at (1,1) width 160: "ROWSPAN =2 in this cell"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.png index 2729aa8..56d79d18 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.txt index 77c6d08..2ba78f3 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/97619-expected.txt
@@ -11,7 +11,7 @@ text run at (2,2) width 57: "check for" text run at (2,22) width 65: "handling of" text run at (2,42) width 67: "whitespace" - LayoutImage {IMG} at (2,62) size 20x17 + LayoutImage {IMG} at (2,62) size 20x16.66 LayoutText {#text} at (22,64) size 66x39 text run at (22,64) width 46: " around" text run at (2,84) width 57: "the image" @@ -28,7 +28,7 @@ text run at (2,22) width 16: "bb" text run at (2,42) width 14: "cc" text run at (2,62) width 16: "dd" - LayoutImage {IMG} at (2,82) size 20x17 + LayoutImage {IMG} at (2,82) size 20x16.66 LayoutText {#text} at (2,99) size 14x59 text run at (2,99) width 14: "ee" text run at (2,119) width 8: "ff" @@ -43,7 +43,7 @@ LayoutTableCell {TD} at (2,2) size 200x46 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 196x19 text run at (2,2) width 196: "checkforhandlingofnowhitespace" - LayoutImage {IMG} at (2,22) size 20x17 + LayoutImage {IMG} at (2,22) size 20x16.66 LayoutText {#text} at (22,24) size 99x19 text run at (22,24) width 99: " aroundtheimage" LayoutTableCell {TD} at (204,13) size 576x24 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1] @@ -56,7 +56,7 @@ LayoutTableCell {TD} at (2,2) size 315x26 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,4) size 196x19 text run at (2,4) width 196: "checkforhandlingofnowhitespace" - LayoutImage {IMG} at (198,2) size 20x17 + LayoutImage {IMG} at (198,2) size 20x16.66 LayoutText {#text} at (218,4) size 95x19 text run at (218,4) width 95: "aroundtheimage" LayoutTableCell {TD} at (319,3) size 461x24 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug85016-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug85016-expected.txt index 16dc26f..25c563c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug85016-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug85016-expected.txt
@@ -12,7 +12,7 @@ text run at (0,0) width 506: "percentage height images in DIV with no height (red) in a DIV with no height (green)" LayoutBlockFlow {DIV} at (32,752) size 672x672 [border: (3px dotted #008000)] LayoutBlockFlow {DIV} at (35,35) size 602x602 [border: (1px solid #FF0000)] - LayoutImage {IMG} at (1,1) size 882x600 + LayoutImage {IMG} at (1,1) size 882.34x600 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1456) size 736x20 LayoutText {#text} at (0,0) size 443x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-2-expected.png index 46414fa..08c23085 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index b1a3574..3b269e42 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index 69827c7..b9e7f780 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt index 8df0e94..329509ad 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [10, 75, 150, 16], [8, 72, 784, 22], [8, 72, 154, 22], - [8, 57, 11, 11] + [8, 56, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index 6dd5f077..46fd9be 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-no-summary4-expected.png index 869c6a5..6e64b74 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open-javascript-expected.png index 0e354778..dfb7e5ff 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open2-expected.png index af1b6c0..168cf5db 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open4-expected.png index af1b6c0..168cf5db 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-summary-child-expected.png index 4633684..4bf209d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-text-expected.png index a9e8de9..1fca38e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/text/international/vertical-text-glyph-test-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/text/international/vertical-text-glyph-test-expected.png index a8598f85..e004e7e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/text/international/vertical-text-glyph-test-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/text/international/vertical-text-glyph-test-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/writing-mode/fieldsets-expected.png index ec15fb2..c70a940 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/svg/wicd/rightsizing-grid-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/svg/wicd/rightsizing-grid-expected.png index c46dad5f..acabbc97 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/svg/wicd/rightsizing-grid-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/svg/wicd/rightsizing-grid-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index 86086f55..33d74f3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index 509260e..35778ff 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index 098a09e..cfb8649 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.png index 822548e0..2a695f7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.txt index ae7ab283..22cb8c0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-no-summary4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.png index ee742039..e77005f0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.txt index d7bca38c..ffad2bd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open-javascript-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 51x18 text run at (16,0) width 51: "details1" LayoutBlockFlow {DIV} at (0,18) size 784x19 @@ -13,7 +13,7 @@ LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {DETAILS} at (0,37) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 51x18 text run at (16,0) width 51: "details2" layer at (11,29) size 117x13
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.png index ef1805a..d75dbf961 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.txt index 3150b9b..de40f303 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.png index ef1805a..d75dbf961 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.txt index a989b319..901b4796 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-open4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.png index 3e7f8c7..cca3dd72 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.txt index 5837d32..5698703 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-summary-child-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 5x18 text run at (16,0) width 5: " " LayoutBlockFlow {SPAN} at (20.94,2) size 62.42x15
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.png index b459ec9..96158db 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.txt index 25dc8a8b..71e2b92 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/html/details-replace-text-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 64x18 text run at (16,0) width 64: "Summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/repaint/details-open-repaint-expected.txt index 24b4f5e..5a01dc95 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [11, 71, 117, 13], [8, 68, 784, 19], [8, 68, 123, 19], - [8, 54, 11, 11] + [8, 53, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/text/international/vertical-text-glyph-test-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/text/international/vertical-text-glyph-test-expected.png index 5c8a93a..a1f7060 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/text/international/vertical-text-glyph-test-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/text/international/vertical-text-glyph-test-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/writing-mode/fieldsets-expected.png index 5eeed0c..615b379 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/svg/wicd/rightsizing-grid-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/svg/wicd/rightsizing-grid-expected.png index 767fbc0..b3e52fa 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/svg/wicd/rightsizing-grid-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/svg/wicd/rightsizing-grid-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index e019cae..7bbf8b8 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index 4eeba60..b30bf4f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mavericks/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt index 24b4f5e..5a01dc95 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [11, 71, 117, 13], [8, 68, 784, 19], [8, 68, 123, 19], - [8, 54, 11, 11] + [8, 53, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index 8559ace..2114d685 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-no-summary4-expected.png index f388ed7..0bff6566 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open-javascript-expected.png index 40441afd..f3eb3d3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open2-expected.png index faae0d2b..4dde615ba 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open4-expected.png index faae0d2b..4dde615ba 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-summary-child-expected.png index 1024ad1f..f9bdf15 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-text-expected.png index 7d3e7fd8..18d51060 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/text/international/vertical-text-glyph-test-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/text/international/vertical-text-glyph-test-expected.png index a3f2266..d8a0c395 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/text/international/vertical-text-glyph-test-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/text/international/vertical-text-glyph-test-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/writing-mode/fieldsets-expected.png index ea75053..e41b11600e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/svg/wicd/rightsizing-grid-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/svg/wicd/rightsizing-grid-expected.png index ecf99eef..ec4b1914 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/svg/wicd/rightsizing-grid-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/svg/wicd/rightsizing-grid-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index eb91ba1..00ab3a0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index 675e272..8cc218a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/perpendicular-layer-sorting-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/perpendicular-layer-sorting-expected.png index b45d024..5ff3e6b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/perpendicular-layer-sorting-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/perpendicular-layer-sorting-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/video-frame-size-change-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/video-frame-size-change-expected.txt index 7ef8b75..4110a3d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/video-frame-size-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/video-frame-size-change-expected.txt
@@ -12,16 +12,16 @@ LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 layer at (8,50) size 320x180 - LayoutVideo {VIDEO} at (0,0) size 320x180 + LayoutVideo {VIDEO} at (0,0) size 320x179.98 layer at (332,50) size 320x180 - LayoutVideo {VIDEO} at (324,0) size 320x180 + LayoutVideo {VIDEO} at (324,0) size 320x179.98 layer at (8,50) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (8,50) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98 layer at (332,50) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (332,50) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css1/box_properties/width-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css1/box_properties/width-expected.txt index 87e91e3..a3339a7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css1/box_properties/width-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css1/box_properties/width-expected.txt
@@ -22,7 +22,7 @@ LayoutText {#text} at (0,0) size 291x18 text run at (0,0) width 291: "The square above should be fifty pixels wide." LayoutBlockFlow (anonymous) at (0,202) size 769x385 - LayoutImage {IMG} at (0,0) size 384.50x385 + LayoutImage {IMG} at (0,0) size 384.50x384.50 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,603) size 769x18 LayoutText {#text} at (0,0) size 683x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css1/formatting_model/replaced_elements-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css1/formatting_model/replaced_elements-expected.txt index a250354..7984b604 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css1/formatting_model/replaced_elements-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css1/formatting_model/replaced_elements-expected.txt
@@ -1,8 +1,8 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2341 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2339 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x2341 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x2341 - LayoutBlockFlow {BODY} at (8,8) size 769x2325 [bgcolor=#CCCCCC] +layer at (0,0) size 785x2339 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x2339 + LayoutBlockFlow {BODY} at (8,8) size 769x2323 [bgcolor=#CCCCCC] LayoutBlockFlow {P} at (0,0) size 769x18 LayoutText {#text} at (0,0) size 363x18 text run at (0,0) width 363: "The style declarations which apply to the text below are:" @@ -37,30 +37,30 @@ LayoutBlockFlow {P} at (0,316) size 769x18 LayoutText {#text} at (0,0) size 406x18 text run at (0,0) width 406: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (192.25,350) size 384.50x385 - LayoutBlockFlow {P} at (0,751) size 769x36 + LayoutImage {IMG} at (192.25,350) size 384.50x384.50 + LayoutBlockFlow {P} at (0,750.50) size 769x36 LayoutText {#text} at (0,0) size 738x36 text run at (0,0) width 738: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally" text run at (0,18) width 172: "within the parent element: " text run at (171,18) width 404: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (384.50,803) size 384.50x385 - LayoutBlockFlow {P} at (0,1204) size 769x36 + LayoutImage {IMG} at (384.50,802.50) size 384.50x384.50 + LayoutBlockFlow {P} at (0,1203) size 769x36 LayoutText {#text} at (0,0) size 750x36 text run at (0,0) width 750: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge of" text run at (0,18) width 127: "the parent element: " text run at (126,18) width 404: "the document body in the first half, and the table in the second." - LayoutTable {TABLE} at (0,1256) size 769x1069 [border: (1px outset #808080)] - LayoutTableSection {TBODY} at (1,1) size 767x1067 + LayoutTable {TABLE} at (0,1255) size 769x1068 [border: (1px outset #808080)] + LayoutTableSection {TBODY} at (1,1) size 767x1066 LayoutTableRow {TR} at (0,0) size 767x26 LayoutTableCell {TD} at (0,0) size 767x26 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] LayoutInline {STRONG} at (0,0) size 161x18 LayoutText {#text} at (4,4) size 161x18 text run at (4,4) width 161: "TABLE Testing Section" - LayoutTableRow {TR} at (0,26) size 767x1041 + LayoutTableRow {TR} at (0,26) size 767x1040 LayoutTableCell {TD} at (0,533) size 12x26 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (4,4) size 4x18 text run at (4,4) width 4: " " - LayoutTableCell {TD} at (12,26) size 755x1041 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (12,26) size 755x1040 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] LayoutBlockFlow {P} at (4,4) size 747x19 LayoutImage {IMG} at (0,0) size 15x15 LayoutText {#text} at (15,1) size 442x18 @@ -73,14 +73,14 @@ LayoutBlockFlow {P} at (4,135) size 747x18 LayoutText {#text} at (0,0) size 406x18 text run at (0,0) width 406: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (190.75,169) size 373.50x374 - LayoutBlockFlow {P} at (4,559) size 747x36 + LayoutImage {IMG} at (190.75,169) size 373.50x373.50 + LayoutBlockFlow {P} at (4,558.50) size 747x36 LayoutText {#text} at (0,0) size 738x36 text run at (0,0) width 738: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally" text run at (0,18) width 172: "within the parent element: " text run at (171,18) width 404: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (377.50,611) size 373.50x374 - LayoutBlockFlow {P} at (4,1001) size 747x36 + LayoutImage {IMG} at (377.50,610.50) size 373.50x373.50 + LayoutBlockFlow {P} at (4,1000) size 747x36 LayoutText {#text} at (0,0) size 733x36 text run at (0,0) width 733: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge" text run at (0,18) width 144: "of the parent element: "
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css1/text_properties/vertical_align-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css1/text_properties/vertical_align-expected.txt index f1128dcf..50d2f15 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css1/text_properties/vertical_align-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css1/text_properties/vertical_align-expected.txt
@@ -174,26 +174,26 @@ LayoutImage {IMG} at (47.33,122) size 6x20 LayoutText {#text} at (53,122) size 99x22 text run at (53,122) width 99: " all of which " - LayoutImage {IMG} at (151.78,122) size 20x65 - LayoutText {#text} at (171,122) size 6x22 - text run at (171,122) width 6: " " - LayoutInline {SPAN} at (0,0) size 264x43 - LayoutText {#text} at (176,105) size 264x43 - text run at (176,105) width 264: "should be aligned" - LayoutText {#text} at (439,122) size 6x22 - text run at (439,122) width 6: " " - LayoutImage {IMG} at (444.42,122) size 11x35 - LayoutText {#text} at (455,122) size 120x22 - text run at (455,122) width 120: " with the top of " - LayoutImage {IMG} at (574.09,122) size 9x30 - LayoutText {#text} at (583,122) size 5x22 - text run at (583,122) width 5: " " - LayoutInline {SPAN} at (0,0) size 701x144 - LayoutText {#text} at (587,114) size 21x32 - text run at (587,114) width 21: "a " - LayoutInline {SPAN} at (0,0) size 701x194 - LayoutText {#text} at (607,75) size 701x194 - text run at (607,75) width 94: "14-" + LayoutImage {IMG} at (151.78,122) size 19.50x65 + LayoutText {#text} at (171,122) size 5x22 + text run at (171,122) width 5: " " + LayoutInline {SPAN} at (0,0) size 265x43 + LayoutText {#text} at (175,105) size 265x43 + text run at (175,105) width 265: "should be aligned" + LayoutText {#text} at (439,122) size 5x22 + text run at (439,122) width 5: " " + LayoutImage {IMG} at (443.92,122) size 10.50x35 + LayoutText {#text} at (454,122) size 120x22 + text run at (454,122) width 120: " with the top of " + LayoutImage {IMG} at (573.09,122) size 9x30 + LayoutText {#text} at (582,122) size 5x22 + text run at (582,122) width 5: " " + LayoutInline {SPAN} at (0,0) size 700x144 + LayoutText {#text} at (586,114) size 21x32 + text run at (586,114) width 21: "a " + LayoutInline {SPAN} at (0,0) size 700x194 + LayoutText {#text} at (606,75) size 700x194 + text run at (606,75) width 94: "14-" text run at (0,187) width 144: "point" LayoutText {#text} at (143,226) size 146x32 text run at (143,226) width 146: " text element" @@ -207,15 +207,15 @@ text run at (312,237) width 195: "regardless of the line in which" LayoutText {#text} at (506,234) size 5x22 text run at (506,234) width 5: " " - LayoutImage {IMG} at (510.72,234) size 5x15 - LayoutText {#text} at (515,234) size 6x22 - text run at (515,234) width 6: " " - LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (520,232) size 159x24 - text run at (520,232) width 159: "the images appear." - LayoutText {#text} at (678,234) size 6x22 - text run at (678,234) width 6: " " - LayoutImage {IMG} at (683.17,234) size 27x90 + LayoutImage {IMG} at (510.72,234) size 4.50x15 + LayoutText {#text} at (515,234) size 5x22 + text run at (515,234) width 5: " " + LayoutInline {BIG} at (0,0) size 160x24 + LayoutText {#text} at (519,232) size 160x24 + text run at (519,232) width 160: "the images appear." + LayoutText {#text} at (678,234) size 5x22 + text run at (678,234) width 5: " " + LayoutImage {IMG} at (682.67,234) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1785.31) size 769x36 LayoutText {#text} at (0,0) size 765x36 @@ -247,28 +247,28 @@ LayoutText {#text} at (699,22) size 737x93 text run at (699,22) width 38: " all of" text run at (0,97) width 44: "which " - LayoutImage {IMG} at (43.09,75) size 20x65 - LayoutText {#text} at (63,97) size 121x18 - text run at (63,97) width 121: " should be aligned " - LayoutImage {IMG} at (183.95,90) size 11x35 - LayoutText {#text} at (194,97) size 5x18 - text run at (194,97) width 5: " " + LayoutImage {IMG} at (43.09,75) size 19.50x65 + LayoutText {#text} at (62,97) size 122x18 + text run at (62,97) width 122: " should be aligned " + LayoutImage {IMG} at (183.45,90) size 10.50x35 + LayoutText {#text} at (193,97) size 5x18 + text run at (193,97) width 5: " " LayoutInline {SPAN} at (0,0) size 237x37 - LayoutText {#text} at (198,82) size 237x37 - text run at (198,82) width 237: "with the middle of" - LayoutText {#text} at (434,97) size 5x18 - text run at (434,97) width 5: " " - LayoutImage {IMG} at (438.47,82) size 15x50 - LayoutText {#text} at (453,97) size 5x18 - text run at (453,97) width 5: " " - LayoutInline {SPAN} at (0,0) size 720x106 - LayoutText {#text} at (457,89) size 18x28 - text run at (457,89) width 18: "a " + LayoutText {#text} at (197,82) size 237x37 + text run at (197,82) width 237: "with the middle of" + LayoutText {#text} at (433,97) size 5x18 + text run at (433,97) width 5: " " + LayoutImage {IMG} at (437.47,82) size 15x50 + LayoutText {#text} at (452,97) size 5x18 + text run at (452,97) width 5: " " + LayoutInline {SPAN} at (0,0) size 719x106 + LayoutText {#text} at (456,89) size 18x28 + text run at (456,89) width 18: "a " LayoutInline {SPAN} at (0,0) size 204x69 - LayoutText {#text} at (474,57) size 204x69 - text run at (474,57) width 204: "14-point" - LayoutText {#text} at (677,89) size 720x106 - text run at (677,89) width 43: " text" + LayoutText {#text} at (473,57) size 204x69 + text run at (473,57) width 204: "14-point" + LayoutText {#text} at (676,89) size 719x106 + text run at (676,89) width 43: " text" text run at (0,167) width 76: "element" LayoutText {#text} at (75,175) size 5x18 text run at (75,175) width 5: " " @@ -280,15 +280,15 @@ text run at (98,175) width 195: "regardless of the line in which" LayoutText {#text} at (292,175) size 5x18 text run at (292,175) width 5: " " - LayoutImage {IMG} at (296.22,178) size 5x15 - LayoutText {#text} at (301,175) size 5x18 - text run at (301,175) width 5: " " + LayoutImage {IMG} at (296.22,178) size 4.50x15 + LayoutText {#text} at (300,175) size 5x18 + text run at (300,175) width 5: " " LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (305,170) size 159x24 - text run at (305,170) width 159: "the images appear." - LayoutText {#text} at (463,175) size 5x18 - text run at (463,175) width 5: " " - LayoutImage {IMG} at (467.36,140) size 27x90 + LayoutText {#text} at (304,170) size 159x24 + text run at (304,170) width 159: "the images appear." + LayoutText {#text} at (462,175) size 5x18 + text run at (462,175) width 5: " " + LayoutImage {IMG} at (466.86,140) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,2083.31) size 769x36 LayoutText {#text} at (0,0) size 750x36 @@ -342,36 +342,36 @@ text run at (70,46) width 78: "all of which" LayoutText {#text} at (147,46) size 5x18 text run at (147,46) width 5: " " - LayoutImage {IMG} at (151.30,46) size 20x65 - LayoutText {#text} at (171,46) size 5x18 - text run at (171,46) width 5: " " + LayoutImage {IMG} at (151.30,46) size 19.50x65 + LayoutText {#text} at (170,46) size 5x18 + text run at (170,46) width 5: " " LayoutInline {SPAN} at (0,0) size 114x18 - LayoutText {#text} at (175,46) size 114x18 - text run at (175,46) width 114: "should be aligned" - LayoutText {#text} at (288,46) size 5x18 - text run at (288,46) width 5: " " - LayoutImage {IMG} at (292.16,46) size 11x35 - LayoutText {#text} at (303,46) size 5x18 - text run at (303,46) width 5: " " + LayoutText {#text} at (174,46) size 114x18 + text run at (174,46) width 114: "should be aligned" + LayoutText {#text} at (287,46) size 5x18 + text run at (287,46) width 5: " " + LayoutImage {IMG} at (291.66,46) size 10.50x35 + LayoutText {#text} at (302,46) size 5x18 + text run at (302,46) width 5: " " LayoutInline {SPAN} at (0,0) size 188x37 - LayoutText {#text} at (307,46) size 188x37 - text run at (307,46) width 188: "with the top of" - LayoutText {#text} at (494,46) size 5x18 - text run at (494,46) width 5: " " - LayoutImage {IMG} at (498.69,46) size 15x50 - LayoutText {#text} at (513,46) size 5x18 - text run at (513,46) width 5: " " + LayoutText {#text} at (306,46) size 188x37 + text run at (306,46) width 188: "with the top of" + LayoutText {#text} at (493,46) size 5x18 + text run at (493,46) width 5: " " + LayoutImage {IMG} at (497.69,46) size 15x50 + LayoutText {#text} at (512,46) size 5x18 + text run at (512,46) width 5: " " LayoutInline {SPAN} at (0,0) size 134x18 - LayoutText {#text} at (517,46) size 134x18 - text run at (517,46) width 134: "the tallest element in" - LayoutText {#text} at (650,46) size 5x18 - text run at (650,46) width 5: " " - LayoutImage {IMG} at (654.53,46) size 5x15 - LayoutText {#text} at (659,46) size 5x18 - text run at (659,46) width 5: " " - LayoutInline {BIG} at (0,0) size 753x89 - LayoutText {#text} at (663,46) size 753x89 - text run at (663,46) width 90: "whichever" + LayoutText {#text} at (516,46) size 134x18 + text run at (516,46) width 134: "the tallest element in" + LayoutText {#text} at (649,46) size 5x18 + text run at (649,46) width 5: " " + LayoutImage {IMG} at (653.53,46) size 4.50x15 + LayoutText {#text} at (658,46) size 5x18 + text run at (658,46) width 5: " " + LayoutInline {BIG} at (0,0) size 751x89 + LayoutText {#text} at (662,46) size 751x89 + text run at (662,46) width 89: "whichever" text run at (0,111) width 211: "line the elements appear." LayoutText {#text} at (210,111) size 5x18 text run at (210,111) width 5: " " @@ -518,7 +518,7 @@ LayoutImage {IMG} at (74.42,122) size 6x20 LayoutText {#text} at (80,122) size 99x22 text run at (80,122) width 99: " all of which " - LayoutImage {IMG} at (178.88,122) size 20x65 + LayoutImage {IMG} at (178.88,122) size 19.50x65 LayoutText {#text} at (198,122) size 6x22 text run at (198,122) width 6: " " LayoutInline {SPAN} at (0,0) size 264x43 @@ -526,18 +526,18 @@ text run at (203,105) width 264: "should be aligned" LayoutText {#text} at (466,122) size 6x22 text run at (466,122) width 6: " " - LayoutImage {IMG} at (471.52,122) size 11x35 - LayoutText {#text} at (482,122) size 120x22 - text run at (482,122) width 120: " with the top of " - LayoutImage {IMG} at (601.19,122) size 9x30 - LayoutText {#text} at (610,122) size 5x22 - text run at (610,122) width 5: " " - LayoutInline {SPAN} at (0,0) size 728x144 - LayoutText {#text} at (614,114) size 21x32 - text run at (614,114) width 21: "a " - LayoutInline {SPAN} at (0,0) size 728x194 - LayoutText {#text} at (634,75) size 728x194 - text run at (634,75) width 94: "14-" + LayoutImage {IMG} at (471.02,122) size 10.50x35 + LayoutText {#text} at (481,122) size 120x22 + text run at (481,122) width 120: " with the top of " + LayoutImage {IMG} at (600.19,122) size 9x30 + LayoutText {#text} at (609,122) size 5x22 + text run at (609,122) width 5: " " + LayoutInline {SPAN} at (0,0) size 727x144 + LayoutText {#text} at (613,114) size 21x32 + text run at (613,114) width 21: "a " + LayoutInline {SPAN} at (0,0) size 727x194 + LayoutText {#text} at (633,75) size 727x194 + text run at (633,75) width 94: "14-" text run at (0,187) width 144: "point" LayoutText {#text} at (143,226) size 146x32 text run at (143,226) width 146: " text element" @@ -551,15 +551,15 @@ text run at (312,237) width 195: "regardless of the line in which" LayoutText {#text} at (506,234) size 5x22 text run at (506,234) width 5: " " - LayoutImage {IMG} at (510.72,234) size 5x15 - LayoutText {#text} at (515,234) size 6x22 - text run at (515,234) width 6: " " - LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (520,232) size 159x24 - text run at (520,232) width 159: "the images appear." - LayoutText {#text} at (678,234) size 6x22 - text run at (678,234) width 6: " " - LayoutImage {IMG} at (683.17,234) size 27x90 + LayoutImage {IMG} at (510.72,234) size 4.50x15 + LayoutText {#text} at (515,234) size 5x22 + text run at (515,234) width 5: " " + LayoutInline {BIG} at (0,0) size 160x24 + LayoutText {#text} at (519,232) size 160x24 + text run at (519,232) width 160: "the images appear." + LayoutText {#text} at (678,234) size 5x22 + text run at (678,234) width 5: " " + LayoutImage {IMG} at (682.67,234) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1454.31) size 747x36 LayoutText {#text} at (0,0) size 746x36 @@ -591,28 +591,28 @@ LayoutText {#text} at (699,22) size 737x93 text run at (699,22) width 38: " all of" text run at (0,97) width 44: "which " - LayoutImage {IMG} at (43.09,75) size 20x65 - LayoutText {#text} at (63,97) size 121x18 - text run at (63,97) width 121: " should be aligned " - LayoutImage {IMG} at (183.95,90) size 11x35 - LayoutText {#text} at (194,97) size 5x18 - text run at (194,97) width 5: " " + LayoutImage {IMG} at (43.09,75) size 19.50x65 + LayoutText {#text} at (62,97) size 122x18 + text run at (62,97) width 122: " should be aligned " + LayoutImage {IMG} at (183.45,90) size 10.50x35 + LayoutText {#text} at (193,97) size 5x18 + text run at (193,97) width 5: " " LayoutInline {SPAN} at (0,0) size 237x37 - LayoutText {#text} at (198,82) size 237x37 - text run at (198,82) width 237: "with the middle of" - LayoutText {#text} at (434,97) size 5x18 - text run at (434,97) width 5: " " - LayoutImage {IMG} at (438.47,82) size 15x50 - LayoutText {#text} at (453,97) size 5x18 - text run at (453,97) width 5: " " - LayoutInline {SPAN} at (0,0) size 720x106 - LayoutText {#text} at (457,89) size 18x28 - text run at (457,89) width 18: "a " + LayoutText {#text} at (197,82) size 237x37 + text run at (197,82) width 237: "with the middle of" + LayoutText {#text} at (433,97) size 5x18 + text run at (433,97) width 5: " " + LayoutImage {IMG} at (437.47,82) size 15x50 + LayoutText {#text} at (452,97) size 5x18 + text run at (452,97) width 5: " " + LayoutInline {SPAN} at (0,0) size 719x106 + LayoutText {#text} at (456,89) size 18x28 + text run at (456,89) width 18: "a " LayoutInline {SPAN} at (0,0) size 204x69 - LayoutText {#text} at (474,57) size 204x69 - text run at (474,57) width 204: "14-point" - LayoutText {#text} at (677,89) size 720x106 - text run at (677,89) width 43: " text" + LayoutText {#text} at (473,57) size 204x69 + text run at (473,57) width 204: "14-point" + LayoutText {#text} at (676,89) size 719x106 + text run at (676,89) width 43: " text" text run at (0,167) width 76: "element" LayoutText {#text} at (75,175) size 5x18 text run at (75,175) width 5: " " @@ -624,15 +624,15 @@ text run at (98,175) width 195: "regardless of the line in which" LayoutText {#text} at (292,175) size 5x18 text run at (292,175) width 5: " " - LayoutImage {IMG} at (296.22,178) size 5x15 - LayoutText {#text} at (301,175) size 5x18 - text run at (301,175) width 5: " " + LayoutImage {IMG} at (296.22,178) size 4.50x15 + LayoutText {#text} at (300,175) size 5x18 + text run at (300,175) width 5: " " LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (305,170) size 159x24 - text run at (305,170) width 159: "the images appear." - LayoutText {#text} at (463,175) size 5x18 - text run at (463,175) width 5: " " - LayoutImage {IMG} at (467.36,140) size 27x90 + LayoutText {#text} at (304,170) size 159x24 + text run at (304,170) width 159: "the images appear." + LayoutText {#text} at (462,175) size 5x18 + text run at (462,175) width 5: " " + LayoutImage {IMG} at (466.86,140) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1752.31) size 747x54 LayoutText {#text} at (0,0) size 723x54 @@ -687,31 +687,31 @@ text run at (70,46) width 78: "all of which" LayoutText {#text} at (147,46) size 5x18 text run at (147,46) width 5: " " - LayoutImage {IMG} at (151.30,46) size 20x65 - LayoutText {#text} at (171,46) size 5x18 - text run at (171,46) width 5: " " + LayoutImage {IMG} at (151.30,46) size 19.50x65 + LayoutText {#text} at (170,46) size 5x18 + text run at (170,46) width 5: " " LayoutInline {SPAN} at (0,0) size 114x18 - LayoutText {#text} at (175,46) size 114x18 - text run at (175,46) width 114: "should be aligned" - LayoutText {#text} at (288,46) size 5x18 - text run at (288,46) width 5: " " - LayoutImage {IMG} at (292.16,46) size 11x35 - LayoutText {#text} at (303,46) size 5x18 - text run at (303,46) width 5: " " + LayoutText {#text} at (174,46) size 114x18 + text run at (174,46) width 114: "should be aligned" + LayoutText {#text} at (287,46) size 5x18 + text run at (287,46) width 5: " " + LayoutImage {IMG} at (291.66,46) size 10.50x35 + LayoutText {#text} at (302,46) size 5x18 + text run at (302,46) width 5: " " LayoutInline {SPAN} at (0,0) size 188x37 - LayoutText {#text} at (307,46) size 188x37 - text run at (307,46) width 188: "with the top of" - LayoutText {#text} at (494,46) size 5x18 - text run at (494,46) width 5: " " - LayoutImage {IMG} at (498.69,46) size 15x50 - LayoutText {#text} at (513,46) size 5x18 - text run at (513,46) width 5: " " + LayoutText {#text} at (306,46) size 188x37 + text run at (306,46) width 188: "with the top of" + LayoutText {#text} at (493,46) size 5x18 + text run at (493,46) width 5: " " + LayoutImage {IMG} at (497.69,46) size 15x50 + LayoutText {#text} at (512,46) size 5x18 + text run at (512,46) width 5: " " LayoutInline {SPAN} at (0,0) size 134x18 - LayoutText {#text} at (517,46) size 134x18 - text run at (517,46) width 134: "the tallest element in" - LayoutText {#text} at (650,46) size 5x18 - text run at (650,46) width 5: " " - LayoutImage {IMG} at (654.53,46) size 5x15 + LayoutText {#text} at (516,46) size 134x18 + text run at (516,46) width 134: "the tallest element in" + LayoutText {#text} at (649,46) size 5x18 + text run at (649,46) width 5: " " + LayoutImage {IMG} at (653.53,46) size 4.50x15 LayoutText {#text} at (0,0) size 0x0 LayoutInline {BIG} at (0,0) size 305x24 LayoutText {#text} at (0,111) size 305x24
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/floating-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/floating-replaced-height-008-expected.txt index 87873c1..3c97d7c9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/floating-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/floating-replaced-height-008-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x186 - LayoutBlockFlow {HTML} at (0,0) size 800x186 + LayoutBlockFlow {HTML} at (0,0) size 800x185.59 LayoutBlockFlow {BODY} at (8,16) size 784x36 LayoutBlockFlow {P} at (0,0) size 784x36 LayoutText {#text} at (0,0) size 108x18 @@ -22,12 +22,12 @@ LayoutText {#text} at (58,18) size 455x18 text run at (58,18) width 455: " to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,52) size 784x0 - LayoutImage (floating) {IMG} at (0,0) size 117.59x118 + LayoutImage (floating) {IMG} at (0,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (117.59,0) size 5x5 - LayoutImage (floating) {IMG} at (122.59,0) size 117.59x118 + LayoutImage (floating) {IMG} at (122.59,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (240.19,0) size 5x5 - LayoutImage (floating) {IMG} at (245.19,0) size 117.59x118 + LayoutImage (floating) {IMG} at (245.19,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (362.78,0) size 5x5 - LayoutImage (floating) {IMG} at (367.78,0) size 117.59x118 + LayoutImage (floating) {IMG} at (367.78,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (485.38,0) size 5x5 - LayoutImage (floating) {IMG} at (490.38,0) size 117.59x118 + LayoutImage (floating) {IMG} at (490.38,0) size 117.59x117.59
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-block-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-block-replaced-height-008-expected.txt index bdd8d1f..70cb4877 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-block-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-block-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (58,18) size 455x18 text run at (58,18) width 455: " to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,52) size 784x122 - LayoutImage {OBJECT} at (0,0) size 117.59x118 + LayoutImage {OBJECT} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,104) size 5x18 text run at (117,104) width 5: " " - LayoutImage {OBJECT} at (121.59,0) size 117.59x118 + LayoutImage {OBJECT} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,104) size 5x18 text run at (239,104) width 5: " " - LayoutImage {OBJECT} at (243.19,0) size 117.59x118 + LayoutImage {OBJECT} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,104) size 5x18 text run at (360,104) width 5: " " - LayoutImage {OBJECT} at (364.78,0) size 117.59x118 + LayoutImage {OBJECT} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,104) size 5x18 text run at (482,104) width 5: " " - LayoutImage {OBJECT} at (486.38,0) size 117.59x118 + LayoutImage {OBJECT} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-replaced-height-008-expected.txt index cf263c19..b4b0dbd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/inline-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (58,18) size 455x18 text run at (58,18) width 455: " to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,52) size 784x122 - LayoutImage {IMG} at (0,0) size 117.59x118 + LayoutImage {IMG} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,104) size 5x18 text run at (117,104) width 5: " " - LayoutImage {IMG} at (121.59,0) size 117.59x118 + LayoutImage {IMG} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,104) size 5x18 text run at (239,104) width 5: " " - LayoutImage {IMG} at (243.19,0) size 117.59x118 + LayoutImage {IMG} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,104) size 5x18 text run at (360,104) width 5: " " - LayoutImage {IMG} at (364.78,0) size 117.59x118 + LayoutImage {IMG} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,104) size 5x18 text run at (482,104) width 5: " " - LayoutImage {IMG} at (486.38,0) size 117.59x118 + LayoutImage {IMG} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt index 4ce79a0..20751137 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt
@@ -10,6 +10,6 @@ LayoutText {#text} at (32,0) size 39x18 text run at (32,0) width 39: "TEST" LayoutBlockFlow {DIV} at (16,68) size 192x18 [color=#FFFF00] [bgcolor=#000080] - LayoutImage {IMG} at (0,13) size 32x1.59 + LayoutImage {IMG} at (0,12) size 32x1.59 LayoutText {#text} at (32,0) size 39x18 text run at (32,0) width 39: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.png index bbe8102..b4e0bb4 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.txt index 94295bbf..f0949c1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/t090501-c414-flt-03-b-g-expected.txt
@@ -7,10 +7,10 @@ LayoutText {#text} at (0,4) size 408x18 text run at (0,4) width 252: "In the following test, the purple square " text run at (251,4) width 157: "should be on the left (\x{21E6}" - LayoutImage {IMG} at (407.45,3) size 19x19.19 + LayoutImage {IMG} at (407.45,3) size 19.19x19.19 LayoutText {#text} at (426,4) size 221x18 text run at (426,4) width 221: "), and the teal square on the right (" - LayoutImage {IMG} at (646.38,3) size 19x19.19 + LayoutImage {IMG} at (646.56,3) size 19.19x19.19 LayoutText {#text} at (665,4) size 760x38 text run at (665,4) width 95: "\x{21E8}) of the blue" text run at (0,24) width 63: "rectangle."
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index f544bcb..9ad8cd0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.png index da13015..54ba9ca 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.txt index 16ab5b3..21478cf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.png index da13015..54ba9ca 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.txt index 74380ce..47587fa 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.png index da13015..54ba9ca 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.txt index 16ab5b3..21478cf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.png index da13015..54ba9ca 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.txt index 871d84f..148bf8d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-details-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.png index 4c254ee..b011dd4 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.txt index 3e82e8a..800f6cf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.png index 0d4f057..e875da1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.txt index e31e2914..7e496f8 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.png index b88da71..ded7d7e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.txt index 1d97c88..d1591db 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.png index 37a4874..62390d9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.txt index 12f256e4..329679d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-10-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.png index dd8ceb04..755bfe2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.txt index fded92a..5a55c0b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.png index 0d4f057..e875da1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.txt index e31e2914..7e496f8 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.png index dcdf23a..cedc9ec69 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.txt index 639aad6..949fee8d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 2" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.png index 2b3b4bf..e1f546d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.txt index c5ae164..643fba0f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 2"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.png index 152b2779..a1f58b0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.txt index f5f2be2..cd5a74a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.png index 5482b00ab..1a0a6a7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.txt index 506df88..970aaa9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-4-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.png index 4907242..04f5c1e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.txt index 1180531..4a4d44b2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.png index 0d4f057..e875da1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.txt index e31e2914..7e496f8 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.png index b88da71..ded7d7e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.txt index 314d429f7..bf0b1bb 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.png index e283a23..2ceeabf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.txt index 1d597242..6a78b90 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.png index b88da71..ded7d7e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.txt index 314d429f7..bf0b1bb 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.png index 0e6206b..c7b894a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.txt index 11b8fb3..34eb9c4e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-7-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.png index 6c24e8d2..8f15d8c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.txt index 4c7868fd..98c468c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.png index 2ec5f9c..33d73bb9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.txt index e42fa923..bc4ac55 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-8-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x18 text run at (16,0) width 40: "new 2" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.png index b713e9e..0d784b0f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.txt index 82b74fa..a9cf7d4 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.png index e2b75a0..4489fece 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.txt index aa771d2..34576a3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-9-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.png index a9df7d3..0239e5cd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.txt index ae33d38..648f8f78 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x18 text run at (16,0) width 65: "summary " LayoutInline {B} at (0,0) size 145x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.png index a9df7d3..0239e5cd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.txt index 3acbc10e..7c1bd8d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-add-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x18 text run at (16,0) width 65: "summary " LayoutInline {SPAN} at (0,0) size 145x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.png index f198771..7b17af0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.txt index 1fd42339..e65c8eb 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-marker-style-expected.txt
@@ -1,27 +1,27 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x473 - LayoutBlockFlow {HTML} at (0,0) size 800x472.70 - LayoutBlockFlow {BODY} at (8,8) size 784x456.70 - LayoutBlockFlow {DIV} at (0,0) size 784x85 - LayoutBlockFlow {DETAILS} at (0,0) size 784x85 - LayoutBlockFlow {SUMMARY} at (0,0) size 784x85 +layer at (0,0) size 800x474 + LayoutBlockFlow {HTML} at (0,0) size 800x473.70 + LayoutBlockFlow {BODY} at (8,8) size 784x457.70 + LayoutBlockFlow {DIV} at (0,0) size 784x86 + LayoutBlockFlow {DETAILS} at (0,0) size 784x86 + LayoutBlockFlow {SUMMARY} at (0,0) size 784x86 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: right - LayoutText {#text} at (121,57) size 94x28 - text run at (121,57) width 94: "Summary" - LayoutBlockFlow {DIV} at (0,85) size 117x182.77 - LayoutBlockFlow {DETAILS} at (0,0) size 117x182.77 - LayoutBlockFlow {SUMMARY} at (0,0) size 117x182.77 + LayoutText {#text} at (121,58) size 94x28 + text run at (121,58) width 94: "Summary" + LayoutBlockFlow {DIV} at (0,86) size 118x182.77 + LayoutBlockFlow {DETAILS} at (0,0) size 118x182.77 + LayoutBlockFlow {SUMMARY} at (0,0) size 118x182.77 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: down - LayoutText {#text} at (89,89) size 28x94 - text run at (89,89) width 94: "Summary" - LayoutBlockFlow {DIV} at (0,267.77) size 784x46 + LayoutText {#text} at (90,89) size 28x94 + text run at (90,89) width 94: "Summary" + LayoutBlockFlow {DIV} at (0,268.77) size 784x46 LayoutBlockFlow {DETAILS} at (0,0) size 784x46 LayoutBlockFlow {SUMMARY} at (0,0) size 784x46 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: right LayoutText {#text} at (73,18) size 94x28 text run at (73,18) width 94: "Summary" - LayoutBlockFlow {DIV} at (0,313.77) size 70x142.94 + LayoutBlockFlow {DIV} at (0,314.77) size 70x142.94 LayoutBlockFlow {DETAILS} at (0,0) size 70x142.94 LayoutBlockFlow {SUMMARY} at (0,0) size 70x142.94 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: down
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.png index e237d68..7e35fc2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.txt index 9b1d8d6..54a979b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-1-expected.txt
@@ -6,13 +6,13 @@ LayoutBlockFlow {DETAILS} at (0,0) size 784x136 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x102 [border: (8px solid #9999CC)] LayoutBlockFlow (anonymous) at (8,8) size 752x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x18 text run at (16,0) width 5: " " text run at (20,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (8,26) size 752x68 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 736x34 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,11) size 10.55x10.55: down LayoutText {#text} at (24,8) size 292x18 text run at (24,8) width 5: " " text run at (28,8) width 288: "nested summary (summary-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.png index e3defc3..b9890935 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.txt index 340cb34..d556ce93 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-nested-2-expected.txt
@@ -5,14 +5,14 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x136 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x34 [border: (8px solid #9999CC)] - LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,11) size 10.55x10.55: down LayoutText {#text} at (24,8) size 65x18 text run at (24,8) width 5: " " text run at (28,8) width 61: "summary" LayoutBlockFlow {DIV} at (8,42) size 768x86 LayoutBlockFlow {DETAILS} at (0,0) size 768x68 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 752x34 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,11) size 10.55x10.55: down LayoutText {#text} at (24,8) size 274x18 text run at (24,8) width 5: " " text run at (28,8) width 270: "nested summary (details-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.png index aa47ca6..aa6ab35d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.txt index f5f92df..f9e73291 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.png index e29b896..91a490d0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.txt index 91f6e22..c7b513db 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.png index aa47ca6..aa6ab35d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.txt index f5f92df..f9e73291 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.png index 10256f01..45d986a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.txt index db0fe2a..dcda023 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-no-summary4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.png index cd1170d..c986058 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.txt index a94d944..4eba677 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open-javascript-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 51x18 text run at (16,0) width 51: "details1" LayoutBlockFlow {DIV} at (0,18) size 784x19 @@ -13,7 +13,7 @@ LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {DETAILS} at (0,37) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 51x18 text run at (16,0) width 51: "details2" layer at (11,29) size 125x13
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.png index 5482b00ab..1a0a6a7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.txt index 506df88..970aaa9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.png index 06c1225..a3a49f2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.txt index 8680fb3..e0c0e85 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.png index 5482b00ab..1a0a6a7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.txt index 506df88..970aaa9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.png index 06c1225..a3a49f2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.txt index aea74fe..ca6842d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x37 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x19
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.png index 5482b00ab..1a0a6a7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.txt index 506df88..970aaa9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.png index 0e6255c..019b6d1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.txt index 87d5c07..f1e42f3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-open6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.png index f0b938d1..7304a24 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.txt index e82a6e1..1875179 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-position-expected.txt
@@ -8,16 +8,16 @@ LayoutBlockFlow {DETAILS} at (0,18) size 784x0 layer at (50,150) size 49x18 LayoutBlockFlow (positioned) {SUMMARY} at (50,150) size 48.95x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 33x18 text run at (16,0) width 33: "fixed" layer at (158,158) size 784x18 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutBlockFlow (relative positioned) {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 49x18 text run at (16,0) width 49: "relative" layer at (250,150) size 70x18 LayoutBlockFlow (positioned) {SUMMARY} at (250,150) size 70.28x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x18 text run at (16,0) width 55: "absolute"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.png index d4285b1..256f52fd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.txt index 010401c..190bcae2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.png index d4285b1..256f52fd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.txt index 6dbb856..f27b827 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.png index 2f385b29..64ecc8b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.txt index b2f5351..d38629a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.png index aa47ca6..aa6ab35d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.txt index f5f92df..f9e73291 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.png index c7493636..d333a86 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.txt index 4288cb4..3a0c9e82 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 2" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.png index 640b26d..31c517e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.txt index 3a43968..bb21a87 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 2"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.png index f2b20e8..22ed7a5 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.txt index 589265f..d5e3aed 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 1" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.png index 2e5d5ae..4def701 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.txt index dbc2a60c..78459f7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 1"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.png index 8a239f7..a69d224 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.txt index be7129c6..d4cdc009 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 0 {CONTENT} of {#document-fragment} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.png index e29b896..91a490d0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.txt index 91f6e22..c7b513db 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x18 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.png index 3b9b8dc..30dd1bcf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.txt index e2a228fe..3b43dff 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 2 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.png index 658d5e1..9dbb1cf 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.txt index cc6055d..89a47b8 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-5-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 2" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.png index 641441a8..d43e42e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.txt index 58d9807d..e435b2f6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.png index b50768c..ea520cd7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.txt index b8f50ea..cb68028 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x18 text run at (16,0) width 73: "summary 1" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.png index 40fd6330..1206ef0 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.txt index aac6c503b..e08989dd 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x18 text run at (16,0) width 65: "summary " LayoutText {#text} at (80,0) size 190x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.png index cd88658d..083c3e3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.txt index f5eeb16..454192b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-remove-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x18 text run at (16,0) width 65: "summary " LayoutInline {SPAN} at (0,0) size 185x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.png index 46429913..5dd6955f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.txt index 49682c0..c3ab3f20 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-summary-child-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 5x18 text run at (16,0) width 5: " " LayoutBlockFlow {SPAN} at (20.94,2) size 62.42x15
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.png index 4241dc38..72a4442 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.txt index 462c5f8..4802ef7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-replace-text-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 64x18 text run at (16,0) width 64: "Summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.png index 9a92b029..a6218d2f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.txt index db07fa40..e6c14ae 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-center-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (21.75,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (21.75,3) size 10.55x10.55: right LayoutText {#text} at (38,0) size 61x18 text run at (38,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (21.75,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (21.75,3) size 10.55x10.55: down LayoutText {#text} at (38,0) size 61x18 text run at (38,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,21.75) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,21.75) size 10.55x10.55: down LayoutText {#text} at (0,38) size 18x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,21.75) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,21.75) size 10.55x10.55: right LayoutText {#text} at (0,38) size 18x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,21.75) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,21.75) size 10.55x10.55: down LayoutText {#text} at (0,38) size 18x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,21.75) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,21.75) size 10.55x10.55: left LayoutText {#text} at (0,38) size 18x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (87.69,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (87.69,3) size 10.55x10.55: left LayoutText {#text} at (21,0) size 61x18 text run at (21,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (87.69,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (87.69,3) size 10.55x10.55: down LayoutText {#text} at (21,0) size 61x18 text run at (21,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,87.69) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,87.69) size 10.55x10.55: up LayoutText {#text} at (0,21) size 18x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,87.69) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,87.69) size 10.55x10.55: right LayoutText {#text} at (0,21) size 18x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,87.69) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,87.69) size 10.55x10.55: up LayoutText {#text} at (0,21) size 18x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,87.69) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,87.69) size 10.55x10.55: left LayoutText {#text} at (0,21) size 18x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.png index 676ddf89..86a83ac7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.txt index 323c3c44..def58be 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-left-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (65.94,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (65.94,3) size 10.55x10.55: left LayoutText {#text} at (0,0) size 60x18 text run at (0,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (65.94,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (65.94,3) size 10.55x10.55: down LayoutText {#text} at (0,0) size 60x18 text run at (0,0) width 60: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,65.94) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,65.94) size 10.55x10.55: up LayoutText {#text} at (0,0) size 18x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,65.94) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,65.94) size 10.55x10.55: right LayoutText {#text} at (0,0) size 18x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,65.94) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,65.94) size 10.55x10.55: up LayoutText {#text} at (0,0) size 18x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,65.94) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,65.94) size 10.55x10.55: left LayoutText {#text} at (0,0) size 18x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.png index 995791a..e3037be 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.txt index c51be0a..9d72735f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-align-right-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (43.52,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (43.52,3) size 10.55x10.55: right LayoutText {#text} at (60,0) size 60x18 text run at (60,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (43.52,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (43.52,3) size 10.55x10.55: down LayoutText {#text} at (60,0) size 60x18 text run at (60,0) width 60: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,43.52) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,43.52) size 10.55x10.55: down LayoutText {#text} at (0,60) size 18x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,43.52) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,43.52) size 10.55x10.55: right LayoutText {#text} at (0,60) size 18x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,43.52) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,43.52) size 10.55x10.55: down LayoutText {#text} at (0,60) size 18x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,43.52) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,43.52) size 10.55x10.55: left LayoutText {#text} at (0,60) size 18x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: left LayoutText {#text} at (43,0) size 61x18 text run at (43,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: down LayoutText {#text} at (43,0) size 61x18 text run at (43,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: left LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.png index b85dbbc6..e1ebf79 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.txt index ccd4b52..fe684ab3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/html/details-writing-mode-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x18 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 18x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: left LayoutText {#text} at (43,0) size 61x18 text run at (43,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: down LayoutText {#text} at (43,0) size 61x18 text run at (43,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: left LayoutText {#text} at (0,43) size 18x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/repaint/details-open-repaint-expected.txt index 1c8cdee..497ede6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [11, 71, 125, 13], [8, 68, 784, 19], [8, 68, 131, 19], - [8, 54, 11, 11] + [8, 53, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/width100percent-image-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/width100percent-image-expected.txt index d16cec86..2362483d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/width100percent-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/replaced/width100percent-image-expected.txt
@@ -27,11 +27,11 @@ LayoutTableSection {TBODY} at (0,0) size 784x279 LayoutTableRow {TR} at (0,1) size 784x277 LayoutTableCell {TD} at (1,1) size 216x277 [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (218,1) size 216x277 [r=0 c=1 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (435,1) size 216x277 [r=0 c=2 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (652,129) size 131x20 [r=0 c=3 rs=1 cs=1] LayoutText {#text} at (1,1) size 4x18 text run at (1,1) width 4: " "
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.png index b821495..dc71400c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.txt index 7077f7d..66f092a6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/international/vertical-text-glyph-test-expected.txt
@@ -1,21 +1,21 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 642 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 644 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x642 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x641.97 - LayoutBlockFlow {BODY} at (8,21.33) size 769x612.64 +layer at (0,0) size 785x644 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x643.97 + LayoutBlockFlow {BODY} at (8,21.33) size 769x614.64 LayoutBlockFlow {P} at (0,0) size 769x33 LayoutText {#text} at (0,5) size 165x22 text run at (0,5) width 165: "Simple text path" - LayoutBlockFlow (anonymous) at (0,54.33) size 769x235 + LayoutBlockFlow (anonymous) at (0,54.33) size 769x236 LayoutBlockFlow {SPAN} at (0,0) size 33x226.88 LayoutText {#text} at (5,0) size 22x227 text run at (5,0) width 227: "string\x{300C}\x{3042}\x{3001}\x{5909}\x{3063}\x{FF01}\x{300D}\x{3002}" LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {HR} at (0,299.98) size 769x2 [border: (1px inset #EEEEEE)] - LayoutBlockFlow {P} at (0,323.31) size 769x33 + LayoutBlockFlow {HR} at (0,300.98) size 769x2 [border: (1px inset #EEEEEE)] + LayoutBlockFlow {P} at (0,324.31) size 769x33 LayoutText {#text} at (0,5) size 185x22 text run at (0,5) width 185: "Complex text path" - LayoutBlockFlow (anonymous) at (0,377.64) size 769x235 + LayoutBlockFlow (anonymous) at (0,378.64) size 769x236 LayoutBlockFlow {SPAN} at (0,0) size 33x226.88 LayoutText {#text} at (5,0) size 22x227 text run at (5,0) width 227: "string\x{300C}\x{3042}\x{3001}\x{5909}\x{3063}\x{FF01}\x{300D}\x{3002}"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.png index 55c4a8d..51b854d7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt index 9df1c98..ebf48827 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt
@@ -1,77 +1,77 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1236 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1240 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1236 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x1236 - LayoutBlockFlow {BODY} at (8,8) size 769x1220 +layer at (0,0) size 785x1240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x1240 + LayoutBlockFlow {BODY} at (8,8) size 769x1224 LayoutText {#text} at (0,0) size 289x18 text run at (0,0) width 289: "LTR fieldset with left/center/right text-align: " LayoutBR {BR} at (288,14) size 1x0 LayoutFieldset {FIELDSET} at (16,34) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (34,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,155) size 4x18 - text run at (260,155) width 4: " " + LayoutText {#text} at (260,156) size 4x18 + text run at (260,156) width 4: " " LayoutFieldset {FIELDSET} at (280,34) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (110,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,189) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,190) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,310) size 4x18 - text run at (260,310) width 4: " " + LayoutText {#text} at (260,312) size 4x18 + text run at (260,312) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,328) size 290x18 - text run at (0,328) width 290: "RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (289,342) size 1x0 - LayoutFieldset {FIELDSET} at (16,362) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,330) size 290x18 + text run at (0,330) width 290: "RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (289,344) size 1x0 + LayoutFieldset {FIELDSET} at (16,364) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,483) size 4x18 - text run at (260,483) width 4: " " - LayoutFieldset {FIELDSET} at (280,362) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (260,486) size 4x18 + text run at (260,486) width 4: " " + LayoutFieldset {FIELDSET} at (280,364) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (90,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,517) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,520) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,638) size 4x18 - text run at (260,638) width 4: " " + LayoutText {#text} at (260,642) size 4x18 + text run at (260,642) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,656) size 343x18 - text run at (0,656) width 343: "Vertical LTR fieldset with left/center/right text-align: " - LayoutBR {BR} at (342,670) size 1x0 - LayoutFieldset {FIELDSET} at (16,690) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,660) size 343x18 + text run at (0,660) width 343: "Vertical LTR fieldset with left/center/right text-align: " + LayoutBR {BR} at (342,674) size 1x0 + LayoutFieldset {FIELDSET} at (16,694) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,34) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,920) size 5x18 - text run at (151,920) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,690) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,924) size 5x18 + text run at (151,924) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,694) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,110) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,920) size 5x18 - text run at (307,920) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,690) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,924) size 5x18 + text run at (307,924) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,694) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (462,920) size 5x18 - text run at (462,920) width 5: " " + LayoutText {#text} at (462,924) size 5x18 + text run at (462,924) width 5: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,938) size 343x18 - text run at (0,938) width 343: "Vertical RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (342,952) size 1x0 - LayoutFieldset {FIELDSET} at (16,972) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,942) size 343x18 + text run at (0,942) width 343: "Vertical RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (342,956) size 1x0 + LayoutFieldset {FIELDSET} at (16,976) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,14) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,1202) size 5x18 - text run at (151,1202) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,972) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,1206) size 5x18 + text run at (151,1206) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,976) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,90) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,1202) size 5x18 - text run at (307,1202) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,972) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,1206) size 5x18 + text run at (307,1206) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,976) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt index bc993a6..ac14064 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt
@@ -26,7 +26,7 @@ LayoutTableCell {TD} at (0,0) size 135x45 [r=0 c=0 rs=1 cs=1] LayoutBlockFlow (floating) {P} at (1,451) size 135x9 layer at (14,131) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -34,7 +34,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,194) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -42,7 +42,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,257) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -50,7 +50,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,356) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -58,7 +58,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,455) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-aspect-ratio-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-aspect-ratio-expected.txt index 1a1dc37..8847e3b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-aspect-ratio-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-aspect-ratio-expected.txt
@@ -18,13 +18,13 @@ layer at (8,282) size 160x120 LayoutVideo {VIDEO} at (0,240) size 160x120 layer at (168,282) size 160x120 - LayoutVideo {VIDEO} at (0,0) size 160x120 + LayoutVideo {VIDEO} at (0,0) size 160x119.98 layer at (328,282) size 160x120 LayoutVideo {VIDEO} at (160,0) size 160x120 layer at (8,402) size 160x120 LayoutVideo {VIDEO} at (0,0) size 160x120 layer at (168,402) size 160x120 - LayoutVideo {VIDEO} at (160,0) size 160x120 + LayoutVideo {VIDEO} at (160,0) size 160x119.98 layer at (8,42) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -36,10 +36,10 @@ layer at (8,282) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (168,282) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (168,282) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98 layer at (328,282) size 160x120 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 LayoutBlockFlow {DIV} at (0,85) size 160x35 @@ -51,10 +51,10 @@ layer at (8,402) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (168,402) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (168,402) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98 layer at (328,402) size 320x120 LayoutBlockFlow (relative positioned) {DIV} at (320,360) size 320x120 layer at (328,402) size 160x120 @@ -65,9 +65,9 @@ layer at (328,402) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (488,402) size 160x120 - LayoutVideo (positioned) {VIDEO} at (160,0) size 160x120 + LayoutVideo (positioned) {VIDEO} at (160,0) size 160x119.98 layer at (488,402) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (488,402) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv420-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv420-expected.txt index 38a602f7..7dc440c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv420-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv420-expected.txt
@@ -8,9 +8,9 @@ text run at (0,0) width 333: "Test correct colorspace for yuv420, i.e. YU12 video" LayoutBlockFlow (anonymous) at (0,34) size 784x156 layer at (8,42) size 206x156 - LayoutVideo {VIDEO} at (0,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (0,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (11,45) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (11,45) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv422-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv422-expected.txt index 917355b..a2f942e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv422-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-colorspace-yuv422-expected.txt
@@ -8,9 +8,9 @@ text run at (0,0) width 333: "Test correct colorspace for yuv422, i.e. YU16 video" LayoutBlockFlow (anonymous) at (0,34) size 784x156 layer at (8,42) size 206x156 - LayoutVideo {VIDEO} at (0,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (0,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (11,45) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (11,45) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-rendering-expected.txt index dd079e53..b566e4fa 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,42) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,282) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,42) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -38,10 +38,10 @@ LayoutBlockFlow {DIV} at (11.89,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,282) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,282) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,487) size 310x30 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30 @@ -60,12 +60,12 @@ LayoutBlockFlow {DIV} at (11.89,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,522) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,522) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,522) size 320x239.98 layer at (8,522) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,522) size 320x205 backgroundClip at (8,522) size 320x78 clip at (8,522) size 320x78 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,727) size 310x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-with-cast-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-with-cast-rendering-expected.txt index 26f08dd..a06fdf81 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-with-cast-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-controls-with-cast-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,50) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,294) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,50) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -39,10 +39,10 @@ LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,294) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,294) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,499) size 310x30 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30 @@ -62,12 +62,12 @@ LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,538) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,538) size 320x239.98 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,538) size 320x205 backgroundClip at (8,538) size 320x62 clip at (8,538) size 320x62 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,743) size 310x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-layer-crash-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-layer-crash-expected.txt index d8587df1..1560ae3 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-layer-crash-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-layer-crash-expected.txt
@@ -17,16 +17,16 @@ text run at (0,320) width 4: " " LayoutBR {BR} at (210,334) size 0x0 layer at (12,60) size 206x156 - LayoutVideo {VIDEO} at (4,18) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,18) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,63) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,63) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,220) size 206x156 - LayoutVideo {VIDEO} at (4,178) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,178) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,223) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,223) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-dark-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-dark-rendering-expected.txt index d65a713..a9cf5c8e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-dark-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-dark-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,50) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,294) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,50) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -23,18 +23,18 @@ layer at (24,60) size 30x30 LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 layer at (8,294) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,294) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (24,304) size 30x30 - LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 + LayoutButton (positioned) {INPUT} at (16,10.23) size 30x30 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,538) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,538) size 320x239.98 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,538) size 320x205 backgroundClip at (8,538) size 320x62 clip at (8,538) size 320x62 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (24,548) size 30x30 - LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 + LayoutButton (positioned) {INPUT} at (16,10.23) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-light-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-light-rendering-expected.txt index 96b71ed..b0fb969 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-light-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-overlay-cast-light-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,50) size 352x288 LayoutVideo {VIDEO} at (0,0) size 352x288 layer at (8,342) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo {VIDEO} at (0,0) size 320x262 + LayoutVideo {VIDEO} at (0,0) size 320x261.81 layer at (8,50) size 352x288 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 352x288 LayoutBlockFlow {DIV} at (0,253) size 352x35 @@ -23,16 +23,16 @@ layer at (26,63) size 30x30 LayoutButton (positioned) {INPUT} at (17.59,12.64) size 30x30 layer at (8,342) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 - LayoutBlockFlow {DIV} at (0,227) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x261.81 + LayoutBlockFlow {DIV} at (0,226.81) size 320x35 layer at (8,342) size 320x227 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x226.81 layer at (24,353) size 30x30 - LayoutButton (positioned) {INPUT} at (16,11.34) size 30x30 + LayoutButton (positioned) {INPUT} at (16,11.33) size 30x30 layer at (8,608) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,608) size 320x262 + LayoutVideo (positioned) {VIDEO} at (8,608) size 320x261.81 layer at (8,608) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 - LayoutBlockFlow {DIV} at (0,227) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x261.81 + LayoutBlockFlow {DIV} at (0,226.81) size 320x35 layer at (8,608) size 320x227 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x226.81
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/video-transformed-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/media/video-transformed-expected.txt index db600a3..8e667f6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/media/video-transformed-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/media/video-transformed-expected.txt
@@ -17,23 +17,23 @@ text run at (0,462) width 4: " " LayoutBR {BR} at (210,476) size 0x0 layer at (12,42) size 206x156 - LayoutVideo {VIDEO} at (4,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,45) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,45) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,202) size 206x156 - LayoutVideo {VIDEO} at (4,160) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,160) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,205) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,205) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,362) size 206x156 - LayoutVideo {VIDEO} at (4,320) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,320) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,365) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,365) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.png index 4d52c88..3d24206 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.txt index 5503d378..14ffbba 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/rightsizing-grid-expected.txt
@@ -1,9 +1,9 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1435 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1360 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1435 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {html} at (0,0) size 785x1435.14 +layer at (0,0) size 785x1360 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {html} at (0,0) size 785x1360.38 LayoutBlockFlow {body} at (8,8) size 769x0 - LayoutBlockFlow (floating) {div} at (0,0) size 769x1427.14 + LayoutBlockFlow (floating) {div} at (0,0) size 769x1352.38 LayoutBlockFlow {h1} at (0,21.44) size 769x37 LayoutText {#text} at (0,0) size 443x37 text run at (0,0) width 443: "SVG grid with percentage width" @@ -11,52 +11,48 @@ LayoutText {#text} at (0,0) size 149x18 text run at (0,0) width 149: "WICD Core 1.0 #20-3" LayoutBlockFlow {p} at (0,119.14) size 769x0 - LayoutBlockFlow (floating) {div} at (256.30,128) size 256.30x448 + LayoutBlockFlow (floating) {div} at (256.30,128.16) size 256.30x448.52 LayoutBR {br} at (256,0) size 1x18 - LayoutBlockFlow (floating) {div} at (512.59,128) size 256.30x448 - LayoutBlockFlow (floating) {div} at (384.50,704) size 384.50x192 - LayoutBlockFlow (floating) {div} at (0,896) size 384.50x192 - LayoutBlockFlow (floating) {div} at (384.50,1024) size 384.50x128 - LayoutBlockFlow {p} at (0,119.14) size 769x594 [color=#FFFFFF] + LayoutBlockFlow (floating) {div} at (512.59,128.16) size 256.30x448.52 + LayoutBlockFlow (floating) {div} at (0,704.83) size 384.50x192.25 + LayoutBlockFlow (floating) {div} at (384.50,704.83) size 384.50x192.25 + LayoutBlockFlow (floating) {div} at (384.50,897.08) size 384.50x128.16 + LayoutBlockFlow {p} at (0,119.14) size 769x1043.23 [color=#FFFFFF] LayoutBR {br} at (769,0) size 0x18 - LayoutText {#text} at (640,576) size 9x18 - text run at (640,576) width 9: ".." - LayoutBlockFlow {p} at (0,729.14) size 769x578 - LayoutText {#text} at (640,0) size 763x578 - text run at (640,0) width 123: "Above, you should" - text run at (640,18) width 100: "see a grid of 17" - text run at (640,36) width 69: "SVG child" - text run at (640,54) width 107: "elements sticked" - text run at (640,72) width 107: "together to build" - text run at (0,542) width 124: "one rectangle grid. " - text run at (123,542) width 622: "You should be able to resize your browser window and the grid rendering should adjust to it. The" - text run at (0,560) width 352: "outcome should look like in these sample screenshots: " + LayoutText {#text} at (0,1025) size 8x18 + text run at (0,1025) width 8: ".." + LayoutBlockFlow {p} at (0,1178.38) size 769x54 + LayoutText {#text} at (0,0) size 766x54 + text run at (0,0) width 644: "Above, you should see a grid of 17 SVG child elements sticked together to build one rectangle grid. " + text run at (643,0) width 123: "You should be able" + text run at (0,18) width 764: "to resize your browser window and the grid rendering should adjust to it. The outcome should look like in these sample" + text run at (0,36) width 83: "screenshots: " LayoutInline {a} at (0,0) size 35x18 [color=#0000EE] - LayoutText {#text} at (351,560) size 35x18 - text run at (351,560) width 35: "small" - LayoutText {#text} at (385,560) size 9x18 - text run at (385,560) width 9: ", " + LayoutText {#text} at (82,36) size 35x18 + text run at (82,36) width 35: "small" + LayoutText {#text} at (116,36) size 9x18 + text run at (116,36) width 9: ", " LayoutInline {a} at (0,0) size 42x18 [color=#0000EE] - LayoutText {#text} at (393,560) size 42x18 - text run at (393,560) width 42: "bigger" - LayoutText {#text} at (434,560) size 32x18 - text run at (434,560) width 32: " and " + LayoutText {#text} at (124,36) size 42x18 + text run at (124,36) width 42: "bigger" + LayoutText {#text} at (165,36) size 32x18 + text run at (165,36) width 32: " and " LayoutInline {a} at (0,0) size 22x18 [color=#0000EE] - LayoutText {#text} at (465,560) size 22x18 - text run at (465,560) width 22: "big" - LayoutText {#text} at (486,560) size 5x18 - text run at (486,560) width 5: "." - LayoutBlockFlow {p} at (0,1323.14) size 769x36 + LayoutText {#text} at (196,36) size 22x18 + text run at (196,36) width 22: "big" + LayoutText {#text} at (217,36) size 5x18 + text run at (217,36) width 5: "." + LayoutBlockFlow {p} at (0,1248.38) size 769x36 LayoutText {#text} at (0,0) size 762x36 text run at (0,0) width 762: "The test is successful, if all SVG elements resize exacly dependend on the width of the browser window, but keep their" text run at (0,18) width 636: "aspect ratio and relative position. The complete grid should always show a perfect rectangle object." - LayoutBlockFlow {p} at (0,1375.14) size 769x36 + LayoutBlockFlow {p} at (0,1300.38) size 769x36 LayoutBR {br} at (0,0) size 0x18 LayoutInline {a} at (0,0) size 33x18 [color=#0000EE] LayoutText {#text} at (0,18) size 33x18 text run at (0,18) width 33: "Back" layer at (8,127) size 385x128 - LayoutEmbeddedObject (floating) {object} at (0,0) size 384.50x128 + LayoutEmbeddedObject (floating) {object} at (0,0) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -70,7 +66,7 @@ LayoutSVGInlineText {#text} at (0,0) size 15x24 chunk 1 (middle anchor) text run 1 at (52.78,27.00) startOffset 0 endOffset 1 width 14.44: "A" layer at (393,127) size 384x128 - LayoutEmbeddedObject (floating) {object} at (384.50,0) size 384.50x128 + LayoutEmbeddedObject (floating) {object} at (384.50,0) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -84,7 +80,7 @@ LayoutSVGInlineText {#text} at (0,0) size 13x24 chunk 1 (middle anchor) text run 1 at (53.89,27.00) startOffset 0 endOffset 1 width 12.22: "L" layer at (8,255) size 256x449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,128) size 256.30x449 + LayoutEmbeddedObject (floating) {object} at (0,128.16) size 256.30x448.52 layer at (0,0) size 256x449 LayoutView at (0,0) size 256x449 layer at (0,0) size 256x449 @@ -118,7 +114,7 @@ LayoutSVGInlineText {#text} at (0,0) size 13x24 chunk 1 (middle anchor) text run 1 at (53.89,127.00) startOffset 0 endOffset 1 width 12.22: "E" layer at (264,255) size 256x256 - LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256 + LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256.30 layer at (0,0) size 256x256 LayoutView at (0,0) size 256x256 layer at (0,0) size 256x256 @@ -131,8 +127,8 @@ LayoutSVGText {text} at (32,28) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x24 chunk 1 (middle anchor) text run 1 at (32.78,47.00) startOffset 0 endOffset 1 width 14.44: "K" -layer at (264,511) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 +layer at (264,512) size 256x193 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,256.30) size 256.30x192.22 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 layer at (0,0) size 256x192 @@ -146,7 +142,7 @@ LayoutSVGInlineText {#text} at (0,0) size 8x24 chunk 1 (middle anchor) text run 1 at (36.11,37.00) startOffset 0 endOffset 1 width 7.78: "J" layer at (521,255) size 256x256 - LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256 + LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256.30 layer at (0,0) size 256x256 LayoutView at (0,0) size 256x256 layer at (0,0) size 256x256 @@ -175,8 +171,8 @@ LayoutSVGText {text} at (52,48) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x24 chunk 1 (middle anchor) text run 1 at (52.78,67.00) startOffset 0 endOffset 1 width 14.44: "O" -layer at (521,511) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 +layer at (521,512) size 256x193 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,256.30) size 256.30x192.22 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 layer at (0,0) size 256x192 @@ -189,8 +185,8 @@ LayoutSVGText {text} at (32,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x24 chunk 1 (middle anchor) text run 1 at (32.78,37.00) startOffset 0 endOffset 1 width 14.44: "Q" -layer at (264,703) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (256.30,576) size 384.50x128 +layer at (8,704) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,576.67) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -203,8 +199,8 @@ LayoutSVGText {text} at (54,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x24 chunk 1 (middle anchor) text run 1 at (54.44,27.00) startOffset 0 endOffset 1 width 11.12: "F" -layer at (8,831) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,704) size 384.50x128 +layer at (393,704) size 384x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (384.50,576.67) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -217,8 +213,8 @@ LayoutSVGText {text} at (56,8) size 8x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 7x24 chunk 1 (middle anchor) text run 1 at (56.67,27.00) startOffset 0 endOffset 1 width 6.66: "I" -layer at (393,831) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192 +layer at (8,832) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192.25 layer at (0,0) size 96x192 LayoutView at (0,0) size 96x192 layer at (0,0) size 96x192 @@ -228,8 +224,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGRect {rect} at (0,0) size 96x192 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [x=1.00] [y=1.00] [width=28.00] [height=58.00] -layer at (489,831) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192 +layer at (104,832) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192.25 layer at (0,0) size 288x192 LayoutView at (0,0) size 288x192 layer at (0,0) size 288x192 @@ -242,8 +238,8 @@ LayoutSVGText {text} at (37,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x24 chunk 1 (middle anchor) text run 1 at (37.78,37.00) startOffset 0 endOffset 1 width 14.44: "G" -layer at (8,1023) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192 +layer at (393,832) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192.25 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 layer at (0,0) size 192x192 @@ -256,8 +252,8 @@ LayoutSVGText {text} at (22,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x24 chunk 1 (middle anchor) text run 1 at (22.78,37.00) startOffset 0 endOffset 1 width 14.44: "H" -layer at (200,1023) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192 +layer at (585,832) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192.25 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 layer at (0,0) size 192x192 @@ -270,8 +266,8 @@ LayoutSVGText {text} at (23,18) size 14x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 14x24 chunk 1 (middle anchor) text run 1 at (23.33,37.00) startOffset 0 endOffset 1 width 13.34: "R" -layer at (393,1023) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (384.50,896) size 192.25x128 +layer at (8,1024) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,897.08) size 192.25x128.16 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 layer at (0,0) size 192x128 @@ -281,8 +277,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (585,1023) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (576.75,896) size 192.25x128 +layer at (200,1024) size 193x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (192.25,897.08) size 192.25x128.16 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 layer at (0,0) size 192x128 @@ -292,8 +288,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (393,1151) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128 +layer at (393,1024) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128.16 layer at (0,0) size 288x128 LayoutView at (0,0) size 288x128 layer at (0,0) size 288x128 @@ -306,8 +302,8 @@ LayoutSVGText {text} at (39,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x24 chunk 1 (middle anchor) text run 1 at (39.44,27.00) startOffset 0 endOffset 1 width 11.12: "S" -layer at (681,1151) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128 +layer at (681,1024) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128.16 layer at (0,0) size 96x128 LayoutView at (0,0) size 96x128 layer at (0,0) size 96x128
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-rightsizing-a-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-rightsizing-a-expected.txt index 32cd242..e5b00c7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-rightsizing-a-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-rightsizing-a-expected.txt
@@ -22,7 +22,7 @@ text run at (0,0) width 690: "Above there must be a GIF- and a SVG-image visible. Both are referenced by an object element (width:100%," text run at (0,16) width 520: "no defined height) and each nested into a div element (width:176px, height:62px)." LayoutBlockFlow {div} at (0,246.77) size 176x62 [bgcolor=#FF0000] - LayoutImage {object} at (0,0) size 176x62 + LayoutImage {object} at (0,0) size 175.98x62 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow (anonymous) at (0,308.77) size 752x18 LayoutBR {br} at (0,0) size 0x17
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.png index 827f5d0..4e2d758 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.txt index 02c6e08..f0beba67 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-hixie-mixed-009-expected.txt
@@ -1,12 +1,12 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x434 - LayoutBlockFlow {html} at (0,0) size 800x434.36 - LayoutBlockFlow {body} at (11.52,14.39) size 776.97x405.58 +layer at (0,0) size 800x435 + LayoutBlockFlow {html} at (0,0) size 800x435.36 + LayoutBlockFlow {body} at (11.52,14.39) size 776.97x406.58 LayoutBlockFlow {p} at (0,0) size 776.97x26 [color=#000080] LayoutText {#text} at (0,0) size 660x26 text run at (0,0) width 660: "The word \"TEST \" should appear twice below, the same size each time." - LayoutBlockFlow (anonymous) at (0,40.39) size 776.97x178 + LayoutBlockFlow (anonymous) at (0,40.39) size 776.97x179 LayoutSVGRoot {svg} at (11,54) size 577x174 LayoutSVGRect {rect} at (11,54) size 577x174 [transform={m=((10.00,0.00)(0.00,10.00)) t=(0.00,0.00)}] [fill={[type=SOLID] [color=#EEEEEE]}] [x=0.00] [y=0.00] [width=60.00] [height=12.00] LayoutSVGForeignObject {foreignObject} at (0,0) size 60x10 @@ -14,6 +14,6 @@ LayoutText {#text} at (0,0) size 24x13 text run at (0,0) width 24: "TEST" LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {div} at (0,232.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] + LayoutBlockFlow {div} at (0,233.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] LayoutText {#text} at (0,0) size 344x166 text run at (0,0) width 344: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png index 52c0bce..381488d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt index 97d187a6..091e761b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt
@@ -1,10 +1,10 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x331 - LayoutBlockFlow {HTML} at (0,0) size 800x331.09 - LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x320 - LayoutTable {TABLE} at (0,0) size 462x320 - LayoutTableSection {TBODY} at (0,0) size 462x320 +layer at (0,0) size 800x339 + LayoutBlockFlow {HTML} at (0,0) size 800x339.09 + LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x328 + LayoutTable {TABLE} at (0,0) size 462x328 + LayoutTableSection {TBODY} at (0,0) size 462x328 LayoutTableRow {TR} at (0,1) size 462x14 LayoutTableCell {TH} at (1,1) size 64x14 [bgcolor=#DDDD99] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (6,0) size 52x14 @@ -18,79 +18,79 @@ LayoutTableCell {TH} at (321,1) size 140x14 [bgcolor=#DDDD99] [r=0 c=3 rs=1 cs=1] LayoutText {#text} at (47,0) size 46x14 text run at (47,0) width 46: "<object>" - LayoutTableRow {TR} at (0,16) size 462x37 - LayoutTableCell {TH} at (1,84) size 64x14 [bgcolor=#DDDD99] [r=1 c=0 rs=4 cs=1] + LayoutTableRow {TR} at (0,16) size 462x38 + LayoutTableCell {TH} at (1,86) size 64x14 [bgcolor=#DDDD99] [r=1 c=0 rs=4 cs=1] LayoutText {#text} at (0,0) size 64x14 text run at (0,0) width 64: "No viewBox" - LayoutTableCell {TH} at (66,34) size 113x0 [bgcolor=#DDDD99] [r=1 c=1 rs=1 cs=1] - LayoutTableCell {TD} at (180,16) size 140x37 [r=1 c=2 rs=1 cs=1] + LayoutTableCell {TH} at (66,35) size 113x0 [bgcolor=#DDDD99] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (180,16) size 140x38 [r=1 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,16) size 140x37 [r=1 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,16) size 140x38 [r=1 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,54) size 462x37 - LayoutTableCell {TH} at (66,65) size 113x14 [bgcolor=#DDDD99] [r=2 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,55) size 462x38 + LayoutTableCell {TH} at (66,67) size 113x14 [bgcolor=#DDDD99] [r=2 c=1 rs=1 cs=1] LayoutText {#text} at (43,0) size 27x14 text run at (43,0) width 27: "none" - LayoutTableCell {TD} at (180,54) size 140x37 [r=2 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (180,55) size 140x38 [r=2 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,54) size 140x37 [r=2 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,55) size 140x38 [r=2 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,92) size 462x37 - LayoutTableCell {TH} at (66,103) size 113x14 [bgcolor=#DDDD99] [r=3 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,94) size 462x38 + LayoutTableCell {TH} at (66,106) size 113x14 [bgcolor=#DDDD99] [r=3 c=1 rs=1 cs=1] LayoutText {#text} at (43,0) size 27x14 text run at (43,0) width 27: "meet" - LayoutTableCell {TD} at (180,92) size 140x37 [r=3 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (180,94) size 140x38 [r=3 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,92) size 140x37 [r=3 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,94) size 140x38 [r=3 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,130) size 462x37 - LayoutTableCell {TH} at (66,141) size 113x14 [bgcolor=#DDDD99] [r=4 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,133) size 462x38 + LayoutTableCell {TH} at (66,145) size 113x14 [bgcolor=#DDDD99] [r=4 c=1 rs=1 cs=1] LayoutText {#text} at (44,0) size 25x14 text run at (44,0) width 25: "slice" - LayoutTableCell {TD} at (180,130) size 140x37 [r=4 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (180,133) size 140x38 [r=4 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,130) size 140x37 [r=4 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,133) size 140x38 [r=4 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,168) size 462x37 - LayoutTableCell {TH} at (1,236) size 64x14 [bgcolor=#DDDD99] [r=5 c=0 rs=4 cs=1] + LayoutTableRow {TR} at (0,172) size 462x38 + LayoutTableCell {TH} at (1,242) size 64x14 [bgcolor=#DDDD99] [r=5 c=0 rs=4 cs=1] LayoutText {#text} at (9,0) size 46x14 text run at (9,0) width 46: "viewBox" - LayoutTableCell {TH} at (66,186) size 113x0 [bgcolor=#DDDD99] [r=5 c=1 rs=1 cs=1] - LayoutTableCell {TD} at (180,168) size 140x37 [r=5 c=2 rs=1 cs=1] + LayoutTableCell {TH} at (66,191) size 113x0 [bgcolor=#DDDD99] [r=5 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (180,172) size 140x38 [r=5 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,168) size 140x37 [r=5 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,172) size 140x38 [r=5 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,206) size 462x37 - LayoutTableCell {TH} at (66,217) size 113x14 [bgcolor=#DDDD99] [r=6 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,211) size 462x38 + LayoutTableCell {TH} at (66,223) size 113x14 [bgcolor=#DDDD99] [r=6 c=1 rs=1 cs=1] LayoutText {#text} at (43,0) size 27x14 text run at (43,0) width 27: "none" - LayoutTableCell {TD} at (180,206) size 140x37 [r=6 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (180,211) size 140x38 [r=6 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,206) size 140x37 [r=6 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,211) size 140x38 [r=6 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,244) size 462x37 - LayoutTableCell {TH} at (66,255) size 113x14 [bgcolor=#DDDD99] [r=7 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,250) size 462x38 + LayoutTableCell {TH} at (66,262) size 113x14 [bgcolor=#DDDD99] [r=7 c=1 rs=1 cs=1] LayoutText {#text} at (43,0) size 27x14 text run at (43,0) width 27: "meet" - LayoutTableCell {TD} at (180,244) size 140x37 [r=7 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (180,250) size 140x38 [r=7 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,244) size 140x37 [r=7 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,250) size 140x38 [r=7 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,282) size 462x37 - LayoutTableCell {TH} at (66,293) size 113x14 [bgcolor=#DDDD99] [r=8 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,289) size 462x38 + LayoutTableCell {TH} at (66,301) size 113x14 [bgcolor=#DDDD99] [r=8 c=1 rs=1 cs=1] LayoutText {#text} at (44,0) size 25x14 text run at (44,0) width 25: "slice" - LayoutTableCell {TD} at (180,282) size 140x37 [r=8 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (180,289) size 140x38 [r=8 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (321,282) size 140x37 [r=8 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (321,289) size 140x38 [r=8 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 layer at (327,22) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] @@ -99,7 +99,7 @@ layer at (0,0) size 133x29 LayoutSVGRoot {svg} at (0,0) size 133x29 LayoutSVGEllipse {circle} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00] -layer at (327,60) size 139x35 +layer at (327,61) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -108,7 +108,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (327,98) size 139x35 +layer at (327,100) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -117,7 +117,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (327,136) size 139x35 +layer at (327,139) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -126,7 +126,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (327,174) size 139x35 +layer at (327,178) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -135,7 +135,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (51,0) size 23x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (51,0) size 23x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (327,212) size 139x35 +layer at (327,217) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -144,7 +144,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 97x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 98x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (327,250) size 139x35 +layer at (327,256) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -153,7 +153,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (51,0) size 23x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (51,0) size 23x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (327,288) size 139x35 +layer at (327,295) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png index e0bfabb5..cf588e1e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt index c868e6a..fdd7f6a6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt
@@ -25,8 +25,8 @@ LayoutTableRow {TR} at (0,0) size 138x45 LayoutTableCell {TD} at (0,0) size 138x45 [r=0 c=0 rs=1 cs=1] LayoutBlockFlow (floating) {P} at (1,459.98) size 138.88x9.25 -layer at (15,134) size 121x44 - LayoutEmbeddedObject {OBJECT} at (9.25,-0.25) size 120.38x44 [bgcolor=#FF0000] [border: (9px solid #0000FF)] +layer at (15,134) size 121x43 + LayoutEmbeddedObject {OBJECT} at (9.25,-0.25) size 120.38x43.59 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x26 LayoutView at (0,0) size 102x26 layer at (0,0) size 102x26 @@ -34,7 +34,7 @@ LayoutSVGRect {rect} at (0,0) size 102x26 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 72x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,199) size 121x44 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x44 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x43.59 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x26 LayoutView at (0,0) size 102x26 layer at (0,0) size 102x26 @@ -42,7 +42,7 @@ LayoutSVGRect {rect} at (0,0) size 102x26 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 72x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,264) size 120x43 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43.38 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x25 LayoutView at (0,0) size 102x25 layer at (0,0) size 102x25 @@ -50,7 +50,7 @@ LayoutSVGRect {rect} at (0,0) size 101x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 70x15 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,364) size 120x43 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43.38 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x25 LayoutView at (0,0) size 102x25 layer at (0,0) size 102x25 @@ -58,7 +58,7 @@ LayoutSVGRect {rect} at (0,0) size 101x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 70x15 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,465) size 121x44 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x44 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x43.59 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x26 LayoutView at (0,0) size 102x26 layer at (0,0) size 102x26
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt index 8265579..09b72ad 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt
@@ -1,10 +1,10 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x172 - LayoutBlockFlow {HTML} at (0,0) size 800x172.09 - LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x161 - LayoutText {#text} at (158,148) size 4x13 - text run at (158,148) width 4: " " +layer at (0,0) size 800x173 + LayoutBlockFlow {HTML} at (0,0) size 800x173.09 + LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x162 + LayoutText {#text} at (158,149) size 4x13 + text run at (158,149) width 4: " " LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png index a271604..eed312bb5 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt index 6e3b644..758b18f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt
@@ -1,14 +1,14 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x350 - LayoutBlockFlow {html} at (0,0) size 800x350.44 - LayoutBlockFlow {body} at (5.55,11.11) size 788.91x328.22 +layer at (0,0) size 800x351 + LayoutBlockFlow {html} at (0,0) size 800x351.44 + LayoutBlockFlow {body} at (5.55,11.11) size 788.91x329.22 LayoutBlockFlow {p} at (0,0) size 788.91x13 LayoutText {#text} at (0,0) size 85x13 text run at (0,0) width 85: "Text above the rect" - LayoutBlockFlow (anonymous) at (0,24.11) size 788.91x280 + LayoutBlockFlow (anonymous) at (0,24.11) size 788.91x281 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {p} at (0,315.22) size 788.91x13 + LayoutBlockFlow {p} at (0,316.22) size 788.91x13 LayoutText {#text} at (0,0) size 86x13 text run at (0,0) width 86: "Text below the rect" layer at (6,35) size 278x278
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt index 4b40075..40e17677 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt
@@ -1,14 +1,14 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x378 - LayoutBlockFlow {html} at (0,0) size 800x378.09 - LayoutBlockFlow {body} at (5.55,5.55) size 788.91x367 - LayoutTable {table} at (41.45,0) size 706x367 - LayoutTableSection (anonymous) at (0,0) size 706x367 - LayoutTableRow {tr} at (0,0) size 706x367 - LayoutTableCell {td} at (0,0) size 706x367 [r=0 c=0 rs=1 cs=3] - LayoutTable {table} at (7.44,6) size 693x355 - LayoutTableSection (anonymous) at (0,0) size 693x355 +layer at (0,0) size 800x379 + LayoutBlockFlow {html} at (0,0) size 800x379.09 + LayoutBlockFlow {body} at (5.55,5.55) size 788.91x368 + LayoutTable {table} at (41.45,0) size 706x368 + LayoutTableSection (anonymous) at (0,0) size 706x368 + LayoutTableRow {tr} at (0,0) size 706x368 + LayoutTableCell {td} at (0,0) size 706x368 [r=0 c=0 rs=1 cs=3] + LayoutTable {table} at (7.44,6) size 693x356 + LayoutTableSection (anonymous) at (0,0) size 693x356 LayoutTableRow {tr} at (0,1) size 693x66 LayoutTableCell {td} at (1,1) size 691x65.75 [r=0 c=0 rs=1 cs=2] LayoutBlockFlow {h1} at (5.55,19.88) size 681x26 @@ -21,10 +21,10 @@ LayoutTableCell {td} at (347,68) size 345x23 [r=1 c=1 rs=1 cs=1] LayoutText {#text} at (146,5) size 53x13 text run at (146,5) width 53: "PNG Image" - LayoutTableRow {tr} at (0,92) size 693x262 - LayoutTableCell {td} at (1,92) size 345x262 [r=2 c=0 rs=1 cs=1] + LayoutTableRow {tr} at (0,92) size 693x263 + LayoutTableCell {td} at (1,92) size 345x263 [r=2 c=0 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {td} at (347,92) size 345x262 [r=2 c=1 rs=1 cs=1] + LayoutTableCell {td} at (347,92) size 345x263 [r=2 c=1 rs=1 cs=1] LayoutImage {img} at (5,5) size 333.33x249.97 LayoutText {#text} at (0,0) size 0x0 layer at (62,109) size 333x250
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.txt index 75b78a24..2a011aee 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug101674-expected.txt
@@ -7,11 +7,11 @@ LayoutTableSection {TBODY} at (1,1) size 767x73 LayoutTableRow {TR} at (0,2) size 767x69 LayoutTableCell {TD} at (2,2) size 444x69 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 - LayoutImage {IMG} at (90,2) size 88x65 - LayoutImage {IMG} at (178,2) size 88x65 - LayoutImage {IMG} at (266,2) size 88x65 - LayoutImage {IMG} at (354,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 + LayoutImage {IMG} at (90,2) size 88x64.53 + LayoutImage {IMG} at (178,2) size 88x64.53 + LayoutImage {IMG} at (266,2) size 88x64.53 + LayoutImage {IMG} at (354,2) size 88x64.53 LayoutTableCell {TD} at (448,25) size 317x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 160x18 text run at (2,2) width 160: "Nothing between images" @@ -19,15 +19,15 @@ LayoutTableSection {TBODY} at (1,1) size 767x333 LayoutTableRow {TR} at (0,2) size 767x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,67) size 88x65 + LayoutImage {IMG} at (2,67) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,132) size 88x65 + LayoutImage {IMG} at (2,132) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,197) size 88x65 + LayoutImage {IMG} at (2,197) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,262) size 88x65 + LayoutImage {IMG} at (2,262) size 88x64.53 LayoutTableCell {TD} at (96,155) size 669x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 152x18 text run at (2,2) width 152: "Spaces between images" @@ -35,15 +35,15 @@ LayoutTableSection {TBODY} at (1,1) size 767x333 LayoutTableRow {TR} at (0,2) size 767x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,67) size 88x65 + LayoutImage {IMG} at (2,67) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,132) size 88x65 + LayoutImage {IMG} at (2,132) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,197) size 88x65 + LayoutImage {IMG} at (2,197) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,262) size 88x65 + LayoutImage {IMG} at (2,262) size 88x64.53 LayoutTableCell {TD} at (96,155) size 669x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 168x18 text run at (2,2) width 168: "Newlines between images" @@ -53,19 +53,19 @@ LayoutTableCell {TD} at (2,2) size 595x73 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,53) size 27x18 text run at (2,53) width 27: "One" - LayoutImage {IMG} at (28.66,2) size 88x65 + LayoutImage {IMG} at (28.66,2) size 88x64.53 LayoutText {#text} at (116,53) size 29x18 text run at (116,53) width 29: "Two" - LayoutImage {IMG} at (144.86,2) size 88x65 + LayoutImage {IMG} at (144.86,2) size 88x64.53 LayoutText {#text} at (232,53) size 39x18 text run at (232,53) width 39: "Three" - LayoutImage {IMG} at (270.16,2) size 88x65 + LayoutImage {IMG} at (270.16,2) size 88x64.53 LayoutText {#text} at (358,53) size 31x18 text run at (358,53) width 31: "Four" - LayoutImage {IMG} at (388.38,2) size 88x65 + LayoutImage {IMG} at (388.38,2) size 88x64.53 LayoutText {#text} at (476,53) size 29x18 text run at (476,53) width 29: "Five" - LayoutImage {IMG} at (504.81,2) size 88x65 + LayoutImage {IMG} at (504.81,2) size 88x64.53 LayoutTableCell {TD} at (599,27) size 166x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 135x18 text run at (2,2) width 135: "Text between images" @@ -75,19 +75,19 @@ LayoutTableCell {TD} at (2,2) size 130x363 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 27x18 text run at (2,2) width 27: "One" - LayoutImage {IMG} at (2,20) size 88x65 + LayoutImage {IMG} at (2,20) size 88x64.53 LayoutText {#text} at (90,71) size 29x18 text run at (90,71) width 29: "Two" - LayoutImage {IMG} at (2,89) size 88x65 + LayoutImage {IMG} at (2,89) size 88x64.53 LayoutText {#text} at (90,140) size 38x18 text run at (90,140) width 38: "Three" - LayoutImage {IMG} at (2,158) size 88x65 + LayoutImage {IMG} at (2,158) size 88x64.53 LayoutText {#text} at (90,209) size 31x18 text run at (90,209) width 31: "Four" - LayoutImage {IMG} at (2,227) size 88x65 + LayoutImage {IMG} at (2,227) size 88x64.53 LayoutText {#text} at (90,278) size 29x18 text run at (90,278) width 29: "Five" - LayoutImage {IMG} at (2,296) size 88x65 + LayoutImage {IMG} at (2,296) size 88x64.53 LayoutTableCell {TD} at (134,172) size 631x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 214x18 text run at (2,2) width 214: "Text with spaces between images" @@ -98,23 +98,23 @@ LayoutText {#text} at (2,2) size 27x87 text run at (2,2) width 27: "One" text run at (2,71) width 12: "A" - LayoutImage {IMG} at (13.55,20) size 88x65 + LayoutImage {IMG} at (13.55,20) size 88x64.53 LayoutText {#text} at (101,71) size 128x87 text run at (101,71) width 29: "Two" text run at (2,140) width 8: "b" - LayoutImage {IMG} at (10,89) size 88x65 + LayoutImage {IMG} at (10,89) size 88x64.53 LayoutText {#text} at (98,140) size 134x87 text run at (98,140) width 38: "Three" text run at (2,209) width 8: "c" - LayoutImage {IMG} at (9.09,158) size 88x65 + LayoutImage {IMG} at (9.09,158) size 88x64.53 LayoutText {#text} at (97,209) size 126x87 text run at (97,209) width 31: "Four" text run at (2,278) width 8: "d" - LayoutImage {IMG} at (10,227) size 88x65 + LayoutImage {IMG} at (10,227) size 88x64.53 LayoutText {#text} at (98,278) size 125x87 text run at (98,278) width 29: "Five" text run at (2,347) width 8: "e" - LayoutImage {IMG} at (9.09,296) size 88x65 + LayoutImage {IMG} at (9.09,296) size 88x64.53 LayoutTableCell {TD} at (142,174) size 623x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 305x18 text run at (2,2) width 305: "Text with spaces and more text between images"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug14929-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug14929-expected.txt index 5541a7b..e0dc8da2 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug14929-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug14929-expected.txt
@@ -28,7 +28,7 @@ LayoutTableSection {TBODY} at (1,1) size 638x50 LayoutTableRow {TR} at (0,2) size 638x22 LayoutTableCell {TD} at (2,2) size 634x22 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] - LayoutImage {IMG} at (2,2) size 190x18 + LayoutImage {IMG} at (2,2) size 190x18.36 LayoutTableRow {TR} at (0,26) size 638x22 LayoutTableCell {TD} at (2,26) size 127x22 [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 58x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug86708-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug86708-expected.txt index e0dd10cc..8c9a9ade 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug86708-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug86708-expected.txt
@@ -8,7 +8,7 @@ LayoutTableRow {TR} at (0,0) size 746x212 LayoutTableCell {TD} at (0,0) size 477x212 [bgcolor=#FF0000] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutInline {A} at (0,0) size 286x18 [color=#0000EE] - LayoutImage {IMG} at (1,1) size 286x210 + LayoutImage {IMG} at (1,1) size 286x209.72 LayoutTableCell {TD} at (477,0) size 269x20 [bgcolor=#FFFF00] [border: (1px inset #808080)] [r=0 c=1 rs=2 cs=1] LayoutText {#text} at (1,1) size 169x18 text run at (1,1) width 169: "ROWSPAN =2 in this cell"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/97619-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/97619-expected.txt index 7172101..f629687 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/97619-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/97619-expected.txt
@@ -12,7 +12,7 @@ text run at (2,20) width 56: "handling" text run at (2,38) width 14: "of" text run at (2,56) width 72: "whitespace" - LayoutImage {IMG} at (2,74) size 20x17 + LayoutImage {IMG} at (2,74) size 20x16.66 LayoutText {#text} at (22,77) size 69x36 text run at (22,77) width 49: " around" text run at (2,95) width 63: "the image" @@ -29,7 +29,7 @@ text run at (2,20) width 16: "bb" text run at (2,38) width 15: "cc" text run at (2,56) width 16: "dd" - LayoutImage {IMG} at (2,74) size 20x17 + LayoutImage {IMG} at (2,74) size 20x16.66 LayoutText {#text} at (2,91) size 16x54 text run at (2,91) width 15: "ee" text run at (2,109) width 11: "ff" @@ -44,7 +44,7 @@ LayoutTableCell {TD} at (2,2) size 217x43 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 213x18 text run at (2,2) width 213: "checkforhandlingofnowhitespace" - LayoutImage {IMG} at (2,20) size 20x17 + LayoutImage {IMG} at (2,20) size 20x16.66 LayoutText {#text} at (22,23) size 108x18 text run at (22,23) width 108: " aroundtheimage" LayoutTableCell {TD} at (221,12) size 559x22 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1] @@ -57,7 +57,7 @@ LayoutTableCell {TD} at (2,2) size 340x25 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,5) size 213x18 text run at (2,5) width 213: "checkforhandlingofnowhitespace" - LayoutImage {IMG} at (214.36,2) size 20x17 + LayoutImage {IMG} at (214.36,2) size 20x16.66 LayoutText {#text} at (234,5) size 104x18 text run at (234,5) width 104: "aroundtheimage" LayoutTableCell {TD} at (344,3) size 436x22 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug85016-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug85016-expected.txt index 6bee3281..f3b5a55e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug85016-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug85016-expected.txt
@@ -12,7 +12,7 @@ text run at (0,0) width 550: "percentage height images in DIV with no height (red) in a DIV with no height (green)" LayoutBlockFlow {DIV} at (32,750) size 672x672 [border: (3px dotted #008000)] LayoutBlockFlow {DIV} at (35,35) size 602x602 [border: (1px solid #FF0000)] - LayoutImage {IMG} at (1,1) size 882x600 + LayoutImage {IMG} at (1,1) size 882.34x600 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1454) size 736x18 LayoutText {#text} at (0,0) size 481x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-2-expected.png index 70b104b..b793216c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index bef14c1e..0d942954 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index c110b94..d4ce8e6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt index 1c8cdee..497ede6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [11, 71, 125, 13], [8, 68, 784, 19], [8, 68, 131, 19], - [8, 54, 11, 11] + [8, 53, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/compositing/video-frame-size-change-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/compositing/video-frame-size-change-expected.txt index 811fce6..fa9d24f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/compositing/video-frame-size-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/compositing/video-frame-size-change-expected.txt
@@ -12,16 +12,16 @@ LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 layer at (8,52) size 320x180 - LayoutVideo {VIDEO} at (0,0) size 320x180 + LayoutVideo {VIDEO} at (0,0) size 320x179.98 layer at (332,52) size 320x180 - LayoutVideo {VIDEO} at (324,0) size 320x180 + LayoutVideo {VIDEO} at (324,0) size 320x179.98 layer at (8,52) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (8,52) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98 layer at (332,52) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (332,52) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css1/box_properties/width-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css1/box_properties/width-expected.txt index 1f197421..2c6f5e9 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css1/box_properties/width-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css1/box_properties/width-expected.txt
@@ -22,7 +22,7 @@ LayoutText {#text} at (0,0) size 271x19 text run at (0,0) width 271: "The square above should be fifty pixels wide." LayoutBlockFlow (anonymous) at (0,209) size 769x385 - LayoutImage {IMG} at (0,0) size 384.50x385 + LayoutImage {IMG} at (0,0) size 384.50x384.50 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,610) size 769x20 LayoutText {#text} at (0,0) size 641x19
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css1/formatting_model/replaced_elements-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css1/formatting_model/replaced_elements-expected.txt index ff77374..bd65a05 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css1/formatting_model/replaced_elements-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css1/formatting_model/replaced_elements-expected.txt
@@ -1,8 +1,8 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2379 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2377 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x2379 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x2379 - LayoutBlockFlow {BODY} at (8,8) size 769x2363 [bgcolor=#CCCCCC] +layer at (0,0) size 785x2377 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x2377 + LayoutBlockFlow {BODY} at (8,8) size 769x2361 [bgcolor=#CCCCCC] LayoutBlockFlow {P} at (0,0) size 769x20 LayoutText {#text} at (0,0) size 337x19 text run at (0,0) width 337: "The style declarations which apply to the text below are:" @@ -37,30 +37,30 @@ LayoutBlockFlow {P} at (0,329) size 769x20 LayoutText {#text} at (0,0) size 383x19 text run at (0,0) width 383: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (192.25,365) size 384.50x385 - LayoutBlockFlow {P} at (0,766) size 769x40 + LayoutImage {IMG} at (192.25,365) size 384.50x384.50 + LayoutBlockFlow {P} at (0,765.50) size 769x40 LayoutText {#text} at (0,0) size 750x39 text run at (0,0) width 750: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally within the" text run at (0,20) width 95: "parent element: " text run at (95,20) width 375: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (384.50,822) size 384.50x385 - LayoutBlockFlow {P} at (0,1223) size 769x40 + LayoutImage {IMG} at (384.50,821.50) size 384.50x384.50 + LayoutBlockFlow {P} at (0,1222) size 769x40 LayoutText {#text} at (0,0) size 765x39 text run at (0,0) width 765: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge of the parent" text run at (0,20) width 53: "element: " text run at (53,20) width 375: "the document body in the first half, and the table in the second." - LayoutTable {TABLE} at (0,1279) size 769x1084 [border: (1px outset #808080)] - LayoutTableSection {TBODY} at (1,1) size 767x1082 + LayoutTable {TABLE} at (0,1278) size 769x1083 [border: (1px outset #808080)] + LayoutTableSection {TBODY} at (1,1) size 767x1081 LayoutTableRow {TR} at (0,0) size 767x28 LayoutTableCell {TD} at (0,0) size 767x28 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] LayoutInline {STRONG} at (0,0) size 159x19 LayoutText {#text} at (4,4) size 159x19 text run at (4,4) width 159: "TABLE Testing Section" - LayoutTableRow {TR} at (0,28) size 767x1054 - LayoutTableCell {TD} at (0,541) size 12x28 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] + LayoutTableRow {TR} at (0,28) size 767x1053 + LayoutTableCell {TD} at (0,540) size 12x28 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (4,4) size 4x19 text run at (4,4) width 4: " " - LayoutTableCell {TD} at (12,28) size 755x1054 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (12,28) size 755x1053 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] LayoutBlockFlow {P} at (4,4) size 747x20 LayoutImage {IMG} at (0,0) size 15x15 LayoutText {#text} at (15,0) size 411x19 @@ -73,14 +73,14 @@ LayoutBlockFlow {P} at (4,138) size 747x20 LayoutText {#text} at (0,0) size 383x19 text run at (0,0) width 383: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (190.75,174) size 373.50x374 - LayoutBlockFlow {P} at (4,564) size 747x40 + LayoutImage {IMG} at (190.75,174) size 373.50x373.50 + LayoutBlockFlow {P} at (4,563.50) size 747x40 LayoutText {#text} at (0,0) size 728x39 text run at (0,0) width 728: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally within" text run at (0,20) width 117: "the parent element: " text run at (117,20) width 375: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (377.50,620) size 373.50x374 - LayoutBlockFlow {P} at (4,1010) size 747x40 + LayoutImage {IMG} at (377.50,619.50) size 373.50x373.50 + LayoutBlockFlow {P} at (4,1009) size 747x40 LayoutText {#text} at (0,0) size 723x39 text run at (0,0) width 723: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge of the" text run at (0,20) width 95: "parent element: "
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css1/text_properties/vertical_align-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css1/text_properties/vertical_align-expected.txt index 8179ff09..b3d0a5f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css1/text_properties/vertical_align-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css1/text_properties/vertical_align-expected.txt
@@ -174,26 +174,26 @@ LayoutImage {IMG} at (44,125) size 6x20 LayoutText {#text} at (50,125) size 96x21 text run at (50,125) width 96: " all of which " - LayoutImage {IMG} at (146,125) size 20x65 - LayoutText {#text} at (166,125) size 5x21 - text run at (166,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 254x42 - LayoutText {#text} at (171,108) size 254x42 - text run at (171,108) width 254: "should be aligned" - LayoutText {#text} at (425,125) size 5x21 - text run at (425,125) width 5: " " - LayoutImage {IMG} at (430,125) size 11x35 - LayoutText {#text} at (441,125) size 119x21 - text run at (441,125) width 119: " with the top of " - LayoutImage {IMG} at (560,125) size 9x30 - LayoutText {#text} at (569,125) size 5x21 - text run at (569,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 686x146 - LayoutText {#text} at (574,115) size 19x33 - text run at (574,115) width 19: "a " - LayoutInline {SPAN} at (0,0) size 686x193 - LayoutText {#text} at (593,78) size 686x193 - text run at (593,78) width 93: "14-" + LayoutImage {IMG} at (146,125) size 19.50x65 + LayoutText {#text} at (165,125) size 6x21 + text run at (165,125) width 6: " " + LayoutInline {SPAN} at (0,0) size 255x42 + LayoutText {#text} at (170,108) size 255x42 + text run at (170,108) width 255: "should be aligned" + LayoutText {#text} at (424,125) size 6x21 + text run at (424,125) width 6: " " + LayoutImage {IMG} at (429.50,125) size 10.50x35 + LayoutText {#text} at (440,125) size 119x21 + text run at (440,125) width 119: " with the top of " + LayoutImage {IMG} at (559,125) size 9x30 + LayoutText {#text} at (568,125) size 5x21 + text run at (568,125) width 5: " " + LayoutInline {SPAN} at (0,0) size 685x146 + LayoutText {#text} at (573,115) size 19x33 + text run at (573,115) width 19: "a " + LayoutInline {SPAN} at (0,0) size 685x193 + LayoutText {#text} at (592,78) size 685x193 + text run at (592,78) width 93: "14-" text run at (0,191) width 142: "point" LayoutText {#text} at (142,228) size 142x33 text run at (142,228) width 142: " text element" @@ -207,15 +207,15 @@ text run at (309,239) width 176: "regardless of the line in which" LayoutText {#text} at (485,238) size 5x21 text run at (485,238) width 5: " " - LayoutImage {IMG} at (490,238) size 5x15 - LayoutText {#text} at (495,238) size 5x21 - text run at (495,238) width 5: " " - LayoutInline {BIG} at (0,0) size 152x23 - LayoutText {#text} at (500,236) size 152x23 - text run at (500,236) width 152: "the images appear." - LayoutText {#text} at (652,238) size 5x21 - text run at (652,238) width 5: " " - LayoutImage {IMG} at (657,238) size 27x90 + LayoutImage {IMG} at (490,238) size 4.50x15 + LayoutText {#text} at (494,238) size 6x21 + text run at (494,238) width 6: " " + LayoutInline {BIG} at (0,0) size 153x23 + LayoutText {#text} at (499,236) size 153x23 + text run at (499,236) width 153: "the images appear." + LayoutText {#text} at (651,238) size 6x21 + text run at (651,238) width 6: " " + LayoutImage {IMG} at (656.50,238) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1829.31) size 769x40 LayoutText {#text} at (0,0) size 751x39 @@ -246,46 +246,46 @@ LayoutImage {IMG} at (666,24) size 6x20 LayoutText {#text} at (672,22) size 76x19 text run at (672,22) width 76: " all of which " - LayoutImage {IMG} at (748,1) size 20x65 + LayoutImage {IMG} at (748,1) size 19.50x65 LayoutText {#text} at (0,106) size 108x19 text run at (0,106) width 108: "should be aligned " - LayoutImage {IMG} at (108,100) size 11x35 - LayoutText {#text} at (119,106) size 4x19 - text run at (119,106) width 4: " " - LayoutInline {SPAN} at (0,0) size 231x36 - LayoutText {#text} at (123,93) size 231x36 - text run at (123,93) width 231: "with the middle of" - LayoutText {#text} at (354,106) size 4x19 - text run at (354,106) width 4: " " - LayoutImage {IMG} at (358,93) size 15x50 - LayoutText {#text} at (373,106) size 4x19 - text run at (373,106) width 4: " " - LayoutInline {SPAN} at (0,0) size 345x27 - LayoutText {#text} at (377,100) size 17x27 - text run at (377,100) width 17: "a " - LayoutInline {SPAN} at (0,0) size 203x68 - LayoutText {#text} at (394,67) size 203x68 - text run at (394,67) width 203: "14-point" - LayoutText {#text} at (597,100) size 125x27 - text run at (597,100) width 125: " text element" - LayoutText {#text} at (722,106) size 4x19 - text run at (722,106) width 4: " " - LayoutImage {IMG} at (726,93) size 15x50 + LayoutImage {IMG} at (108,100) size 10.50x35 + LayoutText {#text} at (118,106) size 5x19 + text run at (118,106) width 5: " " + LayoutInline {SPAN} at (0,0) size 232x36 + LayoutText {#text} at (122,93) size 232x36 + text run at (122,93) width 232: "with the middle of" + LayoutText {#text} at (353,106) size 5x19 + text run at (353,106) width 5: " " + LayoutImage {IMG} at (357.50,93) size 15x50 + LayoutText {#text} at (372,106) size 5x19 + text run at (372,106) width 5: " " + LayoutInline {SPAN} at (0,0) size 346x27 + LayoutText {#text} at (376,100) size 18x27 + text run at (376,100) width 18: "a " + LayoutInline {SPAN} at (0,0) size 204x68 + LayoutText {#text} at (393,67) size 204x68 + text run at (393,67) width 204: "14-point" + LayoutText {#text} at (596,100) size 126x27 + text run at (596,100) width 126: " text element" + LayoutText {#text} at (721,106) size 5x19 + text run at (721,106) width 5: " " + LayoutImage {IMG} at (725.50,93) size 15x50 LayoutText {#text} at (0,0) size 0x0 LayoutInline {SMALL} at (0,0) size 176x19 LayoutText {#text} at (0,176) size 176x19 text run at (0,176) width 176: "regardless of the line in which" LayoutText {#text} at (176,176) size 4x19 text run at (176,176) width 4: " " - LayoutImage {IMG} at (180,180) size 5x15 - LayoutText {#text} at (185,176) size 4x19 - text run at (185,176) width 4: " " - LayoutInline {BIG} at (0,0) size 152x23 - LayoutText {#text} at (189,173) size 152x23 - text run at (189,173) width 152: "the images appear." - LayoutText {#text} at (341,176) size 4x19 - text run at (341,176) width 4: " " - LayoutImage {IMG} at (345,143) size 27x90 + LayoutImage {IMG} at (180,180) size 4.50x15 + LayoutText {#text} at (184,176) size 5x19 + text run at (184,176) width 5: " " + LayoutInline {BIG} at (0,0) size 153x23 + LayoutText {#text} at (188,173) size 153x23 + text run at (188,173) width 153: "the images appear." + LayoutText {#text} at (340,176) size 5x19 + text run at (340,176) width 5: " " + LayoutImage {IMG} at (344.50,143) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,2134.31) size 769x40 LayoutText {#text} at (0,0) size 753x39 @@ -338,36 +338,36 @@ text run at (10,48) width 68: "all of which" LayoutText {#text} at (78,48) size 4x19 text run at (78,48) width 4: " " - LayoutImage {IMG} at (82,48) size 20x65 - LayoutText {#text} at (102,48) size 4x19 - text run at (102,48) width 4: " " - LayoutInline {SPAN} at (0,0) size 104x19 - LayoutText {#text} at (106,48) size 104x19 - text run at (106,48) width 104: "should be aligned" - LayoutText {#text} at (210,48) size 4x19 - text run at (210,48) width 4: " " - LayoutImage {IMG} at (214,48) size 11x35 - LayoutText {#text} at (225,48) size 4x19 - text run at (225,48) width 4: " " + LayoutImage {IMG} at (82,48) size 19.50x65 + LayoutText {#text} at (101,48) size 5x19 + text run at (101,48) width 5: " " + LayoutInline {SPAN} at (0,0) size 105x19 + LayoutText {#text} at (105,48) size 105x19 + text run at (105,48) width 105: "should be aligned" + LayoutText {#text} at (209,48) size 5x19 + text run at (209,48) width 5: " " + LayoutImage {IMG} at (213.50,48) size 10.50x35 + LayoutText {#text} at (224,48) size 4x19 + text run at (224,48) width 4: " " LayoutInline {SPAN} at (0,0) size 185x36 - LayoutText {#text} at (229,48) size 185x36 - text run at (229,48) width 185: "with the top of" - LayoutText {#text} at (414,48) size 4x19 - text run at (414,48) width 4: " " - LayoutImage {IMG} at (418,48) size 15x50 - LayoutText {#text} at (433,48) size 4x19 - text run at (433,48) width 4: " " + LayoutText {#text} at (228,48) size 185x36 + text run at (228,48) width 185: "with the top of" + LayoutText {#text} at (413,48) size 4x19 + text run at (413,48) width 4: " " + LayoutImage {IMG} at (417,48) size 15x50 + LayoutText {#text} at (432,48) size 4x19 + text run at (432,48) width 4: " " LayoutInline {SPAN} at (0,0) size 120x19 - LayoutText {#text} at (437,48) size 120x19 - text run at (437,48) width 120: "the tallest element in" - LayoutText {#text} at (557,48) size 4x19 - text run at (557,48) width 4: " " - LayoutImage {IMG} at (561,48) size 5x15 - LayoutText {#text} at (566,48) size 4x19 - text run at (566,48) width 4: " " - LayoutInline {BIG} at (0,0) size 721x88 - LayoutText {#text} at (570,48) size 721x88 - text run at (570,48) width 151: "whichever line the" + LayoutText {#text} at (436,48) size 120x19 + text run at (436,48) width 120: "the tallest element in" + LayoutText {#text} at (556,48) size 4x19 + text run at (556,48) width 4: " " + LayoutImage {IMG} at (560,48) size 4.50x15 + LayoutText {#text} at (564,48) size 5x19 + text run at (564,48) width 5: " " + LayoutInline {BIG} at (0,0) size 720x88 + LayoutText {#text} at (568,48) size 720x88 + text run at (568,48) width 152: "whichever line the" text run at (0,113) width 137: "elements appear." LayoutText {#text} at (137,113) size 4x19 text run at (137,113) width 4: " " @@ -514,26 +514,26 @@ LayoutImage {IMG} at (70,125) size 6x20 LayoutText {#text} at (76,125) size 96x21 text run at (76,125) width 96: " all of which " - LayoutImage {IMG} at (172,125) size 20x65 - LayoutText {#text} at (192,125) size 5x21 - text run at (192,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 254x42 - LayoutText {#text} at (197,108) size 254x42 - text run at (197,108) width 254: "should be aligned" - LayoutText {#text} at (451,125) size 5x21 - text run at (451,125) width 5: " " - LayoutImage {IMG} at (456,125) size 11x35 - LayoutText {#text} at (467,125) size 119x21 - text run at (467,125) width 119: " with the top of " - LayoutImage {IMG} at (586,125) size 9x30 - LayoutText {#text} at (595,125) size 5x21 - text run at (595,125) width 5: " " - LayoutInline {SPAN} at (0,0) size 712x146 - LayoutText {#text} at (600,115) size 19x33 - text run at (600,115) width 19: "a " - LayoutInline {SPAN} at (0,0) size 712x193 - LayoutText {#text} at (619,78) size 712x193 - text run at (619,78) width 93: "14-" + LayoutImage {IMG} at (172,125) size 19.50x65 + LayoutText {#text} at (191,125) size 6x21 + text run at (191,125) width 6: " " + LayoutInline {SPAN} at (0,0) size 255x42 + LayoutText {#text} at (196,108) size 255x42 + text run at (196,108) width 255: "should be aligned" + LayoutText {#text} at (450,125) size 6x21 + text run at (450,125) width 6: " " + LayoutImage {IMG} at (455.50,125) size 10.50x35 + LayoutText {#text} at (466,125) size 119x21 + text run at (466,125) width 119: " with the top of " + LayoutImage {IMG} at (585,125) size 9x30 + LayoutText {#text} at (594,125) size 5x21 + text run at (594,125) width 5: " " + LayoutInline {SPAN} at (0,0) size 711x146 + LayoutText {#text} at (599,115) size 19x33 + text run at (599,115) width 19: "a " + LayoutInline {SPAN} at (0,0) size 711x193 + LayoutText {#text} at (618,78) size 711x193 + text run at (618,78) width 93: "14-" text run at (0,191) width 142: "point" LayoutText {#text} at (142,228) size 142x33 text run at (142,228) width 142: " text element" @@ -547,15 +547,15 @@ text run at (309,239) width 176: "regardless of the line in which" LayoutText {#text} at (485,238) size 5x21 text run at (485,238) width 5: " " - LayoutImage {IMG} at (490,238) size 5x15 - LayoutText {#text} at (495,238) size 5x21 - text run at (495,238) width 5: " " - LayoutInline {BIG} at (0,0) size 152x23 - LayoutText {#text} at (500,236) size 152x23 - text run at (500,236) width 152: "the images appear." - LayoutText {#text} at (652,238) size 5x21 - text run at (652,238) width 5: " " - LayoutImage {IMG} at (657,238) size 27x90 + LayoutImage {IMG} at (490,238) size 4.50x15 + LayoutText {#text} at (494,238) size 6x21 + text run at (494,238) width 6: " " + LayoutInline {BIG} at (0,0) size 153x23 + LayoutText {#text} at (499,236) size 153x23 + text run at (499,236) width 153: "the images appear." + LayoutText {#text} at (651,238) size 6x21 + text run at (651,238) width 6: " " + LayoutImage {IMG} at (656.50,238) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1478.31) size 747x40 LayoutText {#text} at (0,0) size 705x39 @@ -586,28 +586,28 @@ LayoutImage {IMG} at (666,24) size 6x20 LayoutText {#text} at (672,22) size 72x19 text run at (672,22) width 72: " all of which" - LayoutImage {IMG} at (0,78) size 20x65 - LayoutText {#text} at (20,99) size 112x19 - text run at (20,99) width 112: " should be aligned " - LayoutImage {IMG} at (132,93) size 11x35 - LayoutText {#text} at (143,99) size 4x19 - text run at (143,99) width 4: " " + LayoutImage {IMG} at (0,78) size 19.50x65 + LayoutText {#text} at (19,99) size 113x19 + text run at (19,99) width 113: " should be aligned " + LayoutImage {IMG} at (131.50,93) size 10.50x35 + LayoutText {#text} at (142,99) size 4x19 + text run at (142,99) width 4: " " LayoutInline {SPAN} at (0,0) size 231x36 - LayoutText {#text} at (147,86) size 231x36 - text run at (147,86) width 231: "with the middle of" - LayoutText {#text} at (378,99) size 4x19 - text run at (378,99) width 4: " " - LayoutImage {IMG} at (382,86) size 15x50 - LayoutText {#text} at (397,99) size 4x19 - text run at (397,99) width 4: " " + LayoutText {#text} at (146,86) size 231x36 + text run at (146,86) width 231: "with the middle of" + LayoutText {#text} at (377,99) size 4x19 + text run at (377,99) width 4: " " + LayoutImage {IMG} at (381,86) size 15x50 + LayoutText {#text} at (396,99) size 4x19 + text run at (396,99) width 4: " " LayoutInline {SPAN} at (0,0) size 345x27 - LayoutText {#text} at (401,93) size 17x27 - text run at (401,93) width 17: "a " + LayoutText {#text} at (400,93) size 17x27 + text run at (400,93) width 17: "a " LayoutInline {SPAN} at (0,0) size 203x68 - LayoutText {#text} at (418,60) size 203x68 - text run at (418,60) width 203: "14-point" - LayoutText {#text} at (621,93) size 125x27 - text run at (621,93) width 125: " text element" + LayoutText {#text} at (417,60) size 203x68 + text run at (417,60) width 203: "14-point" + LayoutText {#text} at (620,93) size 125x27 + text run at (620,93) width 125: " text element" LayoutText {#text} at (0,0) size 0x0 LayoutImage {IMG} at (0,163) size 15x50 LayoutText {#text} at (15,176) size 4x19 @@ -617,15 +617,15 @@ text run at (19,176) width 176: "regardless of the line in which" LayoutText {#text} at (195,176) size 4x19 text run at (195,176) width 4: " " - LayoutImage {IMG} at (199,180) size 5x15 - LayoutText {#text} at (204,176) size 4x19 - text run at (204,176) width 4: " " - LayoutInline {BIG} at (0,0) size 152x23 - LayoutText {#text} at (208,173) size 152x23 - text run at (208,173) width 152: "the images appear." - LayoutText {#text} at (360,176) size 4x19 - text run at (360,176) width 4: " " - LayoutImage {IMG} at (364,143) size 27x90 + LayoutImage {IMG} at (199,180) size 4.50x15 + LayoutText {#text} at (203,176) size 5x19 + text run at (203,176) width 5: " " + LayoutInline {BIG} at (0,0) size 153x23 + LayoutText {#text} at (207,173) size 153x23 + text run at (207,173) width 153: "the images appear." + LayoutText {#text} at (359,176) size 5x19 + text run at (359,176) width 5: " " + LayoutImage {IMG} at (363.50,143) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1783.31) size 747x40 LayoutText {#text} at (0,0) size 740x39 @@ -679,36 +679,36 @@ text run at (69,48) width 68: "all of which" LayoutText {#text} at (137,48) size 4x19 text run at (137,48) width 4: " " - LayoutImage {IMG} at (141,48) size 20x65 - LayoutText {#text} at (161,48) size 4x19 - text run at (161,48) width 4: " " - LayoutInline {SPAN} at (0,0) size 104x19 - LayoutText {#text} at (165,48) size 104x19 - text run at (165,48) width 104: "should be aligned" - LayoutText {#text} at (269,48) size 4x19 - text run at (269,48) width 4: " " - LayoutImage {IMG} at (273,48) size 11x35 - LayoutText {#text} at (284,48) size 4x19 - text run at (284,48) width 4: " " + LayoutImage {IMG} at (141,48) size 19.50x65 + LayoutText {#text} at (160,48) size 5x19 + text run at (160,48) width 5: " " + LayoutInline {SPAN} at (0,0) size 105x19 + LayoutText {#text} at (164,48) size 105x19 + text run at (164,48) width 105: "should be aligned" + LayoutText {#text} at (268,48) size 5x19 + text run at (268,48) width 5: " " + LayoutImage {IMG} at (272.50,48) size 10.50x35 + LayoutText {#text} at (283,48) size 4x19 + text run at (283,48) width 4: " " LayoutInline {SPAN} at (0,0) size 185x36 - LayoutText {#text} at (288,48) size 185x36 - text run at (288,48) width 185: "with the top of" - LayoutText {#text} at (473,48) size 4x19 - text run at (473,48) width 4: " " - LayoutImage {IMG} at (477,48) size 15x50 - LayoutText {#text} at (492,48) size 4x19 - text run at (492,48) width 4: " " + LayoutText {#text} at (287,48) size 185x36 + text run at (287,48) width 185: "with the top of" + LayoutText {#text} at (472,48) size 4x19 + text run at (472,48) width 4: " " + LayoutImage {IMG} at (476,48) size 15x50 + LayoutText {#text} at (491,48) size 4x19 + text run at (491,48) width 4: " " LayoutInline {SPAN} at (0,0) size 120x19 - LayoutText {#text} at (496,48) size 120x19 - text run at (496,48) width 120: "the tallest element in" - LayoutText {#text} at (616,48) size 4x19 - text run at (616,48) width 4: " " - LayoutImage {IMG} at (620,48) size 5x15 - LayoutText {#text} at (625,48) size 4x19 - text run at (625,48) width 4: " " - LayoutInline {BIG} at (0,0) size 714x88 - LayoutText {#text} at (629,48) size 714x88 - text run at (629,48) width 85: "whichever" + LayoutText {#text} at (495,48) size 120x19 + text run at (495,48) width 120: "the tallest element in" + LayoutText {#text} at (615,48) size 4x19 + text run at (615,48) width 4: " " + LayoutImage {IMG} at (619,48) size 4.50x15 + LayoutText {#text} at (623,48) size 5x19 + text run at (623,48) width 5: " " + LayoutInline {BIG} at (0,0) size 713x88 + LayoutText {#text} at (627,48) size 713x88 + text run at (627,48) width 86: "whichever" text run at (0,113) width 203: "line the elements appear." LayoutText {#text} at (203,113) size 4x19 text run at (203,113) width 4: " "
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/floating-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/floating-replaced-height-008-expected.txt index 0d3b802..f44dd43 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/floating-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/floating-replaced-height-008-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x190 - LayoutBlockFlow {HTML} at (0,0) size 800x190 + LayoutBlockFlow {HTML} at (0,0) size 800x189.59 LayoutBlockFlow {BODY} at (8,16) size 784x40 LayoutBlockFlow {P} at (0,0) size 784x40 LayoutText {#text} at (0,0) size 101x19 @@ -22,12 +22,12 @@ LayoutText {#text} at (0,20) size 417x19 text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,56) size 784x0 - LayoutImage (floating) {IMG} at (0,0) size 117.59x118 + LayoutImage (floating) {IMG} at (0,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (117.59,0) size 5x5 - LayoutImage (floating) {IMG} at (122.59,0) size 117.59x118 + LayoutImage (floating) {IMG} at (122.59,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (240.19,0) size 5x5 - LayoutImage (floating) {IMG} at (245.19,0) size 117.59x118 + LayoutImage (floating) {IMG} at (245.19,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (362.78,0) size 5x5 - LayoutImage (floating) {IMG} at (367.78,0) size 117.59x118 + LayoutImage (floating) {IMG} at (367.78,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (485.38,0) size 5x5 - LayoutImage (floating) {IMG} at (490.38,0) size 117.59x118 + LayoutImage (floating) {IMG} at (490.38,0) size 117.59x117.59
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-block-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-block-replaced-height-008-expected.txt index 0da43cb..fe17752 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-block-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-block-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (0,20) size 417x19 text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,56) size 784x123 - LayoutImage {OBJECT} at (0,0) size 117.59x118 + LayoutImage {OBJECT} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,103) size 5x19 text run at (117,103) width 5: " " - LayoutImage {OBJECT} at (121.59,0) size 117.59x118 + LayoutImage {OBJECT} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,103) size 5x19 text run at (239,103) width 5: " " - LayoutImage {OBJECT} at (243.19,0) size 117.59x118 + LayoutImage {OBJECT} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,103) size 5x19 text run at (360,103) width 5: " " - LayoutImage {OBJECT} at (364.78,0) size 117.59x118 + LayoutImage {OBJECT} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,103) size 5x19 text run at (482,103) width 5: " " - LayoutImage {OBJECT} at (486.38,0) size 117.59x118 + LayoutImage {OBJECT} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-replaced-height-008-expected.txt index a1173869..ec25e8a6 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/20110323/inline-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (0,20) size 417x19 text run at (0,20) width 417: "to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,56) size 784x123 - LayoutImage {IMG} at (0,0) size 117.59x118 + LayoutImage {IMG} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,103) size 5x19 text run at (117,103) width 5: " " - LayoutImage {IMG} at (121.59,0) size 117.59x118 + LayoutImage {IMG} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,103) size 5x19 text run at (239,103) width 5: " " - LayoutImage {IMG} at (243.19,0) size 117.59x118 + LayoutImage {IMG} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,103) size 5x19 text run at (360,103) width 5: " " - LayoutImage {IMG} at (364.78,0) size 117.59x118 + LayoutImage {IMG} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,103) size 5x19 text run at (482,103) width 5: " " - LayoutImage {IMG} at (486.38,0) size 117.59x118 + LayoutImage {IMG} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt index 373f95c..a0ed58b 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt
@@ -10,6 +10,6 @@ LayoutText {#text} at (32,0) size 36x19 text run at (32,0) width 36: "TEST" LayoutBlockFlow {DIV} at (16,72) size 192x20 [color=#FFFF00] [bgcolor=#000080] - LayoutImage {IMG} at (0,14) size 32x1.59 + LayoutImage {IMG} at (0,13) size 32x1.59 LayoutText {#text} at (32,0) size 36x19 text run at (32,0) width 36: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t090501-c414-flt-03-b-g-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t090501-c414-flt-03-b-g-expected.txt index 13e989ea..0be948c 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t090501-c414-flt-03-b-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/css2.1/t090501-c414-flt-03-b-g-expected.txt
@@ -7,13 +7,13 @@ LayoutText {#text} at (0,2) size 378x19 text run at (0,2) width 233: "In the following test, the purple square " text run at (233,2) width 145: "should be on the left (\x{21E6}" - LayoutImage {IMG} at (378,2) size 19x19.19 - LayoutText {#text} at (397,2) size 206x19 - text run at (397,2) width 206: "), and the teal square on the right (" - LayoutImage {IMG} at (603,2) size 19x19.19 - LayoutText {#text} at (622,2) size 149x19 - text run at (622,2) width 91: "\x{21E8}) of the blue " - text run at (713,2) width 58: "rectangle." + LayoutImage {IMG} at (378,2) size 19.19x19.19 + LayoutText {#text} at (397,2) size 207x19 + text run at (397,2) width 207: "), and the teal square on the right (" + LayoutImage {IMG} at (603.19,2) size 19.19x19.19 + LayoutText {#text} at (622,2) size 150x19 + text run at (622,2) width 92: "\x{21E8}) of the blue " + text run at (713,2) width 59: "rectangle." LayoutBlockFlow {DIV} at (0,38) size 784x76 LayoutBlockFlow {DIV} at (16,0) size 752x76 [color=#0000FF] [bgcolor=#000080] LayoutImage (floating) {IMG} at (8,8) size 160x160
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index bb659cf..8cf3ff1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.png index 25d5e94..5ddc0d5 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.txt index 1aaa98a..79e53a74 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/list-item-gradient-expected.txt
@@ -5,14 +5,14 @@ LayoutBlockFlow {BODY} at (8,8) size 784x576 LayoutBlockFlow {UL} at (0,0) size 784x60 LayoutListItem {LI} at (40,0) size 744x20 - LayoutListMarker (anonymous) at (-14.50,8) size 7.50x7.50 + LayoutListMarker (anonymous) at (-14.50,7) size 7.50x7.50 LayoutText {#text} at (0,0) size 57x19 text run at (0,0) width 57: "Item One" LayoutListItem {LI} at (40,20) size 744x20 - LayoutListMarker (anonymous) at (-14.50,8) size 7.50x7.50 + LayoutListMarker (anonymous) at (-14.50,7) size 7.50x7.50 LayoutText {#text} at (0,0) size 59x19 text run at (0,0) width 59: "Item Two" LayoutListItem {LI} at (40,40) size 744x20 - LayoutListMarker (anonymous) at (-14.50,8) size 7.50x7.50 + LayoutListMarker (anonymous) at (-14.50,7) size 7.50x7.50 LayoutText {#text} at (0,0) size 66x19 text run at (0,0) width 66: "Item Three"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/unprefixed-list-item-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/unprefixed-list-item-gradient-expected.png index ec484036..70f83af 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/unprefixed-list-item-gradient-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/gradients/unprefixed-list-item-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.png index 56e4ae1..633e771 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.txt index d97605e..a6dcf45 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.png index 56e4ae1..633e771 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.txt index 014f73f..edd1db2 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.png index 56e4ae1..633e771 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.txt index d97605e..a6dcf45 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.png index 56e4ae1..633e771 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.txt index d8e4bd45e..b513846 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-details-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.png index 8835d28..4816b1f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.txt index 947f9ba0..bc2c7db 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.png index 453b05433..806c1c2 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.txt index fef6197a..0fef6534 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.png index 585f412e..eaff59e 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.txt index e9c20549..5463aad 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.png index 141c3fc..91a7fdc 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.txt index 0d18691..01273e2 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-10-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.png index 9208a50a..585bed2 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.txt index 7859698f..1017e2f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.png index 453b05433..806c1c2 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.txt index fef6197a..0fef6534 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.png index 0642e23..db4fc27 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.txt index fe8063b..e271c1fd 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.png index 6f06375..d8a2673 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.txt index 470e6cd..0890fa53 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.png index 03fab5c4..c3117e7 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.txt index b9d56d8..90b81a5 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.png index 15ba6de0..98bc9f1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-4-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.png index 244e745f..d750f766 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.txt index 063bafd..9d2f00c 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x40 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 800x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.png index 453b05433..806c1c2 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.txt index fef6197a..0fef6534 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.png index 585f412e..eaff59e 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.txt index d55cccb..a842f6ec6 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.png index d27eb84..276d5cb8 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.txt index 6f310fcf..d61aef54 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.png index 585f412e..eaff59e 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.txt index d55cccb..a842f6ec6 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.png index 06fad58b..16bff19 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.txt index 5367ff0e..cd575c5 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-7-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 1" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.png index aad5f35f..b1a2b74 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.txt index c393bc0b..5d675c9 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.png index 022d202..9858c37 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.txt index 811af93..ef6e8cb 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-8-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 38x19 text run at (16,0) width 38: "new 2" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.png index 9184369..27b7f41e 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.txt index e85d5694..febd913 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.png index d605c07..dac8496 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.txt index 5449c51..f62365c 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-9-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.png index b3c7dad3..a180d2e1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.txt index 5486f4c..5da04e8 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutInline {B} at (0,0) size 141x19
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.png index b3c7dad3..a180d2e1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.txt index 652e921..0db91fd 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-add-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutInline {SPAN} at (0,0) size 141x19
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.png index e6b8eda..cbfc9c8 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.txt index 36c7cb7..05e2753 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-marker-style-expected.txt
@@ -1,27 +1,27 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x470 - LayoutBlockFlow {HTML} at (0,0) size 800x470.05 - LayoutBlockFlow {BODY} at (8,8) size 784x454.05 - LayoutBlockFlow {DIV} at (0,0) size 784x86 - LayoutBlockFlow {DETAILS} at (0,0) size 784x86 - LayoutBlockFlow {SUMMARY} at (0,0) size 784x86 +layer at (0,0) size 800x471 + LayoutBlockFlow {HTML} at (0,0) size 800x471.05 + LayoutBlockFlow {BODY} at (8,8) size 784x455.05 + LayoutBlockFlow {DIV} at (0,0) size 784x87 + LayoutBlockFlow {DETAILS} at (0,0) size 784x87 + LayoutBlockFlow {SUMMARY} at (0,0) size 784x87 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: right - LayoutText {#text} at (121,58) size 92x27 - text run at (121,58) width 92: "Summary" - LayoutBlockFlow {DIV} at (0,86) size 118x180.44 - LayoutBlockFlow {DETAILS} at (0,0) size 118x180.44 - LayoutBlockFlow {SUMMARY} at (0,0) size 118x180.44 + LayoutText {#text} at (121,59) size 92x27 + text run at (121,59) width 92: "Summary" + LayoutBlockFlow {DIV} at (0,87) size 119x180.44 + LayoutBlockFlow {DETAILS} at (0,0) size 119x180.44 + LayoutBlockFlow {SUMMARY} at (0,0) size 119x180.44 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: down - LayoutText {#text} at (90,89) size 27x92 - text run at (90,89) width 91: "Summary" - LayoutBlockFlow {DIV} at (0,266.44) size 784x47 + LayoutText {#text} at (91,89) size 27x92 + text run at (91,89) width 91: "Summary" + LayoutBlockFlow {DIV} at (0,267.44) size 784x47 LayoutBlockFlow {DETAILS} at (0,0) size 784x47 LayoutBlockFlow {SUMMARY} at (0,0) size 784x47 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: right LayoutText {#text} at (73,19) size 92x27 text run at (73,19) width 92: "Summary" - LayoutBlockFlow {DIV} at (0,313.44) size 71x140.61 + LayoutBlockFlow {DIV} at (0,314.44) size 71x140.61 LayoutBlockFlow {DETAILS} at (0,0) size 71x140.61 LayoutBlockFlow {SUMMARY} at (0,0) size 71x140.61 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: down
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.png index d6b99daa..d99bf91 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.txt index 7a3b372..e556859 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-1-expected.txt
@@ -6,13 +6,13 @@ LayoutBlockFlow {DETAILS} at (0,0) size 784x144 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x108 [border: (8px solid #9999CC)] LayoutBlockFlow (anonymous) at (8,8) size 752x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 5: " " text run at (20,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (8,28) size 752x72 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 736x36 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,13) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down LayoutText {#text} at (24,8) size 270x19 text run at (24,8) width 5: " " text run at (28,8) width 266: "nested summary (summary-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.png index 2f806c2..1de8a72 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.txt index ee6d410..d5eb49b 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-nested-2-expected.txt
@@ -5,14 +5,14 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x144 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x36 [border: (8px solid #9999CC)] - LayoutDetailsMarker {DIV} at (8,13) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down LayoutText {#text} at (24,8) size 59x19 text run at (24,8) width 5: " " text run at (28,8) width 55: "summary" LayoutBlockFlow {DIV} at (8,44) size 768x92 LayoutBlockFlow {DETAILS} at (0,0) size 768x72 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 752x36 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,13) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down LayoutText {#text} at (24,8) size 254x19 text run at (24,8) width 5: " " text run at (28,8) width 250: "nested summary (details-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.png index 10359d5b..a4c4fd1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.txt index ebae16a..db4b99f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.png index a052ac3a..c733052 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.txt index d83bb910..074f8ab 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.png index 10359d5b..a4c4fd1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.txt index ebae16a..db4b99f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.png index 8d9f46c..1fd4143 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.txt index 7fe7f923..c78a1a1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-no-summary4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.png index 543030c..297cdeaf 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.txt index d9911dc5..57a1d2e6 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open-javascript-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x19 text run at (16,0) width 47: "details1" LayoutBlockFlow {DIV} at (0,20) size 784x22 @@ -13,7 +13,7 @@ LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {DETAILS} at (0,42) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x19 text run at (16,0) width 47: "details2" layer at (10,31) size 149x16
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.png index 15ba6de0..98bc9f1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.png index 2c372fa6..08a1e5d 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.txt index 50d2c59..bc943c7 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.png index 15ba6de0..98bc9f1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.png index 2c372fa6..08a1e5d 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.txt index 1cb9f42..49c4da65 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x42 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.png index 15ba6de0..98bc9f1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.txt index b7a93d4..00262037 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.png index 1e1349e9..a7d4c0a8 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.txt index dde239e..82add38 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-open6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.png index c5e0c6f..0e9b5c6 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.txt index 80eafd73..b6ce9cf 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-position-expected.txt
@@ -8,16 +8,16 @@ LayoutBlockFlow {DETAILS} at (0,20) size 784x0 layer at (50,150) size 46x20 LayoutBlockFlow (positioned) {SUMMARY} at (50,150) size 45.95x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 30x19 text run at (16,0) width 30: "fixed" layer at (158,158) size 784x20 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutBlockFlow (relative positioned) {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 44x19 text run at (16,0) width 44: "relative" layer at (250,150) size 67x20 LayoutBlockFlow (positioned) {SUMMARY} at (250,150) size 66.95x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 51x19 text run at (16,0) width 51: "absolute"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.png index 6ab80a0..3eb6f46 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.txt index 62cbf8cb..16b0a83f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.png index 6ab80a0..3eb6f46 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.txt index d1dcdbe..293395c 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.png index fb90e6f..a08ce486 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.txt index 670103c..9ce6057 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.png index 10359d5b..a4c4fd1 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.txt index ebae16a..db4b99f 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.png index bf3349d..10657f4 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.txt index fb59ecc1..06a7c485 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.png index c5c5eef..421219c 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.txt index 143c300..668d75d8 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.png index d5515aa..1811984 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.txt index 9617cbbf..facbfd6 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1" LayoutBlockFlow {DIV} at (0,20) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.png index 67564c06..51cf637 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.txt index 107ac8c..4a5efdf 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.png index 4fb5f0fb..e1e89da 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.txt index ca3b0d98..15d0c49d 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 0 {CONTENT} of {#document-fragment} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.png index a052ac3a..c733052 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.txt index d83bb910..074f8ab 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 42x19 text run at (16,0) width 42: "Details" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.png index 2592f4e5..6071f401 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.txt index 120480d..851d781a 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 2 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.png index 9c036f3..bf5fe47 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.txt index 4da4d648..7587091 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-5-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 2" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.png index 1935b0d..3f1baca3 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.txt index b8fc494e..74b923e 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x20 LayoutBlockFlow {SUMMARY} at (0,0) size 800x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.png index 17580f9..8696da0e 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.txt index b1e5b221..a4d5bfa 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 67x19 text run at (16,0) width 67: "summary 1" LayoutBlockFlow {DIV} at (0,20) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.png index b1e1e9a..dad38bd0 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.txt index 77c7bd7..48eded2c9 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutText {#text} at (74,0) size 176x19
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.png index 4c25f6f..f7229f04 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.txt index 6b056d6d..793fd09 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-remove-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 59x19 text run at (16,0) width 59: "summary " LayoutInline {SPAN} at (0,0) size 172x19
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.png index 0b2fcf3d..e13f8ac 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.txt index 39d4ebe1..03fb0342 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-summary-child-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x20 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 5x19 text run at (16,0) width 5: " " LayoutBlockFlow {SPAN} at (20.94,3) size 64x16
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.png index ae3b90b..67add19 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.txt index 2c86397..b02c1eaa 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-replace-text-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 58x19 text run at (16,0) width 58: "Summary" LayoutBlockFlow {DIV} at (0,20) size 784x20
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.png index 454c3c8..cf07d82 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.txt index c06291f..d5de2c4 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-center-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (24.53,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (24.53,4) size 10.55x10.55: right LayoutText {#text} at (41,0) size 55x19 text run at (41,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (24.53,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (24.53,4) size 10.55x10.55: down LayoutText {#text} at (41,0) size 55x19 text run at (41,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,24.53) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,24.53) size 10.55x10.55: down LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,24.53) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,24.53) size 10.55x10.55: right LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,24.53) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,24.53) size 10.55x10.55: down LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,24.53) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,24.53) size 10.55x10.55: left LayoutText {#text} at (0,41) size 19x55 text run at (0,41) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (84.92,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (84.92,4) size 10.55x10.55: left LayoutText {#text} at (24,0) size 55x19 text run at (24,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (84.92,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (84.92,4) size 10.55x10.55: down LayoutText {#text} at (24,0) size 55x19 text run at (24,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,84.92) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,84.92) size 10.55x10.55: up LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,84.92) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,84.92) size 10.55x10.55: right LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,84.92) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,84.92) size 10.55x10.55: up LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,84.92) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,84.92) size 10.55x10.55: left LayoutText {#text} at (0,24) size 19x55 text run at (0,24) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.png index cc40549..d85505c0 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.txt index e2029882..d05b0cdb 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-left-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (60.39,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (60.39,4) size 10.55x10.55: left LayoutText {#text} at (0,0) size 54x19 text run at (0,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (60.39,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (60.39,4) size 10.55x10.55: down LayoutText {#text} at (0,0) size 54x19 text run at (0,0) width 54: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,60.39) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,60.39) size 10.55x10.55: up LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,60.39) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,60.39) size 10.55x10.55: right LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,60.39) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,60.39) size 10.55x10.55: up LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,60.39) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,60.39) size 10.55x10.55: left LayoutText {#text} at (0,0) size 19x54 text run at (0,0) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.png index 120ee2c..1501bda 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.txt index 871500a2..bba7965 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-align-right-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (49.06,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (49.06,4) size 10.55x10.55: right LayoutText {#text} at (66,0) size 54x19 text run at (66,0) width 54: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (49.06,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (49.06,4) size 10.55x10.55: down LayoutText {#text} at (66,0) size 54x19 text run at (66,0) width 54: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,49.06) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,49.06) size 10.55x10.55: down LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,49.06) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,49.06) size 10.55x10.55: right LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,49.06) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,49.06) size 10.55x10.55: down LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,49.06) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,49.06) size 10.55x10.55: left LayoutText {#text} at (0,66) size 19x54 text run at (0,66) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.png index 1c663f2..4c55ea3 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.txt index 1535aae3..6dbad95 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/html/details-writing-mode-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (0,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down LayoutText {#text} at (16,0) size 55x19 text run at (16,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 19x55 text run at (0,16) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DETAILS} at (0,20) size 120x20 LayoutBlockFlow {SUMMARY} at (0,0) size 120x20 - LayoutDetailsMarker {DIV} at (109.45,5) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down LayoutText {#text} at (49,0) size 55x19 text run at (49,0) width 55: "summary" LayoutBlockFlow {DIV} at (0,20) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (4.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DETAILS} at (20,0) size 20x120 LayoutBlockFlow {SUMMARY} at (0,0) size 20x120 - LayoutDetailsMarker {DIV} at (5,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left LayoutText {#text} at (0,49) size 19x55 text run at (0,49) width 54: "summary" LayoutBlockFlow {DIV} at (20,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/repaint/details-open-repaint-expected.txt index e1d66101..498ec73 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [10, 75, 149, 16], [8, 72, 784, 22], [8, 72, 153, 22], - [8, 57, 11, 11] + [8, 56, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/replaced/width100percent-image-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/replaced/width100percent-image-expected.txt index 65b433a8..0fe60d8 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/replaced/width100percent-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/replaced/width100percent-image-expected.txt
@@ -27,11 +27,11 @@ LayoutTableSection {TBODY} at (0,0) size 784x279 LayoutTableRow {TR} at (0,1) size 784x277 LayoutTableCell {TD} at (1,1) size 216x277 [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (218,1) size 216x277 [r=0 c=1 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (435,1) size 216x277 [r=0 c=2 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (652,128) size 131x22 [r=0 c=3 rs=1 cs=1] LayoutText {#text} at (1,1) size 4x19 text run at (1,1) width 4: " "
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.png index f9abac06..3244524 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.txt index c0efd6f..c14c49b 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/fast/writing-mode/fieldsets-expected.txt
@@ -1,77 +1,77 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1250 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1254 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1250 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x1250 - LayoutBlockFlow {BODY} at (8,8) size 769x1234 +layer at (0,0) size 785x1254 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x1254 + LayoutBlockFlow {BODY} at (8,8) size 769x1238 LayoutText {#text} at (0,0) size 262x19 text run at (0,0) width 262: "LTR fieldset with left/center/right text-align: " LayoutBR {BR} at (262,15) size 0x0 LayoutFieldset {FIELDSET} at (16,36) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (34,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,156) size 4x19 - text run at (260,156) width 4: " " + LayoutText {#text} at (260,157) size 4x19 + text run at (260,157) width 4: " " LayoutFieldset {FIELDSET} at (280,36) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (110,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,192) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,193) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,312) size 4x19 - text run at (260,312) width 4: " " + LayoutText {#text} at (260,314) size 4x19 + text run at (260,314) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,332) size 262x19 - text run at (0,332) width 262: "RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (262,347) size 0x0 - LayoutFieldset {FIELDSET} at (16,368) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,334) size 262x19 + text run at (0,334) width 262: "RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (262,349) size 0x0 + LayoutFieldset {FIELDSET} at (16,370) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,488) size 4x19 - text run at (260,488) width 4: " " - LayoutFieldset {FIELDSET} at (280,368) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (260,491) size 4x19 + text run at (260,491) width 4: " " + LayoutFieldset {FIELDSET} at (280,370) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (90,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,524) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,527) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,644) size 4x19 - text run at (260,644) width 4: " " + LayoutText {#text} at (260,648) size 4x19 + text run at (260,648) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,664) size 313x19 - text run at (0,664) width 313: "Vertical LTR fieldset with left/center/right text-align: " - LayoutBR {BR} at (313,679) size 0x0 - LayoutFieldset {FIELDSET} at (16,700) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,668) size 313x19 + text run at (0,668) width 313: "Vertical LTR fieldset with left/center/right text-align: " + LayoutBR {BR} at (313,683) size 0x0 + LayoutFieldset {FIELDSET} at (16,704) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,34) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,929) size 5x19 - text run at (151,929) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,700) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,933) size 5x19 + text run at (151,933) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,704) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,110) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,929) size 5x19 - text run at (307,929) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,700) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,933) size 5x19 + text run at (307,933) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,704) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (462,929) size 5x19 - text run at (462,929) width 5: " " + LayoutText {#text} at (462,933) size 5x19 + text run at (462,933) width 5: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,949) size 313x19 - text run at (0,949) width 313: "Vertical RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (313,964) size 0x0 - LayoutFieldset {FIELDSET} at (16,985) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,953) size 313x19 + text run at (0,953) width 313: "Vertical RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (313,968) size 0x0 + LayoutFieldset {FIELDSET} at (16,989) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,14) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,1214) size 5x19 - text run at (151,1214) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,985) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,1218) size 5x19 + text run at (151,1218) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,989) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,90) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,1214) size 5x19 - text run at (307,1214) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,985) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,1218) size 5x19 + text run at (307,1218) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,989) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-rendering-expected.txt index 1f1f9bff..74d7d76 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-rendering-expected.txt
@@ -1,16 +1,16 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 764 +layer at (0,0) size 800x600 scrollHeight 764 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x600 - LayoutBlockFlow {BODY} at (8,8) size 769x584 - LayoutBlockFlow {P} at (0,0) size 769x20 +layer at (0,0) size 800x600 + LayoutBlockFlow {HTML} at (0,0) size 800x600 + LayoutBlockFlow {BODY} at (8,8) size 784x584 + LayoutBlockFlow {P} at (0,0) size 784x20 LayoutText {#text} at (0,0) size 147x19 text run at (0,0) width 147: "Test controls placement." - LayoutBlockFlow {DIV} at (0,36) size 769x240 + LayoutBlockFlow {DIV} at (0,36) size 784x240 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,276) size 769x240 + LayoutBlockFlow {DIV} at (0,276) size 784x240 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,516) size 769x0 + LayoutBlockFlow {DIV} at (0,516) size 784x0 layer at (8,44) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,284) size 320x240 @@ -59,9 +59,9 @@ LayoutBlockFlow {DIV} at (1,-8) size 43.42x24 LayoutBlockFlow {DIV} at (12.42,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 -layer at (8,524) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,524) size 320x240 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutVideo (positioned) {VIDEO} at (8,524) size 320x240 -layer at (8,524) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,524) size 320x240 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 layer at (8,524) size 320x205 backgroundClip at (8,524) size 320x76 clip at (8,524) size 320x76
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-with-cast-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-with-cast-rendering-expected.txt index 940cc55..96b222b12 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-with-cast-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-controls-with-cast-rendering-expected.txt
@@ -1,16 +1,16 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 782 +layer at (0,0) size 800x600 scrollHeight 782 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x550 - LayoutBlockFlow {HTML} at (0,0) size 785x550 - LayoutBlockFlow {BODY} at (8,16) size 769x526 - LayoutBlockFlow {P} at (0,0) size 769x20 +layer at (0,0) size 800x550 + LayoutBlockFlow {HTML} at (0,0) size 800x550 + LayoutBlockFlow {BODY} at (8,16) size 784x526 + LayoutBlockFlow {P} at (0,0) size 784x20 LayoutText {#text} at (0,0) size 241x19 text run at (0,0) width 241: "Test controls rendering with cast button." - LayoutBlockFlow {DIV} at (0,36) size 769x245 + LayoutBlockFlow {DIV} at (0,36) size 784x245 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,281) size 769x245 + LayoutBlockFlow {DIV} at (0,281) size 784x245 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,526) size 769x0 + LayoutBlockFlow {DIV} at (0,526) size 784x0 layer at (8,52) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,297) size 320x240 @@ -61,9 +61,9 @@ LayoutBlockFlow {DIV} at (-4,0) size 24x24 LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 -layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,542) size 320x240 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutVideo (positioned) {VIDEO} at (8,542) size 320x240 -layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,542) size 320x240 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 layer at (8,542) size 320x205 backgroundClip at (8,542) size 320x58 clip at (8,542) size 320x58
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-dark-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-dark-rendering-expected.txt index 71beaba..2882887 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-dark-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-dark-rendering-expected.txt
@@ -1,16 +1,16 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 782 +layer at (0,0) size 800x600 scrollHeight 782 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x550 - LayoutBlockFlow {HTML} at (0,0) size 785x550 - LayoutBlockFlow {BODY} at (8,16) size 769x526 - LayoutBlockFlow {P} at (0,0) size 769x20 +layer at (0,0) size 800x550 + LayoutBlockFlow {HTML} at (0,0) size 800x550 + LayoutBlockFlow {BODY} at (8,16) size 784x526 + LayoutBlockFlow {P} at (0,0) size 784x20 LayoutText {#text} at (0,0) size 335x19 text run at (0,0) width 335: "Test overlay cast button rendering on dark background." - LayoutBlockFlow {DIV} at (0,36) size 769x245 + LayoutBlockFlow {DIV} at (0,36) size 784x245 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,281) size 769x245 + LayoutBlockFlow {DIV} at (0,281) size 784x245 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,526) size 769x0 + LayoutBlockFlow {DIV} at (0,526) size 784x0 layer at (8,52) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,297) size 320x240 @@ -29,9 +29,9 @@ LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 layer at (24,307) size 30x30 LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 -layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,542) size 320x240 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutVideo (positioned) {VIDEO} at (8,542) size 320x240 -layer at (8,542) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,542) size 320x240 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 layer at (8,542) size 320x205 backgroundClip at (8,542) size 320x58 clip at (8,542) size 320x58
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-light-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-light-rendering-expected.txt index 831fc863..ed780d5 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-light-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/media/video-overlay-cast-light-rendering-expected.txt
@@ -1,19 +1,19 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 874 +layer at (0,0) size 800x600 scrollHeight 874 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x620 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x620 - LayoutBlockFlow {BODY} at (8,16) size 769x596 - LayoutBlockFlow {P} at (0,0) size 769x20 +layer at (0,0) size 800x620 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 + LayoutBlockFlow {HTML} at (0,0) size 800x620 + LayoutBlockFlow {BODY} at (8,16) size 784x596 + LayoutBlockFlow {P} at (0,0) size 784x20 LayoutText {#text} at (0,0) size 331x19 text run at (0,0) width 331: "Test overlay cast button rendering on light background." - LayoutBlockFlow {DIV} at (0,36) size 769x293 + LayoutBlockFlow {DIV} at (0,36) size 784x293 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,329) size 769x267 + LayoutBlockFlow {DIV} at (0,329) size 784x267 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {DIV} at (0,596) size 769x0 + LayoutBlockFlow {DIV} at (0,596) size 784x0 layer at (8,52) size 352x288 LayoutVideo {VIDEO} at (0,0) size 352x288 -layer at (8,345) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,345) size 320x262 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutVideo {VIDEO} at (0,0) size 320x262 layer at (8,52) size 352x288 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 352x288 @@ -22,17 +22,19 @@ LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 352x253 layer at (26,65) size 30x30 LayoutButton (positioned) {INPUT} at (17.59,12.64) size 30x30 -layer at (8,345) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,345) size 320x262 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 LayoutBlockFlow {DIV} at (0,227) size 320x35 layer at (8,345) size 320x227 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 layer at (24,356) size 30x30 LayoutButton (positioned) {INPUT} at (16,11.34) size 30x30 -layer at (8,612) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,612) size 320x262 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutVideo (positioned) {VIDEO} at (8,612) size 320x262 -layer at (8,612) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,612) size 320x262 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 LayoutBlockFlow {DIV} at (0,227) size 320x35 layer at (8,612) size 320x227 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 +layer at (24,623) size 30x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 + LayoutButton (positioned) {INPUT} at (16,11.34) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/svg/wicd/rightsizing-grid-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/svg/wicd/rightsizing-grid-expected.txt index 6505e2e..3c17697 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/svg/wicd/rightsizing-grid-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/svg/wicd/rightsizing-grid-expected.txt
@@ -1,6 +1,6 @@ layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1449 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (0,0) size 785x1449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutBlockFlow {html} at (0,0) size 785x1449.14 LayoutBlockFlow {body} at (8,8) size 769x0 LayoutBlockFlow (floating) {div} at (0,0) size 769x1441.14 @@ -82,7 +82,7 @@ LayoutSVGText {text} at (54,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x23 chunk 1 (middle anchor) text run 1 at (54.06,27.00) startOffset 0 endOffset 1 width 11.88: "L" -layer at (8,257) size 256x449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,257) size 256x449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,128) size 256.30x449 layer at (0,0) size 256x449 LayoutView at (0,0) size 256x449 @@ -130,7 +130,7 @@ LayoutSVGText {text} at (32,28) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (32.81,47.00) startOffset 0 endOffset 1 width 14.38: "K" -layer at (264,513) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (264,513) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 @@ -174,7 +174,7 @@ LayoutSVGText {text} at (52,48) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (52.81,67.00) startOffset 0 endOffset 1 width 14.38: "O" -layer at (521,513) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (521,513) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 @@ -188,7 +188,7 @@ LayoutSVGText {text} at (32,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (32.81,37.00) startOffset 0 endOffset 1 width 14.38: "Q" -layer at (264,705) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (264,705) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (256.30,576) size 384.50x128 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 @@ -202,7 +202,7 @@ LayoutSVGText {text} at (54,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 11x23 chunk 1 (middle anchor) text run 1 at (54.53,27.00) startOffset 0 endOffset 1 width 10.94: "F" -layer at (8,833) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,833) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,704) size 384.50x128 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 @@ -216,7 +216,7 @@ LayoutSVGText {text} at (56,8) size 8x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 7x23 chunk 1 (middle anchor) text run 1 at (56.88,27.00) startOffset 0 endOffset 1 width 6.25: "I" -layer at (393,833) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (393,833) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192 layer at (0,0) size 96x192 LayoutView at (0,0) size 96x192 @@ -227,7 +227,7 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGRect {rect} at (0,0) size 96x192 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [x=1.00] [y=1.00] [width=28.00] [height=58.00] -layer at (489,833) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (489,833) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192 layer at (0,0) size 288x192 LayoutView at (0,0) size 288x192 @@ -241,7 +241,7 @@ LayoutSVGText {text} at (37,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (37.81,37.00) startOffset 0 endOffset 1 width 14.38: "G" -layer at (8,1025) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (8,1025) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 @@ -255,7 +255,7 @@ LayoutSVGText {text} at (22,18) size 16x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (22.81,37.00) startOffset 0 endOffset 1 width 14.38: "H" -layer at (200,1025) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (200,1025) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 @@ -269,7 +269,7 @@ LayoutSVGText {text} at (23,18) size 14x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 14x23 chunk 1 (middle anchor) text run 1 at (23.28,37.00) startOffset 0 endOffset 1 width 13.44: "R" -layer at (393,1025) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (393,1025) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (384.50,896) size 192.25x128 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 @@ -280,7 +280,7 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (585,1025) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (585,1025) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (576.75,896) size 192.25x128 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 @@ -291,7 +291,7 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (393,1153) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (393,1153) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128 layer at (0,0) size 288x128 LayoutView at (0,0) size 288x128 @@ -305,7 +305,7 @@ LayoutSVGText {text} at (39,8) size 12x24 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x23 chunk 1 (middle anchor) text run 1 at (39.38,27.00) startOffset 0 endOffset 1 width 11.25: "S" -layer at (681,1153) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 +layer at (681,1153) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 outlineClip at (0,0) size 785x600 LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128 layer at (0,0) size 96x128 LayoutView at (0,0) size 96x128
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-hixie-mixed-009-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-hixie-mixed-009-expected.txt index 83ec1777..4992629 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-hixie-mixed-009-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-hixie-mixed-009-expected.txt
@@ -1,19 +1,19 @@ layer at (0,0) size 800x600 - LayoutView at (0,0) size 800x600 + RenderView at (0,0) size 800x600 layer at (0,0) size 800x436 - LayoutBlockFlow {html} at (0,0) size 800x436.36 - LayoutBlockFlow {body} at (11.52,14.39) size 776.97x407.58 - LayoutBlockFlow {p} at (0,0) size 776.97x27 [color=#000080] - LayoutText {#text} at (0,0) size 663x26 + RenderBlock {html} at (0,0) size 800x436.36 + RenderBody {body} at (11.52,14.39) size 776.97x407.58 + RenderBlock {p} at (0,0) size 776.97x27 [color=#000080] + RenderText {#text} at (0,0) size 663x26 text run at (0,0) width 663: "The word \"TEST \" should appear twice below, the same size each time." - LayoutBlockFlow (anonymous) at (0,41.39) size 776.97x179 - LayoutSVGRoot {svg} at (11,55) size 577x174 - LayoutSVGRect {rect} at (11,55) size 577x174 [transform={m=((10.00,0.00)(0.00,10.00)) t=(0.00,0.00)}] [fill={[type=SOLID] [color=#EEEEEE]}] [x=0.00] [y=0.00] [width=60.00] [height=12.00] - LayoutSVGForeignObject {foreignObject} at (0,0) size 60x10 - LayoutBlockFlow {div} at (0,0) size 60x12 [color=#000080] - LayoutText {#text} at (0,0) size 26x12 + RenderBlock (anonymous) at (0,41.39) size 776.97x179 + RenderSVGRoot {svg} at (11,55) size 577x174 + RenderSVGRect {rect} at (11,55) size 577x174 [transform={m=((10.00,0.00)(0.00,10.00)) t=(0.00,0.00)}] [fill={[type=SOLID] [color=#EEEEEE]}] [x=0.00] [y=0.00] [width=60.00] [height=12.00] + RenderSVGForeignObject {foreignObject} at (0,0) size 60x10 + RenderBlock {div} at (0,0) size 60x12 [color=#000080] + RenderText {#text} at (0,0) size 26x12 text run at (0,0) width 26: "TEST" - LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {div} at (0,234.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] - LayoutText {#text} at (0,3) size 344x164 + RenderText {#text} at (0,0) size 0x0 + RenderBlock {div} at (0,234.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] + RenderText {#text} at (0,3) size 344x164 text run at (0,3) width 344: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png new file mode 100644 index 0000000..094c95a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt new file mode 100644 index 0000000..3950125e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt
@@ -0,0 +1,164 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x331 + LayoutBlockFlow {HTML} at (0,0) size 800x331.09 + LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x320 + LayoutTable {TABLE} at (0,0) size 467x320 + LayoutTableSection {TBODY} at (0,0) size 467x320 + LayoutTableRow {TR} at (0,1) size 467x14 + LayoutTableCell {TH} at (1,1) size 64x14 [bgcolor=#DDDD99] [r=0 c=0 rs=1 cs=1] + LayoutText {#text} at (5,0) size 54x14 + text run at (5,0) width 54: "viewBox?" + LayoutTableCell {TH} at (66,1) size 118x14 [bgcolor=#DDDD99] [r=0 c=1 rs=1 cs=1] + LayoutText {#text} at (0,0) size 118x14 + text run at (0,0) width 118: "preserve\x{AD}Aspect\x{AD}Ratio" + LayoutTableCell {TH} at (185,1) size 140x14 [bgcolor=#DDDD99] [r=0 c=2 rs=1 cs=1] + LayoutText {#text} at (53,0) size 34x14 + text run at (53,0) width 34: "<img>" + LayoutTableCell {TH} at (326,1) size 140x14 [bgcolor=#DDDD99] [r=0 c=3 rs=1 cs=1] + LayoutText {#text} at (47,0) size 46x14 + text run at (47,0) width 46: "<object>" + LayoutTableRow {TR} at (0,16) size 467x37 + LayoutTableCell {TH} at (1,84) size 64x14 [bgcolor=#DDDD99] [r=1 c=0 rs=4 cs=1] + LayoutText {#text} at (0,0) size 64x14 + text run at (0,0) width 64: "No viewBox" + LayoutTableCell {TH} at (66,34) size 118x0 [bgcolor=#DDDD99] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (185,16) size 140x37 [r=1 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,16) size 140x37 [r=1 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,54) size 467x37 + LayoutTableCell {TH} at (66,65) size 118x14 [bgcolor=#DDDD99] [r=2 c=1 rs=1 cs=1] + LayoutText {#text} at (45,0) size 28x14 + text run at (45,0) width 28: "none" + LayoutTableCell {TD} at (185,54) size 140x37 [r=2 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,54) size 140x37 [r=2 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,92) size 467x37 + LayoutTableCell {TH} at (66,103) size 118x14 [bgcolor=#DDDD99] [r=3 c=1 rs=1 cs=1] + LayoutText {#text} at (44,0) size 30x14 + text run at (44,0) width 30: "meet" + LayoutTableCell {TD} at (185,92) size 140x37 [r=3 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,92) size 140x37 [r=3 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,130) size 467x37 + LayoutTableCell {TH} at (66,141) size 118x14 [bgcolor=#DDDD99] [r=4 c=1 rs=1 cs=1] + LayoutText {#text} at (46,0) size 26x14 + text run at (46,0) width 26: "slice" + LayoutTableCell {TD} at (185,130) size 140x37 [r=4 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,130) size 140x37 [r=4 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,168) size 467x37 + LayoutTableCell {TH} at (1,236) size 64x14 [bgcolor=#DDDD99] [r=5 c=0 rs=4 cs=1] + LayoutText {#text} at (9,0) size 46x14 + text run at (9,0) width 46: "viewBox" + LayoutTableCell {TH} at (66,186) size 118x0 [bgcolor=#DDDD99] [r=5 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (185,168) size 140x37 [r=5 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,168) size 140x37 [r=5 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,206) size 467x37 + LayoutTableCell {TH} at (66,217) size 118x14 [bgcolor=#DDDD99] [r=6 c=1 rs=1 cs=1] + LayoutText {#text} at (45,0) size 28x14 + text run at (45,0) width 28: "none" + LayoutTableCell {TD} at (185,206) size 140x37 [r=6 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,206) size 140x37 [r=6 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,244) size 467x37 + LayoutTableCell {TH} at (66,255) size 118x14 [bgcolor=#DDDD99] [r=7 c=1 rs=1 cs=1] + LayoutText {#text} at (44,0) size 30x14 + text run at (44,0) width 30: "meet" + LayoutTableCell {TD} at (185,244) size 140x37 [r=7 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,244) size 140x37 [r=7 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableRow {TR} at (0,282) size 467x37 + LayoutTableCell {TH} at (66,293) size 118x14 [bgcolor=#DDDD99] [r=8 c=1 rs=1 cs=1] + LayoutText {#text} at (46,0) size 26x14 + text run at (46,0) width 26: "slice" + LayoutTableCell {TD} at (185,282) size 140x37 [r=8 c=2 rs=1 cs=1] + LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] + LayoutText {#text} at (0,0) size 0x0 + LayoutTableCell {TD} at (326,282) size 140x37 [r=8 c=3 rs=1 cs=1] + LayoutText {#text} at (0,0) size 0x0 +layer at (332,22) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (0,0) size 133x29 + LayoutSVGEllipse {circle} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00] +layer at (332,60) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (0,0) size 133x29 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] +layer at (332,98) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (0,0) size 133x29 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] +layer at (332,136) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (0,0) size 133x29 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] +layer at (332,174) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (51,0) size 23x22 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (51,0) size 23x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (51,0) size 23x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] +layer at (332,212) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (0,0) size 97x22 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (0,0) size 97x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (0,0) size 98x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] +layer at (332,250) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (51,0) size 23x22 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (51,0) size 23x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (51,0) size 23x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] +layer at (332,288) size 139x35 + LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] + layer at (0,0) size 133x29 + LayoutView at (0,0) size 133x29 + layer at (0,0) size 133x29 + LayoutSVGRoot {svg} at (0,0) size 97x29 + LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 + LayoutSVGContainer {g} at (0,0) size 97x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] + LayoutSVGPath {path} at (0,0) size 98x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"]
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png new file mode 100644 index 0000000..4695665 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt new file mode 100644 index 0000000..8fc1514b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win-xp/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt
@@ -0,0 +1,26 @@ +layer at (0,0) size 800x600 + RenderView at (0,0) size 800x600 +layer at (0,0) size 800x172 + RenderBlock {HTML} at (0,0) size 800x172.09 + RenderBody {BODY} at (5.55,5.55) size 788.91x161 + RenderText {#text} at (158,147) size 4x14 + text run at (158,147) width 4: " " + RenderText {#text} at (0,0) size 0x0 + RenderText {#text} at (0,0) size 0x0 + RenderText {#text} at (0,0) size 0x0 + RenderText {#text} at (0,0) size 0x0 + RenderText {#text} at (0,0) size 0x0 +layer at (6,6) size 159x159 + RenderEmbeddedObject {OBJECT} at (0,0) size 158.92x158.92 [border: (1px dashed #800000)] + layer at (0,0) size 153x153 + RenderView at (0,0) size 153x153 + layer at (0,0) size 153x153 + RenderSVGRoot {svg} at (0,0) size 153x153 + RenderSVGEllipse {circle} at (0,0) size 153x153 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00] +layer at (167,6) size 159x159 + RenderEmbeddedObject {OBJECT} at (161.92,0) size 158.92x158.92 [border: (1px dashed #800000)] + layer at (0,0) size 153x153 + RenderView at (0,0) size 153x153 + layer at (0,0) size 153x153 + RenderSVGRoot {svg} at (0,0) size 153x153 + RenderSVGEllipse {circle} at (0,0) size 153x153 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00]
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug101674-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug101674-expected.txt index 64825a3..516ad1e5 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug101674-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug101674-expected.txt
@@ -1,23 +1,23 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1596 +layer at (0,0) size 800x600 scrollHeight 1596 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1596 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x1596 - LayoutBlockFlow {BODY} at (8,8) size 769x1580 - LayoutTable {TABLE} at (0,0) size 769x75 [border: (1px solid #000000)] - LayoutTableSection {TBODY} at (1,1) size 767x73 - LayoutTableRow {TR} at (0,2) size 767x69 +layer at (0,0) size 800x1596 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 + LayoutBlockFlow {HTML} at (0,0) size 800x1596 + LayoutBlockFlow {BODY} at (8,8) size 784x1580 + LayoutTable {TABLE} at (0,0) size 784x75 [border: (1px solid #000000)] + LayoutTableSection {TBODY} at (1,1) size 782x73 + LayoutTableRow {TR} at (0,2) size 782x69 LayoutTableCell {TD} at (2,2) size 444x69 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutImage {IMG} at (2,2) size 88x65 LayoutImage {IMG} at (90,2) size 88x65 LayoutImage {IMG} at (178,2) size 88x65 LayoutImage {IMG} at (266,2) size 88x65 LayoutImage {IMG} at (354,2) size 88x65 - LayoutTableCell {TD} at (448,24) size 317x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (448,24) size 332x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 148x19 text run at (2,2) width 148: "Nothing between images" - LayoutTable {TABLE} at (0,75) size 769x335 [border: (1px solid #000000)] - LayoutTableSection {TBODY} at (1,1) size 767x333 - LayoutTableRow {TR} at (0,2) size 767x329 + LayoutTable {TABLE} at (0,75) size 784x335 [border: (1px solid #000000)] + LayoutTableSection {TBODY} at (1,1) size 782x333 + LayoutTableRow {TR} at (0,2) size 782x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutImage {IMG} at (2,2) size 88x65 LayoutText {#text} at (0,0) size 0x0 @@ -28,12 +28,12 @@ LayoutImage {IMG} at (2,197) size 88x65 LayoutText {#text} at (0,0) size 0x0 LayoutImage {IMG} at (2,262) size 88x65 - LayoutTableCell {TD} at (96,154) size 669x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (96,154) size 684x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 144x19 text run at (2,2) width 144: "Spaces between images" - LayoutTable {TABLE} at (0,410) size 769x335 [border: (1px solid #000000)] - LayoutTableSection {TBODY} at (1,1) size 767x333 - LayoutTableRow {TR} at (0,2) size 767x329 + LayoutTable {TABLE} at (0,410) size 784x335 [border: (1px solid #000000)] + LayoutTableSection {TBODY} at (1,1) size 782x333 + LayoutTableRow {TR} at (0,2) size 782x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutImage {IMG} at (2,2) size 88x65 LayoutText {#text} at (0,0) size 0x0 @@ -44,12 +44,12 @@ LayoutImage {IMG} at (2,197) size 88x65 LayoutText {#text} at (0,0) size 0x0 LayoutImage {IMG} at (2,262) size 88x65 - LayoutTableCell {TD} at (96,154) size 669x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (96,154) size 684x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 156x19 text run at (2,2) width 156: "Newlines between images" - LayoutTable {TABLE} at (0,745) size 769x80 [border: (1px solid #000000)] - LayoutTableSection {TBODY} at (1,1) size 767x78 - LayoutTableRow {TR} at (0,2) size 767x74 + LayoutTable {TABLE} at (0,745) size 784x80 [border: (1px solid #000000)] + LayoutTableSection {TBODY} at (1,1) size 782x78 + LayoutTableRow {TR} at (0,2) size 782x74 LayoutTableCell {TD} at (2,2) size 588x74 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,52) size 26x19 text run at (2,52) width 26: "One" @@ -66,12 +66,12 @@ LayoutText {#text} at (472,52) size 26x19 text run at (472,52) width 26: "Five" LayoutImage {IMG} at (498,2) size 88x65 - LayoutTableCell {TD} at (592,27) size 173x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (592,27) size 188x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 127x19 text run at (2,2) width 127: "Text between images" - LayoutTable {TABLE} at (0,825) size 769x375 [border: (1px solid #000000)] - LayoutTableSection {TBODY} at (1,1) size 767x373 - LayoutTableRow {TR} at (0,2) size 767x369 + LayoutTable {TABLE} at (0,825) size 784x375 [border: (1px solid #000000)] + LayoutTableSection {TBODY} at (1,1) size 782x373 + LayoutTableRow {TR} at (0,2) size 782x369 LayoutTableCell {TD} at (2,2) size 127x369 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 26x19 text run at (2,2) width 26: "One" @@ -88,12 +88,12 @@ LayoutText {#text} at (90,282) size 26x19 text run at (90,282) width 26: "Five" LayoutImage {IMG} at (2,302) size 88x65 - LayoutTableCell {TD} at (131,174) size 634x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (131,174) size 649x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 201x19 text run at (2,2) width 201: "Text with spaces between images" - LayoutTable {TABLE} at (0,1200) size 769x380 [border: (1px solid #000000)] - LayoutTableSection {TBODY} at (1,1) size 767x378 - LayoutTableRow {TR} at (0,2) size 767x374 + LayoutTable {TABLE} at (0,1200) size 784x380 [border: (1px solid #000000)] + LayoutTableSection {TBODY} at (1,1) size 782x378 + LayoutTableRow {TR} at (0,2) size 782x374 LayoutTableCell {TD} at (2,2) size 135x374 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 26x89 text run at (2,2) width 26: "One" @@ -115,6 +115,6 @@ text run at (98,282) width 26: "Five" text run at (2,352) width 7: "e" LayoutImage {IMG} at (9,302) size 88x65 - LayoutTableCell {TD} at (139,177) size 626x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (139,177) size 641x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 288x19 text run at (2,2) width 288: "Text with spaces and more text between images"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug86708-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug86708-expected.txt index f370e60..27cd2cd 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug86708-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla/bugs/bug86708-expected.txt
@@ -1,19 +1,19 @@ layer at (0,0) size 800x600 - LayoutView at (0,0) size 800x600 + RenderView at (0,0) size 800x600 layer at (0,0) size 800x600 - LayoutBlockFlow {HTML} at (0,0) size 800x600 - LayoutBlockFlow {BODY} at (8,8) size 784x584 + RenderBlock {HTML} at (0,0) size 800x600 + RenderBody {BODY} at (8,8) size 784x584 LayoutTable {TABLE} at (0,0) size 750x238 [border: (2px outset #808080)] LayoutTableSection {TBODY} at (2,2) size 746x234 LayoutTableRow {TR} at (0,0) size 746x212 LayoutTableCell {TD} at (0,0) size 476x212 [bgcolor=#FF0000] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] - LayoutInline {A} at (0,0) size 286x19 [color=#0000EE] - LayoutImage {IMG} at (1,1) size 286x210 + RenderInline {A} at (0,0) size 286x19 [color=#0000EE] + RenderImage {IMG} at (1,1) size 286x210 LayoutTableCell {TD} at (476,0) size 270x22 [bgcolor=#FFFF00] [border: (1px inset #808080)] [r=0 c=1 rs=2 cs=1] - LayoutText {#text} at (1,1) size 161x19 + RenderText {#text} at (1,1) size 161x19 text run at (1,1) width 161: "ROWSPAN =2 in this cell" LayoutTableRow {TR} at (0,212) size 746x22 LayoutTableCell {TD} at (0,212) size 476x22 [bgcolor=#FFFFFF] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] - LayoutBlockFlow {P} at (1,1) size 474x20 - LayoutText {#text} at (0,0) size 282x19 + RenderBlock {P} at (1,1) size 474x20 + RenderText {#text} at (0,0) size 282x19 text run at (0,0) width 282: "Hello, this text may appear on the image above"
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla_expected_failures/bugs/bug85016-expected.txt b/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla_expected_failures/bugs/bug85016-expected.txt index 16dc26f..a4237e7 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla_expected_failures/bugs/bug85016-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win-xp/tables/mozilla_expected_failures/bugs/bug85016-expected.txt
@@ -1,25 +1,25 @@ layer at (0,0) size 800x600 scrollWidth 982 scrollHeight 2108 - LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x2108 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 - LayoutBlockFlow {HTML} at (0,0) size 800x2108 - LayoutBlockFlow {BODY} at (32,32) size 736x2044 - LayoutBlockFlow {DIV} at (32,0) size 672x668 [border: (1px solid #008000)] - LayoutBlockFlow {DIV} at (33,33) size 606x602 [border: (1px solid #FF0000)] - LayoutImage {IMG} at (1,1) size 604x600 - LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {P} at (0,700) size 736x20 - LayoutText {#text} at (0,0) size 506x19 + RenderView at (0,0) size 800x600 +layer at (0,0) size 800x2108 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 outlineClip at (0,0) size 800x600 + RenderBlock {HTML} at (0,0) size 800x2108 + RenderBody {BODY} at (32,32) size 736x2044 + RenderBlock {DIV} at (32,0) size 672x668 [border: (1px solid #008000)] + RenderBlock {DIV} at (33,33) size 606x602 [border: (1px solid #FF0000)] + RenderImage {IMG} at (1,1) size 604x600 + RenderText {#text} at (0,0) size 0x0 + RenderBlock {P} at (0,700) size 736x20 + RenderText {#text} at (0,0) size 506x19 text run at (0,0) width 506: "percentage height images in DIV with no height (red) in a DIV with no height (green)" - LayoutBlockFlow {DIV} at (32,752) size 672x672 [border: (3px dotted #008000)] - LayoutBlockFlow {DIV} at (35,35) size 602x602 [border: (1px solid #FF0000)] - LayoutImage {IMG} at (1,1) size 882x600 - LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {P} at (0,1456) size 736x20 - LayoutText {#text} at (0,0) size 443x19 + RenderBlock {DIV} at (32,752) size 672x672 [border: (3px dotted #008000)] + RenderBlock {DIV} at (35,35) size 602x602 [border: (1px solid #FF0000)] + RenderImage {IMG} at (1,1) size 882x600 + RenderText {#text} at (0,0) size 0x0 + RenderBlock {P} at (0,1456) size 736x20 + RenderText {#text} at (0,0) size 443x19 text run at (0,0) width 443: "percentage height image in table cell (red), in a DIV with no height (green)" - LayoutBlockFlow {DIV} at (32,1508) size 672x536 + RenderBlock {DIV} at (32,1508) size 672x536 LayoutTable {TABLE} at (0,0) size 260x536 [border: (1px solid #FF0000)] LayoutTableSection {TBODY} at (1,1) size 258x534 LayoutTableRow {TR} at (0,2) size 258x530 LayoutTableCell {TD} at (2,180) size 254x174 [border: (1px solid #FF0000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 250x170 + RenderImage {IMG} at (2,2) size 250x170
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-2-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-2-expected.png index a4a311c6..d5475a29 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index 45d36d6..f53e355 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index 707929c8..3b1fc887 100644 --- a/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win-xp/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/perpendicular-layer-sorting-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/perpendicular-layer-sorting-expected.png index a505f0c8..364c128 100644 --- a/third_party/WebKit/LayoutTests/platform/win/compositing/perpendicular-layer-sorting-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/compositing/perpendicular-layer-sorting-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/video-frame-size-change-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/video-frame-size-change-expected.txt index e6a9969..740727a9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/compositing/video-frame-size-change-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/compositing/video-frame-size-change-expected.txt
@@ -12,16 +12,16 @@ LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 layer at (8,50) size 320x180 - LayoutVideo {VIDEO} at (0,0) size 320x180 + LayoutVideo {VIDEO} at (0,0) size 320x179.98 layer at (332,50) size 320x180 - LayoutVideo {VIDEO} at (324,0) size 320x180 + LayoutVideo {VIDEO} at (324,0) size 320x179.98 layer at (8,50) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (8,50) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98 layer at (332,50) size 320x180 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x180 - LayoutBlockFlow {DIV} at (0,145) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x179.98 + LayoutBlockFlow {DIV} at (0,144.98) size 320x35 layer at (332,50) size 320x145 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x145 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x144.98
diff --git a/third_party/WebKit/LayoutTests/platform/win/css1/box_properties/width-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css1/box_properties/width-expected.txt index 4d2d031..3573a97 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css1/box_properties/width-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css1/box_properties/width-expected.txt
@@ -22,7 +22,7 @@ LayoutText {#text} at (0,0) size 292x17 text run at (0,0) width 292: "The square above should be fifty pixels wide." LayoutBlockFlow (anonymous) at (0,205) size 769x385 - LayoutImage {IMG} at (0,0) size 384.50x385 + LayoutImage {IMG} at (0,0) size 384.50x384.50 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,606) size 769x18 LayoutText {#text} at (0,0) size 683x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/css1/formatting_model/replaced_elements-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css1/formatting_model/replaced_elements-expected.txt index 2d93907..a7880ea 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css1/formatting_model/replaced_elements-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css1/formatting_model/replaced_elements-expected.txt
@@ -1,8 +1,8 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2349 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 2347 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x2349 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x2349 - LayoutBlockFlow {BODY} at (8,8) size 769x2333 [bgcolor=#CCCCCC] +layer at (0,0) size 785x2347 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x2347 + LayoutBlockFlow {BODY} at (8,8) size 769x2331 [bgcolor=#CCCCCC] LayoutBlockFlow {P} at (0,0) size 769x18 LayoutText {#text} at (0,0) size 363x17 text run at (0,0) width 363: "The style declarations which apply to the text below are:" @@ -37,30 +37,30 @@ LayoutBlockFlow {P} at (0,324) size 769x18 LayoutText {#text} at (0,0) size 406x17 text run at (0,0) width 406: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (192.25,358) size 384.50x385 - LayoutBlockFlow {P} at (0,759) size 769x36 + LayoutImage {IMG} at (192.25,358) size 384.50x384.50 + LayoutBlockFlow {P} at (0,758.50) size 769x36 LayoutText {#text} at (0,0) size 738x35 text run at (0,0) width 738: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally" text run at (0,18) width 172: "within the parent element: " text run at (171,18) width 404: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (384.50,811) size 384.50x385 - LayoutBlockFlow {P} at (0,1212) size 769x36 + LayoutImage {IMG} at (384.50,810.50) size 384.50x384.50 + LayoutBlockFlow {P} at (0,1211) size 769x36 LayoutText {#text} at (0,0) size 750x35 text run at (0,0) width 750: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge of" text run at (0,18) width 127: "the parent element: " text run at (126,18) width 405: "the document body in the first half, and the table in the second." - LayoutTable {TABLE} at (0,1264) size 769x1069 [border: (1px outset #808080)] - LayoutTableSection {TBODY} at (1,1) size 767x1067 + LayoutTable {TABLE} at (0,1263) size 769x1068 [border: (1px outset #808080)] + LayoutTableSection {TBODY} at (1,1) size 767x1066 LayoutTableRow {TR} at (0,0) size 767x26 LayoutTableCell {TD} at (0,0) size 767x26 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] LayoutInline {STRONG} at (0,0) size 161x17 LayoutText {#text} at (4,4) size 161x17 text run at (4,4) width 161: "TABLE Testing Section" - LayoutTableRow {TR} at (0,26) size 767x1041 + LayoutTableRow {TR} at (0,26) size 767x1040 LayoutTableCell {TD} at (0,533) size 12x26 [bgcolor=#C0C0C0] [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (4,4) size 4x17 text run at (4,4) width 4: " " - LayoutTableCell {TD} at (12,26) size 755x1041 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (12,26) size 755x1040 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1] LayoutBlockFlow {P} at (4,4) size 747x19 LayoutImage {IMG} at (0,0) size 15x15 LayoutText {#text} at (15,1) size 442x17 @@ -73,14 +73,14 @@ LayoutBlockFlow {P} at (4,135) size 747x18 LayoutText {#text} at (0,0) size 406x17 text run at (0,0) width 406: "The above image should be a 15px square aligned at the center." - LayoutImage {IMG} at (190.75,169) size 373.50x374 - LayoutBlockFlow {P} at (4,559) size 747x36 + LayoutImage {IMG} at (190.75,169) size 373.50x373.50 + LayoutBlockFlow {P} at (4,558.50) size 747x36 LayoutText {#text} at (0,0) size 738x35 text run at (0,0) width 738: "The above image should be a square resized so its width is 50% of the its parent element, and centered horizontally" text run at (0,18) width 172: "within the parent element: " text run at (171,18) width 404: "the document body in the first half, and the table in the second." - LayoutImage {IMG} at (377.50,611) size 373.50x374 - LayoutBlockFlow {P} at (4,1001) size 747x36 + LayoutImage {IMG} at (377.50,610.50) size 373.50x373.50 + LayoutBlockFlow {P} at (4,1000) size 747x36 LayoutText {#text} at (0,0) size 733x35 text run at (0,0) width 733: "The above image should be a square resized so its width is 50% of its parent element, and aligned at the right edge" text run at (0,18) width 144: "of the parent element: "
diff --git a/third_party/WebKit/LayoutTests/platform/win/css1/text_properties/vertical_align-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css1/text_properties/vertical_align-expected.txt index edc84f2..d993621 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css1/text_properties/vertical_align-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css1/text_properties/vertical_align-expected.txt
@@ -174,26 +174,26 @@ LayoutImage {IMG} at (47.33,122) size 6x20 LayoutText {#text} at (53,122) size 99x21 text run at (53,122) width 99: " all of which " - LayoutImage {IMG} at (151.78,122) size 20x65 - LayoutText {#text} at (171,122) size 6x21 - text run at (171,122) width 6: " " - LayoutInline {SPAN} at (0,0) size 264x41 - LayoutText {#text} at (176,106) size 264x41 - text run at (176,106) width 264: "should be aligned" - LayoutText {#text} at (439,122) size 6x21 - text run at (439,122) width 6: " " - LayoutImage {IMG} at (444.42,122) size 11x35 - LayoutText {#text} at (455,122) size 120x21 - text run at (455,122) width 120: " with the top of " - LayoutImage {IMG} at (574.09,122) size 9x30 - LayoutText {#text} at (583,122) size 5x21 - text run at (583,122) width 5: " " - LayoutInline {SPAN} at (0,0) size 701x142 - LayoutText {#text} at (587,114) size 21x31 - text run at (587,114) width 21: "a " - LayoutInline {SPAN} at (0,0) size 701x188 - LayoutText {#text} at (607,77) size 701x188 - text run at (607,77) width 94: "14-" + LayoutImage {IMG} at (151.78,122) size 19.50x65 + LayoutText {#text} at (171,122) size 5x21 + text run at (171,122) width 5: " " + LayoutInline {SPAN} at (0,0) size 265x41 + LayoutText {#text} at (175,106) size 265x41 + text run at (175,106) width 265: "should be aligned" + LayoutText {#text} at (439,122) size 5x21 + text run at (439,122) width 5: " " + LayoutImage {IMG} at (443.92,122) size 10.50x35 + LayoutText {#text} at (454,122) size 120x21 + text run at (454,122) width 120: " with the top of " + LayoutImage {IMG} at (573.09,122) size 9x30 + LayoutText {#text} at (582,122) size 5x21 + text run at (582,122) width 5: " " + LayoutInline {SPAN} at (0,0) size 700x142 + LayoutText {#text} at (586,114) size 21x31 + text run at (586,114) width 21: "a " + LayoutInline {SPAN} at (0,0) size 700x188 + LayoutText {#text} at (606,77) size 700x188 + text run at (606,77) width 94: "14-" text run at (0,188) width 144: "point" LayoutText {#text} at (143,225) size 146x31 text run at (143,225) width 146: " text element" @@ -207,15 +207,15 @@ text run at (312,236) width 195: "regardless of the line in which" LayoutText {#text} at (506,233) size 5x21 text run at (506,233) width 5: " " - LayoutImage {IMG} at (510.72,233) size 5x15 - LayoutText {#text} at (515,233) size 6x21 - text run at (515,233) width 6: " " + LayoutImage {IMG} at (510.72,233) size 4.50x15 + LayoutText {#text} at (515,233) size 5x21 + text run at (515,233) width 5: " " LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (520,231) size 159x24 - text run at (520,231) width 159: "the images appear." - LayoutText {#text} at (678,233) size 6x21 - text run at (678,233) width 6: " " - LayoutImage {IMG} at (683.16,233) size 27x90 + LayoutText {#text} at (519,231) size 159x24 + text run at (519,231) width 159: "the images appear." + LayoutText {#text} at (678,233) size 5x21 + text run at (678,233) width 5: " " + LayoutImage {IMG} at (682.66,233) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1802.31) size 769x36 LayoutText {#text} at (0,0) size 765x35 @@ -247,28 +247,28 @@ LayoutText {#text} at (699,23) size 737x92 text run at (699,23) width 38: " all of" text run at (0,98) width 44: "which " - LayoutImage {IMG} at (43.09,76) size 20x65 - LayoutText {#text} at (63,98) size 121x17 - text run at (63,98) width 121: " should be aligned " - LayoutImage {IMG} at (183.95,91) size 11x35 - LayoutText {#text} at (194,98) size 5x17 - text run at (194,98) width 5: " " + LayoutImage {IMG} at (43.09,76) size 19.50x65 + LayoutText {#text} at (62,98) size 122x17 + text run at (62,98) width 122: " should be aligned " + LayoutImage {IMG} at (183.45,91) size 10.50x35 + LayoutText {#text} at (193,98) size 5x17 + text run at (193,98) width 5: " " LayoutInline {SPAN} at (0,0) size 237x36 - LayoutText {#text} at (198,83) size 237x36 - text run at (198,83) width 237: "with the middle of" - LayoutText {#text} at (434,98) size 5x17 - text run at (434,98) width 5: " " - LayoutImage {IMG} at (438.47,83) size 15x50 - LayoutText {#text} at (453,98) size 5x17 - text run at (453,98) width 5: " " - LayoutInline {SPAN} at (0,0) size 720x104 - LayoutText {#text} at (457,91) size 18x26 - text run at (457,91) width 18: "a " + LayoutText {#text} at (197,83) size 237x36 + text run at (197,83) width 237: "with the middle of" + LayoutText {#text} at (433,98) size 5x17 + text run at (433,98) width 5: " " + LayoutImage {IMG} at (437.47,83) size 15x50 + LayoutText {#text} at (452,98) size 5x17 + text run at (452,98) width 5: " " + LayoutInline {SPAN} at (0,0) size 719x104 + LayoutText {#text} at (456,91) size 18x26 + text run at (456,91) width 18: "a " LayoutInline {SPAN} at (0,0) size 204x66 - LayoutText {#text} at (474,59) size 204x66 - text run at (474,59) width 204: "14-point" - LayoutText {#text} at (677,91) size 720x104 - text run at (677,91) width 43: " text" + LayoutText {#text} at (473,59) size 204x66 + text run at (473,59) width 204: "14-point" + LayoutText {#text} at (676,91) size 719x104 + text run at (676,91) width 43: " text" text run at (0,169) width 76: "element" LayoutText {#text} at (75,176) size 5x17 text run at (75,176) width 5: " " @@ -280,15 +280,15 @@ text run at (98,176) width 195: "regardless of the line in which" LayoutText {#text} at (292,176) size 5x17 text run at (292,176) width 5: " " - LayoutImage {IMG} at (296.22,179) size 5x15 - LayoutText {#text} at (301,176) size 5x17 - text run at (301,176) width 5: " " + LayoutImage {IMG} at (296.22,179) size 4.50x15 + LayoutText {#text} at (300,176) size 5x17 + text run at (300,176) width 5: " " LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (305,171) size 159x24 - text run at (305,171) width 159: "the images appear." - LayoutText {#text} at (463,176) size 5x17 - text run at (463,176) width 5: " " - LayoutImage {IMG} at (467.34,141) size 27x90 + LayoutText {#text} at (304,171) size 159x24 + text run at (304,171) width 159: "the images appear." + LayoutText {#text} at (462,176) size 5x17 + text run at (462,176) width 5: " " + LayoutImage {IMG} at (466.84,141) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,2101.31) size 769x36 LayoutText {#text} at (0,0) size 750x35 @@ -342,36 +342,36 @@ text run at (70,47) width 78: "all of which" LayoutText {#text} at (147,47) size 5x17 text run at (147,47) width 5: " " - LayoutImage {IMG} at (151.30,47) size 20x65 - LayoutText {#text} at (171,47) size 5x17 - text run at (171,47) width 5: " " + LayoutImage {IMG} at (151.30,47) size 19.50x65 + LayoutText {#text} at (170,47) size 5x17 + text run at (170,47) width 5: " " LayoutInline {SPAN} at (0,0) size 114x17 - LayoutText {#text} at (175,47) size 114x17 - text run at (175,47) width 114: "should be aligned" - LayoutText {#text} at (288,47) size 5x17 - text run at (288,47) width 5: " " - LayoutImage {IMG} at (292.16,47) size 11x35 - LayoutText {#text} at (303,47) size 5x17 - text run at (303,47) width 5: " " + LayoutText {#text} at (174,47) size 114x17 + text run at (174,47) width 114: "should be aligned" + LayoutText {#text} at (287,47) size 5x17 + text run at (287,47) width 5: " " + LayoutImage {IMG} at (291.66,47) size 10.50x35 + LayoutText {#text} at (302,47) size 5x17 + text run at (302,47) width 5: " " LayoutInline {SPAN} at (0,0) size 188x36 - LayoutText {#text} at (307,47) size 188x36 - text run at (307,47) width 188: "with the top of" - LayoutText {#text} at (494,47) size 5x17 - text run at (494,47) width 5: " " - LayoutImage {IMG} at (498.69,47) size 15x50 - LayoutText {#text} at (513,47) size 5x17 - text run at (513,47) width 5: " " + LayoutText {#text} at (306,47) size 188x36 + text run at (306,47) width 188: "with the top of" + LayoutText {#text} at (493,47) size 5x17 + text run at (493,47) width 5: " " + LayoutImage {IMG} at (497.69,47) size 15x50 + LayoutText {#text} at (512,47) size 5x17 + text run at (512,47) width 5: " " LayoutInline {SPAN} at (0,0) size 134x17 - LayoutText {#text} at (517,47) size 134x17 - text run at (517,47) width 134: "the tallest element in" - LayoutText {#text} at (650,47) size 5x17 - text run at (650,47) width 5: " " - LayoutImage {IMG} at (654.53,47) size 5x15 - LayoutText {#text} at (659,47) size 5x17 - text run at (659,47) width 5: " " - LayoutInline {BIG} at (0,0) size 753x89 - LayoutText {#text} at (663,47) size 753x89 - text run at (663,47) width 90: "whichever" + LayoutText {#text} at (516,47) size 134x17 + text run at (516,47) width 134: "the tallest element in" + LayoutText {#text} at (649,47) size 5x17 + text run at (649,47) width 5: " " + LayoutImage {IMG} at (653.53,47) size 4.50x15 + LayoutText {#text} at (658,47) size 5x17 + text run at (658,47) width 5: " " + LayoutInline {BIG} at (0,0) size 751x89 + LayoutText {#text} at (662,47) size 751x89 + text run at (662,47) width 89: "whichever" text run at (0,112) width 211: "line the elements appear." LayoutText {#text} at (210,112) size 5x17 text run at (210,112) width 5: " " @@ -518,7 +518,7 @@ LayoutImage {IMG} at (74.42,122) size 6x20 LayoutText {#text} at (80,122) size 99x21 text run at (80,122) width 99: " all of which " - LayoutImage {IMG} at (178.88,122) size 20x65 + LayoutImage {IMG} at (178.88,122) size 19.50x65 LayoutText {#text} at (198,122) size 6x21 text run at (198,122) width 6: " " LayoutInline {SPAN} at (0,0) size 264x41 @@ -526,18 +526,18 @@ text run at (203,106) width 264: "should be aligned" LayoutText {#text} at (466,122) size 6x21 text run at (466,122) width 6: " " - LayoutImage {IMG} at (471.52,122) size 11x35 - LayoutText {#text} at (482,122) size 120x21 - text run at (482,122) width 120: " with the top of " - LayoutImage {IMG} at (601.19,122) size 9x30 - LayoutText {#text} at (610,122) size 5x21 - text run at (610,122) width 5: " " - LayoutInline {SPAN} at (0,0) size 728x142 - LayoutText {#text} at (614,114) size 21x31 - text run at (614,114) width 21: "a " - LayoutInline {SPAN} at (0,0) size 728x188 - LayoutText {#text} at (634,77) size 728x188 - text run at (634,77) width 94: "14-" + LayoutImage {IMG} at (471.02,122) size 10.50x35 + LayoutText {#text} at (481,122) size 120x21 + text run at (481,122) width 120: " with the top of " + LayoutImage {IMG} at (600.19,122) size 9x30 + LayoutText {#text} at (609,122) size 5x21 + text run at (609,122) width 5: " " + LayoutInline {SPAN} at (0,0) size 727x142 + LayoutText {#text} at (613,114) size 21x31 + text run at (613,114) width 21: "a " + LayoutInline {SPAN} at (0,0) size 727x188 + LayoutText {#text} at (633,77) size 727x188 + text run at (633,77) width 94: "14-" text run at (0,188) width 144: "point" LayoutText {#text} at (143,225) size 146x31 text run at (143,225) width 146: " text element" @@ -551,15 +551,15 @@ text run at (312,236) width 195: "regardless of the line in which" LayoutText {#text} at (506,233) size 5x21 text run at (506,233) width 5: " " - LayoutImage {IMG} at (510.72,233) size 5x15 - LayoutText {#text} at (515,233) size 6x21 - text run at (515,233) width 6: " " + LayoutImage {IMG} at (510.72,233) size 4.50x15 + LayoutText {#text} at (515,233) size 5x21 + text run at (515,233) width 5: " " LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (520,231) size 159x24 - text run at (520,231) width 159: "the images appear." - LayoutText {#text} at (678,233) size 6x21 - text run at (678,233) width 6: " " - LayoutImage {IMG} at (683.16,233) size 27x90 + LayoutText {#text} at (519,231) size 159x24 + text run at (519,231) width 159: "the images appear." + LayoutText {#text} at (678,233) size 5x21 + text run at (678,233) width 5: " " + LayoutImage {IMG} at (682.66,233) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1453.31) size 747x36 LayoutText {#text} at (0,0) size 746x35 @@ -591,28 +591,28 @@ LayoutText {#text} at (699,23) size 737x92 text run at (699,23) width 38: " all of" text run at (0,98) width 44: "which " - LayoutImage {IMG} at (43.09,76) size 20x65 - LayoutText {#text} at (63,98) size 121x17 - text run at (63,98) width 121: " should be aligned " - LayoutImage {IMG} at (183.95,91) size 11x35 - LayoutText {#text} at (194,98) size 5x17 - text run at (194,98) width 5: " " + LayoutImage {IMG} at (43.09,76) size 19.50x65 + LayoutText {#text} at (62,98) size 122x17 + text run at (62,98) width 122: " should be aligned " + LayoutImage {IMG} at (183.45,91) size 10.50x35 + LayoutText {#text} at (193,98) size 5x17 + text run at (193,98) width 5: " " LayoutInline {SPAN} at (0,0) size 237x36 - LayoutText {#text} at (198,83) size 237x36 - text run at (198,83) width 237: "with the middle of" - LayoutText {#text} at (434,98) size 5x17 - text run at (434,98) width 5: " " - LayoutImage {IMG} at (438.47,83) size 15x50 - LayoutText {#text} at (453,98) size 5x17 - text run at (453,98) width 5: " " - LayoutInline {SPAN} at (0,0) size 720x104 - LayoutText {#text} at (457,91) size 18x26 - text run at (457,91) width 18: "a " + LayoutText {#text} at (197,83) size 237x36 + text run at (197,83) width 237: "with the middle of" + LayoutText {#text} at (433,98) size 5x17 + text run at (433,98) width 5: " " + LayoutImage {IMG} at (437.47,83) size 15x50 + LayoutText {#text} at (452,98) size 5x17 + text run at (452,98) width 5: " " + LayoutInline {SPAN} at (0,0) size 719x104 + LayoutText {#text} at (456,91) size 18x26 + text run at (456,91) width 18: "a " LayoutInline {SPAN} at (0,0) size 204x66 - LayoutText {#text} at (474,59) size 204x66 - text run at (474,59) width 204: "14-point" - LayoutText {#text} at (677,91) size 720x104 - text run at (677,91) width 43: " text" + LayoutText {#text} at (473,59) size 204x66 + text run at (473,59) width 204: "14-point" + LayoutText {#text} at (676,91) size 719x104 + text run at (676,91) width 43: " text" text run at (0,169) width 76: "element" LayoutText {#text} at (75,176) size 5x17 text run at (75,176) width 5: " " @@ -624,15 +624,15 @@ text run at (98,176) width 195: "regardless of the line in which" LayoutText {#text} at (292,176) size 5x17 text run at (292,176) width 5: " " - LayoutImage {IMG} at (296.22,179) size 5x15 - LayoutText {#text} at (301,176) size 5x17 - text run at (301,176) width 5: " " + LayoutImage {IMG} at (296.22,179) size 4.50x15 + LayoutText {#text} at (300,176) size 5x17 + text run at (300,176) width 5: " " LayoutInline {BIG} at (0,0) size 159x24 - LayoutText {#text} at (305,171) size 159x24 - text run at (305,171) width 159: "the images appear." - LayoutText {#text} at (463,176) size 5x17 - text run at (463,176) width 5: " " - LayoutImage {IMG} at (467.34,141) size 27x90 + LayoutText {#text} at (304,171) size 159x24 + text run at (304,171) width 159: "the images appear." + LayoutText {#text} at (462,176) size 5x17 + text run at (462,176) width 5: " " + LayoutImage {IMG} at (466.84,141) size 27x90 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (4,1752.31) size 747x54 LayoutText {#text} at (0,0) size 723x53 @@ -687,31 +687,31 @@ text run at (70,47) width 78: "all of which" LayoutText {#text} at (147,47) size 5x17 text run at (147,47) width 5: " " - LayoutImage {IMG} at (151.30,47) size 20x65 - LayoutText {#text} at (171,47) size 5x17 - text run at (171,47) width 5: " " + LayoutImage {IMG} at (151.30,47) size 19.50x65 + LayoutText {#text} at (170,47) size 5x17 + text run at (170,47) width 5: " " LayoutInline {SPAN} at (0,0) size 114x17 - LayoutText {#text} at (175,47) size 114x17 - text run at (175,47) width 114: "should be aligned" - LayoutText {#text} at (288,47) size 5x17 - text run at (288,47) width 5: " " - LayoutImage {IMG} at (292.16,47) size 11x35 - LayoutText {#text} at (303,47) size 5x17 - text run at (303,47) width 5: " " + LayoutText {#text} at (174,47) size 114x17 + text run at (174,47) width 114: "should be aligned" + LayoutText {#text} at (287,47) size 5x17 + text run at (287,47) width 5: " " + LayoutImage {IMG} at (291.66,47) size 10.50x35 + LayoutText {#text} at (302,47) size 5x17 + text run at (302,47) width 5: " " LayoutInline {SPAN} at (0,0) size 188x36 - LayoutText {#text} at (307,47) size 188x36 - text run at (307,47) width 188: "with the top of" - LayoutText {#text} at (494,47) size 5x17 - text run at (494,47) width 5: " " - LayoutImage {IMG} at (498.69,47) size 15x50 - LayoutText {#text} at (513,47) size 5x17 - text run at (513,47) width 5: " " + LayoutText {#text} at (306,47) size 188x36 + text run at (306,47) width 188: "with the top of" + LayoutText {#text} at (493,47) size 5x17 + text run at (493,47) width 5: " " + LayoutImage {IMG} at (497.69,47) size 15x50 + LayoutText {#text} at (512,47) size 5x17 + text run at (512,47) width 5: " " LayoutInline {SPAN} at (0,0) size 134x17 - LayoutText {#text} at (517,47) size 134x17 - text run at (517,47) width 134: "the tallest element in" - LayoutText {#text} at (650,47) size 5x17 - text run at (650,47) width 5: " " - LayoutImage {IMG} at (654.53,47) size 5x15 + LayoutText {#text} at (516,47) size 134x17 + text run at (516,47) width 134: "the tallest element in" + LayoutText {#text} at (649,47) size 5x17 + text run at (649,47) width 5: " " + LayoutImage {IMG} at (653.53,47) size 4.50x15 LayoutText {#text} at (0,0) size 0x0 LayoutInline {BIG} at (0,0) size 305x24 LayoutText {#text} at (0,112) size 305x24
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/floating-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/floating-replaced-height-008-expected.txt index 1423cdd..54eda57 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/floating-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/floating-replaced-height-008-expected.txt
@@ -1,7 +1,7 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 layer at (0,0) size 800x186 - LayoutBlockFlow {HTML} at (0,0) size 800x186 + LayoutBlockFlow {HTML} at (0,0) size 800x185.59 LayoutBlockFlow {BODY} at (8,16) size 784x36 LayoutBlockFlow {P} at (0,0) size 784x36 LayoutText {#text} at (0,0) size 108x17 @@ -22,12 +22,12 @@ LayoutText {#text} at (58,18) size 455x17 text run at (58,18) width 455: " to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,52) size 784x0 - LayoutImage (floating) {IMG} at (0,0) size 117.59x118 + LayoutImage (floating) {IMG} at (0,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (117.59,0) size 5x5 - LayoutImage (floating) {IMG} at (122.59,0) size 117.59x118 + LayoutImage (floating) {IMG} at (122.59,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (240.19,0) size 5x5 - LayoutImage (floating) {IMG} at (245.19,0) size 117.59x118 + LayoutImage (floating) {IMG} at (245.19,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (362.78,0) size 5x5 - LayoutImage (floating) {IMG} at (367.78,0) size 117.59x118 + LayoutImage (floating) {IMG} at (367.78,0) size 117.59x117.59 LayoutImage (floating) {IMG} at (485.38,0) size 5x5 - LayoutImage (floating) {IMG} at (490.38,0) size 117.59x118 + LayoutImage (floating) {IMG} at (490.38,0) size 117.59x117.59
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-block-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-block-replaced-height-008-expected.txt index c2fe9b4b..7ec1c85 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-block-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-block-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (58,18) size 455x17 text run at (58,18) width 455: " to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,52) size 784x122 - LayoutImage {OBJECT} at (0,0) size 117.59x118 + LayoutImage {OBJECT} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,104) size 5x17 text run at (117,104) width 5: " " - LayoutImage {OBJECT} at (121.59,0) size 117.59x118 + LayoutImage {OBJECT} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,104) size 5x17 text run at (239,104) width 5: " " - LayoutImage {OBJECT} at (243.19,0) size 117.59x118 + LayoutImage {OBJECT} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,104) size 5x17 text run at (360,104) width 5: " " - LayoutImage {OBJECT} at (364.78,0) size 117.59x118 + LayoutImage {OBJECT} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,104) size 5x17 text run at (482,104) width 5: " " - LayoutImage {OBJECT} at (486.38,0) size 117.59x118 + LayoutImage {OBJECT} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-replaced-height-008-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-replaced-height-008-expected.txt index b0c2d36..83dd3a1d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-replaced-height-008-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/inline-replaced-height-008-expected.txt
@@ -22,17 +22,17 @@ LayoutText {#text} at (58,18) size 455x17 text run at (58,18) width 455: " to each other. This should still remain true even after a window resize." LayoutBlockFlow {DIV} at (0,52) size 784x122 - LayoutImage {IMG} at (0,0) size 117.59x118 + LayoutImage {IMG} at (0,0) size 117.59x117.59 LayoutText {#text} at (117,104) size 5x17 text run at (117,104) width 5: " " - LayoutImage {IMG} at (121.59,0) size 117.59x118 + LayoutImage {IMG} at (121.59,0) size 117.59x117.59 LayoutText {#text} at (239,104) size 5x17 text run at (239,104) width 5: " " - LayoutImage {IMG} at (243.19,0) size 117.59x118 + LayoutImage {IMG} at (243.19,0) size 117.59x117.59 LayoutText {#text} at (360,104) size 5x17 text run at (360,104) width 5: " " - LayoutImage {IMG} at (364.78,0) size 117.59x118 + LayoutImage {IMG} at (364.78,0) size 117.59x117.59 LayoutText {#text} at (482,104) size 5x17 text run at (482,104) width 5: " " - LayoutImage {IMG} at (486.38,0) size 117.59x118 + LayoutImage {IMG} at (486.38,0) size 117.59x117.59 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt index d59c948..11b025e5 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/t0804-c5509-padn-l-03-f-g-expected.txt
@@ -10,6 +10,6 @@ LayoutText {#text} at (32,0) size 39x17 text run at (32,0) width 39: "TEST" LayoutBlockFlow {DIV} at (16,68) size 192x18 [color=#FFFF00] [bgcolor=#000080] - LayoutImage {IMG} at (0,13) size 32x1.59 + LayoutImage {IMG} at (0,12) size 32x1.59 LayoutText {#text} at (32,0) size 39x17 text run at (32,0) width 39: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.png b/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.png index 13bdac7..dd054bb 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.txt index a38fa66d..ce6fb3a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/t090501-c414-flt-03-b-g-expected.txt
@@ -7,10 +7,10 @@ LayoutText {#text} at (0,2) size 406x17 text run at (0,2) width 252: "In the following test, the purple square " text run at (251,2) width 155: "should be on the left (\x{21E6}" - LayoutImage {IMG} at (405.20,0) size 19x19.19 + LayoutImage {IMG} at (405.20,0) size 19.19x19.19 LayoutText {#text} at (424,2) size 221x17 text run at (424,2) width 221: "), and the teal square on the right (" - LayoutImage {IMG} at (644.13,0) size 19x19.19 + LayoutImage {IMG} at (644.31,0) size 19.19x19.19 LayoutText {#text} at (663,2) size 755x37 text run at (663,2) width 92: "\x{21E8}) of the blue" text run at (0,22) width 63: "rectangle."
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png index a721151..5059e232 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index 02d4f8f6..9e0f9fe 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.png index e4d13ffd..c2d70f8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.txt index 3532725a9..bc0fb5f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.png index e4d13ffd..c2d70f8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.txt index 076d991..9726453 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.png index e4d13ffd..c2d70f8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.txt index 3532725a9..bc0fb5f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.png index e4d13ffd..c2d70f8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.txt index b82e501..ea8d976 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-details-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.png index c046aab3..97c0a45 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.txt index e5939aef..8878c10 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.png index fa19dc3..30d16e9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.txt index d159c75..6225d6d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.png index 7907c772..05c613ae 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.txt index 6df73f9..09bbe8d0 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.png index 0c88a858..ec5818d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.txt index b947307..5188359e 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-10-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.png index ce2b763..1455c26 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.txt index 3784da13..267038fbe 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.png index fa19dc3..30d16e9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.txt index d159c75..6225d6d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.png index a5ff0cd..b8d63d6d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.txt index 326c3e5b..8ecb4989 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 2" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.png index 321bd54..88f7b4a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.txt index bf8c112..3aeb0749 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 2"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.png index 95b30823..b8808f84 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.txt index 23306d7..5441672 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.png index 9e82de4..13e63e1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.txt index 0fe7e706..9236b82e2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-4-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.png index dea6de2..f07b573 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.txt index e739748..ebe25f52f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x36 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 800x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.png index fa19dc3..30d16e9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.txt index d159c75..6225d6d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.png index 7907c772..05c613ae 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.txt index 9ccb545..d1b70f2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.png index d1bf0ffb..acda4eb 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.txt index 4333717..2c099bd 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.png index 7907c772..05c613ae 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.txt index 9ccb545..d1b70f2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.png index fafc50c..659d805d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.txt index e6b31ac4..2d474d9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-7-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 1" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.png index cc9e506..8ec96999 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.txt index 1a7e274..8da86043 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.png index 450d4796..bf6384d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.txt index 7c928421..55abb3f4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-8-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 40x17 text run at (16,0) width 40: "new 2" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.png index f646ae5..b911a9f2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.txt index 674a9891..08a182a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.png index 51eeda7..40d702b 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.txt index cde9838..5aa67fd8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-9-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.png index 93b95143..3a596d7 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.txt index f89f5bfd..7f4aa27 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x17 text run at (16,0) width 65: "summary " LayoutInline {B} at (0,0) size 145x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.png index 93b95143..3a596d7 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.txt index e4d4add..6afbf0f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-add-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x17 text run at (16,0) width 65: "summary " LayoutInline {SPAN} at (0,0) size 145x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.png index 40ba22a1..d7f255c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.txt index af12fb5..876a7d52 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-marker-style-expected.txt
@@ -1,27 +1,27 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x473 - LayoutBlockFlow {HTML} at (0,0) size 800x472.70 - LayoutBlockFlow {BODY} at (8,8) size 784x456.70 - LayoutBlockFlow {DIV} at (0,0) size 784x85 - LayoutBlockFlow {DETAILS} at (0,0) size 784x85 - LayoutBlockFlow {SUMMARY} at (0,0) size 784x85 +layer at (0,0) size 800x474 + LayoutBlockFlow {HTML} at (0,0) size 800x473.70 + LayoutBlockFlow {BODY} at (8,8) size 784x457.70 + LayoutBlockFlow {DIV} at (0,0) size 784x86 + LayoutBlockFlow {DETAILS} at (0,0) size 784x86 + LayoutBlockFlow {SUMMARY} at (0,0) size 784x86 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: right - LayoutText {#text} at (121,58) size 94x26 - text run at (121,58) width 94: "Summary" - LayoutBlockFlow {DIV} at (0,85) size 117x182.77 - LayoutBlockFlow {DETAILS} at (0,0) size 117x182.77 - LayoutBlockFlow {SUMMARY} at (0,0) size 117x182.77 + LayoutText {#text} at (121,59) size 94x26 + text run at (121,59) width 94: "Summary" + LayoutBlockFlow {DIV} at (0,86) size 118x182.77 + LayoutBlockFlow {DETAILS} at (0,0) size 118x182.77 + LayoutBlockFlow {SUMMARY} at (0,0) size 118x182.77 LayoutDetailsMarker {DIV} at (0,0) size 111.83x79.83 [border: (8px solid #00FF00) (16px solid #00FF00) (24px solid #00FF00) (32px solid #00FF00)]: down - LayoutText {#text} at (90,89) size 26x94 - text run at (90,89) width 94: "Summary" - LayoutBlockFlow {DIV} at (0,267.77) size 784x46 + LayoutText {#text} at (91,89) size 26x94 + text run at (91,89) width 94: "Summary" + LayoutBlockFlow {DIV} at (0,268.77) size 784x46 LayoutBlockFlow {DETAILS} at (0,0) size 784x46 LayoutBlockFlow {SUMMARY} at (0,0) size 784x46 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: right LayoutText {#text} at (73,19) size 94x26 text run at (73,19) width 94: "Summary" - LayoutBlockFlow {DIV} at (0,313.77) size 70x142.94 + LayoutBlockFlow {DIV} at (0,314.77) size 70x142.94 LayoutBlockFlow {DETAILS} at (0,0) size 70x142.94 LayoutBlockFlow {SUMMARY} at (0,0) size 70x142.94 LayoutDetailsMarker {DIV} at (0,0) size 64x40 [border: (8px solid #00FF00)]: down
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.png index 2c66179..9a93355a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.txt index 23ab241..a6ea7845f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-1-expected.txt
@@ -6,13 +6,13 @@ LayoutBlockFlow {DETAILS} at (0,0) size 784x136 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x102 [border: (8px solid #9999CC)] LayoutBlockFlow (anonymous) at (8,8) size 752x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x17 text run at (16,0) width 5: " " text run at (20,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (8,26) size 752x68 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 736x34 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,11) size 10.55x10.55: down LayoutText {#text} at (24,8) size 292x17 text run at (24,8) width 5: " " text run at (28,8) width 288: "nested summary (summary-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.png index 52da2dc..1bef2bec 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.txt index 2742198..2f7cff3 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-nested-2-expected.txt
@@ -5,14 +5,14 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x136 [border: (8px solid #555599)] LayoutBlockFlow {SUMMARY} at (8,8) size 768x34 [border: (8px solid #9999CC)] - LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,11) size 10.55x10.55: down LayoutText {#text} at (24,8) size 65x17 text run at (24,8) width 5: " " text run at (28,8) width 61: "summary" LayoutBlockFlow {DIV} at (8,42) size 768x86 LayoutBlockFlow {DETAILS} at (0,0) size 768x68 [border: (8px solid #995555)] LayoutBlockFlow {SUMMARY} at (8,8) size 752x34 [border: (8px solid #CC9999)] - LayoutDetailsMarker {DIV} at (8,12) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (8,11) size 10.55x10.55: down LayoutText {#text} at (24,8) size 274x17 text run at (24,8) width 5: " " text run at (28,8) width 270: "nested summary (details-deails-summary)"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.png index d7c6b88..251ef45 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.txt index 3dfdfaf..f3ebe106 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.png index 2083f9c..51889962 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.txt index e2a1871..bff96eb 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.png index d7c6b88..251ef45 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.txt index 3dfdfaf..f3ebe106 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.png index 1539dd3..3e51750 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.txt index 7ded310..8e15542 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-no-summary4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.png index b5787c66..ee00bb77 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.txt index 539a9222..28e4de3 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open-javascript-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 51x17 text run at (16,0) width 51: "details1" LayoutBlockFlow {DIV} at (0,18) size 784x22 @@ -13,7 +13,7 @@ LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {DETAILS} at (0,40) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 51x17 text run at (16,0) width 51: "details2" layer at (10,29) size 169x16
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.png index 9e82de4..13e63e1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.txt index 0fe7e706..9236b82e2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.png index 087ae95..0823217 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.txt index 289bf91..433293d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.png index 9e82de4..13e63e1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.txt index 0fe7e706..9236b82e2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.png index 087ae95..0823217 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.txt index b1d10fd..9d918f0 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x40 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x22
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.png index 9e82de4..13e63e1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.txt index 0fe7e706..9236b82e2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open5-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.png index 9b4de4c..fa894db 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.txt index cea2602d..d478a14 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-open6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.png index 78a5a33..bbd97a61 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.txt index 5d4646b..e00737b 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-position-expected.txt
@@ -8,16 +8,16 @@ LayoutBlockFlow {DETAILS} at (0,18) size 784x0 layer at (50,150) size 50x18 LayoutBlockFlow (positioned) {SUMMARY} at (50,150) size 49.83x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 34x17 text run at (16,0) width 34: "fixed" layer at (158,158) size 784x18 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600 LayoutBlockFlow (relative positioned) {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 49x17 text run at (16,0) width 49: "relative" layer at (250,150) size 70x18 LayoutBlockFlow (positioned) {SUMMARY} at (250,150) size 70.28x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 55x17 text run at (16,0) width 55: "absolute"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.png index 170c899..f6e479b 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.txt index 53eec84..a31535d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.png index 170c899..f6e479b 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.txt index 93cff07d..546d626 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.png index ca53053..91007b5 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.txt index 61279e7..a00e807e 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.png index d7c6b88..251ef45 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.txt index 3dfdfaf..f3ebe106 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-1-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.png index 5a65fde7..88b0ad7 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.txt index 736ab69a..079411d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 2" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.png index 02ac6d1..678aab4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.txt index d8ac8a8..91820c8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-2-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 2"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.png index 7ce0cc5..2f1d660 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.txt index 24fcd800..c3b2463 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 1" LayoutBlockFlow {DIV} at (0,18) size 800x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.png index ca193b1..d454c43 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.txt index 0e7e9da..b252b85b 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-3-expected.txt
@@ -5,6 +5,6 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 1"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.png index 6983552..456b756 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.txt index 3ff47150..7893067 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 0 {SUMMARY} of child 0 {CONTENT} of {#document-fragment} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.png index 2083f9c..51889962 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.txt index e2a1871..bff96eb 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-4-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 47x17 text run at (16,0) width 47: "Details" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.png index 6fb16db9..8fec370 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.txt index 4676cb7..9748707 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 2" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 2 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.png index a9fbb95..36263e8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.txt index 14f1152..809bcb94 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-5-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 2" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.png index 3188165..c68d7ab 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.txt index 9fda682..cbf54ad 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-and-click-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (0,0) size 800x600 LayoutBlockFlow {DETAILS} at (0,0) size 800x18 LayoutBlockFlow {SUMMARY} at (0,0) size 800x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 1" caret: position 0 of child 0 {DIV} of {#document-fragment} of child 1 {SUMMARY} of child 1 {DETAILS} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.png index e676ad9..17ce47a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.txt index e25be63..43bc31f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-6-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 73x17 text run at (16,0) width 73: "summary 1" LayoutBlockFlow {DIV} at (0,18) size 784x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.png index 300f4703..3ecddf62 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.txt index 5a8aac6..91a892a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-1-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x17 text run at (16,0) width 65: "summary " LayoutText {#text} at (80,0) size 190x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.png index 16ae0781..4b4a109 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.txt index dfa5b05d..7ae6b10 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-remove-summary-child-2-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 65x17 text run at (16,0) width 65: "summary " LayoutInline {SPAN} at (0,0) size 185x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.png index 8b694e4..f4093a0 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.txt index 5de2ad7b..430a169c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-summary-child-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x18 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 5x17 text run at (16,0) width 5: " " LayoutBlockFlow {SPAN} at (20.94,2) size 64x16
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.png index e0f912d..ac87119 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.txt index 520fd2d..4239e2a8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-replace-text-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x584 LayoutBlockFlow {DETAILS} at (0,0) size 784x36 LayoutBlockFlow {SUMMARY} at (0,0) size 784x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 64x17 text run at (16,0) width 64: "Summary" LayoutBlockFlow {DIV} at (0,18) size 784x18
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.png index ce4ff305..f841cee 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.txt index 10b5847a4..59b4114 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-center-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (21.75,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (21.75,3) size 10.55x10.55: right LayoutText {#text} at (38,0) size 61x17 text run at (38,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (21.75,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (21.75,3) size 10.55x10.55: down LayoutText {#text} at (38,0) size 61x17 text run at (38,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,21.75) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3.45,21.75) size 10.55x10.55: down LayoutText {#text} at (0,38) size 17x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,21.75) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,21.75) size 10.55x10.55: right LayoutText {#text} at (0,38) size 17x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,21.75) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,21.75) size 10.55x10.55: down LayoutText {#text} at (0,38) size 17x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,21.75) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,21.75) size 10.55x10.55: left LayoutText {#text} at (0,38) size 17x61 text run at (0,38) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (87.69,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (87.69,3) size 10.55x10.55: left LayoutText {#text} at (21,0) size 61x17 text run at (21,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (87.69,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (87.69,3) size 10.55x10.55: down LayoutText {#text} at (21,0) size 61x17 text run at (21,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,87.69) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3.45,87.69) size 10.55x10.55: up LayoutText {#text} at (0,21) size 17x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,87.69) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,87.69) size 10.55x10.55: right LayoutText {#text} at (0,21) size 17x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,87.69) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,87.69) size 10.55x10.55: up LayoutText {#text} at (0,21) size 17x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,87.69) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,87.69) size 10.55x10.55: left LayoutText {#text} at (0,21) size 17x61 text run at (0,21) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.png index bedbf645..8f6e3d4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.txt index f3f6d3c..63665e8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-left-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (65.94,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (65.94,3) size 10.55x10.55: left LayoutText {#text} at (0,0) size 60x17 text run at (0,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (65.94,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (65.94,3) size 10.55x10.55: down LayoutText {#text} at (0,0) size 60x17 text run at (0,0) width 60: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,65.94) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3.45,65.94) size 10.55x10.55: up LayoutText {#text} at (0,0) size 17x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,65.94) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,65.94) size 10.55x10.55: right LayoutText {#text} at (0,0) size 17x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,65.94) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,65.94) size 10.55x10.55: up LayoutText {#text} at (0,0) size 17x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,65.94) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,65.94) size 10.55x10.55: left LayoutText {#text} at (0,0) size 17x60 text run at (0,0) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.png index a3105f65..08946e66 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.txt index 4d29585..cb3b8ede 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-align-right-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (43.52,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (43.52,3) size 10.55x10.55: right LayoutText {#text} at (60,0) size 60x17 text run at (60,0) width 60: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (43.52,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (43.52,3) size 10.55x10.55: down LayoutText {#text} at (60,0) size 60x17 text run at (60,0) width 60: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,43.52) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3.45,43.52) size 10.55x10.55: down LayoutText {#text} at (0,60) size 17x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,43.52) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,43.52) size 10.55x10.55: right LayoutText {#text} at (0,60) size 17x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,43.52) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,43.52) size 10.55x10.55: down LayoutText {#text} at (0,60) size 17x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,43.52) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,43.52) size 10.55x10.55: left LayoutText {#text} at (0,60) size 17x60 text run at (0,60) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: left LayoutText {#text} at (43,0) size 61x17 text run at (43,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: down LayoutText {#text} at (43,0) size 61x17 text run at (43,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: left LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.png index e124977..a4df3a9 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.txt index b3781686..66295c463 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/html/details-writing-mode-expected.txt
@@ -37,12 +37,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: right LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (0,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (0,3) size 10.55x10.55: down LayoutText {#text} at (16,0) size 61x17 text run at (16,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -50,12 +50,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,0) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,0) size 10.55x10.55: right LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -63,12 +63,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: down LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,0) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,0) size 10.55x10.55: left LayoutText {#text} at (0,16) size 17x61 text run at (0,16) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -80,12 +80,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: left LayoutText {#text} at (43,0) size 61x17 text run at (43,0) width 61: "summary" LayoutBlockFlow {DETAILS} at (0,18) size 120x18 LayoutBlockFlow {SUMMARY} at (0,0) size 120x18 - LayoutDetailsMarker {DIV} at (109.45,4) size 10.55x10.55: down + LayoutDetailsMarker {DIV} at (109.45,3) size 10.55x10.55: down LayoutText {#text} at (43,0) size 61x17 text run at (43,0) width 61: "summary" LayoutBlockFlow {DIV} at (0,18) size 120x0 @@ -93,12 +93,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (2.45,109.45) size 10.55x10.55: right + LayoutDetailsMarker {DIV} at (3.45,109.45) size 10.55x10.55: right LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120 @@ -106,12 +106,12 @@ LayoutBlockFlow {DIV} at (2,2) size 120x120 LayoutBlockFlow {DETAILS} at (0,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: up + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: up LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DETAILS} at (18,0) size 18x120 LayoutBlockFlow {SUMMARY} at (0,0) size 18x120 - LayoutDetailsMarker {DIV} at (4,109.45) size 10.55x10.55: left + LayoutDetailsMarker {DIV} at (3,109.45) size 10.55x10.55: left LayoutText {#text} at (0,43) size 17x61 text run at (0,43) width 60: "summary" LayoutBlockFlow {DIV} at (18,0) size 0x120
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/repaint/details-open-repaint-expected.txt index b955518..e1a12f91 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [10, 71, 169, 16], [8, 68, 784, 22], [8, 68, 173, 22], - [8, 54, 11, 11] + [8, 53, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/width100percent-image-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/width100percent-image-expected.txt index d357f5f..3eed87f4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/replaced/width100percent-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/replaced/width100percent-image-expected.txt
@@ -27,11 +27,11 @@ LayoutTableSection {TBODY} at (0,0) size 784x279 LayoutTableRow {TR} at (0,1) size 784x277 LayoutTableCell {TD} at (1,1) size 216x277 [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (218,1) size 216x277 [r=0 c=1 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (435,1) size 216x277 [r=0 c=2 rs=1 cs=1] - LayoutImage {IMG} at (1,1) size 214x275 + LayoutImage {IMG} at (1,1) size 213.98x275 LayoutTableCell {TD} at (652,129) size 131x20 [r=0 c=3 rs=1 cs=1] LayoutText {#text} at (1,1) size 4x17 text run at (1,1) width 4: " "
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.png index 2d54ec2..5afb908 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt index bf305e52..bd360bc 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt
@@ -1,77 +1,77 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1236 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1240 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1236 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {HTML} at (0,0) size 785x1236 - LayoutBlockFlow {BODY} at (8,8) size 769x1220 +layer at (0,0) size 785x1240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {HTML} at (0,0) size 785x1240 + LayoutBlockFlow {BODY} at (8,8) size 769x1224 LayoutText {#text} at (0,0) size 290x17 text run at (0,0) width 290: "LTR fieldset with left/center/right text-align: " LayoutBR {BR} at (289,14) size 1x0 LayoutFieldset {FIELDSET} at (16,34) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (34,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,155) size 4x17 - text run at (260,155) width 4: " " + LayoutText {#text} at (260,156) size 4x17 + text run at (260,156) width 4: " " LayoutFieldset {FIELDSET} at (280,34) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (110,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,189) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,190) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,310) size 4x17 - text run at (260,310) width 4: " " + LayoutText {#text} at (260,312) size 4x17 + text run at (260,312) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,328) size 291x17 - text run at (0,328) width 291: "RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (290,342) size 1x0 - LayoutFieldset {FIELDSET} at (16,362) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,330) size 291x17 + text run at (0,330) width 291: "RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (290,344) size 1x0 + LayoutFieldset {FIELDSET} at (16,364) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (14,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,483) size 4x17 - text run at (260,483) width 4: " " - LayoutFieldset {FIELDSET} at (280,362) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (260,486) size 4x17 + text run at (260,486) width 4: " " + LayoutFieldset {FIELDSET} at (280,364) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (90,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0 - LayoutFieldset {FIELDSET} at (16,517) size 228x119.59 [border: (2px groove #C0C0C0)] + LayoutFieldset {FIELDSET} at (16,520) size 228x119.59 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (62,0) size 104x25 LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500] - LayoutText {#text} at (260,638) size 4x17 - text run at (260,638) width 4: " " + LayoutText {#text} at (260,642) size 4x17 + text run at (260,642) width 4: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,656) size 344x17 - text run at (0,656) width 344: "Vertical LTR fieldset with left/center/right text-align: " - LayoutBR {BR} at (343,670) size 1x0 - LayoutFieldset {FIELDSET} at (16,690) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,660) size 344x17 + text run at (0,660) width 344: "Vertical LTR fieldset with left/center/right text-align: " + LayoutBR {BR} at (343,674) size 1x0 + LayoutFieldset {FIELDSET} at (16,694) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,34) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,920) size 5x17 - text run at (151,920) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,690) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,924) size 5x17 + text run at (151,924) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,694) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,110) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,920) size 5x17 - text run at (307,920) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,690) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,924) size 5x17 + text run at (307,924) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,694) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (462,920) size 5x17 - text run at (462,920) width 5: " " + LayoutText {#text} at (462,924) size 5x17 + text run at (462,924) width 5: " " LayoutBR {BR} at (0,0) size 0x0 - LayoutText {#text} at (0,938) size 344x17 - text run at (0,938) width 344: "Vertical RTL fieldset with left/center/right text-align: " - LayoutBR {BR} at (343,952) size 1x0 - LayoutFieldset {FIELDSET} at (16,972) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (0,942) size 344x17 + text run at (0,942) width 344: "Vertical RTL fieldset with left/center/right text-align: " + LayoutBR {BR} at (343,956) size 1x0 + LayoutFieldset {FIELDSET} at (16,976) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,14) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (151,1202) size 5x17 - text run at (151,1202) width 5: " " - LayoutFieldset {FIELDSET} at (171.59,972) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (151,1206) size 5x17 + text run at (151,1206) width 5: " " + LayoutFieldset {FIELDSET} at (171.59,976) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,90) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] - LayoutText {#text} at (307,1202) size 5x17 - text run at (307,1202) width 5: " " - LayoutFieldset {FIELDSET} at (327.19,972) size 119.59x228 [border: (2px groove #C0C0C0)] + LayoutText {#text} at (307,1206) size 5x17 + text run at (307,1206) width 5: " " + LayoutFieldset {FIELDSET} at (327.19,976) size 119.59x228 [border: (2px groove #C0C0C0)] LayoutBlockFlow {LEGEND} at (0,62) size 25x104 LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500] LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt index 8e7707e..34f1f4d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/object-embedding-svg-delayed-size-negotiation-2-expected.txt
@@ -26,7 +26,7 @@ LayoutTableCell {TD} at (0,0) size 135x44 [r=0 c=0 rs=1 cs=1] LayoutBlockFlow (floating) {P} at (1,449) size 135x9 layer at (14,131) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -34,7 +34,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,194) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -42,7 +42,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,257) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -50,7 +50,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,355) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25 @@ -58,7 +58,7 @@ LayoutSVGRect {rect} at (0,0) size 99x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (14,4) size 71x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (14,453) size 117x43 - LayoutEmbeddedObject {OBJECT} at (9,0) size 117x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9,0) size 117x42.75 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 99x25 LayoutView at (0,0) size 99x25 layer at (0,0) size 99x25
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-aspect-ratio-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-aspect-ratio-expected.txt index 84e7c562..11abe702 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-aspect-ratio-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-aspect-ratio-expected.txt
@@ -18,13 +18,13 @@ layer at (8,282) size 160x120 LayoutVideo {VIDEO} at (0,240) size 160x120 layer at (168,282) size 160x120 - LayoutVideo {VIDEO} at (0,0) size 160x120 + LayoutVideo {VIDEO} at (0,0) size 160x119.98 layer at (328,282) size 160x120 LayoutVideo {VIDEO} at (160,0) size 160x120 layer at (8,402) size 160x120 LayoutVideo {VIDEO} at (0,0) size 160x120 layer at (168,402) size 160x120 - LayoutVideo {VIDEO} at (160,0) size 160x120 + LayoutVideo {VIDEO} at (160,0) size 160x119.98 layer at (8,42) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -36,10 +36,10 @@ layer at (8,282) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (168,282) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (168,282) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98 layer at (328,282) size 160x120 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 LayoutBlockFlow {DIV} at (0,85) size 160x35 @@ -51,10 +51,10 @@ layer at (8,402) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (168,402) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (168,402) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98 layer at (328,402) size 320x120 LayoutBlockFlow (relative positioned) {DIV} at (320,360) size 320x120 layer at (328,402) size 160x120 @@ -65,9 +65,9 @@ layer at (328,402) size 160x85 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 layer at (488,402) size 160x120 - LayoutVideo (positioned) {VIDEO} at (160,0) size 160x120 + LayoutVideo (positioned) {VIDEO} at (160,0) size 160x119.98 layer at (488,402) size 160x120 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x120 - LayoutBlockFlow {DIV} at (0,85) size 160x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x119.98 + LayoutBlockFlow {DIV} at (0,84.98) size 160x35 layer at (488,402) size 160x85 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x85 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 160x84.98
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv420-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv420-expected.txt index ccc435e..2556ea378c 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv420-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv420-expected.txt
@@ -8,9 +8,9 @@ text run at (0,0) width 333: "Test correct colorspace for yuv420, i.e. YU12 video" LayoutBlockFlow (anonymous) at (0,34) size 784x156 layer at (8,42) size 206x156 - LayoutVideo {VIDEO} at (0,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (0,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (11,45) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (11,45) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv422-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv422-expected.txt index b3755c30..76389f59 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv422-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-colorspace-yuv422-expected.txt
@@ -8,9 +8,9 @@ text run at (0,0) width 333: "Test correct colorspace for yuv422, i.e. YU16 video" LayoutBlockFlow (anonymous) at (0,34) size 784x156 layer at (8,42) size 206x156 - LayoutVideo {VIDEO} at (0,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (0,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (11,45) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (11,45) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-controls-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-controls-rendering-expected.txt index 3a6e954..e6deb8e 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-controls-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-controls-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,42) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,282) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,42) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -38,10 +38,10 @@ LayoutBlockFlow {DIV} at (12.42,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,282) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,282) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,487) size 310x30 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30 @@ -60,12 +60,12 @@ LayoutBlockFlow {DIV} at (12.42,0) size 24x24 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,522) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,522) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,522) size 320x239.98 layer at (8,522) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,522) size 320x205 backgroundClip at (8,522) size 320x78 clip at (8,522) size 320x78 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,727) size 310x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-controls-with-cast-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-controls-with-cast-rendering-expected.txt index efc639f..91a6ef1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-controls-with-cast-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-controls-with-cast-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,50) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,294) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,50) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -39,10 +39,10 @@ LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,294) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,294) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,499) size 310x30 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30 @@ -62,12 +62,12 @@ LayoutButton {INPUT} at (237,0) size 30x30 LayoutButton {INPUT} at (271,0) size 30x30 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,538) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,538) size 320x239.98 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,538) size 320x205 backgroundClip at (8,538) size 320x62 clip at (8,538) size 320x62 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (13,743) size 310x30 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 LayoutFlexibleBox (relative positioned) {DIV} at (5,0) size 310x30 [bgcolor=#141414CC] LayoutButton {INPUT} at (9,0) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-layer-crash-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-layer-crash-expected.txt index 9ac79c6..b4f1b6d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-layer-crash-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-layer-crash-expected.txt
@@ -17,16 +17,16 @@ text run at (0,320) width 4: " " LayoutBR {BR} at (210,334) size 0x0 layer at (12,60) size 206x156 - LayoutVideo {VIDEO} at (4,18) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,18) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,63) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,63) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,220) size 206x156 - LayoutVideo {VIDEO} at (4,178) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,178) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,223) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,223) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-dark-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-dark-rendering-expected.txt index 82091537..0f58331 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-dark-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-dark-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,50) size 320x240 LayoutVideo {VIDEO} at (0,0) size 320x240 layer at (8,294) size 320x240 - LayoutVideo {VIDEO} at (0,0) size 320x240 + LayoutVideo {VIDEO} at (0,0) size 320x239.98 layer at (8,50) size 320x240 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 LayoutBlockFlow {DIV} at (0,205) size 320x35 @@ -23,18 +23,18 @@ layer at (24,60) size 30x30 LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 layer at (8,294) size 320x240 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,294) size 320x205 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (24,304) size 30x30 - LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 + LayoutButton (positioned) {INPUT} at (16,10.23) size 30x30 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,538) size 320x240 + LayoutVideo (positioned) {VIDEO} at (8,538) size 320x239.98 layer at (8,538) size 320x240 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x240 - LayoutBlockFlow {DIV} at (0,205) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x239.98 + LayoutBlockFlow {DIV} at (0,204.98) size 320x35 layer at (8,538) size 320x205 backgroundClip at (8,538) size 320x62 clip at (8,538) size 320x62 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x205 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x204.98 layer at (24,548) size 30x30 - LayoutButton (positioned) {INPUT} at (16,10.25) size 30x30 + LayoutButton (positioned) {INPUT} at (16,10.23) size 30x30
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-light-rendering-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-light-rendering-expected.txt index cb7eef9..7020ed2b 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-light-rendering-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-overlay-cast-light-rendering-expected.txt
@@ -14,7 +14,7 @@ layer at (8,50) size 352x288 LayoutVideo {VIDEO} at (0,0) size 352x288 layer at (8,342) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo {VIDEO} at (0,0) size 320x262 + LayoutVideo {VIDEO} at (0,0) size 320x261.81 layer at (8,50) size 352x288 LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 352x288 LayoutBlockFlow {DIV} at (0,253) size 352x35 @@ -23,16 +23,16 @@ layer at (26,63) size 30x30 LayoutButton (positioned) {INPUT} at (17.59,12.64) size 30x30 layer at (8,342) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 - LayoutBlockFlow {DIV} at (0,227) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x261.81 + LayoutBlockFlow {DIV} at (0,226.81) size 320x35 layer at (8,342) size 320x227 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x226.81 layer at (24,353) size 30x30 - LayoutButton (positioned) {INPUT} at (16,11.34) size 30x30 + LayoutButton (positioned) {INPUT} at (16,11.33) size 30x30 layer at (8,608) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutVideo (positioned) {VIDEO} at (8,608) size 320x262 + LayoutVideo (positioned) {VIDEO} at (8,608) size 320x261.81 layer at (8,608) size 320x262 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x262 - LayoutBlockFlow {DIV} at (0,227) size 320x35 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x261.81 + LayoutBlockFlow {DIV} at (0,226.81) size 320x35 layer at (8,608) size 320x227 backgroundClip at (0,0) size 0x0 clip at (0,0) size 0x0 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x227 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x226.81
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/video-transformed-expected.txt b/third_party/WebKit/LayoutTests/platform/win/media/video-transformed-expected.txt index 84c1526..0d58d842 100644 --- a/third_party/WebKit/LayoutTests/platform/win/media/video-transformed-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/media/video-transformed-expected.txt
@@ -17,23 +17,23 @@ text run at (0,462) width 4: " " LayoutBR {BR} at (210,476) size 0x0 layer at (12,42) size 206x156 - LayoutVideo {VIDEO} at (4,0) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,0) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,45) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,45) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,202) size 206x156 - LayoutVideo {VIDEO} at (4,160) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,160) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,205) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,205) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98 layer at (12,362) size 206x156 - LayoutVideo {VIDEO} at (4,320) size 206x156 [border: (3px solid #FF0000)] + LayoutVideo {VIDEO} at (4,320) size 206x155.98 [border: (3px solid #FF0000)] layer at (15,365) size 200x150 - LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150 - LayoutBlockFlow {DIV} at (0,115) size 200x35 + LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x149.98 + LayoutBlockFlow {DIV} at (0,114.98) size 200x35 layer at (15,365) size 200x115 - LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x115 + LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 200x114.98
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.png index 0aeb814..7b62b74 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.txt index 4fd6e57..9e9a2bd6 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/rightsizing-grid-expected.txt
@@ -1,9 +1,9 @@ -layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1435 +layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1360 LayoutView at (0,0) size 800x600 -layer at (0,0) size 785x1435 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutBlockFlow {html} at (0,0) size 785x1435.14 +layer at (0,0) size 785x1360 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutBlockFlow {html} at (0,0) size 785x1360.38 LayoutBlockFlow {body} at (8,8) size 769x0 - LayoutBlockFlow (floating) {div} at (0,0) size 769x1427.14 + LayoutBlockFlow (floating) {div} at (0,0) size 769x1352.38 LayoutBlockFlow {h1} at (0,21.44) size 769x37 LayoutText {#text} at (0,0) size 443x36 text run at (0,0) width 443: "SVG grid with percentage width" @@ -11,52 +11,48 @@ LayoutText {#text} at (0,0) size 149x17 text run at (0,0) width 149: "WICD Core 1.0 #20-3" LayoutBlockFlow {p} at (0,119.14) size 769x0 - LayoutBlockFlow (floating) {div} at (256.30,128) size 256.30x448 + LayoutBlockFlow (floating) {div} at (256.30,128.16) size 256.30x448.52 LayoutBR {br} at (256,0) size 1x17 - LayoutBlockFlow (floating) {div} at (512.59,128) size 256.30x448 - LayoutBlockFlow (floating) {div} at (384.50,704) size 384.50x192 - LayoutBlockFlow (floating) {div} at (0,896) size 384.50x192 - LayoutBlockFlow (floating) {div} at (384.50,1024) size 384.50x128 - LayoutBlockFlow {p} at (0,119.14) size 769x594 [color=#FFFFFF] + LayoutBlockFlow (floating) {div} at (512.59,128.16) size 256.30x448.52 + LayoutBlockFlow (floating) {div} at (0,704.83) size 384.50x192.25 + LayoutBlockFlow (floating) {div} at (384.50,704.83) size 384.50x192.25 + LayoutBlockFlow (floating) {div} at (384.50,897.08) size 384.50x128.16 + LayoutBlockFlow {p} at (0,119.14) size 769x1043.23 [color=#FFFFFF] LayoutBR {br} at (769,0) size 0x17 - LayoutText {#text} at (640,576) size 9x17 - text run at (640,576) width 9: ".." - LayoutBlockFlow {p} at (0,729.14) size 769x578 - LayoutText {#text} at (640,0) size 763x577 - text run at (640,0) width 123: "Above, you should" - text run at (640,18) width 100: "see a grid of 17" - text run at (640,36) width 69: "SVG child" - text run at (640,54) width 107: "elements sticked" - text run at (640,72) width 107: "together to build" - text run at (0,542) width 124: "one rectangle grid. " - text run at (123,542) width 622: "You should be able to resize your browser window and the grid rendering should adjust to it. The" - text run at (0,560) width 352: "outcome should look like in these sample screenshots: " + LayoutText {#text} at (0,1025) size 8x17 + text run at (0,1025) width 8: ".." + LayoutBlockFlow {p} at (0,1178.38) size 769x54 + LayoutText {#text} at (0,0) size 766x53 + text run at (0,0) width 644: "Above, you should see a grid of 17 SVG child elements sticked together to build one rectangle grid. " + text run at (643,0) width 123: "You should be able" + text run at (0,18) width 764: "to resize your browser window and the grid rendering should adjust to it. The outcome should look like in these sample" + text run at (0,36) width 83: "screenshots: " LayoutInline {a} at (0,0) size 35x17 [color=#0000EE] - LayoutText {#text} at (351,560) size 35x17 - text run at (351,560) width 35: "small" - LayoutText {#text} at (385,560) size 9x17 - text run at (385,560) width 9: ", " + LayoutText {#text} at (82,36) size 35x17 + text run at (82,36) width 35: "small" + LayoutText {#text} at (116,36) size 9x17 + text run at (116,36) width 9: ", " LayoutInline {a} at (0,0) size 42x17 [color=#0000EE] - LayoutText {#text} at (393,560) size 42x17 - text run at (393,560) width 42: "bigger" - LayoutText {#text} at (434,560) size 32x17 - text run at (434,560) width 32: " and " + LayoutText {#text} at (124,36) size 42x17 + text run at (124,36) width 42: "bigger" + LayoutText {#text} at (165,36) size 32x17 + text run at (165,36) width 32: " and " LayoutInline {a} at (0,0) size 22x17 [color=#0000EE] - LayoutText {#text} at (465,560) size 22x17 - text run at (465,560) width 22: "big" - LayoutText {#text} at (486,560) size 5x17 - text run at (486,560) width 5: "." - LayoutBlockFlow {p} at (0,1323.14) size 769x36 + LayoutText {#text} at (196,36) size 22x17 + text run at (196,36) width 22: "big" + LayoutText {#text} at (217,36) size 5x17 + text run at (217,36) width 5: "." + LayoutBlockFlow {p} at (0,1248.38) size 769x36 LayoutText {#text} at (0,0) size 762x35 text run at (0,0) width 762: "The test is successful, if all SVG elements resize exacly dependend on the width of the browser window, but keep their" text run at (0,18) width 636: "aspect ratio and relative position. The complete grid should always show a perfect rectangle object." - LayoutBlockFlow {p} at (0,1375.14) size 769x36 + LayoutBlockFlow {p} at (0,1300.38) size 769x36 LayoutBR {br} at (0,0) size 0x17 LayoutInline {a} at (0,0) size 33x17 [color=#0000EE] LayoutText {#text} at (0,18) size 33x17 text run at (0,18) width 33: "Back" layer at (8,127) size 385x128 - LayoutEmbeddedObject (floating) {object} at (0,0) size 384.50x128 + LayoutEmbeddedObject (floating) {object} at (0,0) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -70,7 +66,7 @@ LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (52.78,27.00) startOffset 0 endOffset 1 width 14.44: "A" layer at (393,127) size 384x128 - LayoutEmbeddedObject (floating) {object} at (384.50,0) size 384.50x128 + LayoutEmbeddedObject (floating) {object} at (384.50,0) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -84,7 +80,7 @@ LayoutSVGInlineText {#text} at (0,0) size 13x23 chunk 1 (middle anchor) text run 1 at (53.89,27.00) startOffset 0 endOffset 1 width 12.22: "L" layer at (8,255) size 256x449 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,128) size 256.30x449 + LayoutEmbeddedObject (floating) {object} at (0,128.16) size 256.30x448.52 layer at (0,0) size 256x449 LayoutView at (0,0) size 256x449 layer at (0,0) size 256x449 @@ -118,7 +114,7 @@ LayoutSVGInlineText {#text} at (0,0) size 13x23 chunk 1 (middle anchor) text run 1 at (53.89,127.00) startOffset 0 endOffset 1 width 12.22: "E" layer at (264,255) size 256x256 - LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256 + LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256.30 layer at (0,0) size 256x256 LayoutView at (0,0) size 256x256 layer at (0,0) size 256x256 @@ -131,8 +127,8 @@ LayoutSVGText {text} at (32,29) size 16x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (32.78,47.00) startOffset 0 endOffset 1 width 14.44: "K" -layer at (264,511) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 +layer at (264,512) size 256x193 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,256.30) size 256.30x192.22 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 layer at (0,0) size 256x192 @@ -146,7 +142,7 @@ LayoutSVGInlineText {#text} at (0,0) size 8x23 chunk 1 (middle anchor) text run 1 at (36.11,37.00) startOffset 0 endOffset 1 width 7.78: "J" layer at (521,255) size 256x256 - LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256 + LayoutEmbeddedObject (floating) {object} at (0,0) size 256.30x256.30 layer at (0,0) size 256x256 LayoutView at (0,0) size 256x256 layer at (0,0) size 256x256 @@ -175,8 +171,8 @@ LayoutSVGText {text} at (52,49) size 16x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (52.78,67.00) startOffset 0 endOffset 1 width 14.44: "O" -layer at (521,511) size 256x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,256) size 256.30x192 +layer at (521,512) size 256x193 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,256.30) size 256.30x192.22 layer at (0,0) size 256x192 LayoutView at (0,0) size 256x192 layer at (0,0) size 256x192 @@ -189,8 +185,8 @@ LayoutSVGText {text} at (32,19) size 16x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (32.78,37.00) startOffset 0 endOffset 1 width 14.44: "Q" -layer at (264,703) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (256.30,576) size 384.50x128 +layer at (8,704) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,576.67) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -203,8 +199,8 @@ LayoutSVGText {text} at (54,9) size 12x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x23 chunk 1 (middle anchor) text run 1 at (54.44,27.00) startOffset 0 endOffset 1 width 11.12: "F" -layer at (8,831) size 385x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,704) size 384.50x128 +layer at (393,704) size 384x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (384.50,576.67) size 384.50x128.16 layer at (0,0) size 385x128 LayoutView at (0,0) size 385x128 layer at (0,0) size 385x128 @@ -217,8 +213,8 @@ LayoutSVGText {text} at (56,9) size 8x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 7x23 chunk 1 (middle anchor) text run 1 at (56.67,27.00) startOffset 0 endOffset 1 width 6.66: "I" -layer at (393,831) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192 +layer at (8,832) size 96x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 96.13x192.25 layer at (0,0) size 96x192 LayoutView at (0,0) size 96x192 layer at (0,0) size 96x192 @@ -228,8 +224,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGRect {rect} at (0,0) size 96x192 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [x=1.00] [y=1.00] [width=28.00] [height=58.00] -layer at (489,831) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192 +layer at (104,832) size 289x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (96.13,0) size 288.38x192.25 layer at (0,0) size 288x192 LayoutView at (0,0) size 288x192 layer at (0,0) size 288x192 @@ -242,8 +238,8 @@ LayoutSVGText {text} at (37,19) size 16x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (37.78,37.00) startOffset 0 endOffset 1 width 14.44: "G" -layer at (8,1023) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192 +layer at (393,832) size 192x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 192.25x192.25 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 layer at (0,0) size 192x192 @@ -256,8 +252,8 @@ LayoutSVGText {text} at (22,19) size 16x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 15x23 chunk 1 (middle anchor) text run 1 at (22.78,37.00) startOffset 0 endOffset 1 width 14.44: "H" -layer at (200,1023) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192 +layer at (585,832) size 193x192 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (192.25,0) size 192.25x192.25 layer at (0,0) size 192x192 LayoutView at (0,0) size 192x192 layer at (0,0) size 192x192 @@ -270,8 +266,8 @@ LayoutSVGText {text} at (23,19) size 14x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 14x23 chunk 1 (middle anchor) text run 1 at (23.33,37.00) startOffset 0 endOffset 1 width 13.34: "R" -layer at (393,1023) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (384.50,896) size 192.25x128 +layer at (8,1024) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,897.08) size 192.25x128.16 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 layer at (0,0) size 192x128 @@ -281,8 +277,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (585,1023) size 192x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (576.75,896) size 192.25x128 +layer at (200,1024) size 193x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (192.25,897.08) size 192.25x128.16 layer at (0,0) size 192x128 LayoutView at (0,0) size 192x128 layer at (0,0) size 192x128 @@ -292,8 +288,8 @@ LayoutSVGGradientStop {stop} [offset=0.00] [color=#FFFFFF] LayoutSVGGradientStop {stop} [offset=1.00] [color=#FFEEAA] LayoutSVGEllipse {ellipse} at (0,0) size 192x128 [stroke={[type=SOLID] [color=#FFCC33] [stroke width=2.00]}] [fill={[type=LINEAR-GRADIENT] [id="surface"]}] [cx=30.00] [cy=20.00] [rx=29.00] [ry=19.00] -layer at (393,1151) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128 +layer at (393,1024) size 288x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (0,0) size 288.38x128.16 layer at (0,0) size 288x128 LayoutView at (0,0) size 288x128 layer at (0,0) size 288x128 @@ -306,8 +302,8 @@ LayoutSVGText {text} at (39,9) size 12x23 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 12x23 chunk 1 (middle anchor) text run 1 at (39.44,27.00) startOffset 0 endOffset 1 width 11.12: "S" -layer at (681,1151) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 - LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128 +layer at (681,1024) size 97x128 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600 + LayoutEmbeddedObject (floating) {object} at (288.38,0) size 96.13x128.16 layer at (0,0) size 96x128 LayoutView at (0,0) size 96x128 layer at (0,0) size 96x128
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-rightsizing-a-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-rightsizing-a-expected.txt index 400fb23..b4e9ada 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-rightsizing-a-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-rightsizing-a-expected.txt
@@ -22,7 +22,7 @@ text run at (0,0) width 675: "Above there must be a GIF- and a SVG-image visible. Both are referenced by an object element (width:100%," text run at (0,17) width 506: "no defined height) and each nested into a div element (width:176px, height:62px)." LayoutBlockFlow {div} at (0,248.77) size 176x62 [bgcolor=#FF0000] - LayoutImage {object} at (0,0) size 176x62 + LayoutImage {object} at (0,0) size 175.98x62 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow (anonymous) at (0,310.77) size 752x18 LayoutBR {br} at (0,0) size 0x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.png index a927aa2..7b074fa 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.txt index db70bf89..043f24ef 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-hixie-mixed-009-expected.txt
@@ -1,12 +1,12 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x435 - LayoutBlockFlow {html} at (0,0) size 800x435.36 - LayoutBlockFlow {body} at (11.52,14.39) size 776.97x406.58 +layer at (0,0) size 800x436 + LayoutBlockFlow {html} at (0,0) size 800x436.36 + LayoutBlockFlow {body} at (11.52,14.39) size 776.97x407.58 LayoutBlockFlow {p} at (0,0) size 776.97x27 [color=#000080] LayoutText {#text} at (0,0) size 660x26 text run at (0,0) width 660: "The word \"TEST \" should appear twice below, the same size each time." - LayoutBlockFlow (anonymous) at (0,41.39) size 776.97x178 + LayoutBlockFlow (anonymous) at (0,41.39) size 776.97x179 LayoutSVGRoot {svg} at (11,55) size 577x174 LayoutSVGRect {rect} at (11,55) size 577x174 [transform={m=((10.00,0.00)(0.00,10.00)) t=(0.00,0.00)}] [fill={[type=SOLID] [color=#EEEEEE]}] [x=0.00] [y=0.00] [width=60.00] [height=12.00] LayoutSVGForeignObject {foreignObject} at (0,0) size 60x10 @@ -14,6 +14,6 @@ LayoutText {#text} at (0,0) size 26x12 text run at (0,0) width 26: "TEST" LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {div} at (0,233.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] + LayoutBlockFlow {div} at (0,234.78) size 576x172.80 [color=#000080] [bgcolor=#EEEEEE] LayoutText {#text} at (0,3) size 344x159 text run at (0,3) width 344: "TEST"
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png index 094c95a..41d00463 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt index 3950125e..eef4cb4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.txt
@@ -1,10 +1,10 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x331 - LayoutBlockFlow {HTML} at (0,0) size 800x331.09 - LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x320 - LayoutTable {TABLE} at (0,0) size 467x320 - LayoutTableSection {TBODY} at (0,0) size 467x320 +layer at (0,0) size 800x339 + LayoutBlockFlow {HTML} at (0,0) size 800x339.09 + LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x328 + LayoutTable {TABLE} at (0,0) size 467x328 + LayoutTableSection {TBODY} at (0,0) size 467x328 LayoutTableRow {TR} at (0,1) size 467x14 LayoutTableCell {TH} at (1,1) size 64x14 [bgcolor=#DDDD99] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (5,0) size 54x14 @@ -18,79 +18,79 @@ LayoutTableCell {TH} at (326,1) size 140x14 [bgcolor=#DDDD99] [r=0 c=3 rs=1 cs=1] LayoutText {#text} at (47,0) size 46x14 text run at (47,0) width 46: "<object>" - LayoutTableRow {TR} at (0,16) size 467x37 - LayoutTableCell {TH} at (1,84) size 64x14 [bgcolor=#DDDD99] [r=1 c=0 rs=4 cs=1] + LayoutTableRow {TR} at (0,16) size 467x38 + LayoutTableCell {TH} at (1,86) size 64x14 [bgcolor=#DDDD99] [r=1 c=0 rs=4 cs=1] LayoutText {#text} at (0,0) size 64x14 text run at (0,0) width 64: "No viewBox" - LayoutTableCell {TH} at (66,34) size 118x0 [bgcolor=#DDDD99] [r=1 c=1 rs=1 cs=1] - LayoutTableCell {TD} at (185,16) size 140x37 [r=1 c=2 rs=1 cs=1] + LayoutTableCell {TH} at (66,35) size 118x0 [bgcolor=#DDDD99] [r=1 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (185,16) size 140x38 [r=1 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,16) size 140x37 [r=1 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,16) size 140x38 [r=1 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,54) size 467x37 - LayoutTableCell {TH} at (66,65) size 118x14 [bgcolor=#DDDD99] [r=2 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,55) size 467x38 + LayoutTableCell {TH} at (66,67) size 118x14 [bgcolor=#DDDD99] [r=2 c=1 rs=1 cs=1] LayoutText {#text} at (45,0) size 28x14 text run at (45,0) width 28: "none" - LayoutTableCell {TD} at (185,54) size 140x37 [r=2 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (185,55) size 140x38 [r=2 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,54) size 140x37 [r=2 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,55) size 140x38 [r=2 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,92) size 467x37 - LayoutTableCell {TH} at (66,103) size 118x14 [bgcolor=#DDDD99] [r=3 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,94) size 467x38 + LayoutTableCell {TH} at (66,106) size 118x14 [bgcolor=#DDDD99] [r=3 c=1 rs=1 cs=1] LayoutText {#text} at (44,0) size 30x14 text run at (44,0) width 30: "meet" - LayoutTableCell {TD} at (185,92) size 140x37 [r=3 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (185,94) size 140x38 [r=3 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,92) size 140x37 [r=3 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,94) size 140x38 [r=3 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,130) size 467x37 - LayoutTableCell {TH} at (66,141) size 118x14 [bgcolor=#DDDD99] [r=4 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,133) size 467x38 + LayoutTableCell {TH} at (66,145) size 118x14 [bgcolor=#DDDD99] [r=4 c=1 rs=1 cs=1] LayoutText {#text} at (46,0) size 26x14 text run at (46,0) width 26: "slice" - LayoutTableCell {TD} at (185,130) size 140x37 [r=4 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (185,133) size 140x38 [r=4 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,130) size 140x37 [r=4 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,133) size 140x38 [r=4 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,168) size 467x37 - LayoutTableCell {TH} at (1,236) size 64x14 [bgcolor=#DDDD99] [r=5 c=0 rs=4 cs=1] + LayoutTableRow {TR} at (0,172) size 467x38 + LayoutTableCell {TH} at (1,242) size 64x14 [bgcolor=#DDDD99] [r=5 c=0 rs=4 cs=1] LayoutText {#text} at (9,0) size 46x14 text run at (9,0) width 46: "viewBox" - LayoutTableCell {TH} at (66,186) size 118x0 [bgcolor=#DDDD99] [r=5 c=1 rs=1 cs=1] - LayoutTableCell {TD} at (185,168) size 140x37 [r=5 c=2 rs=1 cs=1] + LayoutTableCell {TH} at (66,191) size 118x0 [bgcolor=#DDDD99] [r=5 c=1 rs=1 cs=1] + LayoutTableCell {TD} at (185,172) size 140x38 [r=5 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,168) size 140x37 [r=5 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,172) size 140x38 [r=5 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,206) size 467x37 - LayoutTableCell {TH} at (66,217) size 118x14 [bgcolor=#DDDD99] [r=6 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,211) size 467x38 + LayoutTableCell {TH} at (66,223) size 118x14 [bgcolor=#DDDD99] [r=6 c=1 rs=1 cs=1] LayoutText {#text} at (45,0) size 28x14 text run at (45,0) width 28: "none" - LayoutTableCell {TD} at (185,206) size 140x37 [r=6 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (185,211) size 140x38 [r=6 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,206) size 140x37 [r=6 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,211) size 140x38 [r=6 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,244) size 467x37 - LayoutTableCell {TH} at (66,255) size 118x14 [bgcolor=#DDDD99] [r=7 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,250) size 467x38 + LayoutTableCell {TH} at (66,262) size 118x14 [bgcolor=#DDDD99] [r=7 c=1 rs=1 cs=1] LayoutText {#text} at (44,0) size 30x14 text run at (44,0) width 30: "meet" - LayoutTableCell {TD} at (185,244) size 140x37 [r=7 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (185,250) size 140x38 [r=7 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,244) size 140x37 [r=7 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,250) size 140x38 [r=7 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableRow {TR} at (0,282) size 467x37 - LayoutTableCell {TH} at (66,293) size 118x14 [bgcolor=#DDDD99] [r=8 c=1 rs=1 cs=1] + LayoutTableRow {TR} at (0,289) size 467x38 + LayoutTableCell {TH} at (66,301) size 118x14 [bgcolor=#DDDD99] [r=8 c=1 rs=1 cs=1] LayoutText {#text} at (46,0) size 26x14 text run at (46,0) width 26: "slice" - LayoutTableCell {TD} at (185,282) size 140x37 [r=8 c=2 rs=1 cs=1] + LayoutTableCell {TD} at (185,289) size 140x38 [r=8 c=2 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 138.88x34.72 [border: (1px dashed #800000)] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {TD} at (326,282) size 140x37 [r=8 c=3 rs=1 cs=1] + LayoutTableCell {TD} at (326,289) size 140x38 [r=8 c=3 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 layer at (332,22) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] @@ -99,7 +99,7 @@ layer at (0,0) size 133x29 LayoutSVGRoot {svg} at (0,0) size 133x29 LayoutSVGEllipse {circle} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [cx=110.00] [cy=110.00] [r=110.00] -layer at (332,60) size 139x35 +layer at (332,61) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -108,7 +108,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (332,98) size 139x35 +layer at (332,100) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -117,7 +117,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (332,136) size 139x35 +layer at (332,139) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -126,7 +126,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 133x29 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 133x29 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (332,174) size 139x35 +layer at (332,178) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -135,7 +135,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (51,0) size 23x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (51,0) size 23x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (332,212) size 139x35 +layer at (332,217) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -144,7 +144,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (0,0) size 97x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (0,0) size 98x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (332,250) size 139x35 +layer at (332,256) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29 @@ -153,7 +153,7 @@ LayoutSVGHiddenContainer {defs} at (0,0) size 0x0 LayoutSVGContainer {g} at (51,0) size 23x22 [transform={m=((1.00,0.00)(0.00,1.00)) t=(-162.36,-403.29)}] LayoutSVGPath {path} at (51,0) size 23x22 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#D9BB7A] [fill rule=EVEN-ODD]}] [data="M 525.714 585.219 A 181.429 181.429 0 1 1 162.857 585.219 A 181.429 181.429 0 1 1 525.714 585.219 Z"] -layer at (332,288) size 139x35 +layer at (332,295) size 139x35 LayoutEmbeddedObject {OBJECT} at (0,0) size 138.88x34.72 [border: (1px dashed #008000)] layer at (0,0) size 133x29 LayoutView at (0,0) size 133x29
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png index 4695665..92b558f39 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt index 57b247b..6a8ef84 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-replaced-intrinsic-ratio-001-expected.txt
@@ -25,8 +25,8 @@ LayoutTableRow {TR} at (0,0) size 138x44 LayoutTableCell {TD} at (0,0) size 138x44 [r=0 c=0 rs=1 cs=1] LayoutBlockFlow (floating) {P} at (1,457.98) size 138.88x9.25 -layer at (15,134) size 121x44 - LayoutEmbeddedObject {OBJECT} at (9.25,-0.25) size 120.38x44 [bgcolor=#FF0000] [border: (9px solid #0000FF)] +layer at (15,134) size 121x43 + LayoutEmbeddedObject {OBJECT} at (9.25,-0.25) size 120.38x43.59 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x26 LayoutView at (0,0) size 102x26 layer at (0,0) size 102x26 @@ -34,7 +34,7 @@ LayoutSVGRect {rect} at (0,0) size 102x26 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 72x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,199) size 121x44 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x44 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x43.59 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x26 LayoutView at (0,0) size 102x26 layer at (0,0) size 102x26 @@ -42,7 +42,7 @@ LayoutSVGRect {rect} at (0,0) size 102x26 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 72x16 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,264) size 120x43 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43.38 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x25 LayoutView at (0,0) size 102x25 layer at (0,0) size 102x25 @@ -50,7 +50,7 @@ LayoutSVGRect {rect} at (0,0) size 101x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 70x15 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,363) size 120x43 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 119.50x43.38 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x25 LayoutView at (0,0) size 102x25 layer at (0,0) size 102x25 @@ -58,7 +58,7 @@ LayoutSVGRect {rect} at (0,0) size 101x25 [stroke={[type=SOLID] [color=#008000] [stroke width=12.00]}] [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=1000.00] [height=250.00] LayoutSVGPath {path} at (15,5) size 70x15 [fill={[type=SOLID] [color=#008000]}] [data="M 500 50 L 150 200 L 850 200 Z"] layer at (15,463) size 121x44 - LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x44 [bgcolor=#FF0000] [border: (9px solid #0000FF)] + LayoutEmbeddedObject {OBJECT} at (9.25,0) size 120.38x43.59 [bgcolor=#FF0000] [border: (9px solid #0000FF)] layer at (0,0) size 102x26 LayoutView at (0,0) size 102x26 layer at (0,0) size 102x26
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.png index 695f112..f440ef1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt index 09e3ce9..0ba525f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-auto-size-expected.txt
@@ -1,10 +1,10 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x172 - LayoutBlockFlow {HTML} at (0,0) size 800x172.09 - LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x161 - LayoutText {#text} at (158,147) size 4x14 - text run at (158,147) width 4: " " +layer at (0,0) size 800x173 + LayoutBlockFlow {HTML} at (0,0) size 800x173.09 + LayoutBlockFlow {BODY} at (5.55,5.55) size 788.91x162 + LayoutText {#text} at (158,148) size 4x14 + text run at (158,148) width 4: " " LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png index b5f5231d..af3d0216 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt index 0bc721f..f661121 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-huge-size-expected.txt
@@ -1,14 +1,14 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x352 - LayoutBlockFlow {html} at (0,0) size 800x352.44 - LayoutBlockFlow {body} at (5.55,11.11) size 788.91x330.22 +layer at (0,0) size 800x353 + LayoutBlockFlow {html} at (0,0) size 800x353.44 + LayoutBlockFlow {body} at (5.55,11.11) size 788.91x331.22 LayoutBlockFlow {p} at (0,0) size 788.91x14 LayoutText {#text} at (0,0) size 92x14 text run at (0,0) width 92: "Text above the rect" - LayoutBlockFlow (anonymous) at (0,25.11) size 788.91x280 + LayoutBlockFlow (anonymous) at (0,25.11) size 788.91x281 LayoutText {#text} at (0,0) size 0x0 - LayoutBlockFlow {p} at (0,316.22) size 788.91x14 + LayoutBlockFlow {p} at (0,317.22) size 788.91x14 LayoutText {#text} at (0,0) size 91x14 text run at (0,0) width 91: "Text below the rect" layer at (6,36) size 278x278
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt index 7832a3ac..61a4e1e 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/zoom/page/zoom-svg-through-object-with-percentage-size-expected.txt
@@ -1,14 +1,14 @@ layer at (0,0) size 800x600 LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x379 - LayoutBlockFlow {html} at (0,0) size 800x379.09 - LayoutBlockFlow {body} at (5.55,5.55) size 788.91x368 - LayoutTable {table} at (41.45,0) size 706x368 - LayoutTableSection (anonymous) at (0,0) size 706x368 - LayoutTableRow {tr} at (0,0) size 706x368 - LayoutTableCell {td} at (0,0) size 706x368 [r=0 c=0 rs=1 cs=3] - LayoutTable {table} at (7.44,6) size 693x356 - LayoutTableSection (anonymous) at (0,0) size 693x356 +layer at (0,0) size 800x380 + LayoutBlockFlow {html} at (0,0) size 800x380.09 + LayoutBlockFlow {body} at (5.55,5.55) size 788.91x369 + LayoutTable {table} at (41.45,0) size 706x369 + LayoutTableSection (anonymous) at (0,0) size 706x369 + LayoutTableRow {tr} at (0,0) size 706x369 + LayoutTableCell {td} at (0,0) size 706x369 [r=0 c=0 rs=1 cs=3] + LayoutTable {table} at (7.44,6) size 693x357 + LayoutTableSection (anonymous) at (0,0) size 693x357 LayoutTableRow {tr} at (0,1) size 693x66 LayoutTableCell {td} at (1,1) size 691x65.75 [r=0 c=0 rs=1 cs=2] LayoutBlockFlow {h1} at (5.55,19.88) size 681x26 @@ -21,10 +21,10 @@ LayoutTableCell {td} at (347,68) size 345x24 [r=1 c=1 rs=1 cs=1] LayoutText {#text} at (146,5) size 53x14 text run at (146,5) width 53: "PNG Image" - LayoutTableRow {tr} at (0,93) size 693x262 - LayoutTableCell {td} at (1,93) size 345x262 [r=2 c=0 rs=1 cs=1] + LayoutTableRow {tr} at (0,93) size 693x263 + LayoutTableCell {td} at (1,93) size 345x263 [r=2 c=0 rs=1 cs=1] LayoutText {#text} at (0,0) size 0x0 - LayoutTableCell {td} at (347,93) size 345x262 [r=2 c=1 rs=1 cs=1] + LayoutTableCell {td} at (347,93) size 345x263 [r=2 c=1 rs=1 cs=1] LayoutImage {img} at (5,5) size 333.33x249.97 LayoutText {#text} at (0,0) size 0x0 layer at (62,110) size 333x250
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.txt b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.txt index 0153ee8..be81079 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug101674-expected.txt
@@ -7,11 +7,11 @@ LayoutTableSection {TBODY} at (1,1) size 767x73 LayoutTableRow {TR} at (0,2) size 767x69 LayoutTableCell {TD} at (2,2) size 444x69 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 - LayoutImage {IMG} at (90,2) size 88x65 - LayoutImage {IMG} at (178,2) size 88x65 - LayoutImage {IMG} at (266,2) size 88x65 - LayoutImage {IMG} at (354,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 + LayoutImage {IMG} at (90,2) size 88x64.53 + LayoutImage {IMG} at (178,2) size 88x64.53 + LayoutImage {IMG} at (266,2) size 88x64.53 + LayoutImage {IMG} at (354,2) size 88x64.53 LayoutTableCell {TD} at (448,25) size 317x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 160x17 text run at (2,2) width 160: "Nothing between images" @@ -19,15 +19,15 @@ LayoutTableSection {TBODY} at (1,1) size 767x333 LayoutTableRow {TR} at (0,2) size 767x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,67) size 88x65 + LayoutImage {IMG} at (2,67) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,132) size 88x65 + LayoutImage {IMG} at (2,132) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,197) size 88x65 + LayoutImage {IMG} at (2,197) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,262) size 88x65 + LayoutImage {IMG} at (2,262) size 88x64.53 LayoutTableCell {TD} at (96,155) size 669x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 152x17 text run at (2,2) width 152: "Spaces between images" @@ -35,15 +35,15 @@ LayoutTableSection {TBODY} at (1,1) size 767x333 LayoutTableRow {TR} at (0,2) size 767x329 LayoutTableCell {TD} at (2,2) size 92x329 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (2,2) size 88x65 + LayoutImage {IMG} at (2,2) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,67) size 88x65 + LayoutImage {IMG} at (2,67) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,132) size 88x65 + LayoutImage {IMG} at (2,132) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,197) size 88x65 + LayoutImage {IMG} at (2,197) size 88x64.53 LayoutText {#text} at (0,0) size 0x0 - LayoutImage {IMG} at (2,262) size 88x65 + LayoutImage {IMG} at (2,262) size 88x64.53 LayoutTableCell {TD} at (96,155) size 669x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 168x17 text run at (2,2) width 168: "Newlines between images" @@ -53,19 +53,19 @@ LayoutTableCell {TD} at (2,2) size 595x73 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,53) size 27x17 text run at (2,53) width 27: "One" - LayoutImage {IMG} at (28.66,2) size 88x65 + LayoutImage {IMG} at (28.66,2) size 88x64.53 LayoutText {#text} at (116,53) size 29x17 text run at (116,53) width 29: "Two" - LayoutImage {IMG} at (144.86,2) size 88x65 + LayoutImage {IMG} at (144.86,2) size 88x64.53 LayoutText {#text} at (232,53) size 39x17 text run at (232,53) width 39: "Three" - LayoutImage {IMG} at (270.16,2) size 88x65 + LayoutImage {IMG} at (270.16,2) size 88x64.53 LayoutText {#text} at (358,53) size 31x17 text run at (358,53) width 31: "Four" - LayoutImage {IMG} at (388.38,2) size 88x65 + LayoutImage {IMG} at (388.38,2) size 88x64.53 LayoutText {#text} at (476,53) size 29x17 text run at (476,53) width 29: "Five" - LayoutImage {IMG} at (504.81,2) size 88x65 + LayoutImage {IMG} at (504.81,2) size 88x64.53 LayoutTableCell {TD} at (599,27) size 166x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 135x17 text run at (2,2) width 135: "Text between images" @@ -75,19 +75,19 @@ LayoutTableCell {TD} at (2,2) size 130x363 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 27x17 text run at (2,2) width 27: "One" - LayoutImage {IMG} at (2,20) size 88x65 + LayoutImage {IMG} at (2,20) size 88x64.53 LayoutText {#text} at (90,71) size 29x17 text run at (90,71) width 29: "Two" - LayoutImage {IMG} at (2,89) size 88x65 + LayoutImage {IMG} at (2,89) size 88x64.53 LayoutText {#text} at (90,140) size 38x17 text run at (90,140) width 38: "Three" - LayoutImage {IMG} at (2,158) size 88x65 + LayoutImage {IMG} at (2,158) size 88x64.53 LayoutText {#text} at (90,209) size 31x17 text run at (90,209) width 31: "Four" - LayoutImage {IMG} at (2,227) size 88x65 + LayoutImage {IMG} at (2,227) size 88x64.53 LayoutText {#text} at (90,278) size 29x17 text run at (90,278) width 29: "Five" - LayoutImage {IMG} at (2,296) size 88x65 + LayoutImage {IMG} at (2,296) size 88x64.53 LayoutTableCell {TD} at (134,172) size 631x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 214x17 text run at (2,2) width 214: "Text with spaces between images" @@ -98,23 +98,23 @@ LayoutText {#text} at (2,2) size 27x86 text run at (2,2) width 27: "One" text run at (2,71) width 12: "A" - LayoutImage {IMG} at (13.55,20) size 88x65 + LayoutImage {IMG} at (13.55,20) size 88x64.53 LayoutText {#text} at (101,71) size 128x86 text run at (101,71) width 29: "Two" text run at (2,140) width 8: "b" - LayoutImage {IMG} at (10,89) size 88x65 + LayoutImage {IMG} at (10,89) size 88x64.53 LayoutText {#text} at (98,140) size 134x86 text run at (98,140) width 38: "Three" text run at (2,209) width 8: "c" - LayoutImage {IMG} at (9.09,158) size 88x65 + LayoutImage {IMG} at (9.09,158) size 88x64.53 LayoutText {#text} at (97,209) size 126x86 text run at (97,209) width 31: "Four" text run at (2,278) width 8: "d" - LayoutImage {IMG} at (10,227) size 88x65 + LayoutImage {IMG} at (10,227) size 88x64.53 LayoutText {#text} at (98,278) size 125x86 text run at (98,278) width 29: "Five" text run at (2,347) width 8: "e" - LayoutImage {IMG} at (9.09,296) size 88x65 + LayoutImage {IMG} at (9.09,296) size 88x64.53 LayoutTableCell {TD} at (142,174) size 623x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1] LayoutText {#text} at (2,2) size 305x17 text run at (2,2) width 305: "Text with spaces and more text between images"
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug14929-expected.txt b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug14929-expected.txt index f397cbc..4368879 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug14929-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug14929-expected.txt
@@ -28,7 +28,7 @@ LayoutTableSection {TBODY} at (1,1) size 638x50 LayoutTableRow {TR} at (0,2) size 638x22 LayoutTableCell {TD} at (2,2) size 634x22 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=2] - LayoutImage {IMG} at (2,2) size 190x18 + LayoutImage {IMG} at (2,2) size 190x18.36 LayoutTableRow {TR} at (0,26) size 638x22 LayoutTableCell {TD} at (2,26) size 127x22 [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 57x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug86708-expected.txt b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug86708-expected.txt index b5a8e5b86d..e8982db 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug86708-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug86708-expected.txt
@@ -8,7 +8,7 @@ LayoutTableRow {TR} at (0,0) size 746x212 LayoutTableCell {TD} at (0,0) size 477x212 [bgcolor=#FF0000] [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutInline {A} at (0,0) size 286x17 [color=#0000EE] - LayoutImage {IMG} at (1,1) size 286x210 + LayoutImage {IMG} at (1,1) size 286x209.72 LayoutTableCell {TD} at (477,0) size 269x20 [bgcolor=#FFFF00] [border: (1px inset #808080)] [r=0 c=1 rs=2 cs=1] LayoutText {#text} at (1,1) size 169x17 text run at (1,1) width 169: "ROWSPAN =2 in this cell"
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/97619-expected.txt b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/97619-expected.txt index b14418a..64c88989 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/97619-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/97619-expected.txt
@@ -12,7 +12,7 @@ text run at (2,20) width 56: "handling" text run at (2,38) width 14: "of" text run at (2,56) width 72: "whitespace" - LayoutImage {IMG} at (2,74) size 20x17 + LayoutImage {IMG} at (2,74) size 20x16.66 LayoutText {#text} at (22,77) size 69x35 text run at (22,77) width 49: " around" text run at (2,95) width 63: "the image" @@ -29,7 +29,7 @@ text run at (2,20) width 16: "bb" text run at (2,38) width 15: "cc" text run at (2,56) width 16: "dd" - LayoutImage {IMG} at (2,74) size 20x17 + LayoutImage {IMG} at (2,74) size 20x16.66 LayoutText {#text} at (2,91) size 16x53 text run at (2,91) width 15: "ee" text run at (2,109) width 11: "ff" @@ -44,7 +44,7 @@ LayoutTableCell {TD} at (2,2) size 217x43 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,2) size 213x17 text run at (2,2) width 213: "checkforhandlingofnowhitespace" - LayoutImage {IMG} at (2,20) size 20x17 + LayoutImage {IMG} at (2,20) size 20x16.66 LayoutText {#text} at (22,23) size 108x17 text run at (22,23) width 108: " aroundtheimage" LayoutTableCell {TD} at (221,12) size 559x22 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1] @@ -57,7 +57,7 @@ LayoutTableCell {TD} at (2,2) size 340x25 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1] LayoutText {#text} at (2,5) size 213x17 text run at (2,5) width 213: "checkforhandlingofnowhitespace" - LayoutImage {IMG} at (214.36,2) size 20x17 + LayoutImage {IMG} at (214.36,2) size 20x16.66 LayoutText {#text} at (234,5) size 104x17 text run at (234,5) width 104: "aroundtheimage" LayoutTableCell {TD} at (344,3) size 436x22 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug85016-expected.txt b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug85016-expected.txt index 4a3acf6..07404935 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug85016-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug85016-expected.txt
@@ -12,7 +12,7 @@ text run at (0,0) width 550: "percentage height images in DIV with no height (red) in a DIV with no height (green)" LayoutBlockFlow {DIV} at (32,750) size 672x672 [border: (3px dotted #008000)] LayoutBlockFlow {DIV} at (35,35) size 602x602 [border: (1px solid #FF0000)] - LayoutImage {IMG} at (1,1) size 882x600 + LayoutImage {IMG} at (1,1) size 882.34x600 LayoutText {#text} at (0,0) size 0x0 LayoutBlockFlow {P} at (0,1454) size 736x18 LayoutText {#text} at (0,0) size 481x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-2-expected.png index 7d4a19c..37267e83 100644 --- a/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-2-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png b/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png index cd61512..6ae94c8f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-deep-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png b/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png index 66fbeb0..a34caba7a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt index b955518..e1a12f91 100644 --- a/third_party/WebKit/LayoutTests/platform/win/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/virtual/syncpaint/fast/repaint/details-open-repaint-expected.txt
@@ -9,7 +9,7 @@ [10, 71, 169, 16], [8, 68, 784, 22], [8, 68, 173, 22], - [8, 54, 11, 11] + [8, 53, 11, 11] ], "paintInvalidationClients": [ "LayoutDetailsMarker DIV id='details-marker'",
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png index bbf87d9b..424d30c 100644 --- a/third_party/WebKit/LayoutTests/platform/win7/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win7/fast/forms/calendar-picker/calendar-picker-appearance-zoom125-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/printing/pageProperty-with-multicol-expected.txt b/third_party/WebKit/LayoutTests/printing/pageProperty-with-multicol-expected.txt new file mode 100644 index 0000000..671eeb5e --- /dev/null +++ b/third_party/WebKit/LayoutTests/printing/pageProperty-with-multicol-expected.txt
@@ -0,0 +1,12 @@ +Test that special layout for pageProperty() doesn't crash or cause trouble when the document contains multicol. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS internals.pageProperty('margin-left', 0) is "77" +PASS successfullyParsed is true + +TEST COMPLETE + + +
diff --git a/third_party/WebKit/LayoutTests/printing/pageProperty-with-multicol.html b/third_party/WebKit/LayoutTests/printing/pageProperty-with-multicol.html new file mode 100644 index 0000000..68ba8ac9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/printing/pageProperty-with-multicol.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="../resources/js-test.js"></script> +<style> + @page { margin:0 77px; } +</style> +<div style="-webkit-columns:3;"> + <br> + <br> +</div> +<script> + description("Test that special layout for pageProperty() doesn't crash or cause trouble when the document contains multicol."); + if (window.internals) { + shouldBeEqualToString("internals.pageProperty('margin-left', 0)", "77"); + } else { + testFailed("This test can be run only with window.internals enabled"); + } +</script>
diff --git a/third_party/WebKit/LayoutTests/svg/css/max-width-2-expected.html b/third_party/WebKit/LayoutTests/svg/css/max-width-2-expected.html index db13e30..a936ee802 100644 --- a/third_party/WebKit/LayoutTests/svg/css/max-width-2-expected.html +++ b/third_party/WebKit/LayoutTests/svg/css/max-width-2-expected.html
@@ -1,6 +1,6 @@ <!DOCTYPE html> <div style="outline: dashed lightblue; height: 300px; width: 400px;"> - <svg style="background-color: yellow;" width="400" height="133" viewBox="0 0 900 300"> + <svg style="background-color: yellow;" width="400" height="133.33" viewBox="0 0 900 300"> <rect x="0" y="0" width="900" height="300" /> </svg> </div>
diff --git a/third_party/WebKit/LayoutTests/svg/wicd/sizing-flakiness-expected.txt b/third_party/WebKit/LayoutTests/svg/wicd/sizing-flakiness-expected.txt index 23d4debc..1ace5dd8 100644 --- a/third_party/WebKit/LayoutTests/svg/wicd/sizing-flakiness-expected.txt +++ b/third_party/WebKit/LayoutTests/svg/wicd/sizing-flakiness-expected.txt
@@ -5,7 +5,7 @@ LayoutBlockFlow {BODY} at (8,8) size 784x0 LayoutBlockFlow (floating) {DIV} at (0,0) size 402x402 [border: (1px solid #FF0000)] layer at (9,9) size 200x67 - LayoutEmbeddedObject (floating) {OBJECT} at (1,1) size 200x67 + LayoutEmbeddedObject (floating) {OBJECT} at (1,1) size 200x66.66 layer at (0,0) size 200x67 LayoutView at (0,0) size 200x67 layer at (0,0) size 200x67
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.png b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.png index 55553e25..9567c2567 100644 --- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.png +++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.txt b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.txt index 2b2ad90a..2446bd8 100644 --- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.txt +++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug12910-2-expected.txt
@@ -7,7 +7,7 @@ LayoutTableSection {TBODY} at (0,0) size 800x20 LayoutTableRow {TR} at (0,0) size 800x20 LayoutTableCell {TD} at (0,0) size 4x20 [r=0 c=0 rs=1 cs=1] - LayoutImage {IMG} at (0,0) size 4x20 + LayoutImage {IMG} at (0,0) size 4x19.98 LayoutTableCell {TD} at (4,6) size 672x14 [r=0 c=1 rs=1 cs=1] LayoutImage {IMG} at (0,0) size 672x14 LayoutTableCell {TD} at (676,3) size 105x14 [r=0 c=2 rs=1 cs=1]
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug85016-expected.png b/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug85016-expected.png index d5cb27fa..f9b4aafe 100644 --- a/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug85016-expected.png +++ b/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug85016-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-lost-gpu-context-expected.txt b/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-lost-gpu-context-expected.txt index d6dc3955..83502ff 100644 --- a/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-lost-gpu-context-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-lost-gpu-context-expected.txt
@@ -5,6 +5,7 @@ PASS contextLostTest is false PASS ctx.isContextLost() is false +PASS ctx.isContextLost() is false Aborting test: Graphics context loss did not destroy canvas contents. This is expected if canvas is not accelerated. PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-lost-gpu-context-expected.txt b/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-lost-gpu-context-expected.txt index 8f9b55a..5d49b35 100644 --- a/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-lost-gpu-context-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/canvas-lost-gpu-context-expected.txt
@@ -5,6 +5,7 @@ PASS contextLostTest is false PASS ctx.isContextLost() is false +PASS ctx.isContextLost() is false PASS contextLostTest is true PASS ctx.isContextLost() is true PASS successfullyParsed is true
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffect.cpp b/third_party/WebKit/Source/core/animation/KeyframeEffect.cpp index 50d453fe..11ca85fa 100644 --- a/third_party/WebKit/Source/core/animation/KeyframeEffect.cpp +++ b/third_party/WebKit/Source/core/animation/KeyframeEffect.cpp
@@ -98,6 +98,8 @@ if (m_target) { m_target->ensureElementAnimations().animations().add(animation); m_target->setNeedsAnimationStyleRecalc(); + if (RuntimeEnabledFeatures::webAnimationsSVGEnabled() && m_target->isSVGElement()) + toSVGElement(m_target)->setWebAnimationsPending(); } AnimationEffect::attach(animation); }
diff --git a/third_party/WebKit/Source/core/css/invalidation/StyleInvalidator.cpp b/third_party/WebKit/Source/core/css/invalidation/StyleInvalidator.cpp index 453479c8..db5fa17 100644 --- a/third_party/WebKit/Source/core/css/invalidation/StyleInvalidator.cpp +++ b/third_party/WebKit/Source/core/css/invalidation/StyleInvalidator.cpp
@@ -43,22 +43,23 @@ void StyleInvalidator::scheduleInvalidationSetsForElement(const InvalidationLists& invalidationLists, Element& element) { ASSERT(element.inActiveDocument()); + if (element.styleChangeType() >= SubtreeStyleChange) + return; + bool requiresDescendantInvalidation = false; - if (element.styleChangeType() < SubtreeStyleChange) { - for (auto& invalidationSet : invalidationLists.descendants) { - if (invalidationSet->wholeSubtreeInvalid()) { - element.setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); - requiresDescendantInvalidation = false; - break; - } - - if (invalidationSet->invalidatesSelf()) - element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); - - if (!invalidationSet->isEmpty()) - requiresDescendantInvalidation = true; + for (auto& invalidationSet : invalidationLists.descendants) { + if (invalidationSet->wholeSubtreeInvalid()) { + element.setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); + clearInvalidation(element); + return; } + + if (invalidationSet->invalidatesSelf()) + element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); + + if (!invalidationSet->isEmpty()) + requiresDescendantInvalidation = true; } if (invalidationLists.siblings.isEmpty() && !requiresDescendantInvalidation)
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp index db7cdba5..6432b84 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -7,6 +7,7 @@ #include "core/StylePropertyShorthand.h" #include "core/css/CSSCalculationValue.h" +#include "core/css/CSSCrossfadeValue.h" #include "core/css/CSSCursorImageValue.h" #include "core/css/CSSCustomIdentValue.h" #include "core/css/CSSFontFaceSrcValue.h" @@ -2405,6 +2406,217 @@ return list.release(); } +static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue* gradient) +{ + bool supportsColorHints = gradient->gradientType() == CSSLinearGradient || gradient->gradientType() == CSSRadialGradient; + + // The first color stop cannot be a color hint. + bool previousStopWasColorHint = true; + do { + CSSGradientColorStop stop; + stop.m_color = consumeColor(range, cssParserMode); + // Two hints in a row are not allowed. + if (!stop.m_color && (!supportsColorHints || previousStopWasColorHint)) + return false; + previousStopWasColorHint = !stop.m_color; + stop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll); + if (!stop.m_color && !stop.m_position) + return false; + gradient->addStop(stop); + } while (consumeCommaIncludingWhitespace(range)); + + // The last color stop cannot be a color hint. + if (previousStopWasColorHint) + return false; + + // Must have 2 or more stops to be valid. + return gradient->stopCount() >= 2; +} + +static PassRefPtrWillBeRawPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating) +{ + RefPtrWillBeRawPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient); + + RefPtrWillBeRawPtr<CSSPrimitiveValue> shape = nullptr; + RefPtrWillBeRawPtr<CSSPrimitiveValue> sizeKeyword = nullptr; + RefPtrWillBeRawPtr<CSSPrimitiveValue> horizontalSize = nullptr; + RefPtrWillBeRawPtr<CSSPrimitiveValue> verticalSize = nullptr; + + // First part of grammar, the size/shape clause: + // [ circle || <length> ] | + // [ ellipse || [ <length> | <percentage> ]{2} ] | + // [ [ circle | ellipse] || <size-keyword> ] + for (int i = 0; i < 3; ++i) { + if (args.peek().type() == IdentToken) { + CSSValueID id = args.peek().id(); + if (id == CSSValueCircle || id == CSSValueEllipse) { + if (shape) + return nullptr; + shape = consumeIdent(args); + } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner || id == CSSValueFarthestSide || id == CSSValueFarthestCorner) { + if (sizeKeyword) + return nullptr; + sizeKeyword = consumeIdent(args); + } else { + break; + } + } else { + RefPtrWillBeRawPtr<CSSPrimitiveValue> center = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll); + if (!center) + break; + if (horizontalSize) + return nullptr; + horizontalSize = center; + if ((center = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll))) { + verticalSize = center.release(); + ++i; + } + } + } + + // You can specify size as a keyword or a length/percentage, not both. + if (sizeKeyword && horizontalSize) + return nullptr; + // Circles must have 0 or 1 lengths. + if (shape && shape->getValueID() == CSSValueCircle && verticalSize) + return nullptr; + // Ellipses must have 0 or 2 length/percentages. + if (shape && shape->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize) + return nullptr; + // If there's only one size, it must be a length. + // TODO(timloh): Calcs with both lengths and percentages should be rejected. + if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) + return nullptr; + + result->setShape(shape); + result->setSizingBehavior(sizeKeyword); + result->setEndHorizontalSize(horizontalSize); + result->setEndVerticalSize(verticalSize); + + RefPtrWillBeRawPtr<CSSValue> centerX = nullptr; + RefPtrWillBeRawPtr<CSSValue> centerY = nullptr; + if (args.peek().id() == CSSValueAt) { + args.consumeIncludingWhitespace(); + consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY); + if (!(centerX && centerY)) + return nullptr; + result->setFirstX(centerX); + result->setFirstY(centerY); + // Right now, CSS radial gradients have the same start and end centers. + result->setSecondX(centerX); + result->setSecondY(centerY); + } + + if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consumeCommaIncludingWhitespace(args)) + return nullptr; + if (!consumeGradientColorStops(args, cssParserMode, result.get())) + return nullptr; + return result.release(); +} + +static PassRefPtrWillBeRawPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating, CSSGradientType gradientType) +{ + RefPtrWillBeRawPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType); + + bool expectComma = true; + RefPtrWillBeRawPtr<CSSPrimitiveValue> angle = consumeAngle(args, cssParserMode); + if (angle) { + result->setAngle(angle.release()); + } else if (gradientType == CSSPrefixedLinearGradient || consumeIdent<CSSValueTo>(args)) { + RefPtrWillBeRawPtr<CSSPrimitiveValue> endX = consumeIdent<CSSValueLeft, CSSValueRight>(args); + RefPtrWillBeRawPtr<CSSPrimitiveValue> endY = consumeIdent<CSSValueBottom, CSSValueTop>(args); + if (!endX && !endY) { + if (gradientType == CSSLinearGradient) + return nullptr; + endY = cssValuePool().createIdentifierValue(CSSValueTop); + expectComma = false; + } else if (!endX) { + endX = consumeIdent<CSSValueLeft, CSSValueRight>(args); + } + + result->setFirstX(endX.release()); + result->setFirstY(endY.release()); + } else { + expectComma = false; + } + + if (expectComma && !consumeCommaIncludingWhitespace(args)) + return nullptr; + if (!consumeGradientColorStops(args, cssParserMode, result.get())) + return nullptr; + return result.release(); +} + +static PassRefPtrWillBeRawPtr<CSSValue> consumeImage(CSSParserTokenRange&, CSSParserContext); + +static PassRefPtrWillBeRawPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserContext context) +{ + RefPtrWillBeRawPtr<CSSValue> fromImageValue = consumeImage(args, context); + if (!fromImageValue || !consumeCommaIncludingWhitespace(args)) + return nullptr; + RefPtrWillBeRawPtr<CSSValue> toImageValue = consumeImage(args, context); + if (!toImageValue || !consumeCommaIncludingWhitespace(args)) + return nullptr; + + RefPtrWillBeRawPtr<CSSPrimitiveValue> percentage = nullptr; + const CSSParserToken& percentageArg = args.consumeIncludingWhitespace(); + if (percentageArg.type() == PercentageToken) + percentage = cssValuePool().createValue(clampTo<double>(percentageArg.numericValue() / 100, 0, 1), CSSPrimitiveValue::UnitType::Number); + else if (percentageArg.type() == NumberToken) + percentage = cssValuePool().createValue(clampTo<double>(percentageArg.numericValue(), 0, 1), CSSPrimitiveValue::UnitType::Number); + + if (!percentage) + return nullptr; + return CSSCrossfadeValue::create(fromImageValue, toImageValue, percentage); +} + +static PassRefPtrWillBeRawPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context) +{ + CSSValueID id = range.peek().functionId(); + CSSParserTokenRange rangeCopy = range; + CSSParserTokenRange args = consumeFunction(rangeCopy); + RefPtrWillBeRawPtr<CSSValue> result = nullptr; + if (id == CSSValueRadialGradient) { + result = consumeRadialGradient(args, context.mode(), NonRepeating); + } else if (id == CSSValueWebkitLinearGradient) { + // FIXME: This should send a deprecation message. + if (context.useCounter()) + context.useCounter()->count(UseCounter::DeprecatedWebKitLinearGradient); + result = consumeLinearGradient(args, context.mode(), NonRepeating, CSSPrefixedLinearGradient); + } else if (id == CSSValueWebkitRepeatingLinearGradient) { + // FIXME: This should send a deprecation message. + if (context.useCounter()) + context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingLinearGradient); + result = consumeLinearGradient(args, context.mode(), Repeating, CSSPrefixedLinearGradient); + } else if (id == CSSValueLinearGradient) { + result = consumeLinearGradient(args, context.mode(), NonRepeating, CSSLinearGradient); + } else if (id == CSSValueWebkitCrossFade) { + result = consumeCrossFade(args, context); + } + if (!result || !args.atEnd()) + return nullptr; + range = rangeCopy; + return result; +} + +static PassRefPtrWillBeRawPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context) +{ + if (range.peek().id() == CSSValueNone) + return consumeIdent(range); + + AtomicString uri(consumeUrl(range)); + if (!uri.isNull()) + return CSSPropertyParser::createCSSImageValueWithReferrer(uri, context); + if (range.peek().type() == FunctionToken) { + CSSValueID id = range.peek().functionId(); + if (id == CSSValueWebkitImageSet) + return consumeImageSet(range, context); + if (CSSPropertyParser::isGeneratedImage(id)) + return consumeGeneratedImage(range, context); + } + return nullptr; +} + PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID unresolvedProperty) { CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty);
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp index b82a53ac..bc5f508 100644 --- a/third_party/WebKit/Source/core/dom/Element.cpp +++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1548,8 +1548,6 @@ if (ElementAnimations* elementAnimations = data->elementAnimations()) elementAnimations->cssAnimations().cancel(); } - - document().styleEngine().styleInvalidator().clearInvalidation(*this); } void Element::attach(const AttachContext& context) @@ -1636,6 +1634,8 @@ document().userActionElements().didDetach(*this); } + document().styleEngine().styleInvalidator().clearInvalidation(*this); + if (svgFilterNeedsLayerUpdate()) document().unscheduleSVGFilterLayerUpdateHack(*this);
diff --git a/third_party/WebKit/Source/core/editing/DOMSelection.cpp b/third_party/WebKit/Source/core/editing/DOMSelection.cpp index d1d378f..cad8eb8 100644 --- a/third_party/WebKit/Source/core/editing/DOMSelection.cpp +++ b/third_party/WebKit/Source/core/editing/DOMSelection.cpp
@@ -43,6 +43,7 @@ #include "core/editing/FrameSelection.h" #include "core/editing/iterators/TextIterator.h" #include "core/frame/LocalFrame.h" +#include "core/frame/UseCounter.h" #include "core/inspector/ConsoleMessage.h" #include "wtf/text/WTFString.h" @@ -208,6 +209,7 @@ return; if (!node) { + UseCounter::count(m_frame, UseCounter::SelectionCollapseNull); m_frame->selection().clear(); return; } @@ -281,6 +283,9 @@ return; } + if (!baseNode || !extentNode) + UseCounter::count(m_frame, UseCounter::SelectionSetBaseAndExtentNull); + if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) return;
diff --git a/third_party/WebKit/Source/core/editing/Selection.idl b/third_party/WebKit/Source/core/editing/Selection.idl index 5eeeb99..ca677de 100644 --- a/third_party/WebKit/Source/core/editing/Selection.idl +++ b/third_party/WebKit/Source/core/editing/Selection.idl
@@ -58,10 +58,11 @@ // TODO(philipj): The offset argument should not have a default value. [MeasureAs=SelectionExtend, RaisesException] void extend(Node node, optional long offset = 0); // TODO(philipj): The arguments should be anchorNode, anchorOffset, - // focusNode and focusOffset, and none of them should be optional. - [MeasureAs=SelectionSetBaseAndExtent, RaisesException, LegacyInterfaceTypeChecking] void setBaseAndExtent([Default=Undefined] optional Node baseNode, + // focusNode and focusOffset, and none of them should be optional or + // nullable. + [MeasureAs=SelectionSetBaseAndExtent, RaisesException, LegacyInterfaceTypeChecking] void setBaseAndExtent([Default=Undefined] optional Node? baseNode, [Default=Undefined] optional long baseOffset, - [Default=Undefined] optional Node extentNode, + [Default=Undefined] optional Node? extentNode, [Default=Undefined] optional long extentOffset); [MeasureAs=SelectionSelectAllChildren, RaisesException] void selectAllChildren(Node node); [MeasureAs=SelectionDeleteDromDocument, CustomElementCallbacks] void deleteFromDocument();
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h index 300bd720..66d3ffdb 100644 --- a/third_party/WebKit/Source/core/frame/UseCounter.h +++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -937,6 +937,8 @@ AudioNodeDisconnectFromAudioNode = 1080, AudioNodeDisconnectFromAudioParam = 1081, V8CSSFontFaceRule_Style_AttributeGetter = 1082, + SelectionCollapseNull = 1083, + SelectionSetBaseAndExtentNull = 1084, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.idl b/third_party/WebKit/Source/core/html/HTMLCanvasElement.idl index 4e7c0cf..393bb2a 100644 --- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.idl +++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.idl
@@ -53,5 +53,5 @@ // FIXME: type should not have a default value. [RaisesException] DOMString toDataURL(optional DOMString type = null, optional any arguments); - [RaisesException, RuntimeEnabled=ExperimentalCanvasFeatures] void toBlob(FileCallback? _callback, optional DOMString type = null, optional any arguments); + [RaisesException, RuntimeEnabled=ExperimentalCanvasFeatures] void toBlob(FileCallback _callback, optional DOMString type = null, optional any arguments); };
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp index 926fc41..9fc19df 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
@@ -1568,13 +1568,14 @@ value->setPseudoElements(pseudoElements.release()); forcePushChildren = true; } + if (!element->ownerDocument()->xmlVersion().isEmpty()) + value->setXmlVersion(element->ownerDocument()->xmlVersion()); } if (element->isInsertionPoint()) { value->setDistributedNodes(buildArrayForDistributedNodes(toInsertionPoint(element))); forcePushChildren = true; } - } else if (node->isDocumentNode()) { Document* document = toDocument(node); value->setDocumentURL(documentURLString(document));
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index 3b671d8..bfad641c 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -4436,7 +4436,7 @@ { ASSERT(linePositionMode == PositionOnContainingLine); if (isReplaced()) { - int result = direction == HorizontalLine ? marginHeight() + size().height() : marginWidth() + size().width(); + int result = direction == HorizontalLine ? roundToInt(marginHeight() + size().height()) : roundToInt(marginWidth() + size().width()); if (baselineType == AlphabeticBaseline) return result; return result - result / 2;
diff --git a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp index a475fb5..a4daa46c 100644 --- a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp
@@ -289,7 +289,7 @@ // of 'width' is: (used height) * (intrinsic ratio) if (intrinsicRatio && ((computedHeightIsAuto && !hasIntrinsicWidth && hasIntrinsicHeight) || !computedHeightIsAuto)) { LayoutUnit logicalHeight = computeReplacedLogicalHeight(); - return computeReplacedLogicalWidthRespectingMinMaxWidth(roundToInt(round(logicalHeight * intrinsicRatio)), shouldComputePreferred); + return computeReplacedLogicalWidthRespectingMinMaxWidth(logicalHeight * intrinsicRatio, shouldComputePreferred); } // If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width, then the used value of @@ -347,7 +347,7 @@ // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is: // (used width) / (intrinsic ratio) if (intrinsicRatio) - return computeReplacedLogicalHeightRespectingMinMaxHeight(roundToInt(round(availableLogicalWidth() / intrinsicRatio))); + return computeReplacedLogicalHeightRespectingMinMaxHeight(availableLogicalWidth() / intrinsicRatio); // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'. if (hasIntrinsicHeight)
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp index 0dbbb38..8e4f6451 100644 --- a/third_party/WebKit/Source/core/layout/LayoutView.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -241,9 +241,7 @@ if (shouldUsePrintingLayout()) { m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = logicalWidth(); - // TODO(mstensho): Get rid of m_pageLogicalHeight zero check. Currently, pageProperty() in - // PrintContext starts printing with zero height, so we have to cope for now. - if (!m_fragmentationContext && m_pageLogicalHeight) + if (!m_fragmentationContext) m_fragmentationContext = adoptPtr(new ViewFragmentationContext(*this)); } else if (m_fragmentationContext) { m_fragmentationContext.clear();
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp index 4fd748b..69c41c0 100644 --- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp +++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
@@ -539,7 +539,7 @@ document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com")); document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true); setBodyInnerHTML( - "<style>body { margin: 0; }</style>" + "<style>body { margin: 0; } ::-webkit-scrollbar { display: none; }</style>" "<iframe id=frame src='http://test.com' width='500' height='500' frameBorder='0'>" "</iframe>"); @@ -552,8 +552,7 @@ document().view()->updateAllLifecyclePhases(); ASSERT_TRUE(frameDocument.view()->layoutView()->hasLayer()); - // The width is 485 pixels due to the size of the scrollbar. - EXPECT_RECT_EQ(IntRect(0, 3500, 485, 4500), recomputeInterestRect(frameDocument.view()->layoutView()->enclosingLayer()->graphicsLayerBacking())); + EXPECT_RECT_EQ(IntRect(0, 3500, 500, 4500), recomputeInterestRect(frameDocument.view()->layoutView()->enclosingLayer()->graphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithContentBoxOffset) @@ -562,7 +561,7 @@ document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true); // Set a 10px border in order to have a contentBoxOffset for the iframe element. setBodyInnerHTML( - "<style>body { margin: 0; } #frame { border: 10px solid black; }</style>" + "<style>body { margin: 0; } #frame { border: 10px solid black; } ::-webkit-scrollbar { display: none; }</style>" "<iframe id=frame src='http://test.com' width='500' height='500' frameBorder='0'>" "</iframe>"); @@ -576,7 +575,7 @@ ASSERT_TRUE(frameDocument.view()->layoutView()->hasLayer()); // The width is 485 pixels due to the size of the scrollbar. - EXPECT_RECT_EQ(IntRect(0, 0, 485, 7500), recomputeInterestRect(frameDocument.view()->layoutView()->enclosingLayer()->graphicsLayerBacking())); + EXPECT_RECT_EQ(IntRect(0, 0, 500, 7500), recomputeInterestRect(frameDocument.view()->layoutView()->enclosingLayer()->graphicsLayerBacking())); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp b/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp index ba63b5c7..4afc570 100644 --- a/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp +++ b/third_party/WebKit/Source/core/layout/svg/line/SVGRootInlineBox.cpp
@@ -75,7 +75,7 @@ for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) { LayoutRect boxRect; if (child->isSVGInlineTextBox()) { - ASSERT(child->layoutObject().isSVGInlineText()); + ASSERT(child->lineLayoutItem().isSVGInlineText()); SVGInlineTextBox* textBox = toSVGInlineTextBox(child); boxRect = textBox->calculateBoundaries(); @@ -85,7 +85,7 @@ textBox->setLogicalHeight(boxRect.height()); } else { // Skip generated content. - if (!child->layoutObject().node()) + if (!child->lineLayoutItem().node()) continue; SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child); @@ -114,7 +114,7 @@ // Position all children relative to the parent block. for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) { // Skip generated content. - if (!child->layoutObject().node()) + if (!child->lineLayoutItem().node()) continue; child->move(LayoutSize(-childRect.x(), -childRect.y())); }
diff --git a/third_party/WebKit/Source/core/page/PageSerializer.cpp b/third_party/WebKit/Source/core/page/PageSerializer.cpp index 08e4010..a1a4845 100644 --- a/third_party/WebKit/Source/core/page/PageSerializer.cpp +++ b/third_party/WebKit/Source/core/page/PageSerializer.cpp
@@ -268,7 +268,6 @@ CString frameHTML = document.encoding().encode(text, WTF::EntitiesForUnencodables); m_resources->append(SerializedResource(url, document.suggestedMIMEType(), SharedBuffer::create(frameHTML.data(), frameHTML.length()))); - m_resourceURLs.add(url); for (Node* node: serializedNodes) { ASSERT(node);
diff --git a/third_party/WebKit/Source/core/page/PrintContext.cpp b/third_party/WebKit/Source/core/page/PrintContext.cpp index c02388f..6372b310 100644 --- a/third_party/WebKit/Source/core/page/PrintContext.cpp +++ b/third_party/WebKit/Source/core/page/PrintContext.cpp
@@ -259,8 +259,9 @@ { Document* document = frame->document(); PrintContext printContext(frame); - printContext.begin(800); // Any width is OK here. - document->updateLayout(); + // Any non-zero size is OK here. We don't care about actual layout. We just want to collect + // @page rules and figure out what declarations apply on a given page (that may or may not exist). + printContext.begin(800, 1000); RefPtr<ComputedStyle> style = document->styleForPage(pageNumber); // Implement formatters for properties we care about.
diff --git a/third_party/WebKit/Source/core/svg/SVGAnimatedEnumerationBase.cpp b/third_party/WebKit/Source/core/svg/SVGAnimatedEnumerationBase.cpp index 0e8c3330..7e4b90a1 100644 --- a/third_party/WebKit/Source/core/svg/SVGAnimatedEnumerationBase.cpp +++ b/third_party/WebKit/Source/core/svg/SVGAnimatedEnumerationBase.cpp
@@ -62,7 +62,7 @@ ASSERT(this->attributeName() != QualifiedName::null()); contextElement()->invalidateSVGAttributes(); - contextElement()->svgAttributeChanged(this->attributeName()); + contextElement()->svgAttributeBaseValChanged(this->attributeName()); } }
diff --git a/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp b/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp index b5dd1e5..8632b2d 100644 --- a/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp +++ b/third_party/WebKit/Source/core/svg/SVGDocumentExtensions.cpp
@@ -22,11 +22,6 @@ #include "config.h" #include "core/svg/SVGDocumentExtensions.h" -#include "core/animation/AnimationStack.h" -#include "core/animation/ElementAnimations.h" -#include "core/animation/InterpolationEnvironment.h" -#include "core/animation/InvalidatableInterpolation.h" -#include "core/animation/SVGInterpolation.h" #include "core/dom/Document.h" #include "core/inspector/ConsoleMessage.h" #include "core/layout/svg/SVGResourcesCache.h" @@ -99,11 +94,6 @@ document.accessSVGExtensions().serviceAnimations(monotonicAnimationStartTime); } -static bool isSVGAttributeHandle(const PropertyHandle& propertyHandle) -{ - return propertyHandle.isSVGAttribute(); -} - void SVGDocumentExtensions::serviceAnimations(double monotonicAnimationStartTime) { if (RuntimeEnabledFeatures::smilEnabled()) { @@ -117,21 +107,8 @@ webAnimationsPendingSVGElements.swap(m_webAnimationsPendingSVGElements); // TODO(alancutter): Make SVG animation effect application a separate document lifecycle phase from servicing animations to be responsive to Javascript manipulation of exposed animation objects. - for (auto& svgElement : webAnimationsPendingSVGElements) { - ActiveInterpolationsMap activeInterpolationsMap = AnimationStack::activeInterpolations( - &svgElement->elementAnimations()->animationStack(), nullptr, nullptr, KeyframeEffect::DefaultPriority, isSVGAttributeHandle); - for (auto& entry : activeInterpolationsMap) { - const QualifiedName& attribute = entry.key.svgAttribute(); - const Interpolation& interpolation = *entry.value.first(); - if (interpolation.isInvalidatableInterpolation()) { - InterpolationEnvironment environment(*svgElement, svgElement->propertyFromAttribute(attribute)->baseValueBase()); - InvalidatableInterpolation::applyStack(entry.value, environment); - } else { - // TODO(alancutter): Remove this old code path once animations have completely migrated to InterpolationTypes. - toSVGInterpolation(interpolation).apply(*svgElement); - } - } - } + for (auto& svgElement : webAnimationsPendingSVGElements) + svgElement->applyActiveWebAnimations(); ASSERT(m_webAnimationsPendingSVGElements.isEmpty()); }
diff --git a/third_party/WebKit/Source/core/svg/SVGElement.cpp b/third_party/WebKit/Source/core/svg/SVGElement.cpp index 4f7fc016..b6e62aa 100644 --- a/third_party/WebKit/Source/core/svg/SVGElement.cpp +++ b/third_party/WebKit/Source/core/svg/SVGElement.cpp
@@ -30,6 +30,12 @@ #include "core/SVGNames.h" #include "core/XLinkNames.h" #include "core/XMLNames.h" +#include "core/animation/AnimationStack.h" +#include "core/animation/DocumentAnimations.h" +#include "core/animation/ElementAnimations.h" +#include "core/animation/InterpolationEnvironment.h" +#include "core/animation/InvalidatableInterpolation.h" +#include "core/animation/SVGInterpolation.h" #include "core/css/CSSCursorImageValue.h" #include "core/css/resolver/StyleResolver.h" #include "core/dom/Document.h" @@ -241,6 +247,31 @@ void SVGElement::setWebAnimationsPending() { document().accessSVGExtensions().addWebAnimationsPendingSVGElement(*this); + ensureSVGRareData()->setWebAnimatedAttributesDirty(true); + elementData()->m_animatedSVGAttributesAreDirty = true; +} + +static bool isSVGAttributeHandle(const PropertyHandle& propertyHandle) +{ + return propertyHandle.isSVGAttribute(); +} + +void SVGElement::applyActiveWebAnimations() +{ + ActiveInterpolationsMap activeInterpolationsMap = AnimationStack::activeInterpolations( + &elementAnimations()->animationStack(), nullptr, nullptr, KeyframeEffect::DefaultPriority, isSVGAttributeHandle); + for (auto& entry : activeInterpolationsMap) { + const QualifiedName& attribute = entry.key.svgAttribute(); + const Interpolation& interpolation = *entry.value.first(); + if (interpolation.isInvalidatableInterpolation()) { + InterpolationEnvironment environment(*this, propertyFromAttribute(attribute)->baseValueBase()); + InvalidatableInterpolation::applyStack(entry.value, environment); + } else { + // TODO(alancutter): Remove this old code path once animations have completely migrated to InterpolationTypes. + toSVGInterpolation(interpolation).apply(*this); + } + } + svgRareData()->setWebAnimatedAttributesDirty(false); } template<typename T> @@ -861,8 +892,10 @@ // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods), // so we don't want changes to the style attribute to result in extra work here. - if (name != HTMLNames::styleAttr) - svgAttributeChanged(name); + if (name == HTMLNames::styleAttr) + return; + + svgAttributeBaseValChanged(name); } void SVGElement::svgAttributeChanged(const QualifiedName& attrName) @@ -891,11 +924,35 @@ } } +void SVGElement::svgAttributeBaseValChanged(const QualifiedName& attribute) +{ + svgAttributeChanged(attribute); + + if (!hasSVGRareData() || svgRareData()->webAnimatedAttributes().isEmpty()) + return; + + // TODO(alancutter): Only mark attributes as dirty if their animation depends on the underlying value. + svgRareData()->setWebAnimatedAttributesDirty(true); + elementData()->m_animatedSVGAttributesAreDirty = true; +} + +void SVGElement::ensureAttributeAnimValUpdated() +{ + if ((hasSVGRareData() && svgRareData()->webAnimatedAttributesDirty()) + || (elementAnimations() && DocumentAnimations::needsAnimationTimingUpdate(document()))) { + DocumentAnimations::updateAnimationTimingIfNeeded(document()); + applyActiveWebAnimations(); + } +} + void SVGElement::synchronizeAnimatedSVGAttribute(const QualifiedName& name) const { if (!elementData() || !elementData()->m_animatedSVGAttributesAreDirty) return; + // We const_cast here because we have deferred baseVal mutation animation updates to this point in time. + const_cast<SVGElement*>(this)->ensureAttributeAnimValUpdated(); + if (name == anyQName()) { AttributeToPropertyMap::const_iterator::Values it = m_attributeToPropertyMap.values().begin(); AttributeToPropertyMap::const_iterator::Values end = m_attributeToPropertyMap.values().end();
diff --git a/third_party/WebKit/Source/core/svg/SVGElement.h b/third_party/WebKit/Source/core/svg/SVGElement.h index 649e589..1c4e3b9 100644 --- a/third_party/WebKit/Source/core/svg/SVGElement.h +++ b/third_party/WebKit/Source/core/svg/SVGElement.h
@@ -83,6 +83,9 @@ // Records the SVG element as having a Web Animation on an SVG attribute that needs applying. void setWebAnimationsPending(); + void applyActiveWebAnimations(); + + void ensureAttributeAnimValUpdated(); void setWebAnimatedAttribute(const QualifiedName& attribute, PassRefPtrWillBeRawPtr<SVGPropertyBase>); void clearWebAnimatedAttributes(); @@ -103,6 +106,7 @@ virtual bool isValid() const { return true; } virtual void svgAttributeChanged(const QualifiedName&); + void svgAttributeBaseValChanged(const QualifiedName&); PassRefPtrWillBeRawPtr<SVGAnimatedPropertyBase> propertyFromAttribute(const QualifiedName& attributeName); static AnimatedPropertyType animatedPropertyTypeForCSSAttribute(const QualifiedName& attributeName);
diff --git a/third_party/WebKit/Source/core/svg/SVGElementRareData.h b/third_party/WebKit/Source/core/svg/SVGElementRareData.h index f4128244..bdb6b613 100644 --- a/third_party/WebKit/Source/core/svg/SVGElementRareData.h +++ b/third_party/WebKit/Source/core/svg/SVGElementRareData.h
@@ -46,6 +46,7 @@ , m_instancesUpdatesBlocked(false) , m_useOverrideComputedStyle(false) , m_needsOverrideComputedStyleUpdate(false) + , m_webAnimatedAttributesDirty(false) { } @@ -69,6 +70,9 @@ CSSCursorImageValue* cursorImageValue() const { return m_cursorImageValue; } void setCursorImageValue(CSSCursorImageValue* cursorImageValue) { m_cursorImageValue = cursorImageValue; } + void setWebAnimatedAttributesDirty(bool dirty) { m_webAnimatedAttributesDirty = dirty; } + bool webAnimatedAttributesDirty() const { return m_webAnimatedAttributesDirty; } + HashSet<const QualifiedName*>& webAnimatedAttributes() { return m_webAnimatedAttributes; } MutableStylePropertySet* animatedSMILStyleProperties() const { return m_animatedSMILStyleProperties.get(); } @@ -98,6 +102,7 @@ bool m_instancesUpdatesBlocked : 1; bool m_useOverrideComputedStyle : 1; bool m_needsOverrideComputedStyleUpdate : 1; + bool m_webAnimatedAttributesDirty : 1; HashSet<const QualifiedName*> m_webAnimatedAttributes; RefPtrWillBeMember<MutableStylePropertySet> m_animatedSMILStyleProperties; RefPtr<ComputedStyle> m_overrideComputedStyle;
diff --git a/third_party/WebKit/Source/core/svg/SVGMarkerElement.cpp b/third_party/WebKit/Source/core/svg/SVGMarkerElement.cpp index 616aead..41477cd 100644 --- a/third_party/WebKit/Source/core/svg/SVGMarkerElement.cpp +++ b/third_party/WebKit/Source/core/svg/SVGMarkerElement.cpp
@@ -120,7 +120,7 @@ { m_orientAngle->baseValue()->orientType()->setEnumValue(SVGMarkerOrientAuto); invalidateSVGAttributes(); - svgAttributeChanged(SVGNames::orientAttr); + svgAttributeBaseValChanged(SVGNames::orientAttr); } void SVGMarkerElement::setOrientToAngle(PassRefPtrWillBeRawPtr<SVGAngleTearOff> angle) @@ -129,7 +129,7 @@ RefPtrWillBeRawPtr<SVGAngle> target = angle->target(); m_orientAngle->baseValue()->newValueSpecifiedUnits(target->unitType(), target->valueInSpecifiedUnits()); invalidateSVGAttributes(); - svgAttributeChanged(SVGNames::orientAttr); + svgAttributeBaseValChanged(SVGNames::orientAttr); } LayoutObject* SVGMarkerElement::createLayoutObject(const ComputedStyle&)
diff --git a/third_party/WebKit/Source/core/svg/properties/SVGAnimatedProperty.h b/third_party/WebKit/Source/core/svg/properties/SVGAnimatedProperty.h index 461bb62..85008ba 100644 --- a/third_party/WebKit/Source/core/svg/properties/SVGAnimatedProperty.h +++ b/third_party/WebKit/Source/core/svg/properties/SVGAnimatedProperty.h
@@ -231,11 +231,12 @@ ASSERT(this->attributeName() != QualifiedName::null()); this->contextElement()->invalidateSVGAttributes(); - this->contextElement()->svgAttributeChanged(this->attributeName()); + this->contextElement()->svgAttributeBaseValChanged(this->attributeName()); } PrimitiveType animVal() { + this->contextElement()->ensureAttributeAnimValUpdated(); return this->currentValue()->value(); }
diff --git a/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.cpp b/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.cpp index b097a277..d03a3b2 100644 --- a/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.cpp +++ b/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.cpp
@@ -42,7 +42,7 @@ return; ASSERT(m_attributeName != QualifiedName::null()); contextElement()->invalidateSVGAttributes(); - contextElement()->svgAttributeChanged(m_attributeName); + contextElement()->svgAttributeBaseValChanged(m_attributeName); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.h b/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.h index 7a8c416..b48aef7d 100644 --- a/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.h +++ b/third_party/WebKit/Source/core/svg/properties/SVGPropertyTearOff.h
@@ -125,6 +125,9 @@ public: Property* target() { + if (isAnimVal()) + contextElement()->ensureAttributeAnimValUpdated(); + return m_target.get(); }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js index 6403e58..09db4742 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
@@ -54,6 +54,7 @@ this._pseudoType = payload.pseudoType; this._shadowRootType = payload.shadowRootType; this._frameId = payload.frameId || null; + this._xmlVersion = payload.xmlVersion; this._shadowRoots = []; @@ -784,7 +785,7 @@ */ isXMLNode: function() { - return !!this.ownerDocument && !!this.ownerDocument.xmlVersion; + return !!this._xmlVersion; }, /** @@ -1058,7 +1059,6 @@ WebInspector.DOMNode.call(this, domModel, this, false, payload); this.documentURL = payload.documentURL || ""; this.baseURL = payload.baseURL || ""; - this.xmlVersion = payload.xmlVersion; this._listeners = {}; }
diff --git a/third_party/WebKit/Source/modules/fetch/FetchDataConsumerHandle.h b/third_party/WebKit/Source/modules/fetch/FetchDataConsumerHandle.h index 34b1467..948eca4 100644 --- a/third_party/WebKit/Source/modules/fetch/FetchDataConsumerHandle.h +++ b/third_party/WebKit/Source/modules/fetch/FetchDataConsumerHandle.h
@@ -52,6 +52,7 @@ // function. // When |policy| is DisallowBlobWithInvalidSize, this function doesn't // return a non-null blob handle with unspecified size. + // The type of the returned handle may not be meaningful. virtual PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(BlobSizePolicy = DisallowBlobWithInvalidSize) { return nullptr; } // Drains the data as an EncodedFormData.
diff --git a/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandle.cpp b/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandle.cpp index 9849648..615fd1e6 100644 --- a/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandle.cpp +++ b/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandle.cpp
@@ -58,6 +58,18 @@ return ReaderImpl::create(this, client); } + PassRefPtr<BlobDataHandle> drainAsBlobDataHandle() + { + if (!m_formData) + return nullptr; + flatten(); + OwnPtr<BlobData> blobData = BlobData::create(); + blobData->appendBytes(m_flattenFormData.data(), m_flattenFormData.size()); + m_flattenFormData.clear(); + auto length = blobData->length(); + return BlobDataHandle::create(blobData.release(), length); + } + PassRefPtr<EncodedFormData> drainFormData() { ASSERT(!m_formData || m_formData->isSafeToSendToAnotherThread()); @@ -124,6 +136,11 @@ { return m_context->endRead(read); } + PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(BlobSizePolicy) override + { + // A "simple" FormData always has a finite known size. + return m_context->drainAsBlobDataHandle(); + } PassRefPtr<EncodedFormData> drainAsFormData() override { return m_context->drainFormData();
diff --git a/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandleTest.cpp b/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandleTest.cpp index 500b9e71..888dd3a 100644 --- a/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandleTest.cpp +++ b/third_party/WebKit/Source/modules/fetch/FetchFormDataConsumerHandleTest.cpp
@@ -226,11 +226,69 @@ EXPECT_EQ("bar", toString(r->data())); } -TEST_F(FetchFormDataConsumerHandleTest, DrainAsBlobDataHandle) +TEST_F(FetchFormDataConsumerHandleTest, DrainAsBlobDataHandleFromString) { OwnPtr<FetchDataConsumerHandle> handle = FetchFormDataConsumerHandle::create(String("hello, world")); OwnPtr<FetchDataConsumerHandle::Reader> reader = handle->obtainReader(nullptr); - EXPECT_FALSE(reader->drainAsBlobDataHandle(FetchDataConsumerHandle::Reader::AllowBlobWithInvalidSize)); + RefPtr<BlobDataHandle> blobDataHandle = reader->drainAsBlobDataHandle(); + ASSERT_TRUE(blobDataHandle); + + EXPECT_EQ(String(), blobDataHandle->type()); + EXPECT_EQ(12u, blobDataHandle->size()); + EXPECT_EQ(nullptr, reader->drainAsFormData()); + char c; + size_t readSize; + EXPECT_EQ(kDone, reader->read(&c, 1, kNone, &readSize)); +} + +TEST_F(FetchFormDataConsumerHandleTest, DrainAsBlobDataHandleFromArrayBuffer) +{ + OwnPtr<FetchDataConsumerHandle> handle = FetchFormDataConsumerHandle::create(DOMArrayBuffer::create("foo", 3)); + OwnPtr<FetchDataConsumerHandle::Reader> reader = handle->obtainReader(nullptr); + RefPtr<BlobDataHandle> blobDataHandle = reader->drainAsBlobDataHandle(); + ASSERT_TRUE(blobDataHandle); + + EXPECT_EQ(String(), blobDataHandle->type()); + EXPECT_EQ(3u, blobDataHandle->size()); + EXPECT_EQ(nullptr, reader->drainAsFormData()); + char c; + size_t readSize; + EXPECT_EQ(kDone, reader->read(&c, 1, kNone, &readSize)); +} + +TEST_F(FetchFormDataConsumerHandleTest, DrainAsBlobDataHandleFromSimpleFormData) +{ + FormData* data = FormData::create(UTF8Encoding()); + data->append("name1", "value1"); + data->append("name2", "value2"); + RefPtr<EncodedFormData> inputFormData = data->encodeMultiPartFormData(); + + OwnPtr<FetchDataConsumerHandle> handle = FetchFormDataConsumerHandle::create(document(), inputFormData); + OwnPtr<FetchDataConsumerHandle::Reader> reader = handle->obtainReader(nullptr); + RefPtr<BlobDataHandle> blobDataHandle = reader->drainAsBlobDataHandle(); + ASSERT_TRUE(blobDataHandle); + + EXPECT_EQ(String(), blobDataHandle->type()); + EXPECT_EQ(inputFormData->flattenToString().utf8().length(), blobDataHandle->size()); + EXPECT_EQ(nullptr, reader->drainAsFormData()); + char c; + size_t readSize; + EXPECT_EQ(kDone, reader->read(&c, 1, kNone, &readSize)); +} + +TEST_F(FetchFormDataConsumerHandleTest, DrainAsBlobDataHandleFromComplexFormData) +{ + RefPtr<EncodedFormData> inputFormData = complexFormData(); + + OwnPtr<FetchDataConsumerHandle> handle = FetchFormDataConsumerHandle::create(document(), inputFormData); + OwnPtr<FetchDataConsumerHandle::Reader> reader = handle->obtainReader(nullptr); + RefPtr<BlobDataHandle> blobDataHandle = reader->drainAsBlobDataHandle(); + ASSERT_TRUE(blobDataHandle); + + EXPECT_EQ(nullptr, reader->drainAsFormData()); + char c; + size_t readSize; + EXPECT_EQ(kDone, reader->read(&c, 1, kNone, &readSize)); } TEST_F(FetchFormDataConsumerHandleTest, DrainAsFormDataFromString)
diff --git a/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp b/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp index 4794fb73..d84100f 100644 --- a/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp +++ b/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
@@ -7,6 +7,8 @@ #include "core/dom/DOMError.h" #include "core/fileapi/Blob.h" +#include "core/frame/ConsoleTypes.h" +#include "core/inspector/ConsoleMessage.h" #include "modules/EventModules.h" #include "modules/EventTargetModules.h" #include "modules/mediarecorder/BlobEvent.h" @@ -64,6 +66,11 @@ { ASSERT(m_stream->getTracks().size()); + // Recording remote Audio streams is not supported: http://crbug.com/121673. + if (!stream->getAudioTracks().isEmpty() && stream->getAudioTracks()[0]->remote()) { + context->addConsoleMessage(ConsoleMessage::create(JSMessageSource, WarningMessageLevel, "Recording remote audio tracks is not supported, ignoring them.")); + } + m_recorderHandler = adoptPtr(Platform::current()->createMediaRecorderHandler()); ASSERT(m_recorderHandler);
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn index 6da7207..fa91981 100644 --- a/third_party/WebKit/Source/platform/BUILD.gn +++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -335,6 +335,15 @@ } } +# TODO(GYP): Delete this after we've converted everything to GN. +# The _run targets exist only for compatibility w/ GYP. +group("blink_heap_unittests_run") { + testonly = true + deps = [ + ":blink_heap_unittests", + ] +} + # GYP: blink_heap_unittests test("blink_heap_unittests") { visibility = [] # Allow re-assignment of list. @@ -370,6 +379,15 @@ } } +# TODO(GYP): Delete this after we've converted everything to GN. +# The _run targets exist only for compatibility w/ GYP. +group("blink_platform_unittests_run") { + testonly = true + deps = [ + ":blink_platform_unittests", + ] +} + test("blink_platform_unittests") { visibility = [] # Allow re-assignment of list. visibility = [ "*" ]
diff --git a/third_party/WebKit/Source/platform/exported/WebString.cpp b/third_party/WebKit/Source/platform/exported/WebString.cpp index 1dafd9f..81e0fcf 100644 --- a/third_party/WebKit/Source/platform/exported/WebString.cpp +++ b/third_party/WebKit/Source/platform/exported/WebString.cpp
@@ -120,6 +120,11 @@ return equal(m_private.get(), s.m_private.get()); } +bool WebString::equals(const char* characters) const +{ + return equal(m_private.get(), reinterpret_cast<const LChar*>(characters)); +} + WebString::WebString(const WTF::String& s) : m_private(s.impl()) {
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp index de389a89..c27bd0b4 100644 --- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp +++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
@@ -89,8 +89,6 @@ SkCanvas* ImageBuffer::canvas() const { - if (!isSurfaceValid()) - return nullptr; return m_surface->canvas(); }
diff --git a/third_party/WebKit/Source/platform/heap/BUILD.gn b/third_party/WebKit/Source/platform/heap/BUILD.gn index 5e77093..5166b28 100644 --- a/third_party/WebKit/Source/platform/heap/BUILD.gn +++ b/third_party/WebKit/Source/platform/heap/BUILD.gn
@@ -49,6 +49,7 @@ configs += [ "//third_party/WebKit/Source:config", "//third_party/WebKit/Source:inside_blink", + "//third_party/WebKit/Source:features", "//build/config/compiler:no_size_t_to_int_warning", ]
diff --git a/third_party/WebKit/Source/platform/weborigin/KURLHash.h b/third_party/WebKit/Source/platform/weborigin/KURLHash.h index 78496835..3b5f543 100644 --- a/third_party/WebKit/Source/platform/weborigin/KURLHash.h +++ b/third_party/WebKit/Source/platform/weborigin/KURLHash.h
@@ -32,6 +32,10 @@ namespace blink { +// KURLHash doesn't support null KURLs. get(), contains(), and add() on +// HashMap<KURL,..., KURLHash> cause a null-pointer dereference when passed null +// KURLs. + struct KURLHash { static unsigned hash(const KURL& key) {
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn index 14d51c1..0113419 100644 --- a/third_party/WebKit/Source/web/BUILD.gn +++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -97,6 +97,29 @@ } } +# TODO(GYP): Delete this after we've converted everything to GN. +# The _run targets exist only for compatibility w/ GYP. +group("webkit_unit_tests_run") { + testonly = true + deps = [ + ":webkit_unit_tests", + ] +} + +group("webkit_unit_tests_data") { + data = [ + "tests/data/", + "../core/paint/test_data/", + + # Required by some image decoder tests. + "../platform/image-decoders/testing/", + "../../LayoutTests/fast/images/resources/", + + # Required by some font tests. + "../platform/testing/data/", + ] +} + # GYP version: WebKit/Source/web/web_tests.gyp:webkit_unit_tests test("webkit_unit_tests") { visibility = [] # Allow re-assignment of list. @@ -120,6 +143,11 @@ "//v8", ] + data_deps = [ + "//content/shell:pak", + ":webkit_unit_tests_data", + ] + sources = [ "tests/RunAllTests.cpp", ]
diff --git a/third_party/WebKit/Source/web/WebPageSerializerImpl.cpp b/third_party/WebKit/Source/web/WebPageSerializerImpl.cpp index 3c933e1..b2a8a48 100644 --- a/third_party/WebKit/Source/web/WebPageSerializerImpl.cpp +++ b/third_party/WebKit/Source/web/WebPageSerializerImpl.cpp
@@ -443,6 +443,10 @@ didSerialization = true; const WTF::TextEncoding& textEncoding = document->encoding().isValid() ? document->encoding() : UTF8Encoding(); + if (textEncoding.isNonByteBasedEncoding()) { + const UChar byteOrderMark = 0xFEFF; + m_dataBuffer.append(byteOrderMark); + } SerializeDomParam param(url, textEncoding, document);
diff --git a/third_party/WebKit/Source/wtf/BUILD.gn b/third_party/WebKit/Source/wtf/BUILD.gn index f4e21d7..5db6483a 100644 --- a/third_party/WebKit/Source/wtf/BUILD.gn +++ b/third_party/WebKit/Source/wtf/BUILD.gn
@@ -95,6 +95,15 @@ } } +# TODO(GYP): Delete this after we've converted everything to GN. +# The _run targets exist only for compatibility w/ GYP. +group("wtf_unittests_run") { + testonly = true + deps = [ + ":wtf_unittests", + ] +} + test("wtf_unittests") { visibility = [] # Allow re-assignment of list. visibility = [ "*" ]
diff --git a/third_party/WebKit/Source/wtf/text/StringImpl.cpp b/third_party/WebKit/Source/wtf/text/StringImpl.cpp index d3a07291..3eb20fa 100644 --- a/third_party/WebKit/Source/wtf/text/StringImpl.cpp +++ b/third_party/WebKit/Source/wtf/text/StringImpl.cpp
@@ -1425,47 +1425,6 @@ return reverseFindInner(characters16(), matchString->characters16(), index, ourLength, matchLength); } -template <typename SearchCharacterType, typename MatchCharacterType> -ALWAYS_INLINE static size_t reverseFindIgnoringCaseInner(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned length, unsigned matchLength) -{ - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = min(index, length - matchLength); - - // keep looping until we match - while (!equalIgnoringCase(searchCharacters + delta, matchCharacters, matchLength)) { - if (!delta) - return kNotFound; - --delta; - } - return delta; -} - -size_t StringImpl::reverseFindIgnoringCase(StringImpl* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (!matchString) - return kNotFound; - unsigned matchLength = matchString->length(); - unsigned ourLength = length(); - if (!matchLength) - return min(index, ourLength); - - // Check index & matchLength are in range. - if (matchLength > ourLength) - return kNotFound; - - if (is8Bit()) { - if (matchString->is8Bit()) - return reverseFindIgnoringCaseInner(characters8(), matchString->characters8(), index, ourLength, matchLength); - return reverseFindIgnoringCaseInner(characters8(), matchString->characters16(), index, ourLength, matchLength); - } - - if (matchString->is8Bit()) - return reverseFindIgnoringCaseInner(characters16(), matchString->characters8(), index, ourLength, matchLength); - - return reverseFindIgnoringCaseInner(characters16(), matchString->characters16(), index, ourLength, matchLength); -} - ALWAYS_INLINE static bool equalInner(const StringImpl* stringImpl, unsigned startOffset, const LChar* matchString, unsigned matchLength) { ASSERT(stringImpl);
diff --git a/third_party/WebKit/Source/wtf/text/StringImpl.h b/third_party/WebKit/Source/wtf/text/StringImpl.h index 889e3252..d93f6d2 100644 --- a/third_party/WebKit/Source/wtf/text/StringImpl.h +++ b/third_party/WebKit/Source/wtf/text/StringImpl.h
@@ -379,7 +379,6 @@ size_t reverseFind(UChar, unsigned index = UINT_MAX); size_t reverseFind(StringImpl*, unsigned index = UINT_MAX); - size_t reverseFindIgnoringCase(StringImpl*, unsigned index = UINT_MAX); size_t count(LChar) const;
diff --git a/third_party/WebKit/Source/wtf/text/TextEncoding.h b/third_party/WebKit/Source/wtf/text/TextEncoding.h index 71b829a..bb40b961 100644 --- a/third_party/WebKit/Source/wtf/text/TextEncoding.h +++ b/third_party/WebKit/Source/wtf/text/TextEncoding.h
@@ -54,8 +54,9 @@ CString encode(const String&, UnencodableHandling) const; -private: bool isNonByteBasedEncoding() const; + +private: bool isUTF7Encoding() const; const char* m_name;
diff --git a/third_party/WebKit/Source/wtf/text/WTFString.h b/third_party/WebKit/Source/wtf/text/WTFString.h index 2f4be91..f692c9f 100644 --- a/third_party/WebKit/Source/wtf/text/WTFString.h +++ b/third_party/WebKit/Source/wtf/text/WTFString.h
@@ -235,16 +235,12 @@ { return m_impl ? m_impl->findIgnoringCase(str, start) : kNotFound; } size_t findIgnoringCase(const String& str, unsigned start = 0) const { return m_impl ? m_impl->findIgnoringCase(str.impl(), start) : kNotFound; } - size_t reverseFindIgnoringCase(const String& str, unsigned start = UINT_MAX) const - { return m_impl ? m_impl->reverseFindIgnoringCase(str.impl(), start) : kNotFound; } - // Wrappers for find & reverseFind adding dynamic sensitivity check. + // Wrappers for find adding dynamic sensitivity check. size_t find(const LChar* str, unsigned start, TextCaseSensitivity caseSensitivity) const { return DISPATCH_CASE_OP(caseSensitivity, find, (str, start)); } size_t find(const String& str, unsigned start, TextCaseSensitivity caseSensitivity) const { return DISPATCH_CASE_OP(caseSensitivity, find, (str, start)); } - size_t reverseFind(const String& str, unsigned start, TextCaseSensitivity caseSensitivity) const - { return (caseSensitivity == TextCaseSensitive) ? reverseFind(str, start) : reverseFindIgnoringCase(str, start); } Vector<UChar> charactersWithNullTermination() const; unsigned copyTo(UChar* buffer, unsigned pos, unsigned maxLength) const;
diff --git a/third_party/WebKit/public/platform/WebString.h b/third_party/WebKit/public/platform/WebString.h index fa72a55e..7c103d1 100644 --- a/third_party/WebKit/public/platform/WebString.h +++ b/third_party/WebKit/public/platform/WebString.h
@@ -78,6 +78,7 @@ BLINK_COMMON_EXPORT void assign(const WebUChar* data, size_t len); BLINK_COMMON_EXPORT bool equals(const WebString&) const; + BLINK_COMMON_EXPORT bool equals(const char* characters) const; BLINK_COMMON_EXPORT size_t length() const; @@ -175,6 +176,26 @@ WebPrivatePtr<WTF::StringImpl> m_private; }; +inline bool operator==(const WebString& a, const char* b) +{ + return a.equals(b); +} + +inline bool operator!=(const WebString& a, const char* b) +{ + return !(a == b); +} + +inline bool operator==(const char* a, const WebString& b) +{ + return b.equals(a); +} + +inline bool operator!=(const char* a, const WebString& b) +{ + return !(a == b); +} + inline bool operator==(const WebString& a, const WebString& b) { return a.equals(b);
diff --git a/third_party/crashpad/DEPS b/third_party/crashpad/DEPS new file mode 100644 index 0000000..64022c3 --- /dev/null +++ b/third_party/crashpad/DEPS
@@ -0,0 +1,9 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Crashpad has its own #include structure not rooted at Chromium’s root. + +skip_child_includes = [ + 'crashpad', +]
diff --git a/third_party/crashpad/OWNERS b/third_party/crashpad/OWNERS index 1e00267..bc85a4b2 100644 --- a/third_party/crashpad/OWNERS +++ b/third_party/crashpad/OWNERS
@@ -1,3 +1,15 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# The rules for third_party/crashpad/crashpad: +# +# Aside from work necessary to adhere to changes in Chromium interfaces used by +# Crashpad, all changes should first be made in the upstream Crashpad +# repository, and then imported here. See README.chromium. + +set noparent + mark@chromium.org rsesek@chromium.org scottmg@chromium.org
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium index 57b42df..6b7cdd490 100644 --- a/third_party/crashpad/README.chromium +++ b/third_party/crashpad/README.chromium
@@ -1,14 +1,38 @@ Name: Crashpad Short Name: crashpad -URL: https://crashpad.googlecode.com/ +URL: https://crashpad.chromium.org/ Version: unknown -Revision: see DEPS +Revision: 6bebb10829332dee5c7315abafb0a8bf32840c15 License: Apache 2.0 License File: crashpad/LICENSE Security Critical: yes Description: -Crashpad is a crash-reporting system. It's the successor to Breakpad. +Crashpad is a crash-reporting system. It’s the successor to Breakpad. + +Crashpad’s authoritative upstream repository is +https://chromium.googlesource.com/crashpad/crashpad. + +Because Crashpad depends on some Chromium code (notably base and build), it is +acceptable to make changes to this in-Chromium copy of Crashpad in order to +conform to interface changes in Chromium. These changes must be noted in the +“local modifications” section below, and should also be reflected in the +authoritative Crashpad repository as soon as practical. + +Substantive development must occur in the authoritative Crashpad repository. If +this type of work is done in the in-Chromium copy, it will be obliterated the +next time Crashpad is imported into Chromium. + +To update the in-Chromium copy of Crashpad, run update.py, located in this +directory. + +To carry changes made in Chromium to Crashpad, run: +$ cd "${THIS_DIR}" +$ mkdir /tmp/patchdir +$ git format-patch -1 --binary --output-directory=/tmp/patchdir/cur "${HASH}" \ + --add-header="Message-Id: Merged from chromium ${HASH}" -- crashpad/ +$ cd "${CRASHPAD_DIR}" +$ git am --3way --message-id -p4 /tmp/patchdir Local Modifications: -None. + - Cherry-pick upstream 7efdc94f5982a1f9654c53fd4bf5663e5efa66ce.
diff --git a/third_party/crashpad/crashpad/.clang-format b/third_party/crashpad/crashpad/.clang-format new file mode 100644 index 0000000..0be5323 --- /dev/null +++ b/third_party/crashpad/crashpad/.clang-format
@@ -0,0 +1,19 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + BasedOnStyle: Chromium, + AlignTrailingComments: false, + BinPackArguments: false, +}
diff --git a/third_party/crashpad/crashpad/.gitignore b/third_party/crashpad/crashpad/.gitignore new file mode 100644 index 0000000..c4f8fbb --- /dev/null +++ b/third_party/crashpad/crashpad/.gitignore
@@ -0,0 +1,17 @@ +*.Makefile +*.ninja +*.pyc +*.target.mk +*.xcodeproj +*~ +.*.sw? +.DS_Store +.gdb_history +.gdbinit +/Makefile +/out +/third_party/gtest/gtest +/third_party/gyp/gyp +/third_party/mini_chromium/mini_chromium +/xcodebuild +tags
diff --git a/third_party/crashpad/crashpad/AUTHORS b/third_party/crashpad/crashpad/AUTHORS new file mode 100644 index 0000000..dd7ff26 --- /dev/null +++ b/third_party/crashpad/crashpad/AUTHORS
@@ -0,0 +1,9 @@ +# This is the official list of Crashpad authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as: +# Name or Organization <email address> +# The email address is not required for organizations. + +Google Inc.
diff --git a/third_party/crashpad/crashpad/CONTRIBUTORS b/third_party/crashpad/crashpad/CONTRIBUTORS new file mode 100644 index 0000000..2080cc45 --- /dev/null +++ b/third_party/crashpad/crashpad/CONTRIBUTORS
@@ -0,0 +1,14 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# Names should be added to this file as: +# Name <email address> + +Mark Mentovai <mark@chromium.org> +Robert Sesek <rsesek@chromium.org> +Scott Graham <scottmg@chromium.org>
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS new file mode 100644 index 0000000..ed993d7 --- /dev/null +++ b/third_party/crashpad/crashpad/DEPS
@@ -0,0 +1,68 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +vars = { + 'chromium_git': 'https://chromium.googlesource.com', +} + +deps = { + 'crashpad/third_party/gtest/gtest': + Var('chromium_git') + '/external/github.com/google/googletest@' + + '786564fa4a3c8e0f908acca32cce481de5481b9f', + 'crashpad/third_party/gyp/gyp': + Var('chromium_git') + '/external/gyp@' + + '01528c7244837168a1c80f06ff60fa5a9793c824', + 'crashpad/third_party/mini_chromium/mini_chromium': + Var('chromium_git') + '/chromium/mini_chromium@' + + '43f04e7299621f708801bf475c84d9f294826ff9', + 'buildtools': + Var('chromium_git') + '/chromium/buildtools.git@' + + 'c2f259809d5ede3275df5ea0842f0431990c4f98', +} + +hooks = [ + { + 'name': 'clang_format_mac', + 'pattern': '.', + 'action': [ + 'download_from_google_storage', + '--platform=^darwin$', + '--no_resume', + '--no_auth', + '--bucket=chromium-clang-format', + '--output=buildtools/mac/clang-format', + '--sha1_file', + 'buildtools/mac/clang-format.sha1', + ], + }, + { + 'name': 'clang_format_win', + 'pattern': '.', + 'action': [ + 'download_from_google_storage', + '--platform=^win32$', + '--no_resume', + '--no_auth', + '--bucket=chromium-clang-format', + '--output=buildtools/win/clang-format.exe', + '--sha1_file', + 'buildtools/win/clang-format.exe.sha1', + ], + }, + { + 'name': 'gyp', + 'pattern': '\.gypi?$', + 'action': ['python', 'crashpad/build/gyp_crashpad.py'], + }, +]
diff --git a/third_party/crashpad/crashpad/LICENSE b/third_party/crashpad/crashpad/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/crashpad/crashpad/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/crashpad/crashpad/build/crashpad.gypi b/third_party/crashpad/crashpad/build/crashpad.gypi new file mode 100644 index 0000000..027c7b6 --- /dev/null +++ b/third_party/crashpad/crashpad/build/crashpad.gypi
@@ -0,0 +1,29 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'variables': { + # When building as a part of Chromium, this variable sets up the build to + # treat Crashpad as Chromium code. This enables warnings at an appropriate + # level and applies Chromium’s build/filename_rules.gypi. In a standalone + # build, this variable has no effect. + 'chromium_code': 1, + }, + 'target_defaults': { + 'msvs_disabled_warnings': [ + 4201, # nonstandard extension used : nameless struct/union. + 4324, # structure was padded due to __declspec(align()). + ], + }, +}
diff --git a/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi b/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi new file mode 100644 index 0000000..0f646377 --- /dev/null +++ b/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi
@@ -0,0 +1,43 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + # Crashpad can build as a standalone project or as part of Chromium. When + # building as a standalone project, it uses mini_chromium to provide the base + # library, and uses its own copy of gtest in third_party. When building as + # part of Chromium, it uses Chromium’s base library and copy of gtest. In + # order for Crashpad’s .gyp files to reference the correct versions depending + # on whether building standalone or as a part of Chromium, include this .gypi + # file and reference the crashpad_in_chromium variable. + + 'variables': { + 'variables': { + # When building as a standalone project, build/gyp_crashpad.py sets + # crashpad_standalone to 1, and this % assignment will not override it. + # The variable will not be set when building as part of Chromium, so in + # that case, this will define it with value 0. + 'crashpad_standalone%': 0, + }, + + 'conditions': [ + ['crashpad_standalone!=0', { + 'crashpad_in_chromium': 0, + }, { + 'crashpad_in_chromium': 1, + }], + ], + + 'crashpad_in_chromium': '<(crashpad_in_chromium)', + }, +}
diff --git a/third_party/crashpad/crashpad/build/gyp_crashpad.py b/third_party/crashpad/crashpad/build/gyp_crashpad.py new file mode 100755 index 0000000..42818ec --- /dev/null +++ b/third_party/crashpad/crashpad/build/gyp_crashpad.py
@@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +import os +import sys + +script_dir = os.path.dirname(__file__) +crashpad_dir = os.path.dirname(script_dir) if script_dir is not '' else '..' +sys.path.insert( + 0, os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib')) + +import gyp + + +def main(args): + if 'GYP_GENERATORS' not in os.environ: + os.environ['GYP_GENERATORS'] = 'ninja' + + crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else '.' + + args.extend(['-D', 'crashpad_standalone=1']) + args.extend(['--include', + os.path.join(crashpad_dir, 'third_party', 'mini_chromium', + 'mini_chromium', 'build', 'common.gypi')]) + args.extend(['--depth', crashpad_dir_or_dot]) + args.append(os.path.join(crashpad_dir, 'crashpad.gyp')) + + result = gyp.main(args) + if result != 0: + return result + + if sys.platform == 'win32': + # Also generate the x86 build. + result = gyp.main(args + ['-D', 'target_arch=ia32', '-G', 'config=Debug']) + if result != 0: + return result + result = gyp.main(args + ['-D', 'target_arch=ia32', '-G', 'config=Release']) + + return result + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/build/run_tests.py b/third_party/crashpad/crashpad/build/run_tests.py new file mode 100755 index 0000000..f630685 --- /dev/null +++ b/third_party/crashpad/crashpad/build/run_tests.py
@@ -0,0 +1,73 @@ +#!/usr/bin/env python + +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +import os +import platform +import subprocess +import sys + + +# This script is primarily used from the waterfall so that the list of tests +# that are run is maintained in-tree, rather than in a separate infrastructure +# location in the recipe. +def main(args): + if len(args) != 1: + print >> sys.stderr, \ + 'usage: run_tests.py {Debug|Release|Debug_x64|Release_x64}' + return 1 + + crashpad_dir = \ + os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) + + # In a standalone Crashpad build, the out directory is in the Crashpad root. + out_dir = os.path.join(crashpad_dir, 'out') + if not os.path.exists(out_dir): + # In an in-Chromium build, the out directory is in the Chromium root, and + # the Crashpad root is in third_party/crashpad/crashpad relative to the + # Chromium root. + chromium_dir = os.path.join(crashpad_dir, os.pardir, os.pardir, os.pardir) + out_dir = os.path.join(chromium_dir, 'out') + if not os.path.exists(out_dir): + raise Exception('could not determine out_dir', crashpad_dir) + + binary_dir = os.path.join(out_dir, args[0]) + + tests = [ + 'crashpad_client_test', + 'crashpad_minidump_test', + 'crashpad_snapshot_test', + 'crashpad_test_test', + 'crashpad_util_test', + ] + for test in tests: + print '-' * 80 + print test + print '-' * 80 + subprocess.check_call(os.path.join(binary_dir, test)) + + if sys.platform == 'win32': + name = 'snapshot/win/end_to_end_test.py' + print '-' * 80 + print name + print '-' * 80 + subprocess.check_call( + [sys.executable, os.path.join(crashpad_dir, name), binary_dir]) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/client/capture_context_mac.S b/third_party/crashpad/crashpad/client/capture_context_mac.S new file mode 100644 index 0000000..942d841 --- /dev/null +++ b/third_party/crashpad/crashpad/client/capture_context_mac.S
@@ -0,0 +1,218 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#if defined(__i386__) || defined(__x86_64__) + +// namespace crashpad { +// void CaptureContext(x86_thread_state_t* x86_thread_state); +// } // namespace crashpad +#define CAPTURECONTEXT_SYMBOL __ZN8crashpad14CaptureContextEP16x86_thread_state + + .section __TEXT,__text,regular,pure_instructions + .private_extern CAPTURECONTEXT_SYMBOL + .globl CAPTURECONTEXT_SYMBOL + .align 4, 0x90 +CAPTURECONTEXT_SYMBOL: + +#if defined(__i386__) + + .cfi_startproc + + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp, %ebp + .cfi_def_cfa_register %ebp + + // Note that 16-byte stack alignment is not maintained because this function + // does not call out to any other. + + // pushfl first, because some instructions (but probably none used here) + // affect %eflags. %eflags will be in -4(%ebp). + pushfl + + // Save the original value of %eax, and use %eax to hold the x86_thread_state* + // argument. The original value of %eax will be in -8(%ebp). + pushl %eax + movl 8(%ebp), %eax + + // Initialize the header identifying the x86_thread_state_t structure as + // carrying an x86_thread_state32_t (flavor x86_THREAD_STATE32) of size + // x86_THREAD_STATE32_COUNT 32-bit values. + movl $1, (%eax) // x86_thread_state->tsh.flavor + movl $16, 4(%eax) // x86_thread_state->tsh.count + + // General-purpose registers whose values haven’t changed can be captured + // directly. + movl %ebx, 12(%eax) // x86_thread_state->uts.ts32.__ebx + movl %ecx, 16(%eax) // x86_thread_state->uts.ts32.__ecx + movl %edx, 20(%eax) // x86_thread_state->uts.ts32.__edx + movl %edi, 24(%eax) // x86_thread_state->uts.ts32.__edi + movl %esi, 28(%eax) // x86_thread_state->uts.ts32.__esi + + // Now that the original value of %edx has been saved, it can be repurposed to + // hold other registers’ values. + + // The original %eax was saved on the stack above. + movl -8(%ebp), %edx + movl %edx, 8(%eax) // x86_thread_state->uts.ts32.__eax + + // The original %ebp was saved on the stack in this function’s prologue. + movl (%ebp), %edx + movl %edx, 32(%eax) // x86_thread_state->uts.ts32.__ebp + + // %esp was saved in %ebp in this function’s prologue, but the caller’s %esp + // is 8 more than this value: 4 for the original %ebp saved on the stack in + // this function’s prologue, and 4 for the return address saved on the stack + // by the call instruction that reached this function. + leal 8(%ebp), %edx + movl %edx, 36(%eax) // x86_thread_state->uts.ts32.__esp + + // The original %eflags was saved on the stack above. + movl -4(%ebp), %edx + movl %edx, 44(%eax) // x86_thread_state->uts.ts32.__eflags + + // %eip can’t be accessed directly, but the return address saved on the stack + // by the call instruction that reached this function can be used. + movl 4(%ebp), %edx + movl %edx, 48(%eax) // x86_thread_state->uts.ts32.__eip + + // The segment registers are 16 bits wide, but x86_thread_state declares them + // as unsigned 32-bit values, so zero the top half. + xorl %edx, %edx + movw %ss, %dx + movl %edx, 40(%eax) // x86_thread_state->uts.ts32.__ss + movw %cs, %dx + movl %edx, 52(%eax) // x86_thread_state->uts.ts32.__cs + movw %ds, %dx + movl %edx, 56(%eax) // x86_thread_state->uts.ts32.__ds + movw %es, %dx + movl %edx, 60(%eax) // x86_thread_state->uts.ts32.__es + movw %fs, %dx + movl %edx, 64(%eax) // x86_thread_state->uts.ts32.__fs + movw %gs, %dx + movl %edx, 68(%eax) // x86_thread_state->uts.ts32.__gs + + // Clean up by restoring clobbered registers, even those considered volatile + // by the ABI, so that the captured context represents the state at this + // function’s exit. + movl 20(%eax), %edx // x86_thread_state->uts.ts32.__edx + popl %eax + popfl + + popl %ebp + + ret + + .cfi_endproc + +#elif defined(__x86_64__) + + .cfi_startproc + + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + + // Note that 16-byte stack alignment is not maintained because this function + // does not call out to any other. + + // pushfq first, because some instructions (but probably none used here) + // affect %rflags. %rflags will be in -8(%rbp). + pushfq + + // Initialize the header identifying the x86_thread_state_t structure as + // carrying an x86_thread_state64_t (flavor x86_THREAD_STATE64) of size + // x86_THREAD_STATE64_COUNT 32-bit values. + movl $4, (%rdi) // x86_thread_state->tsh.flavor + movl $42, 4(%rdi) // x86_thread_state->tsh.count + + // General-purpose registers whose values haven’t changed can be captured + // directly. + movq %rax, 8(%rdi) // x86_thread_state->uts.ts64.__rax + movq %rbx, 16(%rdi) // x86_thread_state->uts.ts64.__rbx + movq %rcx, 24(%rdi) // x86_thread_state->uts.ts64.__rcx + movq %rdx, 32(%rdi) // x86_thread_state->uts.ts64.__rdx + movq %rsi, 48(%rdi) // x86_thread_state->uts.ts64.__rsi + movq %r8, 72(%rdi) // x86_thread_state->uts.ts64.__r8 + movq %r9, 80(%rdi) // x86_thread_state->uts.ts64.__r9 + movq %r10, 88(%rdi) // x86_thread_state->uts.ts64.__r10 + movq %r11, 96(%rdi) // x86_thread_state->uts.ts64.__r11 + movq %r12, 104(%rdi) // x86_thread_state->uts.ts64.__r12 + movq %r13, 112(%rdi) // x86_thread_state->uts.ts64.__r13 + movq %r14, 120(%rdi) // x86_thread_state->uts.ts64.__r14 + movq %r15, 128(%rdi) // x86_thread_state->uts.ts64.__r15 + + // Because of the calling convention, there’s no way to recover the value of + // the caller’s %rdi as it existed prior to calling this function. This + // function captures a snapshot of the register state at its return, which + // involves %rdi containing a pointer to its first argument. Callers that + // require the value of %rdi prior to calling this function should obtain it + // separately. For example: + // uint64_t rdi; + // asm("movq %%rdi, %0" : "=m"(rdi)); + movq %rdi, 40(%rdi) // x86_thread_state->uts.ts64.__rdi + + // Now that the original value of %rax has been saved, it can be repurposed to + // hold other registers’ values. + + // The original %rbp was saved on the stack in this function’s prologue. + movq (%rbp), %rax + movq %rax, 56(%rdi) // x86_thread_state->uts.ts64.__rbp + + // %rsp was saved in %rbp in this function’s prologue, but the caller’s %rsp + // is 16 more than this value: 8 for the original %rbp saved on the stack in + // this function’s prologue, and 8 for the return address saved on the stack + // by the call instruction that reached this function. + leaq 16(%rbp), %rax + movq %rax, 64(%rdi) // x86_thread_state->uts.ts64.__rsp + + // %rip can’t be accessed directly, but the return address saved on the stack + // by the call instruction that reached this function can be used. + movq 8(%rbp), %rax + movq %rax, 136(%rdi) // x86_thread_state->uts.ts64.__rip + + // The original %rflags was saved on the stack above. + movq -8(%rbp), %rax + movq %rax, 144(%rdi) // x86_thread_state->uts.ts64.__rflags + + // The segment registers are 16 bits wide, but x86_thread_state declares them + // as unsigned 64-bit values, so zero the top portion. + xorq %rax, %rax + movw %cs, %ax + movq %rax, 152(%rdi) // x86_thread_state->uts.ts64.__cs + movw %fs, %ax + movq %rax, 160(%rdi) // x86_thread_state->uts.ts64.__fs + movw %gs, %ax + movq %rax, 168(%rdi) // x86_thread_state->uts.ts64.__gs + + // Clean up by restoring clobbered registers, even those considered volatile + // by the ABI, so that the captured context represents the state at this + // function’s exit. + movq 8(%rdi), %rax + popfq + + popq %rbp + + ret + + .cfi_endproc + +#endif + +.subsections_via_symbols + +#endif
diff --git a/third_party/crashpad/crashpad/client/capture_context_mac.h b/third_party/crashpad/crashpad/client/capture_context_mac.h new file mode 100644 index 0000000..74e440e --- /dev/null +++ b/third_party/crashpad/crashpad/client/capture_context_mac.h
@@ -0,0 +1,48 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_ +#define CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_ + +#include <mach/mach.h> + +#include "build/build_config.h" + +namespace crashpad { + +#if defined(ARCH_CPU_X86_FAMILY) +using NativeCPUContext = x86_thread_state; +#endif + +//! \brief Saves the CPU context. +//! +//! The CPU context will be captured as accurately and completely as possible, +//! containing an atomic snapshot at the point of this function’s return. This +//! function does not modify any registers. +//! +//! \param[out] cpu_context The structure to store the context in. +//! +//! \note On x86_64, the value for `%%rdi` will be populated with the address of +//! this function’s argument, as mandated by the ABI. If the value of +//! `%%rdi` prior to calling this function is needed, it must be obtained +//! separately prior to calling this function. For example: +//! \code +//! uint64_t rdi; +//! asm("movq %%rdi, %0" : "=m"(rdi)); +//! \endcode +void CaptureContext(NativeCPUContext* cpu_context); + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_
diff --git a/third_party/crashpad/crashpad/client/capture_context_mac_test.cc b/third_party/crashpad/crashpad/client/capture_context_mac_test.cc new file mode 100644 index 0000000..436ac5ad --- /dev/null +++ b/third_party/crashpad/crashpad/client/capture_context_mac_test.cc
@@ -0,0 +1,152 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/capture_context_mac.h" + +#include <mach/mach.h> +#include <stdint.h> + +#include <algorithm> + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +// If the context structure has fields that tell whether it’s valid, such as +// magic numbers or size fields, sanity-checks those fields for validity with +// fatal gtest assertions. For other fields, where it’s possible to reason about +// their validity based solely on their contents, sanity-checks via nonfatal +// gtest assertions. +void SanityCheckContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + ASSERT_EQ(x86_THREAD_STATE32, context.tsh.flavor); + ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT), context.tsh.count); +#elif defined(ARCH_CPU_X86_64) + ASSERT_EQ(x86_THREAD_STATE64, context.tsh.flavor); + ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT), context.tsh.count); +#endif + +#if defined(ARCH_CPU_X86_FAMILY) + // The segment registers are only capable of storing 16-bit quantities, but + // the context structure provides native integer-width fields for them. Ensure + // that the high bits are all clear. + // + // Many bit positions in the flags register are reserved and will always read + // a known value. Most reserved bits are always 0, but bit 1 is always 1. + // Check that the reserved bits are all set to their expected values. Note + // that the set of reserved bits may be relaxed over time with newer CPUs, and + // that this test may need to be changed to reflect these developments. The + // current set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel + // Software Developer’s Manual, Volume 1: Basic Architecture (253665-051), + // 3.4.3 “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume + // 2: System Programming (24593-3.24), 3.1.6 “RFLAGS Register”. +#if defined(ARCH_CPU_X86) + EXPECT_EQ(0u, context.uts.ts32.__cs & ~0xffff); + EXPECT_EQ(0u, context.uts.ts32.__ds & ~0xffff); + EXPECT_EQ(0u, context.uts.ts32.__es & ~0xffff); + EXPECT_EQ(0u, context.uts.ts32.__fs & ~0xffff); + EXPECT_EQ(0u, context.uts.ts32.__gs & ~0xffff); + EXPECT_EQ(0u, context.uts.ts32.__ss & ~0xffff); + EXPECT_EQ(2u, context.uts.ts32.__eflags & 0xffc0802a); +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(0u, context.uts.ts64.__cs & ~UINT64_C(0xffff)); + EXPECT_EQ(0u, context.uts.ts64.__fs & ~UINT64_C(0xffff)); + EXPECT_EQ(0u, context.uts.ts64.__gs & ~UINT64_C(0xffff)); + EXPECT_EQ(2u, context.uts.ts64.__rflags & UINT64_C(0xffffffffffc0802a)); +#endif +#endif +} + +// A CPU-independent function to return the program counter. +uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + return context.uts.ts32.__eip; +#elif defined(ARCH_CPU_X86_64) + return context.uts.ts64.__rip; +#endif +} + +// A CPU-independent function to return the stack pointer. +uintptr_t StackPointerFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + return context.uts.ts32.__esp; +#elif defined(ARCH_CPU_X86_64) + return context.uts.ts64.__rsp; +#endif +} + +void TestCaptureContext() { + NativeCPUContext context_1; + CaptureContext(&context_1); + + { + SCOPED_TRACE("context_1"); + ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_1)); + } + + // The program counter reference value is this function’s address. The + // captured program counter should be slightly greater than or equal to the + // reference program counter. + uintptr_t pc = ProgramCounterFromContext(context_1); +#if !__has_feature(address_sanitizer) + // AddressSanitizer can cause enough code bloat that the “nearby” check would + // likely fail. + const uintptr_t kReferencePC = + reinterpret_cast<uintptr_t>(TestCaptureContext); + EXPECT_LT(pc - kReferencePC, 64u); +#endif + + // Declare sp and context_2 here because all local variables need to be + // declared before computing the stack pointer reference value, so that the + // reference value can be the lowest value possible. + uintptr_t sp; + NativeCPUContext context_2; + + // The stack pointer reference value is the lowest address of a local variable + // in this function. The captured program counter will be slightly less than + // or equal to the reference stack pointer. + const uintptr_t kReferenceSP = + std::min(std::min(reinterpret_cast<uintptr_t>(&context_1), + reinterpret_cast<uintptr_t>(&context_2)), + std::min(reinterpret_cast<uintptr_t>(&pc), + reinterpret_cast<uintptr_t>(&sp))); + sp = StackPointerFromContext(context_1); + EXPECT_LT(kReferenceSP - sp, 512u); + + // Capture the context again, expecting that the stack pointer stays the same + // and the program counter increases. Strictly speaking, there’s no guarantee + // that these conditions will hold, although they do for known compilers even + // under typical optimization. + CaptureContext(&context_2); + + { + SCOPED_TRACE("context_2"); + ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_2)); + } + + EXPECT_EQ(sp, StackPointerFromContext(context_2)); + EXPECT_GT(ProgramCounterFromContext(context_2), pc); +} + +TEST(CaptureContextMac, CaptureContext) { + ASSERT_NO_FATAL_FAILURE(TestCaptureContext()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/client.gyp b/third_party/crashpad/crashpad/client/client.gyp new file mode 100644 index 0000000..765a2b47 --- /dev/null +++ b/third_party/crashpad/crashpad/client/client.gyp
@@ -0,0 +1,69 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_client', + 'type': 'static_library', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'capture_context_mac.S', + 'capture_context_mac.h', + 'crash_report_database.cc', + 'crash_report_database.h', + 'crash_report_database_mac.mm', + 'crash_report_database_win.cc', + 'crashpad_client.h', + 'crashpad_client_mac.cc', + 'crashpad_client_win.cc', + 'crashpad_info.cc', + 'crashpad_info.h', + 'prune_crash_reports.cc', + 'prune_crash_reports.h', + 'settings.cc', + 'settings.h', + 'simple_string_dictionary.cc', + 'simple_string_dictionary.h', + 'simulate_crash.h', + 'simulate_crash_mac.cc', + 'simulate_crash_mac.h', + 'simulate_crash_win.h', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-lrpcrt4.lib', + ], + }, + }], + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + }, + }, + ], +}
diff --git a/third_party/crashpad/crashpad/client/client_test.gyp b/third_party/crashpad/crashpad/client/client_test.gyp new file mode 100644 index 0000000..80cddc8 --- /dev/null +++ b/third_party/crashpad/crashpad/client/client_test.gyp
@@ -0,0 +1,47 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_client_test', + 'type': 'executable', + 'dependencies': [ + 'client.gyp:crashpad_client', + '../handler/handler.gyp:crashpad_handler', + '../test/test.gyp:crashpad_test', + '../third_party/gtest/gmock.gyp:gmock', + '../third_party/gtest/gmock.gyp:gmock_main', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'capture_context_mac_test.cc', + 'crash_report_database_test.cc', + 'crashpad_client_win_test.cc', + 'prune_crash_reports_test.cc', + 'settings_test.cc', + 'simple_string_dictionary_test.cc', + 'simulate_crash_mac_test.cc', + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/client/crash_report_database.cc b/third_party/crashpad/crashpad/client/crash_report_database.cc new file mode 100644 index 0000000..b758ee5 --- /dev/null +++ b/third_party/crashpad/crashpad/client/crash_report_database.cc
@@ -0,0 +1,47 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crash_report_database.h" + +namespace crashpad { + +CrashReportDatabase::Report::Report() + : uuid(), + file_path(), + id(), + creation_time(0), + uploaded(false), + last_upload_attempt_time(0), + upload_attempts(0) { +} + +CrashReportDatabase::CallErrorWritingCrashReport::CallErrorWritingCrashReport( + CrashReportDatabase* database, + NewReport* new_report) + : database_(database), + new_report_(new_report) { +} + +CrashReportDatabase::CallErrorWritingCrashReport:: + ~CallErrorWritingCrashReport() { + if (new_report_) { + database_->ErrorWritingCrashReport(new_report_); + } +} + +void CrashReportDatabase::CallErrorWritingCrashReport::Disarm() { + new_report_ = nullptr; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crash_report_database.h b/third_party/crashpad/crashpad/client/crash_report_database.h new file mode 100644 index 0000000..1897d2e --- /dev/null +++ b/third_party/crashpad/crashpad/client/crash_report_database.h
@@ -0,0 +1,339 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ +#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ + +#include <time.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "util/file/file_io.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class Settings; + +//! \brief An interface for managing a collection of crash report files and +//! metadata associated with the crash reports. +//! +//! All Report objects that are returned by this class are logically const. +//! They are snapshots of the database at the time the query was run, and the +//! data returned is liable to change after the query is executed. +//! +//! The lifecycle of a crash report has three stages: +//! +//! 1. New: A crash report is created with PrepareNewCrashReport(), the +//! the client then writes the report, and then calls +//! FinishedWritingCrashReport() to make the report Pending. +//! 2. Pending: The report has been written but has not been locally +//! processed. +//! 3. Completed: The report has been locally processed, either by uploading +//! it to a collection server and calling RecordUploadAttempt(), or by +//! calling SkipReportUpload(). +class CrashReportDatabase { + public: + //! \brief A crash report record. + //! + //! This represents the metadata for a crash report, as well as the location + //! of the report itself. A CrashReportDatabase maintains at least this + //! information. + struct Report { + Report(); + + //! A unique identifier by which this report will always be known to the + //! database. + UUID uuid; + + //! The current location of the crash report on the client’s filesystem. + //! The location of a crash report may change over time, so the UUID should + //! be used as the canonical identifier. + base::FilePath file_path; + + //! An identifier issued to this crash report by a collection server. + std::string id; + + //! The time at which the report was generated. + time_t creation_time; + + //! Whether this crash report was successfully uploaded to a collection + //! server. + bool uploaded; + + //! The last timestamp at which an attempt was made to submit this crash + //! report to a collection server. If this is zero, then the report has + //! never been uploaded. If #uploaded is true, then this timestamp is the + //! time at which the report was uploaded, and no other attempts to upload + //! this report will be made. + time_t last_upload_attempt_time; + + //! The number of times an attempt was made to submit this report to + //! a collection server. If this is more than zero, then + //! #last_upload_attempt_time will be set to the timestamp of the most + //! recent attempt. + int upload_attempts; + }; + + //! \brief A crash report that is in the process of being written. + //! + //! An instance of this struct should be created via PrepareNewCrashReport() + //! and destroyed with FinishedWritingCrashReport(). + struct NewReport { + //! The file handle to which the report should be written. + FileHandle handle; + + //! A unique identifier by which this report will always be known to the + //! database. + UUID uuid; + + //! The path to the crash report being written. + base::FilePath path; + }; + + //! \brief A scoper to cleanly handle the interface requirement imposed by + //! PrepareNewCrashReport(). + //! + //! Calls ErrorWritingCrashReport() upon destruction unless disarmed by + //! calling Disarm(). Armed upon construction. + class CallErrorWritingCrashReport { + public: + //! \brief Arms the object to call ErrorWritingCrashReport() on \a database + //! with an argument of \a new_report on destruction. + CallErrorWritingCrashReport(CrashReportDatabase* database, + NewReport* new_report); + + //! \brief Calls ErrorWritingCrashReport() if the object is armed. + ~CallErrorWritingCrashReport(); + + //! \brief Disarms the object so that CallErrorWritingCrashReport() will not + //! be called upon destruction. + void Disarm(); + + private: + CrashReportDatabase* database_; // weak + NewReport* new_report_; // weak + + DISALLOW_COPY_AND_ASSIGN(CallErrorWritingCrashReport); + }; + + //! \brief The result code for operations performed on a database. + enum OperationStatus { + //! \brief No error occurred. + kNoError = 0, + + //! \brief The report that was requested could not be located. + kReportNotFound, + + //! \brief An error occured while performing a file operation on a crash + //! report. + //! + //! A database is responsible for managing both the metadata about a report + //! and the actual crash report itself. This error is returned when an + //! error occurred when managing the report file. Additional information + //! will be logged. + kFileSystemError, + + //! \brief An error occured while recording metadata for a crash report or + //! database-wide settings. + //! + //! A database is responsible for managing both the metadata about a report + //! and the actual crash report itself. This error is returned when an + //! error occurred when managing the metadata about a crash report or + //! database-wide settings. Additional information will be logged. + kDatabaseError, + + //! \brief The operation could not be completed because a concurrent + //! operation affecting the report is occurring. + kBusyError, + }; + + virtual ~CrashReportDatabase() {} + + //! \brief Opens a database of crash reports, possibly creating it. + //! + //! \param[in] path A path to the database to be created or opened. If the + //! database does not yet exist, it will be created if possible. Note that + //! for databases implemented as directory structures, existence refers + //! solely to the outermost directory. + //! + //! \return A database object on success, `nullptr` on failure with an error + //! logged. + //! + //! \sa InitializeWithoutCreating + static scoped_ptr<CrashReportDatabase> Initialize(const base::FilePath& path); + + //! \brief Opens an existing database of crash reports. + //! + //! \param[in] path A path to the database to be opened. If the database does + //! not yet exist, it will not be created. Note that for databases + //! implemented as directory structures, existence refers solely to the + //! outermost directory. On such databases, as long as the outermost + //! directory is present, this method will create the inner structure. + //! + //! \return A database object on success, `nullptr` on failure with an error + //! logged. + //! + //! \sa Initialize + static scoped_ptr<CrashReportDatabase> InitializeWithoutCreating( + const base::FilePath& path); + + //! \brief Returns the Settings object for this database. + //! + //! \return A weak pointer to the Settings object, which is owned by the + //! database. + virtual Settings* GetSettings() = 0; + + //! \brief Creates a record of a new crash report. + //! + //! Callers can then write the crash report using the file handle provided. + //! The caller does not own the new crash report record or its file handle, + //! both of which must be explicitly disposed of by calling + //! FinishedWritingCrashReport() or ErrorWritingCrashReport(). + //! + //! To arrange to call ErrorWritingCrashReport() during any early return, use + //! CallErrorWritingCrashReport. + //! + //! \param[out] report A NewReport object containing a file handle to which + //! the crash report data should be written. Only valid if this returns + //! #kNoError. The caller must not delete the NewReport object or close + //! the file handle within. + //! + //! \return The operation status code. + virtual OperationStatus PrepareNewCrashReport(NewReport** report) = 0; + + //! \brief Informs the database that a crash report has been written. + //! + //! After calling this method, the database is permitted to move and rename + //! the file at NewReport::path. + //! + //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The + //! NewReport object and file handle within will be invalidated as part of + //! this call. + //! \param[out] uuid The UUID of this crash report. + //! + //! \return The operation status code. + virtual OperationStatus FinishedWritingCrashReport(NewReport* report, + UUID* uuid) = 0; + + //! \brief Informs the database that an error occurred while attempting to + //! write a crash report, and that any resources associated with it should + //! be cleaned up. + //! + //! After calling this method, the database is permitted to remove the file at + //! NewReport::path. + //! + //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The + //! NewReport object and file handle within will be invalidated as part of + //! this call. + //! + //! \return The operation status code. + virtual OperationStatus ErrorWritingCrashReport(NewReport* report) = 0; + + //! \brief Returns the crash report record for the unique identifier. + //! + //! \param[in] uuid The crash report record unique identifier. + //! \param[out] report A crash report record. Only valid if this returns + //! #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus LookUpCrashReport(const UUID& uuid, + Report* report) = 0; + + //! \brief Returns a list of crash report records that have not been uploaded. + //! + //! \param[out] reports A list of crash report record objects. This must be + //! empty on entry. Only valid if this returns #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus GetPendingReports(std::vector<Report>* reports) = 0; + + //! \brief Returns a list of crash report records that have been completed, + //! either by being uploaded or by skipping upload. + //! + //! \param[out] reports A list of crash report record objects. This must be + //! empty on entry. Only valid if this returns #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus GetCompletedReports(std::vector<Report>* reports) = 0; + + //! \brief Obtains a report object for uploading to a collection server. + //! + //! The file at Report::file_path should be uploaded by the caller, and then + //! the returned Report object must be disposed of via a call to + //! RecordUploadAttempt(). + //! + //! A subsequent call to this method with the same \a uuid is illegal until + //! RecordUploadAttempt() has been called. + //! + //! \param[in] uuid The unique identifier for the crash report record. + //! \param[out] report A crash report record for the report to be uploaded. + //! The caller does not own this object. Only valid if this returns + //! #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus GetReportForUploading(const UUID& uuid, + const Report** report) = 0; + + //! \brief Adjusts a crash report record’s metadata to account for an upload + //! attempt, and updates the last upload attempt time as returned by + //! Settings::GetLastUploadAttemptTime(). + //! + //! After calling this method, the database is permitted to move and rename + //! the file at Report::file_path. + //! + //! \param[in] report The report object obtained from + //! GetReportForUploading(). This object is invalidated after this call. + //! \param[in] successful Whether the upload attempt was successful. + //! \param[in] id The identifier assigned to this crash report by the + //! collection server. Must be empty if \a successful is `false`; may be + //! empty if it is `true`. + //! + //! \return The operation status code. + virtual OperationStatus RecordUploadAttempt(const Report* report, + bool successful, + const std::string& id) = 0; + + //! \brief Moves a report from the pending state to the completed state, but + //! without the report being uploaded. + //! + //! This can be used if the user has disabled crash report collection, but + //! crash generation is still enabled in the product. + //! + //! \param[in] uuid The unique identifier for the crash report record. + //! + //! \return The operation status code. + virtual OperationStatus SkipReportUpload(const UUID& uuid) = 0; + + //! \brief Deletes a crash report file and its associated metadata. + //! + //! \param[in] uuid The UUID of the report to delete. + //! + //! \return The operation status code. + virtual OperationStatus DeleteReport(const UUID& uuid) = 0; + + protected: + CrashReportDatabase() {} + + private: + DISALLOW_COPY_AND_ASSIGN(CrashReportDatabase); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm new file mode 100644 index 0000000..80cc5cd --- /dev/null +++ b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
@@ -0,0 +1,674 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crash_report_database.h" + +#include <errno.h> +#include <fcntl.h> +#import <Foundation/Foundation.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <uuid/uuid.h> + +#include "base/logging.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/posix/eintr_wrapper.h" +#include "base/scoped_generic.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "client/settings.h" +#include "util/file/file_io.h" +#include "util/mac/xattr.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace { + +const char kWriteDirectory[] = "new"; +const char kUploadPendingDirectory[] = "pending"; +const char kCompletedDirectory[] = "completed"; + +const char kSettings[] = "settings.dat"; + +const char* const kReportDirectories[] = { + kWriteDirectory, + kUploadPendingDirectory, + kCompletedDirectory, +}; + +const char kCrashReportFileExtension[] = "dmp"; + +const char kXattrUUID[] = "uuid"; +const char kXattrCollectorID[] = "id"; +const char kXattrCreationTime[] = "creation_time"; +const char kXattrIsUploaded[] = "uploaded"; +const char kXattrLastUploadTime[] = "last_upload_time"; +const char kXattrUploadAttemptCount[] = "upload_count"; + +const char kXattrDatabaseInitialized[] = "initialized"; + +// Ensures that the node at |path| is a directory. If the |path| refers to a +// file, rather than a directory, returns false. Otherwise, returns true, +// indicating that |path| already was a directory. +bool EnsureDirectoryExists(const base::FilePath& path) { + struct stat st; + if (stat(path.value().c_str(), &st) != 0) { + PLOG(ERROR) << "stat " << path.value(); + return false; + } + if (!S_ISDIR(st.st_mode)) { + LOG(ERROR) << "stat " << path.value() << ": not a directory"; + return false; + } + return true; +} + +// Ensures that the node at |path| is a directory, and creates it if it does +// not exist. If the |path| refers to a file, rather than a directory, or the +// directory could not be created, returns false. Otherwise, returns true, +// indicating that |path| already was or now is a directory. +bool CreateOrEnsureDirectoryExists(const base::FilePath& path) { + if (mkdir(path.value().c_str(), 0755) == 0) { + return true; + } + if (errno != EEXIST) { + PLOG(ERROR) << "mkdir " << path.value(); + return false; + } + return EnsureDirectoryExists(path); +} + +// Creates a long database xattr name from the short constant name. These names +// have changed, and new_name determines whether the returned xattr name will be +// the old name or its new equivalent. +std::string XattrNameInternal(const base::StringPiece& name, bool new_name) { + return base::StringPrintf(new_name ? "org.chromium.crashpad.database.%s" + : "com.googlecode.crashpad.%s", + name.data()); +} + +//! \brief A CrashReportDatabase that uses HFS+ extended attributes to store +//! report metadata. +//! +//! The database maintains three directories of reports: `"new"` to hold crash +//! reports that are in the process of being written, `"completed"` to hold +//! reports that have been written and are awaiting upload, and `"uploaded"` to +//! hold reports successfully uploaded to a collection server. If the user has +//! opted out of report collection, reports will still be written and moved +//! to the completed directory, but they just will not be uploaded. +//! +//! The database stores its metadata in extended filesystem attributes. To +//! ensure safe access, the report file is locked using `O_EXLOCK` during all +//! extended attribute operations. The lock should be obtained using +//! ObtainReportLock(). +class CrashReportDatabaseMac : public CrashReportDatabase { + public: + explicit CrashReportDatabaseMac(const base::FilePath& path); + virtual ~CrashReportDatabaseMac(); + + bool Initialize(bool may_create); + + // CrashReportDatabase: + Settings* GetSettings() override; + OperationStatus PrepareNewCrashReport(NewReport** report) override; + OperationStatus FinishedWritingCrashReport(NewReport* report, + UUID* uuid) override; + OperationStatus ErrorWritingCrashReport(NewReport* report) override; + OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; + OperationStatus GetPendingReports(std::vector<Report>* reports) override; + OperationStatus GetCompletedReports(std::vector<Report>* reports) override; + OperationStatus GetReportForUploading(const UUID& uuid, + const Report** report) override; + OperationStatus RecordUploadAttempt(const Report* report, + bool successful, + const std::string& id) override; + OperationStatus SkipReportUpload(const UUID& uuid) override; + OperationStatus DeleteReport(const UUID& uuid) override; + + private: + //! \brief A private extension of the Report class that maintains bookkeeping + //! information of the database. + struct UploadReport : public Report { + //! \brief Stores the flock of the file for the duration of + //! GetReportForUploading() and RecordUploadAttempt(). + int lock_fd; + }; + + //! \brief Locates a crash report in the database by UUID. + //! + //! \param[in] uuid The UUID of the crash report to locate. + //! + //! \return The full path to the report file, or an empty path if it cannot be + //! found. + base::FilePath LocateCrashReport(const UUID& uuid); + + //! \brief Obtains an exclusive advisory lock on a file. + //! + //! The flock is used to prevent cross-process concurrent metadata reads or + //! writes. While xattrs do not observe the lock, if the lock-then-mutate + //! protocol is observed by all clients of the database, it still enforces + //! synchronization. + //! + //! This does not block, and so callers must ensure that the lock is valid + //! after calling. + //! + //! \param[in] path The path of the file to lock. + //! + //! \return A scoped lock object. If the result is not valid, an error is + //! logged. + static base::ScopedFD ObtainReportLock(const base::FilePath& path); + + //! \brief Reads all the database xattrs from a file into a Report. The file + //! must be locked with ObtainReportLock. + //! + //! \param[in] path The path of the report. + //! \param[out] report The object into which data will be read. + //! + //! \return `true` if all the metadata was read successfully, `false` + //! otherwise. + bool ReadReportMetadataLocked(const base::FilePath& path, Report* report); + + //! \brief Reads the metadata from all the reports in a database subdirectory. + //! Invalid reports are skipped. + //! + //! \param[in] path The database subdirectory path. + //! \param[out] reports An empty vector of reports, which will be filled. + //! + //! \return The operation status code. + OperationStatus ReportsInDirectory(const base::FilePath& path, + std::vector<Report>* reports); + + //! \brief Creates a database xattr name from the short constant name. + //! + //! \param[in] name The short name of the extended attribute. + //! + //! \return The long name of the extended attribute. + std::string XattrName(const base::StringPiece& name); + + base::FilePath base_dir_; + Settings settings_; + bool xattr_new_names_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac); +}; + +CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) + : CrashReportDatabase(), + base_dir_(path), + settings_(base_dir_.Append(kSettings)), + xattr_new_names_(false), + initialized_() { +} + +CrashReportDatabaseMac::~CrashReportDatabaseMac() {} + +bool CrashReportDatabaseMac::Initialize(bool may_create) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + // Check if the database already exists. + if (may_create) { + if (!CreateOrEnsureDirectoryExists(base_dir_)) { + return false; + } + } else if (!EnsureDirectoryExists(base_dir_)) { + return false; + } + + // Create the three processing directories for the database. + for (size_t i = 0; i < arraysize(kReportDirectories); ++i) { + if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i]))) + return false; + } + + if (!settings_.Initialize()) + return false; + + // Do an xattr operation as the last step, to ensure the filesystem has + // support for them. This xattr also serves as a marker for whether the + // database uses old or new xattr names. + bool value; + if (ReadXattrBool(base_dir_, + XattrNameInternal(kXattrDatabaseInitialized, true), + &value) == XattrStatus::kOK && + value) { + xattr_new_names_ = true; + } else if (ReadXattrBool(base_dir_, + XattrNameInternal(kXattrDatabaseInitialized, false), + &value) == XattrStatus::kOK && + value) { + xattr_new_names_ = false; + } else { + xattr_new_names_ = true; + if (!WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true)) + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +Settings* CrashReportDatabaseMac::GetSettings() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &settings_; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::PrepareNewCrashReport(NewReport** out_report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<NewReport> report(new NewReport()); + + uuid_t uuid_gen; + uuid_generate(uuid_gen); + report->uuid.InitializeFromBytes(uuid_gen); + + report->path = + base_dir_.Append(kWriteDirectory) + .Append(report->uuid.ToString() + "." + kCrashReportFileExtension); + + report->handle = HANDLE_EINTR(open(report->path.value().c_str(), + O_CREAT | O_WRONLY | O_EXCL | O_EXLOCK, + 0600)); + if (report->handle < 0) { + PLOG(ERROR) << "open " << report->path.value(); + return kFileSystemError; + } + + // TODO(rsesek): Potentially use an fsetxattr() here instead. + if (!WriteXattr( + report->path, XattrName(kXattrUUID), report->uuid.ToString())) { + PLOG_IF(ERROR, IGNORE_EINTR(close(report->handle)) != 0) << "close"; + return kDatabaseError; + } + + *out_report = report.release(); + + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::FinishedWritingCrashReport(NewReport* report, + UUID* uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Takes ownership of the |handle| and the O_EXLOCK. + base::ScopedFD lock(report->handle); + + // Take ownership of the report. + scoped_ptr<NewReport> scoped_report(report); + + // Get the report's UUID to return. + std::string uuid_string; + if (ReadXattr(report->path, XattrName(kXattrUUID), + &uuid_string) != XattrStatus::kOK || + !uuid->InitializeFromString(uuid_string)) { + LOG(ERROR) << "Failed to read UUID for crash report " + << report->path.value(); + return kDatabaseError; + } + + if (*uuid != report->uuid) { + LOG(ERROR) << "UUID mismatch for crash report " << report->path.value(); + return kDatabaseError; + } + + // Record the creation time of this report. + if (!WriteXattrTimeT(report->path, XattrName(kXattrCreationTime), + time(nullptr))) { + return kDatabaseError; + } + + // Move the report to its new location for uploading. + base::FilePath new_path = + base_dir_.Append(kUploadPendingDirectory).Append(report->path.BaseName()); + if (rename(report->path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << report->path.value() << " to " + << new_path.value(); + return kFileSystemError; + } + + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::ErrorWritingCrashReport(NewReport* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Takes ownership of the |handle| and the O_EXLOCK. + base::ScopedFD lock(report->handle); + + // Take ownership of the report. + scoped_ptr<NewReport> scoped_report(report); + + // Remove the file that the report would have been written to had no error + // occurred. + if (unlink(report->path.value().c_str()) != 0) { + PLOG(ERROR) << "unlink " << report->path.value(); + return kFileSystemError; + } + + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid, + CrashReportDatabase::Report* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path = LocateCrashReport(uuid); + if (path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(path)); + if (!lock.is_valid()) + return kBusyError; + + *report = Report(); + report->file_path = path; + if (!ReadReportMetadataLocked(path, report)) + return kDatabaseError; + + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::GetPendingReports( + std::vector<CrashReportDatabase::Report>* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports); +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::GetCompletedReports( + std::vector<CrashReportDatabase::Report>* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + return ReportsInDirectory(base_dir_.Append(kCompletedDirectory), reports); +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::GetReportForUploading(const UUID& uuid, + const Report** report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath report_path = LocateCrashReport(uuid); + if (report_path.empty()) + return kReportNotFound; + + scoped_ptr<UploadReport> upload_report(new UploadReport()); + upload_report->file_path = report_path; + + base::ScopedFD lock(ObtainReportLock(report_path)); + if (!lock.is_valid()) + return kBusyError; + + if (!ReadReportMetadataLocked(report_path, upload_report.get())) + return kDatabaseError; + + upload_report->lock_fd = lock.release(); + *report = upload_report.release(); + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::RecordUploadAttempt(const Report* report, + bool successful, + const std::string& id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + DCHECK(report); + DCHECK(successful || id.empty()); + + base::FilePath report_path = LocateCrashReport(report->uuid); + if (report_path.empty()) + return kReportNotFound; + + scoped_ptr<const UploadReport> upload_report( + static_cast<const UploadReport*>(report)); + + base::ScopedFD lock(upload_report->lock_fd); + if (!lock.is_valid()) + return kBusyError; + + if (successful) { + base::FilePath new_path = + base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName()); + if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << report_path.value() << " to " + << new_path.value(); + return kFileSystemError; + } + report_path = new_path; + } + + if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) { + return kDatabaseError; + } + if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) { + return kDatabaseError; + } + + time_t now = time(nullptr); + if (!WriteXattrTimeT(report_path, XattrName(kXattrLastUploadTime), now)) { + return kDatabaseError; + } + + int upload_attempts = 0; + std::string name = XattrName(kXattrUploadAttemptCount); + if (ReadXattrInt(report_path, name, &upload_attempts) == + XattrStatus::kOtherError) { + return kDatabaseError; + } + if (!WriteXattrInt(report_path, name, ++upload_attempts)) { + return kDatabaseError; + } + + if (!settings_.SetLastUploadAttemptTime(now)) { + return kDatabaseError; + } + + return kNoError; +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload( + const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath report_path = LocateCrashReport(uuid); + if (report_path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(report_path)); + if (!lock.is_valid()) + return kBusyError; + + base::FilePath new_path = + base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName()); + if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << report_path.value() << " to " + << new_path.value(); + return kFileSystemError; + } + + return kNoError; +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::DeleteReport( + const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath report_path = LocateCrashReport(uuid); + if (report_path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(report_path)); + if (!lock.is_valid()) + return kBusyError; + + if (unlink(report_path.value().c_str()) != 0) { + PLOG(ERROR) << "unlink " << report_path.value(); + return kFileSystemError; + } + + return kNoError; +} + +base::FilePath CrashReportDatabaseMac::LocateCrashReport(const UUID& uuid) { + const std::string target_uuid = uuid.ToString(); + for (size_t i = 0; i < arraysize(kReportDirectories); ++i) { + base::FilePath path = + base_dir_.Append(kReportDirectories[i]) + .Append(target_uuid + "." + kCrashReportFileExtension); + + // Test if the path exists. + struct stat st; + if (lstat(path.value().c_str(), &st)) { + continue; + } + + // Check that the UUID of the report matches. + std::string uuid_string; + if (ReadXattr(path, XattrName(kXattrUUID), + &uuid_string) == XattrStatus::kOK && + uuid_string == target_uuid) { + return path; + } + } + + return base::FilePath(); +} + +// static +base::ScopedFD CrashReportDatabaseMac::ObtainReportLock( + const base::FilePath& path) { + int fd = HANDLE_EINTR(open(path.value().c_str(), + O_RDONLY | O_EXLOCK | O_NONBLOCK)); + PLOG_IF(ERROR, fd < 0) << "open lock " << path.value(); + return base::ScopedFD(fd); +} + +bool CrashReportDatabaseMac::ReadReportMetadataLocked( + const base::FilePath& path, Report* report) { + std::string uuid_string; + if (ReadXattr(path, XattrName(kXattrUUID), + &uuid_string) != XattrStatus::kOK || + !report->uuid.InitializeFromString(uuid_string)) { + return false; + } + + if (ReadXattrTimeT(path, XattrName(kXattrCreationTime), + &report->creation_time) != XattrStatus::kOK) { + return false; + } + + report->id = std::string(); + if (ReadXattr(path, XattrName(kXattrCollectorID), + &report->id) == XattrStatus::kOtherError) { + return false; + } + + report->uploaded = false; + if (ReadXattrBool(path, XattrName(kXattrIsUploaded), + &report->uploaded) == XattrStatus::kOtherError) { + return false; + } + + report->last_upload_attempt_time = 0; + if (ReadXattrTimeT(path, XattrName(kXattrLastUploadTime), + &report->last_upload_attempt_time) == + XattrStatus::kOtherError) { + return false; + } + + report->upload_attempts = 0; + if (ReadXattrInt(path, XattrName(kXattrUploadAttemptCount), + &report->upload_attempts) == XattrStatus::kOtherError) { + return false; + } + + return true; +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::ReportsInDirectory( + const base::FilePath& path, + std::vector<CrashReportDatabase::Report>* reports) { + base::mac::ScopedNSAutoreleasePool pool; + + DCHECK(reports->empty()); + + NSError* error = nil; + NSArray* paths = [[NSFileManager defaultManager] + contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value()) + error:&error]; + if (error) { + LOG(ERROR) << "Failed to enumerate reports in directory " << path.value() + << ": " << [[error description] UTF8String]; + return kFileSystemError; + } + + reports->reserve([paths count]); + for (NSString* entry in paths) { + Report report; + report.file_path = path.Append([entry fileSystemRepresentation]); + base::ScopedFD lock(ObtainReportLock(report.file_path)); + if (!lock.is_valid()) + continue; + + if (!ReadReportMetadataLocked(report.file_path, &report)) { + LOG(WARNING) << "Failed to read report metadata for " + << report.file_path.value(); + continue; + } + reports->push_back(report); + } + + return kNoError; +} + +std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) { + return XattrNameInternal(name, xattr_new_names_); +} + +scoped_ptr<CrashReportDatabase> InitializeInternal(const base::FilePath& path, + bool may_create) { + scoped_ptr<CrashReportDatabaseMac> database_mac( + new CrashReportDatabaseMac(path)); + if (!database_mac->Initialize(may_create)) + database_mac.reset(); + + return scoped_ptr<CrashReportDatabase>(database_mac.release()); +} + +} // namespace + +// static +scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize( + const base::FilePath& path) { + return InitializeInternal(path, true); +} + +// static +scoped_ptr<CrashReportDatabase> CrashReportDatabase::InitializeWithoutCreating( + const base::FilePath& path) { + return InitializeInternal(path, false); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_test.cc b/third_party/crashpad/crashpad/client/crash_report_database_test.cc new file mode 100644 index 0000000..54dcdd7 --- /dev/null +++ b/third_party/crashpad/crashpad/client/crash_report_database_test.cc
@@ -0,0 +1,560 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crash_report_database.h" + +#include "build/build_config.h" +#include "client/settings.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/file.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class CrashReportDatabaseTest : public testing::Test { + public: + CrashReportDatabaseTest() { + } + + protected: + // testing::Test: + void SetUp() override { + db_ = CrashReportDatabase::Initialize(path()); + ASSERT_TRUE(db_); + } + + void ResetDatabase() { + db_.reset(); + } + + CrashReportDatabase* db() { return db_.get(); } + base::FilePath path() const { + return temp_dir_.path().Append(FILE_PATH_LITERAL("crashpad_test_database")); + } + + void CreateCrashReport(CrashReportDatabase::Report* report) { + CrashReportDatabase::NewReport* new_report = nullptr; + ASSERT_EQ(CrashReportDatabase::kNoError, + db_->PrepareNewCrashReport(&new_report)); + const char kTest[] = "test"; + ASSERT_TRUE(LoggingWriteFile(new_report->handle, kTest, sizeof(kTest))); + + UUID uuid; + EXPECT_EQ(CrashReportDatabase::kNoError, + db_->FinishedWritingCrashReport(new_report, &uuid)); + + EXPECT_EQ(CrashReportDatabase::kNoError, + db_->LookUpCrashReport(uuid, report)); + ExpectPreparedCrashReport(*report); + ASSERT_TRUE(FileExists(report->file_path)); + } + + void UploadReport(const UUID& uuid, bool successful, const std::string& id) { + Settings* const settings = db_->GetSettings(); + ASSERT_TRUE(settings); + time_t times[2]; + ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[0])); + + const CrashReportDatabase::Report* report = nullptr; + ASSERT_EQ(CrashReportDatabase::kNoError, + db_->GetReportForUploading(uuid, &report)); + EXPECT_NE(UUID(), report->uuid); + EXPECT_FALSE(report->file_path.empty()); + EXPECT_TRUE(FileExists(report->file_path)) << report->file_path.value(); + EXPECT_GT(report->creation_time, 0); + EXPECT_EQ(CrashReportDatabase::kNoError, + db_->RecordUploadAttempt(report, successful, id)); + + ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[1])); + EXPECT_NE(times[1], 0); + EXPECT_GE(times[1], times[0]); + } + + void ExpectPreparedCrashReport(const CrashReportDatabase::Report& report) { + EXPECT_NE(UUID(), report.uuid); + EXPECT_FALSE(report.file_path.empty()); + EXPECT_TRUE(FileExists(report.file_path)) << report.file_path.value(); + EXPECT_TRUE(report.id.empty()); + EXPECT_GT(report.creation_time, 0); + EXPECT_FALSE(report.uploaded); + EXPECT_EQ(0, report.last_upload_attempt_time); + EXPECT_EQ(0, report.upload_attempts); + } + + void RelocateDatabase() { + ResetDatabase(); + temp_dir_.Rename(); + SetUp(); + } + + private: + ScopedTempDir temp_dir_; + scoped_ptr<CrashReportDatabase> db_; + + DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseTest); +}; + +TEST_F(CrashReportDatabaseTest, Initialize) { + // Initialize the database for the first time, creating it. + ASSERT_TRUE(db()); + + Settings* settings = db()->GetSettings(); + + UUID client_ids[3]; + ASSERT_TRUE(settings->GetClientID(&client_ids[0])); + EXPECT_NE(client_ids[0], UUID()); + + time_t last_upload_attempt_time; + ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); + EXPECT_EQ(0, last_upload_attempt_time); + + // Close and reopen the database at the same path. + ResetDatabase(); + EXPECT_FALSE(db()); + auto db = CrashReportDatabase::InitializeWithoutCreating(path()); + ASSERT_TRUE(db); + + settings = db->GetSettings(); + + ASSERT_TRUE(settings->GetClientID(&client_ids[1])); + EXPECT_EQ(client_ids[0], client_ids[1]); + + ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); + EXPECT_EQ(0, last_upload_attempt_time); + + // Check that the database can also be opened by the method that is permitted + // to create it. + db = CrashReportDatabase::Initialize(path()); + ASSERT_TRUE(db); + + settings = db->GetSettings(); + + ASSERT_TRUE(settings->GetClientID(&client_ids[2])); + EXPECT_EQ(client_ids[0], client_ids[2]); + + ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); + EXPECT_EQ(0, last_upload_attempt_time); + + std::vector<CrashReportDatabase::Report> reports; + EXPECT_EQ(CrashReportDatabase::kNoError, db->GetPendingReports(&reports)); + EXPECT_TRUE(reports.empty()); + reports.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db->GetCompletedReports(&reports)); + EXPECT_TRUE(reports.empty()); + + // InitializeWithoutCreating() shouldn’t create a nonexistent database. + base::FilePath non_database_path = + path().DirName().Append(FILE_PATH_LITERAL("not_a_database")); + db = CrashReportDatabase::InitializeWithoutCreating(non_database_path); + EXPECT_FALSE(db); +} + +TEST_F(CrashReportDatabaseTest, NewCrashReport) { + CrashReportDatabase::NewReport* new_report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->PrepareNewCrashReport(&new_report)); + UUID expect_uuid = new_report->uuid; + EXPECT_TRUE(FileExists(new_report->path)) << new_report->path.value(); + UUID uuid; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->FinishedWritingCrashReport(new_report, &uuid)); + EXPECT_EQ(expect_uuid, uuid); + + CrashReportDatabase::Report report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(uuid, &report)); + ExpectPreparedCrashReport(report); + + std::vector<CrashReportDatabase::Report> reports; + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&reports)); + ASSERT_EQ(1u, reports.size()); + EXPECT_EQ(report.uuid, reports[0].uuid); + + reports.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetCompletedReports(&reports)); + EXPECT_TRUE(reports.empty()); +} + +TEST_F(CrashReportDatabaseTest, ErrorWritingCrashReport) { + CrashReportDatabase::NewReport* new_report = nullptr; + ASSERT_EQ(CrashReportDatabase::kNoError, + db()->PrepareNewCrashReport(&new_report)); + base::FilePath new_report_path = new_report->path; + EXPECT_TRUE(FileExists(new_report_path)) << new_report_path.value(); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->ErrorWritingCrashReport(new_report)); + EXPECT_FALSE(FileExists(new_report_path)) << new_report_path.value(); +} + +TEST_F(CrashReportDatabaseTest, LookUpCrashReport) { + UUID uuid; + + { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + uuid = report.uuid; + } + + { + CrashReportDatabase::Report report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(uuid, &report)); + EXPECT_EQ(uuid, report.uuid); + EXPECT_NE(std::string::npos, report.file_path.value().find(path().value())); + EXPECT_EQ(std::string(), report.id); + EXPECT_FALSE(report.uploaded); + EXPECT_EQ(0, report.last_upload_attempt_time); + EXPECT_EQ(0, report.upload_attempts); + } + + UploadReport(uuid, true, "test"); + + { + CrashReportDatabase::Report report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(uuid, &report)); + EXPECT_EQ(uuid, report.uuid); + EXPECT_NE(std::string::npos, report.file_path.value().find(path().value())); + EXPECT_EQ("test", report.id); + EXPECT_TRUE(report.uploaded); + EXPECT_NE(0, report.last_upload_attempt_time); + EXPECT_EQ(1, report.upload_attempts); + } +} + +TEST_F(CrashReportDatabaseTest, RecordUploadAttempt) { + std::vector<CrashReportDatabase::Report> reports(3); + CreateCrashReport(&reports[0]); + CreateCrashReport(&reports[1]); + CreateCrashReport(&reports[2]); + + // Record two attempts: one successful, one not. + UploadReport(reports[1].uuid, false, std::string()); + UploadReport(reports[2].uuid, true, "abc123"); + + std::vector<CrashReportDatabase::Report> query(3); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[0].uuid, &query[0])); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[1].uuid, &query[1])); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[2].uuid, &query[2])); + + EXPECT_EQ(std::string(), query[0].id); + EXPECT_EQ(std::string(), query[1].id); + EXPECT_EQ("abc123", query[2].id); + + EXPECT_FALSE(query[0].uploaded); + EXPECT_FALSE(query[1].uploaded); + EXPECT_TRUE(query[2].uploaded); + + EXPECT_EQ(0, query[0].last_upload_attempt_time); + EXPECT_NE(0, query[1].last_upload_attempt_time); + EXPECT_NE(0, query[2].last_upload_attempt_time); + + EXPECT_EQ(0, query[0].upload_attempts); + EXPECT_EQ(1, query[1].upload_attempts); + EXPECT_EQ(1, query[2].upload_attempts); + + // Attempt to upload and fail again. + UploadReport(reports[1].uuid, false, std::string()); + + time_t report_2_upload_time = query[2].last_upload_attempt_time; + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[0].uuid, &query[0])); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[1].uuid, &query[1])); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[2].uuid, &query[2])); + + EXPECT_FALSE(query[0].uploaded); + EXPECT_FALSE(query[1].uploaded); + EXPECT_TRUE(query[2].uploaded); + + EXPECT_EQ(0, query[0].last_upload_attempt_time); + EXPECT_GE(query[1].last_upload_attempt_time, report_2_upload_time); + EXPECT_EQ(report_2_upload_time, query[2].last_upload_attempt_time); + + EXPECT_EQ(0, query[0].upload_attempts); + EXPECT_EQ(2, query[1].upload_attempts); + EXPECT_EQ(1, query[2].upload_attempts); + + // Third time's the charm: upload and succeed. + UploadReport(reports[1].uuid, true, "666hahaha"); + + time_t report_1_upload_time = query[1].last_upload_attempt_time; + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[0].uuid, &query[0])); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[1].uuid, &query[1])); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(reports[2].uuid, &query[2])); + + EXPECT_FALSE(query[0].uploaded); + EXPECT_TRUE(query[1].uploaded); + EXPECT_TRUE(query[2].uploaded); + + EXPECT_EQ(0, query[0].last_upload_attempt_time); + EXPECT_GE(query[1].last_upload_attempt_time, report_1_upload_time); + EXPECT_EQ(report_2_upload_time, query[2].last_upload_attempt_time); + + EXPECT_EQ(0, query[0].upload_attempts); + EXPECT_EQ(3, query[1].upload_attempts); + EXPECT_EQ(1, query[2].upload_attempts); +} + +// This test covers both query functions since they are related. +TEST_F(CrashReportDatabaseTest, GetCompletedAndNotUploadedReports) { + std::vector<CrashReportDatabase::Report> reports(5); + CreateCrashReport(&reports[0]); + CreateCrashReport(&reports[1]); + CreateCrashReport(&reports[2]); + CreateCrashReport(&reports[3]); + CreateCrashReport(&reports[4]); + + const UUID& report_0_uuid = reports[0].uuid; + const UUID& report_1_uuid = reports[1].uuid; + const UUID& report_2_uuid = reports[2].uuid; + const UUID& report_3_uuid = reports[3].uuid; + const UUID& report_4_uuid = reports[4].uuid; + + std::vector<CrashReportDatabase::Report> pending; + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); + + std::vector<CrashReportDatabase::Report> completed; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetCompletedReports(&completed)); + + EXPECT_EQ(reports.size(), pending.size()); + EXPECT_EQ(0u, completed.size()); + + // Upload one report successfully. + UploadReport(report_1_uuid, true, "report1"); + + pending.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); + completed.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetCompletedReports(&completed)); + + EXPECT_EQ(4u, pending.size()); + ASSERT_EQ(1u, completed.size()); + + for (const auto& report : pending) { + EXPECT_NE(report_1_uuid, report.uuid); + EXPECT_FALSE(report.file_path.empty()); + } + EXPECT_EQ(report_1_uuid, completed[0].uuid); + EXPECT_EQ("report1", completed[0].id); + EXPECT_EQ(true, completed[0].uploaded); + EXPECT_GT(completed[0].last_upload_attempt_time, 0); + EXPECT_EQ(1, completed[0].upload_attempts); + + // Fail to upload one report. + UploadReport(report_2_uuid, false, std::string()); + + pending.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); + completed.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetCompletedReports(&completed)); + + EXPECT_EQ(4u, pending.size()); + ASSERT_EQ(1u, completed.size()); + + for (const auto& report : pending) { + if (report.upload_attempts != 0) { + EXPECT_EQ(report_2_uuid, report.uuid); + EXPECT_GT(report.last_upload_attempt_time, 0); + EXPECT_FALSE(report.uploaded); + EXPECT_TRUE(report.id.empty()); + } + EXPECT_FALSE(report.file_path.empty()); + } + + // Upload a second report. + UploadReport(report_4_uuid, true, "report_4"); + + pending.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); + completed.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetCompletedReports(&completed)); + + EXPECT_EQ(3u, pending.size()); + ASSERT_EQ(2u, completed.size()); + + // Succeed the failed report. + UploadReport(report_2_uuid, true, "report 2"); + + pending.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); + completed.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetCompletedReports(&completed)); + + EXPECT_EQ(2u, pending.size()); + ASSERT_EQ(3u, completed.size()); + + for (const auto& report : pending) { + EXPECT_TRUE(report.uuid == report_0_uuid || report.uuid == report_3_uuid); + EXPECT_FALSE(report.file_path.empty()); + } + + // Skip upload for one report. + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->SkipReportUpload(report_3_uuid)); + + pending.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); + completed.clear(); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetCompletedReports(&completed)); + + ASSERT_EQ(1u, pending.size()); + ASSERT_EQ(4u, completed.size()); + + EXPECT_EQ(report_0_uuid, pending[0].uuid); + + for (const auto& report : completed) { + if (report.uuid == report_3_uuid) { + EXPECT_FALSE(report.uploaded); + EXPECT_EQ(0, report.upload_attempts); + EXPECT_EQ(0, report.last_upload_attempt_time); + } else { + EXPECT_TRUE(report.uploaded); + EXPECT_GT(report.upload_attempts, 0); + EXPECT_GT(report.last_upload_attempt_time, 0); + } + EXPECT_FALSE(report.file_path.empty()); + } +} + +TEST_F(CrashReportDatabaseTest, DuelingUploads) { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + + const CrashReportDatabase::Report* upload_report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->GetReportForUploading(report.uuid, &upload_report)); + + const CrashReportDatabase::Report* upload_report_2 = nullptr; + EXPECT_EQ(CrashReportDatabase::kBusyError, + db()->GetReportForUploading(report.uuid, &upload_report_2)); + EXPECT_FALSE(upload_report_2); + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->RecordUploadAttempt(upload_report, true, std::string())); +} + +TEST_F(CrashReportDatabaseTest, MoveDatabase) { + CrashReportDatabase::NewReport* new_report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->PrepareNewCrashReport(&new_report)); + EXPECT_TRUE(FileExists(new_report->path)) << new_report->path.value(); + UUID uuid; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->FinishedWritingCrashReport(new_report, &uuid)); + + RelocateDatabase(); + + CrashReportDatabase::Report report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(uuid, &report)); + ExpectPreparedCrashReport(report); + EXPECT_TRUE(FileExists(report.file_path)) << report.file_path.value(); +} + +TEST_F(CrashReportDatabaseTest, ReportRemoved) { + CrashReportDatabase::NewReport* new_report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->PrepareNewCrashReport(&new_report)); + EXPECT_TRUE(FileExists(new_report->path)) << new_report->path.value(); + UUID uuid; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->FinishedWritingCrashReport(new_report, &uuid)); + + CrashReportDatabase::Report report; + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(uuid, &report)); + +#if defined(OS_WIN) + EXPECT_EQ(0, _wunlink(report.file_path.value().c_str())); +#else + EXPECT_EQ(0, unlink(report.file_path.value().c_str())) + << ErrnoMessage("unlink"); +#endif + + EXPECT_EQ(CrashReportDatabase::kReportNotFound, + db()->LookUpCrashReport(uuid, &report)); +} + +TEST_F(CrashReportDatabaseTest, DeleteReport) { + CrashReportDatabase::Report keep_pending; + CrashReportDatabase::Report delete_pending; + CrashReportDatabase::Report keep_completed; + CrashReportDatabase::Report delete_completed; + + CreateCrashReport(&keep_pending); + CreateCrashReport(&delete_pending); + CreateCrashReport(&keep_completed); + CreateCrashReport(&delete_completed); + + EXPECT_TRUE(FileExists(keep_pending.file_path)); + EXPECT_TRUE(FileExists(delete_pending.file_path)); + EXPECT_TRUE(FileExists(keep_completed.file_path)); + EXPECT_TRUE(FileExists(delete_completed.file_path)); + + UploadReport(keep_completed.uuid, true, "1"); + UploadReport(delete_completed.uuid, true, "2"); + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(keep_completed.uuid, &keep_completed)); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(delete_completed.uuid, &delete_completed)); + + EXPECT_TRUE(FileExists(keep_completed.file_path)); + EXPECT_TRUE(FileExists(delete_completed.file_path)); + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->DeleteReport(delete_pending.uuid)); + EXPECT_FALSE(FileExists(delete_pending.file_path)); + EXPECT_EQ(CrashReportDatabase::kReportNotFound, + db()->LookUpCrashReport(delete_pending.uuid, &delete_pending)); + EXPECT_EQ(CrashReportDatabase::kReportNotFound, + db()->DeleteReport(delete_pending.uuid)); + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->DeleteReport(delete_completed.uuid)); + EXPECT_FALSE(FileExists(delete_completed.file_path)); + EXPECT_EQ(CrashReportDatabase::kReportNotFound, + db()->LookUpCrashReport(delete_completed.uuid, &delete_completed)); + EXPECT_EQ(CrashReportDatabase::kReportNotFound, + db()->DeleteReport(delete_completed.uuid)); + + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(keep_pending.uuid, &keep_pending)); + EXPECT_EQ(CrashReportDatabase::kNoError, + db()->LookUpCrashReport(keep_completed.uuid, &keep_completed)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_win.cc b/third_party/crashpad/crashpad/client/crash_report_database_win.cc new file mode 100644 index 0000000..7f4ede4f --- /dev/null +++ b/third_party/crashpad/crashpad/client/crash_report_database_win.cc
@@ -0,0 +1,837 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crash_report_database.h" + +#include <string.h> +#include <time.h> +#include <windows.h> + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "base/strings/string16.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "client/settings.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/stdlib/move.h" + +namespace crashpad { + +namespace { + +const wchar_t kReportsDirectory[] = L"reports"; +const wchar_t kMetadataFileName[] = L"metadata"; + +const wchar_t kSettings[] = L"settings.dat"; + +const wchar_t kCrashReportFileExtension[] = L"dmp"; + +const uint32_t kMetadataFileHeaderMagic = 'CPAD'; +const uint32_t kMetadataFileVersion = 1; + +using OperationStatus = CrashReportDatabase::OperationStatus; + +// Helpers --------------------------------------------------------------------- + +// Adds a string to the string table and returns the byte index where it was +// added. +uint32_t AddStringToTable(std::string* string_table, const std::string& str) { + uint32_t offset = base::checked_cast<uint32_t>(string_table->size()); + *string_table += str; + *string_table += '\0'; + return offset; +} + +// Converts |str| to UTF8, adds the result to the string table and returns the +// byte index where it was added. +uint32_t AddStringToTable(std::string* string_table, + const base::string16& str) { + return AddStringToTable(string_table, base::UTF16ToUTF8(str)); +} + +// Reads from the current file position to EOF and returns as a string of bytes. +std::string ReadRestOfFileAsString(FileHandle file) { + FileOffset read_from = LoggingSeekFile(file, 0, SEEK_CUR); + FileOffset end = LoggingSeekFile(file, 0, SEEK_END); + FileOffset original = LoggingSeekFile(file, read_from, SEEK_SET); + if (read_from == -1 || end == -1 || original == -1 || read_from == end) + return std::string(); + DCHECK_EQ(read_from, original); + DCHECK_GT(end, read_from); + size_t data_length = static_cast<size_t>(end - read_from); + std::string buffer(data_length, '\0'); + return LoggingReadFile(file, &buffer[0], data_length) ? buffer + : std::string(); +} + +// Helper structures, and conversions ------------------------------------------ + +// The format of the on disk metadata file is a MetadataFileHeader, followed by +// a number of fixed size records of MetadataFileReportRecord, followed by a +// string table in UTF8 format, where each string is \0 terminated. +struct MetadataFileHeader { + uint32_t magic; + uint32_t version; + uint32_t num_records; + uint32_t padding; +}; + +struct ReportDisk; + +enum class ReportState { + //! \brief Created and filled out by caller, owned by database. + kPending, + //! \brief In the process of uploading, owned by caller. + kUploading, + //! \brief Upload completed or skipped, owned by database. + kCompleted, +}; + +struct MetadataFileReportRecord { + // Note that this default constructor does no initialization. It is used only + // to create an array of records that are immediately initialized by reading + // from disk in Metadata::Read(). + MetadataFileReportRecord() {} + + // Constructs from a ReportDisk, adding to |string_table| and storing indices + // as strings into that table. + MetadataFileReportRecord(const ReportDisk& report, std::string* string_table); + + UUID uuid; // UUID is a 16 byte, standard layout structure. + uint32_t file_path_index; // Index into string table. File name is relative + // to the reports directory when on disk. + uint32_t id_index; // Index into string table. + int64_t creation_time; // Holds a time_t. + int64_t last_upload_attempt_time; // Holds a time_t. + int32_t upload_attempts; + int32_t state; // A ReportState. + uint8_t uploaded; // Boolean, 0 or 1. + uint8_t padding[7]; +}; + +//! \brief A private extension of the Report class that includes additional data +//! that's stored on disk in the metadata file. +struct ReportDisk : public CrashReportDatabase::Report { + ReportDisk(const MetadataFileReportRecord& record, + const base::FilePath& report_dir, + const std::string& string_table); + + ReportDisk(const UUID& uuid, + const base::FilePath& path, + time_t creation_tim, + ReportState state); + + //! \brief The current state of the report. + ReportState state; +}; + +MetadataFileReportRecord::MetadataFileReportRecord(const ReportDisk& report, + std::string* string_table) + : uuid(report.uuid), + file_path_index( + AddStringToTable(string_table, report.file_path.BaseName().value())), + id_index(AddStringToTable(string_table, report.id)), + creation_time(report.creation_time), + last_upload_attempt_time(report.last_upload_attempt_time), + upload_attempts(report.upload_attempts), + state(static_cast<uint32_t>(report.state)), + uploaded(report.uploaded) { + memset(&padding, 0, sizeof(padding)); +} + +ReportDisk::ReportDisk(const MetadataFileReportRecord& record, + const base::FilePath& report_dir, + const std::string& string_table) + : Report() { + uuid = record.uuid; + file_path = report_dir.Append( + base::UTF8ToUTF16(&string_table[record.file_path_index])); + id = &string_table[record.id_index]; + creation_time = record.creation_time; + uploaded = record.uploaded != 0; + last_upload_attempt_time = record.last_upload_attempt_time; + upload_attempts = record.upload_attempts; + state = static_cast<ReportState>(record.state); +} + +ReportDisk::ReportDisk(const UUID& uuid, + const base::FilePath& path, + time_t creation_time, + ReportState state) + : Report() { + this->uuid = uuid; + this->file_path = path; + this->creation_time = creation_time; + this->state = state; +} + +// Metadata -------------------------------------------------------------------- + +//! \brief Manages the metadata for the set of reports, handling serialization +//! to disk, and queries. +class Metadata { + public: + //! \brief Writes any changes if necessary, unlocks and closes the file + //! handle. + ~Metadata(); + + static scoped_ptr<Metadata> Create(const base::FilePath& metadata_file, + const base::FilePath& report_dir); + + //! \brief Adds a new report to the set. + //! + //! \param[in] new_report_disk The record to add. The #state field must be set + //! to kPending. + void AddNewRecord(const ReportDisk& new_report_disk); + + //! \brief Finds all reports in a given state. The \a reports vector is only + //! valid when CrashReportDatabase::kNoError is returned. + //! + //! \param[in] desired_state The state to match. + //! \param[out] reports Matching reports, must be empty on entry. + OperationStatus FindReports( + ReportState desired_state, + std::vector<CrashReportDatabase::Report>* reports) const; + + //! \brief Finds the report matching the given UUID. + //! + //! The returned report is only valid if CrashReportDatabase::kNoError is + //! returned. + //! + //! \param[in] uuid The report identifier. + //! \param[out] report_disk The found report, valid only if + //! CrashReportDatabase::kNoError is returned. Ownership is not + //! transferred to the caller, and the report may not be modified. + OperationStatus FindSingleReport(const UUID& uuid, + const ReportDisk** report_disk) const; + + //! \brief Finds a single report matching the given UUID and in the desired + //! state, and returns a mutable ReportDisk* if found. + //! + //! This marks the metadata as dirty, and on destruction, changes will be + //! written to disk via Write(). + //! + //! \return #kNoError on success. #kReportNotFound if there was no report with + //! the specified UUID. #kBusyError if the report was not in the specified + //! state. + OperationStatus FindSingleReportAndMarkDirty(const UUID& uuid, + ReportState desired_state, + ReportDisk** report_disk); + + //! \brief Removes a report from the metadata database, without touching the + //! on-disk file. + //! + //! The returned report is only valid if CrashReportDatabase::kNoError is + //! returned. This will mark the database as dirty. Future metadata + //! operations for this report will not succeed. + //! + //! \param[in] uuid The report identifier to remove. + //! \param[out] report_path The found report's file_path, valid only if + //! CrashReportDatabase::kNoError is returned. + OperationStatus DeleteReport(const UUID& uuid, + base::FilePath* report_path); + + private: + Metadata(FileHandle handle, const base::FilePath& report_dir); + + bool Rewind(); + + void Read(); + void Write(); + + //! \brief Confirms that the corresponding report actually exists on disk + //! (that is, the dump file has not been removed), and that the report is + //! in the given state. + static OperationStatus VerifyReport(const ReportDisk& report_disk, + ReportState desired_state); + //! \brief Confirms that the corresponding report actually exists on disk + //! (that is, the dump file has not been removed). + static OperationStatus VerifyReportAnyState(const ReportDisk& report_disk); + + ScopedFileHandle handle_; + const base::FilePath report_dir_; + bool dirty_; //! \brief `true` when a Write() is required on destruction. + std::vector<ReportDisk> reports_; + + DISALLOW_COPY_AND_ASSIGN(Metadata); +}; + +Metadata::~Metadata() { + if (dirty_) + Write(); + // Not actually async, UnlockFileEx requires the Offset fields. + OVERLAPPED overlapped = {0}; + if (!UnlockFileEx(handle_.get(), 0, MAXDWORD, MAXDWORD, &overlapped)) + PLOG(ERROR) << "UnlockFileEx"; +} + +// static +scoped_ptr<Metadata> Metadata::Create(const base::FilePath& metadata_file, + const base::FilePath& report_dir) { + // It is important that dwShareMode be non-zero so that concurrent access to + // this file results in a successful open. This allows us to get to LockFileEx + // which then blocks to guard access. + FileHandle handle = CreateFile(metadata_file.value().c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (handle == kInvalidFileHandle) + return scoped_ptr<Metadata>(); + // Not actually async, LockFileEx requires the Offset fields. + OVERLAPPED overlapped = {0}; + if (!LockFileEx(handle, + LOCKFILE_EXCLUSIVE_LOCK, + 0, + MAXDWORD, + MAXDWORD, + &overlapped)) { + PLOG(ERROR) << "LockFileEx"; + return scoped_ptr<Metadata>(); + } + + scoped_ptr<Metadata> metadata(new Metadata(handle, report_dir)); + // If Read() fails, for whatever reason (corruption, etc.) metadata will not + // have been modified and will be in a clean empty state. We continue on and + // return an empty database to hopefully recover. This means that existing + // crash reports have been orphaned. + metadata->Read(); + return metadata; +} + +void Metadata::AddNewRecord(const ReportDisk& new_report_disk) { + DCHECK(new_report_disk.state == ReportState::kPending); + reports_.push_back(new_report_disk); + dirty_ = true; +} + +OperationStatus Metadata::FindReports( + ReportState desired_state, + std::vector<CrashReportDatabase::Report>* reports) const { + DCHECK(reports->empty()); + for (const auto& report : reports_) { + if (report.state == desired_state && + VerifyReport(report, desired_state) == CrashReportDatabase::kNoError) { + reports->push_back(report); + } + } + return CrashReportDatabase::kNoError; +} + +OperationStatus Metadata::FindSingleReport( + const UUID& uuid, + const ReportDisk** out_report) const { + auto report_iter = std::find_if( + reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { + return report.uuid == uuid; + }); + if (report_iter == reports_.end()) + return CrashReportDatabase::kReportNotFound; + OperationStatus os = VerifyReportAnyState(*report_iter); + if (os == CrashReportDatabase::kNoError) + *out_report = &*report_iter; + return os; +} + +OperationStatus Metadata::FindSingleReportAndMarkDirty( + const UUID& uuid, + ReportState desired_state, + ReportDisk** report_disk) { + auto report_iter = std::find_if( + reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { + return report.uuid == uuid; + }); + if (report_iter == reports_.end()) + return CrashReportDatabase::kReportNotFound; + OperationStatus os = VerifyReport(*report_iter, desired_state); + if (os == CrashReportDatabase::kNoError) { + dirty_ = true; + *report_disk = &*report_iter; + } + return os; +} + +OperationStatus Metadata::DeleteReport(const UUID& uuid, + base::FilePath* report_path) { + auto report_iter = std::find_if( + reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { + return report.uuid == uuid; + }); + if (report_iter == reports_.end()) + return CrashReportDatabase::kReportNotFound; + *report_path = report_iter->file_path; + reports_.erase(report_iter); + dirty_ = true; + return CrashReportDatabase::kNoError; +} + +Metadata::Metadata(FileHandle handle, const base::FilePath& report_dir) + : handle_(handle), report_dir_(report_dir), dirty_(false), reports_() { +} + +bool Metadata::Rewind() { + FileOffset result = LoggingSeekFile(handle_.get(), 0, SEEK_SET); + DCHECK_EQ(result, 0); + return result == 0; +} + +void Metadata::Read() { + FileOffset length = LoggingSeekFile(handle_.get(), 0, SEEK_END); + if (length <= 0) // Failed, or empty: Abort. + return; + if (!Rewind()) { + LOG(ERROR) << "failed to rewind to read"; + return; + } + + MetadataFileHeader header; + if (!LoggingReadFile(handle_.get(), &header, sizeof(header))) { + LOG(ERROR) << "failed to read header"; + return; + } + if (header.magic != kMetadataFileHeaderMagic || + header.version != kMetadataFileVersion) { + LOG(ERROR) << "unexpected header"; + return; + } + + base::CheckedNumeric<uint32_t> records_size = + base::CheckedNumeric<uint32_t>(header.num_records) * + static_cast<uint32_t>(sizeof(MetadataFileReportRecord)); + if (!records_size.IsValid()) { + LOG(ERROR) << "record size out of range"; + return; + } + + std::vector<MetadataFileReportRecord> records(header.num_records); + if (!LoggingReadFile(handle_.get(), &records[0], records_size.ValueOrDie())) { + LOG(ERROR) << "failed to read records"; + return; + } + + std::string string_table = ReadRestOfFileAsString(handle_.get()); + if (string_table.empty() || string_table.back() != '\0') { + LOG(ERROR) << "bad string table"; + return; + } + + std::vector<ReportDisk> reports; + for (const auto& record : records) { + if (record.file_path_index >= string_table.size() || + record.id_index >= string_table.size()) { + LOG(ERROR) << "invalid string table index"; + return; + } + reports.push_back(ReportDisk(record, report_dir_, string_table)); + } + reports_.swap(reports); +} + +void Metadata::Write() { + if (!Rewind()) { + LOG(ERROR) << "failed to rewind to write"; + return; + } + + // Truncate to ensure that a partial write doesn't cause a mix of old and new + // data causing an incorrect interpretation on read. + if (!SetEndOfFile(handle_.get())) { + PLOG(ERROR) << "failed to truncate"; + return; + } + + size_t num_records = reports_.size(); + + // Fill and write out the header. + MetadataFileHeader header = {0}; + header.magic = kMetadataFileHeaderMagic; + header.version = kMetadataFileVersion; + header.num_records = base::checked_cast<uint32_t>(num_records); + if (!LoggingWriteFile(handle_.get(), &header, sizeof(header))) { + LOG(ERROR) << "failed to write header"; + return; + } + + // Build the records and string table we're going to write. + std::string string_table; + std::vector<MetadataFileReportRecord> records; + records.reserve(num_records); + for (const auto& report : reports_) { + const base::FilePath& path = report.file_path; + if (path.DirName() != report_dir_) { + LOG(ERROR) << path.value().c_str() << " expected to start with " + << base::UTF16ToUTF8(report_dir_.value()); + return; + } + records.push_back(MetadataFileReportRecord(report, &string_table)); + } + + if (!LoggingWriteFile(handle_.get(), + &records[0], + records.size() * sizeof(MetadataFileReportRecord))) { + LOG(ERROR) << "failed to write records"; + return; + } + if (!LoggingWriteFile( + handle_.get(), string_table.c_str(), string_table.size())) { + LOG(ERROR) << "failed to write string table"; + return; + } +} + +// static +OperationStatus Metadata::VerifyReportAnyState(const ReportDisk& report_disk) { + DWORD fileattr = GetFileAttributes(report_disk.file_path.value().c_str()); + if (fileattr == INVALID_FILE_ATTRIBUTES) + return CrashReportDatabase::kReportNotFound; + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) + ? CrashReportDatabase::kFileSystemError + : CrashReportDatabase::kNoError; +} + +// static +OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk, + ReportState desired_state) { + return (report_disk.state == desired_state) + ? VerifyReportAnyState(report_disk) + : CrashReportDatabase::kBusyError; +} + +bool EnsureDirectory(const base::FilePath& path) { + DWORD fileattr = GetFileAttributes(path.value().c_str()); + if (fileattr == INVALID_FILE_ATTRIBUTES) { + PLOG(ERROR) << "GetFileAttributes " << base::UTF16ToUTF8(path.value()); + return false; + } + if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0) { + LOG(ERROR) << "GetFileAttributes " + << base::UTF16ToUTF8(path.value()) + << ": not a directory"; + return false; + } + return true; +} + +//! \brief Ensures that the node at path is a directory, and creates it if it +//! does not exist. +//! +//! \return If the path points to a file, rather than a directory, or the +//! directory could not be created, returns `false`. Otherwise, returns +//! `true`, indicating that path already was or now is a directory. +bool CreateDirectoryIfNecessary(const base::FilePath& path) { + if (CreateDirectory(path.value().c_str(), nullptr)) + return true; + if (GetLastError() != ERROR_ALREADY_EXISTS) { + PLOG(ERROR) << "CreateDirectory " << base::UTF16ToUTF8(path.value()); + return false; + } + return EnsureDirectory(path); +} + +// CrashReportDatabaseWin ------------------------------------------------------ + +class CrashReportDatabaseWin : public CrashReportDatabase { + public: + explicit CrashReportDatabaseWin(const base::FilePath& path); + ~CrashReportDatabaseWin() override; + + bool Initialize(bool may_create); + + // CrashReportDatabase: + Settings* GetSettings() override; + OperationStatus PrepareNewCrashReport(NewReport** report) override; + OperationStatus FinishedWritingCrashReport(NewReport* report, + UUID* uuid) override; + OperationStatus ErrorWritingCrashReport(NewReport* report) override; + OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; + OperationStatus GetPendingReports(std::vector<Report>* reports) override; + OperationStatus GetCompletedReports(std::vector<Report>* reports) override; + OperationStatus GetReportForUploading(const UUID& uuid, + const Report** report) override; + OperationStatus RecordUploadAttempt(const Report* report, + bool successful, + const std::string& id) override; + OperationStatus SkipReportUpload(const UUID& uuid) override; + OperationStatus DeleteReport(const UUID& uuid) override; + + private: + scoped_ptr<Metadata> AcquireMetadata(); + + base::FilePath base_dir_; + Settings settings_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseWin); +}; + +CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) + : CrashReportDatabase(), + base_dir_(path), + settings_(base_dir_.Append(kSettings)), + initialized_() { +} + +CrashReportDatabaseWin::~CrashReportDatabaseWin() { +} + +bool CrashReportDatabaseWin::Initialize(bool may_create) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + // Ensure the database directory exists. + if (may_create) { + if (!CreateDirectoryIfNecessary(base_dir_)) + return false; + } else if (!EnsureDirectory(base_dir_)) { + return false; + } + + // Ensure that the report subdirectory exists. + if (!CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory))) + return false; + + if (!settings_.Initialize()) + return false; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +Settings* CrashReportDatabaseWin::GetSettings() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &settings_; +} + +OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport( + NewReport** report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<NewReport> new_report(new NewReport()); + if (!new_report->uuid.InitializeWithNew()) + return kFileSystemError; + new_report->path = base_dir_.Append(kReportsDirectory) + .Append(new_report->uuid.ToString16() + L"." + + kCrashReportFileExtension); + new_report->handle = LoggingOpenFileForWrite(new_report->path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly); + if (new_report->handle == INVALID_HANDLE_VALUE) + return kFileSystemError; + + *report = new_report.release(); + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport( + NewReport* report, + UUID* uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Take ownership of the report. + scoped_ptr<NewReport> scoped_report(report); + // Take ownership of the file handle. + ScopedFileHandle handle(report->handle); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + metadata->AddNewRecord(ReportDisk(scoped_report->uuid, + scoped_report->path, + time(nullptr), + ReportState::kPending)); + *uuid = scoped_report->uuid; + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::ErrorWritingCrashReport( + NewReport* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Take ownership of the report. + scoped_ptr<NewReport> scoped_report(report); + + // Close the outstanding handle. + LoggingCloseFile(report->handle); + + // We failed to write, so remove the dump file. There's no entry in the + // metadata table yet. + if (!DeleteFile(scoped_report->path.value().c_str())) { + PLOG(ERROR) << "DeleteFile " + << base::UTF16ToUTF8(scoped_report->path.value()); + return kFileSystemError; + } + + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::LookUpCrashReport(const UUID& uuid, + Report* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + // Find and return a copy of the matching report. + const ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); + if (os == kNoError) + *report = *report_disk; + return os; +} + +OperationStatus CrashReportDatabaseWin::GetPendingReports( + std::vector<Report>* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + return metadata ? metadata->FindReports(ReportState::kPending, reports) + : kDatabaseError; +} + +OperationStatus CrashReportDatabaseWin::GetCompletedReports( + std::vector<Report>* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + return metadata ? metadata->FindReports(ReportState::kCompleted, reports) + : kDatabaseError; +} + +OperationStatus CrashReportDatabaseWin::GetReportForUploading( + const UUID& uuid, + const Report** report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + // TODO(scottmg): After returning this report to the client, there is no way + // to reap this report if the uploader fails to call RecordUploadAttempt() or + // SkipReportUpload() (if it crashed or was otherwise buggy). To resolve this, + // one possibility would be to change the interface to be FileHandle based, so + // that instead of giving the file_path back to the client and changing state + // to kUploading, we return an exclusive access handle, and use that as the + // signal that the upload is pending, rather than an update to state in the + // metadata. Alternatively, there could be a "garbage collection" at startup + // where any reports that are orphaned in the kUploading state are either + // reset to kPending to retry, or discarded. + ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + uuid, ReportState::kPending, &report_disk); + if (os == kNoError) { + report_disk->state = ReportState::kUploading; + // Create a copy for passing back to client. This will be freed in + // RecordUploadAttempt. + *report = new Report(*report_disk); + } + return os; +} + +OperationStatus CrashReportDatabaseWin::RecordUploadAttempt( + const Report* report, + bool successful, + const std::string& id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Take ownership, allocated in GetReportForUploading. + scoped_ptr<const Report> upload_report(report); + scoped_ptr<Metadata> metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + report->uuid, ReportState::kUploading, &report_disk); + if (os != kNoError) + return os; + + time_t now = time(nullptr); + + report_disk->uploaded = successful; + report_disk->id = id; + report_disk->last_upload_attempt_time = now; + report_disk->upload_attempts++; + report_disk->state = + successful ? ReportState::kCompleted : ReportState::kPending; + + if (!settings_.SetLastUploadAttemptTime(now)) + return kDatabaseError; + + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + + base::FilePath report_path; + OperationStatus os = metadata->DeleteReport(uuid, &report_path); + if (os != kNoError) + return os; + + if (!DeleteFile(report_path.value().c_str())) { + PLOG(ERROR) << "DeleteFile " + << base::UTF16ToUTF8(report_path.value()); + return kFileSystemError; + } + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::SkipReportUpload(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + scoped_ptr<Metadata> metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + uuid, ReportState::kPending, &report_disk); + if (os == kNoError) + report_disk->state = ReportState::kCompleted; + return os; +} + +scoped_ptr<Metadata> CrashReportDatabaseWin::AcquireMetadata() { + base::FilePath metadata_file = base_dir_.Append(kMetadataFileName); + return Metadata::Create(metadata_file, base_dir_.Append(kReportsDirectory)); +} + +scoped_ptr<CrashReportDatabase> InitializeInternal( + const base::FilePath& path, bool may_create) { + scoped_ptr<CrashReportDatabaseWin> database_win( + new CrashReportDatabaseWin(path)); + return database_win->Initialize(may_create) + ? crashpad::move(database_win) + : scoped_ptr<CrashReportDatabaseWin>(); +} + +} // namespace + +// static +scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize( + const base::FilePath& path) { + return InitializeInternal(path, true); +} + +// static +scoped_ptr<CrashReportDatabase> CrashReportDatabase::InitializeWithoutCreating( + const base::FilePath& path) { + return InitializeInternal(path, false); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_client.h b/third_party/crashpad/crashpad/client/crashpad_client.h new file mode 100644 index 0000000..79a4900e --- /dev/null +++ b/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -0,0 +1,215 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_ +#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_mach_port.h" +#elif defined(OS_WIN) +#include <windows.h> +#endif + +namespace crashpad { + +//! \brief The primary interface for an application to have Crashpad monitor +//! it for crashes. +class CrashpadClient { + public: + CrashpadClient(); + ~CrashpadClient(); + + //! \brief Starts a Crashpad handler process, performing any necessary + //! handshake to configure it. + //! + //! This method does not actually direct any crashes to the Crashpad handler, + //! because there are alternative ways to use an existing Crashpad handler. To + //! begin directing crashes to the handler started by this method, call + //! UseHandler() after this method returns successfully. + //! + //! On Mac OS X, this method starts a Crashpad handler and obtains a Mach + //! send right corresponding to a receive right held by the handler process. + //! The handler process runs an exception server on this port. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] restartable If `true`, the handler will be restarted if it + //! dies, if this behavior is supported. This option is not available on + //! all platforms, and does not function on all OS versions. If it is + //! not supported, it will be ignored. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandler(const base::FilePath& handler, + const base::FilePath& database, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + bool restartable); + +#if defined(OS_MACOSX) || DOXYGEN + //! \brief Sets the process’ crash handler to a Mach service registered with + //! the bootstrap server. + //! + //! This method does not actually direct any crashes to the Crashpad handler, + //! because there are alternative ways to start or use an existing Crashpad + //! handler. To begin directing crashes to the handler set by this method, + //! call UseHandler() after this method returns successfully. + //! + //! This method is only defined on OS X. + //! + //! \param[in] service_name The service name of a Crashpad exception handler + //! service previously registered with the bootstrap server. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool SetHandlerMachService(const std::string& service_name); + + //! \brief Sets the process’ crash handler to a Mach port. + //! + //! This method does not actually direct any crashes to the Crashpad handler, + //! because there are alternative ways to start or use an existing Crashpad + //! handler. To begin directing crashes to the handler set by this method, + //! call UseHandler() after this method. + //! + //! This method is only defined on OS X. + //! + //! \param[in] exception_port An `exception_port_t` corresponding to a + //! Crashpad exception handler service. + void SetHandlerMachPort(base::mac::ScopedMachSendRight exception_port); +#endif + +#if defined(OS_WIN) || DOXYGEN + //! \brief Sets the IPC pipe of a presumably-running Crashpad handler process + //! which was started with StartHandler() or by other compatible means + //! and does an IPC message exchange to register this process with the + //! handler. However, just like StartHandler(), crashes are not serviced + //! until UseHandler() is called. + //! + //! This method is only defined on Windows. + //! + //! \param[in] ipc_pipe The full name of the crash handler IPC pipe. This is + //! a string of the form `"\\.\pipe\NAME"`. + //! + //! \return `true` on success and `false` on failure. + bool SetHandlerIPCPipe(const std::wstring& ipc_pipe); + + //! \brief Retrieves the IPC pipe name used to register with the Crashpad + //! handler. + //! + //! This method retrieves the IPC pipe name set by SetHandlerIPCPipe(), or a + //! suitable IPC pipe name chosen by StartHandler(). It is intended to be used + //! to obtain the IPC pipe name so that it may be passed to other processes, + //! so that they may register with an existing Crashpad handler by calling + //! SetHandlerIPCPipe(). + //! + //! This method is only defined on Windows. + //! + //! \return The full name of the crash handler IPC pipe, a string of the form + //! `"\\.\pipe\NAME"`. + std::wstring GetHandlerIPCPipe() const; + + //! \brief Requests that the handler capture a dump even though there hasn't + //! been a crash. + //! + //! \param[in] context A `CONTEXT`, generally captured by CaptureContext() or + //! similar. + static void DumpWithoutCrash(const CONTEXT& context); + + //! \brief Requests that the handler capture a dump using the given \a + //! exception_pointers to get the `EXCEPTION_RECORD` and `CONTEXT`. + //! + //! This function is not necessary in general usage as an unhandled exception + //! filter is installed by UseHandler(). + //! + //! \param[in] exception_pointers An `EXCEPTION_POINTERS`, as would generally + //! passed to an unhandled exception filter. + static void DumpAndCrash(EXCEPTION_POINTERS* exception_pointers); +#endif + + //! \brief Configures the process to direct its crashes to a Crashpad handler. + //! + //! The Crashpad handler must previously have been started by StartHandler() + //! or configured by SetHandlerMachService(), SetHandlerMachPort(), or + //! SetHandlerIPCPipe(). + //! + //! On Mac OS X, this method sets the task’s exception port for `EXC_CRASH`, + //! `EXC_RESOURCE`, and `EXC_GUARD` exceptions to the Mach send right obtained + //! by StartHandler(). The handler will be installed with behavior + //! `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES` and thread state flavor + //! `MACHINE_THREAD_STATE`. Exception ports are inherited, so a Crashpad + //! handler chosen by UseHandler() will remain the handler for any child + //! processes created after UseHandler() is called. Child processes do not + //! need to call StartHandler() or UseHandler() or be aware of Crashpad in any + //! way. The Crashpad handler will receive crashes from child processes that + //! have inherited it as their exception handler even after the process that + //! called StartHandler() exits. + //! + //! On Windows, this method sets the unhandled exception handler to a local + //! function that when reached will "signal and wait" for the crash handler + //! process to create the dump. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool UseHandler(); + +#if defined(OS_MACOSX) || DOXYGEN + //! \brief Configures the process to direct its crashes to the default handler + //! for the operating system. + //! + //! On OS X, this sets the task’s exception port as in UseHandler(), but the + //! exception handler used is obtained from SystemCrashReporterHandler(). If + //! the system’s crash reporter handler cannot be determined or set, the + //! task’s exception ports for crash-type exceptions are cleared. + //! + //! Use of this function is strongly discouraged. + //! + //! \warning After a call to this function, Crashpad will no longer monitor + //! the process for crashes until a subsequent call to UseHandler(). + //! + //! \note This is provided as a static function to allow it to be used in + //! situations where a CrashpadClient object is not otherwise available. + //! This may be useful when a child process inherits its parent’s Crashpad + //! handler, but wants to sever this tie. + static void UseSystemDefaultHandler(); +#endif + + private: +#if defined(OS_MACOSX) + base::mac::ScopedMachSendRight exception_port_; +#elif defined(OS_WIN) + std::wstring ipc_pipe_; +#endif + + DISALLOW_COPY_AND_ASSIGN(CrashpadClient); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc new file mode 100644 index 0000000..4a3673fb --- /dev/null +++ b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
@@ -0,0 +1,580 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crashpad_client.h" + +#include <errno.h> +#include <mach/mach.h> +#include <pthread.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "util/mac/mac_util.h" +#include "util/mach/child_port_handshake.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/notify_server.h" +#include "util/misc/clock.h" +#include "util/misc/implicit_cast.h" +#include "util/stdlib/move.h" +#include "util/posix/close_multiple.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +std::string FormatArgumentInt(const std::string& name, int value) { + return base::StringPrintf("--%s=%d", name.c_str(), value); +} + +// Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. +// +// EXC_CRASH is how most crashes are received. Most other exception types such +// as EXC_BAD_ACCESS are delivered to a host-level exception handler in the +// kernel where they are converted to POSIX signals. See 10.9.5 +// xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a +// core-generating signal (triggered through this hardware mechanism or a +// software mechanism such as abort() sending SIGABRT) is unhandled and the +// process exits, or if the process is killed with SIGKILL for code-signing +// reasons, an EXC_CRASH exception will be sent. See 10.9.5 +// xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit(). +// +// EXC_RESOURCE and EXC_GUARD do not become signals or EXC_CRASH exceptions. The +// host-level exception handler in the kernel does not receive these exception +// types, and even if it did, it would not map them to signals. Instead, the +// first Mach service loaded by the root (process ID 1) launchd with a boolean +// “ExceptionServer” property in its job dictionary (regardless of its value) or +// with any subdictionary property will become the host-level exception handler +// for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5 +// launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job is +// com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since it is +// impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through the +// EXC_CRASH mechanism, an exception handler must be registered for them by name +// if it is to receive these exception types. The default task-level handler for +// these exception types is set by launchd in a similar manner. +// +// EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and +// the kernel will reject attempts to use them if it does not understand them, +// so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported. +bool SetCrashExceptionPorts(exception_handler_t exception_handler) { + ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); + return exception_ports.SetExceptionPort( + (EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(), + exception_handler, + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + MACHINE_THREAD_STATE); +} + +class ScopedPthreadAttrDestroy { + public: + explicit ScopedPthreadAttrDestroy(pthread_attr_t* pthread_attr) + : pthread_attr_(pthread_attr) { + } + + ~ScopedPthreadAttrDestroy() { + errno = pthread_attr_destroy(pthread_attr_); + PLOG_IF(WARNING, errno != 0) << "pthread_attr_destroy"; + } + + private: + pthread_attr_t* pthread_attr_; + + DISALLOW_COPY_AND_ASSIGN(ScopedPthreadAttrDestroy); +}; + +//! \brief Starts a Crashpad handler, possibly restarting it if it dies. +class HandlerStarter final : public NotifyServer::DefaultInterface { + public: + ~HandlerStarter() {} + + //! \brief Starts a Crashpad handler initially, as opposed to starting it for + //! subsequent restarts. + //! + //! All parameters are as in CrashpadClient::StartHandler(). + //! + //! \return On success, a send right to the Crashpad handler that has been + //! started. On failure, `MACH_PORT_NULL` with a message logged. + static base::mac::ScopedMachSendRight InitialStart( + const base::FilePath& handler, + const base::FilePath& database, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + bool restartable) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + if (!receive_right.is_valid()) { + return base::mac::ScopedMachSendRight(); + } + + mach_port_t port; + mach_msg_type_name_t right_type; + kern_return_t kr = mach_port_extract_right(mach_task_self(), + receive_right.get(), + MACH_MSG_TYPE_MAKE_SEND, + &port, + &right_type); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_port_extract_right"; + return base::mac::ScopedMachSendRight(); + } + base::mac::ScopedMachSendRight send_right(port); + DCHECK_EQ(port, receive_right.get()); + DCHECK_EQ(right_type, + implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND)); + + scoped_ptr<HandlerStarter> handler_restarter; + if (restartable) { + handler_restarter.reset(new HandlerStarter()); + if (!handler_restarter->notify_port_.is_valid()) { + // This is an error that NewMachPort() would have logged. Proceed anyway + // without the ability to restart. + handler_restarter.reset(); + } + } + + if (!CommonStart(handler, + database, + url, + annotations, + arguments, + crashpad::move(receive_right), + handler_restarter.get(), + false)) { + return base::mac::ScopedMachSendRight(); + } + + if (handler_restarter && + handler_restarter->StartRestartThread( + handler, database, url, annotations, arguments)) { + // The thread owns the object now. + ignore_result(handler_restarter.release()); + } + + // If StartRestartThread() failed, proceed without the ability to restart. + // handler_restarter will be released when this function returns. + + return send_right; + } + + // NotifyServer::DefaultInterface: + + kern_return_t DoMachNotifyPortDestroyed(notify_port_t notify, + mach_port_t rights, + const mach_msg_trailer_t* trailer, + bool* destroy_request) override { + // The receive right corresponding to this process’ crash exception port is + // now owned by this process. Any crashes that occur before the receive + // right is moved to a new handler process will cause the process to hang in + // an unkillable state prior to OS X 10.10. + + if (notify != notify_port_) { + LOG(WARNING) << "notify port mismatch"; + return KERN_FAILURE; + } + + // If CommonStart() fails, the receive right will die, and this will just + // be called again for another try. + CommonStart(handler_, + database_, + url_, + annotations_, + arguments_, + base::mac::ScopedMachReceiveRight(rights), + this, + true); + + return KERN_SUCCESS; + } + + private: + HandlerStarter() + : NotifyServer::DefaultInterface(), + handler_(), + database_(), + url_(), + annotations_(), + arguments_(), + notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), + last_start_time_(0) { + } + + //! \brief Starts a Crashpad handler. + //! + //! All parameters are as in CrashpadClient::StartHandler(), with these + //! additions: + //! + //! \param[in] receive_right The receive right to move to the Crashpad + //! handler. The handler will use this receive right to run its exception + //! server. + //! \param[in] handler_restarter If CrashpadClient::StartHandler() was invoked + //! with \a restartable set to `true`, this is the restart state object. + //! Otherwise, this is `nullptr`. + //! \param[in] restart If CrashpadClient::StartHandler() was invoked with \a + //! restartable set to `true` and CommonStart() is being called to restart + //! a previously-started handler, this is `true`. Otherwise, this is + //! `false`. + //! + //! \return `true` on success, `false` on failure, with a message logged. + //! Failures include failure to start the handler process and failure to + //! rendezvous with it via ChildPortHandshake. + static bool CommonStart(const base::FilePath& handler, + const base::FilePath& database, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + base::mac::ScopedMachReceiveRight receive_right, + HandlerStarter* handler_restarter, + bool restart) { + DCHECK(!restart || handler_restarter); + + if (handler_restarter) { + // The port-destroyed notification must be requested each time. It uses + // a send-once right, so once the notification is received, it won’t be + // sent again unless re-requested. + mach_port_t previous; + kern_return_t kr = + mach_port_request_notification(mach_task_self(), + receive_right.get(), + MACH_NOTIFY_PORT_DESTROYED, + 0, + handler_restarter->notify_port_.get(), + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &previous); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "mach_port_request_notification"; + + // This will cause the restart thread to terminate after this restart + // attempt. There’s no longer any need for it, because no more + // port-destroyed notifications can be delivered. + handler_restarter->notify_port_.reset(); + } else { + base::mac::ScopedMachSendRight previous_owner(previous); + DCHECK(restart || !previous_owner.is_valid()); + } + + if (restart) { + // If the handler was ever started before, don’t restart it too quickly. + const uint64_t kNanosecondsPerSecond = 1E9; + const uint64_t kMinimumStartInterval = 1 * kNanosecondsPerSecond; + + const uint64_t earliest_next_start_time = + handler_restarter->last_start_time_ + kMinimumStartInterval; + const uint64_t now_time = ClockMonotonicNanoseconds(); + if (earliest_next_start_time > now_time) { + const uint64_t sleep_time = earliest_next_start_time - now_time; + LOG(INFO) << "restarting handler" + << base::StringPrintf(" in %.3fs", + static_cast<double>(sleep_time) / + kNanosecondsPerSecond); + SleepNanoseconds(earliest_next_start_time - now_time); + } else { + LOG(INFO) << "restarting handler"; + } + } + + handler_restarter->last_start_time_ = ClockMonotonicNanoseconds(); + } + + // Set up the arguments for execve() first. These aren’t needed until + // execve() is called, but it’s dangerous to do this in a child process + // after fork(). + ChildPortHandshake child_port_handshake; + base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); + + // Use handler as argv[0], followed by arguments directed by this method’s + // parameters and a --handshake-fd argument. |arguments| are added first so + // that if it erroneously contains an argument such as --url, the actual + // |url| argument passed to this method will supersede it. In normal + // command-line processing, the last parameter wins in the case of a + // conflict. + std::vector<std::string> argv(1, handler.value()); + argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1); + for (const std::string& argument : arguments) { + argv.push_back(argument); + } + if (!database.value().empty()) { + argv.push_back(FormatArgumentString("database", database.value())); + } + if (!url.empty()) { + argv.push_back(FormatArgumentString("url", url)); + } + for (const auto& kv : annotations) { + argv.push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } + argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get())); + + const char* handler_c = handler.value().c_str(); + + // argv_c contains const char* pointers and is terminated by nullptr. argv + // is required because the pointers in argv_c need to point somewhere, and + // they can’t point to temporaries such as those returned by + // FormatArgumentString(). + std::vector<const char*> argv_c; + argv_c.reserve(argv.size() + 1); + for (const std::string& argument : argv) { + argv_c.push_back(argument.c_str()); + } + argv_c.push_back(nullptr); + + // Double-fork(). The three processes involved are parent, child, and + // grandchild. The grandchild will become the handler process. The child + // exits immediately after spawning the grandchild, so the grandchild + // becomes an orphan and its parent process ID becomes 1. This relieves the + // parent and child of the responsibility for reaping the grandchild with + // waitpid() or similar. The handler process is expected to outlive the + // parent process, so the parent shouldn’t be concerned with reaping it. + // This approach means that accidental early termination of the handler + // process will not result in a zombie process. + pid_t pid = fork(); + if (pid < 0) { + PLOG(ERROR) << "fork"; + return false; + } + + if (pid == 0) { + // Child process. + + if (restart) { + // When restarting, reset the system default crash handler first. + // Otherwise, the crash exception port here will have been inherited + // from the parent process, which was probably using the exception + // server now being restarted. The handler can’t monitor itself for its + // own crashes via this interface. + CrashpadClient::UseSystemDefaultHandler(); + } + + // Call setsid(), creating a new process group and a new session, both led + // by this process. The new process group has no controlling terminal. + // This disconnects it from signals generated by the parent process’ + // terminal. + // + // setsid() is done in the child instead of the grandchild so that the + // grandchild will not be a session leader. If it were a session leader, + // an accidental open() of a terminal device without O_NOCTTY would make + // that terminal the controlling terminal. + // + // It’s not desirable for the handler to have a controlling terminal. The + // handler monitors clients on its own and manages its own lifetime, + // exiting when it loses all clients and when it deems it appropraite to + // do so. It may serve clients in different process groups or sessions + // than its original client, and receiving signals intended for its + // original client’s process group could be harmful in that case. + PCHECK(setsid() != -1) << "setsid"; + + pid = fork(); + if (pid < 0) { + PLOG(FATAL) << "fork"; + } + + if (pid > 0) { + // Child process. + + // _exit() instead of exit(), because fork() was called. + _exit(EXIT_SUCCESS); + } + + // Grandchild process. + + CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get()); + + // &argv_c[0] is a pointer to a pointer to const char data, but because of + // how C (not C++) works, execvp() wants a pointer to a const pointer to + // char data. It modifies neither the data nor the pointers, so the + // const_cast is safe. + execvp(handler_c, const_cast<char* const*>(&argv_c[0])); + PLOG(FATAL) << "execvp " << handler_c; + } + + // Parent process. + + // Close the write side of the pipe, so that the handler process is the only + // process that can write to it. + server_write_fd.reset(); + + // waitpid() for the child, so that it does not become a zombie process. The + // child normally exits quickly. + int status; + pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); + PCHECK(wait_pid != -1) << "waitpid"; + DCHECK_EQ(wait_pid, pid); + + if (WIFSIGNALED(status)) { + LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status); + } else if (!WIFEXITED(status)) { + DLOG(WARNING) << "intermediate process: unknown termination " << status; + } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { + LOG(WARNING) << "intermediate process: exit status " + << WEXITSTATUS(status); + } + + // Rendezvous with the handler running in the grandchild process. + if (!child_port_handshake.RunClient(receive_right.get(), + MACH_MSG_TYPE_MOVE_RECEIVE)) { + return false; + } + + ignore_result(receive_right.release()); + return true; + } + + bool StartRestartThread(const base::FilePath& handler, + const base::FilePath& database, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments) { + handler_ = handler; + database_ = database; + url_ = url; + annotations_ = annotations; + arguments_ = arguments; + + pthread_attr_t pthread_attr; + errno = pthread_attr_init(&pthread_attr); + if (errno != 0) { + PLOG(WARNING) << "pthread_attr_init"; + return false; + } + ScopedPthreadAttrDestroy pthread_attr_owner(&pthread_attr); + + errno = pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED); + if (errno != 0) { + PLOG(WARNING) << "pthread_attr_setdetachstate"; + return false; + } + + pthread_t pthread; + errno = pthread_create(&pthread, &pthread_attr, RestartThreadMain, this); + if (errno != 0) { + PLOG(WARNING) << "pthread_create"; + return false; + } + + return true; + } + + static void* RestartThreadMain(void* argument) { + HandlerStarter* self = reinterpret_cast<HandlerStarter*>(argument); + + NotifyServer notify_server(self); + mach_msg_return_t mr; + do { + mr = MachMessageServer::Run(¬ify_server, + self->notify_port_.get(), + 0, + MachMessageServer::kPersistent, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + MACH_LOG_IF(ERROR, mr != MACH_MSG_SUCCESS, mr) + << "MachMessageServer::Run"; + } while (self->notify_port_.is_valid() && mr == MACH_MSG_SUCCESS); + + delete self; + + return nullptr; + } + + base::FilePath handler_; + base::FilePath database_; + std::string url_; + std::map<std::string, std::string> annotations_; + std::vector<std::string> arguments_; + base::mac::ScopedMachReceiveRight notify_port_; + uint64_t last_start_time_; + + DISALLOW_COPY_AND_ASSIGN(HandlerStarter); +}; + +} // namespace + +CrashpadClient::CrashpadClient() + : exception_port_() { +} + +CrashpadClient::~CrashpadClient() { +} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + bool restartable) { + DCHECK(!exception_port_.is_valid()); + + // The “restartable” behavior can only be selected on OS X 10.10 and later. In + // previous OS versions, if the initial client were to crash while attempting + // to restart the handler, it would become an unkillable process. + base::mac::ScopedMachSendRight exception_port( + HandlerStarter::InitialStart(handler, + database, + url, + annotations, + arguments, + restartable && MacOSXMinorVersion() >= 10)); + if (!exception_port.is_valid()) { + return false; + } + + SetHandlerMachPort(crashpad::move(exception_port)); + return true; +} + +bool CrashpadClient::SetHandlerMachService(const std::string& service_name) { + base::mac::ScopedMachSendRight exception_port(BootstrapLookUp(service_name)); + if (!exception_port.is_valid()) { + return false; + } + + SetHandlerMachPort(crashpad::move(exception_port)); + return true; +} + +void CrashpadClient::SetHandlerMachPort( + base::mac::ScopedMachSendRight exception_port) { + DCHECK(exception_port.is_valid()); + exception_port_ = crashpad::move(exception_port); +} + +bool CrashpadClient::UseHandler() { + DCHECK(exception_port_.is_valid()); + + return SetCrashExceptionPorts(exception_port_.get()); +} + +// static +void CrashpadClient::UseSystemDefaultHandler() { + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + + // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL + // to clear the current exception ports. + if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) { + SetCrashExceptionPorts(MACH_PORT_NULL); + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_win.cc b/third_party/crashpad/crashpad/client/crashpad_client_win.cc new file mode 100644 index 0000000..c7cb09a --- /dev/null +++ b/third_party/crashpad/crashpad/client/crashpad_client_win.cc
@@ -0,0 +1,466 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crashpad_client.h" + +#include <string.h> +#include <windows.h> + +#include "base/atomicops.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/scoped_generic.h" +#include "base/strings/string16.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/lock.h" +#include "util/file/file_io.h" +#include "util/win/command_line.h" +#include "util/win/critical_section_with_debug_info.h" +#include "util/win/get_function.h" +#include "util/win/handle.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_handle.h" + +namespace { + +// This handle is never closed. This is used to signal to the server that a dump +// should be taken in the event of a crash. +HANDLE g_signal_exception = INVALID_HANDLE_VALUE; + +// Where we store the exception information that the crash handler reads. +crashpad::ExceptionInformation g_crash_exception_information; + +// These handles are never closed. g_signal_non_crash_dump is used to signal to +// the server to take a dump (not due to an exception), and the server will +// signal g_non_crash_dump_done when the dump is completed. +HANDLE g_signal_non_crash_dump = INVALID_HANDLE_VALUE; +HANDLE g_non_crash_dump_done = INVALID_HANDLE_VALUE; + +// Guards multiple simultaneous calls to DumpWithoutCrash(). This is leaked. +base::Lock* g_non_crash_dump_lock; + +// Where we store a pointer to the context information when taking a non-crash +// dump. +crashpad::ExceptionInformation g_non_crash_exception_information; + +// A CRITICAL_SECTION initialized with +// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a +// valid .DebugInfo field. The address of this critical section is given to the +// handler. All critical sections with debug info are linked in a doubly-linked +// list, so this allows the handler to capture all of them. +CRITICAL_SECTION g_critical_section_with_debug_info; + +LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { + // Tracks whether a thread has already entered UnhandledExceptionHandler. + static base::subtle::AtomicWord have_crashed; + + // This is a per-process handler. While this handler is being invoked, other + // threads are still executing as usual, so multiple threads could enter at + // the same time. Because we're in a crashing state, we shouldn't be doing + // anything that might cause allocations, call into kernel mode, etc. So, we + // don't want to take a critical section here to avoid simultaneous access to + // the global exception pointers in ExceptionInformation. Because the crash + // handler will record all threads, it's fine to simply have the second and + // subsequent entrants block here. They will soon be suspended by the crash + // handler, and then the entire process will be terminated below. This means + // that we won't save the exception pointers from the second and further + // crashes, but contention here is very unlikely, and we'll still have a stack + // that's blocked at this location. + if (base::subtle::Barrier_AtomicIncrement(&have_crashed, 1) > 1) { + SleepEx(INFINITE, false); + } + + // Otherwise, we're the first thread, so record the exception pointer and + // signal the crash handler. + g_crash_exception_information.thread_id = GetCurrentThreadId(); + g_crash_exception_information.exception_pointers = + reinterpret_cast<crashpad::WinVMAddress>(exception_pointers); + + // Now signal the crash server, which will take a dump and then terminate us + // when it's complete. + SetEvent(g_signal_exception); + + // Time to wait for the handler to create a dump. + const DWORD kMillisecondsUntilTerminate = 60 * 1000; + + // Sleep for a while to allow it to process us. Eventually, we terminate + // ourselves in case the crash server is gone, so that we don't leave zombies + // around. This would ideally never happen. + Sleep(kMillisecondsUntilTerminate); + + LOG(ERROR) << "crash server did not respond, self-terminating"; + + const UINT kCrashExitCodeNoDump = 0xffff7001; + TerminateProcess(GetCurrentProcess(), kCrashExitCodeNoDump); + + return EXCEPTION_CONTINUE_SEARCH; +} + +std::wstring FormatArgumentString(const std::string& name, + const std::wstring& value) { + return std::wstring(L"--") + base::UTF8ToUTF16(name) + L"=" + value; +} + +struct ScopedProcThreadAttributeListTraits { + static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() { + return nullptr; + } + + static void Free(PPROC_THREAD_ATTRIBUTE_LIST proc_thread_attribute_list) { + // This is able to use GET_FUNCTION_REQUIRED() instead of GET_FUNCTION() + // because it will only be called if InitializeProcThreadAttributeList() and + // UpdateProcThreadAttribute() are present. + static const auto delete_proc_thread_attribute_list = + GET_FUNCTION_REQUIRED(L"kernel32.dll", ::DeleteProcThreadAttributeList); + delete_proc_thread_attribute_list(proc_thread_attribute_list); + } +}; + +using ScopedProcThreadAttributeList = + base::ScopedGeneric<PPROC_THREAD_ATTRIBUTE_LIST, + ScopedProcThreadAttributeListTraits>; + +bool IsInheritableHandle(HANDLE handle) { + if (!handle || handle == INVALID_HANDLE_VALUE) + return false; + + // File handles (FILE_TYPE_DISK) and pipe handles (FILE_TYPE_PIPE) are known + // to be inheritable. Console handles (FILE_TYPE_CHAR) are not inheritable via + // PROC_THREAD_ATTRIBUTE_HANDLE_LIST. See + // https://crashpad.chromium.org/bug/77. + DWORD handle_type = GetFileType(handle); + return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE; +} + +// Adds |handle| to |handle_list| if it appears valid, and is not already in +// |handle_list|. +// +// Invalid handles (including INVALID_HANDLE_VALUE and null handles) cannot be +// added to a PPROC_THREAD_ATTRIBUTE_LIST’s PROC_THREAD_ATTRIBUTE_HANDLE_LIST. +// If INVALID_HANDLE_VALUE appears, CreateProcess() will fail with +// ERROR_INVALID_PARAMETER. If a null handle appears, the child process will +// silently not inherit any handles. +// +// Use this function to add handles with uncertain validities. +void AddHandleToListIfValidAndInheritable(std::vector<HANDLE>* handle_list, + HANDLE handle) { + // There doesn't seem to be any documentation of this, but if there's a handle + // duplicated in this list, CreateProcess() fails with + // ERROR_INVALID_PARAMETER. + if (IsInheritableHandle(handle) && + std::find(handle_list->begin(), handle_list->end(), handle) == + handle_list->end()) { + handle_list->push_back(handle); + } +} + +} // namespace + +namespace crashpad { + +CrashpadClient::CrashpadClient() + : ipc_pipe_() { +} + +CrashpadClient::~CrashpadClient() { +} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + bool restartable) { + DCHECK(ipc_pipe_.empty()); + + HANDLE pipe_read; + HANDLE pipe_write; + SECURITY_ATTRIBUTES security_attributes = {}; + security_attributes.nLength = sizeof(security_attributes); + security_attributes.bInheritHandle = TRUE; + if (!CreatePipe(&pipe_read, &pipe_write, &security_attributes, 0)) { + PLOG(ERROR) << "CreatePipe"; + return false; + } + ScopedFileHandle pipe_read_owner(pipe_read); + ScopedFileHandle pipe_write_owner(pipe_write); + + // The new process only needs the write side of the pipe. + BOOL rv = SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0); + PLOG_IF(WARNING, !rv) << "SetHandleInformation"; + + std::wstring command_line; + AppendCommandLineArgument(handler.value(), &command_line); + for (const std::string& argument : arguments) { + AppendCommandLineArgument(base::UTF8ToUTF16(argument), &command_line); + } + if (!database.value().empty()) { + AppendCommandLineArgument(FormatArgumentString("database", + database.value()), + &command_line); + } + if (!url.empty()) { + AppendCommandLineArgument(FormatArgumentString("url", + base::UTF8ToUTF16(url)), + &command_line); + } + for (const auto& kv : annotations) { + AppendCommandLineArgument( + FormatArgumentString("annotation", + base::UTF8ToUTF16(kv.first + '=' + kv.second)), + &command_line); + } + AppendCommandLineArgument( + base::UTF8ToUTF16(base::StringPrintf("--handshake-handle=0x%x", + HandleToInt(pipe_write))), + &command_line); + + DWORD creation_flags; + STARTUPINFOEX startup_info = {}; + startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES; + startup_info.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + std::vector<HANDLE> handle_list; + scoped_ptr<uint8_t[]> proc_thread_attribute_list_storage; + ScopedProcThreadAttributeList proc_thread_attribute_list_owner; + + static const auto initialize_proc_thread_attribute_list = + GET_FUNCTION(L"kernel32.dll", ::InitializeProcThreadAttributeList); + static const auto update_proc_thread_attribute = + initialize_proc_thread_attribute_list + ? GET_FUNCTION(L"kernel32.dll", ::UpdateProcThreadAttribute) + : nullptr; + if (!initialize_proc_thread_attribute_list || !update_proc_thread_attribute) { + // The OS doesn’t allow handle inheritance to be restricted, so the handler + // will inherit every inheritable handle. + creation_flags = 0; + startup_info.StartupInfo.cb = sizeof(startup_info.StartupInfo); + } else { + // Restrict handle inheritance to just those needed in the handler. + + creation_flags = EXTENDED_STARTUPINFO_PRESENT; + startup_info.StartupInfo.cb = sizeof(startup_info); + SIZE_T size; + rv = initialize_proc_thread_attribute_list(nullptr, 1, 0, &size); + if (rv) { + LOG(ERROR) << "InitializeProcThreadAttributeList (size) succeeded, " + "expected failure"; + return false; + } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + PLOG(ERROR) << "InitializeProcThreadAttributeList (size)"; + return false; + } + + proc_thread_attribute_list_storage.reset(new uint8_t[size]); + startup_info.lpAttributeList = + reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>( + proc_thread_attribute_list_storage.get()); + rv = initialize_proc_thread_attribute_list( + startup_info.lpAttributeList, 1, 0, &size); + if (!rv) { + PLOG(ERROR) << "InitializeProcThreadAttributeList"; + return false; + } + proc_thread_attribute_list_owner.reset(startup_info.lpAttributeList); + + handle_list.reserve(4); + handle_list.push_back(pipe_write); + AddHandleToListIfValidAndInheritable(&handle_list, + startup_info.StartupInfo.hStdInput); + AddHandleToListIfValidAndInheritable(&handle_list, + startup_info.StartupInfo.hStdOutput); + AddHandleToListIfValidAndInheritable(&handle_list, + startup_info.StartupInfo.hStdError); + rv = update_proc_thread_attribute( + startup_info.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + &handle_list[0], + handle_list.size() * sizeof(handle_list[0]), + nullptr, + nullptr); + if (!rv) { + PLOG(ERROR) << "UpdateProcThreadAttribute"; + return false; + } + } + + PROCESS_INFORMATION process_info; + rv = CreateProcess(handler.value().c_str(), + &command_line[0], + nullptr, + nullptr, + true, + creation_flags, + nullptr, + nullptr, + &startup_info.StartupInfo, + &process_info); + if (!rv) { + PLOG(ERROR) << "CreateProcess"; + return false; + } + + rv = CloseHandle(process_info.hThread); + PLOG_IF(WARNING, !rv) << "CloseHandle thread"; + + rv = CloseHandle(process_info.hProcess); + PLOG_IF(WARNING, !rv) << "CloseHandle process"; + + pipe_write_owner.reset(); + + uint32_t ipc_pipe_length; + if (!LoggingReadFile(pipe_read, &ipc_pipe_length, sizeof(ipc_pipe_length))) { + return false; + } + + ipc_pipe_.resize(ipc_pipe_length); + if (ipc_pipe_length && + !LoggingReadFile( + pipe_read, &ipc_pipe_[0], ipc_pipe_length * sizeof(ipc_pipe_[0]))) { + return false; + } + + return true; +} + +bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { + DCHECK(ipc_pipe_.empty()); + DCHECK(!ipc_pipe.empty()); + + ipc_pipe_ = ipc_pipe; + + return true; +} + +std::wstring CrashpadClient::GetHandlerIPCPipe() const { + DCHECK(!ipc_pipe_.empty()); + return ipc_pipe_; +} + +bool CrashpadClient::UseHandler() { + DCHECK(!ipc_pipe_.empty()); + DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE); + DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE); + DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE); + DCHECK(!g_critical_section_with_debug_info.DebugInfo); + DCHECK(!g_non_crash_dump_lock); + + ClientToServerMessage message; + memset(&message, 0, sizeof(message)); + message.type = ClientToServerMessage::kRegister; + message.registration.version = RegistrationRequest::kMessageVersion; + message.registration.client_process_id = GetCurrentProcessId(); + message.registration.crash_exception_information = + reinterpret_cast<WinVMAddress>(&g_crash_exception_information); + message.registration.non_crash_exception_information = + reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information); + + // We create this dummy CRITICAL_SECTION with the + // RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point + // into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This + // allows us to walk the list at crash time to gather data for !locks. A + // debugger would instead inspect ntdll!RtlCriticalSectionList to get the head + // of the list. But that is not an exported symbol, so on an arbitrary client + // machine, we don't have a way of getting that pointer. + if (InitializeCriticalSectionWithDebugInfoIfPossible( + &g_critical_section_with_debug_info)) { + message.registration.critical_section_address = + reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info); + } + + ServerToClientMessage response = {}; + + if (!SendToCrashHandlerServer(ipc_pipe_, message, &response)) { + return false; + } + + // The server returns these already duplicated to be valid in this process. + g_signal_exception = + IntToHandle(response.registration.request_crash_dump_event); + g_signal_non_crash_dump = + IntToHandle(response.registration.request_non_crash_dump_event); + g_non_crash_dump_done = + IntToHandle(response.registration.non_crash_dump_completed_event); + + g_non_crash_dump_lock = new base::Lock(); + + // In theory we could store the previous handler but it is not clear what + // use we have for it. + SetUnhandledExceptionFilter(&UnhandledExceptionHandler); + return true; +} + +// static +void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) { + if (g_signal_non_crash_dump == INVALID_HANDLE_VALUE || + g_non_crash_dump_done == INVALID_HANDLE_VALUE) { + LOG(ERROR) << "haven't called UseHandler()"; + return; + } + + // In the non-crashing case, we aren't concerned about avoiding calls into + // Win32 APIs, so just use regular locking here in case of multiple threads + // calling this function. If a crash occurs while we're in here, the worst + // that can happen is that the server captures a partial dump for this path + // because on the other thread gathering a crash dump, it TerminateProcess()d, + // causing this one to abort. + base::AutoLock lock(*g_non_crash_dump_lock); + + // Create a fake EXCEPTION_POINTERS to give the handler something to work + // with. + EXCEPTION_POINTERS exception_pointers = {}; + + // This is logically const, but EXCEPTION_POINTERS does not declare it as + // const, so we have to cast that away from the argument. + exception_pointers.ContextRecord = const_cast<CONTEXT*>(&context); + + // We include a fake exception and use a code of '0x517a7ed' (something like + // "simulated") so that it's relatively obvious in windbg that it's not + // actually an exception. Most values in + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx have + // some of the top nibble set, so we make sure to pick a value that doesn't, + // so as to be unlikely to conflict. + const uint32_t kSimulatedExceptionCode = 0x517a7ed; + EXCEPTION_RECORD record = {}; + record.ExceptionCode = kSimulatedExceptionCode; +#if defined(ARCH_CPU_64_BITS) + record.ExceptionAddress = reinterpret_cast<void*>(context.Rip); +#else + record.ExceptionAddress = reinterpret_cast<void*>(context.Eip); +#endif // ARCH_CPU_64_BITS + + exception_pointers.ExceptionRecord = &record; + + g_non_crash_exception_information.thread_id = GetCurrentThreadId(); + g_non_crash_exception_information.exception_pointers = + reinterpret_cast<crashpad::WinVMAddress>(&exception_pointers); + + bool set_event_result = !!SetEvent(g_signal_non_crash_dump); + PLOG_IF(ERROR, !set_event_result) << "SetEvent"; + + DWORD wfso_result = WaitForSingleObject(g_non_crash_dump_done, INFINITE); + PLOG_IF(ERROR, wfso_result != WAIT_OBJECT_0) << "WaitForSingleObject"; +} + +// static +void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { + UnhandledExceptionHandler(exception_pointers); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc b/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc new file mode 100644 index 0000000..2215e2a --- /dev/null +++ b/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
@@ -0,0 +1,91 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crashpad_client.h" + +#include "base/files/file_path.h" +#include "gtest/gtest.h" +#include "test/paths.h" +#include "test/scoped_temp_dir.h" +#include "test/win/win_multiprocess.h" + +namespace crashpad { +namespace test { +namespace { + +void StartAndUseHandler() { + ScopedTempDir temp_dir; + base::FilePath handler_path = Paths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler.exe")); + CrashpadClient client; + ASSERT_TRUE(client.StartHandler(handler_path, + temp_dir.path(), + "", + std::map<std::string, std::string>(), + std::vector<std::string>(), + false)); + EXPECT_TRUE(client.UseHandler()); +} + +class StartWithInvalidHandles final : public WinMultiprocess { + public: + StartWithInvalidHandles() : WinMultiprocess() {} + ~StartWithInvalidHandles() {} + + private: + void WinMultiprocessParent() override {} + + void WinMultiprocessChild() override { + HANDLE original_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE); + SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); + SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); + + StartAndUseHandler(); + + SetStdHandle(STD_OUTPUT_HANDLE, original_stdout); + SetStdHandle(STD_ERROR_HANDLE, original_stderr); + } +}; + +TEST(CrashpadClient, StartWithInvalidHandles) { + WinMultiprocess::Run<StartWithInvalidHandles>(); +} + +class StartWithSameStdoutStderr final : public WinMultiprocess { + public: + StartWithSameStdoutStderr() : WinMultiprocess() {} + ~StartWithSameStdoutStderr() {} + + private: + void WinMultiprocessParent() override {} + + void WinMultiprocessChild() override { + HANDLE original_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE); + SetStdHandle(STD_OUTPUT_HANDLE, original_stderr); + + StartAndUseHandler(); + + SetStdHandle(STD_OUTPUT_HANDLE, original_stdout); + } +}; + +TEST(CrashpadClient, StartWithSameStdoutStderr) { + WinMultiprocess::Run<StartWithSameStdoutStderr>(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_info.cc b/third_party/crashpad/crashpad/client/crashpad_info.cc new file mode 100644 index 0000000..596f349 --- /dev/null +++ b/third_party/crashpad/crashpad/client/crashpad_info.cc
@@ -0,0 +1,111 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/crashpad_info.h" + +#include "util/stdlib/cxx.h" + +#if defined(OS_MACOSX) +#include <mach-o/loader.h> +#endif + +#if CXX_LIBRARY_VERSION >= 2011 +#include <type_traits> +#endif + +namespace { + +static const uint32_t kCrashpadInfoVersion = 1; + +} // namespace + +namespace crashpad { + +#if CXX_LIBRARY_VERSION >= 2011 || DOXYGEN +// In C++11, check that CrashpadInfo has standard layout, which is what is +// actually important. +static_assert(std::is_standard_layout<CrashpadInfo>::value, + "CrashpadInfo must be standard layout"); +#else +// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member +// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this +// property to ensure that CrashpadInfo remains POD. This doesn’t work for C++11 +// because the requirements for unions have been relaxed. +union Compile_Assert { + CrashpadInfo Compile_Assert__CrashpadInfo_must_be_pod; +}; +#endif + +// This structure needs to be stored somewhere that is easy to find without +// external information. +// +// It isn’t placed in an unnamed namespace: hopefully, this will catch attempts +// to place multiple copies of this structure into the same module. If that’s +// attempted, and the name of the symbol is the same in each translation unit, +// it will result in a linker error, which is better than having multiple +// structures show up. +// +// This may result in a static module initializer in debug-mode builds, but +// because it’s POD, no code should need to run to initialize this under +// release-mode optimization. +#if defined(OS_MACOSX) + +// Put the structure in __DATA,__crashpad_info where it can be easily found +// without having to consult the symbol table. The “used” attribute prevents it +// from being dead-stripped. +__attribute__((section(SEG_DATA ",__crashpad_info"), + used, + visibility("hidden") +#if __has_feature(address_sanitizer) +// AddressSanitizer would add a trailing red zone of at least 32 bytes, which +// would be reflected in the size of the custom section. This confuses +// MachOImageReader::GetCrashpadInfo(), which finds that the section’s size +// disagrees with the structure’s size_ field. By specifying an alignment +// greater than the red zone size, the red zone will be suppressed. + , + aligned(64) +#endif + )) CrashpadInfo g_crashpad_info; + +#elif defined(OS_WIN) + +// Put the struct in a section name CPADinfo where it can be found without the +// symbol table. +#pragma section("CPADinfo", read, write) +__declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info; + +#endif + +// static +CrashpadInfo* CrashpadInfo::GetCrashpadInfo() { + return &g_crashpad_info; +} + +CrashpadInfo::CrashpadInfo() + : signature_(kSignature), + size_(sizeof(*this)), + version_(kCrashpadInfoVersion), + crashpad_handler_behavior_(TriState::kUnset), + system_crash_reporter_forwarding_(TriState::kUnset), + padding_0_(0), + simple_annotations_(nullptr) +#if !defined(NDEBUG) && defined(OS_WIN) + , + invalid_read_detection_(0xbadc0de) +#endif +{ + +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/crashpad_info.h b/third_party/crashpad/crashpad/client/crashpad_info.h new file mode 100644 index 0000000..c2182c0d --- /dev/null +++ b/third_party/crashpad/crashpad/client/crashpad_info.h
@@ -0,0 +1,137 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CRASHPAD_INFO_H_ +#define CRASHPAD_CLIENT_CRASHPAD_INFO_H_ + +#include "base/basictypes.h" + +#include <stdint.h> + +#include "build/build_config.h" +#include "client/simple_string_dictionary.h" +#include "util/misc/tri_state.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif // OS_WIN + +namespace crashpad { + +//! \brief A structure that can be used by a Crashpad-enabled program to +//! provide information to the Crashpad crash handler. +//! +//! It is possible for one CrashpadInfo structure to appear in each loaded code +//! module in a process, but from the perspective of the user of the client +//! interface, there is only one global CrashpadInfo structure, located in the +//! module that contains the client interface code. +struct CrashpadInfo { + public: + //! \brief Returns the global CrashpadInfo structure. + static CrashpadInfo* GetCrashpadInfo(); + + CrashpadInfo(); + + //! \brief Sets the simple annotations dictionary. + //! + //! Simple annotations set on a CrashpadInfo structure are interpreted by + //! Crashpad as module-level annotations. + //! + //! Annotations may exist in \a simple_annotations at the time that this + //! method is called, or they may be added, removed, or modified in \a + //! simple_annotations after this method is called. + //! + //! \param[in] simple_annotations A dictionary that maps string keys to string + //! values. The CrashpadInfo object does not take ownership of the + //! SimpleStringDictionary object. It is the caller’s responsibility to + //! ensure that this pointer remains valid while it is in effect for a + //! CrashpadInfo object. + void set_simple_annotations(SimpleStringDictionary* simple_annotations) { + simple_annotations_ = simple_annotations; + } + + //! \brief Enables or disables Crashpad handler processing. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than #kUnset for this field will dictate whether the handler + //! is functional or not. If all modules with a CrashpadInfo structure specify + //! #kUnset, the handler will be enabled. If disabled, the Crashpad handler + //! will still run and receive exceptions, but will not take any action on an + //! exception on its own behalf, except for the action necessary to determine + //! that it has been disabled. + //! + //! The Crashpad handler should not normally be disabled. More commonly, it + //! is appropraite to disable crash report upload by calling + //! Settings::SetUploadsEnabled(). + void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) { + crashpad_handler_behavior_ = crashpad_handler_behavior; + } + + //! \brief Enables or disables Crashpad forwarding of exceptions to the + //! system’s crash reporter. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than #kUnset for this field will dictate whether the + //! exception is forwarded to the system’s crash reporter. If all modules with + //! a CrashpadInfo structure specify #kUnset, forwarding will be enabled. + //! Unless disabled, forwarding may still occur if the Crashpad handler is + //! disabled by SetCrashpadHandlerState(). Even when forwarding is enabled, + //! the Crashpad handler may choose not to forward all exceptions to the + //! system’s crash reporter in cases where it has reason to believe that the + //! system’s crash reporter would not normally have handled the exception in + //! Crashpad’s absence. + void set_system_crash_reporter_forwarding( + TriState system_crash_reporter_forwarding) { + system_crash_reporter_forwarding_ = system_crash_reporter_forwarding; + } + + enum : uint32_t { + kSignature = 'CPad', + }; + + private: + // The compiler won’t necessarily see anyone using these fields, but it + // shouldn’t warn about that. These fields aren’t intended for use by the + // process they’re found in, they’re supposed to be read by the crash + // reporting process. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + + // Fields present in version 1: + uint32_t signature_; // kSignature + uint32_t size_; // The size of the entire CrashpadInfo structure. + uint32_t version_; // kVersion + TriState crashpad_handler_behavior_; + TriState system_crash_reporter_forwarding_; + uint16_t padding_0_; + SimpleStringDictionary* simple_annotations_; // weak + +#if !defined(NDEBUG) && defined(OS_WIN) + uint32_t invalid_read_detection_; +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + DISALLOW_COPY_AND_ASSIGN(CrashpadInfo); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CRASHPAD_INFO_H_
diff --git a/third_party/crashpad/crashpad/client/prune_crash_reports.cc b/third_party/crashpad/crashpad/client/prune_crash_reports.cc new file mode 100644 index 0000000..d94f212c4 --- /dev/null +++ b/third_party/crashpad/crashpad/client/prune_crash_reports.cc
@@ -0,0 +1,131 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/prune_crash_reports.h" + +#include <sys/stat.h> + +#include <algorithm> +#include <vector> + +#include "base/logging.h" + +namespace crashpad { + +void PruneCrashReportDatabase(CrashReportDatabase* database, + PruneCondition* condition) { + std::vector<CrashReportDatabase::Report> all_reports; + CrashReportDatabase::OperationStatus status; + + status = database->GetPendingReports(&all_reports); + if (status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports"; + return; + } + + std::vector<CrashReportDatabase::Report> completed_reports; + status = database->GetCompletedReports(&completed_reports); + if (status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports"; + return; + } + all_reports.insert(all_reports.end(), completed_reports.begin(), + completed_reports.end()); + + std::sort(all_reports.begin(), all_reports.end(), + [](const CrashReportDatabase::Report& lhs, + const CrashReportDatabase::Report& rhs) { + return lhs.creation_time > rhs.creation_time; + }); + + for (const auto& report : all_reports) { + if (condition->ShouldPruneReport(report)) { + status = database->DeleteReport(report.uuid); + if (status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "Database Pruning: Failed to remove report " + << report.uuid.ToString(); + } + } + } + + // TODO(rsesek): For databases that do not use a directory structure, it is + // possible for the metadata sidecar to become corrupted and thus leave + // orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66 +} + +// static +scoped_ptr<PruneCondition> PruneCondition::GetDefault() { + // DatabaseSizePruneCondition must be the LHS so that it is always evaluated, + // due to the short-circuting behavior of BinaryPruneCondition. + return make_scoped_ptr(new BinaryPruneCondition(BinaryPruneCondition::OR, + new DatabaseSizePruneCondition(1024 * 128), new AgePruneCondition(365))); +} + +static const time_t kSecondsInDay = 60 * 60 * 24; + +AgePruneCondition::AgePruneCondition(int max_age_in_days) + : oldest_report_time_( + ((time(nullptr) - (max_age_in_days * kSecondsInDay)) + / kSecondsInDay) * kSecondsInDay) {} + +AgePruneCondition::~AgePruneCondition() {} + +bool AgePruneCondition::ShouldPruneReport( + const CrashReportDatabase::Report& report) { + return report.creation_time < oldest_report_time_; +} + +DatabaseSizePruneCondition::DatabaseSizePruneCondition(size_t max_size_in_kb) + : max_size_in_kb_(max_size_in_kb), measured_size_in_kb_(0) {} + +DatabaseSizePruneCondition::~DatabaseSizePruneCondition() {} + +bool DatabaseSizePruneCondition::ShouldPruneReport( + const CrashReportDatabase::Report& report) { +#if defined(OS_POSIX) + struct stat statbuf; + if (stat(report.file_path.value().c_str(), &statbuf) == 0) { +#elif defined(OS_WIN) + struct _stati64 statbuf; + if (_wstat64(report.file_path.value().c_str(), &statbuf) == 0) { +#else +#error "Not implemented" +#endif + // Round up fractional KB to the next 1-KB boundary. + measured_size_in_kb_ += + static_cast<size_t>((statbuf.st_size + 1023) / 1024); + } + return measured_size_in_kb_ > max_size_in_kb_; +} + +BinaryPruneCondition::BinaryPruneCondition( + Operator op, PruneCondition* lhs, PruneCondition* rhs) + : op_(op), lhs_(lhs), rhs_(rhs) {} + +BinaryPruneCondition::~BinaryPruneCondition() {} + +bool BinaryPruneCondition::ShouldPruneReport( + const CrashReportDatabase::Report& report) { + switch (op_) { + case AND: + return lhs_->ShouldPruneReport(report) && rhs_->ShouldPruneReport(report); + case OR: + return lhs_->ShouldPruneReport(report) || rhs_->ShouldPruneReport(report); + default: + NOTREACHED(); + return false; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/prune_crash_reports.h b/third_party/crashpad/crashpad/client/prune_crash_reports.h new file mode 100644 index 0000000..1c1ce9c --- /dev/null +++ b/third_party/crashpad/crashpad/client/prune_crash_reports.h
@@ -0,0 +1,146 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_ +#define CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_ + +#include <time.h> +#include <sys/types.h> + +#include "base/memory/scoped_ptr.h" +#include "client/crash_report_database.h" + +namespace crashpad { + +class PruneCondition; + +//! \brief Deletes crash reports from \a database that match \a condition. +//! +//! This function can be used to remove old or large reports from the database. +//! The \a condition will be evaluated against each report in the \a database, +//! sorted in descending order by CrashReportDatabase::Report::creation_time. +//! This guarantee allows conditions to be stateful. +//! +//! \param[in] database The database from which crash reports will be deleted. +//! \param[in] condition The condition against which all reports in the database +//! will be evaluated. +void PruneCrashReportDatabase(CrashReportDatabase* database, + PruneCondition* condition); + +scoped_ptr<PruneCondition> GetDefaultDatabasePruneCondition(); + +//! \brief An abstract base class for evaluating crash reports for deletion. +//! +//! When passed to PruneCrashReportDatabase(), each crash report in the +//! database will be evaluated according to ShouldPruneReport(). The reports +//! are evaluated serially in descending sort order by +//! CrashReportDatabase::Report::creation_time. +class PruneCondition { + public: + //! \brief Returns a sensible default condition for removing obsolete crash + //! reports. + //! + //! The default is to keep reports for one year or a maximum database size + //! of 128 MB. + //! + //! \return A PruneCondition for use with PruneCrashReportDatabase(). + static scoped_ptr<PruneCondition> GetDefault(); + + virtual ~PruneCondition() {} + + //! \brief Evaluates a crash report for deletion. + //! + //! \param[in] report The crash report to evaluate. + //! + //! \return `true` if the crash report should be deleted, `false` if it + //! should be kept. + virtual bool ShouldPruneReport(const CrashReportDatabase::Report& report) = 0; +}; + +//! \brief A PruneCondition that deletes reports older than the specified number +//! days. +class AgePruneCondition final : public PruneCondition { + public: + //! \brief Creates a PruneCondition based on Report::creation_time. + //! + //! \param[in] max_age_in_days Reports created more than this many days ago + //! will be deleted. + explicit AgePruneCondition(int max_age_in_days); + ~AgePruneCondition(); + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override; + + private: + const time_t oldest_report_time_; + + DISALLOW_COPY_AND_ASSIGN(AgePruneCondition); +}; + +//! \brief A PruneCondition that deletes older reports to keep the total +//! Crashpad database size under the specified limit. +class DatabaseSizePruneCondition final : public PruneCondition { + public: + //! \brief Creates a PruneCondition that will keep newer reports, until the + //! sum of the size of all reports is not smaller than \a max_size_in_kb. + //! After the limit is reached, older reports will be pruned. + //! + //! \param[in] max_size_in_kb The maximum number of kilobytes that all crash + //! reports should consume. + explicit DatabaseSizePruneCondition(size_t max_size_in_kb); + ~DatabaseSizePruneCondition(); + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override; + + private: + const size_t max_size_in_kb_; + size_t measured_size_in_kb_; + + DISALLOW_COPY_AND_ASSIGN(DatabaseSizePruneCondition); +}; + +//! \breif A PruneCondition that conjoins two other PruneConditions. +class BinaryPruneCondition final : public PruneCondition { + public: + enum Operator { + AND, + OR, + }; + + //! \brief Evaluates two sub-conditions according to the specified logical + //! operator. + //! + //! This implements left-to-right evaluation. For Operator::AND, this means + //! if the \a lhs is `false`, the \a rhs will not be consulted. Similarly, + //! with Operator::OR, if the \a lhs is `true`, the \a rhs will not be + //! consulted. + //! + //! \param[in] op The logical operator to apply on \a lhs and \a rhs. + //! \param[in] lhs The left-hand side of \a op. This class takes ownership. + //! \param[in] rhs The right-hand side of \a op. This class takes ownership. + BinaryPruneCondition(Operator op, PruneCondition* lhs, PruneCondition* rhs); + ~BinaryPruneCondition(); + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override; + + private: + const Operator op_; + scoped_ptr<PruneCondition> lhs_; + scoped_ptr<PruneCondition> rhs_; + + DISALLOW_COPY_AND_ASSIGN(BinaryPruneCondition); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_
diff --git a/third_party/crashpad/crashpad/client/prune_crash_reports_test.cc b/third_party/crashpad/crashpad/client/prune_crash_reports_test.cc new file mode 100644 index 0000000..bb9ea8e --- /dev/null +++ b/third_party/crashpad/crashpad/client/prune_crash_reports_test.cc
@@ -0,0 +1,237 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/prune_crash_reports.h" + +#include <stdlib.h> + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/rand_util.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class MockDatabase : public CrashReportDatabase { + public: + // CrashReportDatabase: + MOCK_METHOD0(GetSettings, Settings*()); + MOCK_METHOD1(PrepareNewCrashReport, OperationStatus(NewReport**)); + MOCK_METHOD2(FinishedWritingCrashReport, OperationStatus(NewReport*, UUID*)); + MOCK_METHOD1(ErrorWritingCrashReport, OperationStatus(NewReport*)); + MOCK_METHOD2(LookUpCrashReport, OperationStatus(const UUID&, Report*)); + MOCK_METHOD1(GetPendingReports, OperationStatus(std::vector<Report>*)); + MOCK_METHOD1(GetCompletedReports, OperationStatus(std::vector<Report>*)); + MOCK_METHOD2(GetReportForUploading, + OperationStatus(const UUID&, const Report**)); + MOCK_METHOD3(RecordUploadAttempt, + OperationStatus(const Report*, bool, const std::string&)); + MOCK_METHOD1(SkipReportUpload, OperationStatus(const UUID&)); + MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&)); +}; + +time_t NDaysAgo(int num_days) { + return time(nullptr) - (num_days * 60 * 60 * 24); +} + +TEST(PruneCrashReports, AgeCondition) { + CrashReportDatabase::Report report_80_days; + report_80_days.creation_time = NDaysAgo(80); + + CrashReportDatabase::Report report_10_days; + report_10_days.creation_time = NDaysAgo(10); + + CrashReportDatabase::Report report_30_days; + report_30_days.creation_time = NDaysAgo(30); + + AgePruneCondition condition(30); + EXPECT_TRUE(condition.ShouldPruneReport(report_80_days)); + EXPECT_FALSE(condition.ShouldPruneReport(report_10_days)); + EXPECT_FALSE(condition.ShouldPruneReport(report_30_days)); +} + +TEST(PruneCrashReports, SizeCondition) { + ScopedTempDir temp_dir; + + CrashReportDatabase::Report report_1k; + report_1k.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file1024")); + CrashReportDatabase::Report report_3k; + report_3k.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file3072")); + + { + ScopedFileHandle scoped_file_1k( + LoggingOpenFileForWrite(report_1k.file_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + ASSERT_TRUE(scoped_file_1k.is_valid()); + + std::string string; + for (int i = 0; i < 128; ++i) + string.push_back(static_cast<char>(i)); + + for (size_t i = 0; i < 1024; i += string.size()) { + ASSERT_TRUE(LoggingWriteFile(scoped_file_1k.get(), + string.c_str(), string.length())); + } + + ScopedFileHandle scoped_file_3k( + LoggingOpenFileForWrite(report_3k.file_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + ASSERT_TRUE(scoped_file_3k.is_valid()); + + for (size_t i = 0; i < 3072; i += string.size()) { + ASSERT_TRUE(LoggingWriteFile(scoped_file_3k.get(), + string.c_str(), string.length())); + } + } + + { + DatabaseSizePruneCondition condition(1); + EXPECT_FALSE(condition.ShouldPruneReport(report_1k)); + EXPECT_TRUE(condition.ShouldPruneReport(report_1k)); + } + + { + DatabaseSizePruneCondition condition(1); + EXPECT_TRUE(condition.ShouldPruneReport(report_3k)); + } + + { + DatabaseSizePruneCondition condition(6); + EXPECT_FALSE(condition.ShouldPruneReport(report_3k)); + EXPECT_FALSE(condition.ShouldPruneReport(report_3k)); + EXPECT_TRUE(condition.ShouldPruneReport(report_1k)); + } +} + +class StaticCondition final : public PruneCondition { + public: + explicit StaticCondition(bool value) : value_(value), did_execute_(false) {} + ~StaticCondition() {} + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override { + did_execute_ = true; + return value_; + } + + bool did_execute() const { return did_execute_; } + + private: + const bool value_; + bool did_execute_; + + DISALLOW_COPY_AND_ASSIGN(StaticCondition); +}; + +TEST(PruneCrashReports, BinaryCondition) { + const struct { + const char* name; + BinaryPruneCondition::Operator op; + bool lhs_value; + bool rhs_value; + bool cond_result; + bool lhs_executed; + bool rhs_executed; + } kTests[] = { + {"false && false", + BinaryPruneCondition::AND, false, false, + false, true, false}, + {"false && true", + BinaryPruneCondition::AND, false, true, + false, true, false}, + {"true && false", + BinaryPruneCondition::AND, true, false, + false, true, true}, + {"true && true", + BinaryPruneCondition::AND, true, true, + true, true, true}, + {"false || false", + BinaryPruneCondition::OR, false, false, + false, true, true}, + {"false || true", + BinaryPruneCondition::OR, false, true, + true, true, true}, + {"true || false", + BinaryPruneCondition::OR, true, false, + true, true, false}, + {"true || true", + BinaryPruneCondition::OR, true, true, + true, true, false}, + }; + for (const auto& test : kTests) { + SCOPED_TRACE(test.name); + auto lhs = new StaticCondition(test.lhs_value); + auto rhs = new StaticCondition(test.rhs_value); + BinaryPruneCondition condition(test.op, lhs, rhs); + CrashReportDatabase::Report report; + EXPECT_EQ(test.cond_result, condition.ShouldPruneReport(report)); + EXPECT_EQ(test.lhs_executed, lhs->did_execute()); + EXPECT_EQ(test.rhs_executed, rhs->did_execute()); + } +} + +MATCHER_P(TestUUID, data_1, "") { + return arg.data_1 == data_1; +} + +TEST(PruneCrashReports, PruneOrder) { + using ::testing::_; + using ::testing::DoAll; + using ::testing::Return; + using ::testing::SetArgPointee; + + std::vector<CrashReportDatabase::Report> reports; + for (int i = 0; i < 10; ++i) { + CrashReportDatabase::Report temp; + temp.uuid.data_1 = i; + temp.creation_time = NDaysAgo(i * 10); + reports.push_back(temp); + } + // The randomness from std::rand() is not, so use a better rand() instead. + const auto random_generator = [](int rand_max) { + return base::RandInt(0, rand_max - 1); + }; + std::random_shuffle(reports.begin(), reports.end(), random_generator); + std::vector<CrashReportDatabase::Report> pending_reports( + reports.begin(), reports.begin() + 5); + std::vector<CrashReportDatabase::Report> completed_reports( + reports.begin() + 5, reports.end()); + + MockDatabase db; + EXPECT_CALL(db, GetPendingReports(_)).WillOnce(DoAll( + SetArgPointee<0>(pending_reports), + Return(CrashReportDatabase::kNoError))); + EXPECT_CALL(db, GetCompletedReports(_)).WillOnce(DoAll( + SetArgPointee<0>(completed_reports), + Return(CrashReportDatabase::kNoError))); + for (size_t i = 0; i < reports.size(); ++i) { + EXPECT_CALL(db, DeleteReport(TestUUID(i))) + .WillOnce(Return(CrashReportDatabase::kNoError)); + } + + StaticCondition delete_all(true); + PruneCrashReportDatabase(&db, &delete_all); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/settings.cc b/third_party/crashpad/crashpad/client/settings.cc new file mode 100644 index 0000000..e7ff2a90e --- /dev/null +++ b/third_party/crashpad/crashpad/client/settings.cc
@@ -0,0 +1,314 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/settings.h" + +#include <limits> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "util/stdlib/move.h" +#include "util/numeric/in_range_cast.h" + +namespace crashpad { + +namespace internal { + +// static +void ScopedLockedFileHandleTraits::Free(FileHandle handle) { + if (handle != kInvalidFileHandle) { + LoggingUnlockFile(handle); + CheckedCloseFile(handle); + } +} + +} // namespace internal + +struct Settings::Data { + static const uint32_t kSettingsMagic = 'CPds'; + static const uint32_t kSettingsVersion = 1; + + enum Options : uint32_t { + kUploadsEnabled = 1 << 0, + }; + + Data() : magic(kSettingsMagic), + version(kSettingsVersion), + options(0), + padding_0(0), + last_upload_attempt_time(0), + client_id() {} + + uint32_t magic; + uint32_t version; + uint32_t options; + uint32_t padding_0; + uint64_t last_upload_attempt_time; // time_t + UUID client_id; +}; + +Settings::Settings(const base::FilePath& file_path) + : file_path_(file_path), + initialized_() { +} + +Settings::~Settings() { +} + +bool Settings::Initialize() { + initialized_.set_invalid(); + + Data settings; + if (!OpenForWritingAndReadSettings(&settings).is_valid()) + return false; + + initialized_.set_valid(); + return true; +} + +bool Settings::GetClientID(UUID* client_id) { + DCHECK(initialized_.is_valid()); + + Data settings; + if (!OpenAndReadSettings(&settings)) + return false; + + *client_id = settings.client_id; + return true; +} + +bool Settings::GetUploadsEnabled(bool* enabled) { + DCHECK(initialized_.is_valid()); + + Data settings; + if (!OpenAndReadSettings(&settings)) + return false; + + *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0; + return true; +} + +bool Settings::SetUploadsEnabled(bool enabled) { + DCHECK(initialized_.is_valid()); + + Data settings; + ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings); + if (!handle.is_valid()) + return false; + + if (enabled) + settings.options |= Data::Options::kUploadsEnabled; + else + settings.options &= ~Data::Options::kUploadsEnabled; + + return WriteSettings(handle.get(), settings); +} + +bool Settings::GetLastUploadAttemptTime(time_t* time) { + DCHECK(initialized_.is_valid()); + + Data settings; + if (!OpenAndReadSettings(&settings)) + return false; + + *time = InRangeCast<time_t>(settings.last_upload_attempt_time, + std::numeric_limits<time_t>::max()); + return true; +} + +bool Settings::SetLastUploadAttemptTime(time_t time) { + DCHECK(initialized_.is_valid()); + + Data settings; + ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings); + if (!handle.is_valid()) + return false; + + settings.last_upload_attempt_time = InRangeCast<uint64_t>(time, 0); + + return WriteSettings(handle.get(), settings); +} + +// static +Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle( + FileHandle file, + FileLocking locking) { + ScopedFileHandle scoped(file); + if (scoped.is_valid()) { + if (!LoggingLockFile(scoped.get(), locking)) + scoped.reset(); + } + return ScopedLockedFileHandle(scoped.release()); +} + +Settings::ScopedLockedFileHandle Settings::OpenForReading() { + return MakeScopedLockedFileHandle(LoggingOpenFileForRead(file_path()), + FileLocking::kShared); +} + +Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting( + FileWriteMode mode, bool log_open_error) { + DCHECK(mode != FileWriteMode::kTruncateOrCreate); + + FileHandle handle; + if (log_open_error) { + handle = LoggingOpenFileForReadAndWrite( + file_path(), mode, FilePermissions::kWorldReadable); + } else { + handle = OpenFileForReadAndWrite( + file_path(), mode, FilePermissions::kWorldReadable); + } + + return MakeScopedLockedFileHandle(handle, FileLocking::kExclusive); +} + +bool Settings::OpenAndReadSettings(Data* out_data) { + ScopedLockedFileHandle handle = OpenForReading(); + if (!handle.is_valid()) + return false; + + if (ReadSettings(handle.get(), out_data, true)) + return true; + + // The settings file is corrupt, so reinitialize it. + handle.reset(); + + // The settings failed to be read, so re-initialize them. + return RecoverSettings(kInvalidFileHandle, out_data); +} + +Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings( + Data* out_data) { + ScopedLockedFileHandle handle; + bool created = false; + if (!initialized_.is_valid()) { + // If this object is initializing, it hasn’t seen a settings file already, + // so go easy on errors. Creating a new settings file for the first time + // shouldn’t spew log messages. + // + // First, try to use an existing settings file. + handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrFail, false); + + if (!handle.is_valid()) { + // Create a new settings file if it didn’t already exist. + handle = OpenForReadingAndWriting(FileWriteMode::kCreateOrFail, false); + + if (handle.is_valid()) { + created = true; + } + + // There may have been a race to create the file, and something else may + // have won. There will be one more attempt to try to open or create the + // file below. + } + } + + if (!handle.is_valid()) { + // Either the object is initialized, meaning it’s already seen a valid + // settings file, or the object is initializing and none of the above + // attempts to create the settings file succeeded. Either way, this is the + // last chance for success, so if this fails, log a message. + handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true); + } + + if (!handle.is_valid()) + return ScopedLockedFileHandle(); + + // Attempt reading the settings even if the file is known to have just been + // created. The file-create and file-lock operations don’t occur atomically, + // and something else may have written the settings before this invocation + // took the lock. If the settings file was definitely just created, though, + // don’t log any read errors. The expected non-race behavior in this case is a + // zero-length read, with ReadSettings() failing. + if (!ReadSettings(handle.get(), out_data, !created)) { + if (!RecoverSettings(handle.get(), out_data)) + return ScopedLockedFileHandle(); + } + + return handle; +} + +bool Settings::ReadSettings(FileHandle handle, + Data* out_data, + bool log_read_error) { + if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) + return false; + + bool read_result; + if (log_read_error) { + read_result = LoggingReadFile(handle, out_data, sizeof(*out_data)); + } else { + read_result = + ReadFile(handle, out_data, sizeof(*out_data)) == sizeof(*out_data); + } + + if (!read_result) + return false; + + if (out_data->magic != Data::kSettingsMagic) { + LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic; + return false; + } + + if (out_data->version != Data::kSettingsVersion) { + LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion; + return false; + } + + return true; +} + +bool Settings::WriteSettings(FileHandle handle, const Data& data) { + if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) + return false; + + if (!LoggingTruncateFile(handle)) + return false; + + return LoggingWriteFile(handle, &data, sizeof(Data)); +} + +bool Settings::RecoverSettings(FileHandle handle, Data* out_data) { + ScopedLockedFileHandle scoped_handle; + if (handle == kInvalidFileHandle) { + scoped_handle = + OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true); + handle = scoped_handle.get(); + + // Test if the file has already been recovered now that the exclusive lock + // is held. + if (ReadSettings(handle, out_data, true)) + return true; + } + + if (handle == kInvalidFileHandle) { + LOG(ERROR) << "Invalid file handle"; + return false; + } + + if (!InitializeSettings(handle)) + return false; + + return ReadSettings(handle, out_data, true); +} + +bool Settings::InitializeSettings(FileHandle handle) { + Data settings; + if (!settings.client_id.InitializeWithNew()) + return false; + + return WriteSettings(handle, settings); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/settings.h b/third_party/crashpad/crashpad/client/settings.h new file mode 100644 index 0000000..016f8d84 --- /dev/null +++ b/third_party/crashpad/crashpad/client/settings.h
@@ -0,0 +1,183 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SETTINGS_H_ +#define CRASHPAD_CLIENT_SETTINGS_H_ + +#include <time.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/scoped_generic.h" +#include "util/file/file_io.h" +#include "util/misc/initialization_state.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +namespace internal { + +struct ScopedLockedFileHandleTraits { + static FileHandle InvalidValue() { return kInvalidFileHandle; } + static void Free(FileHandle handle); +}; + +} // namespace internal + +//! \brief An interface for accessing and modifying the settings of a +//! CrashReportDatabase. +//! +//! This class must not be instantiated directly, but rather an instance of it +//! should be retrieved via CrashReportDatabase::GetSettings(). +class Settings { + public: + explicit Settings(const base::FilePath& file_path); + ~Settings(); + + bool Initialize(); + + //! \brief Retrieves the immutable identifier for this client, which is used + //! on a server to locate all crash reports from a specific Crashpad + //! database. + //! + //! This is automatically initialized when the database is created. + //! + //! \param[out] client_id The unique client identifier. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool GetClientID(UUID* client_id); + + //! \brief Retrieves the user’s preference for submitting crash reports to a + //! collection server. + //! + //! The default value is `false`. + //! + //! \param[out] enabled Whether crash reports should be uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool GetUploadsEnabled(bool* enabled); + + //! \brief Sets the user’s preference for submitting crash reports to a + //! collection server. + //! + //! \param[in] enabled Whether crash reports should be uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool SetUploadsEnabled(bool enabled); + + //! \brief Retrieves the last time at which a report was attempted to be + //! uploaded. + //! + //! The default value is `0` if it has never been set before. + //! + //! \param[out] time The last time at which a report was uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool GetLastUploadAttemptTime(time_t* time); + + //! \brief Sets the last time at which a report was attempted to be uploaded. + //! + //! This is only meant to be used internally by the CrashReportDatabase. + //! + //! \param[in] time The last time at which a report was uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool SetLastUploadAttemptTime(time_t time); + + private: + struct Data; + + // This must be constructed with MakeScopedLockedFileHandle(). It both unlocks + // and closes the file on destruction. + using ScopedLockedFileHandle = + base::ScopedGeneric<FileHandle, internal::ScopedLockedFileHandleTraits>; + static ScopedLockedFileHandle MakeScopedLockedFileHandle(FileHandle file, + FileLocking locking); + + // Opens the settings file for reading. On error, logs a message and returns + // the invalid handle. + ScopedLockedFileHandle OpenForReading(); + + // Opens the settings file for reading and writing. On error, logs a message + // and returns the invalid handle. |mode| determines how the file will be + // opened. |mode| must not be FileWriteMode::kTruncateOrCreate. + // + // If |log_open_error| is false, nothing will be logged for an error + // encountered when attempting to open the file, but this method will still + // return false. This is intended to be used to suppress error messages when + // attempting to create a new settings file when multiple attempts are made. + ScopedLockedFileHandle OpenForReadingAndWriting(FileWriteMode mode, + bool log_open_error); + + // Opens the settings file and reads the data. If that fails, an error will + // be logged and the settings will be recovered and re-initialized. If that + // also fails, returns false with additional log data from recovery. + bool OpenAndReadSettings(Data* out_data); + + // Opens the settings file for writing and reads the data. If reading fails, + // recovery is attempted. Returns the opened file handle on success, or the + // invalid file handle on failure, with an error logged. + ScopedLockedFileHandle OpenForWritingAndReadSettings(Data* out_data); + + // Reads the settings from |handle|. Logs an error and returns false on + // failure. This does not perform recovery. + // + // |handle| must be the result of OpenForReading() or + // OpenForReadingAndWriting(). + // + // If |log_read_error| is false, nothing will be logged for a read error, but + // this method will still return false. This is intended to be used to + // suppress error messages when attempting to read a newly created settings + // file. + bool ReadSettings(FileHandle handle, Data* out_data, bool log_read_error); + + // Writes the settings to |handle|. Logs an error and returns false on + // failure. This does not perform recovery. + // + // |handle| must be the result of OpenForReadingAndWriting(). + bool WriteSettings(FileHandle handle, const Data& data); + + // Recovers the settings file by re-initializing the data. If |handle| is the + // invalid handle, this will open the file; if it is not, then it must be the + // result of OpenForReadingAndWriting(). If the invalid handle is passed, the + // caller must not be holding the handle. The new settings data are stored in + // |out_data|. Returns true on success and false on failure, with an error + // logged. + bool RecoverSettings(FileHandle handle, Data* out_data); + + // Initializes a settings file and writes the data to |handle|. Returns true + // on success and false on failure, with an error logged. + // + // |handle| must be the result of OpenForReadingAndWriting(). + bool InitializeSettings(FileHandle handle); + + const base::FilePath& file_path() const { return file_path_; } + + base::FilePath file_path_; + + InitializationState initialized_; + + DISALLOW_COPY_AND_ASSIGN(Settings); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_SETTINGS_H_
diff --git a/third_party/crashpad/crashpad/client/settings_test.cc b/third_party/crashpad/crashpad/client/settings_test.cc new file mode 100644 index 0000000..203bd21 --- /dev/null +++ b/third_party/crashpad/crashpad/client/settings_test.cc
@@ -0,0 +1,181 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/settings.h" + +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class SettingsTest : public testing::Test { + public: + SettingsTest() : settings_(settings_path()) {} + + base::FilePath settings_path() { + return temp_dir_.path().Append(FILE_PATH_LITERAL("settings")); + } + + Settings* settings() { return &settings_; } + + void InitializeBadFile() { + ScopedFileHandle handle( + LoggingOpenFileForWrite(settings_path(), + FileWriteMode::kTruncateOrCreate, + FilePermissions::kWorldReadable)); + ASSERT_TRUE(handle.is_valid()); + + const char kBuf[] = "test bad file"; + ASSERT_TRUE(LoggingWriteFile(handle.get(), kBuf, sizeof(kBuf))); + handle.reset(); + } + + protected: + // testing::Test: + void SetUp() override { + ASSERT_TRUE(settings()->Initialize()); + } + + private: + ScopedTempDir temp_dir_; + Settings settings_; + + DISALLOW_COPY_AND_ASSIGN(SettingsTest); +}; + +TEST_F(SettingsTest, ClientID) { + UUID client_id; + EXPECT_TRUE(settings()->GetClientID(&client_id)); + EXPECT_NE(UUID(), client_id); + + Settings local_settings(settings_path()); + EXPECT_TRUE(local_settings.Initialize()); + UUID actual; + EXPECT_TRUE(local_settings.GetClientID(&actual)); + EXPECT_EQ(client_id, actual); +} + +TEST_F(SettingsTest, UploadsEnabled) { + bool enabled = true; + // Default value is false. + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); + + EXPECT_TRUE(settings()->SetUploadsEnabled(true)); + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_TRUE(enabled); + + Settings local_settings(settings_path()); + EXPECT_TRUE(local_settings.Initialize()); + enabled = false; + EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); + EXPECT_TRUE(enabled); + + EXPECT_TRUE(settings()->SetUploadsEnabled(false)); + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); + + enabled = true; + EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); +} + +TEST_F(SettingsTest, LastUploadAttemptTime) { + time_t actual = -1; + EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual)); + // Default value is 0. + EXPECT_EQ(0, actual); + + const time_t expected = time(nullptr); + EXPECT_TRUE(settings()->SetLastUploadAttemptTime(expected)); + EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual)); + EXPECT_EQ(expected, actual); + + Settings local_settings(settings_path()); + EXPECT_TRUE(local_settings.Initialize()); + actual = -1; + EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&actual)); + EXPECT_EQ(expected, actual); +} + +// The following tests write a corrupt settings file and test the recovery +// operation. + +TEST_F(SettingsTest, BadFileOnInitialize) { + InitializeBadFile(); + + Settings settings(settings_path()); + EXPECT_TRUE(settings.Initialize()); +} + +TEST_F(SettingsTest, BadFileOnGet) { + InitializeBadFile(); + + UUID client_id; + EXPECT_TRUE(settings()->GetClientID(&client_id)); + EXPECT_NE(UUID(), client_id); + + Settings local_settings(settings_path()); + EXPECT_TRUE(local_settings.Initialize()); + UUID actual; + EXPECT_TRUE(local_settings.GetClientID(&actual)); + EXPECT_EQ(client_id, actual); +} + +TEST_F(SettingsTest, BadFileOnSet) { + InitializeBadFile(); + + EXPECT_TRUE(settings()->SetUploadsEnabled(true)); + bool enabled = false; + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_TRUE(enabled); +} + +TEST_F(SettingsTest, UnlinkFile) { + UUID client_id; + EXPECT_TRUE(settings()->GetClientID(&client_id)); + EXPECT_TRUE(settings()->SetUploadsEnabled(true)); + EXPECT_TRUE(settings()->SetLastUploadAttemptTime(time(nullptr))); + +#if defined(OS_WIN) + EXPECT_EQ(0, _wunlink(settings_path().value().c_str())) + << ErrnoMessage("_wunlink"); +#else + EXPECT_EQ(0, unlink(settings_path().value().c_str())) + << ErrnoMessage("unlink"); +#endif + + Settings local_settings(settings_path()); + EXPECT_TRUE(local_settings.Initialize()); + UUID new_client_id; + EXPECT_TRUE(local_settings.GetClientID(&new_client_id)); + EXPECT_NE(client_id, new_client_id); + + // Check that all values are reset. + bool enabled = true; + EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); + + time_t time = -1; + EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&time)); + EXPECT_EQ(0, time); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/simple_string_dictionary.cc b/third_party/crashpad/crashpad/client/simple_string_dictionary.cc new file mode 100644 index 0000000..985b893 --- /dev/null +++ b/third_party/crashpad/crashpad/client/simple_string_dictionary.cc
@@ -0,0 +1,45 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/simple_string_dictionary.h" + +#include "util/stdlib/cxx.h" + +#if CXX_LIBRARY_VERSION >= 2011 +#include <type_traits> +#endif + +namespace crashpad { +namespace { + +using SimpleStringDictionaryForAssertion = TSimpleStringDictionary<1, 1, 1>; + +#if CXX_LIBRARY_VERSION >= 2011 +// In C++11, check that TSimpleStringDictionary has standard layout, which is +// what is actually important. +static_assert( + std::is_standard_layout<SimpleStringDictionaryForAssertion>::value, + "SimpleStringDictionary must be standard layout"); +#else +// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member +// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this +// property to ensure that Entry remains POD. This doesn’t work for C++11 +// because the requirements for unions have been relaxed. +union Compile_Assert { + SimpleStringDictionaryForAssertion::Entry Compile_Assert__entry_must_be_pod; +}; +#endif + +} // namespace +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/simple_string_dictionary.h b/third_party/crashpad/crashpad/client/simple_string_dictionary.h new file mode 100644 index 0000000..edaca6f --- /dev/null +++ b/third_party/crashpad/crashpad/client/simple_string_dictionary.h
@@ -0,0 +1,275 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_ +#define CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_ + +#include <string.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +// Opaque type for the serialized representation of a TSimpleStringDictionary. +// One is created in TSimpleStringDictionary::Serialize and can be deserialized +// using one of the constructors. +struct SerializedSimpleStringDictionary; + +//! \brief A map/dictionary collection implementation using a fixed amount of +//! storage, so that it does not perform any dynamic allocations for its +//! operations. +//! +//! The actual map storage (TSimpleStringDictionary::Entry) is guaranteed to be +//! POD, so that it can be transmitted over various IPC mechanisms. +//! +//! The template parameters control the amount of storage used for the key, +//! value, and map. The \a KeySize and \a ValueSize are measured in bytes, not +//! glyphs, and include space for a trailing `NUL` byte. This gives space for +//! `KeySize - 1` and `ValueSize - 1` characters in an entry. \a NumEntries is +//! the total number of entries that will fit in the map. +template <size_t KeySize = 256, size_t ValueSize = 256, size_t NumEntries = 64> +class TSimpleStringDictionary { + public: + //! \brief Constant and publicly accessible versions of the template + //! parameters. + //! \{ + static const size_t key_size = KeySize; + static const size_t value_size = ValueSize; + static const size_t num_entries = NumEntries; + //! \} + + //! \brief A single entry in the map. + struct Entry { + //! \brief The entry’s key. + //! + //! If this is a 0-length `NUL`-terminated string, the entry is inactive. + char key[KeySize]; + + //! \brief The entry’s value. + char value[ValueSize]; + + //! \brief Returns the validity of the entry. + //! + //! If #key is an empty string, the entry is considered inactive, and this + //! method returns `false`. Otherwise, returns `true`. + bool is_active() const { + return key[0] != '\0'; + } + }; + + //! \brief An iterator to traverse all of the active entries in a + //! TSimpleStringDictionary. + class Iterator { + public: + explicit Iterator(const TSimpleStringDictionary& map) + : map_(map), + current_(0) { + } + + //! \brief Returns the next entry in the map, or `nullptr` if at the end of + //! the collection. + const Entry* Next() { + while (current_ < map_.num_entries) { + const Entry* entry = &map_.entries_[current_++]; + if (entry->is_active()) { + return entry; + } + } + return nullptr; + } + + private: + const TSimpleStringDictionary& map_; + size_t current_; + + DISALLOW_COPY_AND_ASSIGN(Iterator); + }; + + TSimpleStringDictionary() + : entries_() { + } + + TSimpleStringDictionary(const TSimpleStringDictionary& other) { + *this = other; + } + + TSimpleStringDictionary& operator=(const TSimpleStringDictionary& other) { + memcpy(entries_, other.entries_, sizeof(entries_)); + return *this; + } + + //! \brief Constructs a map from its serialized form. \a map should be the out + //! parameter from Serialize(), and \a size should be its return value. + TSimpleStringDictionary( + const SerializedSimpleStringDictionary* map, size_t size) { + DCHECK_EQ(size, sizeof(entries_)); + if (size == sizeof(entries_)) { + memcpy(entries_, map, size); + } + } + + //! \brief Returns the number of active key/value pairs. The upper limit for + //! this is \a NumEntries. + size_t GetCount() const { + size_t count = 0; + for (size_t i = 0; i < num_entries; ++i) { + if (entries_[i].is_active()) { + ++count; + } + } + return count; + } + + //! \brief Given \a key, returns its corresponding value. + //! + //! \param[in] key The key to look up. This must not be `nullptr`. + //! + //! \return The corresponding value for \a key, or if \a key is not found, + //! `nullptr`. + const char* GetValueForKey(const char* key) const { + DCHECK(key); + if (!key) { + return nullptr; + } + + const Entry* entry = GetConstEntryForKey(key); + if (!entry) { + return nullptr; + } + + return entry->value; + } + + //! \brief Stores \a value into \a key, replacing the existing value if \a key + //! is already present. + //! + //! If \a key is not yet in the map and the map is already full (containing + //! \a NumEntries active entries), this operation silently fails. + //! + //! \param[in] key The key to store. This must not be `nullptr`. + //! \param[in] value The value to store. If `nullptr`, \a key is removed from + //! the map. + void SetKeyValue(const char* key, const char* value) { + if (!value) { + RemoveKey(key); + return; + } + + DCHECK(key); + if (!key) { + return; + } + + // |key| must not be an empty string. + DCHECK_NE(key[0], '\0'); + if (key[0] == '\0') { + return; + } + + Entry* entry = GetEntryForKey(key); + + // If it does not yet exist, attempt to insert it. + if (!entry) { + for (size_t i = 0; i < num_entries; ++i) { + if (!entries_[i].is_active()) { + entry = &entries_[i]; + + strncpy(entry->key, key, key_size); + entry->key[key_size - 1] = '\0'; + + break; + } + } + } + + // If the map is out of space, |entry| will be nullptr. + if (!entry) { + return; + } + +#ifndef NDEBUG + // Sanity check that the key only appears once. + int count = 0; + for (size_t i = 0; i < num_entries; ++i) { + if (strncmp(entries_[i].key, key, key_size) == 0) { + ++count; + } + } + DCHECK_EQ(count, 1); +#endif + + strncpy(entry->value, value, value_size); + entry->value[value_size - 1] = '\0'; + } + + //! \brief Removes \a key from the map. + //! + //! If \a key is not found, this is a no-op. + //! + //! \param[in] key The key of the entry to remove. This must not be `nullptr`. + void RemoveKey(const char* key) { + DCHECK(key); + if (!key) { + return; + } + + Entry* entry = GetEntryForKey(key); + if (entry) { + entry->key[0] = '\0'; + entry->value[0] = '\0'; + } + + DCHECK_EQ(GetEntryForKey(key), implicit_cast<Entry*>(nullptr)); + } + + //! \brief Returns a serialized form of the map. + //! + //! Places a serialized version of the map into \a map and returns the size in + //! bytes. Both \a map and the size should be passed to the deserializing + //! constructor. Note that the serialized \a map is scoped to the lifetime of + //! the non-serialized instance of this class. The \a map data can be copied + //! across IPC boundaries. + size_t Serialize(const SerializedSimpleStringDictionary** map) const { + *map = reinterpret_cast<const SerializedSimpleStringDictionary*>(entries_); + return sizeof(entries_); + } + + private: + const Entry* GetConstEntryForKey(const char* key) const { + for (size_t i = 0; i < num_entries; ++i) { + if (strncmp(key, entries_[i].key, key_size) == 0) { + return &entries_[i]; + } + } + return nullptr; + } + + Entry* GetEntryForKey(const char* key) { + return const_cast<Entry*>(GetConstEntryForKey(key)); + } + + Entry entries_[NumEntries]; +}; + +//! \brief A TSimpleStringDictionary with default template parameters. +//! +//! For historical reasons this specialized version is available with the same +//! size factors as a previous implementation. +using SimpleStringDictionary = TSimpleStringDictionary<256, 256, 64>; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_
diff --git a/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc b/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc new file mode 100644 index 0000000..6396c2fe --- /dev/null +++ b/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc
@@ -0,0 +1,300 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/simple_string_dictionary.h" + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(SimpleStringDictionary, Entry) { + using TestMap = TSimpleStringDictionary<5, 9, 15>; + TestMap map; + + const TestMap::Entry* entry = TestMap::Iterator(map).Next(); + EXPECT_FALSE(entry); + + // Try setting a key/value and then verify. + map.SetKeyValue("key1", "value1"); + entry = TestMap::Iterator(map).Next(); + ASSERT_TRUE(entry); + EXPECT_STREQ(entry->key, "key1"); + EXPECT_STREQ(entry->value, "value1"); + + // Try setting a new value. + map.SetKeyValue("key1", "value3"); + EXPECT_STREQ(entry->value, "value3"); + + // Make sure the key didn't change. + EXPECT_STREQ(entry->key, "key1"); + + // Clear the entry and verify the key and value are empty strings. + map.RemoveKey("key1"); + EXPECT_FALSE(entry->is_active()); + EXPECT_EQ(strlen(entry->key), 0u); + EXPECT_EQ(strlen(entry->value), 0u); +} + +TEST(SimpleStringDictionary, SimpleStringDictionary) { + // Make a new dictionary + SimpleStringDictionary dict; + + // Set three distinct values on three keys + dict.SetKeyValue("key1", "value1"); + dict.SetKeyValue("key2", "value2"); + dict.SetKeyValue("key3", "value3"); + + EXPECT_NE(dict.GetValueForKey("key1"), "value1"); + EXPECT_NE(dict.GetValueForKey("key2"), "value2"); + EXPECT_NE(dict.GetValueForKey("key3"), "value3"); + EXPECT_EQ(dict.GetCount(), 3u); + // try an unknown key + EXPECT_FALSE(dict.GetValueForKey("key4")); + + // Remove a key + dict.RemoveKey("key3"); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict.GetValueForKey("key3")); + + // Remove by setting value to nullptr + dict.SetKeyValue("key2", nullptr); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict.GetValueForKey("key2")); +} + +TEST(SimpleStringDictionary, CopyAndAssign) { + TSimpleStringDictionary<10, 10, 10> map; + map.SetKeyValue("one", "a"); + map.SetKeyValue("two", "b"); + map.SetKeyValue("three", "c"); + map.RemoveKey("two"); + EXPECT_EQ(2u, map.GetCount()); + + // Test copy. + TSimpleStringDictionary<10, 10, 10> map_copy(map); + EXPECT_EQ(2u, map_copy.GetCount()); + EXPECT_STREQ("a", map_copy.GetValueForKey("one")); + EXPECT_STREQ("c", map_copy.GetValueForKey("three")); + map_copy.SetKeyValue("four", "d"); + EXPECT_STREQ("d", map_copy.GetValueForKey("four")); + EXPECT_FALSE(map.GetValueForKey("four")); + + // Test assign. + TSimpleStringDictionary<10, 10, 10> map_assign; + map_assign = map; + EXPECT_EQ(2u, map_assign.GetCount()); + EXPECT_STREQ("a", map_assign.GetValueForKey("one")); + EXPECT_STREQ("c", map_assign.GetValueForKey("three")); + map_assign.SetKeyValue("four", "d"); + EXPECT_STREQ("d", map_assign.GetValueForKey("four")); + EXPECT_FALSE(map.GetValueForKey("four")); + + map.RemoveKey("one"); + EXPECT_FALSE(map.GetValueForKey("one")); + EXPECT_STREQ("a", map_copy.GetValueForKey("one")); + EXPECT_STREQ("a", map_assign.GetValueForKey("one")); +} + +// Add a bunch of values to the dictionary, remove some entries in the middle, +// and then add more. +TEST(SimpleStringDictionary, Iterator) { + SimpleStringDictionary* dict = new SimpleStringDictionary; + ASSERT_TRUE(dict); + + char key[SimpleStringDictionary::key_size]; + char value[SimpleStringDictionary::value_size]; + + const int kDictionaryCapacity = SimpleStringDictionary::num_entries; + const int kPartitionIndex = kDictionaryCapacity - 5; + + // We assume at least this size in the tests below + ASSERT_GE(kDictionaryCapacity, 64); + + // We'll keep track of the number of key/value pairs we think should be in the + // dictionary + int expectedDictionarySize = 0; + + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... + for (int i = 0; i < kPartitionIndex; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize = kPartitionIndex; + + // set a couple of the keys twice (with the same value) - should be nop + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key4", "value4"); + dict->SetKeyValue("key15", "value15"); + + // Remove some random elements in the middle + dict->RemoveKey("key7"); + dict->RemoveKey("key18"); + dict->RemoveKey("key23"); + dict->RemoveKey("key31"); + expectedDictionarySize -= 4; // we just removed four key/value pairs + + // Set some more key/value pairs like key59/value59, key60/value60, ... + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; + + // Now create an iterator on the dictionary + SimpleStringDictionary::Iterator iter(*dict); + + // We then verify that it iterates through exactly the number of key/value + // pairs we expect, and that they match one-for-one with what we would expect. + // The ordering of the iteration does not matter... + + // used to keep track of number of occurrences found for key/value pairs + int count[kDictionaryCapacity]; + memset(count, 0, sizeof(count)); + + int totalCount = 0; + + for (;;) { + const SimpleStringDictionary::Entry* entry = iter.Next(); + if (!entry) + break; + totalCount++; + + // Extract keyNumber from a string of the form key<keyNumber> + int keyNumber; + sscanf(entry->key, "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value<valueNumber> + int valueNumber; + sscanf(entry->value, "value%d", &valueNumber); + + // The value number should equal the key number since that's how we set them + EXPECT_EQ(keyNumber, valueNumber); + + // Key and value numbers should be in proper range: 0 <= keyNumber < + // kDictionaryCapacity + bool isKeyInGoodRange = (keyNumber >= 0 && keyNumber < kDictionaryCapacity); + bool isValueInGoodRange = + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); + EXPECT_TRUE(isKeyInGoodRange); + EXPECT_TRUE(isValueInGoodRange); + + if (isKeyInGoodRange && isValueInGoodRange) { + ++count[keyNumber]; + } + } + + // Make sure each of the key/value pairs showed up exactly one time, except + // for the ones which we removed. + for (size_t i = 0; i < kDictionaryCapacity; ++i) { + // Skip over key7, key18, key23, and key31, since we removed them + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { + EXPECT_EQ(count[i], 1); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + EXPECT_EQ(totalCount, expectedDictionarySize); +} + +TEST(SimpleStringDictionary, AddRemove) { + TSimpleStringDictionary<5, 7, 6> map; + map.SetKeyValue("rob", "ert"); + map.SetKeyValue("mike", "pink"); + map.SetKeyValue("mark", "allays"); + + EXPECT_EQ(3u, map.GetCount()); + EXPECT_STREQ("ert", map.GetValueForKey("rob")); + EXPECT_STREQ("pink", map.GetValueForKey("mike")); + EXPECT_STREQ("allays", map.GetValueForKey("mark")); + + map.RemoveKey("mike"); + + EXPECT_EQ(2u, map.GetCount()); + EXPECT_FALSE(map.GetValueForKey("mike")); + + map.SetKeyValue("mark", "mal"); + EXPECT_EQ(2u, map.GetCount()); + EXPECT_STREQ("mal", map.GetValueForKey("mark")); + + map.RemoveKey("mark"); + EXPECT_EQ(1u, map.GetCount()); + EXPECT_FALSE(map.GetValueForKey("mark")); +} + +TEST(SimpleStringDictionary, Serialize) { + using TestMap = TSimpleStringDictionary<4, 5, 7>; + TestMap map; + map.SetKeyValue("one", "abc"); + map.SetKeyValue("two", "def"); + map.SetKeyValue("tre", "hig"); + + EXPECT_STREQ("abc", map.GetValueForKey("one")); + EXPECT_STREQ("def", map.GetValueForKey("two")); + EXPECT_STREQ("hig", map.GetValueForKey("tre")); + + const SerializedSimpleStringDictionary* serialized; + size_t size = map.Serialize(&serialized); + + SerializedSimpleStringDictionary* serialized_copy = + reinterpret_cast<SerializedSimpleStringDictionary*>(malloc(size)); + ASSERT_TRUE(serialized_copy); + memcpy(serialized_copy, serialized, size); + + TestMap deserialized(serialized_copy, size); + free(serialized_copy); + + EXPECT_EQ(3u, deserialized.GetCount()); + EXPECT_STREQ("abc", deserialized.GetValueForKey("one")); + EXPECT_STREQ("def", deserialized.GetValueForKey("two")); + EXPECT_STREQ("hig", deserialized.GetValueForKey("tre")); +} + +// Running out of space shouldn't crash. +TEST(SimpleStringDictionary, OutOfSpace) { + TSimpleStringDictionary<3, 2, 2> map; + map.SetKeyValue("a", "1"); + map.SetKeyValue("b", "2"); + map.SetKeyValue("c", "3"); + EXPECT_EQ(2u, map.GetCount()); + EXPECT_FALSE(map.GetValueForKey("c")); +} + +#if DCHECK_IS_ON() + +TEST(SimpleStringDictionaryDeathTest, NullKey) { + TSimpleStringDictionary<4, 6, 6> map; + ASSERT_DEATH_CHECK(map.SetKeyValue(nullptr, "hello"), "key"); + + map.SetKeyValue("hi", "there"); + ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key"); + EXPECT_STREQ("there", map.GetValueForKey("hi")); + + ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key"); + map.RemoveKey("hi"); + EXPECT_EQ(0u, map.GetCount()); +} + +#endif + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/simulate_crash.h b/third_party/crashpad/crashpad/client/simulate_crash.h new file mode 100644 index 0000000..299fe97 --- /dev/null +++ b/third_party/crashpad/crashpad/client/simulate_crash.h
@@ -0,0 +1,26 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_H_ + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include "client/simulate_crash_mac.h" +#elif defined(OS_WIN) +#include "client/simulate_crash_win.h" +#endif + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac.cc b/third_party/crashpad/crashpad/client/simulate_crash_mac.cc new file mode 100644 index 0000000..71d5d90 --- /dev/null +++ b/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
@@ -0,0 +1,240 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/simulate_crash_mac.h" + +#include <string.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "util/mach/exc_client_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +namespace { + +//! \brief Sends an exception message to an exception port in accordance with +//! the behavior and thread state flavor it’s registered to receive. +//! +//! \param[in] thread, task, exception, code, code_count These parameters will +//! be passed to the exception handler as appropriate. +//! \param[in] cpu_context The value to use for the thread state, if \a behavior +//! indicates that the handler should receive a thread state and if the +//! supplied thread state matches or can be converted to \a flavor. If \a +//! behavior requires a thread state but this argument cannot be converted +//! to match \a flavor, `thread_get_state()` will be called to obtain a +//! suitable thread state value. +//! \param[in] handler The Mach exception handler to deliver the exception to. +//! \param[in] set_state If `true` and \a behavior indicates that the handler +//! should receive and return a thread state, a new thread state will be set +//! by `thread_set_state()` upon successful completion of the exception +//! handler. If `false`, this will be suppressed, even when \a behavior +//! indicates that the handler receives and returns a thread state. +//! +//! \return `true` if the exception was delivered to the handler and the handler +//! indicated success. `false` otherwise, with a warning message logged. +bool DeliverException(thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t code_count, + const NativeCPUContext& cpu_context, + const ExceptionPorts::ExceptionHandler& handler, + bool set_state) { + kern_return_t kr; + + bool handler_wants_state = ExceptionBehaviorHasState(handler.behavior); + if (!handler_wants_state) { + // Regardless of the passed-in value of |set_state|, if the handler won’t be + // dealing with any state at all, no state should be set. + set_state = false; + } + + // old_state is only used if the context already captured doesn’t match (or + // can’t be converted to) what’s registered for the handler. + thread_state_data_t old_state; + + thread_state_flavor_t flavor = handler.flavor; + ConstThreadState state; + mach_msg_type_number_t state_count; + switch (flavor) { +#if defined(ARCH_CPU_X86_FAMILY) + case x86_THREAD_STATE: + state = reinterpret_cast<ConstThreadState>(&cpu_context); + state_count = x86_THREAD_STATE_COUNT; + break; +#if defined(ARCH_CPU_X86) + case x86_THREAD_STATE32: + state = reinterpret_cast<ConstThreadState>(&cpu_context.uts.ts32); + state_count = cpu_context.tsh.count; + break; +#elif defined(ARCH_CPU_X86_64) + case x86_THREAD_STATE64: + state = reinterpret_cast<ConstThreadState>(&cpu_context.uts.ts64); + state_count = cpu_context.tsh.count; + break; +#endif +#else +#error Port to your CPU architecture +#endif + + case THREAD_STATE_NONE: + // This is only acceptable if the handler doesn’t have one of the “state” + // behaviors. Otherwise, if the kernel were attempting to send an + // exception message to this port, it would call thread_getstatus() (known + // outside the kernel as thread_get_state()) which would fail because + // THREAD_STATE_NONE is not a valid state to get. See 10.9.5 + // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver() and + // xnu-2422.115.4/osfmk/i386/pcb.c machine_thread_get_state(). + if (!handler_wants_state) { + state = nullptr; + state_count = 0; + break; + } + + LOG(WARNING) << "exception handler has unexpected state flavor" << flavor; + return false; + + default: + if (!handler_wants_state) { + // Don’t bother getting any thread state if the handler’s not actually + // going to use it. + state = nullptr; + state_count = 0; + } else { + state = old_state; + state_count = THREAD_STATE_MAX; + kr = thread_get_state(thread, flavor, old_state, &state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_get_state"; + return false; + } + } + break; + } + + // new_state is supposed to be an out parameter only, but in case the handler + // doesn't touch it, make sure it's initialized to a valid thread state. + // Otherwise, the thread_set_state() call below would set a garbage thread + // state. + thread_state_data_t new_state; + size_t state_size = + sizeof(natural_t) * + std::min(state_count, implicit_cast<unsigned int>(THREAD_STATE_MAX)); + memcpy(new_state, state, state_size); + mach_msg_type_number_t new_state_count = THREAD_STATE_MAX; + + kr = UniversalExceptionRaise(handler.behavior, + handler.port, + thread, + task, + exception, + code, + code_count, + &flavor, + state, + state_count, + new_state, + &new_state_count); + + // The kernel treats a return value of MACH_RCV_PORT_DIED as successful, + // although it will not set a new thread state in that case. See 10.9.5 + // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver(), and the more + // elaborate comment at util/mach/exc_server_variants.h + // ExcServerSuccessfulReturnValue(). Duplicate that behavior. + bool success = kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED; + MACH_LOG_IF(WARNING, !success, kr) << "UniversalExceptionRaise"; + + if (kr == KERN_SUCCESS && set_state) { + kr = thread_set_state(thread, flavor, new_state, new_state_count); + MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "thread_set_state"; + } + + return success; +} + +} // namespace + +void SimulateCrash(const NativeCPUContext& cpu_context) { +#if defined(ARCH_CPU_X86) + DCHECK_EQ(cpu_context.tsh.flavor, + implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32)); + DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count), + x86_THREAD_STATE32_COUNT); +#elif defined(ARCH_CPU_X86_64) + DCHECK_EQ(cpu_context.tsh.flavor, + implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64)); + DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count), + x86_THREAD_STATE64_COUNT); +#endif + + base::mac::ScopedMachSendRight thread(mach_thread_self()); + exception_type_t exception = kMachExceptionSimulated; + mach_exception_data_type_t codes[] = {0, 0}; + mach_msg_type_number_t code_count = arraysize(codes); + + // Look up the handler for EXC_CRASH exceptions in the same way that the + // kernel would: try a thread handler, then a task handler, and finally a host + // handler. 10.9.5 xnu-2422.115.4/osfmk/kern/exception.c exception_triage(). + const ExceptionPorts::TargetType kTargetTypes[] = { + ExceptionPorts::kTargetTypeThread, + ExceptionPorts::kTargetTypeTask, + + // This is not expected to succeed, because mach_host_self() doesn’t + // return the host_priv port to non-root users, and this is the port + // that’s required for host_get_exception_ports(). + // + // See 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c set_security_token(), + // xnu-2422.115.4/osfmk/kern/task.c host_security_set_task_token(), and + // xnu-2422.115.4/osfmk/kern/ipc_host.c host_get_exception_ports(). + ExceptionPorts::kTargetTypeHost, + }; + + bool success = false; + + for (size_t target_type_index = 0; + !success && target_type_index < arraysize(kTargetTypes); + ++target_type_index) { + ExceptionPorts::ExceptionHandlerVector handlers; + ExceptionPorts exception_ports(kTargetTypes[target_type_index], + MACH_PORT_NULL); + if (exception_ports.GetExceptionPorts(EXC_MASK_CRASH, &handlers)) { + DCHECK_LE(handlers.size(), 1u); + if (handlers.size() == 1) { + DCHECK(handlers[0].mask & EXC_MASK_CRASH); + success = DeliverException(thread.get(), + mach_task_self(), + exception, + codes, + code_count, + cpu_context, + handlers[0], + false); + } + } + } + + LOG_IF(WARNING, !success) + << "SimulateCrash did not find an appropriate exception handler"; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac.h b/third_party/crashpad/crashpad/client/simulate_crash_mac.h new file mode 100644 index 0000000..e14db3c --- /dev/null +++ b/third_party/crashpad/crashpad/client/simulate_crash_mac.h
@@ -0,0 +1,60 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_ + +#include <mach/mach.h> + +#include "client/capture_context_mac.h" + +//! \file + +namespace crashpad { + +//! \brief Simulates a exception without crashing. +//! +//! This function searches for an `EXC_CRASH` handler in the same manner that +//! the kernel does, and sends it an exception message to that handler in the +//! format that the handler expects, considering the behavior and thread state +//! flavor that are registered for it. The exception sent to the handler will be +//! ::kMachExceptionSimulated, not `EXC_CRASH`. +//! +//! Typically, the CRASHPAD_SIMULATE_CRASH() macro will be used in preference to +//! this function, because it combines the context-capture operation with the +//! raising of a simulated exception. +//! +//! This function returns normally after the exception message is processed. If +//! no valid handler was found, or no handler processed the exception +//! successfully, a warning will be logged, but these conditions are not +//! considered fatal. +//! +//! \param[in] cpu_context The thread state to pass to the exception handler as +//! the exception context, provided that it is compatible with the thread +//! state flavor that the exception handler accepts. If it is not +//! compatible, the correct thread state for the handler will be obtained by +//! calling `thread_get_state()`. +void SimulateCrash(const NativeCPUContext& cpu_context); + +} // namespace crashpad + +//! \brief Captures the CPU context and simulates an exception without crashing. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + crashpad::NativeCPUContext cpu_context; \ + crashpad::CaptureContext(&cpu_context); \ + crashpad::SimulateCrash(cpu_context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc b/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc new file mode 100644 index 0000000..8953114 --- /dev/null +++ b/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
@@ -0,0 +1,387 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "client/simulate_crash.h" + +#include <mach/mach.h> +#include <string.h> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +class TestSimulateCrashMac final : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + // Defines which targets the child should set an EXC_CRASH exception handler + // for. + enum ExceptionPortsTarget { + // The child should clear its EXC_CRASH handler for both its task and thread + // targets. SimulateCrash() will attempt to deliver the exception to the + // host target, which will fail if not running as root. In any case, the + // parent should not expect to receive any exception message from the child. + kExceptionPortsTargetNone = 0, + + // The child will set an EXC_CRASH handler for its task target, and clear it + // for its thread target. The parent runs an exception server to receive + // the child’s simulated crash message. + kExceptionPortsTargetTask, + + // The child will set an EXC_CRASH handler for its thread target, and clear + // it for its task target. The parent runs an exception server to receive + // the child’s simulated crash message. + kExceptionPortsTargetThread, + + // The child sets an EXC_CRASH handler for both its task and thread targets. + // The parent runs an exception server to receive the message expected to be + // delivered to the thread target, but returns an error code. The child will + // then fall back to trying the server registered for the task target, + // sending a second message to the parent. The server in the parent will + // handle this one successfully. + kExceptionPortsTargetBoth, + }; + + TestSimulateCrashMac(ExceptionPortsTarget target, + exception_behavior_t behavior, + thread_state_flavor_t flavor) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + target_(target), + behavior_(behavior), + flavor_(flavor), + succeed_(true) { + } + + ~TestSimulateCrashMac() {} + + // UniversalMachExcServer::Interface: + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + // Check the entire exception message, because most or all of it was + // generated by SimulateCrash() instead of the kernel. + + EXPECT_EQ(behavior_, behavior); + EXPECT_EQ(LocalPort(), exception_port); + if (ExceptionBehaviorHasIdentity(behavior)) { + EXPECT_NE(THREAD_NULL, thread); + EXPECT_EQ(ChildTask(), task); + } else { + EXPECT_EQ(THREAD_NULL, thread); + EXPECT_EQ(TASK_NULL, task); + } + EXPECT_EQ(kMachExceptionSimulated, exception); + EXPECT_EQ(2u, code_count); + if (code_count >= 1) { + EXPECT_EQ(0, code[0]); + } + if (code_count >= 2) { + EXPECT_EQ(0, code[1]); + } + if (!ExceptionBehaviorHasState(behavior)) { + EXPECT_EQ(THREAD_STATE_NONE, *flavor); + } else { + EXPECT_EQ(flavor_, *flavor); + switch (*flavor) { +#if defined(ARCH_CPU_X86_FAMILY) + case x86_THREAD_STATE: { + EXPECT_EQ(x86_THREAD_STATE_COUNT, old_state_count); + const x86_thread_state* state = + reinterpret_cast<const x86_thread_state*>(old_state); + switch (state->tsh.flavor) { + case x86_THREAD_STATE32: + EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT), + state->tsh.count); + break; + case x86_THREAD_STATE64: + EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT), + state->tsh.count); + break; + default: + ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor; + break; + } + break; + } + case x86_FLOAT_STATE: { + EXPECT_EQ(x86_FLOAT_STATE_COUNT, old_state_count); + const x86_float_state* state = + reinterpret_cast<const x86_float_state*>(old_state); + switch (state->fsh.flavor) { + case x86_FLOAT_STATE32: + EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE32_COUNT), + state->fsh.count); + break; + case x86_FLOAT_STATE64: + EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE64_COUNT), + state->fsh.count); + break; + default: + ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor; + break; + } + break; + } + case x86_DEBUG_STATE: { + EXPECT_EQ(x86_DEBUG_STATE_COUNT, old_state_count); + const x86_debug_state* state = + reinterpret_cast<const x86_debug_state*>(old_state); + switch (state->dsh.flavor) { + case x86_DEBUG_STATE32: + EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE32_COUNT), + state->dsh.count); + break; + case x86_DEBUG_STATE64: + EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE64_COUNT), + state->dsh.count); + break; + default: + ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor; + break; + } + break; + } + case x86_THREAD_STATE32: + EXPECT_EQ(x86_THREAD_STATE32_COUNT, old_state_count); + break; + case x86_FLOAT_STATE32: + EXPECT_EQ(x86_FLOAT_STATE32_COUNT, old_state_count); + break; + case x86_DEBUG_STATE32: + EXPECT_EQ(x86_DEBUG_STATE32_COUNT, old_state_count); + break; + case x86_THREAD_STATE64: + EXPECT_EQ(x86_THREAD_STATE64_COUNT, old_state_count); + break; + case x86_FLOAT_STATE64: + EXPECT_EQ(x86_FLOAT_STATE64_COUNT, old_state_count); + break; + case x86_DEBUG_STATE64: + EXPECT_EQ(x86_DEBUG_STATE64_COUNT, old_state_count); + break; +#else +#error Port to your CPU architecture +#endif + default: + ADD_FAILURE() << "unexpected flavor " << *flavor; + break; + } + + // Attempt to set a garbage thread state, which would cause the child to + // crash inside SimulateCrash() if it actually succeeded. This tests that + // SimulateCrash() ignores new_state instead of attempting to set the + // state as the kernel would do. This operates in conjunction with the + // |true| argument to ExcServerSuccessfulReturnValue() below. + *new_state_count = old_state_count; + size_t new_state_size = sizeof(natural_t) * old_state_count; + memset(new_state, 0xa5, new_state_size); + } + + if (!succeed_) { + // The client has registered EXC_CRASH handlers for both its thread and + // task targets, and sent a simulated exception message to its + // thread-level EXC_CRASH handler. To test that it will fall back to + // trying the task-level EXC_CRASH handler, return a failure code, which + // should cause SimulateCrash() to try the next target. + EXPECT_EQ(kExceptionPortsTargetBoth, target_); + return KERN_ABORTED; + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + + return ExcServerSuccessfulReturnValue(exception, behavior, true); + } + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + if (target_ == kExceptionPortsTargetNone) { + // The child does not have any EXC_CRASH handlers registered for its + // thread or task targets, so no exception message is expected to be + // generated. Don’t run the server at all. + return; + } + + UniversalMachExcServer universal_mach_exc_server(this); + + mach_msg_return_t mr; + if (target_ == kExceptionPortsTargetBoth) { + // The client has registered EXC_CRASH handlers for both its thread and + // task targets. Run a server that will return a failure code when the + // exception message is sent to the thread target, which will cause the + // client to fall back to the task target and send another message. + succeed_ = false; + mr = MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(MACH_MSG_SUCCESS, mr) + << MachErrorMessage(mr, "MachMessageServer::Run"); + } + + succeed_ = true; + mr = MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(MACH_MSG_SUCCESS, mr) + << MachErrorMessage(mr, "MachMessageServer::Run"); + } + + void MachMultiprocessChild() override { + bool task_valid = target_ == kExceptionPortsTargetTask || + target_ == kExceptionPortsTargetBoth; + ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask, + TASK_NULL); + ASSERT_TRUE(task_exception_ports.SetExceptionPort( + EXC_MASK_CRASH, + task_valid ? RemotePort() : MACH_PORT_NULL, + behavior_, + flavor_)); + + bool thread_valid = target_ == kExceptionPortsTargetThread || + target_ == kExceptionPortsTargetBoth; + ExceptionPorts thread_exception_ports(ExceptionPorts::kTargetTypeThread, + THREAD_NULL); + ASSERT_TRUE(thread_exception_ports.SetExceptionPort( + EXC_MASK_CRASH, + thread_valid ? RemotePort() : MACH_PORT_NULL, + behavior_, + flavor_)); + + CRASHPAD_SIMULATE_CRASH(); + } + + ExceptionPortsTarget target_; + exception_behavior_t behavior_; + thread_state_flavor_t flavor_; + bool succeed_; + + DISALLOW_COPY_AND_ASSIGN(TestSimulateCrashMac); +}; + +TEST(SimulateCrash, SimulateCrash) { + const TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = { + TestSimulateCrashMac::kExceptionPortsTargetNone, + TestSimulateCrashMac::kExceptionPortsTargetTask, + TestSimulateCrashMac::kExceptionPortsTargetThread, + TestSimulateCrashMac::kExceptionPortsTargetBoth, + }; + + const exception_behavior_t kBehaviors[] = { + EXCEPTION_DEFAULT, + EXCEPTION_STATE, + EXCEPTION_STATE_IDENTITY, + EXCEPTION_DEFAULT | kMachExceptionCodes, + EXCEPTION_STATE | kMachExceptionCodes, + EXCEPTION_STATE_IDENTITY | kMachExceptionCodes, + }; + + const thread_state_flavor_t kFlavors[] = { +#if defined(ARCH_CPU_X86_FAMILY) + x86_THREAD_STATE, + x86_FLOAT_STATE, + x86_DEBUG_STATE, +#if defined(ARCH_CPU_X86) + x86_THREAD_STATE32, + x86_FLOAT_STATE32, + x86_DEBUG_STATE32, +#elif defined(ARCH_CPU_X86_64) + x86_THREAD_STATE64, + x86_FLOAT_STATE64, + x86_DEBUG_STATE64, +#endif +#else +#error Port to your CPU architecture +#endif + }; + + for (size_t target_index = 0; + target_index < arraysize(kTargets); + ++target_index) { + TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index]; + SCOPED_TRACE(base::StringPrintf( + "target_index %zu, target %d", target_index, target)); + + for (size_t behavior_index = 0; + behavior_index < arraysize(kBehaviors); + ++behavior_index) { + exception_behavior_t behavior = kBehaviors[behavior_index]; + SCOPED_TRACE(base::StringPrintf( + "behavior_index %zu, behavior %s", + behavior_index, + ExceptionBehaviorToString(behavior, kUseFullName | kUnknownIsNumeric) + .c_str())); + + if (!ExceptionBehaviorHasState(behavior)) { + TestSimulateCrashMac test_simulate_crash_mac( + target, behavior, THREAD_STATE_NONE); + test_simulate_crash_mac.Run(); + } else { + for (size_t flavor_index = 0; + flavor_index < arraysize(kFlavors); + ++flavor_index) { + thread_state_flavor_t flavor = kFlavors[flavor_index]; + SCOPED_TRACE(base::StringPrintf( + "flavor_index %zu, flavor %s", + flavor_index, + ThreadStateFlavorToString( + flavor, kUseFullName | kUnknownIsNumeric).c_str())); + + TestSimulateCrashMac test_simulate_crash_mac( + target, behavior, flavor); + test_simulate_crash_mac.Run(); + } + } + } + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_win.h b/third_party/crashpad/crashpad/client/simulate_crash_win.h new file mode 100644 index 0000000..a20f3da --- /dev/null +++ b/third_party/crashpad/crashpad/client/simulate_crash_win.h
@@ -0,0 +1,33 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_ + +#include <windows.h> + +#include "client/crashpad_client.h" +#include "util/win/capture_context.h" + +//! \file + +//! \brief Captures the CPU context and captures a dump without an exception. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + CONTEXT context; \ + crashpad::CaptureContext(&context); \ + crashpad::CrashpadClient::DumpWithoutCrash(context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_
diff --git a/third_party/crashpad/crashpad/codereview.settings b/third_party/crashpad/crashpad/codereview.settings new file mode 100644 index 0000000..c3673e8 --- /dev/null +++ b/third_party/crashpad/crashpad/codereview.settings
@@ -0,0 +1,18 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +CODE_REVIEW_SERVER: codereview.chromium.org +CC_LIST: crashpad-dev@chromium.org +VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/ +PROJECT: crashpad
diff --git a/third_party/crashpad/crashpad/compat/compat.gyp b/third_party/crashpad/crashpad/compat/compat.gyp new file mode 100644 index 0000000..176fe4f9 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/compat.gyp
@@ -0,0 +1,85 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_compat', + 'type': 'static_library', + 'sources': [ + 'mac/AvailabilityMacros.h', + 'mac/kern/exc_resource.h' + 'mac/mach/mach.h', + 'mac/mach-o/getsect.cc', + 'mac/mach-o/getsect.h', + 'mac/mach-o/loader.h', + 'mac/sys/resource.h', + 'non_mac/mach/mach.h', + 'non_win/dbghelp.h', + 'non_win/minwinbase.h', + 'non_win/timezoneapi.h', + 'non_win/verrsrc.h', + 'non_win/windows.h', + 'non_win/winnt.h', + 'win/getopt.h', + 'win/strings.cc', + 'win/strings.h', + 'win/sys/types.h', + 'win/time.cc', + 'win/time.h', + 'win/winnt.h', + ], + 'conditions': [ + ['OS=="mac"', { + 'dependencies': [ + '../third_party/apple_cctools/apple_cctools.gyp:apple_cctools', + ], + 'include_dirs': [ + 'mac', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'mac', + ], + }, + }], + ['OS=="win"', { + 'include_dirs': [ + 'win', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'win', + ], + }, + 'dependencies': [ + '../third_party/getopt/getopt.gyp:getopt', + ], + }, { + 'include_dirs': [ + 'non_win', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'non_win', + ], + }, + }], + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h b/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h new file mode 100644 index 0000000..decaeb51 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h
@@ -0,0 +1,50 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_ +#define CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_ + +#include_next <AvailabilityMacros.h> + +// 10.7 SDK + +#ifndef MAC_OS_X_VERSION_10_7 +#define MAC_OS_X_VERSION_10_7 1070 +#endif + +// 10.8 SDK + +#ifndef MAC_OS_X_VERSION_10_8 +#define MAC_OS_X_VERSION_10_8 1080 +#endif + +// 10.9 SDK + +#ifndef MAC_OS_X_VERSION_10_9 +#define MAC_OS_X_VERSION_10_9 1090 +#endif + +// 10.10 SDK + +#ifndef MAC_OS_X_VERSION_10_10 +#define MAC_OS_X_VERSION_10_10 101000 +#endif + +// 10.11 SDK + +#ifndef MAC_OS_X_VERSION_10_11 +#define MAC_OS_X_VERSION_10_11 101100 +#endif + +#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
diff --git a/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h b/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h new file mode 100644 index 0000000..30e9b85 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h
@@ -0,0 +1,62 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_ +#define CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_ + +#if __has_include_next(<kern/exc_resource.h>) +#include_next <kern/exc_resource.h> +#endif + +// 10.9 SDK + +#ifndef EXC_RESOURCE_DECODE_RESOURCE_TYPE +#define EXC_RESOURCE_DECODE_RESOURCE_TYPE(code) (((code) >> 61) & 0x7ull) +#endif + +#ifndef EXC_RESOURCE_DECODE_FLAVOR +#define EXC_RESOURCE_DECODE_FLAVOR(code) (((code) >> 58) & 0x7ull) +#endif + +#ifndef RESOURCE_TYPE_CPU +#define RESOURCE_TYPE_CPU 1 +#endif + +#ifndef RESOURCE_TYPE_WAKEUPS +#define RESOURCE_TYPE_WAKEUPS 2 +#endif + +#ifndef RESOURCE_TYPE_MEMORY +#define RESOURCE_TYPE_MEMORY 3 +#endif + +#ifndef FLAVOR_CPU_MONITOR +#define FLAVOR_CPU_MONITOR 1 +#endif + +#ifndef FLAVOR_WAKEUPS_MONITOR +#define FLAVOR_WAKEUPS_MONITOR 1 +#endif + +#ifndef FLAVOR_HIGH_WATERMARK +#define FLAVOR_HIGH_WATERMARK 1 +#endif + +// 10.10 SDK + +#ifndef FLAVOR_CPU_MONITOR_FATAL +#define FLAVOR_CPU_MONITOR_FATAL 2 +#endif + +#endif // CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_
diff --git a/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc b/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc new file mode 100644 index 0000000..71d3066 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc
@@ -0,0 +1,107 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <mach-o/getsect.h> + +// This is only necessary when building code that might run on systems earlier +// than 10.7. When building for 10.7 or later, getsectiondata() and +// getsegmentdata() are always present in libmacho and made available through +// libSystem. When building for earlier systems, custom definitions of +// these functions are needed. +// +// This file checks the deployment target instead of the SDK. The deployment +// target is correct because it identifies the earliest possible system that +// the code being compiled is expected to run on. + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 + +#include <dlfcn.h> +#include <stddef.h> + +#include "third_party/apple_cctools/cctools/include/mach-o/getsect.h" + +namespace { + +// Returns a dlopen() handle to the same library that provides the +// getsectbyname() function. getsectbyname() is always present in libmacho. +// getsectiondata() and getsegmentdata() are not always present, but when they +// are, they’re in the same library as getsectbyname(). If the library cannot +// be found or a handle to it cannot be returned, returns nullptr. +void* SystemLibMachOHandle() { + Dl_info info; + if (!dladdr(reinterpret_cast<void*>(getsectbyname), &info)) { + return nullptr; + } + return dlopen(info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); +} + +// Returns a function pointer to a function in libmacho based on a lookup of +// that function by symbol name. Returns nullptr if libmacho cannot be found or +// opened, or if the named symbol cannot be found in libmacho. +void* LookUpSystemLibMachOSymbol(const char* symbol) { + static void* dl_handle = SystemLibMachOHandle(); + if (!dl_handle) { + return nullptr; + } + return dlsym(dl_handle, symbol); +} + +#ifndef __LP64__ +using MachHeader = mach_header; +#else +using MachHeader = mach_header_64; +#endif + +using GetSectionDataType = + uint8_t*(*)(const MachHeader*, const char*, const char*, unsigned long*); +using GetSegmentDataType = + uint8_t*(*)(const MachHeader*, const char*, unsigned long*); + +} // namespace + +extern "C" { + +// These implementations look up their functions in libmacho at run time. If +// the system libmacho provides these functions as it normally does on Mac OS X +// 10.7 and later, the system’s versions are used directly. Otherwise, the +// versions in third_party/apple_cctools are used, which are actually just +// copies of the system’s functions. + +uint8_t* getsectiondata(const MachHeader* mhp, + const char* segname, + const char* sectname, + unsigned long* size) { + static GetSectionDataType system_getsectiondata = + reinterpret_cast<GetSectionDataType>( + LookUpSystemLibMachOSymbol("getsectiondata")); + if (system_getsectiondata) { + return system_getsectiondata(mhp, segname, sectname, size); + } + return crashpad_getsectiondata(mhp, segname, sectname, size); +} + +uint8_t* getsegmentdata( + const MachHeader* mhp, const char* segname, unsigned long* size) { + static GetSegmentDataType system_getsegmentdata = + reinterpret_cast<GetSegmentDataType>( + LookUpSystemLibMachOSymbol("getsegmentdata")); + if (system_getsegmentdata) { + return system_getsegmentdata(mhp, segname, size); + } + return crashpad_getsegmentdata(mhp, segname, size); +} + +} // extern "C" + +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
diff --git a/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h b/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h new file mode 100644 index 0000000..c9169e16 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h
@@ -0,0 +1,69 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_ +#define CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_ + +#include_next <mach-o/getsect.h> + +#include <AvailabilityMacros.h> + +// This file checks the SDK instead of the deployment target. The SDK is correct +// because this file is concerned with providing compile-time declarations, +// which are either present in a specific SDK version or not. + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 + +#include <mach-o/loader.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// Don’t use a type alias to account for the mach_header/mach_header_64 +// difference between the 32-bit and 64-bit versions of getsectiondata() and +// getsegmentdata(). This file should be faithfully equivalent to the native +// SDK, and adding type aliases here would pollute the namespace in a way that +// the native SDK does not. + +#if !defined(__LP64__) + +uint8_t* getsectiondata(const struct mach_header* mhp, + const char* segname, + const char* sectname, + unsigned long* size); + +uint8_t* getsegmentdata( + const struct mach_header* mhp, const char* segname, unsigned long* size); + +#else + +uint8_t* getsectiondata(const struct mach_header_64* mhp, + const char* segname, + const char* sectname, + unsigned long* size); + +uint8_t* getsegmentdata( + const struct mach_header_64* mhp, const char* segname, unsigned long* size); + +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 + +#endif // CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_
diff --git a/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h b/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h new file mode 100644 index 0000000..95a73574 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h
@@ -0,0 +1,32 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_ +#define CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_ + +#include_next <mach-o/loader.h> + +// 10.7 SDK + +#ifndef S_THREAD_LOCAL_ZEROFILL +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#endif + +// 10.8 SDK + +#ifndef LC_SOURCE_VERSION +#define LC_SOURCE_VERSION 0x2a +#endif + +#endif // CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_
diff --git a/third_party/crashpad/crashpad/compat/mac/mach/mach.h b/third_party/crashpad/crashpad/compat/mac/mach/mach.h new file mode 100644 index 0000000..9cfe6b62 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/mach/mach.h
@@ -0,0 +1,106 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_MACH_MACH_H_ +#define CRASHPAD_COMPAT_MAC_MACH_MACH_H_ + +#include_next <mach/mach.h> + +// <mach/exception_types.h> + +// 10.8 SDK + +#ifndef EXC_RESOURCE +#define EXC_RESOURCE 11 +#endif + +#ifndef EXC_MASK_RESOURCE +#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE) +#endif + +// 10.9 SDK + +#ifndef EXC_GUARD +#define EXC_GUARD 12 +#endif + +#ifndef EXC_MASK_GUARD +#define EXC_MASK_GUARD (1 << EXC_GUARD) +#endif + +// 10.11 SDK + +#ifndef EXC_CORPSE_NOTIFY +#define EXC_CORPSE_NOTIFY 13 +#endif + +#ifndef EXC_MASK_CORPSE_NOTIFY +#define EXC_MASK_CORPSE_NOTIFY (1 << EXC_CORPSE_NOTIFY) +#endif + +// Don’t expose EXC_MASK_ALL at all, because its definition varies with SDK, and +// older kernels will reject values that they don’t understand. Instead, use +// crashpad::ExcMaskAll(), which computes the correct value of EXC_MASK_ALL for +// the running system. +#undef EXC_MASK_ALL + +#if defined(__i386__) || defined(__x86_64__) + +// <mach/i386/exception.h> + +// 10.11 SDK + +#if EXC_TYPES_COUNT > 14 // Definition varies with SDK +#error Update this file for new exception types +#elif EXC_TYPES_COUNT != 14 +#undef EXC_TYPES_COUNT +#define EXC_TYPES_COUNT 14 +#endif + +// <mach/i386/thread_status.h> + +// 10.6 SDK +// +// Earlier versions of this SDK didn’t have AVX definitions. They didn’t appear +// until the version of the 10.6 SDK that shipped with Xcode 4.2, although +// versions of this SDK appeared with Xcode releases as early as Xcode 3.2. +// Similarly, the kernel didn’t handle AVX state until Mac OS X 10.6.8 +// (xnu-1504.15.3) and presumably the hardware-specific versions of Mac OS X +// 10.6.7 intended to run on processors with AVX. + +#ifndef x86_AVX_STATE32 +#define x86_AVX_STATE32 16 +#endif + +#ifndef x86_AVX_STATE64 +#define x86_AVX_STATE64 17 +#endif + +// 10.8 SDK + +#ifndef x86_AVX_STATE +#define x86_AVX_STATE 18 +#endif + +#endif // defined(__i386__) || defined(__x86_64__) + +// <mach/thread_status.h> + +// 10.8 SDK + +#ifndef THREAD_STATE_FLAVOR_LIST_10_9 +#define THREAD_STATE_FLAVOR_LIST_10_9 129 +#endif + +#endif // CRASHPAD_COMPAT_MAC_MACH_MACH_H_
diff --git a/third_party/crashpad/crashpad/compat/mac/sys/resource.h b/third_party/crashpad/crashpad/compat/mac/sys/resource.h new file mode 100644 index 0000000..0697e16 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/mac/sys/resource.h
@@ -0,0 +1,26 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_ +#define CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_ + +#include_next <sys/resource.h> + +// 10.9 SDK + +#ifndef WAKEMON_MAKE_FATAL +#define WAKEMON_MAKE_FATAL 0x10 +#endif + +#endif // CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_
diff --git a/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h b/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h new file mode 100644 index 0000000..f33bb10 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_ +#define CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_ + +//! \file + +// <mach/exception_types.h> + +//! \anchor EXC_x +//! \name EXC_* +//! +//! \brief Mach exception type definitions. +//! \{ +#define EXC_BAD_ACCESS 1 +#define EXC_BAD_INSTRUCTION 2 +#define EXC_ARITHMETIC 3 +#define EXC_EMULATION 4 +#define EXC_SOFTWARE 5 +#define EXC_BREAKPOINT 6 +#define EXC_SYSCALL 7 +#define EXC_MACH_SYSCALL 8 +#define EXC_RPC_ALERT 9 +#define EXC_CRASH 10 +#define EXC_RESOURCE 11 +#define EXC_GUARD 12 +#define EXC_CORPSE_NOTIFY 13 + +#define EXC_TYPES_COUNT 14 +//! \} + +#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_
diff --git a/third_party/crashpad/crashpad/compat/non_win/dbghelp.h b/third_party/crashpad/crashpad/compat/non_win/dbghelp.h new file mode 100644 index 0000000..ecf8bab --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
@@ -0,0 +1,985 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_ +#define CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_ + +#include <stdint.h> + +#include "base/strings/string16.h" +#include "compat/non_win/timezoneapi.h" +#include "compat/non_win/verrsrc.h" +#include "compat/non_win/winnt.h" + +//! \file + +//! \brief The magic number for a minidump file, stored in +//! MINIDUMP_HEADER::Signature. +//! +//! A hex dump of a little-endian minidump file will begin with the string +//! “MDMP”. +#define MINIDUMP_SIGNATURE ('PMDM') // 0x4d444d50 + +//! \brief The version of a minidump file, stored in MINIDUMP_HEADER::Version. +#define MINIDUMP_VERSION (42899) + +//! \brief An offset within a minidump file, relative to the start of its +//! MINIDUMP_HEADER. +//! +//! RVA stands for “relative virtual address”. Within a minidump file, RVAs are +//! used as pointers to link structures together. +//! +//! \sa MINIDUMP_LOCATION_DESCRIPTOR +typedef uint32_t RVA; + +//! \brief A pointer to a structure or union within a minidump file. +struct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR { + //! \brief The size of the referenced structure or union, in bytes. + uint32_t DataSize; + + //! \brief The relative virtual address of the structure or union within the + //! minidump file. + RVA Rva; +}; + +//! \brief A pointer to a snapshot of a region of memory contained within a +//! minidump file. +//! +//! \sa MINIDUMP_MEMORY_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_DESCRIPTOR { + //! \brief The base address of the memory region in the address space of the + //! process that the minidump file contains a snapshot of. + uint64_t StartOfMemoryRange; + + //! \brief The contents of the memory region. + MINIDUMP_LOCATION_DESCRIPTOR Memory; +}; + +//! \brief The top-level structure identifying a minidump file. +//! +//! This structure contains a pointer to the stream directory, a second-level +//! structure which in turn contains pointers to third-level structures +//! (“streams”) containing the data within the minidump file. This structure +//! also contains the minidump file’s magic numbers, and other bookkeeping data. +//! +//! This structure must be present at the beginning of a minidump file (at ::RVA +//! 0). +struct __attribute__((packed, aligned(4))) MINIDUMP_HEADER { + //! \brief The minidump file format magic number, ::MINIDUMP_SIGNATURE. + uint32_t Signature; + + //! \brief The minidump file format version number, ::MINIDUMP_VERSION. + uint32_t Version; + + //! \brief The number of MINIDUMP_DIRECTORY elements present in the directory + //! referenced by #StreamDirectoryRva. + uint32_t NumberOfStreams; + + //! \brief A pointer to an array of MINIDUMP_DIRECTORY structures that + //! identify all of the streams within this minidump file. The array has + //! #NumberOfStreams elements present. + RVA StreamDirectoryRva; + + //! \brief The minidump file’s checksum. This can be `0`, and in practice, `0` + //! is the only value that has ever been seen in this field. + uint32_t CheckSum; + + //! \brief The time that the minidump file was generated, in `time_t` format, + //! the number of seconds since the POSIX epoch. + uint32_t TimeDateStamp; + + //! \brief A bitfield containing members of ::MINIDUMP_TYPE, describing the + //! types of data carried within this minidump file. + uint64_t Flags; +}; + +//! \brief A pointer to a stream within a minidump file. +//! +//! Each stream present in a minidump file will have a corresponding +//! MINIDUMP_DIRECTORY entry in the stream directory referenced by +//! MINIDUMP_HEADER::StreamDirectoryRva. +struct __attribute__((packed, aligned(4))) MINIDUMP_DIRECTORY { + //! \brief The type of stream referenced, a value of ::MINIDUMP_STREAM_TYPE. + uint32_t StreamType; + + //! \brief A pointer to the stream data within the minidump file. + MINIDUMP_LOCATION_DESCRIPTOR Location; +}; + +//! \brief A variable-length UTF-16-encoded string carried within a minidump +//! file. +//! +//! The UTF-16 string is stored as UTF-16LE or UTF-16BE according to the byte +//! ordering of the minidump file itself. +//! +//! \sa crashpad::MinidumpUTF8String +struct __attribute__((packed, aligned(4))) MINIDUMP_STRING { + //! \brief The length of the #Buffer field in bytes, not including the `NUL` + //! terminator. + //! + //! \note This field is interpreted as a byte count, not a count of UTF-16 + //! code units or Unicode code points. + uint32_t Length; + + //! \brief The string, encoded in UTF-16, and terminated with a UTF-16 `NUL` + //! code unit (two `NUL` bytes). + base::char16 Buffer[0]; +}; + +//! \brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each +//! stream structure has a corresponding stream type value to identify it. +//! +//! \sa crashpad::MinidumpStreamType +enum MINIDUMP_STREAM_TYPE { + //! \brief The stream type for MINIDUMP_THREAD_LIST. + ThreadListStream = 3, + + //! \brief The stream type for MINIDUMP_MODULE_LIST. + ModuleListStream = 4, + + //! \brief The stream type for MINIDUMP_MEMORY_LIST. + MemoryListStream = 5, + + //! \brief The stream type for MINIDUMP_EXCEPTION_STREAM. + ExceptionStream = 6, + + //! \brief The stream type for MINIDUMP_SYSTEM_INFO. + SystemInfoStream = 7, + + //! \brief The stream contains information about active `HANDLE`s. + HandleDataStream = 12, + + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, + //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. + //! + //! More recent versions of this stream are supersets of earlier versions. + //! + //! The exact version of the stream that is present is implied by the stream’s + //! size. Furthermore, this stream contains a field, + //! MINIDUMP_MISC_INFO::Flags1, that indicates which data is present and + //! valid. + MiscInfoStream = 15, + + //! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST. + MemoryInfoListStream = 16, +}; + +//! \brief Information about the CPU (or CPUs) that ran the process that the +//! minidump file contains a snapshot of. +//! +//! This union only appears as MINIDUMP_SYSTEM_INFO::Cpu. Its interpretation is +//! controlled by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. +union __attribute__((packed, aligned(4))) CPU_INFORMATION { + //! \brief Information about 32-bit x86 CPUs, or x86_64 CPUs when running + //! 32-bit x86 processes. + struct __attribute__((packed, aligned(4))) { + //! \brief The CPU’s vendor identification string as encoded in `cpuid 0` + //! `ebx`, `edx`, and `ecx`, represented as it appears in these + //! registers. + //! + //! For Intel CPUs, `[0]` will encode “Genu”, `[1]` will encode “ineI”, and + //! `[2]` will encode “ntel”, for a vendor ID string “GenuineIntel”. + //! + //! \note The Windows documentation incorrectly states that these fields are + //! to be interpreted as `cpuid 0` `eax`, `ebx`, and `ecx`. + uint32_t VendorId[3]; + + //! \brief Family, model, and stepping ID values as encoded in `cpuid 1` + //! `eax`. + uint32_t VersionInformation; + + //! \brief A bitfield containing supported CPU capabilities as encoded in + //! `cpuid 1` `edx`. + uint32_t FeatureInformation; + + //! \brief A bitfield containing supported CPU capabalities as encoded in + //! `cpuid 0x80000001` `edx`. + //! + //! This field is only valid if #VendorId identifies the CPU vendor as + //! “AuthenticAMD”. + uint32_t AMDExtendedCpuFeatures; + } X86CpuInfo; + + //! \brief Information about non-x86 CPUs, and x86_64 CPUs when not running + //! 32-bit x86 processes. + struct __attribute__((packed, aligned(4))) { + //! \brief Bitfields containing supported CPU capabilities as identified by + //! bits corresponding to \ref PF_x "PF_*" values passed to + //! `IsProcessorFeaturePresent()`. + uint64_t ProcessorFeatures[2]; + } OtherCpuInfo; +}; + +//! \brief Information about the system that hosted the process that the +//! minidump file contains a snapshot of. +struct __attribute__((packed, aligned(4))) MINIDUMP_SYSTEM_INFO { + // The next 4 fields are from the SYSTEM_INFO structure returned by + // GetSystemInfo(). + + //! \brief The system’s CPU architecture. This may be a \ref + //! PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*" value, or a member + //! of crashpad::MinidumpCPUArchitecture. + //! + //! In some cases, a system may be able to run processes of multiple specific + //! architecture types. For example, systems based on 64-bit architectures + //! such as x86_64 are often able to run 32-bit code of another architecture + //! in the same family, such as 32-bit x86. On these systems, this field will + //! identify the architecture of the process that the minidump file contains a + //! snapshot of. + uint16_t ProcessorArchitecture; + + //! \brief General CPU version information. + //! + //! The precise interpretation of this field is specific to each CPU + //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this + //! field contains the CPU family ID value from `cpuid 1` `eax`, adjusted to + //! take the extended family ID into account. + uint16_t ProcessorLevel; + + //! \brief Specific CPU version information. + //! + //! The precise interpretation of this field is specific to each CPU + //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this + //! field contains values obtained from `cpuid 1` `eax`: the high byte + //! contains the CPU model ID value adjusted to take the extended model ID + //! into account, and the low byte contains the CPU stepping ID value. + uint16_t ProcessorRevision; + + //! \brief The total number of CPUs present in the system. + uint8_t NumberOfProcessors; + + // The next 7 fields are from the OSVERSIONINFOEX structure returned by + // GetVersionEx(). + + //! \brief The system’s operating system type, which distinguishes between + //! “desktop” or “workstation” systems and “server” systems. This may be a + //! \ref VER_NT_x "VER_NT_*" value, or a member of + //! crashpad::MinidumpOSType. + uint8_t ProductType; + + //! \brief The system’s operating system version number’s first (major) + //! component. + //! + //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `6`. + //! - For Mac OS X 10.9.2, this would be `10`. + uint32_t MajorVersion; + + //! \brief The system’s operating system version number’s second (minor) + //! component. + //! + //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `1`. + //! - For Mac OS X 10.9.2, this would be `9`. + uint32_t MinorVersion; + + //! \brief The system’s operating system version number’s third (build or + //! patch) component. + //! + //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `7601`. + //! - For Mac OS X 10.9.2, this would be `2`. + uint32_t BuildNumber; + + //! \brief The system’s operating system family. This may be a \ref + //! VER_PLATFORM_x "VER_PLATFORM_*" value, or a member of + //! crashpad::MinidumpOS. + uint32_t PlatformId; + + //! \brief ::RVA of a MINIDUMP_STRING containing operating system-specific + //! version information. + //! + //! This field further identifies an operating system version beyond its + //! version number fields. Historically, “CSD” stands for “corrective service + //! diskette.” + //! + //! - On Windows, this is the name of the installed operating system service + //! pack, such as “Service Pack 1”. If no service pack is installed, this + //! field references an empty string. + //! - On Mac OS X, this is the operating system build number from `sw_vers + //! -buildVersion`. For Mac OS X 10.9.2 on most hardware types, this would + //! be `13C64`. + //! - On Linux and other Unix-like systems, this is the kernel version from + //! `uname -srvm`, possibly with additional information appended. On + //! Android, the `ro.build.fingerprint` system property is appended. + RVA CSDVersionRva; + + //! \brief A bitfield identifying products installed on the system. This is + //! composed of \ref VER_SUITE_x "VER_SUITE_*" values. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint16_t SuiteMask; + + uint16_t Reserved2; + + //! \brief Information about the system’s CPUs. + //! + //! This field is a union. Which of its members should be expressed is + //! controlled by the #ProcessorArchitecture field. If it is set to + //! crashpad::kMinidumpCPUArchitectureX86, the CPU_INFORMATION::X86CpuInfo + //! field is expressed. Otherwise, the CPU_INFORMATION::OtherCpuInfo field is + //! expressed. + //! + //! \note Older Breakpad implementations produce minidump files that express + //! CPU_INFORMATION::X86CpuInfo when #ProcessorArchitecture is set to + //! crashpad::kMinidumpCPUArchitectureAMD64. Minidump files produced by + //! `dbghelp.dll` on Windows express CPU_INFORMATION::OtherCpuInfo in this + //! case. + CPU_INFORMATION Cpu; +}; + +//! \brief Information about a specific thread within the process. +//! +//! \sa MINIDUMP_THREAD_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD { + //! \brief The thread’s ID. This may be referenced by + //! MINIDUMP_EXCEPTION_STREAM::ThreadId. + uint32_t ThreadId; + + //! \brief The thread’s suspend count. + //! + //! This field will be `0` if the thread is schedulable (not suspended). + uint32_t SuspendCount; + + //! \brief The thread’s priority class. + //! + //! On Windows, this is a `*_PRIORITY_CLASS` value. `NORMAL_PRIORITY_CLASS` + //! has value `0x20`; higher priority classes have higher values. + uint32_t PriorityClass; + + //! \brief The thread’s priority level. + //! + //! On Windows, this is a `THREAD_PRIORITY_*` value. `THREAD_PRIORITY_NORMAL` + //! has value `0`; higher priorities have higher values, and lower priorities + //! have lower (negative) values. + uint32_t Priority; + + //! \brief The address of the thread’s thread environment block in the address + //! space of the process that the minidump file contains a snapshot of. + //! + //! The thread environment block contains thread-local data. + //! + //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST + //! stream containing the thread-local data pointed to by this field. + uint64_t Teb; + + //! \brief A snapshot of the thread’s stack. + //! + //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST + //! stream containing a pointer to the same memory range referenced by this + //! field. + MINIDUMP_MEMORY_DESCRIPTOR Stack; + + //! \brief A pointer to a CPU-specific CONTEXT structure containing the + //! thread’s context at the time the snapshot was taken. + //! + //! If the minidump file was generated as a result of an exception taken on + //! this thread, this field may identify a different context than the + //! exception context. For these minidump files, a MINIDUMP_EXCEPTION_STREAM + //! stream will be present, and the context contained within that stream will + //! be the exception context. + //! + //! The interpretation of the context structure is dependent on the CPU + //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + //! For crashpad::kMinidumpCPUArchitectureX86, this will be + //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64, + //! this will be crashpad::MinidumpContextAMD64. + MINIDUMP_LOCATION_DESCRIPTOR ThreadContext; +}; + +//! \brief Information about all threads within the process. +struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_LIST { + //! \brief The number of threads present in the #Threads array. + uint32_t NumberOfThreads; + + //! \brief Structures identifying each thread within the process. + MINIDUMP_THREAD Threads[0]; +}; + +//! \brief Information about an exception that occurred in the process. +struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION { + //! \brief The top-level exception code identifying the exception, in + //! operating system-specific values. + //! + //! For Mac OS X minidumps, this will be an \ref EXC_x "EXC_*" exception type, + //! such as `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions + //! processed as `EXC_CRASH` when generated from another preceding exception: + //! the original exception code will appear instead. The exception type as it + //! was received will appear at index 0 of #ExceptionInformation. + //! + //! For Windows minidumps, this will be an \ref EXCEPTION_x "EXCEPTION_*" + //! exception type, such as `EXCEPTION_ACCESS_VIOLATION`. + //! + //! \note This field is named ExceptionCode, but what is known as the + //! “exception code” on Mac OS X/Mach is actually stored in the + //! #ExceptionFlags field of a minidump file. + //! + //! \todo Document the possible values by OS. There may be OS-specific enums + //! in minidump_extensions.h. + uint32_t ExceptionCode; + + //! \brief Additional exception flags that further identify the exception, in + //! operating system-specific values. + //! + //! For Mac OS X minidumps, this will be the value of the exception code at + //! index 0 as received by a Mach exception handler, except: + //! * For exception type `EXC_CRASH` generated from another preceding + //! exception, the original exception code will appear here, not the code + //! as received by the Mach exception handler. + //! * For exception types `EXC_RESOURCE` and `EXC_GUARD`, the high 32 bits of + //! the code received by the Mach exception handler will appear here. + //! + //! In all cases for Mac OS X minidumps, the code as it was received by the + //! Mach exception handler will appear at index 1 of #ExceptionInformation. + //! + //! For Windows minidumps, this will either be `0` if the exception is + //! continuable, or `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable + //! exception. + //! + //! \todo Document the possible values by OS. There may be OS-specific enums + //! in minidump_extensions.h. + uint32_t ExceptionFlags; + + //! \brief An address, in the address space of the process that this minidump + //! file contains a snapshot of, of another MINIDUMP_EXCEPTION. This field + //! is used for nested exceptions. + uint64_t ExceptionRecord; + + //! \brief The address that caused the exception. + //! + //! This may be the address that caused a fault on data access, or it may be + //! the instruction pointer that contained an offending instruction. + uint64_t ExceptionAddress; + + //! \brief The number of valid elements in #ExceptionInformation. + uint32_t NumberParameters; + + uint32_t __unusedAlignment; + + //! \brief Additional information about the exception, specific to the + //! operating system and possibly the #ExceptionCode. + //! + //! For Mac OS X minidumps, this will contain the exception type as received + //! by a Mach exception handler and the values of the `codes[0]` and + //! `codes[1]` (exception code and subcode) parameters supplied to the Mach + //! exception handler. Unlike #ExceptionCode and #ExceptionFlags, the values + //! received by a Mach exception handler are used directly here even for the + //! `EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD` exception types. + + //! For Windows, these are additional arguments (if any) as provided to + //! `RaiseException()`. + uint64_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; +}; + +//! \brief Information about the exception that triggered a minidump file’s +//! generation. +struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION_STREAM { + //! \brief The ID of the thread that caused the exception. + //! + //! \sa MINIDUMP_THREAD::ThreadId + uint32_t ThreadId; + + uint32_t __alignment; + + //! \brief Information about the exception. + MINIDUMP_EXCEPTION ExceptionRecord; + + //! \brief A pointer to a CPU-specific CONTEXT structure containing the + //! thread’s context at the time the exception was caused. + //! + //! The interpretation of the context structure is dependent on the CPU + //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + //! For crashpad::kMinidumpCPUArchitectureX86, this will be + //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64, + //! this will be crashpad::MinidumpContextAMD64. + MINIDUMP_LOCATION_DESCRIPTOR ThreadContext; +}; + +//! \brief Information about a specific module loaded within the process at the +//! time the snapshot was taken. +//! +//! A module may be the main executable, a shared library, or a loadable module. +//! +//! \sa MINIDUMP_MODULE_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE { + //! \brief The base address of the loaded module in the address space of the + //! process that the minidump file contains a snapshot of. + uint64_t BaseOfImage; + + //! \brief The size of the loaded module. + uint32_t SizeOfImage; + + //! \brief The loaded module’s checksum, or `0` if unknown. + //! + //! On Windows, this field comes from the `CheckSum` field of the module’s + //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at + //! the time the module was linked. + uint32_t CheckSum; + + //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX + //! epoch. + //! + //! On Windows, this field comes from the `TimeDateStamp` field of the + //! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time + //! the module was linked. + uint32_t TimeDateStamp; + + //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file + //! name. + RVA ModuleNameRva; + + //! \brief The module’s version information. + VS_FIXEDFILEINFO VersionInfo; + + //! \brief A pointer to the module’s CodeView record, typically a link to its + //! debugging information in crashpad::CodeViewRecordPDB70 format. + //! + //! The specific format of the CodeView record is indicated by its signature, + //! the first 32-bit value in the structure. For links to debugging + //! information in contemporary usage, this is normally a + //! crashpad::CodeViewRecordPDB70 structure, but may be a + //! crashpad::CodeViewRecordPDB20 structure instead. These structures identify + //! a link to debugging data within a `.pdb` (Program Database) file. See <a + //! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching + //! Debug Information</a>, PDB Files. + //! + //! On Windows, it is also possible for the CodeView record to contain + //! debugging information itself, as opposed to a link to a `.pdb` file. See + //! <a + //! href="http://pierrelib.pagesperso-orange.fr/exec_formats/MS_Symbol_Type_v1.0.pdf#page=71">Microsoft + //! Symbol and Type Information</a>, section 7.2, “Debug Information Format” + //! for a list of debug information formats, and <a + //! href="http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf#page=63">Undocumented + //! Windows 2000 Secrets</a>, Windows 2000 Debugging Support/Microsoft Symbol + //! File Internals/CodeView Subsections for an in-depth description of the + //! CodeView 4.1 format. Signatures seen in the wild include “NB09” + //! (0x3930424e) for CodeView 4.1 and “NB11” (0x3131424e) for CodeView 5.0. + //! This form of debugging information within the module, as opposed to a link + //! to an external `.pdb` file, is chosen by building with `/Z7` in Visual + //! Studio 6.0 (1998) and earlier. This embedded form of debugging information + //! is now considered obsolete. + //! + //! On Windows, the CodeView record is taken from a module’s + //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value + //! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in + //! crashpad::CodeViewRecordPDB70 format are generated by Visual Studio .NET + //! (2002) (version 7.0) and later. + //! + //! When the CodeView record is not present, the fields of this + //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`. + MINIDUMP_LOCATION_DESCRIPTOR CvRecord; + + //! \brief A pointer to the module’s miscellaneous debugging record, a + //! structure of type IMAGE_DEBUG_MISC. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. It is largely obsolete on Windows, where it was used to link to + //! debugging information stored in a `.dbg` file. `.dbg` files have been + //! superseded by `.pdb` files. + //! + //! On Windows, the miscellaneous debugging record is taken from module’s + //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value + //! IMAGE_DEBUG_TYPE_MISC (`4`), if any. + //! + //! When the miscellaneous debugging record is not present, the fields of this + //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`. + //! + //! \sa #CvRecord + MINIDUMP_LOCATION_DESCRIPTOR MiscRecord; + + uint64_t Reserved0; + uint64_t Reserved1; +}; + +//! \brief Information about all modules loaded within the process at the time +//! the snapshot was taken. +struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE_LIST { + //! \brief The number of modules present in the #Modules array. + uint32_t NumberOfModules; + + //! \brief Structures identifying each module present in the minidump file. + MINIDUMP_MODULE Modules[0]; +}; + +//! \brief Information about memory regions within the process. +//! +//! Typically, a minidump file will not contain a snapshot of a process’ entire +//! memory image. For minidump files identified as ::MiniDumpNormal in +//! MINIDUMP_HEADER::Flags, memory regions are limited to those referenced by +//! MINIDUMP_THREAD::Stack fields, and a small number of others possibly related +//! to the exception that triggered the snapshot to be taken. +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_LIST { + //! \brief The number of memory regions present in the #MemoryRanges array. + uint32_t NumberOfMemoryRanges; + + //! \brief Structures identifying each memory region present in the minidump + //! file. + MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[0]; +}; + +//! \brief Contains the state of an individual system handle at the time the +//! snapshot was taken. This structure is Windows-specific. +//! +//! \sa MINIDUMP_HANDLE_DESCRIPTOR_2 +struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR { + //! \brief The Windows `HANDLE` value. + uint64_t Handle; + + //! \brief An RVA to a MINIDUMP_STRING structure that specifies the object + //! type of the handle. This member can be zero. + RVA TypeNameRva; + + //! \brief An RVA to a MINIDUMP_STRING structure that specifies the object + //! name of the handle. This member can be zero. + RVA ObjectNameRva; + + //! \brief The attributes for the handle, this corresponds to `OBJ_INHERIT`, + //! `OBJ_CASE_INSENSITIVE`, etc. + uint32_t Attributes; + + //! \brief The `ACCESS_MASK` for the handle. + uint32_t GrantedAccess; + + //! \brief This is the number of open handles to the object that this handle + //! refers to. + uint32_t HandleCount; + + //! \brief This is the number kernel references to the object that this + //! handle refers to. + uint32_t PointerCount; +}; + +//! \brief Contains the state of an individual system handle at the time the +//! snapshot was taken. This structure is Windows-specific. +//! +//! \sa MINIDUMP_HANDLE_DESCRIPTOR +struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2 + : public MINIDUMP_HANDLE_DESCRIPTOR { + //! \brief An RVA to a MINIDUMP_HANDLE_OBJECT_INFORMATION structure that + //! specifies object-specific information. This member can be zero if + //! there is no extra information. + RVA ObjectInfoRva; + + //! \brief Must be zero. + uint32_t Reserved0; +}; + +//! \brief Represents the header for a handle data stream. +struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM { + //! \brief The size of the header information for the stream, in bytes. This + //! value is `sizeof(MINIDUMP_HANDLE_DATA_STREAM)`. + uint32_t SizeOfHeader; + + //! \brief The size of a descriptor in the stream, in bytes. This value is + //! `sizeof(MINIDUMP_HANDLE_DESCRIPTOR)` or + //! `sizeof(MINIDUMP_HANDLE_DESCRIPTOR_2)`. + uint32_t SizeOfDescriptor; + + //! \brief The number of descriptors in the stream. + uint32_t NumberOfDescriptors; + + //! \brief Must be zero. + uint32_t Reserved; +}; + +//! \anchor MINIDUMP_MISCx +//! \name MINIDUMP_MISC* +//! +//! \brief Field validity flag values for MINIDUMP_MISC_INFO::Flags1. +//! \{ + +//! \brief MINIDUMP_MISC_INFO::ProcessId is valid. +#define MINIDUMP_MISC1_PROCESS_ID 0x00000001 + +//! \brief The time-related fields in MINIDUMP_MISC_INFO are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO::ProcessCreateTime +//! - MINIDUMP_MISC_INFO::ProcessUserTime +//! - MINIDUMP_MISC_INFO::ProcessKernelTime +#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002 + +//! \brief The CPU-related fields in MINIDUMP_MISC_INFO_2 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_2::ProcessorMaxMhz +//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentMhz +//! - MINIDUMP_MISC_INFO_2::ProcessorMhzLimit +//! - MINIDUMP_MISC_INFO_2::ProcessorMaxIdleState +//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentIdleState +//! +//! \note This macro should likely have been named +//! MINIDUMP_MISC2_PROCESSOR_POWER_INFO. +#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004 + +//! \brief MINIDUMP_MISC_INFO_3::ProcessIntegrityLevel is valid. +#define MINIDUMP_MISC3_PROCESS_INTEGRITY 0x00000010 + +//! \brief MINIDUMP_MISC_INFO_3::ProcessExecuteFlags is valid. +#define MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS 0x00000020 + +//! \brief The time zone-related fields in MINIDUMP_MISC_INFO_3 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_3::TimeZoneId +//! - MINIDUMP_MISC_INFO_3::TimeZone +#define MINIDUMP_MISC3_TIMEZONE 0x00000040 + +//! \brief MINIDUMP_MISC_INFO_3::ProtectedProcess is valid. +#define MINIDUMP_MISC3_PROTECTED_PROCESS 0x00000080 + +//! \brief The build string-related fields in MINIDUMP_MISC_INFO_4 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_4::BuildString +//! - MINIDUMP_MISC_INFO_4::DbgBldStr +#define MINIDUMP_MISC4_BUILDSTRING 0x00000100 +//! \} + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO { + //! \brief The size of the structure. + //! + //! This field can be used to distinguish between different versions of this + //! structure: MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, + //! and MINIDUMP_MISC_INFO_4. + //! + //! \sa Flags1 + uint32_t SizeOfInfo; + + //! \brief A bit field of \ref MINIDUMP_MISCx "MINIDUMP_MISC*" values + //! indicating which fields of this structure contain valid data. + uint32_t Flags1; + + //! \brief The process ID of the process. + uint32_t ProcessId; + + //! \brief The time that the process started, in `time_t` units, seconds since + //! the POSIX epoch. + uint32_t ProcessCreateTime; + + //! \brief The amount of user-mode CPU time used by the process, in seconds, + //! at the time of the snapshot. + uint32_t ProcessUserTime; + + //! \brief The amount of system-mode (kernel) CPU time used by the process, in + //! seconds, at the time of the snapshot. + uint32_t ProcessKernelTime; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows Vista (NT 6.0) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2 + : public MINIDUMP_MISC_INFO { + //! \brief The maximum clock rate of the system’s CPU or CPUs, in MHz. + uint32_t ProcessorMaxMhz; + + //! \brief The clock rate of the system’s CPU or CPUs, in MHz, at the time of + //! the snapshot. + uint32_t ProcessorCurrentMhz; + + //! \brief The maximum clock rate of the system’s CPU or CPUs, in MHz, reduced + //! by any thermal limitations, at the time of the snapshot. + uint32_t ProcessorMhzLimit; + + //! \brief The maximum idle state of the system’s CPU or CPUs. + uint32_t ProcessorMaxIdleState; + + //! \brief The idle state of the system’s CPU or CPUs at the time of the + //! snapshot. + uint32_t ProcessorCurrentIdleState; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 7 (NT 6.1) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3 + : public MINIDUMP_MISC_INFO_2 { + //! \brief The process’ integrity level. + //! + //! Windows typically uses `SECURITY_MANDATORY_MEDIUM_RID` (0x2000) for + //! processes belonging to normal authenticated users and + //! `SECURITY_MANDATORY_HIGH_RID` (0x3000) for elevated processes. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProcessIntegrityLevel; + + //! \brief The process’ execute flags. + //! + //! On Windows, this appears to be returned by `NtQueryInformationProcess()` + //! with an argument of `ProcessExecuteFlags` (34). + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProcessExecuteFlags; + + //! \brief Whether the process is protected. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProtectedProcess; + + //! \brief Whether daylight saving time was being observed in the system’s + //! location at the time of the snapshot. + //! + //! This field can contain the following values: + //! - `0` if the location does not observe daylight saving time at all. The + //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the + //! time zone name. + //! - `1` if the location observes daylight saving time, but standard time + //! was in effect at the time of the snapshot. The + //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the + //! time zone name. + //! - `2` if the location observes daylight saving time, and it was in effect + //! at the time of the snapshot. The TIME_ZONE_INFORMATION::DaylightName + //! field of #TimeZoneId contains the time zone name. + //! + //! \sa #TimeZone + uint32_t TimeZoneId; + + //! \brief Information about the time zone at the system’s location. + //! + //! \sa #TimeZoneId + TIME_ZONE_INFORMATION TimeZone; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 8 (NT 6.2) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4 + : public MINIDUMP_MISC_INFO_3 { + //! \brief The operating system’s “build string”, a string identifying a + //! specific build of the operating system. + //! + //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit. + //! + //! On Windows 8.1 (NT 6.3), this is “6.3.9600.17031 + //! (winblue_gdr.140221-1952)”. + base::char16 BuildString[260]; + + //! \brief The minidump producer’s “build string”, a string identifying the + //! module that produced a minidump file. + //! + //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit. + //! + //! On Windows 8.1 (NT 6.3), this may be “dbghelp.i386,6.3.9600.16520” or + //! “dbghelp.amd64,6.3.9600.16520” depending on CPU architecture. + base::char16 DbgBldStr[40]; +}; + +//! \brief The latest known version of the MINIDUMP_MISC_INFO structure. +typedef MINIDUMP_MISC_INFO_4 MINIDUMP_MISC_INFO_N; + +//! \brief Describes a region of memory. +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO { + //! \brief The base address of the region of pages. + uint64_t BaseAddress; + + //! \brief The base address of a range of pages in this region. The page is + //! contained within this memory region. + uint64_t AllocationBase; + + //! \brief The memory protection when the region was initially allocated. This + //! member can be one of the memory protection options (such as + //! \ref PAGE_x PAGE_EXECUTE, \ref PAGE_x PAGE_NOACCESS, etc.), along with + //! \ref PAGE_x PAGE_GUARD or \ref PAGE_x PAGE_NOCACHE, as needed. + uint32_t AllocationProtect; + + uint32_t __alignment1; + + //! \brief The size of the region beginning at the base address in which all + //! pages have identical attributes, in bytes. + uint64_t RegionSize; + + //! \brief The state of the pages in the region. This can be one of + //! \ref MEM_x MEM_COMMIT, \ref MEM_x MEM_FREE, or \ref MEM_x MEM_RESERVE. + uint32_t State; + + //! \brief The access protection of the pages in the region. This member is + //! one of the values listed for the #AllocationProtect member. + uint32_t Protect; + + //! \brief The type of pages in the region. This can be one of \ref MEM_x + //! MEM_IMAGE, \ref MEM_x MEM_MAPPED, or \ref MEM_x MEM_PRIVATE. + uint32_t Type; + + uint32_t __alignment2; +}; + +//! \brief Contains a list of memory regions. +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO_LIST { + //! \brief The size of the header data for the stream, in bytes. This is + //! generally sizeof(MINIDUMP_MEMORY_INFO_LIST). + uint32_t SizeOfHeader; + + //! \brief The size of each entry following the header, in bytes. This is + //! generally sizeof(MINIDUMP_MEMORY_INFO). + uint32_t SizeOfEntry; + + //! \brief The number of entries in the stream. These are generally + //! MINIDUMP_MEMORY_INFO structures. The entries follow the header. + uint64_t NumberOfEntries; +}; + +//! \brief Minidump file type values for MINIDUMP_HEADER::Flags. These bits +//! describe the types of data carried within a minidump file. +enum MINIDUMP_TYPE { + //! \brief A minidump file without any additional data. + //! + //! This type of minidump file contains: + //! - A MINIDUMP_SYSTEM_INFO stream. + //! - A MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or + //! MINIDUMP_MISC_INFO_4 stream, depending on which fields are present. + //! - A MINIDUMP_THREAD_LIST stream. All threads are present, along with a + //! snapshot of each thread’s stack memory sufficient to obtain backtraces. + //! - If the minidump file was generated as a result of an exception, a + //! MINIDUMP_EXCEPTION_STREAM describing the exception. + //! - A MINIDUMP_MODULE_LIST stream. All loaded modules are present. + //! - Typically, a MINIDUMP_MEMORY_LIST stream containing duplicate pointers + //! to the stack memory regions also referenced by the MINIDUMP_THREAD_LIST + //! stream. This type of minidump file also includes a + //! MINIDUMP_MEMORY_DESCRIPTOR containing the 256 bytes centered around + //! the exception address or the instruction pointer. + MiniDumpNormal = 0x00000000, +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_
diff --git a/third_party/crashpad/crashpad/compat/non_win/minwinbase.h b/third_party/crashpad/crashpad/compat/non_win/minwinbase.h new file mode 100644 index 0000000..2761b1c --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_win/minwinbase.h
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_ +#define CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_ + +#include <stdint.h> + +//! \brief Represents a date and time. +struct SYSTEMTIME { + //! \brief The year, represented fully. + //! + //! The year 2014 would be represented in this field as `2014`. + uint16_t wYear; + + //! \brief The month of the year, `1` for January and `12` for December. + uint16_t wMonth; + + //! \brief The day of the week, `0` for Sunday and `6` for Saturday. + uint16_t wDayOfWeek; + + //! \brief The day of the month, `1` through `31`. + uint16_t wDay; + + //! \brief The hour of the day, `0` through `23`. + uint16_t wHour; + + //! \brief The minute of the hour, `0` through `59`. + uint16_t wMinute; + + //! \brief The second of the minute, `0` through `60`. + uint16_t wSecond; + + //! \brief The millisecond of the second, `0` through `999`. + uint16_t wMilliseconds; +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_
diff --git a/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h b/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h new file mode 100644 index 0000000..59efae28 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h
@@ -0,0 +1,61 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_ +#define CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_ + +#include <stdint.h> + +#include "base/strings/string16.h" +#include "compat/non_win/minwinbase.h" + +//! \brief Information about a time zone and its daylight saving rules. +struct TIME_ZONE_INFORMATION { + //! \brief The number of minutes west of UTC. + int32_t Bias; + + //! \brief The UTF-16-encoded name of the time zone when observing standard + //! time. + base::char16 StandardName[32]; + + //! \brief The date and time to switch from daylight saving time to standard + //! time. + //! + //! This can be a specific time, or with SYSTEMTIME::wYear set to `0`, it can + //! reflect an annual recurring transition. In that case, SYSTEMTIME::wDay in + //! the range `1` to `5` is interpreted as the given occurrence of + //! SYSTEMTIME::wDayOfWeek within the month, `1` being the first occurrence + //! and `5` being the last (even if there are fewer than 5). + SYSTEMTIME StandardDate; + + //! \brief The bias relative to #Bias to be applied when observing standard + //! time. + int32_t StandardBias; + + //! \brief The UTF-16-encoded name of the time zone when observing daylight + //! saving time. + base::char16 DaylightName[32]; + + //! \brief The date and time to switch from standard time to daylight saving + //! time. + //! + //! This field is specified in the same manner as #StandardDate. + SYSTEMTIME DaylightDate; + + //! \brief The bias relative to #Bias to be applied when observing daylight + //! saving time. + int32_t DaylightBias; +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_
diff --git a/third_party/crashpad/crashpad/compat/non_win/verrsrc.h b/third_party/crashpad/crashpad/compat/non_win/verrsrc.h new file mode 100644 index 0000000..70f53442 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_win/verrsrc.h
@@ -0,0 +1,184 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_ +#define CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_ + +#include <stdint.h> + +//! \file + +//! \brief The magic number for a VS_FIXEDFILEINFO structure, stored in +//! VS_FIXEDFILEINFO::dwSignature. +#define VS_FFI_SIGNATURE 0xfeef04bd + +//! \brief The version of a VS_FIXEDFILEINFO structure, stored in +//! VS_FIXEDFILEINFO::dwStrucVersion. +#define VS_FFI_STRUCVERSION 0x00010000 + +//! \anchor VS_FF_x +//! \name VS_FF_* +//! +//! \brief File attribute values for VS_FIXEDFILEINFO::dwFileFlags and +//! VS_FIXEDFILEINFO::dwFileFlagsMask. +//! \{ +#define VS_FF_DEBUG 0x00000001 +#define VS_FF_PRERELEASE 0x00000002 +#define VS_FF_PATCHED 0x00000004 +#define VS_FF_PRIVATEBUILD 0x00000008 +#define VS_FF_INFOINFERRED 0x00000010 +#define VS_FF_SPECIALBUILD 0x00000020 +//! \} + +//! \anchor VOS_x +//! \name VOS_* +//! +//! \brief Operating system values for VS_FIXEDFILEINFO::dwFileOS. +//! \{ +#define VOS_UNKNOWN 0x00000000 +#define VOS_DOS 0x00010000 +#define VOS_OS216 0x00020000 +#define VOS_OS232 0x00030000 +#define VOS_NT 0x00040000 +#define VOS_WINCE 0x00050000 +#define VOS__BASE 0x00000000 +#define VOS__WINDOWS16 0x00000001 +#define VOS__PM16 0x00000002 +#define VOS__PM32 0x00000003 +#define VOS__WINDOWS32 0x00000004 +#define VOS_DOS_WINDOWS16 0x00010001 +#define VOS_DOS_WINDOWS32 0x00010004 +#define VOS_OS216_PM16 0x00020002 +#define VOS_OS232_PM32 0x00030003 +#define VOS_NT_WINDOWS32 0x00040004 +//! \} + +//! \anchor VFT_x +//! \name VFT_* +//! +//! \brief File type values for VS_FIXEDFILEINFO::dwFileType. +//! \{ +#define VFT_UNKNOWN 0x00000000 +#define VFT_APP 0x00000001 +#define VFT_DLL 0x00000002 +#define VFT_DRV 0x00000003 +#define VFT_FONT 0x00000004 +#define VFT_VXD 0x00000005 +#define VFT_STATIC_LIB 0x00000007 +//! \} + +//! \anchor VFT2_x +//! \name VFT2_* +//! +//! \brief File subtype values for VS_FIXEDFILEINFO::dwFileSubtype. +//! \{ +#define VFT2_UNKNOWN 0x00000000 +#define VFT2_DRV_PRINTER 0x00000001 +#define VFT2_DRV_KEYBOARD 0x00000002 +#define VFT2_DRV_LANGUAGE 0x00000003 +#define VFT2_DRV_DISPLAY 0x00000004 +#define VFT2_DRV_MOUSE 0x00000005 +#define VFT2_DRV_NETWORK 0x00000006 +#define VFT2_DRV_SYSTEM 0x00000007 +#define VFT2_DRV_INSTALLABLE 0x00000008 +#define VFT2_DRV_SOUND 0x00000009 +#define VFT2_DRV_COMM 0x0000000A +#define VFT2_DRV_INPUTMETHOD 0x0000000B +#define VFT2_DRV_VERSIONED_PRINTER 0x0000000C +#define VFT2_FONT_RASTER 0x00000001 +#define VFT2_FONT_VECTOR 0x00000002 +#define VFT2_FONT_TRUETYPE 0x00000003 +//! \} + +//! \brief Version information for a file. +//! +//! On Windows, this information is derived from a file’s version information +//! resource, and is obtained by calling `VerQueryValue()` with an `lpSubBlock` +//! argument of `"\"` (a single backslash). +struct VS_FIXEDFILEINFO { + //! \brief The structure’s magic number, ::VS_FFI_SIGNATURE. + uint32_t dwSignature; + + //! \brief The structure’s version, ::VS_FFI_STRUCVERSION. + uint32_t dwStrucVersion; + + //! \brief The more-significant portion of the file’s version number. + //! + //! This field contains the first two components of a four-component version + //! number. For a file whose version is 1.2.3.4, this field would be + //! `0x00010002`. + //! + //! \sa dwFileVersionLS + uint32_t dwFileVersionMS; + + //! \brief The less-significant portion of the file’s version number. + //! + //! This field contains the last two components of a four-component version + //! number. For a file whose version is 1.2.3.4, this field would be + //! `0x00030004`. + //! + //! \sa dwFileVersionMS + uint32_t dwFileVersionLS; + + //! \brief The more-significant portion of the product’s version number. + //! + //! This field contains the first two components of a four-component version + //! number. For a product whose version is 1.2.3.4, this field would be + //! `0x00010002`. + //! + //! \sa dwProductVersionLS + uint32_t dwProductVersionMS; + + //! \brief The less-significant portion of the product’s version number. + //! + //! This field contains the last two components of a four-component version + //! number. For a product whose version is 1.2.3.4, this field would be + //! `0x00030004`. + //! + //! \sa dwProductVersionMS + uint32_t dwProductVersionLS; + + //! \brief A bitmask of \ref VS_FF_x "VS_FF_*" values indicating which bits in + //! #dwFileFlags are valid. + uint32_t dwFileFlagsMask; + + //! \brief A bitmask of \ref VS_FF_x "VS_FF_*" values identifying attributes + //! of the file. Only bits present in #dwFileFlagsMask are valid. + uint32_t dwFileFlags; + + //! \brief The file’s intended operating system, a value of \ref VOS_x + //! "VOS_*". + uint32_t dwFileOS; + + //! \brief The file’s type, a value of \ref VFT_x "VFT_*". + uint32_t dwFileType; + + //! \brief The file’s subtype, a value of \ref VFT2_x "VFT2_*" corresponding + //! to its #dwFileType, if the file type has subtypes. + uint32_t dwFileSubtype; + + //! \brief The more-significant portion of the file’s creation date. + //! + //! The intended encoding of this field is unknown. This field is unused and + //! always has the value `0`. + uint32_t dwFileDateMS; + + //! \brief The less-significant portion of the file’s creation date. + //! + //! The intended encoding of this field is unknown. This field is unused and + //! always has the value `0`. + uint32_t dwFileDateLS; +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_
diff --git a/third_party/crashpad/crashpad/compat/non_win/windows.h b/third_party/crashpad/crashpad/compat/non_win/windows.h new file mode 100644 index 0000000..45774514 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_win/windows.h
@@ -0,0 +1,17 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +// dbghelp.h on Windows requires inclusion of windows.h before it. To avoid +// cluttering all inclusions of dbghelp.h with #ifdefs, always include windows.h +// and have an empty one on non-Windows platforms.
diff --git a/third_party/crashpad/crashpad/compat/non_win/winnt.h b/third_party/crashpad/crashpad/compat/non_win/winnt.h new file mode 100644 index 0000000..03167aa5 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/non_win/winnt.h
@@ -0,0 +1,217 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_WINNT_H_ +#define CRASHPAD_COMPAT_NON_WIN_WINNT_H_ + +//! \file + +//! \anchor VER_SUITE_x +//! \name VER_SUITE_* +//! +//! \brief Installable product values for MINIDUMP_SYSTEM_INFO::SuiteMask. +//! \{ +#define VER_SUITE_SMALLBUSINESS 0x0001 +#define VER_SUITE_ENTERPRISE 0x0002 +#define VER_SUITE_BACKOFFICE 0x0004 +#define VER_SUITE_COMMUNICATIONS 0x0008 +#define VER_SUITE_TERMINAL 0x0010 +#define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x0020 +#define VER_SUITE_EMBEDDEDNT 0x0040 +#define VER_SUITE_DATACENTER 0x0080 +#define VER_SUITE_SINGLEUSERTS 0x0100 +#define VER_SUITE_PERSONAL 0x0200 +#define VER_SUITE_BLADE 0x0400 +#define VER_SUITE_EMBEDDED_RESTRICTED 0x0800 +#define VER_SUITE_SECURITY_APPLIANCE 0x1000 +#define VER_SUITE_STORAGE_SERVER 0x2000 +#define VER_SUITE_COMPUTE_SERVER 0x4000 +#define VER_SUITE_WH_SERVER 0x8000 +//! \} + +//! \brief The maximum number of exception parameters present in the +//! MINIDUMP_EXCEPTION::ExceptionInformation array. +#define EXCEPTION_MAXIMUM_PARAMETERS 15 + +//! \anchor PROCESSOR_ARCHITECTURE_x +//! \name PROCESSOR_ARCHITECTURE_* +//! +//! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. +//! +//! \sa crashpad::MinidumpCPUArchitecture +//! \{ +#define PROCESSOR_ARCHITECTURE_INTEL 0 +#define PROCESSOR_ARCHITECTURE_MIPS 1 +#define PROCESSOR_ARCHITECTURE_ALPHA 2 +#define PROCESSOR_ARCHITECTURE_PPC 3 +#define PROCESSOR_ARCHITECTURE_SHX 4 +#define PROCESSOR_ARCHITECTURE_ARM 5 +#define PROCESSOR_ARCHITECTURE_IA64 6 +#define PROCESSOR_ARCHITECTURE_ALPHA64 7 +#define PROCESSOR_ARCHITECTURE_MSIL 8 +#define PROCESSOR_ARCHITECTURE_AMD64 9 +#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 +#define PROCESSOR_ARCHITECTURE_NEUTRAL 11 +#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff +//! \} + +//! \anchor PF_x +//! \name PF_* +//! +//! \brief CPU feature values for \ref CPU_INFORMATION::ProcessorFeatures +//! "CPU_INFORMATION::OtherCpuInfo::ProcessorFeatures". +//! +//! \{ +#define PF_FLOATING_POINT_PRECISION_ERRATA 0 +#define PF_FLOATING_POINT_EMULATED 1 +#define PF_COMPARE_EXCHANGE_DOUBLE 2 +#define PF_MMX_INSTRUCTIONS_AVAILABLE 3 +#define PF_PPC_MOVEMEM_64BIT_OK 4 +#define PF_ALPHA_BYTE_INSTRUCTIONS 5 +#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6 +#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7 +#define PF_RDTSC_INSTRUCTION_AVAILABLE 8 +#define PF_PAE_ENABLED 9 +#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10 +#define PF_SSE_DAZ_MODE_AVAILABLE 11 +#define PF_NX_ENABLED 12 +#define PF_SSE3_INSTRUCTIONS_AVAILABLE 13 +#define PF_COMPARE_EXCHANGE128 14 +#define PF_COMPARE64_EXCHANGE128 15 +#define PF_CHANNELS_ENABLED 16 +#define PF_XSAVE_ENABLED 17 +#define PF_ARM_VFP_32_REGISTERS_AVAILABLE 18 +#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19 +#define PF_SECOND_LEVEL_ADDRESS_TRANSLATION 20 +#define PF_VIRT_FIRMWARE_ENABLED 21 +#define PF_RDWRFSGSBASE_AVAILABLE 22 +#define PF_FASTFAIL_AVAILABLE 23 +#define PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE 24 +#define PF_ARM_64BIT_LOADSTORE_ATOMIC 25 +#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 26 +#define PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE 27 +#define PF_RDRAND_INSTRUCTION_AVAILABLE 28 +//! \} + +//! \anchor IMAGE_DEBUG_MISC_x +//! \name IMAGE_DEBUG_MISC_* +//! +//! Data type values for IMAGE_DEBUG_MISC::DataType. +//! \{ + +//! \brief A pointer to a `.dbg` file. +//! +//! IMAGE_DEBUG_MISC::Data will contain the path or file name of the `.dbg` file +//! associated with the module. +#define IMAGE_DEBUG_MISC_EXENAME 1 + +//! \} + +//! \brief Miscellaneous debugging record. +//! +//! This structure is referenced by MINIDUMP_MODULE::MiscRecord. It is obsolete, +//! superseded by the CodeView record. +struct IMAGE_DEBUG_MISC { + //! \brief The type of data carried in the #Data field. + //! + //! This is a value of \ref IMAGE_DEBUG_MISC_x "IMAGE_DEBUG_MISC_*". + uint32_t DataType; + + //! \brief The length of this structure in bytes, including the entire #Data + //! field and its `NUL` terminator. + //! + //! \note The Windows documentation states that this field is rounded up to + //! nearest nearest 4-byte multiple. + uint32_t Length; + + //! \brief The encoding of the #Data field. + //! + //! If this field is `0`, #Data contains narrow or multibyte character data. + //! If this field is `1`, #Data is UTF-16-encoded. + //! + //! On Windows, with this field set to `0`, #Data will be encoded in the code + //! page of the system that linked the module. On other operating systems, + //! UTF-8 may be used. + uint8_t Unicode; + + uint8_t Reserved[3]; + + //! \brief The data carried within this structure. + //! + //! For string data, this field will be `NUL`-terminated. If #Unicode is `1`, + //! this field is UTF-16-encoded, and will be terminated by a UTF-16 `NUL` + //! code unit (two `NUL` bytes). + uint8_t Data[1]; +}; + +//! \anchor VER_NT_x +//! \name VER_NT_* +//! +//! \brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType. +//! +//! \sa crashpad::MinidumpOSType +//! \{ +#define VER_NT_WORKSTATION 1 +#define VER_NT_DOMAIN_CONTROLLER 2 +#define VER_NT_SERVER 3 +//! \} + +//! \anchor VER_PLATFORM_x +//! \name VER_PLATFORM_* +//! +//! \brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId. +//! +//! \sa crashpad::MinidumpOS +//! \{ +#define VER_PLATFORM_WIN32s 0 +#define VER_PLATFORM_WIN32_WINDOWS 1 +#define VER_PLATFORM_WIN32_NT 2 +//! \} + +//! \anchor PAGE_x +//! \name PAGE_* +//! +//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and +//! MINIDUMP_MEMORY_INFO::AllocationProtect. +//! \{ +#define PAGE_NOACCESS 0x1 +#define PAGE_READONLY 0x2 +#define PAGE_READWRITE 0x4 +#define PAGE_WRITECOPY 0x8 +#define PAGE_EXECUTE 0x10 +#define PAGE_EXECUTE_READ 0x20 +#define PAGE_EXECUTE_READWRITE 0x40 +#define PAGE_EXECUTE_WRITECOPY 0x80 +#define PAGE_GUARD 0x100 +#define PAGE_NOCACHE 0x200 +#define PAGE_WRITECOMBINE 0x400 +//! \} + +//! \anchor MEM_x +//! \name MEM_* +//! +//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and +//! MINIDUMP_MEMORY_INFO::Type. +//! \{ +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 +#define MEM_FREE 0x10000 +#define MEM_PRIVATE 0x20000 +#define MEM_MAPPED 0x40000 +#define MEM_RESET 0x80000 +//! \} + +#endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_
diff --git a/third_party/crashpad/crashpad/compat/win/getopt.h b/third_party/crashpad/crashpad/compat/win/getopt.h new file mode 100644 index 0000000..45fcbcc --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/getopt.h
@@ -0,0 +1,20 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_GETOPT_H_ +#define CRASHPAD_COMPAT_WIN_GETOPT_H_ + +#include "third_party/getopt/getopt.h" + +#endif // CRASHPAD_COMPAT_WIN_GETOPT_H_
diff --git a/third_party/crashpad/crashpad/compat/win/strings.cc b/third_party/crashpad/crashpad/compat/win/strings.cc new file mode 100644 index 0000000..6af256e --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/strings.cc
@@ -0,0 +1,25 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <strings.h> + +#include <string.h> + +extern "C" { + +int strcasecmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} + +} // extern "C"
diff --git a/third_party/crashpad/crashpad/compat/win/strings.h b/third_party/crashpad/crashpad/compat/win/strings.h new file mode 100644 index 0000000..50360b16 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/strings.h
@@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_STRINGS_H_ +#define CRASHPAD_COMPAT_WIN_STRINGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int strcasecmp(const char* s1, const char* s2); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CRASHPAD_COMPAT_WIN_STRINGS_H_
diff --git a/third_party/crashpad/crashpad/compat/win/sys/time.h b/third_party/crashpad/crashpad/compat/win/sys/time.h new file mode 100644 index 0000000..46bdef2 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/sys/time.h
@@ -0,0 +1,23 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_SYS_TIME_H_ +#define CRASHPAD_COMPAT_WIN_SYS_TIME_H_ + +struct timeval { + long tv_sec; + long tv_usec; +}; + +#endif // CRASHPAD_COMPAT_WIN_SYS_TIME_H_
diff --git a/third_party/crashpad/crashpad/compat/win/sys/types.h b/third_party/crashpad/crashpad/compat/win/sys/types.h new file mode 100644 index 0000000..5355bcf7 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/sys/types.h
@@ -0,0 +1,29 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_SYS_TYPES_H_ +#define CRASHPAD_COMPAT_WIN_SYS_TYPES_H_ + +// This is intended to be roughly equivalent to #include_next. +#if _MSC_VER < 1900 +#include <../include/sys/types.h> +#else +#include <../ucrt/sys/types.h> +#endif + +#include <stdint.h> + +typedef unsigned int pid_t; + +#endif // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
diff --git a/third_party/crashpad/crashpad/compat/win/time.cc b/third_party/crashpad/crashpad/compat/win/time.cc new file mode 100644 index 0000000..e673887 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/time.cc
@@ -0,0 +1,40 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <time.h> + +extern "C" { + +struct tm* gmtime_r(const time_t* timep, struct tm* result) { + if (gmtime_s(result, timep) != 0) + return nullptr; + return result; +} + +struct tm* localtime_r(const time_t* timep, struct tm* result) { + if (localtime_s(result, timep) != 0) + return nullptr; + return result; +} + +const char* strptime(const char* buf, const char* format, struct tm* tm) { + // TODO(scottmg): strptime implementation. + return nullptr; +} + +time_t timegm(struct tm* tm) { + return _mkgmtime(tm); +} + +} // extern "C"
diff --git a/third_party/crashpad/crashpad/compat/win/time.h b/third_party/crashpad/crashpad/compat/win/time.h new file mode 100644 index 0000000..913b469 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/time.h
@@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_TIME_H_ +#define CRASHPAD_COMPAT_WIN_TIME_H_ + +// This is intended to be roughly equivalent to #include_next. +#if _MSC_VER < 1900 +#include <../include/time.h> +#else +#include <../ucrt/time.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct tm* gmtime_r(const time_t* timep, struct tm* result); + +struct tm* localtime_r(const time_t* timep, struct tm* result); + +const char* strptime(const char* buf, const char* format, struct tm* tm); + +time_t timegm(struct tm* tm); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CRASHPAD_COMPAT_WIN_TIME_H_
diff --git a/third_party/crashpad/crashpad/compat/win/winnt.h b/third_party/crashpad/crashpad/compat/win/winnt.h new file mode 100644 index 0000000..2c5ac66 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/winnt.h
@@ -0,0 +1,32 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_WINNT_H_ +#define CRASHPAD_COMPAT_WIN_WINNT_H_ + +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa373184.aspx: +// "Note that this structure definition was accidentally omitted from WinNT.h." +struct PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +}; + +// include_next <winnt.h> +#include <../um/winnt.h> + +#endif // CRASHPAD_COMPAT_WIN_WINNT_H_
diff --git a/third_party/crashpad/crashpad/crashpad.gyp b/third_party/crashpad/crashpad/crashpad.gyp new file mode 100644 index 0000000..fd2a2691 --- /dev/null +++ b/third_party/crashpad/crashpad/crashpad.gyp
@@ -0,0 +1,42 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'targets': [ + { + 'target_name': 'All', + 'type': 'none', + 'suppress_wildcard': 1, + 'dependencies': [ + 'client/client.gyp:*', + 'client/client_test.gyp:*', + 'compat/compat.gyp:*', + 'handler/handler.gyp:*', + 'minidump/minidump.gyp:*', + 'minidump/minidump_test.gyp:*', + 'snapshot/snapshot.gyp:*', + 'snapshot/snapshot_test.gyp:*', + 'test/test.gyp:*', + 'test/test_test.gyp:*', + 'tools/tools.gyp:*', + 'util/util.gyp:*', + 'util/util_test.gyp:*', + ], + 'sources': [ + 'doc/support/crashpad.doxy.h', + 'package.h', + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/doc/.gitignore b/third_party/crashpad/crashpad/doc/.gitignore new file mode 100644 index 0000000..e324eac9 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/.gitignore
@@ -0,0 +1 @@ +/generated
diff --git a/third_party/crashpad/crashpad/doc/appengine/README b/third_party/crashpad/crashpad/doc/appengine/README new file mode 100644 index 0000000..00a7f76 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/appengine/README
@@ -0,0 +1,39 @@ +This is the App Engine app that serves https://crashpad.chromium.org/. + +To work on this app, obtain the App Engine SDK for Go from +https://cloud.google.com/appengine/downloads. Unpacking it produces a +go_appengine directory. This may be added to your $PATH for convenience, +although it is not necessary. + +The commands in this README are expected to be run from the directory containing +app.yaml. + +The App Engine SDK for Go provides App Engine packages at the “appengine” import +path, but not the newer “google.golang.org/appengine” path. The Crashpad app +uses the newer paths. See +https://github.com/golang/appengine#2-update-import-paths and +https://code.google.com/p/googleappengine/issues/detail?id=11670. To make these +available, obtain a Go release from https://golang.org/dl/, and run: + +$ GOROOT=…/go_appengine/goroot GOPATH=…/go_appengine/gopath go get -d + +To test locally: + +$ goapp serve + +Look for the “Starting module "default" running at: http://localhost:8080” line, +which tells you the URL of the local running instance of the app. + +To deploy: + +$ version=$(git rev-parse --short=12 HEAD) +$ [[ -n "$(git status --porcelain)" ]] && version+=-dirty +$ goapp deploy -version "${version}" + +Note that app.yaml does not name a “version” to encourage you to use a git hash +as the version, as above. + +Activate a newly-deployed version by visiting the App Engine console at +https://appengine.google.com/deployment?&app_id=s~crashpad-home, selecting it, +and choosing “Make Default”. It is also possible to delete old versions from +this page when they are no longer needed.
diff --git a/third_party/crashpad/crashpad/doc/appengine/app.yaml b/third_party/crashpad/crashpad/doc/appengine/app.yaml new file mode 100644 index 0000000..3d3fbdff --- /dev/null +++ b/third_party/crashpad/crashpad/doc/appengine/app.yaml
@@ -0,0 +1,8 @@ +application: crashpad-home +runtime: go +api_version: go1 + +handlers: +- url: /.* + script: _go_app + secure: always
diff --git a/third_party/crashpad/crashpad/doc/appengine/main.go b/third_party/crashpad/crashpad/doc/appengine/main.go new file mode 100644 index 0000000..e05870f5 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/appengine/main.go
@@ -0,0 +1,136 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +// Package crashpad mirrors crashpad documentation from Chromium’s git repo. +package crashpad + +import ( + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "path" + "strings" + "time" + + "google.golang.org/appengine" + "google.golang.org/appengine/memcache" + "google.golang.org/appengine/urlfetch" +) + +func init() { + http.HandleFunc("/", handler) +} + +func handler(w http.ResponseWriter, r *http.Request) { + const ( + baseURL = "https://chromium.googlesource.com/crashpad/crashpad/+/doc/doc/generated/?format=TEXT" + bugBaseURL = "https://bugs.chromium.org/p/crashpad/" + ) + + ctx := appengine.NewContext(r) + client := urlfetch.Client(ctx) + + // Don’t show dotfiles. + if strings.HasPrefix(path.Base(r.URL.Path), ".") { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + + if r.URL.Path == "/bug" || r.URL.Path == "/bug/" { + http.Redirect(w, r, bugBaseURL, http.StatusFound) + return + } else if r.URL.Path == "/bug/new" { + http.Redirect(w, r, bugBaseURL+"issues/entry", http.StatusFound) + return + } else if strings.HasPrefix(r.URL.Path, "/bug/") { + http.Redirect(w, r, bugBaseURL+"issues/detail?id="+r.URL.Path[5:], http.StatusFound) + return + } + + u, err := url.Parse(baseURL) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + u.Path = path.Join(u.Path, r.URL.Path) + urlStr := u.String() + + item, err := memcache.Get(ctx, urlStr) + if err == memcache.ErrCacheMiss { + resp, err := client.Get(urlStr) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + w.WriteHeader(resp.StatusCode) + for k, v := range w.Header() { + w.Header()[k] = v + } + io.Copy(w, resp.Body) + return + } + + // Redirect directories to their index pages (/doc/ -> /doc/index.html). + if resp.Header.Get("X-Gitiles-Object-Type") == "tree" { + http.Redirect(w, r, path.Join(r.URL.Path, "/index.html"), http.StatusFound) + return + } + + decoder := base64.NewDecoder(base64.StdEncoding, resp.Body) + b, err := ioutil.ReadAll(decoder) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + item = &memcache.Item{ + Key: urlStr, + Value: b, + Expiration: 1 * time.Hour, + } + if err := memcache.Set(ctx, item); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } else if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", contentType(path.Base(u.Path))) + fmt.Fprintf(w, "%s", item.Value) +} + +// contentType returns the appropriate content type header for file. +func contentType(file string) string { + contentTypes := map[string]string{ + ".html": "text/html; charset=UTF-8", + ".css": "text/css; charset=UTF-8", + ".js": "text/javascript; charset=UTF-8", + ".png": "image/png", + ".ico": "image/x-icon", + } + for suffix, typ := range contentTypes { + if strings.HasSuffix(file, suffix) { + return typ + } + } + return "text/plain; charset=UTF-8" +}
diff --git a/third_party/crashpad/crashpad/doc/developing.ad b/third_party/crashpad/crashpad/doc/developing.ad new file mode 100644 index 0000000..9174f6b1 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/developing.ad
@@ -0,0 +1,216 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: article + += Developing Crashpad + +== Status + +link:status.html[Project status] information has moved to its own page. + +== Introduction + +Crashpad is a https://dev.chromium.org/Home[Chromium project]. Most of +its development practices follow Chromium’s. In order to function on its +own in other projects, Crashpad uses +https://chromium.googlesource.com/chromium/mini_chromium/[mini_chromium], +a small, self-contained library that provides many of Chromium’s useful +low-level base routines. +https://chromium.googlesource.com/chromium/mini_chromium/+/master/README[mini_chromium’s +README] provides more detail. + +== Prerequisites + +To develop Crashpad, the following tools are necessary, and must be +present in the `$PATH` environment variable: + + * Chromium’s + https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. + * http://git-scm.com/[Git]. This is provided by Xcode on Mac OS X and by + depot_tools on Windows. + * https://www.python.org/[Python]. This is provided by the operating system on + Mac OS X, and by depot_tools on Windows. + * Appropriate development tools. For Mac OS X, this is + https://developer.apple.com/xcode/[Xcode], and for Windows, it’s + https://www.visualstudio.com/[Visual Studio]. + +== Getting the Source Code + +The main source code repository is a Git repository hosted at +https://chromium.googlesource.com/crashpad/crashpad. Although it is possible to +check out this repository directly with `git clone`, Crashpad’s dependencies are +managed by +https://dev.chromium.org/developers/how-tos/depottools#TOC-gclient[`gclient`] +instead of Git submodules, so to work on Crashpad, it is best to use `fetch` to +get the source code. + +`fetch` and `gclient` are part of the +https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no +need to install them separately. + +=== Initial Checkout + +[subs="verbatim,quotes"] +---- +$ *mkdir \~/crashpad* +$ *cd ~/crashpad* +$ *fetch crashpad* +---- + +`fetch crashpad` performs the initial `gclient sync`, establishing a +fully-functional local checkout. + +=== Subsequent Checkouts + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *git pull -r* +$ *gclient sync* +---- + +== Building + +Crashpad uses https://gyp.gsrc.io/[GYP] to generate +https://martine.github.io/ninja/[Ninja] build files. The build is described by +`.gyp` files throughout the Crashpad source code tree. The +`build/gyp_crashpad.py` script runs GYP properly for Crashpad, and is also +called when you run `fetch crashpad`, `gclient sync`, or `gclient runhooks`. + +The Ninja build files and build output are in the `out` directory. Both debug- +and release-mode configurations are available. The examples below show the debug +configuration. To build and test the release configuration, substitute `Release` +for `Debug`. + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *ninja -C out/Debug* +---- + +Ninja is part of the +https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no +need to install it separately. + +== Testing + +Crashpad uses https://github.com/google/googletest/[Google Test] as its +unit-testing framework, and some tests use +https://github.com/google/googletest/tree/master/googlemock/[Google Mock] as +well. Its tests are currently split up into several test executables, each +dedicated to testing a different component. This may change in the future. After +a successful build, the test executables will be found at +`out/Debug/crashpad_*_test`. + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *out/Debug/crashpad_minidump_test* +$ *out/Debug/crashpad_util_test* +---- + +A script is provided to run all of Crashpad’s tests. It accepts a single +argument that tells it which configuration to test. + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *python build/run_tests.py Debug* +---- + +== Contributing + +Crashpad’s contribution process is very similar to +https://dev.chromium.org/developers/contributing-code[Chromium’s contribution +process]. + +=== Code Review + +A code review must be conducted for every change to Crashpad’s source code. Code +review is conducted on https://codereview.chromium.org/[Chromium’s Rietveld] +system, and all code reviews must be sent to an appropriate reviewer, with a Cc +sent to +https://groups.google.com/a/chromium.org/group/crashpad-dev[crashpad-dev]. The +`codereview.settings` file specifies this environment to `git-cl`. + +`git-cl` is part of the +https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no +need to install it separately. + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *git checkout -b work_branch origin/master* +_…do some work…_ +$ *git add …* +$ *git commit* +$ *git cl upload* +---- + +Uploading a patch to Rietveld does not automatically request a review. You must +select a reviewer and mail your request to them (with a Cc to crashpad-dev) from +the Rietveld issue page after running `git cl upload`. If you have lost track of +the issue page, `git cl issue` will remind you of its URL. Alternatively, you +can request review when uploading to Rietveld by using `git cl upload +--send-mail` + +Git branches maintain their association with Rietveld issues, so if you need to +make changes based on review feedback, you can do so on the correct Git branch, +committing your changes locally with `git commit`. You can then upload a new +patch set with `git cl upload` and let your reviewer know you’ve addressed the +feedback. + +=== Landing Changes + +After code review is complete and “LGTM” (“looks good to me”) has been received +from all reviewers, project members can commit the patch themselves: + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *git checkout work_branch* +$ *git cl land* +---- + +Crashpad does not currently have a +https://dev.chromium.org/developers/testing/commit-queue[commit queue], so +contributors that are not project members will have to ask a project member to +commit the patch for them. Project members can commit changes on behalf of +external contributors by patching the change into a local branch and landing it: + +[subs="verbatim,quotes"] +---- +$ *cd ~/crashpad/crashpad* +$ *git checkout -b for_external_contributor origin/master* +$ *git cl patch 12345678* _# 12345678 is the Rietveld issue number_ +$ *git cl land -c \'External Contributor <external@contributor.org>'* +---- + +=== External Contributions + +Copyright holders must complete the +https://developers.google.com/open-source/cla/individual[Individual Contributor +License Agreement] or +https://developers.google.com/open-source/cla/corporate[Corporate Contributor +License Agreement] as appropriate before any submission can be accepted, and +must be listed in the `AUTHORS` file. Contributors may be listed in the +`CONTRIBUTORS` file. + +== Buildbot + +The https://build.chromium.org/p/client.crashpad/[Crashpad Buildbot] performs +automated builds and tests of Crashpad. Before checking out or updating the +Crashpad source code, and after checking in a new change, it is prudent to check +the Buildbot to ensure that “the tree is green.”
diff --git a/third_party/crashpad/crashpad/doc/favicon.ico b/third_party/crashpad/crashpad/doc/favicon.ico new file mode 100644 index 0000000..23c553a2 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/favicon.ico Binary files differ
diff --git a/third_party/crashpad/crashpad/doc/index.ad b/third_party/crashpad/crashpad/doc/index.ad new file mode 100644 index 0000000..3b06292 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/index.ad
@@ -0,0 +1,42 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: article + += Crashpad + +https://crashpad.chromium.org/[Crashpad] is a crash-reporting system. + +== Documentation + + * link:status.html[Project status] + * link:developing.html[Developing Crashpad]: instructions for getting the + source code, building, testing, and contributing to the project. + * https://crashpad.chromium.org/doxygen/index.html[Crashpad interface + documentation] + * https://crashpad.chromium.org/man/index.html[Crashpad tool man pages] + +== Source Code + +Crashpad’s source code is hosted in a Git repository at +https://chromium.googlesource.com/crashpad/crashpad. + +== Other Links + + * Bugs can be reported at the https://crashpad.chromium.org/bug/[Crashpad issue + tracker]. + * The https://build.chromium.org/p/client.crashpad[Crashpad Buildbot] performs + automated builds and tests. + * https://groups.google.com/a/chromium.org/group/crashpad-dev[crashpad-dev] is + the Crashpad developers’ mailing list.
diff --git a/third_party/crashpad/crashpad/doc/status.ad b/third_party/crashpad/crashpad/doc/status.ad new file mode 100644 index 0000000..e980b9d --- /dev/null +++ b/third_party/crashpad/crashpad/doc/status.ad
@@ -0,0 +1,44 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: article + += Project Status + +== Completed + +Crashpad currently consists of a crash-reporting client and some related tools +for Mac OS X. The core client work for Mac OS X is substantially complete. +Crashpad has become the crash reporter client for +https://dev.chromium.org/Home[Chromium] on Mac OS X as of +https://chromium.googlesource.com/chromium/src/+/d413b2dcb54d523811d386f1ff4084f677a6d089[March +2015]. + +== In Progress + +Crashpad is actively being bootstrapped on +https://crashpad.chromium.org/bug/1[Windows]. All major components are now +working on Windows, and integration into Chromium is expected shortly. + +Initial work on a Crashpad client for +https://crashpad.chromium.org/bug/30[Android] has begun. This is currently in +the design phase. + +== Future + +There are plans to bring Crashpad clients to other operating systems in the +future, including a more generic non-Android Linux implementation. There are +also plans to implement a https://crashpad.chromium.org/bug/29[crash report +processor] as part of Crashpad. No timeline for completing this work has been +set yet.
diff --git a/third_party/crashpad/crashpad/doc/support/asciidoc.conf b/third_party/crashpad/crashpad/doc/support/asciidoc.conf new file mode 100644 index 0000000..a4ab2c4 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/asciidoc.conf
@@ -0,0 +1,58 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +[miscellaneous] +# AsciiDoc uses \r\n by default. +newline=\n + +# The default AsciiDoc lang-en.conf uses docdate and doctime for the +# last-updated line in footer-text. These attributes are taken from the file’s +# mtime and cannot be overridden. For a git-based project, the date of the last +# revision is desirable, so change this to use git_date, an attribute that can +# be computed and passed in by the script that runs AsciiDoc. For man pages, use +# the mansource and manversion attributes instead of the hard-coded “Version” +# string and revnumber attribute, so that the version will appear as “Crashpad +# 0.7.0” as it does in “man” output. +ifdef::basebackend-html[] +[footer-text] +ifdef::doctype-manpage[] +{mansource=Version} {manversion={revnumber}}{basebackend-xhtml11?<br />}{basebackend-xhtml11=<br>} +endif::doctype-manpage[] +ifndef::doctype-manpage[] +Version {revnumber}{basebackend-xhtml11?<br />}{basebackend-xhtml11=<br>} +endif::doctype-manpage[] +Last updated {git_date} +endif::basebackend-html[] + +# The man_link macro was inspired by git’s linkgit macro. See +# https://github.com/git/git/blob/master/Documentation/asciidoc.conf. +ifdef::doctype-manpage[] + +[macros] +(?su)[\\]?(?P<name>man_link):(?P<target>\S*?)\[(?P<attrlist>.*?)\]= + +ifdef::backend-docbook[] +[man_link-inlinemacro] +{0%{target}} +{0#<citerefentry>} +{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>} +{0#</citerefentry>} +endif::backend-docbook[] + +ifdef::basebackend-html[] +[man_link-inlinemacro] +<a href="{target}.html">{target}{0?({0})}</a> +endif::basebackend-html[] + +endif::doctype-manpage[]
diff --git a/third_party/crashpad/crashpad/doc/support/asciidoc.css b/third_party/crashpad/crashpad/doc/support/asciidoc.css new file mode 100644 index 0000000..9808c216 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/asciidoc.css
@@ -0,0 +1,20 @@ +/* Copyright 2015 The Crashpad Authors. All rights reserved. + * + * 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. */ + +/* The default AsciiDoc asciidoc.css specifies fuchsia as the visited link + * color. This has a dated appearance. Replace it with blue, the same color used + * for unvisited links. */ +a:visited { + color: blue; +}
diff --git a/third_party/crashpad/crashpad/doc/support/compat.sh b/third_party/crashpad/crashpad/doc/support/compat.sh new file mode 100644 index 0000000..8c5d30d --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/compat.sh
@@ -0,0 +1,37 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +if [[ "${BASH_SOURCE[0]}" = "${0}" ]]; then + echo "${0}: this file must be sourced, not run directly" >& 2 + exit 1 +fi + +# Some extensions of command-line tools behave differently on different systems. +# $sed_ext should be a sed invocation that enables extended regular expressions. +# $date_time_t should be a date invocation that causes it to print the date and +# time corresponding to a time_t string that immediately follows it. +case "$(uname -s)" in + Darwin) + sed_ext="sed -E" + date_time_t="date -r" + ;; + Linux) + sed_ext="sed -r" + date_time_t="date -d@" + ;; + *) + echo "${0}: unknown operating system" >& 2 + exit 1 + ;; +esac
diff --git a/third_party/crashpad/crashpad/doc/support/crashpad.doxy b/third_party/crashpad/crashpad/doc/support/crashpad.doxy new file mode 100644 index 0000000..276a7945 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/crashpad.doxy
@@ -0,0 +1,2372 @@ +# Doxyfile 1.8.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Crashpad" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = out/doc/doxygen + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = . \ + compat/mac \ + compat/non_win + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = YES + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.h \ + *.m \ + *.mm + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = third_party + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = out* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 0 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. To get the times font for +# instance you can specify +# EXTRA_PACKAGES=times +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = ALIGNAS(x)= \ + DOXYGEN \ + MSVC_POP_WARNING()= \ + MSVC_PUSH_DISABLE_WARNING(x)= \ + __attribute__(x)= + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif and svg. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES
diff --git a/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h b/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h new file mode 100644 index 0000000..5bfefed --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h
@@ -0,0 +1,24 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#error This file is not intended to be #included. + +//! \namespace crashpad +//! \brief The main namespace. + +//! \namespace crashpad::internal +//! \brief The internal namespace, not for public use. + +//! \namespace crashpad::test +//! \brief The testing namespace, for use in test code only.
diff --git a/third_party/crashpad/crashpad/doc/support/generate.sh b/third_party/crashpad/crashpad/doc/support/generate.sh new file mode 100755 index 0000000..0a2be484 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/generate.sh
@@ -0,0 +1,79 @@ +#!/bin/bash + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +set -e + +function maybe_mkdir() { + local dir="${1}" + if [[ ! -d "${dir}" ]]; then + mkdir "${dir}" + fi +} + +# Run from the Crashpad project root directory. +cd "$(dirname "${0}")/../.." + +source doc/support/compat.sh + +doc/support/generate_doxygen.sh +doc/support/generate_asciidoc.sh + +output_dir=doc/generated +maybe_mkdir "${output_dir}" + +for subdir in doc doxygen man ; do + output_subdir="${output_dir}/${subdir}" + maybe_mkdir "${output_subdir}" + rsync -Ilr --delete --exclude .git "out/doc/${subdir}/html/" \ + "${output_subdir}/" +done + +# Move doc/index.html to index.html, adjusting relative paths to other files in +# doc. +base_url=https://crashpad.chromium.org/ +${sed_ext} -e 's%<a href="([^/]+)\.html">%<a href="doc/\1.html">%g' \ + -e 's%<a href="'"${base_url}"'">%<a href="index.html">%g' \ + -e 's%<a href="'"${base_url}"'%<a href="%g' \ + < "${output_dir}/doc/index.html" > "${output_dir}/index.html" +rm "${output_dir}/doc/index.html" + +# Ensure a favicon exists at the root since the browser will always request it. +cp doc/favicon.ico "${output_dir}/" + +# Create man/index.html +cd "${output_dir}/man" +cat > index.html << __EOF__ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Crashpad Man Pages</title> +<ul> +__EOF__ + +for html_file in *.html; do + if [[ "${html_file}" = "index.html" ]]; then + continue + fi + basename=$(${sed_ext} -e 's/\.html$//' <<< "${html_file}") + cat >> index.html << __EOF__ + <li> + <a href="${html_file}">${basename}</a> + </li> +__EOF__ +done + +cat >> index.html << __EOF__ +</ul> +__EOF__
diff --git a/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh b/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh new file mode 100755 index 0000000..54a5734 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh
@@ -0,0 +1,111 @@ +#!/bin/bash + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +set -e + +# Generating AsciiDoc documentation requires AsciiDoc, +# http://www.methods.co.nz/asciidoc/. For “man” and PDF output, a DocBook +# toolchain including docbook-xml and docbook-xsl is also required. + +# Run from the Crashpad project root directory. +cd "$(dirname "${0}")/../.." + +source doc/support/compat.sh + +output_dir=out/doc + +rm -rf \ + "${output_dir}/doc" \ + "${output_dir}/man" +mkdir -p \ + "${output_dir}/doc/html" \ + "${output_dir}/man/html" \ + "${output_dir}/man/man" + +# Get the version from package.h. +version=$(${sed_ext} -n -e 's/^#define PACKAGE_VERSION "(.*)"$/\1/p' package.h) + +generate() { + input="$1" + type="$2" + + case "${type}" in + doc) + doctype="article" + ;; + man) + doctype="manpage" + ;; + *) + echo "${0}: unknown type ${type}" >& 2 + exit 1 + ;; + esac + + echo "${input}" + + base=$(${sed_ext} -e 's%^.*/([^/]+)\.ad$%\1%' <<< "${input}") + + # Get the last-modified date of $input according to Git, in UTC. + git_time_t="$(git log -1 --format=%at "${input}")" + git_date="$(LC_ALL=C ${date_time_t}"${git_time_t}" -u '+%B %-d, %Y')" + + # Create HTML output. + asciidoc \ + --attribute mansource=Crashpad \ + --attribute manversion="${version}" \ + --attribute manmanual="Crashpad Manual" \ + --attribute git_date="${git_date}" \ + --conf-file doc/support/asciidoc.conf \ + --doctype "${doctype}" \ + --backend html5 \ + --attribute stylesheet="${PWD}/doc/support/asciidoc.css" \ + --out-file "${output_dir}/${type}/html/${base}.html" \ + "${input}" + + if [[ "${type}" = "man" ]]; then + # Create “man” output. + # + # AsciiDoc 8.6.9 produces harmless incorrect warnings each time this is run: + # “a2x: WARNING: --destination-dir option is only applicable to HTML based + # outputs”. https://github.com/asciidoc/asciidoc/issues/44 + a2x \ + --attribute mansource=Crashpad \ + --attribute manversion="${version}" \ + --attribute manmanual="Crashpad Manual" \ + --attribute git_date="${git_date}" \ + --asciidoc-opts=--conf-file=doc/support/asciidoc.conf \ + --doctype "${doctype}" \ + --format manpage \ + --destination-dir "${output_dir}/${type}/man" \ + "${input}" + fi + + # For PDF output, use an a2x command like the one above, with these options: + # --format pdf --fop --destination-dir "${output_dir}/${type}/pdf" +} + +for input in \ + doc/*.ad; do + generate "${input}" "doc" +done + +for input in \ + handler/crashpad_handler.ad \ + tools/*.ad \ + tools/mac/*.ad; do + generate "${input}" "man" +done
diff --git a/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh b/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh new file mode 100755 index 0000000..ba565e7 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh
@@ -0,0 +1,29 @@ +#!/bin/sh + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +set -e + +# Generating Doxygen documentation requires Doxygen, http://www.doxygen.org/. + +# Run from the Crashpad project root directory. +cd "$(dirname "${0}")/../.." + +output_dir=out/doc/doxygen + +rm -rf "${output_dir}" +mkdir -p "${output_dir}" + +doxygen doc/support/crashpad.doxy
diff --git a/third_party/crashpad/crashpad/doc/support/generate_git.sh b/third_party/crashpad/crashpad/doc/support/generate_git.sh new file mode 100755 index 0000000..209c11b9 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/generate_git.sh
@@ -0,0 +1,85 @@ +#!/bin/bash + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +set -e + +# Run from the Crashpad project root directory. +cd "$(dirname "${0}")/../.." + +source doc/support/compat.sh + +basename="$(basename "${0}")" + +status="$(git status --porcelain)" +if [[ -n "${status}" ]]; then + echo "${basename}: the working directory must be clean" >& 2 + git status + exit 1 +fi + +# git symbolic-ref gives the current branch name, but fails for detached HEAD. +# In that case, git rev-parse will give the current hash. +original_branch="$(git symbolic-ref --short --quiet HEAD || git rev-parse HEAD)" + +local_branch="\ +$(${sed_ext} -e 's/(.*)\..*/\1/' <<< "${basename}").${$}.${RANDOM}" + +remote_name=origin +remote_master_branch_name=master +remote_master_branch="${remote_name}/${remote_master_branch_name}" +remote_doc_branch_name=doc +remote_doc_branch="${remote_name}/${remote_doc_branch_name}" + +git fetch + +git checkout -b "${local_branch}" "${remote_doc_branch}" + +dirty= + +function cleanup() { + if [[ "${dirty}" ]]; then + git reset --hard HEAD + git clean --force + fi + + git checkout "${original_branch}" + git branch -D "${local_branch}" +} + +trap cleanup EXIT + +master_hash=$(git rev-parse --short=12 "${remote_master_branch}") +git merge "${remote_master_branch}" \ + -m "Merge ${remote_master_branch_name} ${master_hash} into doc" + +dirty=y + +doc/support/generate.sh + +git add -A doc/generated + +count="$(git diff --staged --numstat | wc -l)" +if [[ $count -gt 0 ]]; then + git commit \ + -m "Update documentation to ${remote_master_branch_name} ${master_hash}" + dirty= + + git push "${remote_name}" "HEAD:${remote_doc_branch_name}" +else + dirty= +fi + +# cleanup will run on exit
diff --git a/third_party/crashpad/crashpad/doc/support/man_footer.ad b/third_party/crashpad/crashpad/doc/support/man_footer.ad new file mode 100644 index 0000000..146dfd1 --- /dev/null +++ b/third_party/crashpad/crashpad/doc/support/man_footer.ad
@@ -0,0 +1,40 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +== Resources + +Crashpad home page: https://crashpad.chromium.org/. + +Report bugs at https://crashpad.chromium.org/bug/new. + +== Copyright + +Copyright 2014 +https://chromium.googlesource.com/crashpad/crashpad/+/master/AUTHORS[The +Crashpad Authors]. + +== License + +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 + +[subs="macros"] + 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/crashpad/crashpad/handler/crash_report_upload_thread.cc b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc new file mode 100644 index 0000000..c5fdfaad --- /dev/null +++ b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
@@ -0,0 +1,381 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "handler/crash_report_upload_thread.h" + +#include <errno.h> +#include <time.h> + +#include <map> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/settings.h" +#include "snapshot/minidump/process_snapshot_minidump.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_reader.h" +#include "util/stdlib/move.h" +#include "util/misc/uuid.h" +#include "util/net/http_body.h" +#include "util/net/http_multipart_builder.h" +#include "util/net/http_transport.h" +#include "util/stdlib/map_insert.h" +#include "util/thread/thread.h" + +namespace crashpad { + +namespace { + +void InsertOrReplaceMapEntry(std::map<std::string, std::string>* map, + const std::string& key, + const std::string& value) { + std::string old_value; + if (!MapInsertOrReplace(map, key, value, &old_value)) { + LOG(WARNING) << "duplicate key " << key << ", discarding value " + << old_value; + } +} + +// Given a minidump file readable by |minidump_file_reader|, returns a map of +// key-value pairs to use as HTTP form parameters for upload to a Breakpad +// server. The map is built by combining the process simple annotations map with +// each module’s simple annotations map. In the case of duplicate keys, the map +// will retain the first value found for any key, and will log a warning about +// discarded values. Each module’s annotations vector is also examined and built +// into a single string value, with distinct elements separated by newlines, and +// stored at the key named “list_annotations”, which supersedes any other key +// found by that name. The client ID stored in the minidump is converted to +// a string and stored at the key named “guid”, which supersedes any other key +// found by that name. +// +// In the event of an error reading the minidump file, a message will be logged. +std::map<std::string, std::string> BreakpadHTTPFormParametersFromMinidump( + FileReader* minidump_file_reader) { + ProcessSnapshotMinidump minidump_process_snapshot; + if (!minidump_process_snapshot.Initialize(minidump_file_reader)) { + return std::map<std::string, std::string>(); + } + + std::map<std::string, std::string> parameters = + minidump_process_snapshot.AnnotationsSimpleMap(); + + std::string list_annotations; + for (const ModuleSnapshot* module : minidump_process_snapshot.Modules()) { + for (const auto& kv : module->AnnotationsSimpleMap()) { + if (!parameters.insert(kv).second) { + LOG(WARNING) << "duplicate key " << kv.first << ", discarding value " + << kv.second; + } + } + + for (std::string annotation : module->AnnotationsVector()) { + list_annotations.append(annotation); + list_annotations.append("\n"); + } + } + + if (!list_annotations.empty()) { + // Remove the final newline character. + list_annotations.resize(list_annotations.size() - 1); + + InsertOrReplaceMapEntry(¶meters, "list_annotations", list_annotations); + } + + UUID client_id; + minidump_process_snapshot.ClientID(&client_id); + InsertOrReplaceMapEntry(¶meters, "guid", client_id.ToString()); + + return parameters; +} + +// Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to +// false upon destruction unless disarmed by calling Fire() or Disarm(). Fire() +// triggers an immediate call. Armed upon construction. +class CallRecordUploadAttempt { + public: + CallRecordUploadAttempt(CrashReportDatabase* database, + const CrashReportDatabase::Report* report) + : database_(database), + report_(report) { + } + + ~CallRecordUploadAttempt() { + Fire(); + } + + void Fire() { + if (report_) { + database_->RecordUploadAttempt(report_, false, std::string()); + } + + Disarm(); + } + + void Disarm() { + report_ = nullptr; + } + + private: + CrashReportDatabase* database_; // weak + const CrashReportDatabase::Report* report_; // weak + + DISALLOW_COPY_AND_ASSIGN(CallRecordUploadAttempt); +}; + +} // namespace + +namespace internal { + +class CrashReportUploadHelperThread final : public Thread { + public: + explicit CrashReportUploadHelperThread(CrashReportUploadThread* self) + : self_(self) {} + ~CrashReportUploadHelperThread() override {} + + void ThreadMain() override { + self_->ThreadMain(); + } + + private: + CrashReportUploadThread* self_; + + DISALLOW_COPY_AND_ASSIGN(CrashReportUploadHelperThread); +}; + +} // namespace internal + +CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database, + const std::string& url) + : url_(url), + database_(database), + semaphore_(0), + thread_(), + running_(false) { +} + +CrashReportUploadThread::~CrashReportUploadThread() { + DCHECK(!running_); + DCHECK(!thread_); +} + +void CrashReportUploadThread::Start() { + DCHECK(!running_); + DCHECK(!thread_); + + running_ = true; + thread_.reset(new internal::CrashReportUploadHelperThread(this)); + thread_->Start(); +} + +void CrashReportUploadThread::Stop() { + DCHECK(running_); + DCHECK(thread_); + + if (!running_) { + return; + } + + running_ = false; + semaphore_.Signal(); + + thread_->Join(); + thread_.reset(); +} + +void CrashReportUploadThread::ReportPending() { + semaphore_.Signal(); +} + +void CrashReportUploadThread::ThreadMain() { + while (running_) { + ProcessPendingReports(); + + // Check for pending reports every 15 minutes, even in the absence of a + // signal from the handler thread. This allows for failed uploads to be + // retried periodically, and for pending reports written by other processes + // to be recognized. + semaphore_.TimedWait(15 * 60); + } +} + +void CrashReportUploadThread::ProcessPendingReports() { + std::vector<CrashReportDatabase::Report> reports; + if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) { + // The database is sick. It might be prudent to stop trying to poke it from + // this thread by abandoning the thread altogether. On the other hand, if + // the problem is transient, it might be possible to talk to it again on the + // next pass. For now, take the latter approach. + return; + } + + for (const CrashReportDatabase::Report& report : reports) { + ProcessPendingReport(report); + + // Respect Stop() being called after at least one attempt to process a + // report. + if (!running_) { + return; + } + } +} + +void CrashReportUploadThread::ProcessPendingReport( + const CrashReportDatabase::Report& report) { + Settings* const settings = database_->GetSettings(); + + bool uploads_enabled; + if (!settings->GetUploadsEnabled(&uploads_enabled) || + !uploads_enabled || + url_.empty()) { + // If the upload-enabled state can’t be determined, uploads are disabled, or + // there’s no URL to upload to, don’t attempt to upload the new report. + database_->SkipReportUpload(report.uuid); + return; + } + + // This currently implements very simplistic rate-limiting, compatible with + // the Breakpad client, where the strategy is to permit one upload attempt per + // hour, and retire reports that would exceed this limit or for which the + // upload fails on the first attempt. + // + // TODO(mark): Provide a proper rate-limiting strategy and allow for failed + // upload attempts to be retried. + time_t last_upload_attempt_time; + if (settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) { + time_t now = time(nullptr); + if (now >= last_upload_attempt_time) { + // If the most recent upload attempt occurred within the past hour, don’t + // attempt to upload the new report. If it happened longer ago, attempt to + // upload the report. + const int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour + if (now - last_upload_attempt_time < kUploadAttemptIntervalSeconds) { + database_->SkipReportUpload(report.uuid); + return; + } + } else { + // The most recent upload attempt purportedly occurred in the future. If + // it “happened” at least one day in the future, assume that the last + // upload attempt time is bogus, and attempt to upload the report. If the + // most recent upload time is in the future but within one day, accept it + // and don’t attempt to upload the report. + const int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day + if (last_upload_attempt_time - now < kBackwardsClockTolerance) { + database_->SkipReportUpload(report.uuid); + return; + } + } + } + + const CrashReportDatabase::Report* upload_report; + CrashReportDatabase::OperationStatus status = + database_->GetReportForUploading(report.uuid, &upload_report); + switch (status) { + case CrashReportDatabase::kNoError: + break; + + case CrashReportDatabase::kBusyError: + return; + + case CrashReportDatabase::kReportNotFound: + case CrashReportDatabase::kFileSystemError: + case CrashReportDatabase::kDatabaseError: + // In these cases, SkipReportUpload() might not work either, but it’s best + // to at least try to get the report out of the way. + database_->SkipReportUpload(report.uuid); + return; + } + + CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report); + + std::string response_body; + UploadResult upload_result = UploadReport(upload_report, &response_body); + switch (upload_result) { + case UploadResult::kSuccess: + call_record_upload_attempt.Disarm(); + database_->RecordUploadAttempt(upload_report, true, response_body); + break; + case UploadResult::kPermanentFailure: + case UploadResult::kRetry: + call_record_upload_attempt.Fire(); + + // TODO(mark): Deal with retries properly: don’t call SkipReportUplaod() + // if the result was kRetry and the report hasn’t already been retried + // too many times. + database_->SkipReportUpload(report.uuid); + break; + } +} + +CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( + const CrashReportDatabase::Report* report, + std::string* response_body) { + std::map<std::string, std::string> parameters; + + { + FileReader minidump_file_reader; + if (!minidump_file_reader.Open(report->file_path)) { + // If the minidump file can’t be opened, all hope is lost. + return UploadResult::kPermanentFailure; + } + + // If the minidump file could be opened, ignore any errors that might occur + // when attempting to interpret it. This may result in its being uploaded + // with few or no parameters, but as long as there’s a dump file, the server + // can decide what to do with it. + parameters = BreakpadHTTPFormParametersFromMinidump(&minidump_file_reader); + } + + HTTPMultipartBuilder http_multipart_builder; + + const char kMinidumpKey[] = "upload_file_minidump"; + + for (const auto& kv : parameters) { + if (kv.first == kMinidumpKey) { + LOG(WARNING) << "reserved key " << kv.first << ", discarding value " + << kv.second; + } else { + http_multipart_builder.SetFormData(kv.first, kv.second); + } + } + + http_multipart_builder.SetFileAttachment( + kMinidumpKey, +#if defined(OS_WIN) + base::UTF16ToUTF8(report->file_path.BaseName().value()), +#else + report->file_path.BaseName().value(), +#endif + report->file_path, + "application/octet-stream"); + + scoped_ptr<HTTPTransport> http_transport(HTTPTransport::Create()); + http_transport->SetURL(url_); + HTTPHeaders::value_type content_type = + http_multipart_builder.GetContentType(); + http_transport->SetHeader(content_type.first, content_type.second); + http_transport->SetBodyStream(http_multipart_builder.GetBodyStream()); + // TODO(mark): The timeout should be configurable by the client. + http_transport->SetTimeout(60.0); // 1 minute. + + if (!http_transport->ExecuteSynchronously(response_body)) { + return UploadResult::kRetry; + } + + return UploadResult::kSuccess; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h new file mode 100644 index 0000000..0372a83a --- /dev/null +++ b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
@@ -0,0 +1,150 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_ +#define CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_ + +#include "base/basictypes.h" + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "client/crash_report_database.h" +#include "util/synchronization/semaphore.h" + +namespace crashpad { + +namespace internal { +class CrashReportUploadHelperThread; +} // namespace internal + +//! \brief A thread that processes pending crash reports in a +//! CrashReportDatabase by uploading them or marking them as completed +//! without upload, as desired. +//! +//! A producer of crash reports should notify an object of this class that a new +//! report has been added to the database by calling ReportPending(). +//! +//! Independently of being triggered by ReportPending(), objects of this class +//! periodically examine the database for pending reports. This allows failed +//! upload attempts for reports left in the pending state to be retried. It also +//! catches reports that are added without a ReportPending() signal being +//! caught. This may happen if crash reports are added to the database by other +//! processes. +class CrashReportUploadThread { + public: + //! \brief Constructs a new object. + //! + //! \param[in] database The database to upload crash reports from. + //! \param[in] url The URL of the server to upload crash reports to. + CrashReportUploadThread(CrashReportDatabase* database, + const std::string& url); + ~CrashReportUploadThread(); + + //! \brief Starts a dedicated upload thread, which executes ThreadMain(). + //! + //! This method may only be be called on a newly-constructed object or after + //! a call to Stop(). + void Start(); + + //! \brief Stops the upload thread. + //! + //! The upload thread will terminate after completing whatever task it is + //! performing. If it is not performing any task, it will terminate + //! immediately. This method blocks while waiting for the upload thread to + //! terminate. + //! + //! This method must only be called after Start(). If Start() has been called, + //! this method must be called before destroying an object of this class. + //! + //! This method may be called from any thread other than the upload thread. + //! It is expected to only be called from the same thread that called Start(). + void Stop(); + + //! \brief Informs the upload thread that a new pending report has been added + //! to the database. + //! + //! This method may be called from any thread. + void ReportPending(); + + private: + friend internal::CrashReportUploadHelperThread; + + //! \brief The result code from UploadReport(). + enum class UploadResult { + //! \brief The crash report was uploaded successfully. + kSuccess, + + //! \brief The crash report upload failed in such a way that recovery is + //! impossible. + //! + //! No further upload attempts should be made for the report. + kPermanentFailure, + + //! \brief The crash report upload failed, but it might succeed again if + //! retried in the future. + //! + //! If the report has not already been retried too many times, the caller + //! may arrange to call UploadReport() for the report again in the future, + //! after a suitable delay. + kRetry, + }; + + //! \brief Calls ProcessPendingReports() in response to ReportPending() having + //! been called on any thread, as well as periodically on a timer. + void ThreadMain(); + + //! \brief Obtains all pending reports from the database, and calls + //! ProcessPendingReport() to process each one. + void ProcessPendingReports(); + + //! \brief Processes a single pending report from the database. + //! + //! \param[in] report The crash report to process. + //! + //! If report upload is enabled, this method attempts to upload \a report by + //! calling UplaodReport(). If the upload is successful, the report will be + //! marked as “completed” in the database. If the upload fails and more + //! retries are desired, the report’s upload-attempt count and + //! last-upload-attempt time will be updated in the database and it will + //! remain in the “pending” state. If the upload fails and no more retries are + //! desired, or report upload is disabled, it will be marked as “completed” in + //! the database without ever having been uploaded. + void ProcessPendingReport(const CrashReportDatabase::Report& report); + + //! \brief Attempts to upload a crash report. + //! + //! \param[in] report The report to upload. The caller is responsible for + //! calling CrashReportDatabase::GetReportForUploading() before calling + //! this method, and for calling + //! CrashReportDatabase::RecordUploadAttempt() after calling this method. + //! \param[out] response_body If the upload attempt is successful, this will + //! be set to the response body sent by the server. Breakpad-type servers + //! provide the crash ID assigned by the server in the response body. + //! + //! \return A member of UploadResult indicating the result of the upload + //! attempt. + UploadResult UploadReport(const CrashReportDatabase::Report* report, + std::string* response_body); + + std::string url_; + CrashReportDatabase* database_; // weak + Semaphore semaphore_; // TODO(mark): Use a condition variable instead? + scoped_ptr<internal::CrashReportUploadHelperThread> thread_; + bool running_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_
diff --git a/third_party/crashpad/crashpad/handler/crashpad_handler.ad b/third_party/crashpad/crashpad/handler/crashpad_handler.ad new file mode 100644 index 0000000..e7fd66b --- /dev/null +++ b/third_party/crashpad/crashpad/handler/crashpad_handler.ad
@@ -0,0 +1,164 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += crashpad_handler(8) + +== Name + +crashpad_handler - Crashpad’s exception handler server + +== Synopsis + +[verse] +*crashpad_handler* ['OPTION…'] + +== Description + +This program is Crashpad’s main exception-handling server. It is responsible for +catching exceptions, writing crash reports, and uploading them to a crash report +collection server. Uploads are disabled by default, and can only be enabled by a +Crashpad client using the Crashpad client library, typically in response to a +user requesting this behavior. + +On OS X, this server may be started by its initial client, in which case it +performs a handshake with this client via a pipe established by the client that +is inherited by the server, referenced by the *--handshake-fd* argument. During +the handshake, the server furnishes the client with a send right that the client +may use as an exception port. The server retains the corresponding receive +right, which it monitors for exception messages. When the receive right loses +all senders, the server exits after allowing any upload in progress to complete. + +Alternatively, on OS X, this server may be started from launchd(8), where it +receives the Mach service name in a *--mach-service* argument. It checks in with +the bootstrap server under this service name, and clients may look it up with +the bootstrap server under this service name. It monitors this service for +exception messages. Upon receipt of +SIGTERM+, the server exits after allowing +any upload in progress to complete. +SIGTERM+ is normally sent by launchd(8) +when it determines that the server should exit. + +On Windows, clients register with this server by communicating with it via the +named pipe identified by the *--pipe-name* argument. Alternatively, the server +can create a new pipe with a random name and inform a client of this name via +the *--handshake-handle* mechanism; clients may then register by communicating +with it via that named pipe. During registration, a client provides the server +with an OS event object that it will signal should it crash. The server obtains +the client’s process handle and waits on the crash event object for a crash, as +well as the client’s process handle for the client to exit cleanly without +crashing. When a server started via the *--handshake-handle* mechanism loses all +of its clients, it exits after allowing any upload in progress to complete. + +It is not normally appropriate to invoke this program directly. Usually, it will +be invoked by a Crashpad client using the Crashpad client library, or started by +another system service. On OS X, arbitrary programs may be run with a Crashpad +handler by using man_link:run_with_crashpad[1] to establish the Crashpad client +environment before running a program. + +== Options +*--annotation*='KEY=VALUE':: +Sets a process-level annotation mapping 'KEY' to 'VALUE' in each crash report +that is written. This option may appear zero, one, or multiple times. ++ +Most annotations should be provided by the Crashpad client as module-level +annotations instead of process-level annotations. Module-level annotations are +more flexible in that they can be modified and cleared during the client +program’s lifetime. Module-level annotations can be set via the Crashpad client +library. Process-level annotations are useful for annotations that the +collection server requires be present, that have fixed values, and for cases +where a program that does not use the Crashpad client library is being +monitored. ++ +Breakpad-type collection servers only require the +"prod"+ and +"ver"+ +annotations, which should be set to the product name or identifier and product +version, respectively. It is unusual to specify other annotations as +process-level annotations via this argument. + +*--database*='PATH':: +Use 'PATH' as the path to the Crashpad crash report database. This option is +required. Crash reports are written to this database, and if uploads are +enabled, uploaded from this database to a crash report collection server. If the +database does not exist, it will be created, provided that the parent directory +of 'PATH' exists. + +*--handshake-fd*='FD':: +Perform the handshake with the initial client on the file descriptor at 'FD'. +Either this option or *--mach-service*, but not both, is required. This option +is only valid on OS X. + +*--mach-service*='SERVICE':: +Check in with the bootstrap server under the name 'SERVICE'. Either this option +or *--handshake-fd*, but not both, is required. This option is only valid on OS +X. ++ +'SERVICE' may already be reserved with the bootstrap server in cases where this +tool is started by launchd(8) as a result of a message being sent to a service +declared in a job’s +MachServices+ dictionary (see launchd.plist(5)). The +service name may also be completely unknown to the system. + +*--handshake-handle*='HANDLE':: +Perform the handshake with the initial client on the HANDLE at 'HANDLE'. Either +this option or *--pipe-name*, but not both, is required. This option is only +valid on Windows. ++ +When this option is present, the server creates a new named pipe at a random +name and informs its client of the name. The server waits for at least one +client to register, and exits when all clients have exited, after waiting for +any uploads in progress to complete. + +*--pipe-name*='PIPE':: +Listen on the given pipe name for connections from clients. 'PIPE' must be of +the form +\\.\pipe\<somename>+. Either this option or *--handshake-handle*, but +not both, is required. This option is only valid on Windows. ++ +When this option is present, the server creates a named pipe at 'PIPE', a name +known to both the server and its clients. The server continues running even +after all clients have exited. + +*--reset-own-crash-exception-port-to-system-default*:: +Causes the exception handler server to set its own crash handler to the system +default before beginning operation. This is only expected to be useful in cases +where the server inherits an inappropriate crash handler from its parent +process. This option is only valid on OS X. Use of this option is discouraged. +It should not be used absent extraordinary circumstances. + +*--url*='URL':: +If uploads are enabled, sends crash reports to the Breakpad-type crash report +collection server at 'URL'. Uploads are disabled by default, and can only be +enabled for a database by a Crashpad client using the Crashpad client library, +typically in response to a user requesting this behavior. If this option is not +specified, this program will behave as if uploads are disabled. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Exit Status + +*0*:: +Success. + +*1*:: +Failure, with a message printed to the standard error stream. + +== See Also + +man_link:catch_exception_tool[1], +man_link:crashpad_database_util[1], +man_link:generate_dump[1], +man_link:run_with_crashpad[1] + +include::../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/handler/handler.gyp b/third_party/crashpad/crashpad/handler/handler.gyp new file mode 100644 index 0000000..e7f54c8 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/handler.gyp
@@ -0,0 +1,155 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + '../build/crashpad_in_chromium.gypi', + ], + 'targets': [ + { + # This target exists so that the crashpad_handler can be embedded into + # another binary. + 'target_name': 'crashpad_handler_lib', + 'type': 'static_library', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../minidump/minidump.gyp:crashpad_minidump', + '../snapshot/snapshot.gyp:crashpad_snapshot', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../tools/tools.gyp:crashpad_tool_support', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'crash_report_upload_thread.cc', + 'crash_report_upload_thread.h', + 'mac/crash_report_exception_handler.cc', + 'mac/crash_report_exception_handler.h', + 'mac/exception_handler_server.cc', + 'mac/exception_handler_server.h', + 'handler_main.cc', + 'handler_main.h', + 'win/crash_report_exception_handler.cc', + 'win/crash_report_exception_handler.h', + ], + }, + { + 'target_name': 'crashpad_handler', + 'type': 'executable', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../tools/tools.gyp:crashpad_tool_support', + 'crashpad_handler_lib', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'main.cc', + ], + + 'conditions': [ + ['OS=="mac"', { + # In an in-Chromium build with component=shared_library, + # crashpad_handler will depend on shared libraries such as + # libbase.dylib located in out/{Debug,Release} via the @rpath + # mechanism. When crashpad_handler is copied to its home deep inside + # the Chromium app bundle, it needs to have an LC_RPATH command + # pointing back to the directory containing these dependency + # libraries. + 'variables': { + 'component%': 'static_library', + }, + 'conditions': [ + ['crashpad_in_chromium!=0 and component=="shared_library"', { + 'xcode_settings': { + 'LD_RUNPATH_SEARCH_PATHS': [ # -Wl,-rpath + # Get back from + # Chromium.app/Contents/Versions/V/Framework.framework/Helpers + '@loader_path/../../../../../..', + ], + }, + }], + ], + }], + ], + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'crashy_program', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'win/crashy_test_program.cc', + ], + }, + { + 'target_name': 'self_destroying_program', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../snapshot/snapshot.gyp:crashpad_snapshot', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'win/self_destroying_test_program.cc', + ], + }, + ], + 'conditions': [ + # Cannot create an x64 DLL with embedded debug info. + ['target_arch=="ia32"', { + 'targets': [ + { + 'target_name': 'crashy_z7_loader', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../test/test.gyp:crashpad_test', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'win/crashy_test_z7_loader.cc', + ], + }, + ], + }], + ], + }, { + 'targets': [], + }], + ], +}
diff --git a/third_party/crashpad/crashpad/handler/handler_main.cc b/third_party/crashpad/crashpad/handler/handler_main.cc new file mode 100644 index 0000000..923d2b7 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/handler_main.cc
@@ -0,0 +1,382 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "handler/handler_main.h" + +#include <getopt.h> +#include <stdlib.h> + +#include <map> +#include <string> + +#include "base/auto_reset.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/scoped_generic.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/crash_report_database.h" +#include "client/crashpad_client.h" +#include "tools/tool_support.h" +#include "handler/crash_report_upload_thread.h" +#include "util/file/file_io.h" +#include "util/stdlib/move.h" +#include "util/stdlib/map_insert.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" +#include "util/synchronization/semaphore.h" + +#if defined(OS_MACOSX) +#include <libgen.h> +#include <signal.h> + +#include "base/mac/scoped_mach_port.h" +#include "handler/mac/crash_report_exception_handler.h" +#include "handler/mac/exception_handler_server.h" +#include "util/mach/child_port_handshake.h" +#include "util/mach/mach_extensions.h" +#include "util/posix/close_stdio.h" +#elif defined(OS_WIN) +#include <windows.h> + +#include "handler/win/crash_report_exception_handler.h" +#include "util/win/exception_handler_server.h" +#include "util/win/handle.h" +#endif // OS_MACOSX + +namespace crashpad { + +namespace { + +void Usage(const base::FilePath& me) { + fprintf(stderr, +"Usage: %" PRFilePath " [OPTION]...\n" +"Crashpad's exception handler server.\n" +"\n" +" --annotation=KEY=VALUE set a process annotation in each crash report\n" +" --database=PATH store the crash report database at PATH\n" +#if defined(OS_MACOSX) +" --handshake-fd=FD establish communication with the client over FD\n" +" --mach-service=SERVICE register SERVICE with the bootstrap server\n" +" --reset-own-crash-exception-port-to-system-default\n" +" reset the server's exception handler to default\n" +#elif defined(OS_WIN) +" --handshake-handle=HANDLE\n" +" create a new pipe and send its name via HANDLE\n" +" --pipe-name=PIPE communicate with the client over PIPE\n" +#endif // OS_MACOSX +" --url=URL send crash reports to this Breakpad server URL,\n" +" only if uploads are enabled for the database\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.value().c_str()); + ToolSupport::UsageTail(me); +} + +#if defined(OS_MACOSX) + +struct ResetSIGTERMTraits { + static struct sigaction* InvalidValue() { + return nullptr; + } + + static void Free(struct sigaction* sa) { + int rv = sigaction(SIGTERM, sa, nullptr); + PLOG_IF(ERROR, rv != 0) << "sigaction"; + } +}; +using ScopedResetSIGTERM = + base::ScopedGeneric<struct sigaction*, ResetSIGTERMTraits>; + +ExceptionHandlerServer* g_exception_handler_server; + +// This signal handler is only operative when being run from launchd. +void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { + DCHECK(g_exception_handler_server); + g_exception_handler_server->Stop(); +} + +#endif // OS_MACOSX + +} // namespace + +int HandlerMain(int argc, char* argv[]) { + const base::FilePath argv0( + ToolSupport::CommandLineArgumentToFilePathStringType(argv[0])); + const base::FilePath me(argv0.BaseName()); + + enum OptionFlags { + // Long options without short equivalents. + kOptionLastChar = 255, + kOptionAnnotation, + kOptionDatabase, +#if defined(OS_MACOSX) + kOptionHandshakeFD, + kOptionMachService, + kOptionResetOwnCrashExceptionPortToSystemDefault, +#elif defined(OS_WIN) + kOptionHandshakeHandle, + kOptionPipeName, +#endif // OS_MACOSX + kOptionURL, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + struct { + std::map<std::string, std::string> annotations; + std::string url; + const char* database; +#if defined(OS_MACOSX) + int handshake_fd; + std::string mach_service; + bool reset_own_crash_exception_port_to_system_default; +#elif defined(OS_WIN) + HANDLE handshake_handle; + std::string pipe_name; +#endif // OS_MACOSX + } options = {}; +#if defined(OS_MACOSX) + options.handshake_fd = -1; +#elif defined(OS_WIN) + options.handshake_handle = INVALID_HANDLE_VALUE; +#endif + + const option long_options[] = { + {"annotation", required_argument, nullptr, kOptionAnnotation}, + {"database", required_argument, nullptr, kOptionDatabase}, +#if defined(OS_MACOSX) + {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD}, + {"mach-service", required_argument, nullptr, kOptionMachService}, + {"reset-own-crash-exception-port-to-system-default", + no_argument, + nullptr, + kOptionResetOwnCrashExceptionPortToSystemDefault}, +#elif defined(OS_WIN) + {"handshake-handle", required_argument, nullptr, kOptionHandshakeHandle}, + {"pipe-name", required_argument, nullptr, kOptionPipeName}, +#endif // OS_MACOSX + {"url", required_argument, nullptr, kOptionURL}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { + switch (opt) { + case kOptionAnnotation: { + std::string key; + std::string value; + if (!SplitString(optarg, '=', &key, &value)) { + ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE"); + return EXIT_FAILURE; + } + std::string old_value; + if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) { + LOG(WARNING) << "duplicate key " << key << ", discarding value " + << old_value; + } + break; + } + case kOptionDatabase: { + options.database = optarg; + break; + } +#if defined(OS_MACOSX) + case kOptionHandshakeFD: { + if (!StringToNumber(optarg, &options.handshake_fd) || + options.handshake_fd < 0) { + ToolSupport::UsageHint(me, + "--handshake-fd requires a file descriptor"); + return EXIT_FAILURE; + } + break; + } + case kOptionMachService: { + options.mach_service = optarg; + break; + } + case kOptionResetOwnCrashExceptionPortToSystemDefault: { + options.reset_own_crash_exception_port_to_system_default = true; + break; + } +#elif defined(OS_WIN) + case kOptionHandshakeHandle: { + // Use unsigned int, because the handle was presented by the client in + // 0x%x format. + unsigned int handle_uint; + if (!StringToNumber(optarg, &handle_uint) || + (options.handshake_handle = IntToHandle(handle_uint)) == + INVALID_HANDLE_VALUE) { + ToolSupport::UsageHint(me, "--handshake-handle requires a HANDLE"); + return EXIT_FAILURE; + } + break; + } + case kOptionPipeName: { + options.pipe_name = optarg; + break; + } +#endif // OS_MACOSX + case kOptionURL: { + options.url = optarg; + break; + } + case kOptionHelp: { + Usage(me); + return EXIT_SUCCESS; + } + case kOptionVersion: { + ToolSupport::Version(me); + return EXIT_SUCCESS; + } + default: { + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + } + } + argc -= optind; + argv += optind; + +#if defined(OS_MACOSX) + if (options.handshake_fd < 0 && options.mach_service.empty()) { + ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required"); + return EXIT_FAILURE; + } + if (options.handshake_fd >= 0 && !options.mach_service.empty()) { + ToolSupport::UsageHint( + me, "--handshake-fd and --mach-service are incompatible"); + return EXIT_FAILURE; + } +#elif defined(OS_WIN) + if (options.handshake_handle == INVALID_HANDLE_VALUE && + options.pipe_name.empty()) { + ToolSupport::UsageHint(me, "--handshake-handle or --pipe-name is required"); + return EXIT_FAILURE; + } + if (options.handshake_handle != INVALID_HANDLE_VALUE && + !options.pipe_name.empty()) { + ToolSupport::UsageHint( + me, "--handshake-handle and --pipe-name are incompatible"); + return EXIT_FAILURE; + } +#endif // OS_MACOSX + + if (!options.database) { + ToolSupport::UsageHint(me, "--database is required"); + return EXIT_FAILURE; + } + + if (argc) { + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + +#if defined(OS_MACOSX) + if (options.mach_service.empty()) { + // Don’t do this when being run by launchd. See launchd.plist(5). + CloseStdinAndStdout(); + } + + if (options.reset_own_crash_exception_port_to_system_default) { + CrashpadClient::UseSystemDefaultHandler(); + } + + base::mac::ScopedMachReceiveRight receive_right; + + if (options.handshake_fd >= 0) { + receive_right.reset( + ChildPortHandshake::RunServerForFD( + base::ScopedFD(options.handshake_fd), + ChildPortHandshake::PortRightType::kReceiveRight)); + } else if (!options.mach_service.empty()) { + receive_right = BootstrapCheckIn(options.mach_service); + } + + if (!receive_right.is_valid()) { + return EXIT_FAILURE; + } + + ExceptionHandlerServer exception_handler_server( + crashpad::move(receive_right), !options.mach_service.empty()); + base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server( + &g_exception_handler_server, &exception_handler_server); + + struct sigaction old_sa; + ScopedResetSIGTERM reset_sigterm; + if (!options.mach_service.empty()) { + // When running from launchd, no no-senders notification could ever be + // triggered, because launchd maintains a send right to the service. When + // launchd wants the job to exit, it will send a SIGTERM. See + // launchd.plist(5). + // + // Set up a SIGTERM handler that will call exception_handler_server.Stop(). + struct sigaction sa = {}; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = HandleSIGTERM; + int rv = sigaction(SIGTERM, &sa, &old_sa); + PCHECK(rv == 0) << "sigaction"; + reset_sigterm.reset(&old_sa); + } +#elif defined(OS_WIN) + ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty()); + + std::string pipe_name; + if (!options.pipe_name.empty()) { + exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name)); + } else if (options.handshake_handle != INVALID_HANDLE_VALUE) { + std::wstring pipe_name = exception_handler_server.CreatePipe(); + + uint32_t pipe_name_length = static_cast<uint32_t>(pipe_name.size()); + if (!LoggingWriteFile(options.handshake_handle, + &pipe_name_length, + sizeof(pipe_name_length))) { + return EXIT_FAILURE; + } + if (!LoggingWriteFile(options.handshake_handle, + pipe_name.c_str(), + pipe_name.size() * sizeof(pipe_name[0]))) { + return EXIT_FAILURE; + } + } +#endif // OS_MACOSX + + scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize( + base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType( + options.database)))); + if (!database) { + return EXIT_FAILURE; + } + + CrashReportUploadThread upload_thread(database.get(), options.url); + upload_thread.Start(); + + CrashReportExceptionHandler exception_handler( + database.get(), &upload_thread, &options.annotations); + + exception_handler_server.Run(&exception_handler); + + upload_thread.Stop(); + + return EXIT_SUCCESS; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/handler_main.h b/third_party/crashpad/crashpad/handler/handler_main.h new file mode 100644 index 0000000..5b5568e --- /dev/null +++ b/third_party/crashpad/crashpad/handler/handler_main.h
@@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_ +#define CRASHPAD_HANDLER_HANDLER_MAIN_H_ + +namespace crashpad { + +//! \brief The `main()` of the `crashpad_handler` binary. +//! +//! This is exposed so that `crashpad_handler` can be embedded into another +//! binary, but called and used as if it were a standalone executable. +int HandlerMain(int argc, char* argv[]); + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_HANDLER_MAIN_H_
diff --git a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc new file mode 100644 index 0000000..288148e --- /dev/null +++ b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc
@@ -0,0 +1,229 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "handler/mac/crash_report_exception_handler.h" + +#include <vector> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "client/settings.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/mac/process_snapshot_mac.h" +#include "util/file/file_writer.h" +#include "util/mach/exc_client_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_types.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/scoped_task_suspend.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map<std::string, std::string>* process_annotations) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations) { +} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() { +} + +kern_return_t CrashReportExceptionHandler::CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) { + *destroy_complex_request = true; + + // The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + // but it’s possible to deal with any exception behavior as long as it + // carries identity information (valid thread and task ports). + if (!ExceptionBehaviorHasIdentity(behavior)) { + LOG(ERROR) << base::StringPrintf( + "unexpected exception behavior %s, rejecting", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str()); + return KERN_FAILURE; + } else if (behavior != (EXCEPTION_STATE_IDENTITY | kMachExceptionCodes)) { + LOG(WARNING) << base::StringPrintf( + "unexpected exception behavior %s, proceeding", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str()); + } + + if (task == mach_task_self()) { + LOG(ERROR) << "cannot suspend myself"; + return KERN_FAILURE; + } + + ScopedTaskSuspend suspend(task); + + ProcessSnapshotMac process_snapshot; + if (!process_snapshot.Initialize(task)) { + return KERN_FAILURE; + } + + // Check for suspicious message sources. A suspicious exception message comes + // from a source other than the kernel or the process that the exception + // purportedly occurred in. + // + // TODO(mark): Consider exceptions outside of the range (0, 32) from the + // kernel to be suspicious, and exceptions other than kMachExceptionSimulated + // from the process itself to be suspicious. + const pid_t pid = process_snapshot.ProcessID(); + pid_t audit_pid = AuditPIDFromMachMessageTrailer(trailer); + if (audit_pid != -1 && audit_pid != 0) { + if (audit_pid != pid) { + LOG(WARNING) << "exception for pid " << pid << " sent by pid " + << audit_pid; + } + } + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + + if (client_options.crashpad_handler_behavior != TriState::kDisabled && + !IsExceptionNonfatalResource(exception, code[0], pid)) { + // Non-fatal resource exceptions are never user-visible and are not + // currently of interest to Crashpad. + + if (!process_snapshot.InitializeException(behavior, + thread, + exception, + code, + code_count, + *flavor, + old_state, + old_state_count)) { + return KERN_FAILURE; + } + + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings) { + // If GetSettings() or GetClientID() fails, something else will log a + // message and client_id will be left at its default value, all zeroes, + // which is appropriate. + settings->GetClientID(&client_id); + } + + process_snapshot.SetClientID(client_id); + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + CrashReportDatabase::NewReport* new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + return KERN_FAILURE; + } + + process_snapshot.SetReportID(new_report->uuid); + + CrashReportDatabase::CallErrorWritingCrashReport + call_error_writing_crash_report(database_, new_report); + + WeakFileHandleFileWriter file_writer(new_report->handle); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + if (!minidump.WriteEverything(&file_writer)) { + return KERN_FAILURE; + } + + call_error_writing_crash_report.Disarm(); + + UUID uuid; + database_status = database_->FinishedWritingCrashReport(new_report, &uuid); + if (database_status != CrashReportDatabase::kNoError) { + return KERN_FAILURE; + } + + upload_thread_->ReportPending(); + } + + if (client_options.system_crash_reporter_forwarding != TriState::kDisabled && + (exception == EXC_CRASH || + exception == EXC_RESOURCE || + exception == EXC_GUARD)) { + // Don’t forward simulated exceptions such as kMachExceptionSimulated to the + // system crash reporter. Only forward the types of exceptions that it would + // receive under normal conditions. Although the system crash reporter is + // able to deal with other exceptions including simulated ones, forwarding + // them to the system crash reporter could present the system’s crash UI for + // processes that haven’t actually crashed, and could result in reports not + // actually associated with crashes being sent to the operating system + // vendor. + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + if (system_crash_reporter_handler.get()) { + // Make copies of mutable out parameters so that the system crash reporter + // can’t influence the state returned by this method. + thread_state_flavor_t flavor_forward = *flavor; + mach_msg_type_number_t new_state_forward_count = *new_state_count; + std::vector<natural_t> new_state_forward( + new_state, new_state + new_state_forward_count); + + // The system crash reporter requires the behavior to be + // EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES. It uses the identity + // parameters but doesn’t appear to use the state parameters, including + // |flavor|, and doesn’t care if they are 0 or invalid. As long as an + // identity is available (checked above), any other exception behavior is + // converted to what the system crash reporter wants, with the caveat that + // problems may arise if the state wasn’t available and the system crash + // reporter changes in the future to use it. However, normally, the state + // will be available. + kern_return_t kr = UniversalExceptionRaise( + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + system_crash_reporter_handler.get(), + thread, + task, + exception, + code, + code_count, + &flavor_forward, + old_state, + old_state_count, + new_state_forward_count ? &new_state_forward[0] : nullptr, + &new_state_forward_count); + MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "UniversalExceptionRaise"; + } + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + + return ExcServerSuccessfulReturnValue(exception, behavior, false); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h new file mode 100644 index 0000000..a09c6b8 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
@@ -0,0 +1,86 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include <mach/mach.h> + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "client/crash_report_database.h" +#include "handler/crash_report_upload_thread.h" +#include "util/mach/exc_server_variants.h" + +namespace crashpad { + +//! \brief An exception handler that writes crash reports for exception messages +//! to a CrashReportDatabase. +class CrashReportExceptionHandler : public UniversalMachExcServer::Interface { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome’s + //! “crash keys.” Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map<std::string, std::string>* process_annotations); + + ~CrashReportExceptionHandler(); + + // UniversalMachExcServer::Interface: + + //! \brief Processes an exception message by writing a crash report to this + //! object’s CrashReportDatabase. + kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override; + + private: + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map<std::string, std::string>* process_annotations_; // weak + + DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_
diff --git a/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc b/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc new file mode 100644 index 0000000..050cc6f3 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc
@@ -0,0 +1,234 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "handler/mac/exception_handler_server.h" + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "util/mach/composite_mach_message_server.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/mach/notify_server.h" +#include "util/stdlib/move.h" + +namespace crashpad { + +namespace { + +class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, + public NotifyServer::DefaultInterface { + public: + ExceptionHandlerServerRun( + mach_port_t exception_port, + mach_port_t notify_port, + bool launchd, + UniversalMachExcServer::Interface* exception_interface) + : UniversalMachExcServer::Interface(), + NotifyServer::DefaultInterface(), + mach_exc_server_(this), + notify_server_(this), + composite_mach_message_server_(), + exception_interface_(exception_interface), + exception_port_(exception_port), + notify_port_(notify_port), + running_(true), + launchd_(launchd) { + composite_mach_message_server_.AddHandler(&mach_exc_server_); + composite_mach_message_server_.AddHandler(¬ify_server_); + } + + ~ExceptionHandlerServerRun() { + } + + void Run() { + DCHECK(running_); + + kern_return_t kr; + if (!launchd_) { + // Request that a no-senders notification for exception_port_ be sent to + // notify_port_. + mach_port_t previous; + kr = mach_port_request_notification(mach_task_self(), + exception_port_, + MACH_NOTIFY_NO_SENDERS, + 0, + notify_port_, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &previous); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification"; + base::mac::ScopedMachSendRight previous_owner(previous); + } + + // A single CompositeMachMessageServer will dispatch both exception messages + // and the no-senders notification. Put both receive rights into a port set. + // + // A single receive right can’t be used because the notification request + // requires a send-once right, which would prevent the no-senders condition + // from ever existing. Using distinct receive rights also allows the handler + // methods to ensure that the messages they process were sent by a holder of + // the proper send right. + base::mac::ScopedMachPortSet server_port_set( + NewMachPort(MACH_PORT_RIGHT_PORT_SET)); + CHECK(server_port_set.is_valid()); + + kr = mach_port_insert_member( + mach_task_self(), exception_port_, server_port_set.get()); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; + + kr = mach_port_insert_member( + mach_task_self(), notify_port_, server_port_set.get()); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; + + // Run the server in kOneShot mode so that running_ can be reevaluated after + // each message. Receipt of a valid no-senders notification causes it to be + // set to false. + while (running_) { + // This will result in a call to CatchMachException() or + // DoMachNotifyNoSenders() as appropriate. + mach_msg_return_t mr = + MachMessageServer::Run(&composite_mach_message_server_, + server_port_set.get(), + kMachMessageReceiveAuditTrailer, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeIgnore, + kMachMessageTimeoutWaitIndefinitely); + MACH_CHECK(mr == MACH_MSG_SUCCESS, mr) << "MachMessageServer::Run"; + } + } + + // UniversalMachExcServer::Interface: + + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + if (exception_port != exception_port_) { + LOG(WARNING) << "exception port mismatch"; + return KERN_FAILURE; + } + + return exception_interface_->CatchMachException(behavior, + exception_port, + thread, + task, + exception, + code, + code_count, + flavor, + old_state, + old_state_count, + new_state, + new_state_count, + trailer, + destroy_complex_request); + } + + // NotifyServer::DefaultInterface: + + kern_return_t DoMachNotifyNoSenders( + notify_port_t notify, + mach_port_mscount_t mscount, + const mach_msg_trailer_t* trailer) override { + if (notify != notify_port_) { + // The message was received as part of a port set. This check ensures that + // only the authorized sender of the no-senders notification is able to + // stop the exception server. Otherwise, a malicious client would be able + // to craft and send a no-senders notification via its exception port, and + // cause the handler to stop processing exceptions and exit. + LOG(WARNING) << "notify port mismatch"; + return KERN_FAILURE; + } + + running_ = false; + + return KERN_SUCCESS; + } + + private: + UniversalMachExcServer mach_exc_server_; + NotifyServer notify_server_; + CompositeMachMessageServer composite_mach_message_server_; + UniversalMachExcServer::Interface* exception_interface_; // weak + mach_port_t exception_port_; // weak + mach_port_t notify_port_; // weak + bool running_; + bool launchd_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerRun); +}; + +} // namespace + +ExceptionHandlerServer::ExceptionHandlerServer( + base::mac::ScopedMachReceiveRight receive_port, + bool launchd) + : receive_port_(crashpad::move(receive_port)), + notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), + launchd_(launchd) { + CHECK(receive_port_.is_valid()); + CHECK(notify_port_.is_valid()); +} + +ExceptionHandlerServer::~ExceptionHandlerServer() { +} + +void ExceptionHandlerServer::Run( + UniversalMachExcServer::Interface* exception_interface) { + ExceptionHandlerServerRun run( + receive_port_.get(), notify_port_.get(), launchd_, exception_interface); + run.Run(); +} + +void ExceptionHandlerServer::Stop() { + // Cause the exception handler server to stop running by sending it a + // synthesized no-senders notification. + // + // mach_no_senders_notification_t defines the receive side of this structure, + // with a trailer element that’s undesirable for the send side. + struct { + mach_msg_header_t header; + NDR_record_t ndr; + mach_msg_type_number_t mscount; + } no_senders_notification = {}; + no_senders_notification.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); + no_senders_notification.header.msgh_size = sizeof(no_senders_notification); + no_senders_notification.header.msgh_remote_port = notify_port_.get(); + no_senders_notification.header.msgh_local_port = MACH_PORT_NULL; + no_senders_notification.header.msgh_id = MACH_NOTIFY_NO_SENDERS; + no_senders_notification.ndr = NDR_record; + no_senders_notification.mscount = 0; + + kern_return_t kr = mach_msg(&no_senders_notification.header, + MACH_SEND_MSG, + sizeof(no_senders_notification), + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg"; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h b/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h new file mode 100644 index 0000000..421038e --- /dev/null +++ b/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h
@@ -0,0 +1,85 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_ +#define CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_ + +#include "base/basictypes.h" + +#include <mach/mach.h> + +#include "base/mac/scoped_mach_port.h" +#include "util/mach/exc_server_variants.h" + +namespace crashpad { + +//! \brief Runs the main exception-handling server in Crashpad’s handler +//! process. +class ExceptionHandlerServer { + public: + //! \brief Constructs an ExceptionHandlerServer object. + //! + //! \param[in] receive_port The port that exception messages and no-senders + //! notifications will be received on. + //! \param[in] launchd If `true`, the exception handler is being run from + //! launchd. \a receive_port is not monitored for no-senders + //! notifications, and instead, Stop() must be called to provide a “quit” + //! signal. + ExceptionHandlerServer(base::mac::ScopedMachReceiveRight receive_port, + bool launchd); + ~ExceptionHandlerServer(); + + //! \brief Runs the exception-handling server. + //! + //! \param[in] exception_interface An object to send exception messages to. + //! + //! This method monitors the receive port for exception messages and, if + //! not being run by launchd, no-senders notifications. It continues running + //! until it has no more clients, indicated by the receipt of a no-senders + //! notification, or until Stop() is called. When not being run by launchd, it + //! is important to assure that a send right exists in a client (or has been + //! queued by `mach_msg()` to be sent to a client) prior to calling this + //! method, or it will detect that it is sender-less and return immediately. + //! + //! All exception messages will be passed to \a exception_interface. + //! + //! This method must only be called once on an ExceptionHandlerServer object. + //! + //! If an unexpected condition that prevents this method from functioning is + //! encountered, it will log a message and terminate execution. Receipt of an + //! invalid message on the receive port will cause a message to be logged, but + //! this method will continue running normally. + void Run(UniversalMachExcServer::Interface* exception_interface); + + //! \brief Stops a running exception-handling server. + //! + //! The normal mode of operation is to call Stop() while Run() is running. It + //! is expected that Stop() would be called from a signal handler. + //! + //! If Stop() is called before Run() it will cause Run() to return as soon as + //! it is called. It is harmless to call Stop() after Run() has already + //! returned, or to call Stop() after it has already been called. + void Stop(); + + private: + base::mac::ScopedMachReceiveRight receive_port_; + base::mac::ScopedMachReceiveRight notify_port_; + bool launchd_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_
diff --git a/third_party/crashpad/crashpad/handler/main.cc b/third_party/crashpad/crashpad/handler/main.cc new file mode 100644 index 0000000..f326bd7 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/main.cc
@@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "handler/handler_main.h" + +#include "build/build_config.h" +#include "tools/tool_support.h" + +#if defined(OS_MACOSX) +int main(int argc, char* argv[]) { + return crashpad::HandlerMain(argc, argv); +} +#elif defined(OS_WIN) +int wmain(int argc, wchar_t* argv[]) { + return crashpad::ToolSupport::Wmain(argc, argv, crashpad::HandlerMain); +} +#endif // OS_MACOSX
diff --git a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc new file mode 100644 index 0000000..c33020e --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc
@@ -0,0 +1,121 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "handler/win/crash_report_exception_handler.h" + +#include "client/crash_report_database.h" +#include "client/settings.h" +#include "handler/crash_report_upload_thread.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/win/process_snapshot_win.h" +#include "util/file/file_writer.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_process_suspend.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map<std::string, std::string>* process_annotations) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations) { +} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() { +} + +void CrashReportExceptionHandler::ExceptionHandlerServerStarted() { +} + +unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) { + const unsigned int kFailedTerminationCode = 0xffff7002; + + ScopedProcessSuspend suspend(process); + + ProcessSnapshotWin process_snapshot; + if (!process_snapshot.Initialize(process, + ProcessSuspensionState::kSuspended, + debug_critical_section_address)) { + LOG(WARNING) << "ProcessSnapshotWin::Initialize failed"; + return kFailedTerminationCode; + } + + if (!process_snapshot.InitializeException(exception_information_address)) { + LOG(WARNING) << "ProcessSnapshotWin::InitializeException failed"; + return kFailedTerminationCode; + } + + // Now that we have the exception information, even if something else fails we + // can terminate the process with the correct exit code. + const unsigned int termination_code = + process_snapshot.Exception()->Exception(); + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + if (client_options.crashpad_handler_behavior != TriState::kDisabled) { + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings) { + // If GetSettings() or GetClientID() fails, something else will log a + // message and client_id will be left at its default value, all zeroes, + // which is appropriate. + settings->GetClientID(&client_id); + } + + process_snapshot.SetClientID(client_id); + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + CrashReportDatabase::NewReport* new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PrepareNewCrashReport failed"; + return termination_code; + } + + process_snapshot.SetReportID(new_report->uuid); + + CrashReportDatabase::CallErrorWritingCrashReport + call_error_writing_crash_report(database_, new_report); + + WeakFileHandleFileWriter file_writer(new_report->handle); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + if (!minidump.WriteEverything(&file_writer)) { + LOG(ERROR) << "WriteEverything failed"; + return termination_code; + } + + call_error_writing_crash_report.Disarm(); + + UUID uuid; + database_status = database_->FinishedWritingCrashReport(new_report, &uuid); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "FinishedWritingCrashReport failed"; + return termination_code; + } + + upload_thread_->ReportPending(); + } + + return termination_code; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h new file mode 100644 index 0000000..f03da16 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
@@ -0,0 +1,76 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include <windows.h> + +#include <map> +#include <string> + +#include "util/win/exception_handler_server.h" + +namespace crashpad { + +class CrashReportDatabase; +class CrashReportUploadThread; + +//! \brief An exception handler that writes crash reports for exception messages +//! to a CrashReportDatabase. +class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome's + //! "crash keys." Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it's able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map<std::string, std::string>* process_annotations); + + ~CrashReportExceptionHandler() override; + + // ExceptionHandlerServer::Delegate: + + //! \brief Processes an exception message by writing a crash report to this + //! object's CrashReportDatabase. + void ExceptionHandlerServerStarted() override; + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override; + + private: + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map<std::string, std::string>* process_annotations_; // weak + + DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_
diff --git a/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc b/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc new file mode 100644 index 0000000..847093a --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
@@ -0,0 +1,142 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <stdint.h> +#include <stdlib.h> +#include <windows.h> +#include <winternl.h> + +#include <string> +#include <map> +#include <vector> + +// ntstatus.h conflicts with windows.h so define this locally. +#ifndef STATUS_NO_SUCH_FILE +#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F) +#endif + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "util/win/critical_section_with_debug_info.h" +#include "util/win/get_function.h" + +namespace crashpad { +namespace { + +CRITICAL_SECTION g_test_critical_section; + +ULONG RtlNtStatusToDosError(NTSTATUS status) { + static const auto rtl_nt_status_to_dos_error = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlNtStatusToDosError); + return rtl_nt_status_to_dos_error(status); +} + +void AllocateMemoryOfVariousProtections() { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + + const size_t kPageSize = system_info.dwPageSize; + + const uint32_t kPageTypes[] = { + PAGE_NOACCESS, + PAGE_READONLY, + PAGE_READWRITE, + PAGE_EXECUTE, + PAGE_EXECUTE_READ, + PAGE_EXECUTE_READWRITE, + + // PAGE_NOACCESS is invalid with PAGE_GUARD. + PAGE_READONLY | PAGE_GUARD, + PAGE_READWRITE | PAGE_GUARD, + PAGE_EXECUTE | PAGE_GUARD, + PAGE_EXECUTE_READ | PAGE_GUARD, + PAGE_EXECUTE_READWRITE | PAGE_GUARD, + }; + + // All of these allocations are leaked, we want to view them in windbg via + // !vprot. + void* reserve = VirtualAlloc( + nullptr, arraysize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE); + PCHECK(reserve) << "VirtualAlloc MEM_RESERVE"; + uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve); + + for (size_t i = 0; i < arraysize(kPageTypes); ++i) { + void* result = + VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)), + kPageSize, + MEM_COMMIT, + kPageTypes[i]); + PCHECK(result) << "VirtualAlloc MEM_COMMIT " << i; + } +} + +void SomeCrashyFunction() { + // SetLastError and NTSTATUS so that we have something to view in !gle in + // windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the + // LastStatusError of the TEB as a side-effect, and we'll be setting + // ERROR_FILE_NOT_FOUND for GetLastError(). + SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE)); + volatile int* foo = reinterpret_cast<volatile int*>(7); + *foo = 42; +} + +int CrashyMain(int argc, wchar_t* argv[]) { + CrashpadClient client; + + if (argc == 2) { + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + } else if (argc == 3) { + if (!client.StartHandler(base::FilePath(argv[1]), + base::FilePath(argv[2]), + std::string(), + std::map<std::string, std::string>(), + std::vector<std::string>(), + false)) { + LOG(ERROR) << "StartHandler"; + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]); + fprintf(stderr, " %ls <handler_path> <database_path>\n", argv[0]); + return EXIT_FAILURE; + } + + if (!client.UseHandler()) { + LOG(ERROR) << "UseHandler"; + return EXIT_FAILURE; + } + + AllocateMemoryOfVariousProtections(); + + if (InitializeCriticalSectionWithDebugInfoIfPossible( + &g_test_critical_section)) { + EnterCriticalSection(&g_test_critical_section); + } + + SomeCrashyFunction(); + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::CrashyMain(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc b/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc new file mode 100644 index 0000000..284afa86 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/crashy_test_z7_loader.cc
@@ -0,0 +1,72 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <stdlib.h> +#include <windows.h> + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "client/crashpad_client.h" +#include "test/paths.h" + +#if !defined(ARCH_CPU_X86) +#error This test is only supported on x86. +#endif // !ARCH_CPU_X86 + +namespace crashpad { +namespace { + +int CrashyLoadZ7Main(int argc, wchar_t* argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]); + return EXIT_FAILURE; + } + + CrashpadClient client; + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + if (!client.UseHandler()) { + LOG(ERROR) << "UseHandler"; + return EXIT_FAILURE; + } + + // The DLL has /Z7 symbols embedded in the binary (rather than in a .pdb). + // There's only an x86 version of this dll as newer x64 toolchains can't + // generate this format any more. + base::FilePath z7_path = test::Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("handler/win/z7_test.dll")); + HMODULE z7_test = LoadLibrary(z7_path.value().c_str()); + if (!z7_test) { + PLOG(ERROR) << "LoadLibrary"; + return EXIT_FAILURE; + } + FARPROC crash_me = GetProcAddress(z7_test, "CrashMe"); + if (!crash_me) { + PLOG(ERROR) << "GetProcAddress"; + return EXIT_FAILURE; + } + reinterpret_cast<void(*)()>(crash_me)(); + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::CrashyLoadZ7Main(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc b/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc new file mode 100644 index 0000000..ac358ef0 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/self_destroying_test_program.cc
@@ -0,0 +1,96 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <malloc.h> +#include <stdlib.h> +#include <windows.h> +#include <winternl.h> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_client.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace { + +// We VirtualFree a region in ourselves (the stack) to confirm that the +// exception reporter captures as much as possible in the minidump and doesn't +// abort. __debugbreak() immediately after doing so because the process is +// clearly in a very broken state at this point. +bool FreeOwnStackAndBreak() { + ProcessReaderWin process_reader; + if (!process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)) { + LOG(ERROR) << "ProcessReaderWin Initialize"; + return false; + } + + const std::vector<ProcessReaderWin::Thread> threads = + process_reader.Threads(); + if (threads.empty()) { + LOG(ERROR) << "no threads"; + return false; + } + + // Push the stack up a bit so that hopefully the crash handler can succeed, + // but won't be able to read the base of the stack. + _alloca(16384); + + // We can't succeed at MEM_RELEASEing this memory, but MEM_DECOMMIT is good + // enough to make it inaccessible. + if (!VirtualFree(reinterpret_cast<void*>(threads[0].stack_region_address), + 100, + MEM_DECOMMIT)) { + PLOG(ERROR) << "VirtualFree"; + return false; + } + + // If the VirtualFree() succeeds, we may have already crashed. __debugbreak() + // just to be sure. + __debugbreak(); + + return true; +} + +int SelfDestroyingMain(int argc, wchar_t* argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]); + return EXIT_FAILURE; + } + + CrashpadClient client; + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + if (!client.UseHandler()) { + LOG(ERROR) << "UseHandler"; + return EXIT_FAILURE; + } + + if (!FreeOwnStackAndBreak()) + return EXIT_FAILURE; + + // This will never be reached. On success, we'll have crashed above, or + // otherwise returned before here. + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::SelfDestroyingMain(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/handler/win/z7_test.cpp b/third_party/crashpad/crashpad/handler/win/z7_test.cpp new file mode 100644 index 0000000..ad7b5d9 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/z7_test.cpp
@@ -0,0 +1,32 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +// Build in VC++6 or older command prompt with: +// +// cl /nologo /W4 /MT /Z7 z7_test.cpp /link /dll /out:z7_test.dll /debugtype:cv /pdb:none +// +// Given that this is quite tedious to build, the result is also checked in. + +#include <windows.h> +#include <stdio.h> + +extern "C" __declspec(dllexport) void CrashMe() { + volatile int* foo = reinterpret_cast<volatile int*>(7); + *foo = 42; +} + +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID) { + printf("%p %d\n", hinstance, reason); + return TRUE; +}
diff --git a/third_party/crashpad/crashpad/handler/win/z7_test.dll b/third_party/crashpad/crashpad/handler/win/z7_test.dll new file mode 100755 index 0000000..d470742 --- /dev/null +++ b/third_party/crashpad/crashpad/handler/win/z7_test.dll Binary files differ
diff --git a/third_party/crashpad/crashpad/minidump/minidump.gyp b/third_party/crashpad/crashpad/minidump/minidump.gyp new file mode 100644 index 0000000..0e7fb0a --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump.gyp
@@ -0,0 +1,80 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_minidump', + 'type': 'static_library', + 'dependencies': [ + '../compat/compat.gyp:crashpad_compat', + '../snapshot/snapshot.gyp:crashpad_snapshot', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'export_dependent_settings': [ + '../compat/compat.gyp:crashpad_compat', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'minidump_context.h', + 'minidump_context_writer.cc', + 'minidump_context_writer.h', + 'minidump_crashpad_info_writer.cc', + 'minidump_crashpad_info_writer.h', + 'minidump_exception_writer.cc', + 'minidump_exception_writer.h', + 'minidump_extensions.cc', + 'minidump_extensions.h', + 'minidump_file_writer.cc', + 'minidump_file_writer.h', + 'minidump_handle_writer.cc', + 'minidump_handle_writer.h', + 'minidump_memory_info_writer.cc', + 'minidump_memory_info_writer.h', + 'minidump_memory_writer.cc', + 'minidump_memory_writer.h', + 'minidump_misc_info_writer.cc', + 'minidump_misc_info_writer.h', + 'minidump_module_crashpad_info_writer.cc', + 'minidump_module_crashpad_info_writer.h', + 'minidump_module_writer.cc', + 'minidump_module_writer.h', + 'minidump_rva_list_writer.cc', + 'minidump_rva_list_writer.h', + 'minidump_simple_string_dictionary_writer.cc', + 'minidump_simple_string_dictionary_writer.h', + 'minidump_stream_writer.cc', + 'minidump_stream_writer.h', + 'minidump_string_writer.cc', + 'minidump_string_writer.h', + 'minidump_system_info_writer.cc', + 'minidump_system_info_writer.h', + 'minidump_thread_id_map.cc', + 'minidump_thread_id_map.h', + 'minidump_thread_writer.cc', + 'minidump_thread_writer.h', + 'minidump_writable.cc', + 'minidump_writable.h', + 'minidump_writer_util.cc', + 'minidump_writer_util.h', + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/minidump/minidump_context.h b/third_party/crashpad/crashpad/minidump/minidump_context.h new file mode 100644 index 0000000..4eb3acf --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_context.h
@@ -0,0 +1,344 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ + +#include <stdint.h> + +#include "base/compiler_specific.h" +#include "snapshot/cpu_context.h" +#include "util/numeric/int128.h" + +namespace crashpad { + +//! \brief Architecture-independent flags for `context_flags` fields in Minidump +//! context structures. +// +// http://zachsaw.blogspot.com/2010/11/wow64-bug-getthreadcontext-may-return.html#c5639760895973344002 +enum MinidumpContextFlags : uint32_t { + //! \brief The thread was executing a trap handler in kernel mode + //! (`CONTEXT_EXCEPTION_ACTIVE`). + //! + //! If this bit is set, it indicates that the context is from a thread that + //! was executing a trap handler in the kernel. This bit is only valid when + //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on + //! Windows. + kMinidumpContextExceptionActive = 0x08000000, + + //! \brief The thread was executing a system call in kernel mode + //! (`CONTEXT_SERVICE_ACTIVE`). + //! + //! If this bit is set, it indicates that the context is from a thread that + //! was executing a system call in the kernel. This bit is only valid when + //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on + //! Windows. + kMinidumpContextServiceActive = 0x10000000, + + //! \brief Kernel-mode state reporting is desired + //! (`CONTEXT_EXCEPTION_REQUEST`). + //! + //! This bit is not used in context structures containing snapshots of thread + //! CPU context. It used when calling `GetThreadContext()` on Windows to + //! specify that kernel-mode state reporting + //! (::kMinidumpContextExceptionReporting) is desired in the returned context + //! structure. + kMinidumpContextExceptionRequest = 0x40000000, + + //! \brief Kernel-mode state reporting is provided + //! (`CONTEXT_EXCEPTION_REPORTING`). + //! + //! If this bit is set, it indicates that the bits indicating how the thread + //! had entered kernel mode (::kMinidumpContextExceptionActive and + //! and ::kMinidumpContextServiceActive) are valid. This bit is only used on + //! Windows. + kMinidumpContextExceptionReporting = 0x80000000, +}; + +//! \brief 32-bit x86-specifc flags for MinidumpContextX86::context_flags. +enum MinidumpContextX86Flags : uint32_t { + //! \brief Identifies the context structure as 32-bit x86. This is the same as + //! `CONTEXT_i386` and `CONTEXT_i486` on Windows for this architecture. + kMinidumpContextX86 = 0x00010000, + + //! \brief Indicates the validity of control registers (`CONTEXT_CONTROL`). + //! + //! The `ebp`, `eip`, `cs`, `eflags`, `esp`, and `ss` fields are valid. + kMinidumpContextX86Control = kMinidumpContextX86 | 0x00000001, + + //! \brief Indicates the validity of non-control integer registers + //! (`CONTEXT_INTEGER`). + //! + //! The `edi`, `esi`, `ebx`, `edx`, `ecx, and `eax` fields are valid. + kMinidumpContextX86Integer = kMinidumpContextX86 | 0x00000002, + + //! \brief Indicates the validity of non-control segment registers + //! (`CONTEXT_SEGMENTS`). + //! + //! The `gs`, `fs`, `es`, and `ds` fields are valid. + kMinidumpContextX86Segment = kMinidumpContextX86 | 0x00000004, + + //! \brief Indicates the validity of floating-point state + //! (`CONTEXT_FLOATING_POINT`). + //! + //! The `float_save` field is valid. + kMinidumpContextX86FloatingPoint = kMinidumpContextX86 | 0x00000008, + + //! \brief Indicates the validity of debug registers + //! (`CONTEXT_DEBUG_REGISTERS`). + //! + //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid. + kMinidumpContextX86Debug = kMinidumpContextX86 | 0x00000010, + + //! \brief Indicates the validity of extended registers in `fxsave` format + //! (`CONTEXT_EXTENDED_REGISTERS`). + //! + //! The `extended_registers` field is valid and contains `fxsave` data. + kMinidumpContextX86Extended = kMinidumpContextX86 | 0x00000020, + + //! \brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`). + //! + //! The context contains `xsave` data. This is used with an extended context + //! structure not currently defined here. + kMinidumpContextX86Xstate = kMinidumpContextX86 | 0x00000040, + + //! \brief Indicates the validity of control, integer, and segment registers. + //! (`CONTEXT_FULL`). + kMinidumpContextX86Full = kMinidumpContextX86Control | + kMinidumpContextX86Integer | + kMinidumpContextX86Segment, + + //! \brief Indicates the validity of all registers except `xsave` data. + //! (`CONTEXT_ALL`). + kMinidumpContextX86All = kMinidumpContextX86Full | + kMinidumpContextX86FloatingPoint | + kMinidumpContextX86Debug | + kMinidumpContextX86Extended, +}; + +//! \brief A 32-bit x86 CPU context (register state) carried in a minidump file. +//! +//! This is analogous to the `CONTEXT` structure on Windows when targeting +//! 32-bit x86. This structure is used instead of `CONTEXT` to make it available +//! when targeting other architectures. +//! +//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and +//! normally alias `dr6` and `dr7`, respectively. See Intel Software +//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052), +//! 17.2.2 “Debug Registers DR4 and DR5”. +struct MinidumpContextX86 { + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextX86Flags. + //! + //! This field identifies the context structure as a 32-bit x86 CPU context, + //! and indicates which other fields in the structure are valid. + uint32_t context_flags; + + uint32_t dr0; + uint32_t dr1; + uint32_t dr2; + uint32_t dr3; + uint32_t dr6; + uint32_t dr7; + + struct { + uint32_t control_word; + uint32_t status_word; + uint32_t tag_word; + uint32_t error_offset; + uint32_t error_selector; + uint32_t data_offset; + uint32_t data_selector; + uint8_t register_area[80]; + uint32_t spare_0; + } float_save; + + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + + uint32_t edi; + uint32_t esi; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + + uint32_t ebp; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; + + // CPUContextX86::Fxsave has identical layout to what the x86 CONTEXT + // structure places here. + CPUContextX86::Fxsave fxsave; +}; + +//! \brief x86_64-specific flags for MinidumpContextAMD64::context_flags. +enum MinidumpContextAMD64Flags : uint32_t { + //! \brief Identifies the context structure as x86_64. This is the same as + //! `CONTEXT_AMD64` on Windows for this architecture. + kMinidumpContextAMD64 = 0x00100000, + + //! \brief Indicates the validity of control registers (`CONTEXT_CONTROL`). + //! + //! The `cs`, `ss`, `eflags`, `rsp`, and `rip` fields are valid. + kMinidumpContextAMD64Control = kMinidumpContextAMD64 | 0x00000001, + + //! \brief Indicates the validity of non-control integer registers + //! (`CONTEXT_INTEGER`). + //! + //! The `rax`, `rcx`, `rdx`, `rbx`, `rbp`, `rsi`, `rdi`, and `r8` through + //! `r15` fields are valid. + kMinidumpContextAMD64Integer = kMinidumpContextAMD64 | 0x00000002, + + //! \brief Indicates the validity of non-control segment registers + //! (`CONTEXT_SEGMENTS`). + //! + //! The `ds`, `es`, `fs`, and `gs` fields are valid. + kMinidumpContextAMD64Segment = kMinidumpContextAMD64 | 0x00000004, + + //! \brief Indicates the validity of floating-point state + //! (`CONTEXT_FLOATING_POINT`). + //! + //! The `xmm0` through `xmm15` fields are valid. + kMinidumpContextAMD64FloatingPoint = kMinidumpContextAMD64 | 0x00000008, + + //! \brief Indicates the validity of debug registers + //! (`CONTEXT_DEBUG_REGISTERS`). + //! + //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid. + kMinidumpContextAMD64Debug = kMinidumpContextAMD64 | 0x00000010, + + //! \brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`). + //! + //! The context contains `xsave` data. This is used with an extended context + //! structure not currently defined here. + kMinidumpContextAMD64Xstate = kMinidumpContextAMD64 | 0x00000040, + + //! \brief Indicates the validity of control, integer, and floating-point + //! registers (`CONTEXT_FULL`). + kMinidumpContextAMD64Full = kMinidumpContextAMD64Control | + kMinidumpContextAMD64Integer | + kMinidumpContextAMD64FloatingPoint, + + //! \brief Indicates the validity of all registers except `xsave` data + //! (`CONTEXT_ALL`). + kMinidumpContextAMD64All = kMinidumpContextAMD64Full | + kMinidumpContextAMD64Segment | + kMinidumpContextAMD64Debug, +}; + +//! \brief An x86_64 (AMD64) CPU context (register state) carried in a minidump +//! file. +//! +//! This is analogous to the `CONTEXT` structure on Windows when targeting +//! x86_64. This structure is used instead of `CONTEXT` to make it available +//! when targeting other architectures. +//! +//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and +//! normally alias `dr6` and `dr7`, respectively. See Intel Software +//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052), +//! 17.2.2 “Debug Registers DR4 and DR5”. +struct ALIGNAS(16) MinidumpContextAMD64 { + //! \brief Register parameter home address. + //! + //! On Windows, this field may contain the “home” address (on-stack, in the + //! shadow area) of a parameter passed by register. This field is present for + //! convenience but is not necessarily populated, even if a corresponding + //! parameter was passed by register. + //! + //! \{ + uint64_t p1_home; + uint64_t p2_home; + uint64_t p3_home; + uint64_t p4_home; + uint64_t p5_home; + uint64_t p6_home; + //! \} + + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextAMD64Flags. + //! + //! This field identifies the context structure as an x86_64 CPU context, and + //! indicates which other fields in the structure are valid. + uint32_t context_flags; + + uint32_t mx_csr; + + uint16_t cs; + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; + uint16_t ss; + + uint32_t eflags; + + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr6; + uint64_t dr7; + + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + + uint64_t rip; + + // CPUContextX86_64::Fxsave has identical layout to what the x86_64 CONTEXT + // structure places here. + CPUContextX86_64::Fxsave fxsave; + + uint128_struct vector_register[26]; + uint64_t vector_control; + + //! \brief Model-specific debug extension register. + //! + //! See Intel Software Developer’s Manual, Volume 3B: System Programming, Part + //! 2 (253669-051), 17.4 “Last Branch, Interrupt, and Exception Recording + //! Overview”, and AMD Architecture Programmer’s Manual, Volume 2: + //! System Programming (24593-3.24), 13.1.6 “Control-Transfer Breakpoint + //! Features”. + //! + //! \{ + uint64_t debug_control; + uint64_t last_branch_to_rip; + uint64_t last_branch_from_rip; + uint64_t last_exception_to_rip; + uint64_t last_exception_from_rip; + //! \} +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc new file mode 100644 index 0000000..fef1af31 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc
@@ -0,0 +1,215 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_context_writer.h" + +#include <string.h> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "snapshot/cpu_context.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +MinidumpContextWriter::~MinidumpContextWriter() { +} + +// static +scoped_ptr<MinidumpContextWriter> MinidumpContextWriter::CreateFromSnapshot( + const CPUContext* context_snapshot) { + scoped_ptr<MinidumpContextWriter> context; + + switch (context_snapshot->architecture) { + case kCPUArchitectureX86: { + MinidumpContextX86Writer* context_x86 = new MinidumpContextX86Writer(); + context.reset(context_x86); + context_x86->InitializeFromSnapshot(context_snapshot->x86); + break; + } + + case kCPUArchitectureX86_64: { + MSVC_PUSH_DISABLE_WARNING(4316); // Object on heap may not be aligned. + MinidumpContextAMD64Writer* context_amd64 = + new MinidumpContextAMD64Writer(); + MSVC_POP_WARNING(); // C4316 + context.reset(context_amd64); + context_amd64->InitializeFromSnapshot(context_snapshot->x86_64); + break; + } + + default: { + LOG(ERROR) << "unknown context architecture " + << context_snapshot->architecture; + break; + } + } + + return context; +} + +size_t MinidumpContextWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return ContextSize(); +} + +MinidumpContextX86Writer::MinidumpContextX86Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextX86; +} + +MinidumpContextX86Writer::~MinidumpContextX86Writer() { +} + + +void MinidumpContextX86Writer::InitializeFromSnapshot( + const CPUContextX86* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextX86); + + context_.context_flags = kMinidumpContextX86All; + + context_.dr0 = context_snapshot->dr0; + context_.dr1 = context_snapshot->dr1; + context_.dr2 = context_snapshot->dr2; + context_.dr3 = context_snapshot->dr3; + context_.dr6 = context_snapshot->dr6; + context_.dr7 = context_snapshot->dr7; + + // The contents of context_.float_save effectively alias everything in + // context_.fxsave that’s related to x87 FPU state. context_.float_save + // doesn’t carry state specific to SSE (or later), such as mxcsr and the xmm + // registers. + context_.float_save.control_word = context_snapshot->fxsave.fcw; + context_.float_save.status_word = context_snapshot->fxsave.fsw; + context_.float_save.tag_word = + CPUContextX86::FxsaveToFsaveTagWord(context_snapshot->fxsave.fsw, + context_snapshot->fxsave.ftw, + context_snapshot->fxsave.st_mm); + context_.float_save.error_offset = context_snapshot->fxsave.fpu_ip; + context_.float_save.error_selector = context_snapshot->fxsave.fpu_cs; + context_.float_save.data_offset = context_snapshot->fxsave.fpu_dp; + context_.float_save.data_selector = context_snapshot->fxsave.fpu_ds; + + for (size_t index = 0, offset = 0; + index < arraysize(context_snapshot->fxsave.st_mm); + offset += sizeof(context_snapshot->fxsave.st_mm[index].st), ++index) { + memcpy(&context_.float_save.register_area[offset], + &context_snapshot->fxsave.st_mm[index].st, + sizeof(context_snapshot->fxsave.st_mm[index].st)); + } + + context_.gs = context_snapshot->gs; + context_.fs = context_snapshot->fs; + context_.es = context_snapshot->es; + context_.ds = context_snapshot->ds; + context_.edi = context_snapshot->edi; + context_.esi = context_snapshot->esi; + context_.ebx = context_snapshot->ebx; + context_.edx = context_snapshot->edx; + context_.ecx = context_snapshot->ecx; + context_.eax = context_snapshot->eax; + context_.ebp = context_snapshot->ebp; + context_.eip = context_snapshot->eip; + context_.cs = context_snapshot->cs; + context_.eflags = context_snapshot->eflags; + context_.esp = context_snapshot->esp; + context_.ss = context_snapshot->ss; + + // This is effectively a memcpy() of a big structure. + context_.fxsave = context_snapshot->fxsave; +} + +bool MinidumpContextX86Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextX86Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(context_); +} + +MinidumpContextAMD64Writer::MinidumpContextAMD64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextAMD64; +} + +MinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() { +} + +void MinidumpContextAMD64Writer::InitializeFromSnapshot( + const CPUContextX86_64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64); + + context_.context_flags = kMinidumpContextAMD64All; + + context_.mx_csr = context_snapshot->fxsave.mxcsr; + context_.cs = context_snapshot->cs; + context_.fs = context_snapshot->fs; + context_.gs = context_snapshot->gs; + // The top 32 bits of rflags are reserved/unused. + context_.eflags = static_cast<uint32_t>(context_snapshot->rflags); + context_.dr0 = context_snapshot->dr0; + context_.dr1 = context_snapshot->dr1; + context_.dr2 = context_snapshot->dr2; + context_.dr3 = context_snapshot->dr3; + context_.dr6 = context_snapshot->dr6; + context_.dr7 = context_snapshot->dr7; + context_.rax = context_snapshot->rax; + context_.rcx = context_snapshot->rcx; + context_.rdx = context_snapshot->rdx; + context_.rbx = context_snapshot->rbx; + context_.rsp = context_snapshot->rsp; + context_.rbp = context_snapshot->rbp; + context_.rsi = context_snapshot->rsi; + context_.rdi = context_snapshot->rdi; + context_.r8 = context_snapshot->r8; + context_.r9 = context_snapshot->r9; + context_.r10 = context_snapshot->r10; + context_.r11 = context_snapshot->r11; + context_.r12 = context_snapshot->r12; + context_.r13 = context_snapshot->r13; + context_.r14 = context_snapshot->r14; + context_.r15 = context_snapshot->r15; + context_.rip = context_snapshot->rip; + + // This is effectively a memcpy() of a big structure. + context_.fxsave = context_snapshot->fxsave; +} + +size_t MinidumpContextAMD64Writer::Alignment() { + DCHECK_GE(state(), kStateFrozen); + + // Match the alignment of MinidumpContextAMD64. + return 16; +} + +bool MinidumpContextAMD64Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextAMD64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(context_); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_context_writer.h b/third_party/crashpad/crashpad/minidump/minidump_context_writer.h new file mode 100644 index 0000000..e48650e3 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_context_writer.h
@@ -0,0 +1,149 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ + +#include <sys/types.h> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_context.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +struct CPUContext; +struct CPUContextX86; +struct CPUContextX86_64; + +//! \brief The base class for writers of CPU context structures in minidump +//! files. +class MinidumpContextWriter : public internal::MinidumpWritable { + public: + ~MinidumpContextWriter() override; + + //! \brief Creates a MinidumpContextWriter based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \return A MinidumpContextWriter subclass, such as MinidumpContextWriterX86 + //! or MinidumpContextWriterAMD64, appropriate to the CPU type of \a + //! context_snapshot. The returned object is initialized using the source + //! data in \a context_snapshot. If \a context_snapshot is an unknown CPU + //! type’s context, logs a message and returns `nullptr`. + static scoped_ptr<MinidumpContextWriter> CreateFromSnapshot( + const CPUContext* context_snapshot); + + protected: + MinidumpContextWriter() : MinidumpWritable() {} + + //! \brief Returns the size of the context structure that this object will + //! write. + //! + //! \note This method will only be called in #kStateFrozen or a subsequent + //! state. + virtual size_t ContextSize() const = 0; + + // MinidumpWritable: + size_t SizeOfObject() final; + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpContextWriter); +}; + +//! \brief The writer for a MinidumpContextX86 structure in a minidump file. +class MinidumpContextX86Writer final : public MinidumpContextWriter { + public: + MinidumpContextX86Writer(); + ~MinidumpContextX86Writer() override; + + //! \brief Initializes the MinidumpContextX86 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextX86* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextX86* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextX86 context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextX86Writer); +}; + +//! \brief The writer for a MinidumpContextAMD64 structure in a minidump file. +class MinidumpContextAMD64Writer final : public MinidumpContextWriter { + public: + MinidumpContextAMD64Writer(); + ~MinidumpContextAMD64Writer() override; + + //! \brief Initializes the MinidumpContextAMD64 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextX86_64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextAMD64* context() { return &context_; } + + protected: + // MinidumpWritable: + size_t Alignment() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextAMD64 context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc new file mode 100644 index 0000000..9c66996 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc
@@ -0,0 +1,157 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_context_writer.h" + +#include <stdint.h> + +#include "gtest/gtest.h" +#include "minidump/minidump_context.h" +#include "minidump/test/minidump_context_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/cpu_context.h" +#include "snapshot/test/test_cpu_context.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpContextWriter, MinidumpContextX86Writer) { + StringFile string_file; + + { + // Make sure that a context writer that’s untouched writes a zeroed-out + // context. + SCOPED_TRACE("zero"); + + MinidumpContextX86Writer context_writer; + + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpContextX86), string_file.string().size()); + + const MinidumpContextX86* observed = + MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextX86(0, observed, false); + } + + { + SCOPED_TRACE("nonzero"); + + string_file.Reset(); + const uint32_t kSeed = 0x8086; + + MinidumpContextX86Writer context_writer; + InitializeMinidumpContextX86(context_writer.context(), kSeed); + + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpContextX86), string_file.string().size()); + + const MinidumpContextX86* observed = + MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextX86(kSeed, observed, false); + } +} + +TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { + StringFile string_file; + + { + // Make sure that a context writer that’s untouched writes a zeroed-out + // context. + SCOPED_TRACE("zero"); + + MinidumpContextAMD64Writer context_writer; + + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpContextAMD64), string_file.string().size()); + + const MinidumpContextAMD64* observed = + MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextAMD64(0, observed, false); + } + + { + SCOPED_TRACE("nonzero"); + + string_file.Reset(); + const uint32_t kSeed = 0x808664; + + MinidumpContextAMD64Writer context_writer; + InitializeMinidumpContextAMD64(context_writer.context(), kSeed); + + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpContextAMD64), string_file.string().size()); + + const MinidumpContextAMD64* observed = + MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextAMD64(kSeed, observed, false); + } +} + +TEST(MinidumpContextWriter, CreateFromSnapshot_X86) { + const uint32_t kSeed = 32; + + CPUContextX86 context_snapshot_x86; + CPUContext context_snapshot; + context_snapshot.x86 = &context_snapshot_x86; + InitializeCPUContextX86(&context_snapshot, kSeed); + + scoped_ptr<MinidumpContextWriter> context_writer = + MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); + ASSERT_TRUE(context_writer); + + StringFile string_file; + ASSERT_TRUE(context_writer->WriteEverything(&string_file)); + + const MinidumpContextX86* observed = + MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextX86(kSeed, observed, true); +} + +TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) { + const uint32_t kSeed = 64; + + CPUContextX86_64 context_snapshot_x86_64; + CPUContext context_snapshot; + context_snapshot.x86_64 = &context_snapshot_x86_64; + InitializeCPUContextX86_64(&context_snapshot, kSeed); + + scoped_ptr<MinidumpContextWriter> context_writer = + MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); + ASSERT_TRUE(context_writer); + + StringFile string_file; + ASSERT_TRUE(context_writer->WriteEverything(&string_file)); + + const MinidumpContextAMD64* observed = + MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextAMD64(kSeed, observed, true); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc new file mode 100644 index 0000000..ea50152 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc
@@ -0,0 +1,148 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_crashpad_info_writer.h" + +#include "base/logging.h" +#include "minidump/minidump_module_crashpad_info_writer.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "snapshot/process_snapshot.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" + +namespace crashpad { + +MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter() + : MinidumpStreamWriter(), + crashpad_info_(), + simple_annotations_(nullptr), + module_list_(nullptr) { + crashpad_info_.version = MinidumpCrashpadInfo::kVersion; +} + +MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() { +} + +void MinidumpCrashpadInfoWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!module_list_); + + UUID report_id; + process_snapshot->ReportID(&report_id); + SetReportID(report_id); + + UUID client_id; + process_snapshot->ClientID(&client_id); + SetClientID(client_id); + + auto simple_annotations = + make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter()); + simple_annotations->InitializeFromMap( + process_snapshot->AnnotationsSimpleMap()); + if (simple_annotations->IsUseful()) { + SetSimpleAnnotations(crashpad::move(simple_annotations)); + } + + auto modules = make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + modules->InitializeFromSnapshot(process_snapshot->Modules()); + + if (modules->IsUseful()) { + SetModuleList(crashpad::move(modules)); + } +} + +void MinidumpCrashpadInfoWriter::SetReportID(const UUID& report_id) { + DCHECK_EQ(state(), kStateMutable); + + crashpad_info_.report_id = report_id; +} + +void MinidumpCrashpadInfoWriter::SetClientID(const UUID& client_id) { + DCHECK_EQ(state(), kStateMutable); + + crashpad_info_.client_id = client_id; +} + +void MinidumpCrashpadInfoWriter::SetSimpleAnnotations( + scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations) { + DCHECK_EQ(state(), kStateMutable); + + simple_annotations_ = crashpad::move(simple_annotations); +} + +void MinidumpCrashpadInfoWriter::SetModuleList( + scoped_ptr<MinidumpModuleCrashpadInfoListWriter> module_list) { + DCHECK_EQ(state(), kStateMutable); + + module_list_ = crashpad::move(module_list); +} + +bool MinidumpCrashpadInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + if (simple_annotations_) { + simple_annotations_->RegisterLocationDescriptor( + &crashpad_info_.simple_annotations); + } + if (module_list_) { + module_list_->RegisterLocationDescriptor(&crashpad_info_.module_list); + } + + return true; +} + +size_t MinidumpCrashpadInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(crashpad_info_); +} + +std::vector<internal::MinidumpWritable*> +MinidumpCrashpadInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children; + if (simple_annotations_) { + children.push_back(simple_annotations_.get()); + } + if (module_list_) { + children.push_back(module_list_.get()); + } + + return children; +} + +bool MinidumpCrashpadInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&crashpad_info_, sizeof(crashpad_info_)); +} + +MinidumpStreamType MinidumpCrashpadInfoWriter::StreamType() const { + return kMinidumpStreamTypeCrashpadInfo; +} + +bool MinidumpCrashpadInfoWriter::IsUseful() const { + return crashpad_info_.report_id != UUID() || + crashpad_info_.client_id != UUID() || + simple_annotations_ || + module_list_; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h b/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h new file mode 100644 index 0000000..99945da --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h
@@ -0,0 +1,112 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class MinidumpModuleCrashpadInfoListWriter; +class MinidumpSimpleStringDictionaryWriter; +class ProcessSnapshot; + +//! \brief The writer for a MinidumpCrashpadInfo stream in a minidump file. +class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpCrashpadInfoWriter(); + ~MinidumpCrashpadInfoWriter() override; + + //! \brief Initializes MinidumpCrashpadInfo based on \a process_snapshot. + //! + //! This method may add additional structures to the minidump file as children + //! of the MinidumpCrashpadInfo stream. To do so, it may obtain other + //! snapshot information from \a process_snapshot, such as a list of + //! ModuleSnapshot objects used to initialize + //! MinidumpCrashpadInfo::module_list. Only data that is considered useful + //! will be included. For module information, usefulness is determined by + //! MinidumpModuleCrashpadInfoListWriter::IsUseful(). + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + + //! \brief Sets MinidumpCrashpadInfo::report_id. + void SetReportID(const UUID& report_id); + + //! \brief Sets MinidumpCrashpadInfo::client_id. + void SetClientID(const UUID& client_id); + + //! \brief Arranges for MinidumpCrashpadInfo::simple_annotations to point to + //! the MinidumpSimpleStringDictionaryWriter object to be written by \a + //! simple_annotations. + //! + //! This object takes ownership of \a simple_annotations and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetSimpleAnnotations( + scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations); + + //! \brief Arranges for MinidumpCrashpadInfo::module_list to point to the + //! MinidumpModuleCrashpadInfoList object to be written by \a + //! module_list. + //! + //! This object takes ownership of \a module_list and becomes its parent in + //! the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetModuleList( + scoped_ptr<MinidumpModuleCrashpadInfoListWriter> module_list); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying children would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MinidumpCrashpadInfo crashpad_info_; + scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations_; + scoped_ptr<MinidumpModuleCrashpadInfoListWriter> module_list_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpCrashpadInfoWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc new file mode 100644 index 0000000..bbfbdfc --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc
@@ -0,0 +1,316 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_crashpad_info_writer.h" + +#include <windows.h> +#include <dbghelp.h> + +#include <map> +#include <string> + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_module_crashpad_info_writer.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +void GetCrashpadInfoStream( + const std::string& file_contents, + const MinidumpCrashpadInfo** crashpad_info, + const MinidumpSimpleStringDictionary** simple_annotations, + const MinidumpModuleCrashpadInfoList** module_list) { + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[0].StreamType); + + *crashpad_info = MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>( + file_contents, directory[0].Location); + ASSERT_TRUE(*crashpad_info); + + *simple_annotations = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + file_contents, (*crashpad_info)->simple_annotations); + + *module_list = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>( + file_contents, (*crashpad_info)->module_list); +} + +TEST(MinidumpCrashpadInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + EXPECT_FALSE(crashpad_info_writer->IsUseful()); + + minidump_file_writer.AddStream(crashpad::move(crashpad_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version); + EXPECT_EQ(UUID(), crashpad_info->report_id); + EXPECT_EQ(UUID(), crashpad_info->client_id); + EXPECT_FALSE(simple_annotations); + EXPECT_FALSE(module_list); +} + +TEST(MinidumpCrashpadInfoWriter, ReportAndClientID) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + + UUID report_id; + ASSERT_TRUE( + report_id.InitializeFromString("01234567-89ab-cdef-0123-456789abcdef")); + crashpad_info_writer->SetReportID(report_id); + + UUID client_id; + ASSERT_TRUE( + client_id.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff")); + crashpad_info_writer->SetClientID(client_id); + + EXPECT_TRUE(crashpad_info_writer->IsUseful()); + + minidump_file_writer.AddStream(crashpad::move(crashpad_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version); + EXPECT_EQ(report_id, crashpad_info->report_id); + EXPECT_EQ(client_id, crashpad_info->client_id); + EXPECT_FALSE(simple_annotations); + EXPECT_FALSE(module_list); +} + +TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + + const char kKey[] = + "a thing that provides a means of gaining access to or understanding " + "something"; + const char kValue[] = + "the numerical amount denoted by an algebraic term; a magnitude, " + "quantity, or number"; + auto simple_string_dictionary_writer = + make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter()); + auto simple_string_dictionary_entry_writer = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue); + simple_string_dictionary_writer->AddEntry( + crashpad::move(simple_string_dictionary_entry_writer)); + crashpad_info_writer->SetSimpleAnnotations( + crashpad::move(simple_string_dictionary_writer)); + + EXPECT_TRUE(crashpad_info_writer->IsUseful()); + + minidump_file_writer.AddStream(crashpad::move(crashpad_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version); + EXPECT_FALSE(module_list); + + ASSERT_TRUE(simple_annotations); + ASSERT_EQ(1u, simple_annotations->count); + EXPECT_EQ(kKey, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].key)); + EXPECT_EQ(kValue, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].value)); +} + +TEST(MinidumpCrashpadInfoWriter, CrashpadModuleList) { + const uint32_t kMinidumpModuleListIndex = 3; + + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + + auto module_list_writer = + make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + auto module_writer = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + module_list_writer->AddModule(crashpad::move(module_writer), kMinidumpModuleListIndex); + crashpad_info_writer->SetModuleList(crashpad::move(module_list_writer)); + + EXPECT_TRUE(crashpad_info_writer->IsUseful()); + + minidump_file_writer.AddStream(crashpad::move(crashpad_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version); + EXPECT_FALSE(simple_annotations); + + ASSERT_TRUE(module_list); + ASSERT_EQ(1u, module_list->count); + + EXPECT_EQ(kMinidumpModuleListIndex, + module_list->modules[0].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + EXPECT_EQ(0u, module->list_annotations.DataSize); + EXPECT_EQ(0u, module->list_annotations.Rva); + EXPECT_EQ(0u, module->simple_annotations.DataSize); + EXPECT_EQ(0u, module->simple_annotations.Rva); +} + +TEST(MinidumpCrashpadInfoWriter, InitializeFromSnapshot) { + UUID report_id; + ASSERT_TRUE( + report_id.InitializeFromString("fedcba98-7654-3210-fedc-ba9876543210")); + + UUID client_id; + ASSERT_TRUE( + client_id.InitializeFromString("fedcba98-7654-3210-0123-456789abcdef")); + + const char kKey[] = "version"; + const char kValue[] = "40.0.2214.111"; + const char kEntry[] = "This is a simple annotation in a list."; + + // Test with a useless module, one that doesn’t carry anything that would + // require MinidumpCrashpadInfo or any child object. + auto process_snapshot = make_scoped_ptr(new TestProcessSnapshot()); + + auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot()); + process_snapshot->AddModule(crashpad::move(module_snapshot)); + + auto info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + info_writer->InitializeFromSnapshot(process_snapshot.get()); + EXPECT_FALSE(info_writer->IsUseful()); + + // Try again with a useful module. + process_snapshot.reset(new TestProcessSnapshot()); + + process_snapshot->SetReportID(report_id); + process_snapshot->SetClientID(client_id); + + std::map<std::string, std::string> annotations_simple_map; + annotations_simple_map[kKey] = kValue; + process_snapshot->SetAnnotationsSimpleMap(annotations_simple_map); + + module_snapshot.reset(new TestModuleSnapshot()); + std::vector<std::string> annotations_list(1, std::string(kEntry)); + module_snapshot->SetAnnotationsVector(annotations_list); + process_snapshot->AddModule(crashpad::move(module_snapshot)); + + info_writer.reset(new MinidumpCrashpadInfoWriter()); + info_writer->InitializeFromSnapshot(process_snapshot.get()); + EXPECT_TRUE(info_writer->IsUseful()); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations; + const MinidumpModuleCrashpadInfoList* module_list; + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &info, &simple_annotations, &module_list)); + + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, info->version); + + EXPECT_EQ(report_id, info->report_id); + EXPECT_EQ(client_id, info->client_id); + + ASSERT_TRUE(simple_annotations); + ASSERT_EQ(1u, simple_annotations->count); + EXPECT_EQ(kKey, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].key)); + EXPECT_EQ(kValue, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].value)); + + ASSERT_TRUE(module_list); + ASSERT_EQ(1u, module_list->count); + + EXPECT_EQ(0u, module_list->modules[0].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + + const MinidumpRVAList* list_annotations = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module->list_annotations); + ASSERT_TRUE(list_annotations); + + ASSERT_EQ(1u, list_annotations->count); + EXPECT_EQ(kEntry, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations->children[0])); + + const MinidumpSimpleStringDictionary* module_simple_annotations = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module->simple_annotations); + EXPECT_FALSE(module_simple_annotations); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc new file mode 100644 index 0000000..f11b86f --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc
@@ -0,0 +1,122 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_exception_writer.h" + +#include <sys/types.h> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "minidump/minidump_context_writer.h" +#include "snapshot/exception_snapshot.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" + +namespace crashpad { + +MinidumpExceptionWriter::MinidumpExceptionWriter() + : MinidumpStreamWriter(), exception_(), context_(nullptr) { +} + +MinidumpExceptionWriter::~MinidumpExceptionWriter() { +} + +void MinidumpExceptionWriter::InitializeFromSnapshot( + const ExceptionSnapshot* exception_snapshot, + const MinidumpThreadIDMap& thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!context_); + + auto thread_id_it = thread_id_map.find(exception_snapshot->ThreadID()); + DCHECK(thread_id_it != thread_id_map.end()); + SetThreadID(thread_id_it->second); + + SetExceptionCode(exception_snapshot->Exception()); + SetExceptionFlags(exception_snapshot->ExceptionInfo()); + SetExceptionAddress(exception_snapshot->ExceptionAddress()); + SetExceptionInformation(exception_snapshot->Codes()); + + scoped_ptr<MinidumpContextWriter> context = + MinidumpContextWriter::CreateFromSnapshot(exception_snapshot->Context()); + SetContext(crashpad::move(context)); +} + +void MinidumpExceptionWriter::SetContext( + scoped_ptr<MinidumpContextWriter> context) { + DCHECK_EQ(state(), kStateMutable); + + context_ = crashpad::move(context); +} + +void MinidumpExceptionWriter::SetExceptionInformation( + const std::vector<uint64_t>& exception_information) { + DCHECK_EQ(state(), kStateMutable); + + const size_t parameters = exception_information.size(); + const size_t kMaxParameters = + arraysize(exception_.ExceptionRecord.ExceptionInformation); + CHECK_LE(parameters, kMaxParameters); + + exception_.ExceptionRecord.NumberParameters = + base::checked_cast<uint32_t>(parameters); + size_t parameter = 0; + for (; parameter < parameters; ++parameter) { + exception_.ExceptionRecord.ExceptionInformation[parameter] = + exception_information[parameter]; + } + for (; parameter < kMaxParameters; ++parameter) { + exception_.ExceptionRecord.ExceptionInformation[parameter] = 0; + } +} + +bool MinidumpExceptionWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(context_); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + context_->RegisterLocationDescriptor(&exception_.ThreadContext); + + return true; +} + +size_t MinidumpExceptionWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(exception_); +} + +std::vector<internal::MinidumpWritable*> MinidumpExceptionWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(context_); + + std::vector<MinidumpWritable*> children; + children.push_back(context_.get()); + + return children; +} + +bool MinidumpExceptionWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&exception_, sizeof(exception_)); +} + +MinidumpStreamType MinidumpExceptionWriter::StreamType() const { + return kMinidumpStreamTypeException; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h b/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h new file mode 100644 index 0000000..b4de21a --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h
@@ -0,0 +1,124 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_thread_id_map.h" + +namespace crashpad { + +class ExceptionSnapshot; +class MinidumpContextWriter; + +//! \brief The writer for a MINIDUMP_EXCEPTION_STREAM stream in a minidump file. +class MinidumpExceptionWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpExceptionWriter(); + ~MinidumpExceptionWriter() override; + + //! \brief Initializes the MINIDUMP_EXCEPTION_STREAM based on \a + //! exception_snapshot. + //! + //! \param[in] exception_snapshot The exception snapshot to use as source + //! data. + //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to + //! determine the 32-bit minidump thread ID to use for the thread + //! identified by \a exception_snapshot. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ExceptionSnapshot* exception_snapshot, + const MinidumpThreadIDMap& thread_id_map); + + //! \brief Arranges for MINIDUMP_EXCEPTION_STREAM::ThreadContext to point to + //! the CPU context to be written by \a context. + //! + //! A context is required in all MINIDUMP_EXCEPTION_STREAM objects. + //! + //! This object takes ownership of \a context and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetContext(scoped_ptr<MinidumpContextWriter> context); + + //! \brief Sets MINIDUMP_EXCEPTION_STREAM::ThreadId. + void SetThreadID(uint32_t thread_id) { exception_.ThreadId = thread_id; } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionCode. + void SetExceptionCode(uint32_t exception_code) { + exception_.ExceptionRecord.ExceptionCode = exception_code; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionFlags. + void SetExceptionFlags(uint32_t exception_flags) { + exception_.ExceptionRecord.ExceptionFlags = exception_flags; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionRecord. + void SetExceptionRecord(uint64_t exception_record) { + exception_.ExceptionRecord.ExceptionRecord = exception_record; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionAddress. + void SetExceptionAddress(uint64_t exception_address) { + exception_.ExceptionRecord.ExceptionAddress = exception_address; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionInformation and + //! MINIDUMP_EXCEPTION::NumberParameters. + //! + //! MINIDUMP_EXCEPTION::NumberParameters is set to the number of elements in + //! \a exception_information. The elements of + //! MINIDUMP_EXCEPTION::ExceptionInformation are set to the elements of \a + //! exception_information. Unused elements in + //! MINIDUMP_EXCEPTION::ExceptionInformation are set to `0`. + //! + //! \a exception_information must have no more than + //! #EXCEPTION_MAXIMUM_PARAMETERS elements. + //! + //! \note Valid in #kStateMutable. + void SetExceptionInformation( + const std::vector<uint64_t>& exception_information); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_EXCEPTION_STREAM exception_; + scoped_ptr<MinidumpContextWriter> context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpExceptionWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc new file mode 100644 index 0000000..4c6e267 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc
@@ -0,0 +1,282 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_exception_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <sys/types.h> + +#include <string> +#include <vector> + +#include "gtest/gtest.h" +#include "minidump/minidump_context.h" +#include "minidump/minidump_context_writer.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/test/minidump_context_test_util.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_exception_snapshot.h" +#include "test/gtest_death_check.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +// This returns the MINIDUMP_EXCEPTION_STREAM stream in |exception_stream|. +void GetExceptionStream(const std::string& file_contents, + const MINIDUMP_EXCEPTION_STREAM** exception_stream) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kExceptionStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kContextOffset = + kExceptionStreamOffset + sizeof(MINIDUMP_EXCEPTION_STREAM); + const size_t kFileSize = kContextOffset + sizeof(MinidumpContextX86); + ASSERT_EQ(file_contents.size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + + ASSERT_EQ(kMinidumpStreamTypeException, directory[0].StreamType); + EXPECT_EQ(kExceptionStreamOffset, directory[0].Location.Rva); + + *exception_stream = + MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>( + file_contents, directory[0].Location); + ASSERT_TRUE(exception_stream); +} + +// The MINIDUMP_EXCEPTION_STREAMs |expected| and |observed| are compared against +// each other using gtest assertions. The context will be recovered from +// |file_contents| and stored in |context|. +void ExpectExceptionStream(const MINIDUMP_EXCEPTION_STREAM* expected, + const MINIDUMP_EXCEPTION_STREAM* observed, + const std::string& file_contents, + const MinidumpContextX86** context) { + EXPECT_EQ(expected->ThreadId, observed->ThreadId); + EXPECT_EQ(0u, observed->__alignment); + EXPECT_EQ(expected->ExceptionRecord.ExceptionCode, + observed->ExceptionRecord.ExceptionCode); + EXPECT_EQ(expected->ExceptionRecord.ExceptionFlags, + observed->ExceptionRecord.ExceptionFlags); + EXPECT_EQ(expected->ExceptionRecord.ExceptionRecord, + observed->ExceptionRecord.ExceptionRecord); + EXPECT_EQ(expected->ExceptionRecord.ExceptionAddress, + observed->ExceptionRecord.ExceptionAddress); + EXPECT_EQ(expected->ExceptionRecord.NumberParameters, + observed->ExceptionRecord.NumberParameters); + EXPECT_EQ(0u, observed->ExceptionRecord.__unusedAlignment); + for (size_t index = 0; + index < arraysize(observed->ExceptionRecord.ExceptionInformation); + ++index) { + EXPECT_EQ(expected->ExceptionRecord.ExceptionInformation[index], + observed->ExceptionRecord.ExceptionInformation[index]); + } + *context = MinidumpWritableAtLocationDescriptor<MinidumpContextX86>( + file_contents, observed->ThreadContext); + ASSERT_TRUE(context); +} + +TEST(MinidumpExceptionWriter, Minimal) { + MinidumpFileWriter minidump_file_writer; + auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter()); + + const uint32_t kSeed = 100; + + auto context_x86_writer = make_scoped_ptr(new MinidumpContextX86Writer()); + InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); + exception_writer->SetContext(crashpad::move(context_x86_writer)); + + minidump_file_writer.AddStream(crashpad::move(exception_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetExceptionStream(string_file.string(), &observed_exception_stream)); + + MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {}; + expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream, + observed_exception_stream, + string_file.string(), + &observed_context)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, false)); +} + +TEST(MinidumpExceptionWriter, Standard) { + MinidumpFileWriter minidump_file_writer; + auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter()); + + const uint32_t kSeed = 200; + const uint32_t kThreadID = 1; + const uint32_t kExceptionCode = 2; + const uint32_t kExceptionFlags = 3; + const uint32_t kExceptionRecord = 4; + const uint32_t kExceptionAddress = 5; + const uint64_t kExceptionInformation0 = 6; + const uint64_t kExceptionInformation1 = 7; + const uint64_t kExceptionInformation2 = 7; + + auto context_x86_writer = make_scoped_ptr(new MinidumpContextX86Writer()); + InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); + exception_writer->SetContext(crashpad::move(context_x86_writer)); + + exception_writer->SetThreadID(kThreadID); + exception_writer->SetExceptionCode(kExceptionCode); + exception_writer->SetExceptionFlags(kExceptionFlags); + exception_writer->SetExceptionRecord(kExceptionRecord); + exception_writer->SetExceptionAddress(kExceptionAddress); + + // Set a lot of exception information at first, and then replace it with less. + // This tests that the exception that is written does not contain the + // “garbage” from the initial SetExceptionInformation() call. + std::vector<uint64_t> exception_information(EXCEPTION_MAXIMUM_PARAMETERS, + 0x5a5a5a5a5a5a5a5a); + exception_writer->SetExceptionInformation(exception_information); + + exception_information.clear(); + exception_information.push_back(kExceptionInformation0); + exception_information.push_back(kExceptionInformation1); + exception_information.push_back(kExceptionInformation2); + exception_writer->SetExceptionInformation(exception_information); + + minidump_file_writer.AddStream(crashpad::move(exception_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetExceptionStream(string_file.string(), &observed_exception_stream)); + + MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {}; + expected_exception_stream.ThreadId = kThreadID; + expected_exception_stream.ExceptionRecord.ExceptionCode = kExceptionCode; + expected_exception_stream.ExceptionRecord.ExceptionFlags = kExceptionFlags; + expected_exception_stream.ExceptionRecord.ExceptionRecord = kExceptionRecord; + expected_exception_stream.ExceptionRecord.ExceptionAddress = + kExceptionAddress; + expected_exception_stream.ExceptionRecord.NumberParameters = + static_cast<uint32_t>(exception_information.size()); + for (size_t index = 0; index < exception_information.size(); ++index) { + expected_exception_stream.ExceptionRecord.ExceptionInformation[index] = + exception_information[index]; + } + expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream, + observed_exception_stream, + string_file.string(), + &observed_context)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, false)); +} + +TEST(MinidumpExceptionWriter, InitializeFromSnapshot) { + std::vector<uint64_t> exception_codes; + exception_codes.push_back(0x1000000000000000); + exception_codes.push_back(0x5555555555555555); + + MINIDUMP_EXCEPTION_STREAM expect_exception = {}; + + expect_exception.ThreadId = 123; + expect_exception.ExceptionRecord.ExceptionCode = 100; + expect_exception.ExceptionRecord.ExceptionFlags = 1; + expect_exception.ExceptionRecord.ExceptionAddress = 0xfedcba9876543210; + expect_exception.ExceptionRecord.NumberParameters = + static_cast<uint32_t>(exception_codes.size()); + for (size_t index = 0; index < exception_codes.size(); ++index) { + expect_exception.ExceptionRecord.ExceptionInformation[index] = + exception_codes[index]; + } + const uint64_t kThreadID = 0xaaaaaaaaaaaaaaaa; + const uint32_t kSeed = 65; + + TestExceptionSnapshot exception_snapshot; + exception_snapshot.SetThreadID(kThreadID); + exception_snapshot.SetException( + expect_exception.ExceptionRecord.ExceptionCode); + exception_snapshot.SetExceptionInfo( + expect_exception.ExceptionRecord.ExceptionFlags); + exception_snapshot.SetExceptionAddress( + expect_exception.ExceptionRecord.ExceptionAddress); + exception_snapshot.SetCodes(exception_codes); + + InitializeCPUContextX86(exception_snapshot.MutableContext(), kSeed); + + MinidumpThreadIDMap thread_id_map; + thread_id_map[kThreadID] = expect_exception.ThreadId; + + auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter()); + exception_writer->InitializeFromSnapshot(&exception_snapshot, thread_id_map); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(exception_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_EXCEPTION_STREAM* exception = nullptr; + ASSERT_NO_FATAL_FAILURE(GetExceptionStream(string_file.string(), &exception)); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expect_exception, + exception, + string_file.string(), + &observed_context)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, true)); +} + +TEST(MinidumpExceptionWriterDeathTest, NoContext) { + MinidumpFileWriter minidump_file_writer; + auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter()); + + minidump_file_writer.AddStream(crashpad::move(exception_writer)); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "context_"); +} + +TEST(MinidumpExceptionWriterDeathTest, TooMuchInformation) { + MinidumpExceptionWriter exception_writer; + std::vector<uint64_t> exception_information(EXCEPTION_MAXIMUM_PARAMETERS + 1, + 0x5a5a5a5a5a5a5a5a); + ASSERT_DEATH_CHECK( + exception_writer.SetExceptionInformation(exception_information), + "kMaxParameters"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_extensions.cc b/third_party/crashpad/crashpad/minidump/minidump_extensions.cc new file mode 100644 index 0000000..a956817e --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_extensions.cc
@@ -0,0 +1,22 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_extensions.h" + +namespace crashpad { + +const uint32_t MinidumpModuleCrashpadInfo::kVersion; +const uint32_t MinidumpCrashpadInfo::kVersion; + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_extensions.h b/third_party/crashpad/crashpad/minidump/minidump_extensions.h new file mode 100644 index 0000000..5e43055 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_extensions.h
@@ -0,0 +1,436 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <winnt.h> + +#include "base/compiler_specific.h" +#include "build/build_config.h" +#include "util/misc/pdb_structures.h" +#include "util/misc/uuid.h" + +// C4200 is "nonstandard extension used : zero-sized array in struct/union". +// We would like to globally disable this warning, but unfortunately, the +// compiler is buggy and only supports disabling it with a pragma, so we can't +// disable it with other silly warnings in build/common.gypi. See: +// https://connect.microsoft.com/VisualStudio/feedback/details/1114440 +MSVC_PUSH_DISABLE_WARNING(4200); + +#if defined(COMPILER_MSVC) +#define PACKED +#pragma pack(push, 1) +#else +#define PACKED __attribute__((packed)) +#endif // COMPILER_MSVC + +namespace crashpad { + +//! \brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each +//! stream structure has a corresponding stream type value to identify it. +//! +//! \sa MINIDUMP_STREAM_TYPE +enum MinidumpStreamType : uint32_t { + //! \brief The stream type for MINIDUMP_THREAD_LIST. + //! + //! \sa ThreadListStream + kMinidumpStreamTypeThreadList = ThreadListStream, + + //! \brief The stream type for MINIDUMP_MODULE_LIST. + //! + //! \sa ModuleListStream + kMinidumpStreamTypeModuleList = ModuleListStream, + + //! \brief The stream type for MINIDUMP_MEMORY_LIST. + //! + //! \sa MemoryListStream + kMinidumpStreamTypeMemoryList = MemoryListStream, + + //! \brief The stream type for MINIDUMP_EXCEPTION_STREAM. + //! + //! \sa ExceptionStream + kMinidumpStreamTypeException = ExceptionStream, + + //! \brief The stream type for MINIDUMP_SYSTEM_INFO. + //! + //! \sa SystemInfoStream + kMinidumpStreamTypeSystemInfo = SystemInfoStream, + + //! \brief The stream type for MINIDUMP_HANDLE_DATA_STREAM. + //! + //! \sa HandleDataStream + kMinidumpStreamTypeHandleData = HandleDataStream, + + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, + //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. + //! + //! \sa MiscInfoStream + kMinidumpStreamTypeMiscInfo = MiscInfoStream, + + //! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST. + //! + //! \sa MemoryInfoListStream + kMinidumpStreamTypeMemoryInfoList = MemoryInfoListStream, + + // 0x4350 = "CP" + + //! \brief The stream type for MinidumpCrashpadInfo. + kMinidumpStreamTypeCrashpadInfo = 0x43500001, +}; + +//! \brief A variable-length UTF-8-encoded string carried within a minidump +//! file. +//! +//! \sa MINIDUMP_STRING +struct ALIGNAS(4) PACKED MinidumpUTF8String { + // The field names do not conform to typical style, they match the names used + // in MINIDUMP_STRING. This makes it easier to operate on MINIDUMP_STRING (for + // UTF-16 strings) and MinidumpUTF8String using templates. + + //! \brief The length of the #Buffer field in bytes, not including the `NUL` + //! terminator. + //! + //! \note This field is interpreted as a byte count, not a count of Unicode + //! code points. + uint32_t Length; + + //! \brief The string, encoded in UTF-8, and terminated with a `NUL` byte. + uint8_t Buffer[0]; +}; + +//! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. +//! +//! \sa \ref PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*" +enum MinidumpCPUArchitecture : uint16_t { + //! \brief 32-bit x86. + //! + //! These systems identify their CPUs generically as “x86” or “ia32”, or with + //! more specific names such as “i386”, “i486”, “i586”, and “i686”. + kMinidumpCPUArchitectureX86 = PROCESSOR_ARCHITECTURE_INTEL, + + kMinidumpCPUArchitectureMIPS = PROCESSOR_ARCHITECTURE_MIPS, + kMinidumpCPUArchitectureAlpha = PROCESSOR_ARCHITECTURE_ALPHA, + + //! \brief 32-bit PowerPC. + //! + //! These systems identify their CPUs generically as “ppc”, or with more + //! specific names such as “ppc6xx”, “ppc7xx”, and “ppc74xx”. + kMinidumpCPUArchitecturePPC = PROCESSOR_ARCHITECTURE_PPC, + + kMinidumpCPUArchitectureSHx = PROCESSOR_ARCHITECTURE_SHX, + + //! \brief 32-bit ARM. + //! + //! These systems identify their CPUs generically as “arm”, or with more + //! specific names such as “armv6” and “armv7”. + kMinidumpCPUArchitectureARM = PROCESSOR_ARCHITECTURE_ARM, + + kMinidumpCPUArchitectureIA64 = PROCESSOR_ARCHITECTURE_IA64, + kMinidumpCPUArchitectureAlpha64 = PROCESSOR_ARCHITECTURE_ALPHA64, + kMinidumpCPUArchitectureMSIL = PROCESSOR_ARCHITECTURE_MSIL, + + //! \brief 64-bit x86. + //! + //! These systems identify their CPUs as “x86_64”, “amd64”, or “x64”. + kMinidumpCPUArchitectureAMD64 = PROCESSOR_ARCHITECTURE_AMD64, + + //! \brief A 32-bit x86 process running on IA-64 (Itanium). + //! + //! \note This value is not used in minidump files for 32-bit x86 processes + //! running on a 64-bit-capable x86 CPU and operating system. In that + //! configuration, #kMinidumpCPUArchitectureX86 is used instead. + kMinidumpCPUArchitectureX86Win64 = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64, + + kMinidumpCPUArchitectureNeutral = PROCESSOR_ARCHITECTURE_NEUTRAL, + kMinidumpCPUArchitectureSPARC = 0x8001, + + //! \brief 64-bit PowerPC. + //! + //! These systems identify their CPUs generically as “ppc64”, or with more + //! specific names such as “ppc970”. + kMinidumpCPUArchitecturePPC64 = 0x8002, + + //! \brief 64-bit ARM. + //! + //! These systems identify their CPUs generically as “arm64” or “aarch64”, or + //! with more specific names such as “armv8”. + kMinidumpCPUArchitectureARM64 = 0x8003, + + //! \brief Unknown CPU architecture. + kMinidumpCPUArchitectureUnknown = PROCESSOR_ARCHITECTURE_UNKNOWN, +}; + +//! \brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType. +//! +//! \sa \ref VER_NT_x "VER_NT_*" +enum MinidumpOSType : uint8_t { + //! \brief A “desktop” or “workstation” system. + kMinidumpOSTypeWorkstation = VER_NT_WORKSTATION, + + //! \brief A “domain controller” system. Windows-specific. + kMinidumpOSTypeDomainController = VER_NT_DOMAIN_CONTROLLER, + + //! \brief A “server” system. + kMinidumpOSTypeServer = VER_NT_SERVER, +}; + +//! \brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId. +//! +//! \sa \ref VER_PLATFORM_x "VER_PLATFORM_*" +enum MinidumpOS : uint32_t { + //! \brief Windows 3.1. + kMinidumpOSWin32s = VER_PLATFORM_WIN32s, + + //! \brief Windows 95, Windows 98, and Windows Me. + kMinidumpOSWin32Windows = VER_PLATFORM_WIN32_WINDOWS, + + //! \brief Windows NT, Windows 2000, and later. + kMinidumpOSWin32NT = VER_PLATFORM_WIN32_NT, + + kMinidumpOSUnix = 0x8000, + + //! \brief Mac OS X, Darwin for traditional systems. + kMinidumpOSMacOSX = 0x8101, + + //! \brief iOS, Darwin for mobile devices. + kMinidumpOSiOS = 0x8102, + + //! \brief Linux, not including Android. + kMinidumpOSLinux = 0x8201, + + kMinidumpOSSolaris = 0x8202, + + //! \brief Android. + kMinidumpOSAndroid = 0x8203, + + kMinidumpOSPS3 = 0x8204, + + //! \brief Native Client (NaCl). + kMinidumpOSNaCl = 0x8205, + + //! \brief Unknown operating system. + kMinidumpOSUnknown = 0xffffffff, +}; + + +//! \brief A list of ::RVA pointers. +struct ALIGNAS(4) PACKED MinidumpRVAList { + //! \brief The number of children present in the #children array. + uint32_t count; + + //! \brief Pointers to other structures in the minidump file. + RVA children[0]; +}; + +//! \brief A key-value pair. +struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionaryEntry { + //! \brief ::RVA of a MinidumpUTF8String containing the key of a key-value + //! pair. + RVA key; + + //! \brief ::RVA of a MinidumpUTF8String containing the value of a key-value + //! pair. + RVA value; +}; + +//! \brief A list of key-value pairs. +struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionary { + //! \brief The number of key-value pairs present. + uint32_t count; + + //! \brief A list of MinidumpSimpleStringDictionaryEntry entries. + MinidumpSimpleStringDictionaryEntry entries[0]; +}; + +//! \brief Additional Crashpad-specific information about a module carried +//! within a minidump file. +//! +//! This structure augments the information provided by MINIDUMP_MODULE. The +//! minidump file must contain a module list stream +//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear. +//! +//! This structure is versioned. When changing this structure, leave the +//! existing structure intact so that earlier parsers will be able to understand +//! the fields they are aware of, and make additions at the end of the +//! structure. Revise #kVersion and document each field’s validity based on +//! #version, so that newer parsers will be able to determine whether the added +//! fields are valid or not. +//! +//! \sa #MinidumpModuleCrashpadInfoList +struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfo { + //! \brief The structure’s currently-defined version number. + //! + //! \sa version + static const uint32_t kVersion = 1; + + //! \brief The structure’s version number. + //! + //! Readers can use this field to determine which other fields in the + //! structure are valid. Upon encountering a value greater than #kVersion, a + //! reader should assume that the structure’s layout is compatible with the + //! structure defined as having value #kVersion. + //! + //! Writers may produce values less than #kVersion in this field if there is + //! no need for any fields present in later versions. + uint32_t version; + + //! \brief A MinidumpRVAList pointing to MinidumpUTF8String objects. The + //! module controls the data that appears here. + //! + //! These strings correspond to ModuleSnapshot::AnnotationsVector() and do not + //! duplicate anything in #simple_annotations. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR list_annotations; + + //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as + //! key-value pairs. The module controls the data that appears here. + //! + //! These key-value pairs correspond to + //! ModuleSnapshot::AnnotationsSimpleMap() and do not duplicate anything in + //! #list_annotations. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; +}; + +//! \brief A link between a MINIDUMP_MODULE structure and additional +//! Crashpad-specific information about a module carried within a minidump +//! file. +struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfoLink { + //! \brief A link to a MINIDUMP_MODULE structure in the module list stream. + //! + //! This field is an index into MINIDUMP_MODULE_LIST::Modules. This field’s + //! value must be in the range of MINIDUMP_MODULE_LIST::NumberOfEntries. + uint32_t minidump_module_list_index; + + //! \brief A link to a MinidumpModuleCrashpadInfo structure. + //! + //! MinidumpModuleCrashpadInfo structures are accessed indirectly through + //! MINIDUMP_LOCATION_DESCRIPTOR pointers to allow for future growth of the + //! MinidumpModuleCrashpadInfo structure. + MINIDUMP_LOCATION_DESCRIPTOR location; +}; + +//! \brief Additional Crashpad-specific information about modules carried within +//! a minidump file. +//! +//! This structure augments the information provided by +//! MINIDUMP_MODULE_LIST. The minidump file must contain a module list stream +//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear. +//! +//! MinidumpModuleCrashpadInfoList::count may be less than the value of +//! MINIDUMP_MODULE_LIST::NumberOfModules because not every MINIDUMP_MODULE +//! structure carried within the minidump file will necessarily have +//! Crashpad-specific information provided by a MinidumpModuleCrashpadInfo +//! structure. +struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfoList { + //! \brief The number of children present in the #modules array. + uint32_t count; + + //! \brief Crashpad-specific information about modules, along with links to + //! MINIDUMP_MODULE structures that contain module information + //! traditionally carried within minidump files. + MinidumpModuleCrashpadInfoLink modules[0]; +}; + +//! \brief Additional Crashpad-specific information carried within a minidump +//! file. +//! +//! This structure is versioned. When changing this structure, leave the +//! existing structure intact so that earlier parsers will be able to understand +//! the fields they are aware of, and make additions at the end of the +//! structure. Revise #kVersion and document each field’s validity based on +//! #version, so that newer parsers will be able to determine whether the added +//! fields are valid or not. +struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { + // UUID has a constructor, which makes it non-POD, which makes this structure + // non-POD. In order for the default constructor to zero-initialize other + // members, an explicit constructor must be provided. + MinidumpCrashpadInfo() + : version(), + report_id(), + client_id(), + simple_annotations(), + module_list() { + } + + //! \brief The structure’s currently-defined version number. + //! + //! \sa version + static const uint32_t kVersion = 1; + + //! \brief The structure’s version number. + //! + //! Readers can use this field to determine which other fields in the + //! structure are valid. Upon encountering a value greater than #kVersion, a + //! reader should assume that the structure’s layout is compatible with the + //! structure defined as having value #kVersion. + //! + //! Writers may produce values less than #kVersion in this field if there is + //! no need for any fields present in later versions. + uint32_t version; + + //! \brief A %UUID identifying an individual crash report. + //! + //! This provides a stable identifier for a crash even as the report is + //! converted to different formats, provided that all formats support storing + //! a crash report ID. + //! + //! If no identifier is available, this field will contain zeroes. + //! + //! This field is present when #version is at least `1`. + UUID report_id; + + //! \brief A %UUID identifying the client that crashed. + //! + //! Client identification is within the scope of the application, but it is + //! expected that the identifier will be unique for an instance of Crashpad + //! monitoring an application or set of applications for a user. The + //! identifier shall remain stable over time. + //! + //! If no identifier is available, this field will contain zeroes. + //! + //! This field is present when #version is at least `1`. + UUID client_id; + + //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as + //! key-value pairs. + //! + //! These key-value pairs correspond to + //! ProcessSnapshot::AnnotationsSimpleMap(). + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; + + //! \brief A pointer to a #MinidumpModuleCrashpadInfoList structure. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR module_list; +}; + +#if defined(COMPILER_MSVC) +#pragma pack(pop) +#endif // COMPILER_MSVC +#undef PACKED + +MSVC_POP_WARNING(); // C4200 + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc new file mode 100644 index 0000000..be2b1cd --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
@@ -0,0 +1,249 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_file_writer.h" + +#include "base/logging.h" +#include "minidump/minidump_crashpad_info_writer.h" +#include "minidump/minidump_exception_writer.h" +#include "minidump/minidump_handle_writer.h" +#include "minidump/minidump_memory_info_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "minidump/minidump_misc_info_writer.h" +#include "minidump/minidump_module_writer.h" +#include "minidump/minidump_system_info_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_thread_writer.h" +#include "minidump/minidump_writer_util.h" +#include "snapshot/process_snapshot.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpFileWriter::MinidumpFileWriter() + : MinidumpWritable(), header_(), streams_(), stream_types_() { + // Don’t set the signature field right away. Leave it set to 0, so that a + // partially-written minidump file isn’t confused for a complete and valid + // one. The header will be rewritten in WriteToFile(). + header_.Signature = 0; + + header_.Version = MINIDUMP_VERSION; + header_.CheckSum = 0; + header_.Flags = MiniDumpNormal; +} + +MinidumpFileWriter::~MinidumpFileWriter() { +} + +void MinidumpFileWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(header_.Signature, 0u); + DCHECK_EQ(header_.TimeDateStamp, 0u); + DCHECK_EQ(header_.Flags, MiniDumpNormal); + DCHECK(streams_.empty()); + + // This time is truncated to an integer number of seconds, not rounded, for + // compatibility with the truncation of process_snapshot->ProcessStartTime() + // done by MinidumpMiscInfoWriter::InitializeFromSnapshot(). Handling both + // timestamps in the same way allows the highest-fidelity computation of + // process uptime as the difference between the two values. + timeval snapshot_time; + process_snapshot->SnapshotTime(&snapshot_time); + SetTimestamp(snapshot_time.tv_sec); + + const SystemSnapshot* system_snapshot = process_snapshot->System(); + auto system_info = make_scoped_ptr(new MinidumpSystemInfoWriter()); + system_info->InitializeFromSnapshot(system_snapshot); + AddStream(crashpad::move(system_info)); + + auto misc_info = make_scoped_ptr(new MinidumpMiscInfoWriter()); + misc_info->InitializeFromSnapshot(process_snapshot); + AddStream(crashpad::move(misc_info)); + + auto memory_list = make_scoped_ptr(new MinidumpMemoryListWriter()); + auto thread_list = make_scoped_ptr(new MinidumpThreadListWriter()); + thread_list->SetMemoryListWriter(memory_list.get()); + MinidumpThreadIDMap thread_id_map; + thread_list->InitializeFromSnapshot(process_snapshot->Threads(), + &thread_id_map); + AddStream(crashpad::move(thread_list)); + + const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception(); + if (exception_snapshot) { + auto exception = make_scoped_ptr(new MinidumpExceptionWriter()); + exception->InitializeFromSnapshot(exception_snapshot, thread_id_map); + AddStream(crashpad::move(exception)); + } + + auto module_list = make_scoped_ptr(new MinidumpModuleListWriter()); + module_list->InitializeFromSnapshot(process_snapshot->Modules()); + AddStream(crashpad::move(module_list)); + + auto crashpad_info = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + crashpad_info->InitializeFromSnapshot(process_snapshot); + + // Since the MinidumpCrashpadInfo stream is an extension, it’s safe to not add + // it to the minidump file if it wouldn’t carry any useful information. + if (crashpad_info->IsUseful()) { + AddStream(crashpad::move(crashpad_info)); + } + + std::vector<const MemoryMapRegionSnapshot*> memory_map_snapshot = + process_snapshot->MemoryMap(); + if (!memory_map_snapshot.empty()) { + auto memory_info_list = make_scoped_ptr(new MinidumpMemoryInfoListWriter()); + memory_info_list->InitializeFromSnapshot(memory_map_snapshot); + AddStream(crashpad::move(memory_info_list)); + } + + std::vector<HandleSnapshot> handles_snapshot = process_snapshot->Handles(); + if (!handles_snapshot.empty()) { + auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter()); + handle_data_writer->InitializeFromSnapshot(handles_snapshot); + AddStream(crashpad::move(handle_data_writer)); + } + + memory_list->AddFromSnapshot(process_snapshot->ExtraMemory()); + + AddStream(crashpad::move(memory_list)); +} + +void MinidumpFileWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&header_.TimeDateStamp, timestamp); +} + +void MinidumpFileWriter::AddStream( + scoped_ptr<internal::MinidumpStreamWriter> stream) { + DCHECK_EQ(state(), kStateMutable); + + MinidumpStreamType stream_type = stream->StreamType(); + + auto rv = stream_types_.insert(stream_type); + CHECK(rv.second) << "stream_type " << stream_type << " already present"; + + streams_.push_back(stream.release()); + + DCHECK_EQ(streams_.size(), stream_types_.size()); +} + +bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateMutable); + + FileOffset start_offset = file_writer->Seek(0, SEEK_CUR); + if (start_offset < 0) { + return false; + } + + if (!MinidumpWritable::WriteEverything(file_writer)) { + return false; + } + + FileOffset end_offset = file_writer->Seek(0, SEEK_CUR); + if (end_offset < 0) { + return false; + } + + // Now that the entire minidump file has been completely written, go back to + // the beginning and rewrite the header with the correct signature to identify + // it as a valid minidump file. + header_.Signature = MINIDUMP_SIGNATURE; + + if (file_writer->Seek(start_offset, SEEK_SET) != 0) { + return false; + } + + if (!file_writer->Write(&header_, sizeof(header_))) { + return false; + } + + // Seek back to the end of the file, in case some non-minidump content will be + // written to the file after the minidump content. + return file_writer->Seek(end_offset, SEEK_SET) >= 0; +} + +bool MinidumpFileWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t stream_count = streams_.size(); + CHECK_EQ(stream_count, stream_types_.size()); + + if (!AssignIfInRange(&header_.NumberOfStreams, stream_count)) { + LOG(ERROR) << "stream_count " << stream_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpFileWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + return sizeof(header_) + streams_.size() * sizeof(MINIDUMP_DIRECTORY); +} + +std::vector<internal::MinidumpWritable*> MinidumpFileWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + std::vector<MinidumpWritable*> children; + for (internal::MinidumpStreamWriter* stream : streams_) { + children.push_back(stream); + } + + return children; +} + +bool MinidumpFileWriter::WillWriteAtOffsetImpl(FileOffset offset) { + DCHECK_EQ(state(), kStateFrozen); + DCHECK_EQ(offset, 0); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + auto directory_offset = streams_.empty() ? 0 : offset + sizeof(header_); + if (!AssignIfInRange(&header_.StreamDirectoryRva, directory_offset)) { + LOG(ERROR) << "offset " << directory_offset << " out of range"; + return false; + } + + return MinidumpWritable::WillWriteAtOffsetImpl(offset); +} + +bool MinidumpFileWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + WritableIoVec iov; + iov.iov_base = &header_; + iov.iov_len = sizeof(header_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (internal::MinidumpStreamWriter* stream : streams_) { + iov.iov_base = stream->DirectoryListEntry(); + iov.iov_len = sizeof(MINIDUMP_DIRECTORY); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer.h b/third_party/crashpad/crashpad/minidump/minidump_file_writer.h new file mode 100644 index 0000000..8b738b6 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer.h
@@ -0,0 +1,124 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <sys/types.h> + +#include <set> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "util/file/file_io.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class ProcessSnapshot; + +//! \brief The root-level object in a minidump file. +//! +//! This object writes a MINIDUMP_HEADER and list of MINIDUMP_DIRECTORY entries +//! to a minidump file. +class MinidumpFileWriter final : public internal::MinidumpWritable { + public: + MinidumpFileWriter(); + ~MinidumpFileWriter() override; + + //! \brief Initializes the MinidumpFileWriter and populates it with + //! appropriate child streams based on \a process_snapshot. + //! + //! This method will add additional streams to the minidump file as children + //! of the MinidumpFileWriter object and as pointees of the top-level + //! MINIDUMP_DIRECTORY. To do so, it will obtain other snapshot information + //! from \a process_snapshot, such as a SystemSnapshot, lists of + //! ThreadSnapshot and ModuleSnapshot objects, and, if available, an + //! ExceptionSnapshot. + //! + //! The streams are added in the order that they are expected to be most + //! useful to minidump readers, to improve data locality and minimize seeking. + //! The streams are added in this order: + //! - kMinidumpStreamTypeSystemInfo + //! - kMinidumpStreamTypeMiscInfo + //! - kMinidumpStreamTypeThreadList + //! - kMinidumpStreamTypeException (if present) + //! - kMinidumpStreamTypeModuleList + //! - kMinidumpStreamTypeCrashpadInfo (if present) + //! - kMinidumpStreamTypeMemoryList + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + + //! \brief Sets MINIDUMP_HEADER::Timestamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + //! \brief Adds a stream to the minidump file and arranges for a + //! MINIDUMP_DIRECTORY entry to point to it. + //! + //! This object takes ownership of \a stream and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! At most one object of each stream type (as obtained from + //! internal::MinidumpStreamWriter::StreamType()) may be added to a + //! MinidumpFileWriter object. It is an error to attempt to add multiple + //! streams with the same stream type. + //! + //! \note Valid in #kStateMutable. + void AddStream(scoped_ptr<internal::MinidumpStreamWriter> stream); + + // MinidumpWritable: + + //! \copydoc internal::MinidumpWritable::WriteEverything() + //! + //! This method does not initially write the final value for + //! MINIDUMP_HEADER::Signature. After all child objects have been written, it + //! rewinds to the beginning of the file and writes the correct value for this + //! field. This prevents incompletely-written minidump files from being + //! mistaken for valid ones. + bool WriteEverything(FileWriterInterface* file_writer) override; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WillWriteAtOffsetImpl(FileOffset offset) override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_HEADER header_; + PointerVector<internal::MinidumpStreamWriter> streams_; + + // Protects against multiple streams with the same ID being added. + std::set<MinidumpStreamType> stream_types_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpFileWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc new file mode 100644 index 0000000..2295a99 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
@@ -0,0 +1,463 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_file_writer.h" + +#include <windows.h> +#include <dbghelp.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "gtest/gtest.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_exception_snapshot.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "snapshot/test/test_system_snapshot.h" +#include "snapshot/test/test_thread_snapshot.h" +#include "test/gtest_death_check.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpFileWriter, Empty) { + MinidumpFileWriter minidump_file; + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MINIDUMP_HEADER), string_file.string().size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 0, 0)); + EXPECT_FALSE(directory); +} + +class TestStream final : public internal::MinidumpStreamWriter { + public: + TestStream(MinidumpStreamType stream_type, + size_t stream_size, + uint8_t stream_value) + : stream_data_(stream_size, stream_value), stream_type_(stream_type) {} + + ~TestStream() override {} + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override { + return stream_type_; + } + + protected: + // MinidumpWritable: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return stream_data_.size(); + } + + bool WriteObject(FileWriterInterface* file_writer) override { + EXPECT_EQ(state(), kStateWritable); + return file_writer->Write(&stream_data_[0], stream_data_.size()); + } + + private: + std::string stream_data_; + MinidumpStreamType stream_type_; + + DISALLOW_COPY_AND_ASSIGN(TestStream); +}; + +TEST(MinidumpFileWriter, OneStream) { + MinidumpFileWriter minidump_file; + const time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + const size_t kStreamSize = 5; + const MinidumpStreamType kStreamType = static_cast<MinidumpStreamType>(0x4d); + const uint8_t kStreamValue = 0x5a; + auto stream = + make_scoped_ptr(new TestStream(kStreamType, kStreamSize, kStreamValue)); + minidump_file.AddStream(crashpad::move(stream)); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kFileSize = kStreamOffset + kStreamSize; + + ASSERT_EQ(kFileSize, string_file.string().size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kStreamType, directory[0].StreamType); + EXPECT_EQ(kStreamSize, directory[0].Location.DataSize); + EXPECT_EQ(kStreamOffset, directory[0].Location.Rva); + + const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream_data); + + std::string expected_stream(kStreamSize, kStreamValue); + EXPECT_EQ(0, memcmp(stream_data, expected_stream.c_str(), kStreamSize)); +} + +TEST(MinidumpFileWriter, ThreeStreams) { + MinidumpFileWriter minidump_file; + const time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + const size_t kStream0Size = 5; + const MinidumpStreamType kStream0Type = static_cast<MinidumpStreamType>(0x6d); + const uint8_t kStream0Value = 0x5a; + auto stream0 = make_scoped_ptr( + new TestStream(kStream0Type, kStream0Size, kStream0Value)); + minidump_file.AddStream(crashpad::move(stream0)); + + // Make the second stream’s type be a smaller quantity than the first stream’s + // to test that the streams show up in the order that they were added, not in + // numeric order. + const size_t kStream1Size = 3; + const MinidumpStreamType kStream1Type = static_cast<MinidumpStreamType>(0x4d); + const uint8_t kStream1Value = 0xa5; + auto stream1 = make_scoped_ptr( + new TestStream(kStream1Type, kStream1Size, kStream1Value)); + minidump_file.AddStream(crashpad::move(stream1)); + + const size_t kStream2Size = 1; + const MinidumpStreamType kStream2Type = static_cast<MinidumpStreamType>(0x7e); + const uint8_t kStream2Value = 0x36; + auto stream2 = make_scoped_ptr( + new TestStream(kStream2Type, kStream2Size, kStream2Value)); + minidump_file.AddStream(crashpad::move(stream2)); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kStream0Offset = + kDirectoryOffset + 3 * sizeof(MINIDUMP_DIRECTORY); + const size_t kStream1Padding = 3; + const size_t kStream1Offset = kStream0Offset + kStream0Size + kStream1Padding; + const size_t kStream2Padding = 1; + const size_t kStream2Offset = kStream1Offset + kStream1Size + kStream2Padding; + const size_t kFileSize = kStream2Offset + kStream2Size; + + ASSERT_EQ(kFileSize, string_file.string().size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 3, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kStream0Type, directory[0].StreamType); + EXPECT_EQ(kStream0Size, directory[0].Location.DataSize); + EXPECT_EQ(kStream0Offset, directory[0].Location.Rva); + EXPECT_EQ(kStream1Type, directory[1].StreamType); + EXPECT_EQ(kStream1Size, directory[1].Location.DataSize); + EXPECT_EQ(kStream1Offset, directory[1].Location.Rva); + EXPECT_EQ(kStream2Type, directory[2].StreamType); + EXPECT_EQ(kStream2Size, directory[2].Location.DataSize); + EXPECT_EQ(kStream2Offset, directory[2].Location.Rva); + + const uint8_t* stream0_data = MinidumpWritableAtLocationDescriptor<uint8_t>( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream0_data); + + std::string expected_stream0(kStream0Size, kStream0Value); + EXPECT_EQ(0, memcmp(stream0_data, expected_stream0.c_str(), kStream0Size)); + + const int kZeroes[16] = {}; + ASSERT_GE(sizeof(kZeroes), kStream1Padding); + EXPECT_EQ(0, memcmp(stream0_data + kStream0Size, kZeroes, kStream1Padding)); + + const uint8_t* stream1_data = MinidumpWritableAtLocationDescriptor<uint8_t>( + string_file.string(), directory[1].Location); + ASSERT_TRUE(stream1_data); + + std::string expected_stream1(kStream1Size, kStream1Value); + EXPECT_EQ(0, memcmp(stream1_data, expected_stream1.c_str(), kStream1Size)); + + ASSERT_GE(sizeof(kZeroes), kStream2Padding); + EXPECT_EQ(0, memcmp(stream1_data + kStream1Size, kZeroes, kStream2Padding)); + + const uint8_t* stream2_data = MinidumpWritableAtLocationDescriptor<uint8_t>( + string_file.string(), directory[2].Location); + ASSERT_TRUE(stream2_data); + + std::string expected_stream2(kStream2Size, kStream2Value); + EXPECT_EQ(0, memcmp(stream2_data, expected_stream2.c_str(), kStream2Size)); +} + +TEST(MinidumpFileWriter, ZeroLengthStream) { + MinidumpFileWriter minidump_file; + + const size_t kStreamSize = 0; + const MinidumpStreamType kStreamType = static_cast<MinidumpStreamType>(0x4d); + auto stream = make_scoped_ptr(new TestStream(kStreamType, kStreamSize, 0)); + minidump_file.AddStream(crashpad::move(stream)); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kFileSize = kStreamOffset + kStreamSize; + + ASSERT_EQ(kFileSize, string_file.string().size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kStreamType, directory[0].StreamType); + EXPECT_EQ(kStreamSize, directory[0].Location.DataSize); + EXPECT_EQ(kStreamOffset, directory[0].Location.Rva); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) { + const uint32_t kSnapshotTime = 0x4976043c; + const timeval kSnapshotTimeval = { static_cast<time_t>(kSnapshotTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(crashpad::move(system_snapshot)); + + auto peb_snapshot = make_scoped_ptr(new TestMemorySnapshot()); + const uint64_t kPebAddress = 0x07f90000; + peb_snapshot->SetAddress(kPebAddress); + const size_t kPebSize = 0x280; + peb_snapshot->SetSize(kPebSize); + peb_snapshot->SetValue('p'); + process_snapshot.AddExtraMemory(crashpad::move(peb_snapshot)); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 5, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>( + string_file.string(), directory[0].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>( + string_file.string(), directory[1].Location)); + + EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>( + string_file.string(), directory[2].Location)); + + EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[3].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>( + string_file.string(), directory[3].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[4].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>( + string_file.string(), directory[4].Location)); + + const MINIDUMP_MEMORY_LIST* memory_list = + MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>( + string_file.string(), directory[4].Location); + EXPECT_EQ(1u, memory_list->NumberOfMemoryRanges); + EXPECT_EQ(kPebAddress, memory_list->MemoryRanges[0].StartOfMemoryRange); + EXPECT_EQ(kPebSize, memory_list->MemoryRanges[0].Memory.DataSize); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) { + // In a 32-bit environment, this will give a “timestamp out of range” warning, + // but the test should complete without failure. + const uint32_t kSnapshotTime = 0xfd469ab8; + MSVC_SUPPRESS_WARNING(4309); // Truncation of constant value. + MSVC_SUPPRESS_WARNING(4838); // Narrowing conversion. + const timeval kSnapshotTimeval = { static_cast<time_t>(kSnapshotTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(crashpad::move(system_snapshot)); + + auto thread_snapshot = make_scoped_ptr(new TestThreadSnapshot()); + InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5); + process_snapshot.AddThread(crashpad::move(thread_snapshot)); + + auto exception_snapshot = make_scoped_ptr(new TestExceptionSnapshot()); + InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11); + process_snapshot.SetException(crashpad::move(exception_snapshot)); + + // The module does not have anything that needs to be represented in a + // MinidumpModuleCrashpadInfo structure, so no such structure is expected to + // be present, which will in turn suppress the addition of a + // MinidumpCrashpadInfo stream. + auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot()); + process_snapshot.AddModule(crashpad::move(module_snapshot)); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 6, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>( + string_file.string(), directory[0].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>( + string_file.string(), directory[1].Location)); + + EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>( + string_file.string(), directory[2].Location)); + + EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>( + string_file.string(), directory[3].Location)); + + EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>( + string_file.string(), directory[4].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[5].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>( + string_file.string(), directory[5].Location)); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) { + const uint32_t kSnapshotTime = 0x15393bd3; + const timeval kSnapshotTimeval = { static_cast<time_t>(kSnapshotTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(crashpad::move(system_snapshot)); + + auto thread_snapshot = make_scoped_ptr(new TestThreadSnapshot()); + InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5); + process_snapshot.AddThread(crashpad::move(thread_snapshot)); + + auto exception_snapshot = make_scoped_ptr(new TestExceptionSnapshot()); + InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11); + process_snapshot.SetException(crashpad::move(exception_snapshot)); + + // The module needs an annotation for the MinidumpCrashpadInfo stream to be + // considered useful and be included. + auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot()); + std::vector<std::string> annotations_list(1, std::string("annotation")); + module_snapshot->SetAnnotationsVector(annotations_list); + process_snapshot.AddModule(crashpad::move(module_snapshot)); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 7, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>( + string_file.string(), directory[0].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>( + string_file.string(), directory[1].Location)); + + EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>( + string_file.string(), directory[2].Location)); + + EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>( + string_file.string(), directory[3].Location)); + + EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>( + string_file.string(), directory[4].Location)); + + EXPECT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[5].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>( + string_file.string(), directory[5].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[6].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>( + string_file.string(), directory[6].Location)); +} + +TEST(MinidumpFileWriterDeathTest, SameStreamType) { + MinidumpFileWriter minidump_file; + + const size_t kStream0Size = 5; + const MinidumpStreamType kStream0Type = static_cast<MinidumpStreamType>(0x4d); + const uint8_t kStream0Value = 0x5a; + auto stream0 = make_scoped_ptr( + new TestStream(kStream0Type, kStream0Size, kStream0Value)); + minidump_file.AddStream(crashpad::move(stream0)); + + // It is an error to add a second stream of the same type. + const size_t kStream1Size = 3; + const MinidumpStreamType kStream1Type = static_cast<MinidumpStreamType>(0x4d); + const uint8_t kStream1Value = 0xa5; + auto stream1 = make_scoped_ptr( + new TestStream(kStream1Type, kStream1Size, kStream1Value)); + ASSERT_DEATH_CHECK(minidump_file.AddStream(crashpad::move(stream1)), + "already present"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_handle_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_handle_writer.cc new file mode 100644 index 0000000..73eeb0c --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_handle_writer.cc
@@ -0,0 +1,127 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_handle_writer.h" + +#include <string> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "minidump/minidump_extensions.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpHandleDataWriter::MinidumpHandleDataWriter() + : handle_data_stream_base_(), handle_descriptors_(), strings_() { +} + +MinidumpHandleDataWriter::~MinidumpHandleDataWriter() { + STLDeleteContainerPairSecondPointers(strings_.begin(), strings_.end()); +} + +void MinidumpHandleDataWriter::InitializeFromSnapshot( + const std::vector<HandleSnapshot>& handle_snapshots) { + DCHECK_EQ(state(), kStateMutable); + + DCHECK(handle_descriptors_.empty()); + // Because we RegisterRVA() on the string writer below, we preallocate and + // never resize the handle_descriptors_ vector. + handle_descriptors_.resize(handle_snapshots.size()); + for (size_t i = 0; i < handle_snapshots.size(); ++i) { + const HandleSnapshot& handle_snapshot = handle_snapshots[i]; + MINIDUMP_HANDLE_DESCRIPTOR& descriptor = handle_descriptors_[i]; + + descriptor.Handle = handle_snapshot.handle; + + if (handle_snapshot.type_name.empty()) { + descriptor.TypeNameRva = 0; + } else { + auto it = strings_.lower_bound(handle_snapshot.type_name); + internal::MinidumpUTF16StringWriter* writer; + if (it != strings_.end() && it->first == handle_snapshot.type_name) { + writer = it->second; + } else { + writer = new internal::MinidumpUTF16StringWriter(); + strings_.insert(it, std::make_pair(handle_snapshot.type_name, writer)); + writer->SetUTF8(handle_snapshot.type_name); + } + writer->RegisterRVA(&descriptor.TypeNameRva); + } + + descriptor.ObjectNameRva = 0; + descriptor.Attributes = handle_snapshot.attributes; + descriptor.GrantedAccess = handle_snapshot.granted_access; + descriptor.HandleCount = handle_snapshot.handle_count; + descriptor.PointerCount = handle_snapshot.pointer_count; + } +} + +bool MinidumpHandleDataWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) + return false; + + handle_data_stream_base_.SizeOfHeader = sizeof(handle_data_stream_base_); + handle_data_stream_base_.SizeOfDescriptor = sizeof(handle_descriptors_[0]); + const size_t handle_count = handle_descriptors_.size(); + if (!AssignIfInRange(&handle_data_stream_base_.NumberOfDescriptors, + handle_count)) { + LOG(ERROR) << "handle_count " << handle_count << " out of range"; + return false; + } + handle_data_stream_base_.Reserved = 0; + + return true; +} + +size_t MinidumpHandleDataWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return sizeof(handle_data_stream_base_) + + sizeof(handle_descriptors_[0]) * handle_descriptors_.size(); +} + +std::vector<internal::MinidumpWritable*> MinidumpHandleDataWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children; + for (const auto& pair : strings_) + children.push_back(pair.second); + return children; +} + +bool MinidumpHandleDataWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &handle_data_stream_base_; + iov.iov_len = sizeof(handle_data_stream_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (const auto& descriptor : handle_descriptors_) { + iov.iov_base = &descriptor; + iov.iov_len = sizeof(descriptor); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpHandleDataWriter::StreamType() const { + return kMinidumpStreamTypeHandleData; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_handle_writer.h b/third_party/crashpad/crashpad/minidump/minidump_handle_writer.h new file mode 100644 index 0000000..43bdac6 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_handle_writer.h
@@ -0,0 +1,76 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include <map> +#include <string> +#include <vector> + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/handle_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_HANDLE_DATA_STREAM stream in a minidump +//! and its contained MINIDUMP_HANDLE_DESCRIPTOR s. +//! +//! As we currently do not track any data beyond what MINIDUMP_HANDLE_DESCRIPTOR +//! supports, we only write that type of record rather than the newer +//! MINIDUMP_HANDLE_DESCRIPTOR_2. +//! +//! Note that this writer writes both the header (MINIDUMP_HANDLE_DATA_STREAM) +//! and the list of objects (MINIDUMP_HANDLE_DESCRIPTOR), which is different +//! from some of the other list writers. +class MinidumpHandleDataWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpHandleDataWriter(); + ~MinidumpHandleDataWriter() override; + + //! \brief Adds a MINIDUMP_HANDLE_DESCRIPTOR for each handle in \a + //! handle_snapshot to the MINIDUMP_HANDLE_DATA_STREAM. + //! + //! \param[in] handle_snapshots The handle snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot( + const std::vector<HandleSnapshot>& handle_snapshots); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_HANDLE_DATA_STREAM handle_data_stream_base_; + std::vector<MINIDUMP_HANDLE_DESCRIPTOR> handle_descriptors_; + std::map<std::string, internal::MinidumpUTF16StringWriter*> strings_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpHandleDataWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_handle_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_handle_writer_test.cc new file mode 100644 index 0000000..049a095 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_handle_writer_test.cc
@@ -0,0 +1,202 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_handle_writer.h" + +#include <string> + +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +// The handle data stream is expected to be the only stream. +void GetHandleDataStream( + const std::string& file_contents, + const MINIDUMP_HANDLE_DATA_STREAM** handle_data_stream) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kHandleDataStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + const size_t kDirectoryIndex = 0; + + ASSERT_EQ(kMinidumpStreamTypeHandleData, + directory[kDirectoryIndex].StreamType); + EXPECT_EQ(kHandleDataStreamOffset, directory[kDirectoryIndex].Location.Rva); + + *handle_data_stream = + MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>( + file_contents, directory[kDirectoryIndex].Location); + ASSERT_TRUE(*handle_data_stream); +} + +TEST(MinidumpHandleDataWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter()); + minidump_file_writer.AddStream(crashpad::move(handle_data_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_HANDLE_DATA_STREAM), + string_file.string().size()); + + const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetHandleDataStream(string_file.string(), &handle_data_stream)); + + EXPECT_EQ(0u, handle_data_stream->NumberOfDescriptors); +} + +TEST(MinidumpHandleDataWriter, OneHandle) { + MinidumpFileWriter minidump_file_writer; + auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter()); + + HandleSnapshot handle_snapshot; + handle_snapshot.handle = 0x1234; + handle_snapshot.type_name = "Something"; + handle_snapshot.attributes = 0x12345678; + handle_snapshot.granted_access = 0x9abcdef0; + handle_snapshot.pointer_count = 4567; + handle_snapshot.handle_count = 9876; + + std::vector<HandleSnapshot> snapshot; + snapshot.push_back(handle_snapshot); + + handle_data_writer->InitializeFromSnapshot(snapshot); + + minidump_file_writer.AddStream(crashpad::move(handle_data_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const size_t kTypeNameStringDataLength = + (handle_snapshot.type_name.size() + 1) * sizeof(base::char16); + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_HANDLE_DATA_STREAM) + + sizeof(MINIDUMP_HANDLE_DESCRIPTOR) + sizeof(MINIDUMP_STRING) + + kTypeNameStringDataLength, + string_file.string().size()); + + const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetHandleDataStream(string_file.string(), &handle_data_stream)); + + EXPECT_EQ(1u, handle_data_stream->NumberOfDescriptors); + const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor = + reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>( + &handle_data_stream[1]); + EXPECT_EQ(handle_snapshot.handle, handle_descriptor->Handle); + EXPECT_EQ(handle_snapshot.type_name, + base::UTF16ToUTF8(MinidumpStringAtRVAAsString( + string_file.string(), handle_descriptor->TypeNameRva))); + EXPECT_EQ(0u, handle_descriptor->ObjectNameRva); + EXPECT_EQ(handle_snapshot.attributes, handle_descriptor->Attributes); + EXPECT_EQ(handle_snapshot.granted_access, handle_descriptor->GrantedAccess); + EXPECT_EQ(handle_snapshot.handle_count, handle_descriptor->HandleCount); + EXPECT_EQ(handle_snapshot.pointer_count, handle_descriptor->PointerCount); +} + +TEST(MinidumpHandleDataWriter, RepeatedTypeName) { + MinidumpFileWriter minidump_file_writer; + auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter()); + + HandleSnapshot handle_snapshot; + handle_snapshot.handle = 0x1234; + handle_snapshot.type_name = "Something"; + handle_snapshot.attributes = 0x12345678; + handle_snapshot.granted_access = 0x9abcdef0; + handle_snapshot.pointer_count = 4567; + handle_snapshot.handle_count = 9876; + + HandleSnapshot handle_snapshot2; + handle_snapshot2.handle = 0x4321; + handle_snapshot2.type_name = "Something"; // Note: same as above. + handle_snapshot2.attributes = 0x87654321; + handle_snapshot2.granted_access = 0x0fedcba9; + handle_snapshot2.pointer_count = 7654; + handle_snapshot2.handle_count = 6789; + + std::vector<HandleSnapshot> snapshot; + snapshot.push_back(handle_snapshot); + snapshot.push_back(handle_snapshot2); + + handle_data_writer->InitializeFromSnapshot(snapshot); + + minidump_file_writer.AddStream(crashpad::move(handle_data_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const size_t kTypeNameStringDataLength = + (handle_snapshot.type_name.size() + 1) * sizeof(base::char16); + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_HANDLE_DATA_STREAM) + + (sizeof(MINIDUMP_HANDLE_DESCRIPTOR) * 2) + + sizeof(MINIDUMP_STRING) + kTypeNameStringDataLength, + string_file.string().size()); + + const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetHandleDataStream(string_file.string(), &handle_data_stream)); + + EXPECT_EQ(2u, handle_data_stream->NumberOfDescriptors); + const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor = + reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>( + &handle_data_stream[1]); + EXPECT_EQ(handle_snapshot.handle, handle_descriptor->Handle); + EXPECT_EQ(handle_snapshot.type_name, + base::UTF16ToUTF8(MinidumpStringAtRVAAsString( + string_file.string(), handle_descriptor->TypeNameRva))); + EXPECT_EQ(0u, handle_descriptor->ObjectNameRva); + EXPECT_EQ(handle_snapshot.attributes, handle_descriptor->Attributes); + EXPECT_EQ(handle_snapshot.granted_access, handle_descriptor->GrantedAccess); + EXPECT_EQ(handle_snapshot.handle_count, handle_descriptor->HandleCount); + EXPECT_EQ(handle_snapshot.pointer_count, handle_descriptor->PointerCount); + + const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor2 = + reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>( + reinterpret_cast<const unsigned char*>(&handle_data_stream[1]) + + sizeof(MINIDUMP_HANDLE_DESCRIPTOR)); + EXPECT_EQ(handle_snapshot2.handle, handle_descriptor2->Handle); + EXPECT_EQ(handle_snapshot2.type_name, + base::UTF16ToUTF8(MinidumpStringAtRVAAsString( + string_file.string(), handle_descriptor2->TypeNameRva))); + EXPECT_EQ(0u, handle_descriptor2->ObjectNameRva); + EXPECT_EQ(handle_snapshot2.attributes, handle_descriptor2->Attributes); + EXPECT_EQ(handle_snapshot2.granted_access, handle_descriptor2->GrantedAccess); + EXPECT_EQ(handle_snapshot2.handle_count, handle_descriptor2->HandleCount); + EXPECT_EQ(handle_snapshot2.pointer_count, handle_descriptor2->PointerCount); + + EXPECT_EQ(handle_descriptor->TypeNameRva, handle_descriptor2->TypeNameRva); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer.cc new file mode 100644 index 0000000..8b6bdc8 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer.cc
@@ -0,0 +1,85 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_memory_info_writer.h" + +#include "base/logging.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +MinidumpMemoryInfoListWriter::MinidumpMemoryInfoListWriter() + : memory_info_list_base_(), items_() { +} + +MinidumpMemoryInfoListWriter::~MinidumpMemoryInfoListWriter() { +} + +void MinidumpMemoryInfoListWriter::InitializeFromSnapshot( + const std::vector<const MemoryMapRegionSnapshot*>& memory_map) { + DCHECK_EQ(state(), kStateMutable); + + DCHECK(items_.empty()); + for (const auto& region : memory_map) + items_.push_back(region->AsMinidumpMemoryInfo()); +} + +bool MinidumpMemoryInfoListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) + return false; + + memory_info_list_base_.SizeOfHeader = sizeof(MINIDUMP_MEMORY_INFO_LIST); + memory_info_list_base_.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO); + memory_info_list_base_.NumberOfEntries = items_.size(); + + return true; +} + +size_t MinidumpMemoryInfoListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return sizeof(memory_info_list_base_) + sizeof(items_[0]) * items_.size(); +} + +std::vector<internal::MinidumpWritable*> +MinidumpMemoryInfoListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + return std::vector<internal::MinidumpWritable*>(); +} + +bool MinidumpMemoryInfoListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &memory_info_list_base_; + iov.iov_len = sizeof(memory_info_list_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (const auto& minidump_memory_info : items_) { + iov.iov_base = &minidump_memory_info; + iov.iov_len = sizeof(minidump_memory_info); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpMemoryInfoListWriter::StreamType() const { + return kMinidumpStreamTypeMemoryInfoList; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer.h b/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer.h new file mode 100644 index 0000000..6f6ea59 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer.h
@@ -0,0 +1,71 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class MemoryMapRegionSnapshot; +class MinidumpContextWriter; +class MinidumpMemoryListWriter; +class MinidumpMemoryWriter; + +//! \brief The writer for a MINIDUMP_MEMORY_INFO_LIST stream in a minidump file, +//! containing a list of MINIDUMP_MEMORY_INFO objects. +class MinidumpMemoryInfoListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpMemoryInfoListWriter(); + ~MinidumpMemoryInfoListWriter() override; + + //! \brief Initializes a MINIDUMP_MEMORY_INFO_LIST based on \a memory_map. + //! + //! \param[in] memory_map The vector of memory map region snapshots to use as + //! source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot( + const std::vector<const MemoryMapRegionSnapshot*>& memory_map); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<internal::MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_MEMORY_INFO_LIST memory_info_list_base_; + std::vector<MINIDUMP_MEMORY_INFO> items_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryInfoListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc new file mode 100644 index 0000000..a1073f8 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc
@@ -0,0 +1,127 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_memory_info_writer.h" + +#include <string> + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_map_region_snapshot.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +// The memory info list is expected to be the only stream. +void GetMemoryInfoListStream( + const std::string& file_contents, + const MINIDUMP_MEMORY_INFO_LIST** memory_info_list) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kMemoryInfoListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + const size_t kDirectoryIndex = 0; + + ASSERT_EQ(kMinidumpStreamTypeMemoryInfoList, + directory[kDirectoryIndex].StreamType); + EXPECT_EQ(kMemoryInfoListStreamOffset, + directory[kDirectoryIndex].Location.Rva); + + *memory_info_list = + MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>( + file_contents, directory[kDirectoryIndex].Location); + ASSERT_TRUE(*memory_info_list); +} + +TEST(MinidumpMemoryInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto memory_info_list_writer = + make_scoped_ptr(new MinidumpMemoryInfoListWriter()); + minidump_file_writer.AddStream(crashpad::move(memory_info_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_INFO_LIST), + string_file.string().size()); + + const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryInfoListStream(string_file.string(), &memory_info_list)); + + EXPECT_EQ(0u, memory_info_list->NumberOfEntries); +} + +TEST(MinidumpMemoryInfoWriter, OneRegion) { + MinidumpFileWriter minidump_file_writer; + auto memory_info_list_writer = + make_scoped_ptr(new MinidumpMemoryInfoListWriter()); + + auto memory_map_region = make_scoped_ptr(new TestMemoryMapRegionSnapshot()); + + MINIDUMP_MEMORY_INFO mmi = {0}; + mmi.BaseAddress = 0x12340000; + mmi.AllocationBase = 0x12000000; + mmi.AllocationProtect = PAGE_READWRITE; + mmi.RegionSize = 0x6000; + mmi.State = MEM_COMMIT; + mmi.Protect = PAGE_NOACCESS; + mmi.Type = MEM_PRIVATE; + memory_map_region->SetMindumpMemoryInfo(mmi); + + std::vector<const MemoryMapRegionSnapshot*> memory_map; + memory_map.push_back(memory_map_region.get()); + memory_info_list_writer->InitializeFromSnapshot(memory_map); + + minidump_file_writer.AddStream(crashpad::move(memory_info_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_INFO_LIST) + + sizeof(MINIDUMP_MEMORY_INFO), + string_file.string().size()); + + const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryInfoListStream(string_file.string(), &memory_info_list)); + + EXPECT_EQ(1u, memory_info_list->NumberOfEntries); + const MINIDUMP_MEMORY_INFO* memory_info = + reinterpret_cast<const MINIDUMP_MEMORY_INFO*>(&memory_info_list[1]); + EXPECT_EQ(mmi.BaseAddress, memory_info->BaseAddress); + EXPECT_EQ(mmi.AllocationBase, memory_info->AllocationBase); + EXPECT_EQ(mmi.AllocationProtect, memory_info->AllocationProtect); + EXPECT_EQ(mmi.RegionSize, memory_info->RegionSize); + EXPECT_EQ(mmi.State, memory_info->State); + EXPECT_EQ(mmi.Protect, memory_info->Protect); + EXPECT_EQ(mmi.Type, memory_info->Type); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc new file mode 100644 index 0000000..1c9b2858 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc
@@ -0,0 +1,261 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_memory_writer.h" + +#include "base/auto_reset.h" +#include "base/logging.h" +#include "snapshot/memory_snapshot.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace { + +class SnapshotMinidumpMemoryWriter final : public MinidumpMemoryWriter, + public MemorySnapshot::Delegate { + public: + explicit SnapshotMinidumpMemoryWriter(const MemorySnapshot* memory_snapshot) + : MinidumpMemoryWriter(), + MemorySnapshot::Delegate(), + memory_snapshot_(memory_snapshot), + file_writer_(nullptr) { + } + + ~SnapshotMinidumpMemoryWriter() override {} + + // MemorySnapshot::Delegate: + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(size, MemoryRangeSize()); + return file_writer_->Write(data, size); + } + + protected: + // MinidumpMemoryWriter: + + bool WriteObject(FileWriterInterface* file_writer) override { + DCHECK_EQ(state(), kStateWritable); + DCHECK(!file_writer_); + + base::AutoReset<FileWriterInterface*> file_writer_reset(&file_writer_, + file_writer); + + // This will result in MemorySnapshotDelegateRead() being called. + return memory_snapshot_->Read(this); + } + + uint64_t MemoryRangeBaseAddress() const override { + DCHECK_EQ(state(), kStateFrozen); + return memory_snapshot_->Address(); + } + + size_t MemoryRangeSize() const override { + DCHECK_GE(state(), kStateFrozen); + return memory_snapshot_->Size(); + } + + private: + const MemorySnapshot* memory_snapshot_; + FileWriterInterface* file_writer_; + + DISALLOW_COPY_AND_ASSIGN(SnapshotMinidumpMemoryWriter); +}; + +} // namespace + +MinidumpMemoryWriter::~MinidumpMemoryWriter() { +} + +scoped_ptr<MinidumpMemoryWriter> MinidumpMemoryWriter::CreateFromSnapshot( + const MemorySnapshot* memory_snapshot) { + return make_scoped_ptr(new SnapshotMinidumpMemoryWriter(memory_snapshot)); +} + +const MINIDUMP_MEMORY_DESCRIPTOR* +MinidumpMemoryWriter::MinidumpMemoryDescriptor() const { + DCHECK_EQ(state(), kStateWritable); + + return &memory_descriptor_; +} + +void MinidumpMemoryWriter::RegisterMemoryDescriptor( + MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor) { + DCHECK_LE(state(), kStateFrozen); + + registered_memory_descriptors_.push_back(memory_descriptor); + RegisterLocationDescriptor(&memory_descriptor->Memory); +} + +MinidumpMemoryWriter::MinidumpMemoryWriter() + : MinidumpWritable(), + memory_descriptor_(), + registered_memory_descriptors_() { +} + +bool MinidumpMemoryWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + RegisterMemoryDescriptor(&memory_descriptor_); + + return true; +} + +size_t MinidumpMemoryWriter::Alignment() { + DCHECK_GE(state(), kStateFrozen); + + return 16; +} + +size_t MinidumpMemoryWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return MemoryRangeSize(); +} + +bool MinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { + DCHECK_EQ(state(), kStateFrozen); + + // There will always be at least one registered descriptor, the one for this + // object’s own memory_descriptor_ field. + DCHECK_GE(registered_memory_descriptors_.size(), 1u); + + uint64_t base_address = MemoryRangeBaseAddress(); + decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address; + if (!AssignIfInRange(&local_address, base_address)) { + LOG(ERROR) << "base_address " << base_address << " out of range"; + return false; + } + + for (MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor : + registered_memory_descriptors_) { + memory_descriptor->StartOfMemoryRange = local_address; + } + + return MinidumpWritable::WillWriteAtOffsetImpl(offset); +} + +internal::MinidumpWritable::Phase MinidumpMemoryWriter::WritePhase() { + // Memory dumps are large and are unlikely to be consumed in their entirety. + // Data accesses are expected to be sparse and sporadic, and are expected to + // occur after all of the other structural and informational data from the + // minidump file has been read. Put memory dumps at the end of the minidump + // file to improve spatial locality. + return kPhaseLate; +} + +MinidumpMemoryListWriter::MinidumpMemoryListWriter() + : MinidumpStreamWriter(), + memory_writers_(), + children_(), + memory_list_base_() { +} + +MinidumpMemoryListWriter::~MinidumpMemoryListWriter() { +} + +void MinidumpMemoryListWriter::AddFromSnapshot( + const std::vector<const MemorySnapshot*>& memory_snapshots) { + DCHECK_EQ(state(), kStateMutable); + + for (const MemorySnapshot* memory_snapshot : memory_snapshots) { + scoped_ptr<MinidumpMemoryWriter> memory = + MinidumpMemoryWriter::CreateFromSnapshot(memory_snapshot); + AddMemory(crashpad::move(memory)); + } +} + +void MinidumpMemoryListWriter::AddMemory( + scoped_ptr<MinidumpMemoryWriter> memory_writer) { + DCHECK_EQ(state(), kStateMutable); + + AddExtraMemory(memory_writer.get()); + children_.push_back(memory_writer.release()); +} + +void MinidumpMemoryListWriter::AddExtraMemory( + MinidumpMemoryWriter* memory_writer) { + DCHECK_EQ(state(), kStateMutable); + + memory_writers_.push_back(memory_writer); +} + +bool MinidumpMemoryListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t memory_region_count = memory_writers_.size(); + CHECK_LE(children_.size(), memory_region_count); + + if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges, + memory_region_count)) { + LOG(ERROR) << "memory_region_count " << memory_region_count + << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpMemoryListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_LE(children_.size(), memory_writers_.size()); + + return sizeof(memory_list_base_) + + memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); +} + +std::vector<internal::MinidumpWritable*> MinidumpMemoryListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_LE(children_.size(), memory_writers_.size()); + + std::vector<MinidumpWritable*> children; + for (MinidumpMemoryWriter* child : children_) { + children.push_back(child); + } + + return children; +} + +bool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &memory_list_base_; + iov.iov_len = sizeof(memory_list_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (const MinidumpMemoryWriter* memory_writer : memory_writers_) { + iov.iov_base = memory_writer->MinidumpMemoryDescriptor(); + iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpMemoryListWriter::StreamType() const { + return kMinidumpStreamTypeMemoryList; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h b/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h new file mode 100644 index 0000000..6b5f2df --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h
@@ -0,0 +1,193 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <sys/types.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "util/file/file_io.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class MemorySnapshot; + +//! \brief The base class for writers of memory ranges pointed to by +//! MINIDUMP_MEMORY_DESCRIPTOR objects in a minidump file. +//! +//! This is an abstract base class because users are expected to provide their +//! own implementations that, when possible, obtain the memory contents +//! on-demand in their WriteObject() methods. Memory ranges may be large, and +//! the alternative construction would require the contents of multiple ranges +//! to be held in memory simultaneously while a minidump file is being written. +class MinidumpMemoryWriter : public internal::MinidumpWritable { + public: + ~MinidumpMemoryWriter() override; + + //! \brief Creates a concrete initialized MinidumpMemoryWriter based on \a + //! memory_snapshot. + //! + //! \param[in] memory_snapshot The memory snapshot to use as source data. + //! + //! \return An object of a MinidumpMemoryWriter subclass initialized using the + //! source data in \a memory_snapshot. + static scoped_ptr<MinidumpMemoryWriter> CreateFromSnapshot( + const MemorySnapshot* memory_snapshot); + + //! \brief Returns a MINIDUMP_MEMORY_DESCRIPTOR referencing the data that this + //! object writes. + //! + //! This method is expected to be called by a MinidumpMemoryListWriter in + //! order to obtain a MINIDUMP_MEMORY_DESCRIPTOR to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_MEMORY_DESCRIPTOR* MinidumpMemoryDescriptor() const; + + //! \brief Registers a memory descriptor as one that should point to the + //! object on which this method is called. + //! + //! This method is expected to be called by objects of other classes, when + //! those other classes have their own memory descriptors that need to point + //! to memory ranges within a minidump file. MinidumpThreadWriter is one such + //! class. This method is public for this reason, otherwise it would suffice + //! to be private. + //! + //! \note Valid in #kStateFrozen or any preceding state. + void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor); + + protected: + MinidumpMemoryWriter(); + + //! \brief Returns the base address of the memory region in the address space + //! of the process that the snapshot describes. + //! + //! \note This method will only be called in #kStateFrozen. + virtual uint64_t MemoryRangeBaseAddress() const = 0; + + //! \brief Returns the size of the memory region in bytes. + //! + //! \note This method will only be called in #kStateFrozen or a subsequent + //! state. + virtual size_t MemoryRangeSize() const = 0; + + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() final; + + //! \brief Returns the object’s desired byte-boundary alignment. + //! + //! Memory regions are aligned to a 16-byte boundary. The actual alignment + //! requirements of any data within the memory region are unknown, and may be + //! more or less strict than this depending on the platform. + //! + //! \return `16`. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + size_t Alignment() override; + + bool WillWriteAtOffsetImpl(FileOffset offset) override; + + //! \brief Returns the object’s desired write phase. + //! + //! Memory regions are written at the end of minidump files, because it is + //! expected that unlike most other data in a minidump file, the contents of + //! memory regions will be accessed sparsely. + //! + //! \return #kPhaseLate. + //! + //! \note Valid in any state. + Phase WritePhase() final; + + private: + MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_; + + // weak + std::vector<MINIDUMP_MEMORY_DESCRIPTOR*> registered_memory_descriptors_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryWriter); +}; + +//! \brief The writer for a MINIDUMP_MEMORY_LIST stream in a minidump file, +//! containing a list of MINIDUMP_MEMORY_DESCRIPTOR objects. +class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpMemoryListWriter(); + ~MinidumpMemoryListWriter() override; + + //! \brief Adds a concrete initialized MinidumpMemoryWriter for each memory + //! snapshot in \a memory_snapshots to the MINIDUMP_MEMORY_LIST. + //! + //! Memory snapshots are added in the fashion of AddMemory(). + //! + //! \param[in] memory_snapshots The memory snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. + void AddFromSnapshot( + const std::vector<const MemorySnapshot*>& memory_snapshots); + + //! \brief Adds a MinidumpMemoryWriter to the MINIDUMP_MEMORY_LIST. + //! + //! This object takes ownership of \a memory_writer and becomes its parent in + //! the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddMemory(scoped_ptr<MinidumpMemoryWriter> memory_writer); + + //! \brief Adds a MinidumpMemoryWriter that’s a child of another + //! internal::MinidumpWritable object to the MINIDUMP_MEMORY_LIST. + //! + //! \a memory_writer does not become a child of this object, but the + //! MINIDUMP_MEMORY_LIST will still contain a MINIDUMP_MEMORY_DESCRIPTOR for + //! it. \a memory_writer must be a child of another object in the + //! internal::MinidumpWritable tree. + //! + //! This method exists to be called by objects that have their own + //! MinidumpMemoryWriter children but wish for them to also appear in the + //! minidump file’s MINIDUMP_MEMORY_LIST. MinidumpThreadWriter, which has a + //! MinidumpMemoryWriter for thread stack memory, is an example. + //! + //! \note Valid in #kStateMutable. + void AddExtraMemory(MinidumpMemoryWriter* memory_writer); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + std::vector<MinidumpMemoryWriter*> memory_writers_; // weak + PointerVector<MinidumpMemoryWriter> children_; + MINIDUMP_MEMORY_LIST memory_list_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc new file mode 100644 index 0000000..38d3cb2 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
@@ -0,0 +1,364 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_memory_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include "base/basictypes.h" +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_memory_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpStreamType kBogusStreamType = + static_cast<MinidumpStreamType>(1234); + +// expected_streams is the expected number of streams in the file. The memory +// list must be the last stream. If there is another stream, it must come first, +// have stream type kBogusStreamType, and have zero-length data. +void GetMemoryListStream(const std::string& file_contents, + const MINIDUMP_MEMORY_LIST** memory_list, + const uint32_t expected_streams) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kMemoryListStreamOffset = + kDirectoryOffset + expected_streams * sizeof(MINIDUMP_DIRECTORY); + const size_t kMemoryDescriptorsOffset = + kMemoryListStreamOffset + sizeof(MINIDUMP_MEMORY_LIST); + + ASSERT_GE(file_contents.size(), kMemoryDescriptorsOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, expected_streams, 0)); + ASSERT_TRUE(directory); + + size_t directory_index = 0; + if (expected_streams > 1) { + ASSERT_EQ(kBogusStreamType, directory[directory_index].StreamType); + ASSERT_EQ(0u, directory[directory_index].Location.DataSize); + ASSERT_EQ(kMemoryListStreamOffset, directory[directory_index].Location.Rva); + ++directory_index; + } + + ASSERT_EQ(kMinidumpStreamTypeMemoryList, + directory[directory_index].StreamType); + EXPECT_EQ(kMemoryListStreamOffset, directory[directory_index].Location.Rva); + + *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>( + file_contents, directory[directory_index].Location); + ASSERT_TRUE(memory_list); +} + +TEST(MinidumpMemoryWriter, EmptyMemoryList) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST), + string_file.string().size()); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + EXPECT_EQ(0u, memory_list->NumberOfMemoryRanges); +} + +TEST(MinidumpMemoryWriter, OneMemoryRegion) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + + const uint64_t kBaseAddress = 0xfedcba9876543210; + const uint64_t kSize = 0x1000; + const uint8_t kValue = 'm'; + + auto memory_writer = make_scoped_ptr( + new TestMinidumpMemoryWriter(kBaseAddress, kSize, kValue)); + memory_list_writer->AddMemory(crashpad::move(memory_writer)); + + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + expected.StartOfMemoryRange = kBaseAddress; + expected.Memory.DataSize = kSize; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[0], + string_file.string(), + kValue, + true); +} + +TEST(MinidumpMemoryWriter, TwoMemoryRegions) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + + const uint64_t kBaseAddress0 = 0xc0ffee; + const uint64_t kSize0 = 0x0100; + const uint8_t kValue0 = '6'; + const uint64_t kBaseAddress1 = 0xfac00fac; + const uint64_t kSize1 = 0x0200; + const uint8_t kValue1 = '!'; + + auto memory_writer_0 = make_scoped_ptr( + new TestMinidumpMemoryWriter(kBaseAddress0, kSize0, kValue0)); + memory_list_writer->AddMemory(crashpad::move(memory_writer_0)); + auto memory_writer_1 = make_scoped_ptr( + new TestMinidumpMemoryWriter(kBaseAddress1, kSize1, kValue1)); + memory_list_writer->AddMemory(crashpad::move(memory_writer_1)); + + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + EXPECT_EQ(2u, memory_list->NumberOfMemoryRanges); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + + { + SCOPED_TRACE("region 0"); + + expected.StartOfMemoryRange = kBaseAddress0; + expected.Memory.DataSize = kSize0; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[0], + string_file.string(), + kValue0, + false); + } + + { + SCOPED_TRACE("region 1"); + + expected.StartOfMemoryRange = kBaseAddress1; + expected.Memory.DataSize = kSize1; + expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva + + memory_list->MemoryRanges[0].Memory.DataSize; + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[1], + string_file.string(), + kValue1, + true); + } +} + +class TestMemoryStream final : public internal::MinidumpStreamWriter { + public: + TestMemoryStream(uint64_t base_address, size_t size, uint8_t value) + : MinidumpStreamWriter(), memory_(base_address, size, value) {} + + ~TestMemoryStream() override {} + + TestMinidumpMemoryWriter* memory() { + return &memory_; + } + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override { + return kBogusStreamType; + } + + protected: + // MinidumpWritable: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return 0; + } + + std::vector<MinidumpWritable*> Children() override { + EXPECT_GE(state(), kStateFrozen); + std::vector<MinidumpWritable*> children(1, memory()); + return children; + } + + bool WriteObject(FileWriterInterface* file_writer) override { + EXPECT_EQ(kStateWritable, state()); + return true; + } + + private: + TestMinidumpMemoryWriter memory_; + + DISALLOW_COPY_AND_ASSIGN(TestMemoryStream); +}; + +TEST(MinidumpMemoryWriter, ExtraMemory) { + // This tests MinidumpMemoryListWriter::AddExtraMemory(). That method adds + // a MinidumpMemoryWriter to the MinidumpMemoryListWriter without making the + // memory writer a child of the memory list writer. + MinidumpFileWriter minidump_file_writer; + + const uint64_t kBaseAddress0 = 0x1000; + const size_t kSize0 = 0x0400; + const uint8_t kValue0 = '1'; + auto test_memory_stream = + make_scoped_ptr(new TestMemoryStream(kBaseAddress0, kSize0, kValue0)); + + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + memory_list_writer->AddExtraMemory(test_memory_stream->memory()); + + minidump_file_writer.AddStream(crashpad::move(test_memory_stream)); + + const uint64_t kBaseAddress1 = 0x2000; + const size_t kSize1 = 0x0400; + const uint8_t kValue1 = 'm'; + + auto memory_writer = make_scoped_ptr( + new TestMinidumpMemoryWriter(kBaseAddress1, kSize1, kValue1)); + memory_list_writer->AddMemory(crashpad::move(memory_writer)); + + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 2)); + + EXPECT_EQ(2u, memory_list->NumberOfMemoryRanges); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + + { + SCOPED_TRACE("region 0"); + + expected.StartOfMemoryRange = kBaseAddress0; + expected.Memory.DataSize = kSize0; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[0], + string_file.string(), + kValue0, + false); + } + + { + SCOPED_TRACE("region 1"); + + expected.StartOfMemoryRange = kBaseAddress1; + expected.Memory.DataSize = kSize1; + expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva + + memory_list->MemoryRanges[0].Memory.DataSize; + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[1], + string_file.string(), + kValue1, + true); + } +} + +TEST(MinidumpMemoryWriter, AddFromSnapshot) { + MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {}; + uint8_t values[arraysize(expect_memory_descriptors)] = {}; + + expect_memory_descriptors[0].StartOfMemoryRange = 0; + expect_memory_descriptors[0].Memory.DataSize = 0x1000; + values[0] = 0x01; + + expect_memory_descriptors[1].StartOfMemoryRange = 0x1000; + expect_memory_descriptors[1].Memory.DataSize = 0x2000; + values[1] = 0xf4; + + expect_memory_descriptors[2].StartOfMemoryRange = 0x7654321000000000; + expect_memory_descriptors[2].Memory.DataSize = 0x800; + values[2] = 0xa9; + + PointerVector<TestMemorySnapshot> memory_snapshots_owner; + std::vector<const MemorySnapshot*> memory_snapshots; + for (size_t index = 0; + index < arraysize(expect_memory_descriptors); + ++index) { + TestMemorySnapshot* memory_snapshot = new TestMemorySnapshot(); + memory_snapshots_owner.push_back(memory_snapshot); + memory_snapshot->SetAddress( + expect_memory_descriptors[index].StartOfMemoryRange); + memory_snapshot->SetSize(expect_memory_descriptors[index].Memory.DataSize); + memory_snapshot->SetValue(values[index]); + memory_snapshots.push_back(memory_snapshot); + } + + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(3u, memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + values[index], + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc new file mode 100644 index 0000000..4d3b143 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
@@ -0,0 +1,369 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_misc_info_writer.h" + +#include <limits> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "minidump/minidump_writer_util.h" +#include "package.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "util/file/file_writer.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +#if defined(OS_MACOSX) +#include <AvailabilityMacros.h> +#endif + +namespace crashpad { +namespace { + +uint32_t TimevalToRoundedSeconds(const timeval& tv) { + uint32_t seconds = + InRangeCast<uint32_t>(tv.tv_sec, std::numeric_limits<uint32_t>::max()); + const int kMicrosecondsPerSecond = static_cast<int>(1E6); + if (tv.tv_usec >= kMicrosecondsPerSecond / 2 && + seconds != std::numeric_limits<uint32_t>::max()) { + ++seconds; + } + return seconds; +} + +// For MINIDUMP_MISC_INFO_4::BuildString. dbghelp only places OS version +// information here, but if a machine description is also available, this is the +// only reasonable place in a minidump file to put it. +std::string BuildString(const SystemSnapshot* system_snapshot) { + std::string os_version_full = system_snapshot->OSVersionFull(); + std::string machine_description = system_snapshot->MachineDescription(); + if (!os_version_full.empty()) { + if (!machine_description.empty()) { + return base::StringPrintf( + "%s; %s", os_version_full.c_str(), machine_description.c_str()); + } + return os_version_full; + } + return machine_description; +} + +#if defined(OS_MACOSX) +// Converts the value of the MAC_OS_VERSION_MIN_REQUIRED or +// MAC_OS_X_VERSION_MAX_ALLOWED macro from <AvailabilityMacros.h> to a number +// identifying the minor Mac OS X version that it represents. For example, with +// an argument of MAC_OS_X_VERSION_10_6, this function will return 6. +int AvailabilityVersionToMacOSXMinorVersion(int availability) { + // Through MAC_OS_X_VERSION_10_9, the minor version is the tens digit. + if (availability >= 1000 && availability <= 1099) { + return (availability / 10) % 10; + } + + // After MAC_OS_X_VERSION_10_9, the older format was insufficient to represent + // versions. Since then, the minor version is the thousands and hundreds + // digits. + if (availability >= 100000 && availability <= 109999) { + return (availability / 100) % 100; + } + + return 0; +} +#endif + +} // namespace + +namespace internal { + +// For MINIDUMP_MISC_INFO_4::DbgBldStr. dbghelp produces strings like +// “dbghelp.i386,6.3.9600.16520” and “dbghelp.amd64,6.3.9600.16520”. Mimic that +// format, and add the OS that wrote the minidump along with any relevant +// platform-specific data describing the compilation environment. +std::string MinidumpMiscInfoDebugBuildString() { + // Caution: the minidump file format only has room for 39 UTF-16 code units + // plus a UTF-16 NUL terminator. Don’t let strings get longer than this, or + // they will be truncated and a message will be logged. +#if defined(OS_MACOSX) + const char kOS[] = "mac"; +#elif defined(OS_LINUX) + const char kOS[] = "linux"; +#elif defined(OS_WIN) + const char kOS[] = "win"; +#else +#error define kOS for this operating system +#endif + +#if defined(ARCH_CPU_X86) + const char kCPU[] = "i386"; +#elif defined(ARCH_CPU_X86_64) + const char kCPU[] = "amd64"; +#else +#error define kCPU for this CPU +#endif + + std::string debug_build_string = base::StringPrintf("%s.%s,%s,%s", + PACKAGE_TARNAME, + kCPU, + PACKAGE_VERSION, + kOS); + +#if defined(OS_MACOSX) + debug_build_string += base::StringPrintf( + ",%d,%d", + AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MIN_REQUIRED), + AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MAX_ALLOWED)); +#endif + + return debug_build_string; +} + +} // namespace internal + +MinidumpMiscInfoWriter::MinidumpMiscInfoWriter() + : MinidumpStreamWriter(), misc_info_() { +} + +MinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() { +} + +void MinidumpMiscInfoWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(misc_info_.Flags1, 0u); + + SetProcessID(InRangeCast<uint32_t>(process_snapshot->ProcessID(), 0)); + + const SystemSnapshot* system_snapshot = process_snapshot->System(); + + uint64_t current_mhz; + uint64_t max_mhz; + system_snapshot->CPUFrequency(¤t_mhz, &max_mhz); + const uint32_t kHzPerMHz = static_cast<const uint32_t>(1E6); + SetProcessorPowerInfo( + InRangeCast<uint32_t>(current_mhz / kHzPerMHz, + std::numeric_limits<uint32_t>::max()), + InRangeCast<uint32_t>(max_mhz / kHzPerMHz, + std::numeric_limits<uint32_t>::max()), + 0, + 0, + 0); + + timeval start_time; + process_snapshot->ProcessStartTime(&start_time); + + timeval user_time; + timeval system_time; + process_snapshot->ProcessCPUTimes(&user_time, &system_time); + + // Round the resource usage fields to the nearest second, because the minidump + // format only has one-second resolution. The start_time field is truncated + // instead of rounded so that the process uptime is reflected more accurately + // when the start time is compared to the snapshot time in the + // MINIDUMP_HEADER, which is also truncated, not rounded. + uint32_t user_seconds = TimevalToRoundedSeconds(user_time); + uint32_t system_seconds = TimevalToRoundedSeconds(system_time); + + SetProcessTimes(start_time.tv_sec, user_seconds, system_seconds); + + // This determines the system’s time zone, which may be different than the + // process’ notion of the time zone. + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + system_snapshot->TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + + // standard_offset_seconds is seconds east of UTC, but the minidump file wants + // minutes west of UTC. daylight_offset_seconds is also seconds east of UTC, + // but the minidump file wants minutes west of the standard offset. The empty + // ({}) arguments are for the transition times in and out of daylight saving + // time. These are not determined because no API exists to do so, and the + // transition times may vary from year to year. + SetTimeZone(dst_status, + standard_offset_seconds / -60, + standard_name, + {}, + 0, + daylight_name, + {}, + (standard_offset_seconds - daylight_offset_seconds) / 60); + + SetBuildString(BuildString(system_snapshot), + internal::MinidumpMiscInfoDebugBuildString()); +} + +void MinidumpMiscInfoWriter::SetProcessID(uint32_t process_id) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessId = process_id; + misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_ID; +} + +void MinidumpMiscInfoWriter::SetProcessTimes(time_t process_create_time, + uint32_t process_user_time, + uint32_t process_kernel_time) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&misc_info_.ProcessCreateTime, + process_create_time); + + misc_info_.ProcessUserTime = process_user_time; + misc_info_.ProcessKernelTime = process_kernel_time; + misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_TIMES; +} + +void MinidumpMiscInfoWriter::SetProcessorPowerInfo( + uint32_t processor_max_mhz, + uint32_t processor_current_mhz, + uint32_t processor_mhz_limit, + uint32_t processor_max_idle_state, + uint32_t processor_current_idle_state) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessorMaxMhz = processor_max_mhz; + misc_info_.ProcessorCurrentMhz = processor_current_mhz; + misc_info_.ProcessorMhzLimit = processor_mhz_limit; + misc_info_.ProcessorMaxIdleState = processor_max_idle_state; + misc_info_.ProcessorCurrentIdleState = processor_current_idle_state; + misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESSOR_POWER_INFO; +} + +void MinidumpMiscInfoWriter::SetProcessIntegrityLevel( + uint32_t process_integrity_level) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessIntegrityLevel = process_integrity_level; + misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_INTEGRITY; +} + +void MinidumpMiscInfoWriter::SetProcessExecuteFlags( + uint32_t process_execute_flags) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessExecuteFlags = process_execute_flags; + misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS; +} + +void MinidumpMiscInfoWriter::SetProtectedProcess(uint32_t protected_process) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProtectedProcess = protected_process; + misc_info_.Flags1 |= MINIDUMP_MISC3_PROTECTED_PROCESS; +} + +void MinidumpMiscInfoWriter::SetTimeZone(uint32_t time_zone_id, + int32_t bias, + const std::string& standard_name, + const SYSTEMTIME& standard_date, + int32_t standard_bias, + const std::string& daylight_name, + const SYSTEMTIME& daylight_date, + int32_t daylight_bias) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.TimeZoneId = time_zone_id; + misc_info_.TimeZone.Bias = bias; + + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + misc_info_.TimeZone.StandardName, + arraysize(misc_info_.TimeZone.StandardName), + standard_name); + + misc_info_.TimeZone.StandardDate = standard_date; + misc_info_.TimeZone.StandardBias = standard_bias; + + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + misc_info_.TimeZone.DaylightName, + arraysize(misc_info_.TimeZone.DaylightName), + daylight_name); + + misc_info_.TimeZone.DaylightDate = daylight_date; + misc_info_.TimeZone.DaylightBias = daylight_bias; + + misc_info_.Flags1 |= MINIDUMP_MISC3_TIMEZONE; +} + +void MinidumpMiscInfoWriter::SetBuildString( + const std::string& build_string, + const std::string& debug_build_string) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING; + + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + misc_info_.BuildString, arraysize(misc_info_.BuildString), build_string); + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + misc_info_.DbgBldStr, + arraysize(misc_info_.DbgBldStr), + debug_build_string); +} + +bool MinidumpMiscInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t size = CalculateSizeOfObjectFromFlags(); + if (!AssignIfInRange(&misc_info_.SizeOfInfo, size)) { + LOG(ERROR) << "size " << size << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpMiscInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return CalculateSizeOfObjectFromFlags(); +} + +bool MinidumpMiscInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&misc_info_, CalculateSizeOfObjectFromFlags()); +} + +MinidumpStreamType MinidumpMiscInfoWriter::StreamType() const { + return kMinidumpStreamTypeMiscInfo; +} + +size_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const { + DCHECK_GE(state(), kStateFrozen); + + if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) { + return sizeof(MINIDUMP_MISC_INFO_4); + } + if (misc_info_.Flags1 & + (MINIDUMP_MISC3_PROCESS_INTEGRITY | MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS | + MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC3_PROTECTED_PROCESS)) { + return sizeof(MINIDUMP_MISC_INFO_3); + } + if (misc_info_.Flags1 & MINIDUMP_MISC1_PROCESSOR_POWER_INFO) { + return sizeof(MINIDUMP_MISC_INFO_2); + } + return sizeof(MINIDUMP_MISC_INFO); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h new file mode 100644 index 0000000..b7c3549 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h
@@ -0,0 +1,132 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <sys/types.h> +#include <time.h> + +#include <string> + +#include "base/basictypes.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class ProcessSnapshot; + +namespace internal { + +//! \brief Returns the string to set in MINIDUMP_MISC_INFO_4::DbgBldStr. +//! +//! dbghelp produces strings like `"dbghelp.i386,6.3.9600.16520"` and +//! `"dbghelp.amd64,6.3.9600.16520"`. This function mimics that format, and adds +//! the OS that wrote the minidump along with any relevant platform-specific +//! data describing the compilation environment. +//! +//! This function is an implementation detail of +//! MinidumpMiscInfoWriter::InitializeFromSnapshot() and is only exposed for +//! testing purposes. +std::string MinidumpMiscInfoDebugBuildString(); + +} // namespace internal + +//! \brief The writer for a stream in the MINIDUMP_MISC_INFO family in a +//! minidump file. +//! +//! The actual stream written will be a MINIDUMP_MISC_INFO, +//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or MINIDUMP_MISC_INFO_4 stream. +//! Later versions of MINIDUMP_MISC_INFO are supersets of earlier versions. The +//! earliest version that supports all of the information that an object of this +//! class contains will be used. +class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpMiscInfoWriter(); + ~MinidumpMiscInfoWriter() override; + + //! \brief Initializes MINIDUMP_MISC_INFO_N based on \a process_snapshot. + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + + //! \brief Sets the field referenced by #MINIDUMP_MISC1_PROCESS_ID. + void SetProcessID(uint32_t process_id); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESS_TIMES. + void SetProcessTimes(time_t process_create_time, + uint32_t process_user_time, + uint32_t process_kernel_time); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESSOR_POWER_INFO. + void SetProcessorPowerInfo(uint32_t processor_max_mhz, + uint32_t processor_current_mhz, + uint32_t processor_mhz_limit, + uint32_t processor_max_idle_state, + uint32_t processor_current_idle_state); + + //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_INTEGRITY. + void SetProcessIntegrityLevel(uint32_t process_integrity_level); + + //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS. + void SetProcessExecuteFlags(uint32_t process_execute_flags); + + //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROTECTED_PROCESS. + void SetProtectedProcess(uint32_t protected_process); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC3_TIMEZONE. + void SetTimeZone(uint32_t time_zone_id, + int32_t bias, + const std::string& standard_name, + const SYSTEMTIME& standard_date, + int32_t standard_bias, + const std::string& daylight_name, + const SYSTEMTIME& daylight_date, + int32_t daylight_bias); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC4_BUILDSTRING. + void SetBuildString(const std::string& build_string, + const std::string& debug_build_string); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + MinidumpStreamType StreamType() const override; + + private: + //! \brief Returns the size of the object to be written based on + //! MINIDUMP_MISC_INFO_N::Flags1. + //! + //! The smallest defined structure type in the MINIDUMP_MISC_INFO family that + //! can hold all of the data that has been populated will be used. + size_t CalculateSizeOfObjectFromFlags() const; + + MINIDUMP_MISC_INFO_N misc_info_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMiscInfoWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc new file mode 100644 index 0000000..64825ad --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
@@ -0,0 +1,724 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_misc_info_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <string.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_process_snapshot.h" +#include "snapshot/test/test_system_snapshot.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" +#include "util/stdlib/strlcpy.h" + +namespace crashpad { +namespace test { +namespace { + +template <typename T> +void GetMiscInfoStream(const std::string& file_contents, const T** misc_info) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kMiscInfoStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kMiscInfoStreamSize = sizeof(T); + const size_t kFileSize = kMiscInfoStreamOffset + kMiscInfoStreamSize; + + ASSERT_EQ(kFileSize, file_contents.size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeMiscInfo, directory[0].StreamType); + EXPECT_EQ(kMiscInfoStreamOffset, directory[0].Location.Rva); + + *misc_info = MinidumpWritableAtLocationDescriptor<T>(file_contents, + directory[0].Location); + ASSERT_TRUE(misc_info); +} + +void ExpectNULPaddedString16Equal(const base::char16* expected, + const base::char16* observed, + size_t size) { + base::string16 expected_string(expected, size); + base::string16 observed_string(observed, size); + EXPECT_EQ(expected_string, observed_string); +} + +void ExpectSystemTimeEqual(const SYSTEMTIME* expected, + const SYSTEMTIME* observed) { + EXPECT_EQ(expected->wYear, observed->wYear); + EXPECT_EQ(expected->wMonth, observed->wMonth); + EXPECT_EQ(expected->wDayOfWeek, observed->wDayOfWeek); + EXPECT_EQ(expected->wDay, observed->wDay); + EXPECT_EQ(expected->wHour, observed->wHour); + EXPECT_EQ(expected->wMinute, observed->wMinute); + EXPECT_EQ(expected->wSecond, observed->wSecond); + EXPECT_EQ(expected->wMilliseconds, observed->wMilliseconds); +} + +template <typename T> +void ExpectMiscInfoEqual(const T* expected, const T* observed); + +template <> +void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO>( + const MINIDUMP_MISC_INFO* expected, + const MINIDUMP_MISC_INFO* observed) { + EXPECT_EQ(expected->Flags1, observed->Flags1); + EXPECT_EQ(expected->ProcessId, observed->ProcessId); + EXPECT_EQ(expected->ProcessCreateTime, observed->ProcessCreateTime); + EXPECT_EQ(expected->ProcessUserTime, observed->ProcessUserTime); + EXPECT_EQ(expected->ProcessKernelTime, observed->ProcessKernelTime); +} + +template <> +void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_2>( + const MINIDUMP_MISC_INFO_2* expected, + const MINIDUMP_MISC_INFO_2* observed) { + ExpectMiscInfoEqual<MINIDUMP_MISC_INFO>( + reinterpret_cast<const MINIDUMP_MISC_INFO*>(expected), + reinterpret_cast<const MINIDUMP_MISC_INFO*>(observed)); + EXPECT_EQ(expected->ProcessorMaxMhz, observed->ProcessorMaxMhz); + EXPECT_EQ(expected->ProcessorCurrentMhz, observed->ProcessorCurrentMhz); + EXPECT_EQ(expected->ProcessorMhzLimit, observed->ProcessorMhzLimit); + EXPECT_EQ(expected->ProcessorMaxIdleState, observed->ProcessorMaxIdleState); + EXPECT_EQ(expected->ProcessorCurrentIdleState, + observed->ProcessorCurrentIdleState); +} + +template <> +void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_3>( + const MINIDUMP_MISC_INFO_3* expected, + const MINIDUMP_MISC_INFO_3* observed) { + ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_2>( + reinterpret_cast<const MINIDUMP_MISC_INFO_2*>(expected), + reinterpret_cast<const MINIDUMP_MISC_INFO_2*>(observed)); + EXPECT_EQ(expected->ProcessIntegrityLevel, observed->ProcessIntegrityLevel); + EXPECT_EQ(expected->ProcessExecuteFlags, observed->ProcessExecuteFlags); + EXPECT_EQ(expected->ProtectedProcess, observed->ProtectedProcess); + EXPECT_EQ(expected->TimeZoneId, observed->TimeZoneId); + EXPECT_EQ(expected->TimeZone.Bias, observed->TimeZone.Bias); + { + SCOPED_TRACE("Standard"); + ExpectNULPaddedString16Equal(expected->TimeZone.StandardName, + observed->TimeZone.StandardName, + arraysize(expected->TimeZone.StandardName)); + ExpectSystemTimeEqual(&expected->TimeZone.StandardDate, + &observed->TimeZone.StandardDate); + EXPECT_EQ(expected->TimeZone.StandardBias, observed->TimeZone.StandardBias); + } + { + SCOPED_TRACE("Daylight"); + ExpectNULPaddedString16Equal(expected->TimeZone.DaylightName, + observed->TimeZone.DaylightName, + arraysize(expected->TimeZone.DaylightName)); + ExpectSystemTimeEqual(&expected->TimeZone.DaylightDate, + &observed->TimeZone.DaylightDate); + EXPECT_EQ(expected->TimeZone.DaylightBias, observed->TimeZone.DaylightBias); + } +} + +template <> +void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>( + const MINIDUMP_MISC_INFO_4* expected, + const MINIDUMP_MISC_INFO_4* observed) { + ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_3>( + reinterpret_cast<const MINIDUMP_MISC_INFO_3*>(expected), + reinterpret_cast<const MINIDUMP_MISC_INFO_3*>(observed)); + { + SCOPED_TRACE("BuildString"); + ExpectNULPaddedString16Equal(expected->BuildString, + observed->BuildString, + arraysize(expected->BuildString)); + } + { + SCOPED_TRACE("DbgBldStr"); + ExpectNULPaddedString16Equal(expected->DbgBldStr, + observed->DbgBldStr, + arraysize(expected->DbgBldStr)); + } +} + +TEST(MinidumpMiscInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO expected = {}; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessId) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kProcessId = 12345; + + misc_info_writer->SetProcessID(kProcessId); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO expected = {}; + expected.Flags1 = MINIDUMP_MISC1_PROCESS_ID; + expected.ProcessId = kProcessId; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessTimes) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const time_t kProcessCreateTime = 0x15252f00; + const uint32_t kProcessUserTime = 10; + const uint32_t kProcessKernelTime = 5; + + misc_info_writer->SetProcessTimes( + kProcessCreateTime, kProcessUserTime, kProcessKernelTime); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO expected = {}; + expected.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES; + expected.ProcessCreateTime = kProcessCreateTime; + expected.ProcessUserTime = kProcessUserTime; + expected.ProcessKernelTime = kProcessKernelTime; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessorPowerInfo) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kProcessorMaxMhz = 2800; + const uint32_t kProcessorCurrentMhz = 2300; + const uint32_t kProcessorMhzLimit = 3300; + const uint32_t kProcessorMaxIdleState = 5; + const uint32_t kProcessorCurrentIdleState = 1; + + misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz, + kProcessorCurrentMhz, + kProcessorMhzLimit, + kProcessorMaxIdleState, + kProcessorCurrentIdleState); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_2* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_2 expected = {}; + expected.Flags1 = MINIDUMP_MISC1_PROCESSOR_POWER_INFO; + expected.ProcessorMaxMhz = kProcessorMaxMhz; + expected.ProcessorCurrentMhz = kProcessorCurrentMhz; + expected.ProcessorMhzLimit = kProcessorMhzLimit; + expected.ProcessorMaxIdleState = kProcessorMaxIdleState; + expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessIntegrityLevel) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kProcessIntegrityLevel = 0x2000; + + misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_PROCESS_INTEGRITY; + expected.ProcessIntegrityLevel = kProcessIntegrityLevel; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessExecuteFlags) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kProcessExecuteFlags = 0x13579bdf; + + misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS; + expected.ProcessExecuteFlags = kProcessExecuteFlags; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProtectedProcess) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kProtectedProcess = 1; + + misc_info_writer->SetProtectedProcess(kProtectedProcess); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_PROTECTED_PROCESS; + expected.ProtectedProcess = kProtectedProcess; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, TimeZone) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kTimeZoneId = 2; + const int32_t kBias = 300; + const char kStandardName[] = "EST"; + const SYSTEMTIME kStandardDate = {0, 11, 1, 0, 2, 0, 0, 0}; + const int32_t kStandardBias = 0; + const char kDaylightName[] = "EDT"; + const SYSTEMTIME kDaylightDate = {0, 3, 2, 0, 2, 0, 0, 0}; + const int32_t kDaylightBias = -60; + + misc_info_writer->SetTimeZone(kTimeZoneId, + kBias, + kStandardName, + kStandardDate, + kStandardBias, + kDaylightName, + kDaylightDate, + kDaylightBias); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_TIMEZONE; + expected.TimeZoneId = kTimeZoneId; + expected.TimeZone.Bias = kBias; + base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName); + c16lcpy(expected.TimeZone.StandardName, + standard_name_utf16.c_str(), + arraysize(expected.TimeZone.StandardName)); + memcpy(&expected.TimeZone.StandardDate, + &kStandardDate, + sizeof(expected.TimeZone.StandardDate)); + expected.TimeZone.StandardBias = kStandardBias; + base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName); + c16lcpy(expected.TimeZone.DaylightName, + daylight_name_utf16.c_str(), + arraysize(expected.TimeZone.DaylightName)); + memcpy(&expected.TimeZone.DaylightDate, + &kDaylightDate, + sizeof(expected.TimeZone.DaylightDate)); + expected.TimeZone.DaylightBias = kDaylightBias; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, TimeZoneStringsOverflow) { + // This test makes sure that the time zone name strings are truncated properly + // to the widths of their fields. + + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kTimeZoneId = 2; + const int32_t kBias = 300; + MINIDUMP_MISC_INFO_N tmp; + ALLOW_UNUSED_LOCAL(tmp); + std::string standard_name(arraysize(tmp.TimeZone.StandardName) + 1, 's'); + const int32_t kStandardBias = 0; + std::string daylight_name(arraysize(tmp.TimeZone.DaylightName), 'd'); + const int32_t kDaylightBias = -60; + + // Test using kSystemTimeZero, because not all platforms will be able to + // provide daylight saving time transition times. + const SYSTEMTIME kSystemTimeZero = {}; + + misc_info_writer->SetTimeZone(kTimeZoneId, + kBias, + standard_name, + kSystemTimeZero, + kStandardBias, + daylight_name, + kSystemTimeZero, + kDaylightBias); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_TIMEZONE; + expected.TimeZoneId = kTimeZoneId; + expected.TimeZone.Bias = kBias; + base::string16 standard_name_utf16 = base::UTF8ToUTF16(standard_name); + c16lcpy(expected.TimeZone.StandardName, + standard_name_utf16.c_str(), + arraysize(expected.TimeZone.StandardName)); + memcpy(&expected.TimeZone.StandardDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.StandardDate)); + expected.TimeZone.StandardBias = kStandardBias; + base::string16 daylight_name_utf16 = base::UTF8ToUTF16(daylight_name); + c16lcpy(expected.TimeZone.DaylightName, + daylight_name_utf16.c_str(), + arraysize(expected.TimeZone.DaylightName)); + memcpy(&expected.TimeZone.DaylightDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.DaylightDate)); + expected.TimeZone.DaylightBias = kDaylightBias; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, BuildStrings) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const char kBuildString[] = "build string"; + const char kDebugBuildString[] = "debug build string"; + + misc_info_writer->SetBuildString(kBuildString, kDebugBuildString); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_4 expected = {}; + expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING; + base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString); + c16lcpy(expected.BuildString, + build_string_utf16.c_str(), + arraysize(expected.BuildString)); + base::string16 debug_build_string_utf16 = + base::UTF8ToUTF16(kDebugBuildString); + c16lcpy(expected.DbgBldStr, + debug_build_string_utf16.c_str(), + arraysize(expected.DbgBldStr)); + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) { + // This test makes sure that the build strings are truncated properly to the + // widths of their fields. + + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + MINIDUMP_MISC_INFO_4 tmp; + ALLOW_UNUSED_LOCAL(tmp); + std::string build_string(arraysize(tmp.BuildString) + 1, 'B'); + std::string debug_build_string(arraysize(tmp.DbgBldStr), 'D'); + + misc_info_writer->SetBuildString(build_string, debug_build_string); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_4 expected = {}; + expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING; + base::string16 build_string_utf16 = base::UTF8ToUTF16(build_string); + c16lcpy(expected.BuildString, + build_string_utf16.c_str(), + arraysize(expected.BuildString)); + base::string16 debug_build_string_utf16 = + base::UTF8ToUTF16(debug_build_string); + c16lcpy(expected.DbgBldStr, + debug_build_string_utf16.c_str(), + arraysize(expected.DbgBldStr)); + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, Everything) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + + const uint32_t kProcessId = 12345; + const time_t kProcessCreateTime = 0x15252f00; + const uint32_t kProcessUserTime = 10; + const uint32_t kProcessKernelTime = 5; + const uint32_t kProcessorMaxMhz = 2800; + const uint32_t kProcessorCurrentMhz = 2300; + const uint32_t kProcessorMhzLimit = 3300; + const uint32_t kProcessorMaxIdleState = 5; + const uint32_t kProcessorCurrentIdleState = 1; + const uint32_t kProcessIntegrityLevel = 0x2000; + const uint32_t kProcessExecuteFlags = 0x13579bdf; + const uint32_t kProtectedProcess = 1; + const uint32_t kTimeZoneId = 2; + const int32_t kBias = 300; + const char kStandardName[] = "EST"; + const int32_t kStandardBias = 0; + const char kDaylightName[] = "EDT"; + const int32_t kDaylightBias = -60; + const SYSTEMTIME kSystemTimeZero = {}; + const char kBuildString[] = "build string"; + const char kDebugBuildString[] = "debug build string"; + + misc_info_writer->SetProcessID(kProcessId); + misc_info_writer->SetProcessTimes( + kProcessCreateTime, kProcessUserTime, kProcessKernelTime); + misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz, + kProcessorCurrentMhz, + kProcessorMhzLimit, + kProcessorMaxIdleState, + kProcessorCurrentIdleState); + misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel); + misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags); + misc_info_writer->SetProtectedProcess(kProtectedProcess); + misc_info_writer->SetTimeZone(kTimeZoneId, + kBias, + kStandardName, + kSystemTimeZero, + kStandardBias, + kDaylightName, + kSystemTimeZero, + kDaylightBias); + misc_info_writer->SetBuildString(kBuildString, kDebugBuildString); + + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_4 expected = {}; + expected.Flags1 = + MINIDUMP_MISC1_PROCESS_ID | MINIDUMP_MISC1_PROCESS_TIMES | + MINIDUMP_MISC1_PROCESSOR_POWER_INFO | MINIDUMP_MISC3_PROCESS_INTEGRITY | + MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS | MINIDUMP_MISC3_PROTECTED_PROCESS | + MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC4_BUILDSTRING; + expected.ProcessId = kProcessId; + expected.ProcessCreateTime = kProcessCreateTime; + expected.ProcessUserTime = kProcessUserTime; + expected.ProcessKernelTime = kProcessKernelTime; + expected.ProcessorMaxMhz = kProcessorMaxMhz; + expected.ProcessorCurrentMhz = kProcessorCurrentMhz; + expected.ProcessorMhzLimit = kProcessorMhzLimit; + expected.ProcessorMaxIdleState = kProcessorMaxIdleState; + expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState; + expected.ProcessIntegrityLevel = kProcessIntegrityLevel; + expected.ProcessExecuteFlags = kProcessExecuteFlags; + expected.ProtectedProcess = kProtectedProcess; + expected.TimeZoneId = kTimeZoneId; + expected.TimeZone.Bias = kBias; + base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName); + c16lcpy(expected.TimeZone.StandardName, + standard_name_utf16.c_str(), + arraysize(expected.TimeZone.StandardName)); + memcpy(&expected.TimeZone.StandardDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.StandardDate)); + expected.TimeZone.StandardBias = kStandardBias; + base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName); + c16lcpy(expected.TimeZone.DaylightName, + daylight_name_utf16.c_str(), + arraysize(expected.TimeZone.DaylightName)); + memcpy(&expected.TimeZone.DaylightDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.DaylightDate)); + expected.TimeZone.DaylightBias = kDaylightBias; + base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString); + c16lcpy(expected.BuildString, + build_string_utf16.c_str(), + arraysize(expected.BuildString)); + base::string16 debug_build_string_utf16 = + base::UTF8ToUTF16(kDebugBuildString); + c16lcpy(expected.DbgBldStr, + debug_build_string_utf16.c_str(), + arraysize(expected.DbgBldStr)); + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, InitializeFromSnapshot) { + MINIDUMP_MISC_INFO_4 expect_misc_info = {}; + + const char kStandardTimeName[] = "EST"; + const char kDaylightTimeName[] = "EDT"; + const char kOSVersionFull[] = + "Mac OS X 10.9.5 (13F34); " + "Darwin 13.4.0 Darwin Kernel Version 13.4.0: " + "Sun Aug 17 19:50:11 PDT 2014; " + "root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64"; + const char kMachineDescription[] = "MacBookPro11,3 (Mac-2BD1B31983FE1663)"; + base::string16 standard_time_name_utf16 = + base::UTF8ToUTF16(kStandardTimeName); + base::string16 daylight_time_name_utf16 = + base::UTF8ToUTF16(kDaylightTimeName); + base::string16 build_string_utf16 = base::UTF8ToUTF16( + std::string(kOSVersionFull) + "; " + kMachineDescription); + std::string debug_build_string = internal::MinidumpMiscInfoDebugBuildString(); + EXPECT_FALSE(debug_build_string.empty()); + base::string16 debug_build_string_utf16 = + base::UTF8ToUTF16(debug_build_string); + + expect_misc_info.SizeOfInfo = sizeof(expect_misc_info); + expect_misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID | + MINIDUMP_MISC1_PROCESS_TIMES | + MINIDUMP_MISC1_PROCESSOR_POWER_INFO | + MINIDUMP_MISC3_TIMEZONE | + MINIDUMP_MISC4_BUILDSTRING; + expect_misc_info.ProcessId = 12345; + expect_misc_info.ProcessCreateTime = 0x555c7740; + expect_misc_info.ProcessUserTime = 60; + expect_misc_info.ProcessKernelTime = 15; + expect_misc_info.ProcessorCurrentMhz = 2800; + expect_misc_info.ProcessorMaxMhz = 2800; + expect_misc_info.TimeZoneId = 1; + expect_misc_info.TimeZone.Bias = 300; + c16lcpy(expect_misc_info.TimeZone.StandardName, + standard_time_name_utf16.c_str(), + arraysize(expect_misc_info.TimeZone.StandardName)); + expect_misc_info.TimeZone.StandardBias = 0; + c16lcpy(expect_misc_info.TimeZone.DaylightName, + daylight_time_name_utf16.c_str(), + arraysize(expect_misc_info.TimeZone.DaylightName)); + expect_misc_info.TimeZone.DaylightBias = -60; + c16lcpy(expect_misc_info.BuildString, + build_string_utf16.c_str(), + arraysize(expect_misc_info.BuildString)); + c16lcpy(expect_misc_info.DbgBldStr, + debug_build_string_utf16.c_str(), + arraysize(expect_misc_info.DbgBldStr)); + + const timeval kStartTime = + { static_cast<time_t>(expect_misc_info.ProcessCreateTime), 0 }; + const timeval kUserCPUTime = + { static_cast<time_t>(expect_misc_info.ProcessUserTime), 0 }; + const timeval kSystemCPUTime = + { static_cast<time_t>(expect_misc_info.ProcessKernelTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetProcessID(expect_misc_info.ProcessId); + process_snapshot.SetProcessStartTime(kStartTime); + process_snapshot.SetProcessCPUTimes(kUserCPUTime, kSystemCPUTime); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + const uint64_t kHzPerMHz = static_cast<uint64_t>(1E6); + system_snapshot->SetCPUFrequency( + expect_misc_info.ProcessorCurrentMhz * kHzPerMHz, + expect_misc_info.ProcessorMaxMhz * kHzPerMHz); + system_snapshot->SetTimeZone(SystemSnapshot::kObservingStandardTime, + expect_misc_info.TimeZone.Bias * -60, + (expect_misc_info.TimeZone.Bias + + expect_misc_info.TimeZone.DaylightBias) * -60, + kStandardTimeName, + kDaylightTimeName); + system_snapshot->SetOSVersionFull(kOSVersionFull); + system_snapshot->SetMachineDescription(kMachineDescription); + + process_snapshot.SetSystem(crashpad::move(system_snapshot)); + + auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter()); + misc_info_writer->InitializeFromSnapshot(&process_snapshot); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(misc_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* misc_info = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &misc_info)); + + ExpectMiscInfoEqual(&expect_misc_info, misc_info); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc new file mode 100644 index 0000000..5b71ec2 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc
@@ -0,0 +1,240 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_module_crashpad_info_writer.h" + +#include <sys/types.h> + +#include "base/logging.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpModuleCrashpadInfoWriter::MinidumpModuleCrashpadInfoWriter() + : MinidumpWritable(), + module_(), + list_annotations_(), + simple_annotations_() { + module_.version = MinidumpModuleCrashpadInfo::kVersion; +} + +MinidumpModuleCrashpadInfoWriter::~MinidumpModuleCrashpadInfoWriter() { +} + +void MinidumpModuleCrashpadInfoWriter::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!list_annotations_); + DCHECK(!simple_annotations_); + + auto list_annotations = make_scoped_ptr(new MinidumpUTF8StringListWriter()); + list_annotations->InitializeFromVector(module_snapshot->AnnotationsVector()); + if (list_annotations->IsUseful()) { + SetListAnnotations(crashpad::move(list_annotations)); + } + + auto simple_annotations = + make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter()); + simple_annotations->InitializeFromMap( + module_snapshot->AnnotationsSimpleMap()); + if (simple_annotations->IsUseful()) { + SetSimpleAnnotations(crashpad::move(simple_annotations)); + } +} + +void MinidumpModuleCrashpadInfoWriter::SetListAnnotations( + scoped_ptr<MinidumpUTF8StringListWriter> list_annotations) { + DCHECK_EQ(state(), kStateMutable); + + list_annotations_ = crashpad::move(list_annotations); +} + +void MinidumpModuleCrashpadInfoWriter::SetSimpleAnnotations( + scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations) { + DCHECK_EQ(state(), kStateMutable); + + simple_annotations_ = crashpad::move(simple_annotations); +} + +bool MinidumpModuleCrashpadInfoWriter::IsUseful() const { + return list_annotations_ || simple_annotations_; +} + +bool MinidumpModuleCrashpadInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + if (list_annotations_) { + list_annotations_->RegisterLocationDescriptor(&module_.list_annotations); + } + + if (simple_annotations_) { + simple_annotations_->RegisterLocationDescriptor( + &module_.simple_annotations); + } + + return true; +} + +size_t MinidumpModuleCrashpadInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(module_); +} + +std::vector<internal::MinidumpWritable*> +MinidumpModuleCrashpadInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children; + if (list_annotations_) { + children.push_back(list_annotations_.get()); + } + if (simple_annotations_) { + children.push_back(simple_annotations_.get()); + } + + return children; +} + +bool MinidumpModuleCrashpadInfoWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&module_, sizeof(module_)); +} + +MinidumpModuleCrashpadInfoListWriter::MinidumpModuleCrashpadInfoListWriter() + : MinidumpWritable(), + module_crashpad_infos_(), + module_crashpad_info_links_(), + module_crashpad_info_list_base_() { +} + +MinidumpModuleCrashpadInfoListWriter::~MinidumpModuleCrashpadInfoListWriter() { +} + +void MinidumpModuleCrashpadInfoListWriter::InitializeFromSnapshot( + const std::vector<const ModuleSnapshot*>& module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(module_crashpad_infos_.empty()); + DCHECK(module_crashpad_info_links_.empty()); + + size_t count = module_snapshots.size(); + for (size_t index = 0; index < count; ++index) { + const ModuleSnapshot* module_snapshot = module_snapshots[index]; + + auto module = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + module->InitializeFromSnapshot(module_snapshot); + if (module->IsUseful()) { + AddModule(crashpad::move(module), index); + } + } +} + +void MinidumpModuleCrashpadInfoListWriter::AddModule( + scoped_ptr<MinidumpModuleCrashpadInfoWriter> module_crashpad_info, + size_t minidump_module_list_index) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + MinidumpModuleCrashpadInfoLink module_crashpad_info_link = {}; + if (!AssignIfInRange(&module_crashpad_info_link.minidump_module_list_index, + minidump_module_list_index)) { + LOG(ERROR) << "minidump_module_list_index " << minidump_module_list_index + << " out of range"; + return; + } + + module_crashpad_info_links_.push_back(module_crashpad_info_link); + module_crashpad_infos_.push_back(module_crashpad_info.release()); +} + +bool MinidumpModuleCrashpadInfoListWriter::IsUseful() const { + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + return !module_crashpad_infos_.empty(); +} + +bool MinidumpModuleCrashpadInfoListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t module_count = module_crashpad_infos_.size(); + if (!AssignIfInRange(&module_crashpad_info_list_base_.count, module_count)) { + LOG(ERROR) << "module_count " << module_count << " out of range"; + return false; + } + + for (size_t index = 0; index < module_count; ++index) { + module_crashpad_infos_[index]->RegisterLocationDescriptor( + &module_crashpad_info_links_[index].location); + } + + return true; +} + +size_t MinidumpModuleCrashpadInfoListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + return sizeof(module_crashpad_info_list_base_) + + module_crashpad_info_links_.size() * + sizeof(module_crashpad_info_links_[0]); +} + +std::vector<internal::MinidumpWritable*> +MinidumpModuleCrashpadInfoListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + std::vector<MinidumpWritable*> children; + for (MinidumpModuleCrashpadInfoWriter* module : module_crashpad_infos_) { + children.push_back(module); + } + + return children; +} + +bool MinidumpModuleCrashpadInfoListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + WritableIoVec iov; + iov.iov_base = &module_crashpad_info_list_base_; + iov.iov_len = sizeof(module_crashpad_info_list_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + if (!module_crashpad_info_links_.empty()) { + iov.iov_base = &module_crashpad_info_links_[0]; + iov.iov_len = module_crashpad_info_links_.size() * + sizeof(module_crashpad_info_links_[0]); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h new file mode 100644 index 0000000..601da7ee7 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h
@@ -0,0 +1,166 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class MinidumpSimpleStringDictionaryWriter; +class ModuleSnapshot; + +//! \brief The writer for a MinidumpModuleCrashpadInfo object in a minidump +//! file. +class MinidumpModuleCrashpadInfoWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleCrashpadInfoWriter(); + ~MinidumpModuleCrashpadInfoWriter() override; + + //! \brief Initializes MinidumpModuleCrashpadInfo based on \a module_snapshot. + //! + //! Only data in \a module_snapshot that is considered useful will be + //! included. For simple annotations, usefulness is determined by + //! MinidumpSimpleStringDictionaryWriter::IsUseful(). + //! + //! \param[in] module_snapshot The module snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); + + //! \brief Arranges for MinidumpModuleCrashpadInfo::list_annotations to point + //! to the internal::MinidumpUTF8StringListWriter object to be written by + //! \a list_annotations. + //! + //! This object takes ownership of \a simple_annotations and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetListAnnotations( + scoped_ptr<MinidumpUTF8StringListWriter> list_annotations); + + //! \brief Arranges for MinidumpModuleCrashpadInfo::simple_annotations to + //! point to the MinidumpSimpleStringDictionaryWriter object to be written + //! by \a simple_annotations. + //! + //! This object takes ownership of \a simple_annotations and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetSimpleAnnotations( + scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying list annotations or + //! simple annotations would be considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MinidumpModuleCrashpadInfo module_; + scoped_ptr<MinidumpUTF8StringListWriter> list_annotations_; + scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoWriter); +}; + +//! \brief The writer for a MinidumpModuleCrashpadInfoList object in a minidump +//! file, containing a list of MinidumpModuleCrashpadInfo objects. +class MinidumpModuleCrashpadInfoListWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleCrashpadInfoListWriter(); + ~MinidumpModuleCrashpadInfoListWriter() override; + + //! \brief Adds an initialized MinidumpModuleCrashpadInfo for modules in \a + //! module_snapshots to the MinidumpModuleCrashpadInfoList. + //! + //! Only modules in \a module_snapshots that would produce a useful + //! MinidumpModuleCrashpadInfo structure are included. Usefulness is + //! determined by MinidumpModuleCrashpadInfoWriter::IsUseful(). + //! + //! \param[in] module_snapshots The module snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. AddModule() may not be called before this + //! method, and it is not normally necessary to call AddModule() after + //! this method. + void InitializeFromSnapshot( + const std::vector<const ModuleSnapshot*>& module_snapshots); + + //! \brief Adds a MinidumpModuleCrashpadInfo to the + //! MinidumpModuleCrashpadInfoList. + //! + //! \param[in] module Extended Crashpad-specific information about the module. + //! This object takes ownership of \a module and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! \param[in] module_list_index The index of the MINIDUMP_MODULE in the + //! minidump file’s MINIDUMP_MODULE_LIST stream that corresponds to \a + //! module_crashpad_info. + //! + //! \note Valid in #kStateMutable. + void AddModule( + scoped_ptr<MinidumpModuleCrashpadInfoWriter> module_crashpad_info, + size_t minidump_module_list_index); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying children would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + PointerVector<MinidumpModuleCrashpadInfoWriter> module_crashpad_infos_; + std::vector<MinidumpModuleCrashpadInfoLink> module_crashpad_info_links_; + MinidumpModuleCrashpadInfoList module_crashpad_info_list_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc new file mode 100644 index 0000000..79ed2667 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc
@@ -0,0 +1,474 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_module_crashpad_info_writer.h" + +#include <windows.h> +#include <dbghelp.h> + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpModuleCrashpadInfoList* MinidumpModuleCrashpadInfoListAtStart( + const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = + static_cast<uint32_t>(sizeof(MinidumpModuleCrashpadInfoList) + + count * sizeof(MinidumpModuleCrashpadInfoLink)); + location_descriptor.Rva = 0; + + const MinidumpModuleCrashpadInfoList* list = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>( + file_contents, location_descriptor); + if (!list) { + return nullptr; + } + + if (list->count != count) { + EXPECT_EQ(count, list->count); + return nullptr; + } + + return list; +} + +TEST(MinidumpModuleCrashpadInfoWriter, EmptyList) { + StringFile string_file; + + auto module_list_writer = + make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + EXPECT_FALSE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList), + string_file.string().size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 0); + ASSERT_TRUE(module_list); +} + +TEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) { + StringFile string_file; + + auto module_list_writer = + make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + auto module_writer = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + EXPECT_FALSE(module_writer->IsUseful()); + module_list_writer->AddModule(crashpad::move(module_writer), 0); + + EXPECT_TRUE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList) + + sizeof(MinidumpModuleCrashpadInfoLink) + + sizeof(MinidumpModuleCrashpadInfo), + string_file.string().size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1); + ASSERT_TRUE(module_list); + + EXPECT_EQ(0u, module_list->modules[0].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + EXPECT_EQ(0u, module->list_annotations.DataSize); + EXPECT_EQ(0u, module->list_annotations.Rva); + EXPECT_EQ(0u, module->simple_annotations.DataSize); + EXPECT_EQ(0u, module->simple_annotations.Rva); +} + +TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { + const uint32_t kMinidumpModuleListIndex = 1; + const char kKey[] = "key"; + const char kValue[] = "value"; + const char kEntry[] = "entry"; + std::vector<std::string> vector(1, std::string(kEntry)); + + StringFile string_file; + + auto module_list_writer = + make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + + auto module_writer = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + auto string_list_writer = make_scoped_ptr(new MinidumpUTF8StringListWriter()); + string_list_writer->InitializeFromVector(vector); + module_writer->SetListAnnotations(crashpad::move(string_list_writer)); + auto simple_string_dictionary_writer = + make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter()); + auto simple_string_dictionary_entry_writer = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue); + simple_string_dictionary_writer->AddEntry( + crashpad::move(simple_string_dictionary_entry_writer)); + module_writer->SetSimpleAnnotations( + crashpad::move(simple_string_dictionary_writer)); + EXPECT_TRUE(module_writer->IsUseful()); + module_list_writer->AddModule(crashpad::move(module_writer), + kMinidumpModuleListIndex); + + EXPECT_TRUE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList) + + sizeof(MinidumpModuleCrashpadInfoLink) + + sizeof(MinidumpModuleCrashpadInfo) + + sizeof(MinidumpRVAList) + + sizeof(RVA) + + sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + sizeof(MinidumpUTF8String) + arraysize(kEntry) + 2 + // padding + sizeof(MinidumpUTF8String) + arraysize(kKey) + + sizeof(MinidumpUTF8String) + arraysize(kValue), + string_file.string().size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1); + ASSERT_TRUE(module_list); + + EXPECT_EQ(kMinidumpModuleListIndex, + module_list->modules[0].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + EXPECT_NE(0u, module->list_annotations.DataSize); + EXPECT_NE(0u, module->list_annotations.Rva); + EXPECT_NE(0u, module->simple_annotations.DataSize); + EXPECT_NE(0u, module->simple_annotations.Rva); + + const MinidumpRVAList* list_annotations = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module->list_annotations); + ASSERT_TRUE(list_annotations); + + ASSERT_EQ(1u, list_annotations->count); + EXPECT_EQ(kEntry, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations->children[0])); + + const MinidumpSimpleStringDictionary* simple_annotations = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module->simple_annotations); + ASSERT_TRUE(simple_annotations); + + ASSERT_EQ(1u, simple_annotations->count); + EXPECT_EQ(kKey, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].key)); + EXPECT_EQ(kValue, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].value)); +} + +TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { + const uint32_t kMinidumpModuleListIndex0 = 0; + const char kKey0[] = "key"; + const char kValue0[] = "value"; + const uint32_t kMinidumpModuleListIndex1 = 2; + const uint32_t kMinidumpModuleListIndex2 = 5; + const char kKey2A[] = "K"; + const char kValue2A[] = "VVV"; + const char kKey2B[] = "river"; + const char kValue2B[] = "hudson"; + + StringFile string_file; + + auto module_list_writer = + make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + + auto module_writer_0 = + make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + auto simple_string_dictionary_writer_0 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter()); + auto simple_string_dictionary_entry_writer_0 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + simple_string_dictionary_entry_writer_0->SetKeyValue(kKey0, kValue0); + simple_string_dictionary_writer_0->AddEntry( + crashpad::move(simple_string_dictionary_entry_writer_0)); + module_writer_0->SetSimpleAnnotations( + crashpad::move(simple_string_dictionary_writer_0)); + EXPECT_TRUE(module_writer_0->IsUseful()); + module_list_writer->AddModule(crashpad::move(module_writer_0), + kMinidumpModuleListIndex0); + + auto module_writer_1 = + make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + EXPECT_FALSE(module_writer_1->IsUseful()); + module_list_writer->AddModule(crashpad::move(module_writer_1), + kMinidumpModuleListIndex1); + + auto module_writer_2 = + make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter()); + auto simple_string_dictionary_writer_2 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter()); + auto simple_string_dictionary_entry_writer_2a = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + simple_string_dictionary_entry_writer_2a->SetKeyValue(kKey2A, kValue2A); + simple_string_dictionary_writer_2->AddEntry( + crashpad::move(simple_string_dictionary_entry_writer_2a)); + auto simple_string_dictionary_entry_writer_2b = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + simple_string_dictionary_entry_writer_2b->SetKeyValue(kKey2B, kValue2B); + simple_string_dictionary_writer_2->AddEntry( + crashpad::move(simple_string_dictionary_entry_writer_2b)); + module_writer_2->SetSimpleAnnotations( + crashpad::move(simple_string_dictionary_writer_2)); + EXPECT_TRUE(module_writer_2->IsUseful()); + module_list_writer->AddModule(crashpad::move(module_writer_2), + kMinidumpModuleListIndex2); + + EXPECT_TRUE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3); + ASSERT_TRUE(module_list); + + EXPECT_EQ(kMinidumpModuleListIndex0, + module_list->modules[0].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module_0 = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module_0); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_0->version); + + const MinidumpRVAList* list_annotations_0 = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module_0->list_annotations); + EXPECT_FALSE(list_annotations_0); + + const MinidumpSimpleStringDictionary* simple_annotations_0 = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module_0->simple_annotations); + ASSERT_TRUE(simple_annotations_0); + + ASSERT_EQ(1u, simple_annotations_0->count); + EXPECT_EQ(kKey0, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].key)); + EXPECT_EQ(kValue0, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].value)); + + EXPECT_EQ(kMinidumpModuleListIndex1, + module_list->modules[1].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module_1 = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[1].location); + ASSERT_TRUE(module_1); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_1->version); + + const MinidumpRVAList* list_annotations_1 = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module_1->list_annotations); + EXPECT_FALSE(list_annotations_1); + + const MinidumpSimpleStringDictionary* simple_annotations_1 = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module_1->simple_annotations); + EXPECT_FALSE(simple_annotations_1); + + EXPECT_EQ(kMinidumpModuleListIndex2, + module_list->modules[2].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module_2 = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[2].location); + ASSERT_TRUE(module_2); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_2->version); + + const MinidumpRVAList* list_annotations_2 = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module_2->list_annotations); + EXPECT_FALSE(list_annotations_2); + + const MinidumpSimpleStringDictionary* simple_annotations_2 = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module_2->simple_annotations); + ASSERT_TRUE(simple_annotations_2); + + ASSERT_EQ(2u, simple_annotations_2->count); + EXPECT_EQ(kKey2A, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].key)); + EXPECT_EQ(kValue2A, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].value)); + EXPECT_EQ(kKey2B, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[1].key)); + EXPECT_EQ(kValue2B, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[1].value)); +} + +TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { + const char kKey0A[] = "k"; + const char kValue0A[] = "value"; + const char kKey0B[] = "hudson"; + const char kValue0B[] = "estuary"; + const char kKey2[] = "k"; + const char kValue2[] = "different_value"; + const char kEntry3A[] = "list"; + const char kEntry3B[] = "erine"; + + std::vector<const ModuleSnapshot*> module_snapshots; + + TestModuleSnapshot module_snapshot_0; + std::map<std::string, std::string> annotations_simple_map_0; + annotations_simple_map_0[kKey0A] = kValue0A; + annotations_simple_map_0[kKey0B] = kValue0B; + module_snapshot_0.SetAnnotationsSimpleMap(annotations_simple_map_0); + module_snapshots.push_back(&module_snapshot_0); + + // module_snapshot_1 is not expected to be written because it would not carry + // any MinidumpModuleCrashpadInfo data. + TestModuleSnapshot module_snapshot_1; + module_snapshots.push_back(&module_snapshot_1); + + TestModuleSnapshot module_snapshot_2; + std::map<std::string, std::string> annotations_simple_map_2; + annotations_simple_map_2[kKey2] = kValue2; + module_snapshot_2.SetAnnotationsSimpleMap(annotations_simple_map_2); + module_snapshots.push_back(&module_snapshot_2); + + TestModuleSnapshot module_snapshot_3; + std::vector<std::string> annotations_vector_3; + annotations_vector_3.push_back(kEntry3A); + annotations_vector_3.push_back(kEntry3B); + module_snapshot_3.SetAnnotationsVector(annotations_vector_3); + module_snapshots.push_back(&module_snapshot_3); + + auto module_list_writer = + make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter()); + module_list_writer->InitializeFromSnapshot(module_snapshots); + EXPECT_TRUE(module_list_writer->IsUseful()); + + StringFile string_file; + ASSERT_TRUE(module_list_writer->WriteEverything(&string_file)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3); + ASSERT_TRUE(module_list); + + EXPECT_EQ(0u, module_list->modules[0].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module_0 = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module_0); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_0->version); + + const MinidumpRVAList* list_annotations_0 = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module_0->list_annotations); + EXPECT_FALSE(list_annotations_0); + + const MinidumpSimpleStringDictionary* simple_annotations_0 = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module_0->simple_annotations); + ASSERT_TRUE(simple_annotations_0); + + ASSERT_EQ(annotations_simple_map_0.size(), simple_annotations_0->count); + EXPECT_EQ(kKey0B, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].key)); + EXPECT_EQ(kValue0B, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].value)); + EXPECT_EQ(kKey0A, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[1].key)); + EXPECT_EQ(kValue0A, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[1].value)); + + EXPECT_EQ(2u, module_list->modules[1].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module_2 = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[1].location); + ASSERT_TRUE(module_2); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_2->version); + + const MinidumpRVAList* list_annotations_2 = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module_2->list_annotations); + EXPECT_FALSE(list_annotations_2); + + const MinidumpSimpleStringDictionary* simple_annotations_2 = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module_2->simple_annotations); + ASSERT_TRUE(simple_annotations_2); + + ASSERT_EQ(annotations_simple_map_2.size(), simple_annotations_2->count); + EXPECT_EQ(kKey2, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].key)); + EXPECT_EQ(kValue2, + MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].value)); + + EXPECT_EQ(3u, module_list->modules[2].minidump_module_list_index); + const MinidumpModuleCrashpadInfo* module_3 = + MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>( + string_file.string(), module_list->modules[2].location); + ASSERT_TRUE(module_3); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_3->version); + + const MinidumpRVAList* list_annotations_3 = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + string_file.string(), module_3->list_annotations); + ASSERT_TRUE(list_annotations_3); + + ASSERT_EQ(annotations_vector_3.size(), list_annotations_3->count); + EXPECT_EQ(kEntry3A, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations_3->children[0])); + EXPECT_EQ(kEntry3B, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations_3->children[1])); + + const MinidumpSimpleStringDictionary* simple_annotations_3 = + MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + string_file.string(), module_3->simple_annotations); + EXPECT_FALSE(simple_annotations_3); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc new file mode 100644 index 0000000..8cbf855 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc
@@ -0,0 +1,454 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_module_writer.h" + +#include <sys/types.h> + +#include <limits> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writer_util.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_writer.h" +#include "util/misc/implicit_cast.h" +#include "util/stdlib/move.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() { +} + +namespace internal { + +template <typename CodeViewRecordType> +MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordType>::MinidumpModuleCodeViewRecordPDBLinkWriter() + : MinidumpModuleCodeViewRecordWriter(), codeview_record_(), pdb_name_() { + codeview_record_.signature = CodeViewRecordType::kSignature; +} + +template <typename CodeViewRecordType> +MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() { +} + +template <typename CodeViewRecordType> +size_t +MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // NUL-terminate. + return offsetof(decltype(codeview_record_), pdb_name) + + (pdb_name_.size() + 1) * sizeof(pdb_name_[0]); +} + +template <typename CodeViewRecordType> +bool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &codeview_record_; + iov.iov_len = offsetof(decltype(codeview_record_), pdb_name); + std::vector<WritableIoVec> iovecs(1, iov); + + // NUL-terminate. + iov.iov_base = &pdb_name_[0]; + iov.iov_len = (pdb_name_.size() + 1) * sizeof(pdb_name_[0]); + iovecs.push_back(iov); + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace internal + +template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB20>; + +MinidumpModuleCodeViewRecordPDB20Writer:: + ~MinidumpModuleCodeViewRecordPDB20Writer() { +} + +void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge( + time_t timestamp, + uint32_t age) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&codeview_record()->timestamp, + timestamp); + + codeview_record()->age = age; +} + +template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB70>; + +MinidumpModuleCodeViewRecordPDB70Writer:: + ~MinidumpModuleCodeViewRecordPDB70Writer() { +} + +void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + + SetPDBName(module_snapshot->DebugFileName()); + + UUID uuid; + uint32_t age; + module_snapshot->UUIDAndAge(&uuid, &age); + SetUUIDAndAge(uuid, age); +} + +MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter() + : internal::MinidumpWritable(), + image_debug_misc_(), + data_(), + data_utf16_() { +} + +MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() { +} + +void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data, + bool utf16) { + DCHECK_EQ(state(), kStateMutable); + + if (!utf16) { + data_utf16_.clear(); + image_debug_misc_.Unicode = 0; + data_ = data; + } else { + data_.clear(); + image_debug_misc_.Unicode = 1; + data_utf16_ = internal::MinidumpWriterUtil::ConvertUTF8ToUTF16(data); + } +} + +bool MinidumpModuleMiscDebugRecordWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + // NUL-terminate. + if (!image_debug_misc_.Unicode) { + DCHECK(data_utf16_.empty()); + image_debug_misc_.Length = base::checked_cast<uint32_t>( + offsetof(decltype(image_debug_misc_), Data) + + (data_.size() + 1) * sizeof(data_[0])); + } else { + DCHECK(data_.empty()); + image_debug_misc_.Length = base::checked_cast<uint32_t>( + offsetof(decltype(image_debug_misc_), Data) + + (data_utf16_.size() + 1) * sizeof(data_utf16_[0])); + } + + return true; +} + +size_t MinidumpModuleMiscDebugRecordWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return image_debug_misc_.Length; +} + +bool MinidumpModuleMiscDebugRecordWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + const size_t base_length = offsetof(decltype(image_debug_misc_), Data); + + WritableIoVec iov; + iov.iov_base = &image_debug_misc_; + iov.iov_len = base_length; + std::vector<WritableIoVec> iovecs(1, iov); + + if (!image_debug_misc_.Unicode) { + DCHECK(data_utf16_.empty()); + iov.iov_base = &data_[0]; + } else { + DCHECK(data_.empty()); + iov.iov_base = &data_utf16_[0]; + } + iov.iov_len = image_debug_misc_.Length - base_length; + iovecs.push_back(iov); + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpModuleWriter::MinidumpModuleWriter() + : MinidumpWritable(), + module_(), + name_(), + codeview_record_(nullptr), + misc_debug_record_(nullptr) { + module_.VersionInfo.dwSignature = VS_FFI_SIGNATURE; + module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION; +} + +MinidumpModuleWriter::~MinidumpModuleWriter() { +} + +void MinidumpModuleWriter::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!name_); + DCHECK(!codeview_record_); + DCHECK(!misc_debug_record_); + + SetName(module_snapshot->Name()); + + SetImageBaseAddress(module_snapshot->Address()); + SetImageSize(InRangeCast<uint32_t>(module_snapshot->Size(), + std::numeric_limits<uint32_t>::max())); + SetTimestamp(module_snapshot->Timestamp()); + + uint16_t v[4]; + module_snapshot->FileVersion(&v[0], &v[1], &v[2], &v[3]); + SetFileVersion(v[0], v[1], v[2], v[3]); + + module_snapshot->SourceVersion(&v[0], &v[1], &v[2], &v[3]); + SetProductVersion(v[0], v[1], v[2], v[3]); + + uint32_t file_type; + switch (module_snapshot->GetModuleType()) { + case ModuleSnapshot::kModuleTypeExecutable: + file_type = VFT_APP; + break; + case ModuleSnapshot::kModuleTypeSharedLibrary: + case ModuleSnapshot::kModuleTypeLoadableModule: + file_type = VFT_DLL; + break; + default: + file_type = VFT_UNKNOWN; + break; + } + SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN); + + auto codeview_record = + make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer()); + codeview_record->InitializeFromSnapshot(module_snapshot); + SetCodeViewRecord(crashpad::move(codeview_record)); +} + +const MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const { + DCHECK_EQ(state(), kStateWritable); + + return &module_; +} + +void MinidumpModuleWriter::SetName(const std::string& name) { + DCHECK_EQ(state(), kStateMutable); + + if (!name_) { + name_.reset(new internal::MinidumpUTF16StringWriter()); + } + name_->SetUTF8(name); +} + +void MinidumpModuleWriter::SetCodeViewRecord( + scoped_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record) { + DCHECK_EQ(state(), kStateMutable); + + codeview_record_ = crashpad::move(codeview_record); +} + +void MinidumpModuleWriter::SetMiscDebugRecord( + scoped_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record) { + DCHECK_EQ(state(), kStateMutable); + + misc_debug_record_ = crashpad::move(misc_debug_record); +} + +void MinidumpModuleWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&module_.TimeDateStamp, timestamp); +} + +void MinidumpModuleWriter::SetFileVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3) { + DCHECK_EQ(state(), kStateMutable); + + module_.VersionInfo.dwFileVersionMS = + (implicit_cast<uint32_t>(version_0) << 16) | version_1; + module_.VersionInfo.dwFileVersionLS = + (implicit_cast<uint32_t>(version_2) << 16) | version_3; +} + +void MinidumpModuleWriter::SetProductVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3) { + DCHECK_EQ(state(), kStateMutable); + + module_.VersionInfo.dwProductVersionMS = + (implicit_cast<uint32_t>(version_0) << 16) | version_1; + module_.VersionInfo.dwProductVersionLS = + (implicit_cast<uint32_t>(version_2) << 16) | version_3; +} + +void MinidumpModuleWriter::SetFileFlagsAndMask(uint32_t file_flags, + uint32_t file_flags_mask) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(file_flags & ~file_flags_mask, 0u); + + module_.VersionInfo.dwFileFlags = file_flags; + module_.VersionInfo.dwFileFlagsMask = file_flags_mask; +} + +bool MinidumpModuleWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(name_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + name_->RegisterRVA(&module_.ModuleNameRva); + + if (codeview_record_) { + codeview_record_->RegisterLocationDescriptor(&module_.CvRecord); + } + + if (misc_debug_record_) { + misc_debug_record_->RegisterLocationDescriptor(&module_.MiscRecord); + } + + return true; +} + +size_t MinidumpModuleWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is + // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children + // are responsible for writing themselves. + return 0; +} + +std::vector<internal::MinidumpWritable*> MinidumpModuleWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(name_); + + std::vector<MinidumpWritable*> children; + children.push_back(name_.get()); + if (codeview_record_) { + children.push_back(codeview_record_.get()); + } + if (misc_debug_record_) { + children.push_back(misc_debug_record_.get()); + } + + return children; +} + +bool MinidumpModuleWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is + // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children + // are responsible for writing themselves. + return true; +} + +MinidumpModuleListWriter::MinidumpModuleListWriter() + : MinidumpStreamWriter(), modules_(), module_list_base_() { +} + +MinidumpModuleListWriter::~MinidumpModuleListWriter() { +} + +void MinidumpModuleListWriter::InitializeFromSnapshot( + const std::vector<const ModuleSnapshot*>& module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(modules_.empty()); + + for (const ModuleSnapshot* module_snapshot : module_snapshots) { + auto module = make_scoped_ptr(new MinidumpModuleWriter()); + module->InitializeFromSnapshot(module_snapshot); + AddModule(crashpad::move(module)); + } +} + +void MinidumpModuleListWriter::AddModule( + scoped_ptr<MinidumpModuleWriter> module) { + DCHECK_EQ(state(), kStateMutable); + + modules_.push_back(module.release()); +} + +bool MinidumpModuleListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t module_count = modules_.size(); + if (!AssignIfInRange(&module_list_base_.NumberOfModules, module_count)) { + LOG(ERROR) << "module_count " << module_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpModuleListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(module_list_base_) + modules_.size() * sizeof(MINIDUMP_MODULE); +} + +std::vector<internal::MinidumpWritable*> MinidumpModuleListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children; + for (MinidumpModuleWriter* module : modules_) { + children.push_back(module); + } + + return children; +} + +bool MinidumpModuleListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &module_list_base_; + iov.iov_len = sizeof(module_list_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (const MinidumpModuleWriter* module : modules_) { + iov.iov_base = module->MinidumpModule(); + iov.iov_len = sizeof(MINIDUMP_MODULE); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpModuleListWriter::StreamType() const { + return kMinidumpStreamTypeModuleList; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_writer.h b/third_party/crashpad/crashpad/minidump/minidump_module_writer.h new file mode 100644 index 0000000..ecf3cb85 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_module_writer.h
@@ -0,0 +1,352 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <time.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class ModuleSnapshot; + +namespace internal { +class MinidumpUTF16StringWriter; +} // namespace internal + +//! \brief The base class for writers of CodeView records referenced by +//! MINIDUMP_MODULE::CvRecord in minidump files. +class MinidumpModuleCodeViewRecordWriter : public internal::MinidumpWritable { + public: + ~MinidumpModuleCodeViewRecordWriter() override; + + protected: + MinidumpModuleCodeViewRecordWriter() : MinidumpWritable() {} + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordWriter); +}; + +namespace internal { + +//! \brief The base class for writers of CodeView records that serve as links to +//! `.pdb` (program database) files. +template <typename CodeViewRecordType> +class MinidumpModuleCodeViewRecordPDBLinkWriter + : public MinidumpModuleCodeViewRecordWriter { + public: + //! \brief Sets the name of the `.pdb` file being linked to. + void SetPDBName(const std::string& pdb_name) { pdb_name_ = pdb_name; } + + protected: + MinidumpModuleCodeViewRecordPDBLinkWriter(); + ~MinidumpModuleCodeViewRecordPDBLinkWriter() override; + + // MinidumpWritable: + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + //! \brief Returns a pointer to the raw CodeView record’s data. + //! + //! Subclasses can use this to set fields in their codeview records other than + //! the `pdb_name` field. + CodeViewRecordType* codeview_record() { return &codeview_record_; } + + private: + CodeViewRecordType codeview_record_; + std::string pdb_name_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDBLinkWriter); +}; + +} // namespace internal + +//! \brief The writer for a CodeViewRecordPDB20 object in a minidump file. +//! +//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead. +class MinidumpModuleCodeViewRecordPDB20Writer final + : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB20> { + public: + MinidumpModuleCodeViewRecordPDB20Writer() + : internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB20>() {} + + ~MinidumpModuleCodeViewRecordPDB20Writer() override; + + //! \brief Sets CodeViewRecordPDB20::timestamp and CodeViewRecordPDB20::age. + void SetTimestampAndAge(time_t timestamp, uint32_t age); + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB20Writer); +}; + +//! \brief The writer for a CodeViewRecordPDB70 object in a minidump file. +class MinidumpModuleCodeViewRecordPDB70Writer final + : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB70> { + public: + MinidumpModuleCodeViewRecordPDB70Writer() + : internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB70>() {} + + ~MinidumpModuleCodeViewRecordPDB70Writer() override; + + //! \brief Initializes the CodeViewRecordPDB70 based on \a module_snapshot. + //! + //! \param[in] module_snapshot The module snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); + + //! \brief Sets CodeViewRecordPDB70::uuid and CodeViewRecordPDB70::age. + void SetUUIDAndAge(const UUID& uuid, uint32_t age) { + codeview_record()->uuid = uuid; + codeview_record()->age = age; + } + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB70Writer); +}; + +//! \brief The writer for an IMAGE_DEBUG_MISC object in a minidump file. +//! +//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead. +class MinidumpModuleMiscDebugRecordWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleMiscDebugRecordWriter(); + ~MinidumpModuleMiscDebugRecordWriter() override; + + //! \brief Sets IMAGE_DEBUG_MISC::DataType. + void SetDataType(uint32_t data_type) { + image_debug_misc_.DataType = data_type; + } + + //! \brief Sets IMAGE_DEBUG_MISC::Data, IMAGE_DEBUG_MISC::Length, and + //! IMAGE_DEBUG_MISC::Unicode. + //! + //! If \a utf16 is `true`, \a data will be treated as UTF-8 data and will be + //! converted to UTF-16, and IMAGE_DEBUG_MISC::Unicode will be set to `1`. + //! Otherwise, \a data will be used as-is and IMAGE_DEBUG_MISC::Unicode will + //! be set to `0`. + void SetData(const std::string& data, bool utf16); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + IMAGE_DEBUG_MISC image_debug_misc_; + std::string data_; + base::string16 data_utf16_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleMiscDebugRecordWriter); +}; + +//! \brief The writer for a MINIDUMP_MODULE object in a minidump file. +//! +//! Because MINIDUMP_MODULE objects only appear as elements of +//! MINIDUMP_MODULE_LIST objects, this class does not write any data on its own. +//! It makes its MINIDUMP_MODULE data available to its MinidumpModuleListWriter +//! parent, which writes it as part of a MINIDUMP_MODULE_LIST. +class MinidumpModuleWriter final : public internal::MinidumpWritable { + public: + MinidumpModuleWriter(); + ~MinidumpModuleWriter() override; + + //! \brief Initializes the MINIDUMP_MODULE based on \a module_snapshot. + //! + //! \param[in] module_snapshot The module snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); + + //! \brief Returns a MINIDUMP_MODULE referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpModuleListWriter in + //! order to obtain a MINIDUMP_MODULE to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_MODULE* MinidumpModule() const; + + //! \brief Arranges for MINIDUMP_MODULE::ModuleNameRva to point to a + //! MINIDUMP_STRING containing \a name. + //! + //! A name is required in all MINIDUMP_MODULE objects. + //! + //! \note Valid in #kStateMutable. + void SetName(const std::string& name); + + //! \brief Arranges for MINIDUMP_MODULE::CvRecord to point to a CodeView + //! record to be written by \a codeview_record. + //! + //! This object takes ownership of \a codeview_record and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetCodeViewRecord( + scoped_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record); + + //! \brief Arranges for MINIDUMP_MODULE::MiscRecord to point to an + //! IMAGE_DEBUG_MISC object to be written by \a misc_debug_record. + //! + //! This object takes ownership of \a misc_debug_record and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetMiscDebugRecord( + scoped_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record); + + //! \brief Sets IMAGE_DEBUG_MISC::BaseOfImage. + void SetImageBaseAddress(uint64_t image_base_address) { + module_.BaseOfImage = image_base_address; + } + + //! \brief Sets IMAGE_DEBUG_MISC::SizeOfImage. + void SetImageSize(uint32_t image_size) { module_.SizeOfImage = image_size; } + + //! \brief Sets IMAGE_DEBUG_MISC::CheckSum. + void SetChecksum(uint32_t checksum) { module_.CheckSum = checksum; } + + //! \brief Sets IMAGE_DEBUG_MISC::TimeDateStamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileVersionMS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionMS" and \ref + //! VS_FIXEDFILEINFO::dwFileVersionLS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionLS". + //! + //! \note Valid in #kStateMutable. + void SetFileVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwProductVersionMS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionMS" and \ref + //! VS_FIXEDFILEINFO::dwProductVersionLS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionLS". + //! + //! \note Valid in #kStateMutable. + void SetProductVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileFlags + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlags" and \ref + //! VS_FIXEDFILEINFO::dwFileFlagsMask + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlagsMask". + //! + //! \note Valid in #kStateMutable. + void SetFileFlagsAndMask(uint32_t file_flags, uint32_t file_flags_mask); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileOS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileOS". + void SetFileOS(uint32_t file_os) { module_.VersionInfo.dwFileOS = file_os; } + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileType + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileType" and \ref + //! VS_FIXEDFILEINFO::dwFileSubtype + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileSubtype". + void SetFileTypeAndSubtype(uint32_t file_type, uint32_t file_subtype) { + module_.VersionInfo.dwFileType = file_type; + module_.VersionInfo.dwFileSubtype = file_subtype; + } + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_MODULE module_; + scoped_ptr<internal::MinidumpUTF16StringWriter> name_; + scoped_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record_; + scoped_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleWriter); +}; + +//! \brief The writer for a MINIDUMP_MODULE_LIST stream in a minidump file, +//! containing a list of MINIDUMP_MODULE objects. +class MinidumpModuleListWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpModuleListWriter(); + ~MinidumpModuleListWriter() override; + + //! \brief Adds an initialized MINIDUMP_MODULE for each module in \a + //! module_snapshots to the MINIDUMP_MODULE_LIST. + //! + //! \param[in] module_snapshots The module snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. AddModule() may not be called before this + //! this method, and it is not normally necessary to call AddModule() + //! after this method. + void InitializeFromSnapshot( + const std::vector<const ModuleSnapshot*>& module_snapshots); + + //! \brief Adds a MinidumpModuleWriter to the MINIDUMP_MODULE_LIST. + //! + //! This object takes ownership of \a module and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddModule(scoped_ptr<MinidumpModuleWriter> module); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + PointerVector<MinidumpModuleWriter> modules_; + MINIDUMP_MODULE_LIST module_list_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc new file mode 100644 index 0000000..9919b0c --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc
@@ -0,0 +1,773 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_module_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" +#include "test/gtest_death_check.h" +#include "util/file/string_file.h" +#include "util/misc/implicit_cast.h" +#include "util/stdlib/move.h" +#include "util/misc/uuid.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace test { +namespace { + +void GetModuleListStream(const std::string& file_contents, + const MINIDUMP_MODULE_LIST** module_list) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kModuleListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kModulesOffset = + kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST); + + ASSERT_GE(file_contents.size(), kModulesOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeModuleList, directory[0].StreamType); + EXPECT_EQ(kModuleListStreamOffset, directory[0].Location.Rva); + + *module_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>( + file_contents, directory[0].Location); + ASSERT_TRUE(module_list); +} + +TEST(MinidumpModuleWriter, EmptyModuleList) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST), + string_file.string().size()); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(0u, module_list->NumberOfModules); +} + +// If |expected_pdb_name| is not nullptr, |codeview_record| is used to locate a +// CodeView record in |file_contents|, and its fields are compared against the +// the |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView +// record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If +// |expected_pdb_name| is nullptr, |codeview_record| must not point to anything. +void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record, + const std::string& file_contents, + const char* expected_pdb_name, + const UUID* expected_pdb_uuid, + time_t expected_pdb_timestamp, + uint32_t expected_pdb_age) { + if (expected_pdb_name) { + EXPECT_NE(0u, codeview_record->Rva); + + std::string observed_pdb_name; + if (expected_pdb_uuid) { + // The CodeView record should be a PDB 7.0 link. + const CodeViewRecordPDB70* codeview_pdb70_record = + MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>( + file_contents, *codeview_record); + ASSERT_TRUE(codeview_pdb70_record); + EXPECT_EQ(0, + memcmp(expected_pdb_uuid, + &codeview_pdb70_record->uuid, + sizeof(codeview_pdb70_record->uuid))); + EXPECT_EQ(expected_pdb_age, codeview_pdb70_record->age); + + observed_pdb_name.assign( + reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]), + codeview_record->DataSize - offsetof(CodeViewRecordPDB70, pdb_name)); + } else { + // The CodeView record should be a PDB 2.0 link. + const CodeViewRecordPDB20* codeview_pdb20_record = + MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>( + file_contents, *codeview_record); + ASSERT_TRUE(codeview_pdb20_record); + EXPECT_EQ(static_cast<uint32_t>(expected_pdb_timestamp), + codeview_pdb20_record->timestamp); + EXPECT_EQ(expected_pdb_age, codeview_pdb20_record->age); + + observed_pdb_name.assign( + reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]), + codeview_record->DataSize - offsetof(CodeViewRecordPDB20, pdb_name)); + } + + // Check for, and then remove, the NUL terminator. + EXPECT_EQ('\0', observed_pdb_name[observed_pdb_name.size() - 1]); + observed_pdb_name.resize(observed_pdb_name.size() - 1); + + EXPECT_EQ(expected_pdb_name, observed_pdb_name); + } else { + // There should be no CodeView record. + EXPECT_EQ(0u, codeview_record->DataSize); + EXPECT_EQ(0u, codeview_record->Rva); + } +} + +// If |expected_debug_name| is not nullptr, |misc_record| is used to locate a +// miscellanous debugging record in |file_contents|, and its fields are compared +// against the the |expected_debug_*| values. If |expected_debug_name| is +// nullptr, |misc_record| must not point to anything. +void ExpectMiscellaneousDebugRecord( + const MINIDUMP_LOCATION_DESCRIPTOR* misc_record, + const std::string& file_contents, + const char* expected_debug_name, + uint32_t expected_debug_type, + bool expected_debug_utf16) { + if (expected_debug_name) { + EXPECT_NE(0u, misc_record->Rva); + const IMAGE_DEBUG_MISC* misc_debug_record = + MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents, + *misc_record); + ASSERT_TRUE(misc_debug_record); + EXPECT_EQ(expected_debug_type, misc_debug_record->DataType); + EXPECT_EQ(expected_debug_utf16, misc_debug_record->Unicode != 0); + EXPECT_EQ(0u, misc_debug_record->Reserved[0]); + EXPECT_EQ(0u, misc_debug_record->Reserved[1]); + EXPECT_EQ(0u, misc_debug_record->Reserved[2]); + + // Check for the NUL terminator. + size_t bytes_available = + misc_debug_record->Length - offsetof(IMAGE_DEBUG_MISC, Data); + EXPECT_EQ('\0', misc_debug_record->Data[bytes_available - 1]); + std::string observed_data( + reinterpret_cast<const char*>(misc_debug_record->Data)); + + size_t bytes_used; + if (misc_debug_record->Unicode) { + base::string16 observed_data_utf16( + reinterpret_cast<const base::char16*>(misc_debug_record->Data)); + bytes_used = (observed_data_utf16.size() + 1) * sizeof(base::char16); + observed_data = base::UTF16ToUTF8(observed_data_utf16); + } else { + observed_data = reinterpret_cast<const char*>(misc_debug_record->Data); + bytes_used = (observed_data.size() + 1) * sizeof(char); + } + EXPECT_LE(bytes_used, bytes_available); + + // Make sure that any padding bytes after the first NUL are also NUL. + for (size_t index = bytes_used; index < bytes_available; ++index) { + EXPECT_EQ('\0', misc_debug_record->Data[index]); + } + + EXPECT_EQ(expected_debug_name, observed_data); + } else { + // There should be no miscellaneous debugging record. + EXPECT_EQ(0u, misc_record->DataSize); + EXPECT_EQ(0u, misc_record->Rva); + } +} + +// ExpectModule() verifies that |expected| matches |observed|. Fields that are +// supposed to contain constant magic numbers are verified against the expected +// constants instead of |expected|. Reserved fields are verified to be 0. RVA +// and MINIDUMP_LOCATION_DESCRIPTOR fields are not verified against |expected|. +// Instead, |ModuleNameRva| is used to locate the module name, which is compared +// against |expected_module_name|. ExpectCodeViewRecord() and +// ExpectMiscellaneousDebugRecord() are used to verify the |CvRecord| and +// |MiscRecord| fields against |expected_pdb_*| and |expected_debug_*| +// parameters, respectively. +void ExpectModule(const MINIDUMP_MODULE* expected, + const MINIDUMP_MODULE* observed, + const std::string& file_contents, + const std::string& expected_module_name, + const char* expected_pdb_name, + const UUID* expected_pdb_uuid, + time_t expected_pdb_timestamp, + uint32_t expected_pdb_age, + const char* expected_debug_name, + uint32_t expected_debug_type, + bool expected_debug_utf16) { + EXPECT_EQ(expected->BaseOfImage, observed->BaseOfImage); + EXPECT_EQ(expected->SizeOfImage, observed->SizeOfImage); + EXPECT_EQ(expected->CheckSum, observed->CheckSum); + EXPECT_EQ(expected->TimeDateStamp, observed->TimeDateStamp); + EXPECT_EQ(implicit_cast<uint32_t>(VS_FFI_SIGNATURE), + observed->VersionInfo.dwSignature); + EXPECT_EQ(implicit_cast<uint32_t>(VS_FFI_STRUCVERSION), + observed->VersionInfo.dwStrucVersion); + EXPECT_EQ(expected->VersionInfo.dwFileVersionMS, + observed->VersionInfo.dwFileVersionMS); + EXPECT_EQ(expected->VersionInfo.dwFileVersionLS, + observed->VersionInfo.dwFileVersionLS); + EXPECT_EQ(expected->VersionInfo.dwProductVersionMS, + observed->VersionInfo.dwProductVersionMS); + EXPECT_EQ(expected->VersionInfo.dwProductVersionLS, + observed->VersionInfo.dwProductVersionLS); + EXPECT_EQ(expected->VersionInfo.dwFileFlagsMask, + observed->VersionInfo.dwFileFlagsMask); + EXPECT_EQ(expected->VersionInfo.dwFileFlags, + observed->VersionInfo.dwFileFlags); + EXPECT_EQ(expected->VersionInfo.dwFileOS, observed->VersionInfo.dwFileOS); + EXPECT_EQ(expected->VersionInfo.dwFileType, observed->VersionInfo.dwFileType); + EXPECT_EQ(expected->VersionInfo.dwFileSubtype, + observed->VersionInfo.dwFileSubtype); + EXPECT_EQ(expected->VersionInfo.dwFileDateMS, + observed->VersionInfo.dwFileDateMS); + EXPECT_EQ(expected->VersionInfo.dwFileDateLS, + observed->VersionInfo.dwFileDateLS); + EXPECT_EQ(0u, observed->Reserved0); + EXPECT_EQ(0u, observed->Reserved1); + + EXPECT_NE(0u, observed->ModuleNameRva); + base::string16 observed_module_name_utf16 = + MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva); + base::string16 expected_module_name_utf16 = + base::UTF8ToUTF16(expected_module_name); + EXPECT_EQ(expected_module_name_utf16, observed_module_name_utf16); + + ASSERT_NO_FATAL_FAILURE(ExpectCodeViewRecord(&observed->CvRecord, + file_contents, + expected_pdb_name, + expected_pdb_uuid, + expected_pdb_timestamp, + expected_pdb_age)); + + ASSERT_NO_FATAL_FAILURE(ExpectMiscellaneousDebugRecord(&observed->MiscRecord, + file_contents, + expected_debug_name, + expected_debug_type, + expected_debug_utf16)); +} + +TEST(MinidumpModuleWriter, EmptyModule) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + + const char kModuleName[] = "test_executable"; + + auto module_writer = make_scoped_ptr(new MinidumpModuleWriter()); + module_writer->SetName(kModuleName); + + module_list_writer->AddModule(crashpad::move(module_writer)); + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(1u, module_list->NumberOfModules); + + MINIDUMP_MODULE expected = {}; + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + nullptr, + nullptr, + 0, + 0, + nullptr, + 0, + false)); +} + +TEST(MinidumpModuleWriter, OneModule) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + + const char kModuleName[] = "statically_linked"; + const uint64_t kModuleBase = 0x10da69000; + const uint32_t kModuleSize = 0x1000; + const uint32_t kChecksum = 0x76543210; + const time_t kTimestamp = 0x386d4380; + const uint32_t kFileVersionMS = 0x00010002; + const uint32_t kFileVersionLS = 0x00030004; + const uint32_t kProductVersionMS = 0x00050006; + const uint32_t kProductVersionLS = 0x00070008; + const uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE | + VS_FF_PATCHED | VS_FF_PRIVATEBUILD | + VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD; + const uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD; + const uint32_t kFileOS = VOS_DOS; + const uint32_t kFileType = VFT_DRV; + const uint32_t kFileSubtype = VFT2_DRV_KEYBOARD; + const char kPDBName[] = "statical.pdb"; + const uint8_t kPDBUUIDBytes[16] = + {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x08, 0x19, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x7f}; + UUID pdb_uuid; + pdb_uuid.InitializeFromBytes(kPDBUUIDBytes); + const uint32_t kPDBAge = 1; + const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; + const char kDebugName[] = "statical.dbg"; + const bool kDebugUTF16 = false; + + auto module_writer = make_scoped_ptr(new MinidumpModuleWriter()); + module_writer->SetName(kModuleName); + module_writer->SetImageBaseAddress(kModuleBase); + module_writer->SetImageSize(kModuleSize); + module_writer->SetChecksum(kChecksum); + module_writer->SetTimestamp(kTimestamp); + module_writer->SetFileVersion(kFileVersionMS >> 16, + kFileVersionMS & 0xffff, + kFileVersionLS >> 16, + kFileVersionLS & 0xffff); + module_writer->SetProductVersion(kProductVersionMS >> 16, + kProductVersionMS & 0xffff, + kProductVersionLS >> 16, + kProductVersionLS & 0xffff); + module_writer->SetFileFlagsAndMask(kFileFlags, kFileFlagsMask); + module_writer->SetFileOS(kFileOS); + module_writer->SetFileTypeAndSubtype(kFileType, kFileSubtype); + + auto codeview_pdb70_writer = + make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer()); + codeview_pdb70_writer->SetPDBName(kPDBName); + codeview_pdb70_writer->SetUUIDAndAge(pdb_uuid, kPDBAge); + module_writer->SetCodeViewRecord(crashpad::move(codeview_pdb70_writer)); + + auto misc_debug_writer = + make_scoped_ptr(new MinidumpModuleMiscDebugRecordWriter()); + misc_debug_writer->SetDataType(kDebugType); + misc_debug_writer->SetData(kDebugName, kDebugUTF16); + module_writer->SetMiscDebugRecord(crashpad::move(misc_debug_writer)); + + module_list_writer->AddModule(crashpad::move(module_writer)); + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(1u, module_list->NumberOfModules); + + MINIDUMP_MODULE expected = {}; + expected.BaseOfImage = kModuleBase; + expected.SizeOfImage = kModuleSize; + expected.CheckSum = kChecksum; + expected.TimeDateStamp = kTimestamp; + expected.VersionInfo.dwFileVersionMS = kFileVersionMS; + expected.VersionInfo.dwFileVersionLS = kFileVersionLS; + expected.VersionInfo.dwProductVersionMS = kProductVersionMS; + expected.VersionInfo.dwProductVersionLS = kProductVersionLS; + expected.VersionInfo.dwFileFlagsMask = kFileFlagsMask; + expected.VersionInfo.dwFileFlags = kFileFlags; + expected.VersionInfo.dwFileOS = kFileOS; + expected.VersionInfo.dwFileType = kFileType; + expected.VersionInfo.dwFileSubtype = kFileSubtype; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + kPDBName, + &pdb_uuid, + 0, + kPDBAge, + kDebugName, + kDebugType, + kDebugUTF16)); +} + +TEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) { + // MinidumpModuleWriter.OneModule tested with a PDB 7.0 link as the CodeView + // record and an IMAGE_DEBUG_MISC record in UTF-8. This test exercises the + // alternatives, a PDB 2.0 link as the CodeView record and an IMAGE_DEBUG_MISC + // record with UTF-16 data. + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + + const char kModuleName[] = "dinosaur"; + const char kPDBName[] = "d1n05.pdb"; + const time_t kPDBTimestamp = 0x386d4380; + const uint32_t kPDBAge = 1; + const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; + const char kDebugName[] = "d1n05.dbg"; + const bool kDebugUTF16 = true; + + auto module_writer = make_scoped_ptr(new MinidumpModuleWriter()); + module_writer->SetName(kModuleName); + + auto codeview_pdb20_writer = + make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB20Writer()); + codeview_pdb20_writer->SetPDBName(kPDBName); + codeview_pdb20_writer->SetTimestampAndAge(kPDBTimestamp, kPDBAge); + module_writer->SetCodeViewRecord(crashpad::move(codeview_pdb20_writer)); + + auto misc_debug_writer = + make_scoped_ptr(new MinidumpModuleMiscDebugRecordWriter()); + misc_debug_writer->SetDataType(kDebugType); + misc_debug_writer->SetData(kDebugName, kDebugUTF16); + module_writer->SetMiscDebugRecord(crashpad::move(misc_debug_writer)); + + module_list_writer->AddModule(crashpad::move(module_writer)); + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(1u, module_list->NumberOfModules); + + MINIDUMP_MODULE expected = {}; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + kPDBName, + nullptr, + kPDBTimestamp, + kPDBAge, + kDebugName, + kDebugType, + kDebugUTF16)); +} + +TEST(MinidumpModuleWriter, ThreeModules) { + // As good exercise, this test uses three modules, one with a PDB 7.0 link as + // its CodeView record, one with no CodeView record, and one with a PDB 2.0 + // link as its CodeView record. + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + + const char kModuleName0[] = "main"; + const uint64_t kModuleBase0 = 0x100101000; + const uint32_t kModuleSize0 = 0xf000; + const char kPDBName0[] = "main"; + const uint8_t kPDBUUIDBytes0[16] = + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, + 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99}; + UUID pdb_uuid_0; + pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0); + const uint32_t kPDBAge0 = 0; + + const char kModuleName1[] = "ld.so"; + const uint64_t kModuleBase1 = 0x200202000; + const uint32_t kModuleSize1 = 0x1e000; + + const char kModuleName2[] = "libc.so"; + const uint64_t kModuleBase2 = 0x300303000; + const uint32_t kModuleSize2 = 0x2d000; + const char kPDBName2[] = "libc.so"; + const time_t kPDBTimestamp2 = 0x386d4380; + const uint32_t kPDBAge2 = 2; + + auto module_writer_0 = make_scoped_ptr(new MinidumpModuleWriter()); + module_writer_0->SetName(kModuleName0); + module_writer_0->SetImageBaseAddress(kModuleBase0); + module_writer_0->SetImageSize(kModuleSize0); + + auto codeview_pdb70_writer_0 = + make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer()); + codeview_pdb70_writer_0->SetPDBName(kPDBName0); + codeview_pdb70_writer_0->SetUUIDAndAge(pdb_uuid_0, kPDBAge0); + module_writer_0->SetCodeViewRecord(crashpad::move(codeview_pdb70_writer_0)); + + module_list_writer->AddModule(crashpad::move(module_writer_0)); + + auto module_writer_1 = make_scoped_ptr(new MinidumpModuleWriter()); + module_writer_1->SetName(kModuleName1); + module_writer_1->SetImageBaseAddress(kModuleBase1); + module_writer_1->SetImageSize(kModuleSize1); + + module_list_writer->AddModule(crashpad::move(module_writer_1)); + + auto module_writer_2 = make_scoped_ptr(new MinidumpModuleWriter()); + module_writer_2->SetName(kModuleName2); + module_writer_2->SetImageBaseAddress(kModuleBase2); + module_writer_2->SetImageSize(kModuleSize2); + + auto codeview_pdb70_writer_2 = + make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB20Writer()); + codeview_pdb70_writer_2->SetPDBName(kPDBName2); + codeview_pdb70_writer_2->SetTimestampAndAge(kPDBTimestamp2, kPDBAge2); + module_writer_2->SetCodeViewRecord(crashpad::move(codeview_pdb70_writer_2)); + + module_list_writer->AddModule(crashpad::move(module_writer_2)); + + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(3u, module_list->NumberOfModules); + + MINIDUMP_MODULE expected = {}; + + { + SCOPED_TRACE("module 0"); + + expected.BaseOfImage = kModuleBase0; + expected.SizeOfImage = kModuleSize0; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName0, + kPDBName0, + &pdb_uuid_0, + 0, + kPDBAge0, + nullptr, + 0, + false)); + } + + { + SCOPED_TRACE("module 1"); + + expected.BaseOfImage = kModuleBase1; + expected.SizeOfImage = kModuleSize1; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[1], + string_file.string(), + kModuleName1, + nullptr, + nullptr, + 0, + 0, + nullptr, + 0, + false)); + } + + { + SCOPED_TRACE("module 2"); + + expected.BaseOfImage = kModuleBase2; + expected.SizeOfImage = kModuleSize2; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[2], + string_file.string(), + kModuleName2, + kPDBName2, + nullptr, + kPDBTimestamp2, + kPDBAge2, + nullptr, + 0, + false)); + } +} + +void InitializeTestModuleSnapshotFromMinidumpModule( + TestModuleSnapshot* module_snapshot, + const MINIDUMP_MODULE& minidump_module, + const std::string& name, + const std::string& pdb_name, + const crashpad::UUID& uuid, + uint32_t age) { + module_snapshot->SetName(name); + + module_snapshot->SetAddressAndSize(minidump_module.BaseOfImage, + minidump_module.SizeOfImage); + module_snapshot->SetTimestamp(minidump_module.TimeDateStamp); + module_snapshot->SetFileVersion( + minidump_module.VersionInfo.dwFileVersionMS >> 16, + minidump_module.VersionInfo.dwFileVersionMS & 0xffff, + minidump_module.VersionInfo.dwFileVersionLS >> 16, + minidump_module.VersionInfo.dwFileVersionLS & 0xffff); + module_snapshot->SetSourceVersion( + minidump_module.VersionInfo.dwProductVersionMS >> 16, + minidump_module.VersionInfo.dwProductVersionMS & 0xffff, + minidump_module.VersionInfo.dwProductVersionLS >> 16, + minidump_module.VersionInfo.dwProductVersionLS & 0xffff); + + ModuleSnapshot::ModuleType module_type; + switch (minidump_module.VersionInfo.dwFileType) { + case VFT_APP: + module_type = ModuleSnapshot::kModuleTypeExecutable; + break; + case VFT_DLL: + module_type = ModuleSnapshot::kModuleTypeSharedLibrary; + break; + default: + module_type = ModuleSnapshot::kModuleTypeUnknown; + break; + } + module_snapshot->SetModuleType(module_type); + + module_snapshot->SetUUIDAndAge(uuid, age); + module_snapshot->SetDebugFileName(pdb_name); +} + +TEST(MinidumpModuleWriter, InitializeFromSnapshot) { + MINIDUMP_MODULE expect_modules[3] = {}; + const char* module_paths[arraysize(expect_modules)] = {}; + const char* module_names[arraysize(expect_modules)] = {}; + const char* module_pdbs[arraysize(expect_modules)] = {}; + UUID uuids[arraysize(expect_modules)] = {}; + uint32_t ages[arraysize(expect_modules)] = {}; + + expect_modules[0].BaseOfImage = 0x100101000; + expect_modules[0].SizeOfImage = 0xf000; + expect_modules[0].TimeDateStamp = 0x01234567; + expect_modules[0].VersionInfo.dwFileVersionMS = 0x00010002; + expect_modules[0].VersionInfo.dwFileVersionLS = 0x00030004; + expect_modules[0].VersionInfo.dwProductVersionMS = 0x00050006; + expect_modules[0].VersionInfo.dwProductVersionLS = 0x00070008; + expect_modules[0].VersionInfo.dwFileType = VFT_APP; + module_paths[0] = "/usr/bin/true"; + module_names[0] = "true"; + module_pdbs[0] = "true"; + const uint8_t kUUIDBytes0[16] = + {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + uuids[0].InitializeFromBytes(kUUIDBytes0); + ages[0] = 10; + + expect_modules[1].BaseOfImage = 0x200202000; + expect_modules[1].SizeOfImage = 0x1e1000; + expect_modules[1].TimeDateStamp = 0x89abcdef; + expect_modules[1].VersionInfo.dwFileVersionMS = 0x0009000a; + expect_modules[1].VersionInfo.dwFileVersionLS = 0x000b000c; + expect_modules[1].VersionInfo.dwProductVersionMS = 0x000d000e; + expect_modules[1].VersionInfo.dwProductVersionLS = 0x000f0000; + expect_modules[1].VersionInfo.dwFileType = VFT_DLL; + module_paths[1] = "/usr/lib/libSystem.B.dylib"; + module_names[1] = "libSystem.B.dylib"; + module_pdbs[1] = "libSystem.B.dylib.pdb"; + const uint8_t kUUIDBytes1[16] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + uuids[1].InitializeFromBytes(kUUIDBytes1); + ages[1] = 20; + + expect_modules[2].BaseOfImage = 0x300303000; + expect_modules[2].SizeOfImage = 0x2d000; + expect_modules[2].TimeDateStamp = 0x76543210; + expect_modules[2].VersionInfo.dwFileVersionMS = 0x11112222; + expect_modules[2].VersionInfo.dwFileVersionLS = 0x33334444; + expect_modules[2].VersionInfo.dwProductVersionMS = 0x9999aaaa; + expect_modules[2].VersionInfo.dwProductVersionLS = 0xbbbbcccc; + expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN; + module_paths[2] = "/usr/lib/dyld"; + module_names[2] = "dyld"; + module_pdbs[2] = "/usr/lib/dyld.pdb"; + const uint8_t kUUIDBytes2[16] = + {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0}; + uuids[2].InitializeFromBytes(kUUIDBytes2); + ages[2] = 30; + + PointerVector<TestModuleSnapshot> module_snapshots_owner; + std::vector<const ModuleSnapshot*> module_snapshots; + for (size_t index = 0; index < arraysize(expect_modules); ++index) { + TestModuleSnapshot* module_snapshot = new TestModuleSnapshot(); + module_snapshots_owner.push_back(module_snapshot); + InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot, + expect_modules[index], + module_paths[index], + module_pdbs[index], + uuids[index], + ages[index]); + module_snapshots.push_back(module_snapshot); + } + + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + module_list_writer->InitializeFromSnapshot(module_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + ASSERT_EQ(3u, module_list->NumberOfModules); + + for (size_t index = 0; index < module_list->NumberOfModules; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expect_modules[index], + &module_list->Modules[index], + string_file.string(), + module_paths[index], + module_pdbs[index], + &uuids[index], + 0, + ages[index], + nullptr, + 0, + false)); + } +} + +TEST(MinidumpModuleWriterDeathTest, NoModuleName) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + auto module_writer = make_scoped_ptr(new MinidumpModuleWriter()); + module_list_writer->AddModule(crashpad::move(module_writer)); + minidump_file_writer.AddStream(crashpad::move(module_list_writer)); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "name_"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc new file mode 100644 index 0000000..74508305 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc
@@ -0,0 +1,98 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_rva_list_writer.h" + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace internal { + +MinidumpRVAListWriter::MinidumpRVAListWriter() + : MinidumpWritable(), + rva_list_base_(new MinidumpRVAList()), + children_(), + child_rvas_() { +} + +MinidumpRVAListWriter::~MinidumpRVAListWriter() { +} + +void MinidumpRVAListWriter::AddChild(scoped_ptr<MinidumpWritable> child) { + DCHECK_EQ(state(), kStateMutable); + + children_.push_back(child.release()); +} + +bool MinidumpRVAListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + DCHECK(child_rvas_.empty()); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t child_count = children_.size(); + if (!AssignIfInRange(&rva_list_base_->count, child_count)) { + LOG(ERROR) << "child_count " << child_count << " out of range"; + return false; + } + + child_rvas_.resize(child_count); + for (size_t index = 0; index < child_count; ++index) { + children_[index]->RegisterRVA(&child_rvas_[index]); + } + + return true; +} + +size_t MinidumpRVAListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(*rva_list_base_) + children_.size() * sizeof(RVA); +} + +std::vector<MinidumpWritable*> MinidumpRVAListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children; + for (MinidumpWritable* child : children_) { + children.push_back(child); + } + + return children; +} + +bool MinidumpRVAListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(children_.size(), child_rvas_.size()); + + WritableIoVec iov; + iov.iov_base = rva_list_base_.get(); + iov.iov_len = sizeof(*rva_list_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + if (!child_rvas_.empty()) { + iov.iov_base = &child_rvas_[0]; + iov.iov_len = child_rvas_.size() * sizeof(RVA); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h new file mode 100644 index 0000000..42bb1b5 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h
@@ -0,0 +1,79 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_ +#define CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace internal { + +//! \brief The writer for a MinidumpRVAList object in a minidump file, +//! containing a list of ::RVA pointers. +class MinidumpRVAListWriter : public MinidumpWritable { + protected: + MinidumpRVAListWriter(); + ~MinidumpRVAListWriter() override; + + //! \brief Adds an ::RVA referencing an MinidumpWritable to the + //! MinidumpRVAList. + //! + //! This object takes ownership of \a child and becomes its parent in the + //! overall tree of MinidumpWritable objects. + //! + //! To provide type-correctness, subclasses are expected to provide a public + //! method that accepts a `scoped_ptr`-wrapped argument of the proper + //! MinidumpWritable subclass, and call this method with that argument. + //! + //! \note Valid in #kStateMutable. + void AddChild(scoped_ptr<MinidumpWritable> child); + + //! \brief Returns `true` if no child objects have been added by AddChild(), + //! and `false` if child objects are present. + bool IsEmpty() const { return children_.empty(); } + + //! \brief Returns an object’s ::RVA objects referencing its children. + //! + //! \note The returned vector will be empty until the object advances to + //! #kStateFrozen or beyond. + const std::vector<RVA>& child_rvas() const { return child_rvas_; } + + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + scoped_ptr<MinidumpRVAList> rva_list_base_; + PointerVector<MinidumpWritable> children_; + std::vector<RVA> child_rvas_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpRVAListWriter); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc new file mode 100644 index 0000000..19a908ca --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc
@@ -0,0 +1,103 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_rva_list_writer.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_rva_list_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMinidumpRVAListWriter final : public internal::MinidumpRVAListWriter { + public: + TestMinidumpRVAListWriter() : MinidumpRVAListWriter() {} + ~TestMinidumpRVAListWriter() override {} + + void AddChild(uint32_t value) { + auto child = make_scoped_ptr(new TestUInt32MinidumpWritable(value)); + MinidumpRVAListWriter::AddChild(crashpad::move(child)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestMinidumpRVAListWriter); +}; + +TEST(MinidumpRVAListWriter, Empty) { + TestMinidumpRVAListWriter list_writer; + + StringFile string_file; + + ASSERT_TRUE(list_writer.WriteEverything(&string_file)); + EXPECT_EQ(sizeof(MinidumpRVAList), string_file.string().size()); + + const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 0); + ASSERT_TRUE(list); +} + +TEST(MinidumpRVAListWriter, OneChild) { + TestMinidumpRVAListWriter list_writer; + + const uint32_t kValue = 0; + list_writer.AddChild(kValue); + + StringFile string_file; + + ASSERT_TRUE(list_writer.WriteEverything(&string_file)); + + const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 1); + ASSERT_TRUE(list); + + const uint32_t* child = MinidumpWritableAtRVA<uint32_t>( + string_file.string(), list->children[0]); + ASSERT_TRUE(child); + EXPECT_EQ(kValue, *child); +} + +TEST(MinidumpRVAListWriter, ThreeChildren) { + TestMinidumpRVAListWriter list_writer; + + const uint32_t kValues[] = { 0x80000000, 0x55555555, 0x66006600 }; + + list_writer.AddChild(kValues[0]); + list_writer.AddChild(kValues[1]); + list_writer.AddChild(kValues[2]); + + StringFile string_file; + + ASSERT_TRUE(list_writer.WriteEverything(&string_file)); + + const MinidumpRVAList* list = + MinidumpRVAListAtStart(string_file.string(), arraysize(kValues)); + ASSERT_TRUE(list); + + for (size_t index = 0; index < arraysize(kValues); ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + + const uint32_t* child = MinidumpWritableAtRVA<uint32_t>( + string_file.string(), list->children[index]); + ASSERT_TRUE(child); + EXPECT_EQ(kValues[index], *child); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc new file mode 100644 index 0000000..50c1f720 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc
@@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_simple_string_dictionary_writer.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpSimpleStringDictionaryEntryWriter:: + MinidumpSimpleStringDictionaryEntryWriter() + : MinidumpWritable(), entry_(), key_(), value_() { +} + +MinidumpSimpleStringDictionaryEntryWriter:: + ~MinidumpSimpleStringDictionaryEntryWriter() { +} + +const MinidumpSimpleStringDictionaryEntry* +MinidumpSimpleStringDictionaryEntryWriter::MinidumpSimpleStringDictionaryEntry() + const { + DCHECK_EQ(state(), kStateWritable); + + return &entry_; +} + +void MinidumpSimpleStringDictionaryEntryWriter::SetKeyValue( + const std::string& key, + const std::string& value) { + DCHECK_EQ(state(), kStateMutable); + + key_.SetUTF8(key); + value_.SetUTF8(value); +} + +bool MinidumpSimpleStringDictionaryEntryWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + key_.RegisterRVA(&entry_.key); + value_.RegisterRVA(&entry_.value); + + return true; +} + +size_t MinidumpSimpleStringDictionaryEntryWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its + // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a + // MinidumpSimpleStringDictionary, and its children are responsible for + // writing themselves. + return 0; +} + +std::vector<internal::MinidumpWritable*> +MinidumpSimpleStringDictionaryEntryWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children(1, &key_); + children.push_back(&value_); + return children; +} + +bool MinidumpSimpleStringDictionaryEntryWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its + // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a + // MinidumpSimpleStringDictionary, and its children are responsible for + // writing themselves. + return true; +} + +MinidumpSimpleStringDictionaryWriter::MinidumpSimpleStringDictionaryWriter() + : MinidumpWritable(), + entries_(), + simple_string_dictionary_base_(new MinidumpSimpleStringDictionary()) { +} + +MinidumpSimpleStringDictionaryWriter::~MinidumpSimpleStringDictionaryWriter() { + STLDeleteContainerPairSecondPointers(entries_.begin(), entries_.end()); +} + +void MinidumpSimpleStringDictionaryWriter::InitializeFromMap( + const std::map<std::string, std::string>& map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(entries_.empty()); + + for (const auto& iterator : map) { + auto entry = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry->SetKeyValue(iterator.first, iterator.second); + AddEntry(crashpad::move(entry)); + } +} + +void MinidumpSimpleStringDictionaryWriter::AddEntry( + scoped_ptr<MinidumpSimpleStringDictionaryEntryWriter> entry) { + DCHECK_EQ(state(), kStateMutable); + + const std::string& key = entry->Key(); + auto iterator = entries_.find(key); + if (iterator != entries_.end()) { + delete iterator->second; + iterator->second = entry.release(); + } else { + entries_[key] = entry.release(); + } +} + +bool MinidumpSimpleStringDictionaryWriter::IsUseful() const { + return !entries_.empty(); +} + +bool MinidumpSimpleStringDictionaryWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t entry_count = entries_.size(); + if (!AssignIfInRange(&simple_string_dictionary_base_->count, entry_count)) { + LOG(ERROR) << "entry_count " << entry_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpSimpleStringDictionaryWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(*simple_string_dictionary_base_) + + entries_.size() * sizeof(MinidumpSimpleStringDictionaryEntry); +} + +std::vector<internal::MinidumpWritable*> +MinidumpSimpleStringDictionaryWriter::Children() { + DCHECK_GE(state(), kStateMutable); + + std::vector<MinidumpWritable*> children; + for (const auto& key_entry : entries_) { + children.push_back(key_entry.second); + } + + return children; +} + +bool MinidumpSimpleStringDictionaryWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_GE(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = simple_string_dictionary_base_.get(); + iov.iov_len = sizeof(*simple_string_dictionary_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (const auto& key_entry : entries_) { + iov.iov_base = key_entry.second->MinidumpSimpleStringDictionaryEntry(); + iov.iov_len = sizeof(MinidumpSimpleStringDictionaryEntry); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h b/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h new file mode 100644 index 0000000..88059bd --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h
@@ -0,0 +1,145 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_ + +#include <sys/types.h> + +#include <string> +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +//! \brief The writer for a MinidumpSimpleStringDictionaryEntry object in a +//! minidump file. +//! +//! Because MinidumpSimpleStringDictionaryEntry objects only appear as elements +//! of MinidumpSimpleStringDictionary objects, this class does not write any +//! data on its own. It makes its MinidumpSimpleStringDictionaryEntry data +//! available to its MinidumpSimpleStringDictionaryWriter parent, which writes +//! it as part of a MinidumpSimpleStringDictionary. +class MinidumpSimpleStringDictionaryEntryWriter final + : public internal::MinidumpWritable { + public: + MinidumpSimpleStringDictionaryEntryWriter(); + ~MinidumpSimpleStringDictionaryEntryWriter() override; + + //! \brief Returns a MinidumpSimpleStringDictionaryEntry referencing this + //! object’s data. + //! + //! This method is expected to be called by a + //! MinidumpSimpleStringDictionaryWriter in order to obtain a + //! MinidumpSimpleStringDictionaryEntry to include in its list. + //! + //! \note Valid in #kStateWritable. + const MinidumpSimpleStringDictionaryEntry* + MinidumpSimpleStringDictionaryEntry() const; + + //! \brief Sets the strings to be written as the entry object’s key and value. + //! + //! \note Valid in #kStateMutable. + void SetKeyValue(const std::string& key, const std::string& value); + + //! \brief Retrieves the key to be written. + //! + //! \note Valid in any state. + const std::string& Key() const { return key_.UTF8(); } + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + struct MinidumpSimpleStringDictionaryEntry entry_; + internal::MinidumpUTF8StringWriter key_; + internal::MinidumpUTF8StringWriter value_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpSimpleStringDictionaryEntryWriter); +}; + +//! \brief The writer for a MinidumpSimpleStringDictionary object in a minidump +//! file, containing a list of MinidumpSimpleStringDictionaryEntry objects. +//! +//! Because this class writes a representatin of a dictionary, the order of +//! entries is insignificant. Entries may be written in any order. +class MinidumpSimpleStringDictionaryWriter final + : public internal::MinidumpWritable { + public: + MinidumpSimpleStringDictionaryWriter(); + ~MinidumpSimpleStringDictionaryWriter() override; + + //! \brief Adds an initialized MinidumpSimpleStringDictionaryEntryWriter for + //! each key-value pair in \a map to the MinidumpSimpleStringDictionary. + //! + //! \param[in] map The map to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromMap(const std::map<std::string, std::string>& map); + + //! \brief Adds a MinidumpSimpleStringDictionaryEntryWriter to the + //! MinidumpSimpleStringDictionary. + //! + //! This object takes ownership of \a entry and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! If the key contained in \a entry duplicates the key of an entry already + //! present in the MinidumpSimpleStringDictionary, the new \a entry will + //! replace the previous one. + //! + //! \note Valid in #kStateMutable. + void AddEntry(scoped_ptr<MinidumpSimpleStringDictionaryEntryWriter> entry); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying entries would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + // This object owns the MinidumpSimpleStringDictionaryEntryWriter objects. + std::map<std::string, MinidumpSimpleStringDictionaryEntryWriter*> entries_; + + scoped_ptr<MinidumpSimpleStringDictionary> simple_string_dictionary_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpSimpleStringDictionaryWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc new file mode 100644 index 0000000..a8c8d02d --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc
@@ -0,0 +1,288 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_simple_string_dictionary_writer.h" + +#include <map> +#include <string> + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpSimpleStringDictionary* MinidumpSimpleStringDictionaryAtStart( + const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = static_cast<uint32_t>( + sizeof(MinidumpSimpleStringDictionary) + + count * sizeof(MinidumpSimpleStringDictionaryEntry)); + location_descriptor.Rva = 0; + return MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + file_contents, location_descriptor); +} + +TEST(MinidumpSimpleStringDictionaryWriter, EmptySimpleStringDictionary) { + StringFile string_file; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + + EXPECT_FALSE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary), + string_file.string().size()); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 0); + ASSERT_TRUE(dictionary); + EXPECT_EQ(0u, dictionary->count); +} + +TEST(MinidumpSimpleStringDictionaryWriter, EmptyKeyValue) { + StringFile string_file; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + dictionary_writer.AddEntry(crashpad::move(entry_writer)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + 2 * sizeof(MinidumpUTF8String) + 1 + 3 + 1, // 3 for padding + string_file.string().size()); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1); + ASSERT_TRUE(dictionary); + EXPECT_EQ(1u, dictionary->count); + EXPECT_EQ(12u, dictionary->entries[0].key); + EXPECT_EQ(20u, dictionary->entries[0].value); + EXPECT_EQ("", + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key)); + EXPECT_EQ("", + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value)); +} + +TEST(MinidumpSimpleStringDictionaryWriter, OneKeyValue) { + StringFile string_file; + + char kKey[] = "key"; + char kValue[] = "value"; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry_writer->SetKeyValue(kKey, kValue); + dictionary_writer.AddEntry(crashpad::move(entry_writer)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + 2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue), + string_file.string().size()); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1); + ASSERT_TRUE(dictionary); + EXPECT_EQ(1u, dictionary->count); + EXPECT_EQ(12u, dictionary->entries[0].key); + EXPECT_EQ(20u, dictionary->entries[0].value); + EXPECT_EQ(kKey, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key)); + EXPECT_EQ(kValue, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value)); +} + +TEST(MinidumpSimpleStringDictionaryWriter, ThreeKeysValues) { + StringFile string_file; + + char kKey0[] = "m0"; + char kValue0[] = "value0"; + char kKey1[] = "zzz1"; + char kValue1[] = "v1"; + char kKey2[] = "aa2"; + char kValue2[] = "val2"; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer_0 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry_writer_0->SetKeyValue(kKey0, kValue0); + dictionary_writer.AddEntry(crashpad::move(entry_writer_0)); + auto entry_writer_1 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry_writer_1->SetKeyValue(kKey1, kValue1); + dictionary_writer.AddEntry(crashpad::move(entry_writer_1)); + auto entry_writer_2 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry_writer_2->SetKeyValue(kKey2, kValue2); + dictionary_writer.AddEntry(crashpad::move(entry_writer_2)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) + + 3 * sizeof(MinidumpSimpleStringDictionaryEntry) + + 6 * sizeof(MinidumpUTF8String) + sizeof(kKey2) + + sizeof(kValue2) + 3 + sizeof(kKey0) + 1 + sizeof(kValue0) + 1 + + sizeof(kKey1) + 3 + sizeof(kValue1), + string_file.string().size()); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 3); + ASSERT_TRUE(dictionary); + EXPECT_EQ(3u, dictionary->count); + EXPECT_EQ(28u, dictionary->entries[0].key); + EXPECT_EQ(36u, dictionary->entries[0].value); + EXPECT_EQ(48u, dictionary->entries[1].key); + EXPECT_EQ(56u, dictionary->entries[1].value); + EXPECT_EQ(68u, dictionary->entries[2].key); + EXPECT_EQ(80u, dictionary->entries[2].value); + + // The entries don’t appear in the order they were added. The current + // implementation uses a std::map and sorts keys, so the entires appear in + // alphabetical order. However, this is an implementation detail, and it’s OK + // if the writer stops sorting in this order. Testing for a specific order is + // just the easiest way to write this test while the writer will output things + // in a known order. + EXPECT_EQ(kKey2, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key)); + EXPECT_EQ(kValue2, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value)); + EXPECT_EQ(kKey0, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].key)); + EXPECT_EQ(kValue0, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].value)); + EXPECT_EQ(kKey1, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].key)); + EXPECT_EQ(kValue1, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].value)); +} + +TEST(MinidumpSimpleStringDictionaryWriter, DuplicateKeyValue) { + StringFile string_file; + + char kKey[] = "key"; + char kValue0[] = "fake_value"; + char kValue1[] = "value"; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer_0 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry_writer_0->SetKeyValue(kKey, kValue0); + dictionary_writer.AddEntry(crashpad::move(entry_writer_0)); + auto entry_writer_1 = + make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter()); + entry_writer_1->SetKeyValue(kKey, kValue1); + dictionary_writer.AddEntry(crashpad::move(entry_writer_1)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + 2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue1), + string_file.string().size()); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1); + ASSERT_TRUE(dictionary); + EXPECT_EQ(1u, dictionary->count); + EXPECT_EQ(12u, dictionary->entries[0].key); + EXPECT_EQ(20u, dictionary->entries[0].value); + EXPECT_EQ(kKey, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key)); + EXPECT_EQ(kValue1, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value)); +} + +TEST(MinidumpSimpleStringDictionaryWriter, InitializeFromMap) { + char kKey0[] = "Dictionaries"; + char kValue0[] = "USEFUL*"; + char kKey1[] = "#1 Key!"; + char kValue1[] = ""; + char kKey2[] = "key two"; + char kValue2[] = "value two"; + + std::map<std::string, std::string> map; + map[kKey0] = kValue0; + map[kKey1] = kValue1; + map[kKey2] = kValue2; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + dictionary_writer.InitializeFromMap(map); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + StringFile string_file; + ASSERT_TRUE(dictionary_writer.WriteEverything(&string_file)); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), map.size()); + ASSERT_TRUE(dictionary); + ASSERT_EQ(3u, dictionary->count); + + // The entries don’t appear in the order they were added. The current + // implementation uses a std::map and sorts keys, so the entires appear in + // alphabetical order. However, this is an implementation detail, and it’s OK + // if the writer stops sorting in this order. Testing for a specific order is + // just the easiest way to write this test while the writer will output things + // in a known order. + EXPECT_EQ(kKey1, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key)); + EXPECT_EQ(kValue1, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value)); + EXPECT_EQ(kKey0, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].key)); + EXPECT_EQ(kValue0, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].value)); + EXPECT_EQ(kKey2, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].key)); + EXPECT_EQ(kValue2, + MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].value)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc new file mode 100644 index 0000000..8e5dcb3 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_stream_writer.h" + +#include "base/logging.h" + +namespace crashpad { +namespace internal { + +MinidumpStreamWriter::~MinidumpStreamWriter() { +} + +const MINIDUMP_DIRECTORY* MinidumpStreamWriter::DirectoryListEntry() const { + DCHECK_EQ(state(), kStateWritable); + + return &directory_list_entry_; +} + +MinidumpStreamWriter::MinidumpStreamWriter() + : MinidumpWritable(), directory_list_entry_() { +} + +bool MinidumpStreamWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + directory_list_entry_.StreamType = StreamType(); + RegisterLocationDescriptor(&directory_list_entry_.Location); + + return true; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h b/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h new file mode 100644 index 0000000..c812f5f --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h
@@ -0,0 +1,66 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include "base/basictypes.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { +namespace internal { + +//! \brief The base class for all second-level objects (“streams”) in a minidump +//! file. +//! +//! Instances of subclasses of this class are children of the root-level +//! MinidumpFileWriter object. +class MinidumpStreamWriter : public MinidumpWritable { + public: + ~MinidumpStreamWriter() override; + + //! \brief Returns an object’s stream type. + //! + //! \note Valid in any state. + virtual MinidumpStreamType StreamType() const = 0; + + //! \brief Returns a MINIDUMP_DIRECTORY entry that serves as a pointer to this + //! stream. + //! + //! This method is provided for MinidumpFileWriter, which calls it in order to + //! obtain the directory entry for a stream. + //! + //! \note Valid only in #kStateWritable. + const MINIDUMP_DIRECTORY* DirectoryListEntry() const; + + protected: + MinidumpStreamWriter(); + + // MinidumpWritable: + bool Freeze() override; + + private: + MINIDUMP_DIRECTORY directory_list_entry_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpStreamWriter); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_STREAM_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc new file mode 100644 index 0000000..af210c2 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc
@@ -0,0 +1,139 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_string_writer.h" + +#include <sys/types.h> + +#include "base/logging.h" +#include "minidump/minidump_writer_util.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace internal { + +template <typename Traits> +MinidumpStringWriter<Traits>::MinidumpStringWriter() + : MinidumpWritable(), string_base_(new MinidumpStringType()), string_() { +} + +template <typename Traits> +MinidumpStringWriter<Traits>::~MinidumpStringWriter() { +} + +template <typename Traits> +bool MinidumpStringWriter<Traits>::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t string_bytes = string_.size() * sizeof(string_[0]); + if (!AssignIfInRange(&string_base_->Length, string_bytes)) { + LOG(ERROR) << "string_bytes " << string_bytes << " out of range"; + return false; + } + + return true; +} + +template <typename Traits> +size_t MinidumpStringWriter<Traits>::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // Include the NUL terminator. + return sizeof(*string_base_) + (string_.size() + 1) * sizeof(string_[0]); +} + +template <typename Traits> +bool MinidumpStringWriter<Traits>::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // The string’s length is stored in string_base_, and its data is stored in + // string_. Write them both. + WritableIoVec iov; + iov.iov_base = string_base_.get(); + iov.iov_len = sizeof(*string_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + // Include the NUL terminator. + iov.iov_base = &string_[0]; + iov.iov_len = (string_.size() + 1) * sizeof(string_[0]); + iovecs.push_back(iov); + + return file_writer->WriteIoVec(&iovecs); +} + +// Explicit template instantiation of the forms of MinidumpStringWriter<> used +// as base classes. +template class MinidumpStringWriter<MinidumpStringWriterUTF16Traits>; +template class MinidumpStringWriter<MinidumpStringWriterUTF8Traits>; + +MinidumpUTF16StringWriter::~MinidumpUTF16StringWriter() { +} + +void MinidumpUTF16StringWriter::SetUTF8(const std::string& string_utf8) { + DCHECK_EQ(state(), kStateMutable); + + set_string(MinidumpWriterUtil::ConvertUTF8ToUTF16(string_utf8)); +} + +MinidumpUTF8StringWriter::~MinidumpUTF8StringWriter() { +} + +template <typename MinidumpStringWriterType> +MinidumpStringListWriter<MinidumpStringWriterType>::MinidumpStringListWriter() + : MinidumpRVAListWriter() { +} + +template <typename MinidumpStringWriterType> +MinidumpStringListWriter< + MinidumpStringWriterType>::~MinidumpStringListWriter() { +} + +template <typename MinidumpStringWriterType> +void MinidumpStringListWriter<MinidumpStringWriterType>::InitializeFromVector( + const std::vector<std::string>& vector) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(IsEmpty()); + + for (const std::string& string : vector) { + AddStringUTF8(string); + } +} + +template <typename MinidumpStringWriterType> +void MinidumpStringListWriter<MinidumpStringWriterType>::AddStringUTF8( + const std::string& string_utf8) { + auto string_writer = make_scoped_ptr(new MinidumpStringWriterType()); + string_writer->SetUTF8(string_utf8); + AddChild(crashpad::move(string_writer)); +} + +template <typename MinidumpStringWriterType> +bool MinidumpStringListWriter<MinidumpStringWriterType>::IsUseful() const { + return !IsEmpty(); +} + +// Explicit template instantiation of the forms of MinidumpStringListWriter<> +// used as type aliases. +template class MinidumpStringListWriter<MinidumpUTF16StringWriter>; +template class MinidumpStringListWriter<MinidumpUTF8StringWriter>; + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_string_writer.h b/third_party/crashpad/crashpad/minidump/minidump_string_writer.h new file mode 100644 index 0000000..ec57179 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_string_writer.h
@@ -0,0 +1,183 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_rva_list_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { +namespace internal { + +//! \cond + +struct MinidumpStringWriterUTF16Traits { + using StringType = base::string16; + using MinidumpStringType = MINIDUMP_STRING; +}; + +struct MinidumpStringWriterUTF8Traits { + using StringType = std::string; + using MinidumpStringType = MinidumpUTF8String; +}; + +//! \endcond + +//! \brief Writes a variable-length string to a minidump file in accordance with +//! the string type’s characteristics. +//! +//! MinidumpStringWriter objects should not be instantiated directly. To write +//! strings to minidump file, use the MinidumpUTF16StringWriter and +//! MinidumpUTF8StringWriter subclasses instead. +template <typename Traits> +class MinidumpStringWriter : public MinidumpWritable { + public: + MinidumpStringWriter(); + ~MinidumpStringWriter() override; + + protected: + using MinidumpStringType = typename Traits::MinidumpStringType; + using StringType = typename Traits::StringType; + + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + //! \brief Sets the string to be written. + //! + //! \note Valid in #kStateMutable. + void set_string(const StringType& string) { string_.assign(string); } + + //! \brief Retrieves the string to be written. + //! + //! \note Valid in any state. + const StringType& string() const { return string_; } + + private: + scoped_ptr<MinidumpStringType> string_base_; + StringType string_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpStringWriter); +}; + +//! \brief Writes a variable-length UTF-16-encoded MINIDUMP_STRING to a minidump +//! file. +//! +//! MinidumpUTF16StringWriter objects should not be instantiated directly +//! outside of the MinidumpWritable family of classes. +class MinidumpUTF16StringWriter final + : public MinidumpStringWriter<MinidumpStringWriterUTF16Traits> { + public: + MinidumpUTF16StringWriter() : MinidumpStringWriter() {} + ~MinidumpUTF16StringWriter() override; + + //! \brief Converts a UTF-8 string to UTF-16 and sets it as the string to be + //! written. + //! + //! \note Valid in #kStateMutable. + void SetUTF8(const std::string& string_utf8); + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpUTF16StringWriter); +}; + +//! \brief Writes a variable-length UTF-8-encoded MinidumpUTF8String to a +//! minidump file. +//! +//! MinidumpUTF8StringWriter objects should not be instantiated directly outside +//! of the MinidumpWritable family of classes. +class MinidumpUTF8StringWriter final + : public MinidumpStringWriter<MinidumpStringWriterUTF8Traits> { + public: + MinidumpUTF8StringWriter() : MinidumpStringWriter() {} + ~MinidumpUTF8StringWriter() override; + + //! \brief Sets the string to be written. + //! + //! \note Valid in #kStateMutable. + void SetUTF8(const std::string& string_utf8) { set_string(string_utf8); } + + //! \brief Retrieves the string to be written. + //! + //! \note Valid in any state. + const std::string& UTF8() const { return string(); } + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpUTF8StringWriter); +}; + +//! \brief The writer for a MinidumpRVAList object in a minidump file, +//! containing a list of \a MinidumpStringWriterType objects. +template <typename MinidumpStringWriterType> +class MinidumpStringListWriter final : public MinidumpRVAListWriter { + public: + MinidumpStringListWriter(); + ~MinidumpStringListWriter() override; + + //! \brief Adds a new \a Traits::MinidumpStringWriterType for each element in + //! \a vector to the MinidumpRVAList. + //! + //! \param[in] vector The vector to use as source data. Each string in the + //! vector is treated as a UTF-8 string, and a new string writer will be + //! created for each one and made a child of the MinidumpStringListWriter. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromVector(const std::vector<std::string>& vector); + + //! \brief Creates a new \a Traits::MinidumpStringWriterType object and adds + //! it to the MinidumpRVAList. + //! + //! This object creates a new string writer with string value \a string_utf8, + //! takes ownership of it, and becomes its parent in the overall tree of + //! MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddStringUTF8(const std::string& string_utf8); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying entries would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + private: + DISALLOW_COPY_AND_ASSIGN(MinidumpStringListWriter); +}; + +} // namespace internal + +using MinidumpUTF16StringListWriter = internal::MinidumpStringListWriter< + internal::MinidumpUTF16StringWriter>; +using MinidumpUTF8StringListWriter = internal::MinidumpStringListWriter< + internal::MinidumpUTF8StringWriter>; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc new file mode 100644 index 0000000..aa8e48c6 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
@@ -0,0 +1,268 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_string_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <sys/types.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/format_macros.h" +#include "base/strings/string16.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_rva_list_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + crashpad::internal::MinidumpUTF16StringWriter string_writer; + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + ASSERT_EQ(6u, string_file.string().size()); + + const MINIDUMP_STRING* minidump_string = + MinidumpStringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + EXPECT_EQ(base::string16(), + MinidumpStringAtRVAAsString(string_file.string(), 0)); + } + + const struct { + size_t input_length; + const char* input_string; + size_t output_length; + base::char16 output_string[10]; + } kTestData[] = { + {0, "", 0, {}}, + {1, "a", 1, {'a'}}, + {2, "\0b", 2, {0, 'b'}}, + {3, "cde", 3, {'c', 'd', 'e'}}, + {9, "Hi world!", 9, {'H', 'i', ' ', 'w', 'o', 'r', 'l', 'd', '!'}}, + {7, "ret\nurn", 7, {'r', 'e', 't', '\n', 'u', 'r', 'n'}}, + {2, "\303\251", 1, {0x00e9}}, // é + + // oóöőo + {8, "o\303\263\303\266\305\221o", 5, {'o', 0x00f3, 0x00f6, 0x151, 'o'}}, + {4, "\360\220\204\202", 2, {0xd800, 0xdd02}}, // 𐄂 (non-BMP) + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", input %s", index, kTestData[index].input_string)); + + // Make sure that the expected output string with its NUL terminator fits in + // the space provided. + ASSERT_EQ( + 0, + kTestData[index] + .output_string[arraysize(kTestData[index].output_string) - 1]); + + string_file.Reset(); + crashpad::internal::MinidumpUTF16StringWriter string_writer; + string_writer.SetUTF8(std::string(kTestData[index].input_string, + kTestData[index].input_length)); + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + + const size_t expected_utf16_units_with_nul = + kTestData[index].output_length + 1; + MINIDUMP_STRING tmp = {0}; + ALLOW_UNUSED_LOCAL(tmp); + const size_t expected_utf16_bytes = + expected_utf16_units_with_nul * sizeof(tmp.Buffer[0]); + ASSERT_EQ(sizeof(MINIDUMP_STRING) + expected_utf16_bytes, + string_file.string().size()); + + const MINIDUMP_STRING* minidump_string = + MinidumpStringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + base::string16 expect_string = base::string16( + kTestData[index].output_string, kTestData[index].output_length); + EXPECT_EQ(expect_string, + MinidumpStringAtRVAAsString(string_file.string(), 0)); + } +} + +TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) { + StringFile string_file; + + const char* kTestData[] = { + "\200", // continuation byte + "\300", // start byte followed by EOF + "\310\177", // start byte without continuation + "\340\200", // EOF in middle of 3-byte sequence + "\340\200\115", // invalid 3-byte sequence + "\303\0\251", // NUL in middle of valid sequence + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", input %s", index, kTestData[index])); + string_file.Reset(); + crashpad::internal::MinidumpUTF16StringWriter string_writer; + string_writer.SetUTF8(kTestData[index]); + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + + // The requirements for conversion of invalid UTF-8 input are lax. Make sure + // that at least enough data was written for a string that has one unit and + // a NUL terminator, make sure that the length field matches the length of + // data written, and make sure that at least one U+FFFD replacement + // character was written. + const MINIDUMP_STRING* minidump_string = + MinidumpStringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + MINIDUMP_STRING tmp = {0}; + ALLOW_UNUSED_LOCAL(tmp); + EXPECT_EQ(string_file.string().size() - sizeof(MINIDUMP_STRING) - + sizeof(tmp.Buffer[0]), + minidump_string->Length); + base::string16 output_string = + MinidumpStringAtRVAAsString(string_file.string(), 0); + EXPECT_FALSE(output_string.empty()); + EXPECT_NE(base::string16::npos, output_string.find(0xfffd)); + } +} + +TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + crashpad::internal::MinidumpUTF8StringWriter string_writer; + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + ASSERT_EQ(5u, string_file.string().size()); + + const MinidumpUTF8String* minidump_string = + MinidumpUTF8StringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + EXPECT_EQ(std::string(), + MinidumpUTF8StringAtRVAAsString(string_file.string(), 0)); + } + + const struct { + size_t length; + const char* string; + } kTestData[] = { + {0, ""}, + {1, "a"}, + {2, "\0b"}, + {3, "cde"}, + {9, "Hi world!"}, + {7, "ret\nurn"}, + {2, "\303\251"}, // é + + // oóöőo + {8, "o\303\263\303\266\305\221o"}, + {4, "\360\220\204\202"}, // 𐄂 (non-BMP) + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", input %s", index, kTestData[index].string)); + + string_file.Reset(); + crashpad::internal::MinidumpUTF8StringWriter string_writer; + std::string test_string(kTestData[index].string, kTestData[index].length); + string_writer.SetUTF8(test_string); + EXPECT_EQ(test_string, string_writer.UTF8()); + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + + const size_t expected_utf8_bytes_with_nul = kTestData[index].length + 1; + ASSERT_EQ(sizeof(MinidumpUTF8String) + expected_utf8_bytes_with_nul, + string_file.string().size()); + + const MinidumpUTF8String* minidump_string = + MinidumpUTF8StringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + EXPECT_EQ(test_string, + MinidumpUTF8StringAtRVAAsString(string_file.string(), 0)); + } +} + +struct MinidumpUTF16StringListWriterTraits { + using MinidumpStringListWriterType = MinidumpUTF16StringListWriter; + static base::string16 ExpectationForUTF8(const std::string& utf8) { + return base::UTF8ToUTF16(utf8); + } + static base::string16 ObservationAtRVA(const std::string& file_contents, + RVA rva) { + return MinidumpStringAtRVAAsString(file_contents, rva); + } +}; + +struct MinidumpUTF8StringListWriterTraits { + using MinidumpStringListWriterType = MinidumpUTF8StringListWriter; + static std::string ExpectationForUTF8(const std::string& utf8) { + return utf8; + } + static std::string ObservationAtRVA(const std::string& file_contents, + RVA rva) { + return MinidumpUTF8StringAtRVAAsString(file_contents, rva); + } +}; + +template <typename Traits> +void MinidumpStringListTest() { + std::vector<std::string> strings; + strings.push_back(std::string("One")); + strings.push_back(std::string()); + strings.push_back(std::string("3")); + strings.push_back(std::string("\360\237\222\251")); + + typename Traits::MinidumpStringListWriterType string_list_writer; + EXPECT_FALSE(string_list_writer.IsUseful()); + string_list_writer.InitializeFromVector(strings); + EXPECT_TRUE(string_list_writer.IsUseful()); + + StringFile string_file; + + ASSERT_TRUE(string_list_writer.WriteEverything(&string_file)); + + const MinidumpRVAList* list = + MinidumpRVAListAtStart(string_file.string(), strings.size()); + ASSERT_TRUE(list); + + for (size_t index = 0; index < strings.size(); ++index) { + EXPECT_EQ(Traits::ExpectationForUTF8(strings[index]), + Traits::ObservationAtRVA(string_file.string(), + list->children[index])); + } +} + +TEST(MinidumpStringWriter, MinidumpUTF16StringList) { + MinidumpStringListTest<MinidumpUTF16StringListWriterTraits>(); +} + +TEST(MinidumpStringWriter, MinidumpUTF8StringList) { + MinidumpStringListTest<MinidumpUTF8StringListWriterTraits>(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc new file mode 100644 index 0000000..88687234 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
@@ -0,0 +1,300 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_system_info_writer.h" + +#include <string.h> +#include <sys/types.h> + +#include "base/logging.h" +#include "minidump/minidump_string_writer.h" +#include "snapshot/system_snapshot.h" +#include "util/file/file_writer.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +namespace { + +uint64_t AMD64FeaturesFromSystemSnapshot( + const SystemSnapshot* system_snapshot) { +#define ADD_FEATURE(minidump_bit) (UINT64_C(1) << (minidump_bit)) + + // Features for which no cpuid bits are present, but that always exist on + // x86_64. cmpxchg is supported on 486 and later. + uint64_t minidump_features = ADD_FEATURE(PF_COMPARE_EXCHANGE_DOUBLE); + +#define MAP_FEATURE(features, cpuid_bit, minidump_bit) \ + do { \ + if ((features) & (implicit_cast<decltype(features)>(1) << (cpuid_bit))) { \ + minidump_features |= ADD_FEATURE(minidump_bit); \ + } \ + } while (false) + +#define F_TSC 4 +#define F_PAE 6 +#define F_MMX 23 +#define F_SSE 25 +#define F_SSE2 26 +#define F_SSE3 32 +#define F_CX16 45 +#define F_XSAVE 58 +#define F_RDRAND 62 + + uint64_t cpuid_features = system_snapshot->CPUX86Features(); + + MAP_FEATURE(cpuid_features, F_TSC, PF_RDTSC_INSTRUCTION_AVAILABLE); + MAP_FEATURE(cpuid_features, F_PAE, PF_PAE_ENABLED); + MAP_FEATURE(cpuid_features, F_MMX, PF_MMX_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_SSE, PF_XMMI_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_SSE2, PF_XMMI64_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_SSE3, PF_SSE3_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_CX16, PF_COMPARE_EXCHANGE128); + MAP_FEATURE(cpuid_features, F_XSAVE, PF_XSAVE_ENABLED); + MAP_FEATURE(cpuid_features, F_RDRAND, PF_RDRAND_INSTRUCTION_AVAILABLE); + +#define FX_XD 20 +#define FX_3DNOW 31 + + uint64_t extended_features = system_snapshot->CPUX86ExtendedFeatures(); + + MAP_FEATURE(extended_features, FX_3DNOW, PF_3DNOW_INSTRUCTIONS_AVAILABLE); + +#define F7_FSGSBASE 0 + + uint32_t leaf7_features = system_snapshot->CPUX86Leaf7Features(); + + MAP_FEATURE(leaf7_features, F7_FSGSBASE, PF_RDWRFSGSBASE_AVAILABLE); + + // This feature bit should be set if NX (XD, DEP) is enabled, not just if + // it’s available on the CPU as indicated by the XF_XD bit. + if (system_snapshot->NXEnabled()) { + minidump_features |= ADD_FEATURE(PF_NX_ENABLED); + } + + if (system_snapshot->CPUX86SupportsDAZ()) { + minidump_features |= ADD_FEATURE(PF_SSE_DAZ_MODE_AVAILABLE); + } + + // PF_SECOND_LEVEL_ADDRESS_TRANSLATION can’t be determined without + // consulting model-specific registers, a privileged operation. The exact + // use of PF_VIRT_FIRMWARE_ENABLED is unknown. PF_FASTFAIL_AVAILABLE is + // irrelevant outside of Windows. + +#undef MAP_FEATURE +#undef ADD_FEATURE + + return minidump_features; +} + +} // namespace + +MinidumpSystemInfoWriter::MinidumpSystemInfoWriter() + : MinidumpStreamWriter(), system_info_(), csd_version_() { + system_info_.ProcessorArchitecture = kMinidumpCPUArchitectureUnknown; +} + +MinidumpSystemInfoWriter::~MinidumpSystemInfoWriter() { +} + +void MinidumpSystemInfoWriter::InitializeFromSnapshot( + const SystemSnapshot* system_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!csd_version_); + + MinidumpCPUArchitecture cpu_architecture; + switch (system_snapshot->GetCPUArchitecture()) { + case kCPUArchitectureX86: + cpu_architecture = kMinidumpCPUArchitectureX86; + break; + case kCPUArchitectureX86_64: + cpu_architecture = kMinidumpCPUArchitectureAMD64; + break; + default: + NOTREACHED(); + cpu_architecture = kMinidumpCPUArchitectureUnknown; + break; + } + SetCPUArchitecture(cpu_architecture); + + uint32_t cpu_revision = system_snapshot->CPURevision(); + SetCPULevelAndRevision((cpu_revision & 0xffff0000) >> 16, + cpu_revision & 0x0000ffff); + SetCPUCount(system_snapshot->CPUCount()); + + if (cpu_architecture == kMinidumpCPUArchitectureX86) { + std::string cpu_vendor = system_snapshot->CPUVendor(); + SetCPUX86VendorString(cpu_vendor); + + // The minidump file format only has room for the bottom 32 bits of CPU + // features and extended CPU features. + SetCPUX86VersionAndFeatures(system_snapshot->CPUX86Signature(), + system_snapshot->CPUX86Features() & 0xffffffff); + + if (cpu_vendor == "AuthenticAMD") { + SetCPUX86AMDExtendedFeatures( + system_snapshot->CPUX86ExtendedFeatures() & 0xffffffff); + } + } else if (cpu_architecture == kMinidumpCPUArchitectureAMD64) { + SetCPUOtherFeatures(AMD64FeaturesFromSystemSnapshot(system_snapshot), 0); + } + + MinidumpOS operating_system; + switch (system_snapshot->GetOperatingSystem()) { + case SystemSnapshot::kOperatingSystemMacOSX: + operating_system = kMinidumpOSMacOSX; + break; + case SystemSnapshot::kOperatingSystemWindows: + operating_system = kMinidumpOSWin32NT; + break; + default: + NOTREACHED(); + operating_system = kMinidumpOSUnknown; + break; + } + SetOS(operating_system); + + SetOSType(system_snapshot->OSServer() ? kMinidumpOSTypeServer + : kMinidumpOSTypeWorkstation); + + int major; + int minor; + int bugfix; + std::string build; + system_snapshot->OSVersion(&major, &minor, &bugfix, &build); + SetOSVersion(major, minor, bugfix); + SetCSDVersion(build); +} + +void MinidumpSystemInfoWriter::SetCSDVersion(const std::string& csd_version) { + DCHECK_EQ(state(), kStateMutable); + + if (!csd_version_) { + csd_version_.reset(new internal::MinidumpUTF16StringWriter()); + } + + csd_version_->SetUTF8(csd_version); +} + +void MinidumpSystemInfoWriter::SetCPUX86Vendor(uint32_t ebx, + uint32_t edx, + uint32_t ecx) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + + static_assert(arraysize(system_info_.Cpu.X86CpuInfo.VendorId) == 3, + "VendorId must have 3 elements"); + + system_info_.Cpu.X86CpuInfo.VendorId[0] = ebx; + system_info_.Cpu.X86CpuInfo.VendorId[1] = edx; + system_info_.Cpu.X86CpuInfo.VendorId[2] = ecx; +} + +void MinidumpSystemInfoWriter::SetCPUX86VendorString( + const std::string& vendor) { + DCHECK_EQ(state(), kStateMutable); + CHECK_EQ(vendor.size(), sizeof(system_info_.Cpu.X86CpuInfo.VendorId)); + + uint32_t registers[3]; + static_assert( + sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId), + "VendorId sizes must be equal"); + + for (size_t index = 0; index < arraysize(registers); ++index) { + memcpy(®isters[index], + &vendor[index * sizeof(*registers)], + sizeof(*registers)); + } + + SetCPUX86Vendor(registers[0], registers[1], registers[2]); +} + +void MinidumpSystemInfoWriter::SetCPUX86VersionAndFeatures(uint32_t version, + uint32_t features) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + + system_info_.Cpu.X86CpuInfo.VersionInformation = version; + system_info_.Cpu.X86CpuInfo.FeatureInformation = features; +} + +void MinidumpSystemInfoWriter::SetCPUX86AMDExtendedFeatures( + uint32_t extended_features) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + DCHECK(system_info_.Cpu.X86CpuInfo.VendorId[0] == 'htuA' && + system_info_.Cpu.X86CpuInfo.VendorId[1] == 'itne' && + system_info_.Cpu.X86CpuInfo.VendorId[2] == 'DMAc'); + + system_info_.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = extended_features; +} + +void MinidumpSystemInfoWriter::SetCPUOtherFeatures(uint64_t features_0, + uint64_t features_1) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture != kMinidumpCPUArchitectureX86 && + system_info_.ProcessorArchitecture != + kMinidumpCPUArchitectureX86Win64); + + static_assert(arraysize(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2, + "ProcessorFeatures must have 2 elements"); + + system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[0] = features_0; + system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[1] = features_1; +} + +bool MinidumpSystemInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(csd_version_); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + csd_version_->RegisterRVA(&system_info_.CSDVersionRva); + + return true; +} + +size_t MinidumpSystemInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(system_info_); +} + +std::vector<internal::MinidumpWritable*> MinidumpSystemInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(csd_version_); + + std::vector<MinidumpWritable*> children(1, csd_version_.get()); + return children; +} + +bool MinidumpSystemInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&system_info_, sizeof(system_info_)); +} + +MinidumpStreamType MinidumpSystemInfoWriter::StreamType() const { + return kMinidumpStreamTypeSystemInfo; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h new file mode 100644 index 0000000..a1653dc --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h
@@ -0,0 +1,196 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class SystemSnapshot; + +namespace internal { +class MinidumpUTF16StringWriter; +} // namespace internal + +//! \brief The writer for a MINIDUMP_SYSTEM_INFO stream in a minidump file. +class MinidumpSystemInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpSystemInfoWriter(); + ~MinidumpSystemInfoWriter() override; + + //! \brief Initializes MINIDUMP_SYSTEM_INFO based on \a system_snapshot. + //! + //! \param[in] system_snapshot The system snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const SystemSnapshot* system_snapshot); + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + void SetCPUArchitecture(MinidumpCPUArchitecture processor_architecture) { + system_info_.ProcessorArchitecture = processor_architecture; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorLevel and + //! MINIDUMP_SYSTEM_INFO::ProcessorRevision. + void SetCPULevelAndRevision(uint16_t processor_level, + uint16_t processor_revision) { + system_info_.ProcessorLevel = processor_level; + system_info_.ProcessorRevision = processor_revision; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::NumberOfProcessors. + void SetCPUCount(uint8_t number_of_processors) { + system_info_.NumberOfProcessors = number_of_processors; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::PlatformId. + void SetOS(MinidumpOS platform_id) { system_info_.PlatformId = platform_id; } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProductType. + void SetOSType(MinidumpOSType product_type) { + system_info_.ProductType = product_type; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::MajorVersion, + //! MINIDUMP_SYSTEM_INFO::MinorVersion, and + //! MINIDUMP_SYSTEM_INFO::BuildNumber. + void SetOSVersion(uint32_t major_version, + uint32_t minor_version, + uint32_t build_number) { + system_info_.MajorVersion = major_version; + system_info_.MinorVersion = minor_version; + system_info_.BuildNumber = build_number; + } + + //! \brief Arranges for MINIDUMP_SYSTEM_INFO::CSDVersionRva to point to a + //! MINIDUMP_STRING containing the supplied string. + //! + //! This method must be called prior to Freeze(). A CSD version is required + //! in all MINIDUMP_SYSTEM_INFO streams. An empty string is an acceptable + //! value. + void SetCSDVersion(const std::string& csd_version); + + //! \brief Sets MINIDUMP_SYSTEM_INFO::SuiteMask. + void SetSuiteMask(uint16_t suite_mask) { + system_info_.SuiteMask = suite_mask; + } + + //! \brief Sets \ref CPU_INFORMATION::VendorId + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \param[in] ebx The first 4 bytes of the CPU vendor string, the value + //! reported in `cpuid 0` `ebx`. + //! \param[in] edx The middle 4 bytes of the CPU vendor string, the value + //! reported in `cpuid 0` `edx`. + //! \param[in] ecx The last 4 bytes of the CPU vendor string, the value + //! reported by `cpuid 0` `ecx`. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + //! + //! \sa SetCPUX86VendorString() + void SetCPUX86Vendor(uint32_t ebx, uint32_t edx, uint32_t ecx); + + //! \brief Sets \ref CPU_INFORMATION::VendorId + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \param[in] vendor The entire CPU vendor string, which must be exactly 12 + //! bytes long. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + //! + //! \sa SetCPUX86Vendor() + void SetCPUX86VendorString(const std::string& vendor); + + //! \brief Sets \ref CPU_INFORMATION::VersionInformation + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VersionInformation" and + //! \ref CPU_INFORMATION::FeatureInformation + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::FeatureInformation". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUX86VersionAndFeatures(uint32_t version, uint32_t features); + + //! \brief Sets \ref CPU_INFORMATION::AMDExtendedCpuFeatures + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::AMDExtendedCPUFeatures". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64, and if SetCPUX86Vendor() or + //! SetCPUX86VendorString() has been used to set the CPU vendor to + //! “AuthenticAMD”. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUX86AMDExtendedFeatures(uint32_t extended_features); + + //! \brief Sets \ref CPU_INFORMATION::ProcessorFeatures + //! "MINIDUMP_SYSTEM_INFO::Cpu::OtherCpuInfo::ProcessorFeatures". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to an architecture other than #kMinidumpCPUArchitectureX86 + //! or #kMinidumpCPUArchitectureX86Win64. + //! + //! \note This method may be called if SetCPUArchitecture() has been used to + //! set the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUOtherFeatures(uint64_t features_0, uint64_t features_1); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_SYSTEM_INFO system_info_; + scoped_ptr<internal::MinidumpUTF16StringWriter> csd_version_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpSystemInfoWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc new file mode 100644 index 0000000..bc1a1b6 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc
@@ -0,0 +1,482 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_system_info_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <string.h> +#include <sys/types.h> + +#include <algorithm> +#include <string> + +#include "base/compiler_specific.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_system_snapshot.h" +#include "test/gtest_death_check.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +void GetSystemInfoStream(const std::string& file_contents, + size_t csd_version_length, + const MINIDUMP_SYSTEM_INFO** system_info, + const MINIDUMP_STRING** csd_version) { + // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer. + MINIDUMP_STRING tmp = {0}; + ALLOW_UNUSED_LOCAL(tmp); + const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp.Buffer[0]); + const size_t kCSDVersionBytesWithNUL = + kCSDVersionBytes + sizeof(tmp.Buffer[0]); + + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kSystemInfoStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kCSDVersionOffset = + kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO); + const size_t kFileSize = + kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL; + + ASSERT_EQ(kFileSize, file_contents.size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_EQ(kSystemInfoStreamOffset, directory[0].Location.Rva); + + *system_info = MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>( + file_contents, directory[0].Location); + ASSERT_TRUE(system_info); + + EXPECT_EQ(kCSDVersionOffset, (*system_info)->CSDVersionRva); + + *csd_version = + MinidumpStringAtRVA(file_contents, (*system_info)->CSDVersionRva); + EXPECT_EQ(kCSDVersionBytes, (*csd_version)->Length); +} + +TEST(MinidumpSystemInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + + system_info_writer->SetCSDVersion(std::string()); + + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + + ASSERT_NO_FATAL_FAILURE( + GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version)); + + EXPECT_EQ(kMinidumpCPUArchitectureUnknown, + system_info->ProcessorArchitecture); + EXPECT_EQ(0u, system_info->ProcessorLevel); + EXPECT_EQ(0u, system_info->ProcessorRevision); + EXPECT_EQ(0u, system_info->NumberOfProcessors); + EXPECT_EQ(0u, system_info->ProductType); + EXPECT_EQ(0u, system_info->MajorVersion); + EXPECT_EQ(0u, system_info->MinorVersion); + EXPECT_EQ(0u, system_info->BuildNumber); + EXPECT_EQ(0u, system_info->PlatformId); + EXPECT_EQ(0u, system_info->SuiteMask); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VersionInformation); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.FeatureInformation); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures); + EXPECT_EQ(0u, system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]); + EXPECT_EQ(0u, system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]); + + EXPECT_EQ('\0', csd_version->Buffer[0]); +} + +TEST(MinidumpSystemInfoWriter, X86_Win) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + + const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86; + const uint16_t kCPULevel = 0x0010; + const uint16_t kCPURevision = 0x0602; + const uint8_t kCPUCount = 1; + const MinidumpOS kOS = kMinidumpOSWin32NT; + const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + const uint32_t kOSVersionMajor = 6; + const uint32_t kOSVersionMinor = 1; + const uint32_t kOSVersionBuild = 7601; + const char kCSDVersion[] = "Service Pack 1"; + const uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS; + const char kCPUVendor[] = "AuthenticAMD"; + const uint32_t kCPUVersion = 0x00100f62; + const uint32_t kCPUFeatures = 0x078bfbff; + const uint32_t kAMDFeatures = 0xefd3fbff; + + uint32_t cpu_vendor_registers[3]; + ASSERT_EQ(sizeof(cpu_vendor_registers), strlen(kCPUVendor)); + memcpy(cpu_vendor_registers, kCPUVendor, sizeof(cpu_vendor_registers)); + + system_info_writer->SetCPUArchitecture(kCPUArchitecture); + system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision); + system_info_writer->SetCPUCount(kCPUCount); + system_info_writer->SetOS(kOS); + system_info_writer->SetOSType(kMinidumpOSTypeWorkstation); + system_info_writer->SetOSVersion( + kOSVersionMajor, kOSVersionMinor, kOSVersionBuild); + system_info_writer->SetCSDVersion(kCSDVersion); + system_info_writer->SetSuiteMask(kSuiteMask); + system_info_writer->SetCPUX86VendorString(kCPUVendor); + system_info_writer->SetCPUX86VersionAndFeatures(kCPUVersion, kCPUFeatures); + system_info_writer->SetCPUX86AMDExtendedFeatures(kAMDFeatures); + + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream( + string_file.string(), strlen(kCSDVersion), &system_info, &csd_version)); + + EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture); + EXPECT_EQ(kCPULevel, system_info->ProcessorLevel); + EXPECT_EQ(kCPURevision, system_info->ProcessorRevision); + EXPECT_EQ(kCPUCount, system_info->NumberOfProcessors); + EXPECT_EQ(kOSType, system_info->ProductType); + EXPECT_EQ(kOSVersionMajor, system_info->MajorVersion); + EXPECT_EQ(kOSVersionMinor, system_info->MinorVersion); + EXPECT_EQ(kOSVersionBuild, system_info->BuildNumber); + EXPECT_EQ(kOS, system_info->PlatformId); + EXPECT_EQ(kSuiteMask, system_info->SuiteMask); + EXPECT_EQ(cpu_vendor_registers[0], system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(cpu_vendor_registers[1], system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(cpu_vendor_registers[2], system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(kCPUVersion, system_info->Cpu.X86CpuInfo.VersionInformation); + EXPECT_EQ(kCPUFeatures, system_info->Cpu.X86CpuInfo.FeatureInformation); + EXPECT_EQ(kAMDFeatures, system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures); + + for (size_t index = 0; index < strlen(kCSDVersion); ++index) { + EXPECT_EQ(kCSDVersion[index], csd_version->Buffer[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriter, AMD64_Mac) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + + const MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureAMD64; + const uint16_t kCPULevel = 0x0006; + const uint16_t kCPURevision = 0x3a09; + const uint8_t kCPUCount = 8; + const MinidumpOS kOS = kMinidumpOSMacOSX; + const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + const uint32_t kOSVersionMajor = 10; + const uint32_t kOSVersionMinor = 9; + const uint32_t kOSVersionBuild = 4; + const char kCSDVersion[] = "13E28"; + const uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000}; + + system_info_writer->SetCPUArchitecture(kCPUArchitecture); + system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision); + system_info_writer->SetCPUCount(kCPUCount); + system_info_writer->SetOS(kOS); + system_info_writer->SetOSType(kMinidumpOSTypeWorkstation); + system_info_writer->SetOSVersion( + kOSVersionMajor, kOSVersionMinor, kOSVersionBuild); + system_info_writer->SetCSDVersion(kCSDVersion); + system_info_writer->SetCPUOtherFeatures(kCPUFeatures[0], kCPUFeatures[1]); + + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version; + + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream( + string_file.string(), strlen(kCSDVersion), &system_info, &csd_version)); + + EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture); + EXPECT_EQ(kCPULevel, system_info->ProcessorLevel); + EXPECT_EQ(kCPURevision, system_info->ProcessorRevision); + EXPECT_EQ(kCPUCount, system_info->NumberOfProcessors); + EXPECT_EQ(kOSType, system_info->ProductType); + EXPECT_EQ(kOSVersionMajor, system_info->MajorVersion); + EXPECT_EQ(kOSVersionMinor, system_info->MinorVersion); + EXPECT_EQ(kOSVersionBuild, system_info->BuildNumber); + EXPECT_EQ(kOS, system_info->PlatformId); + EXPECT_EQ(0u, system_info->SuiteMask); + EXPECT_EQ(kCPUFeatures[0], + system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]); + EXPECT_EQ(kCPUFeatures[1], + system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]); +} + +TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) { + // MinidumpSystemInfoWriter.X86_Win already tested SetCPUX86VendorString(). + // This test exercises SetCPUX86Vendor() to set the vendor from register + // values. + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + + const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86; + const uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'}; + + system_info_writer->SetCPUArchitecture(kCPUArchitecture); + system_info_writer->SetCPUX86Vendor( + kCPUVendor[0], kCPUVendor[1], kCPUVendor[2]); + system_info_writer->SetCSDVersion(std::string()); + + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version; + + ASSERT_NO_FATAL_FAILURE( + GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version)); + + EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture); + EXPECT_EQ(0u, system_info->ProcessorLevel); + EXPECT_EQ(kCPUVendor[0], system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(kCPUVendor[1], system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(kCPUVendor[2], system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VersionInformation); +} + +TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) { + MINIDUMP_SYSTEM_INFO expect_system_info = {}; + + const uint16_t kCPUFamily = 6; + const uint8_t kCPUModel = 70; + const uint8_t kCPUStepping = 1; + + const uint8_t kCPUBasicFamily = + static_cast<uint8_t>(std::min(kCPUFamily, static_cast<uint16_t>(15))); + const uint8_t kCPUExtendedFamily = kCPUFamily - kCPUBasicFamily; + + // These checks ensure that even if the constants above change, they represent + // something that can legitimately be encoded in the form used by cpuid 1 eax. + EXPECT_LE(kCPUFamily, 270); + EXPECT_LE(kCPUStepping, 15); + EXPECT_TRUE(kCPUBasicFamily == 6 || kCPUBasicFamily == 15 || kCPUModel <= 15); + + const uint8_t kCPUBasicModel = kCPUModel & 0xf; + const uint8_t kCPUExtendedModel = kCPUModel >> 4; + const uint32_t kCPUSignature = + (kCPUExtendedFamily << 20) | (kCPUExtendedModel << 16) | + (kCPUBasicFamily << 8) | (kCPUBasicModel << 4) | kCPUStepping; + const uint64_t kCPUX86Features = 0x7ffafbffbfebfbff; + expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86; + expect_system_info.ProcessorLevel = kCPUFamily; + expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping; + expect_system_info.NumberOfProcessors = 8; + expect_system_info.ProductType = kMinidumpOSTypeServer; + expect_system_info.MajorVersion = 10; + expect_system_info.MinorVersion = 9; + expect_system_info.BuildNumber = 5; + expect_system_info.PlatformId = kMinidumpOSMacOSX; + expect_system_info.SuiteMask = 0; + expect_system_info.Cpu.X86CpuInfo.VendorId[0] = 'uneG'; + expect_system_info.Cpu.X86CpuInfo.VendorId[1] = 'Ieni'; + expect_system_info.Cpu.X86CpuInfo.VendorId[2] = 'letn'; + expect_system_info.Cpu.X86CpuInfo.VersionInformation = kCPUSignature; + expect_system_info.Cpu.X86CpuInfo.FeatureInformation = + kCPUX86Features & 0xffffffff; + const char kCPUVendor[] = "GenuineIntel"; + const char kOSVersionBuild[] = "13F34"; + + TestSystemSnapshot system_snapshot; + system_snapshot.SetCPUArchitecture(kCPUArchitectureX86); + system_snapshot.SetCPURevision( + (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping); + system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors); + system_snapshot.SetCPUVendor(kCPUVendor); + system_snapshot.SetCPUX86Signature(kCPUSignature); + system_snapshot.SetCPUX86Features(kCPUX86Features); + system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + system_snapshot.SetOSServer(true); + system_snapshot.SetOSVersion(expect_system_info.MajorVersion, + expect_system_info.MinorVersion, + expect_system_info.BuildNumber, + kOSVersionBuild); + + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + system_info_writer->InitializeFromSnapshot(&system_snapshot); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(), + strlen(kOSVersionBuild), + &system_info, + &csd_version)); + + EXPECT_EQ(expect_system_info.ProcessorArchitecture, + system_info->ProcessorArchitecture); + EXPECT_EQ(expect_system_info.ProcessorLevel, system_info->ProcessorLevel); + EXPECT_EQ(expect_system_info.ProcessorRevision, + system_info->ProcessorRevision); + EXPECT_EQ(expect_system_info.NumberOfProcessors, + system_info->NumberOfProcessors); + EXPECT_EQ(expect_system_info.ProductType, system_info->ProductType); + EXPECT_EQ(expect_system_info.MajorVersion, system_info->MajorVersion); + EXPECT_EQ(expect_system_info.MinorVersion, system_info->MinorVersion); + EXPECT_EQ(expect_system_info.BuildNumber, system_info->BuildNumber); + EXPECT_EQ(expect_system_info.PlatformId, system_info->PlatformId); + EXPECT_EQ(expect_system_info.SuiteMask, system_info->SuiteMask); + EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VendorId[0], + system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VendorId[1], + system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VendorId[2], + system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VersionInformation, + system_info->Cpu.X86CpuInfo.VersionInformation); + EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.FeatureInformation, + system_info->Cpu.X86CpuInfo.FeatureInformation); + + for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) { + EXPECT_EQ(kOSVersionBuild[index], csd_version->Buffer[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) { + MINIDUMP_SYSTEM_INFO expect_system_info = {}; + + const uint8_t kCPUFamily = 6; + const uint8_t kCPUModel = 70; + const uint8_t kCPUStepping = 1; + expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64; + expect_system_info.ProcessorLevel = kCPUFamily; + expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping; + expect_system_info.NumberOfProcessors = 8; + expect_system_info.ProductType = kMinidumpOSTypeServer; + expect_system_info.MajorVersion = 10; + expect_system_info.MinorVersion = 9; + expect_system_info.BuildNumber = 5; + expect_system_info.PlatformId = kMinidumpOSMacOSX; + expect_system_info.SuiteMask = 0; + expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0] = + (1 << PF_COMPARE_EXCHANGE_DOUBLE) | + (1 << PF_MMX_INSTRUCTIONS_AVAILABLE) | + (1 << PF_XMMI_INSTRUCTIONS_AVAILABLE) | + (1 << PF_RDTSC_INSTRUCTION_AVAILABLE) | + (1 << PF_PAE_ENABLED) | + (1 << PF_XMMI64_INSTRUCTIONS_AVAILABLE) | + (1 << PF_SSE_DAZ_MODE_AVAILABLE) | + (1 << PF_NX_ENABLED) | + (1 << PF_SSE3_INSTRUCTIONS_AVAILABLE) | + (1 << PF_COMPARE_EXCHANGE128) | + (1 << PF_XSAVE_ENABLED) | + (1 << PF_RDWRFSGSBASE_AVAILABLE) | + (1 << PF_RDRAND_INSTRUCTION_AVAILABLE); + expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0; + const char kOSVersionBuild[] = "13F34"; + + TestSystemSnapshot system_snapshot; + system_snapshot.SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot.SetCPURevision( + (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping); + system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors); + system_snapshot.SetCPUX86Features(0x7ffafbffbfebfbff); + system_snapshot.SetCPUX86ExtendedFeatures(0x000000212c100900); + system_snapshot.SetCPUX86Leaf7Features(0x00002fbb); + system_snapshot.SetCPUX86SupportsDAZ(true); + system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + system_snapshot.SetOSServer(true); + system_snapshot.SetOSVersion(expect_system_info.MajorVersion, + expect_system_info.MinorVersion, + expect_system_info.BuildNumber, + kOSVersionBuild); + system_snapshot.SetNXEnabled(true); + + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + system_info_writer->InitializeFromSnapshot(&system_snapshot); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(), + strlen(kOSVersionBuild), + &system_info, + &csd_version)); + + EXPECT_EQ(expect_system_info.ProcessorArchitecture, + system_info->ProcessorArchitecture); + EXPECT_EQ(expect_system_info.ProcessorLevel, system_info->ProcessorLevel); + EXPECT_EQ(expect_system_info.ProcessorRevision, + system_info->ProcessorRevision); + EXPECT_EQ(expect_system_info.NumberOfProcessors, + system_info->NumberOfProcessors); + EXPECT_EQ(expect_system_info.ProductType, system_info->ProductType); + EXPECT_EQ(expect_system_info.MajorVersion, system_info->MajorVersion); + EXPECT_EQ(expect_system_info.MinorVersion, system_info->MinorVersion); + EXPECT_EQ(expect_system_info.BuildNumber, system_info->BuildNumber); + EXPECT_EQ(expect_system_info.PlatformId, system_info->PlatformId); + EXPECT_EQ(expect_system_info.SuiteMask, system_info->SuiteMask); + EXPECT_EQ(expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0], + system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]); + EXPECT_EQ(expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1], + system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]); + + for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) { + EXPECT_EQ(kOSVersionBuild[index], csd_version->Buffer[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriterDeathTest, NoCSDVersion) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter()); + minidump_file_writer.AddStream(crashpad::move(system_info_writer)); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "csd_version_"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_test.gyp b/third_party/crashpad/crashpad/minidump/minidump_test.gyp new file mode 100644 index 0000000..6f8dc19 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_test.gyp
@@ -0,0 +1,68 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_minidump_test', + 'type': 'executable', + 'dependencies': [ + 'minidump.gyp:crashpad_minidump', + '../snapshot/snapshot_test.gyp:crashpad_snapshot_test_lib', + '../test/test.gyp:crashpad_test', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/gtest/gtest.gyp:gtest_main', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'minidump_context_writer_test.cc', + 'minidump_crashpad_info_writer_test.cc', + 'minidump_exception_writer_test.cc', + 'minidump_handle_writer_test.cc', + 'minidump_file_writer_test.cc', + 'minidump_memory_info_writer_test.cc', + 'minidump_memory_writer_test.cc', + 'minidump_misc_info_writer_test.cc', + 'minidump_module_crashpad_info_writer_test.cc', + 'minidump_module_writer_test.cc', + 'minidump_rva_list_writer_test.cc', + 'minidump_simple_string_dictionary_writer_test.cc', + 'minidump_string_writer_test.cc', + 'minidump_system_info_writer_test.cc', + 'minidump_thread_id_map_test.cc', + 'minidump_thread_writer_test.cc', + 'minidump_writable_test.cc', + 'test/minidump_context_test_util.cc', + 'test/minidump_context_test_util.h', + 'test/minidump_file_writer_test_util.cc', + 'test/minidump_file_writer_test_util.h', + 'test/minidump_memory_writer_test_util.cc', + 'test/minidump_memory_writer_test_util.h', + 'test/minidump_rva_list_test_util.cc', + 'test/minidump_rva_list_test_util.h', + 'test/minidump_string_writer_test_util.cc', + 'test/minidump_string_writer_test_util.h', + 'test/minidump_writable_test_util.cc', + 'test/minidump_writable_test_util.h', + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc new file mode 100644 index 0000000..327d12e --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc
@@ -0,0 +1,67 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_thread_id_map.h" + +#include <limits> +#include <set> +#include <utility> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "snapshot/thread_snapshot.h" + +namespace crashpad { + +void BuildMinidumpThreadIDMap( + const std::vector<const ThreadSnapshot*>& thread_snapshots, + MinidumpThreadIDMap* thread_id_map) { + DCHECK(thread_id_map->empty()); + + // First, try truncating each 64-bit thread ID to 32 bits. If that’s possible + // for each unique 64-bit thread ID, then this will be used as the mapping. + // This preserves as much of the original thread ID as possible when feasible. + bool collision = false; + std::set<uint32_t> thread_ids_32; + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + uint64_t thread_id_64 = thread_snapshot->ThreadID(); + if (thread_id_map->find(thread_id_64) == thread_id_map->end()) { + uint32_t thread_id_32 = static_cast<uint32_t>(thread_id_64); + if (!thread_ids_32.insert(thread_id_32).second) { + collision = true; + break; + } + thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32)); + } + } + + if (collision) { + // Since there was a collision, go back and assign each unique 64-bit thread + // ID its own sequential 32-bit equivalent. The 32-bit thread IDs will not + // bear any resemblance to the original 64-bit thread IDs. + thread_id_map->clear(); + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + uint64_t thread_id_64 = thread_snapshot->ThreadID(); + if (thread_id_map->find(thread_id_64) == thread_id_map->end()) { + uint32_t thread_id_32 = + base::checked_cast<uint32_t>(thread_id_map->size()); + thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32)); + } + } + + DCHECK_LE(thread_id_map->size(), std::numeric_limits<uint32_t>::max()); + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h new file mode 100644 index 0000000..33b105f --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h
@@ -0,0 +1,52 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_ + +#include <stdint.h> + +#include <map> +#include <vector> + +namespace crashpad { + +class ThreadSnapshot; + +//! \brief A map that connects 64-bit snapshot thread IDs to 32-bit minidump +//! thread IDs. +//! +//! 64-bit snapshot thread IDs are obtained from ThreadSnapshot::ThreadID(). +//! 32-bit minidump thread IDs are stored in MINIDUMP_THREAD::ThreadId. +//! +//! A ThreadIDMap ensures that there are no collisions among the set of 32-bit +//! minidump thread IDs. +using MinidumpThreadIDMap = std::map<uint64_t, uint32_t>; + +//! \brief Builds a MinidumpThreadIDMap for a group of ThreadSnapshot objects. +//! +//! \param[in] thread_snapshots The thread snapshots to use as source data. +//! \param[out] thread_id_map A MinidumpThreadIDMap to be built by this method. +//! This map must be empty when this function is called. +//! +//! The map ensures that for any unique 64-bit thread ID found in a +//! ThreadSnapshot, the 32-bit thread ID used in a minidump file will also be +//! unique. +void BuildMinidumpThreadIDMap( + const std::vector<const ThreadSnapshot*>& thread_snapshots, + MinidumpThreadIDMap* thread_id_map); + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc new file mode 100644 index 0000000..058709e --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc
@@ -0,0 +1,190 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_thread_id_map.h" + +#include <vector> + +#include "base/basictypes.h" +#include "gtest/gtest.h" +#include "snapshot/test/test_thread_snapshot.h" + +namespace crashpad { +namespace test { +namespace { + +class MinidumpThreadIDMapTest : public testing::Test { + public: + MinidumpThreadIDMapTest() + : Test(), + thread_snapshots_(), + test_thread_snapshots_() { + } + + ~MinidumpThreadIDMapTest() override {} + + // testing::Test: + void SetUp() override { + for (size_t index = 0; index < arraysize(test_thread_snapshots_); ++index) { + thread_snapshots_.push_back(&test_thread_snapshots_[index]); + } + } + + protected: + static bool MapHasKeyValue( + const MinidumpThreadIDMap* map, uint64_t key, uint32_t expected_value) { + auto iterator = map->find(key); + if (iterator == map->end()) { + EXPECT_NE(map->end(), iterator); + return false; + } + if (iterator->second != expected_value) { + EXPECT_EQ(expected_value, iterator->second); + return false; + } + return true; + } + + void SetThreadID(size_t index, uint64_t thread_id) { + ASSERT_LT(index, arraysize(test_thread_snapshots_)); + test_thread_snapshots_[index].SetThreadID(thread_id); + } + + const std::vector<const ThreadSnapshot*>& thread_snapshots() const { + return thread_snapshots_; + } + + private: + std::vector<const ThreadSnapshot*> thread_snapshots_; + TestThreadSnapshot test_thread_snapshots_[5]; + + DISALLOW_COPY_AND_ASSIGN(MinidumpThreadIDMapTest); +}; + +TEST_F(MinidumpThreadIDMapTest, NoThreads) { + // Don’t use thread_snapshots(), because it’s got some threads in it, and the + // point of this test is to make sure that BuildMinidumpThreadIDMap() works + // with no threads. + std::vector<const ThreadSnapshot*> thread_snapshots; + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots, &thread_id_map); + + EXPECT_TRUE(thread_id_map.empty()); +} + +TEST_F(MinidumpThreadIDMapTest, SimpleMapping) { + SetThreadID(0, 1); + SetThreadID(1, 3); + SetThreadID(2, 5); + SetThreadID(3, 7); + SetThreadID(4, 9); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(5u, thread_id_map.size()); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 1, 1); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 3, 3); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 5, 5); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 7, 7); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 9, 9); +} + +TEST_F(MinidumpThreadIDMapTest, Truncation) { + SetThreadID(0, 0x0000000000000000); + SetThreadID(1, 0x9999999900000001); + SetThreadID(2, 0x9999999980000001); + SetThreadID(3, 0x99999999fffffffe); + SetThreadID(4, 0x99999999ffffffff); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(5u, thread_id_map.size()); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000000, 0x00000000); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999900000001, 0x00000001); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999980000001, 0x80000001); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999fffffffe, 0xfffffffe); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999ffffffff, 0xffffffff); +} + +TEST_F(MinidumpThreadIDMapTest, DuplicateThreadID) { + SetThreadID(0, 2); + SetThreadID(1, 4); + SetThreadID(2, 4); + SetThreadID(3, 6); + SetThreadID(4, 8); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(4u, thread_id_map.size()); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 2, 2); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 4, 4); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 8, 8); +} + +TEST_F(MinidumpThreadIDMapTest, Collision) { + SetThreadID(0, 0x0000000000000010); + SetThreadID(1, 0x0000000000000020); + SetThreadID(2, 0x0000000000000030); + SetThreadID(3, 0x0000000000000040); + SetThreadID(4, 0x0000000100000010); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(5u, thread_id_map.size()); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 0); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 1); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 2); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000040, 3); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 4); +} + +TEST_F(MinidumpThreadIDMapTest, DuplicateAndCollision) { + SetThreadID(0, 0x0000000100000010); + SetThreadID(1, 0x0000000000000010); + SetThreadID(2, 0x0000000000000020); + SetThreadID(3, 0x0000000000000030); + SetThreadID(4, 0x0000000000000020); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(4u, thread_id_map.size()); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 0); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 1); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 2); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 3); +} + +TEST_F(MinidumpThreadIDMapTest, AllDuplicates) { + SetThreadID(0, 6); + SetThreadID(1, 6); + SetThreadID(2, 6); + SetThreadID(3, 6); + SetThreadID(4, 6); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(1u, thread_id_map.size()); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc new file mode 100644 index 0000000..ab426c3d --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc
@@ -0,0 +1,237 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_thread_writer.h" + +#include <sys/types.h> + +#include "base/logging.h" +#include "minidump/minidump_context_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpThreadWriter::MinidumpThreadWriter() + : MinidumpWritable(), thread_(), stack_(nullptr), context_(nullptr) { +} + +MinidumpThreadWriter::~MinidumpThreadWriter() { +} + +void MinidumpThreadWriter::InitializeFromSnapshot( + const ThreadSnapshot* thread_snapshot, + const MinidumpThreadIDMap* thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!stack_); + DCHECK(!context_); + + auto thread_id_it = thread_id_map->find(thread_snapshot->ThreadID()); + DCHECK(thread_id_it != thread_id_map->end()); + SetThreadID(thread_id_it->second); + + SetSuspendCount(thread_snapshot->SuspendCount()); + SetPriority(thread_snapshot->Priority()); + SetTEB(thread_snapshot->ThreadSpecificDataAddress()); + + const MemorySnapshot* stack_snapshot = thread_snapshot->Stack(); + if (stack_snapshot && stack_snapshot->Size() > 0) { + scoped_ptr<MinidumpMemoryWriter> stack = + MinidumpMemoryWriter::CreateFromSnapshot(stack_snapshot); + SetStack(crashpad::move(stack)); + } + + scoped_ptr<MinidumpContextWriter> context = + MinidumpContextWriter::CreateFromSnapshot(thread_snapshot->Context()); + SetContext(crashpad::move(context)); +} + +const MINIDUMP_THREAD* MinidumpThreadWriter::MinidumpThread() const { + DCHECK_EQ(state(), kStateWritable); + + return &thread_; +} + +void MinidumpThreadWriter::SetStack(scoped_ptr<MinidumpMemoryWriter> stack) { + DCHECK_EQ(state(), kStateMutable); + + stack_ = crashpad::move(stack); +} + +void MinidumpThreadWriter::SetContext( + scoped_ptr<MinidumpContextWriter> context) { + DCHECK_EQ(state(), kStateMutable); + + context_ = crashpad::move(context); +} + +bool MinidumpThreadWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(context_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + if (stack_) { + stack_->RegisterMemoryDescriptor(&thread_.Stack); + } + + context_->RegisterLocationDescriptor(&thread_.ThreadContext); + + return true; +} + +size_t MinidumpThreadWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is + // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children + // are responsible for writing themselves. + return 0; +} + +std::vector<internal::MinidumpWritable*> MinidumpThreadWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(context_); + + std::vector<MinidumpWritable*> children; + if (stack_) { + children.push_back(stack_.get()); + } + children.push_back(context_.get()); + + return children; +} + +bool MinidumpThreadWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is + // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children + // are responsible for writing themselves. + return true; +} + +MinidumpThreadListWriter::MinidumpThreadListWriter() + : MinidumpStreamWriter(), + threads_(), + memory_list_writer_(nullptr), + thread_list_base_() { +} + +MinidumpThreadListWriter::~MinidumpThreadListWriter() { +} + +void MinidumpThreadListWriter::InitializeFromSnapshot( + const std::vector<const ThreadSnapshot*>& thread_snapshots, + MinidumpThreadIDMap* thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(threads_.empty()); + + BuildMinidumpThreadIDMap(thread_snapshots, thread_id_map); + + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + auto thread = make_scoped_ptr(new MinidumpThreadWriter()); + thread->InitializeFromSnapshot(thread_snapshot, thread_id_map); + AddThread(crashpad::move(thread)); + } + + // Do this in a separate loop to keep the thread stacks earlier in the dump, + // and together. + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) + memory_list_writer_->AddFromSnapshot(thread_snapshot->ExtraMemory()); +} + +void MinidumpThreadListWriter::SetMemoryListWriter( + MinidumpMemoryListWriter* memory_list_writer) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(threads_.empty()); + + memory_list_writer_ = memory_list_writer; +} + +void MinidumpThreadListWriter::AddThread( + scoped_ptr<MinidumpThreadWriter> thread) { + DCHECK_EQ(state(), kStateMutable); + + if (memory_list_writer_) { + MinidumpMemoryWriter* stack = thread->Stack(); + if (stack) { + memory_list_writer_->AddExtraMemory(stack); + } + } + + threads_.push_back(thread.release()); +} + +bool MinidumpThreadListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t thread_count = threads_.size(); + if (!AssignIfInRange(&thread_list_base_.NumberOfThreads, thread_count)) { + LOG(ERROR) << "thread_count " << thread_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpThreadListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(thread_list_base_) + threads_.size() * sizeof(MINIDUMP_THREAD); +} + +std::vector<internal::MinidumpWritable*> MinidumpThreadListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector<MinidumpWritable*> children; + for (MinidumpThreadWriter* thread : threads_) { + children.push_back(thread); + } + + return children; +} + +bool MinidumpThreadListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &thread_list_base_; + iov.iov_len = sizeof(thread_list_base_); + std::vector<WritableIoVec> iovecs(1, iov); + + for (const MinidumpThreadWriter* thread : threads_) { + iov.iov_base = thread->MinidumpThread(); + iov.iov_len = sizeof(MINIDUMP_THREAD); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpThreadListWriter::StreamType() const { + return kMinidumpStreamTypeThreadList; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h b/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h new file mode 100644 index 0000000..f8b7d20 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h
@@ -0,0 +1,214 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_writable.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class MinidumpContextWriter; +class MinidumpMemoryListWriter; +class MinidumpMemoryWriter; +class ThreadSnapshot; + +//! \brief The writer for a MINIDUMP_THREAD object in a minidump file. +//! +//! Because MINIDUMP_THREAD objects only appear as elements of +//! MINIDUMP_THREAD_LIST objects, this class does not write any data on its own. +//! It makes its MINIDUMP_THREAD data available to its MinidumpThreadListWriter +//! parent, which writes it as part of a MINIDUMP_THREAD_LIST. +class MinidumpThreadWriter final : public internal::MinidumpWritable { + public: + MinidumpThreadWriter(); + ~MinidumpThreadWriter() override; + + //! \brief Initializes the MINIDUMP_THREAD based on \a thread_snapshot. + //! + //! \param[in] thread_snapshot The thread snapshot to use as source data. + //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to + //! determine the 32-bit minidump thread ID to use for \a thread_snapshot. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot, + const MinidumpThreadIDMap* thread_id_map); + + //! \brief Returns a MINIDUMP_THREAD referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpThreadListWriter in + //! order to obtain a MINIDUMP_THREAD to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_THREAD* MinidumpThread() const; + + //! \brief Returns a MinidumpMemoryWriter that will write the memory region + //! corresponding to this object’s stack. + //! + //! If the thread does not have a stack, or its stack could not be determined, + //! this will return `nullptr`. + //! + //! This method is provided so that MinidumpThreadListWriter can obtain thread + //! stack memory regions for the purposes of adding them to a + //! MinidumpMemoryListWriter (configured by calling + //! MinidumpThreadListWriter::SetMemoryListWriter()) by calling + //! MinidumpMemoryListWriter::AddExtraMemory(). + //! + //! \note Valid in any state. + MinidumpMemoryWriter* Stack() const { return stack_.get(); } + + //! \brief Arranges for MINIDUMP_THREAD::Stack to point to the MINIDUMP_MEMORY + //! object to be written by \a stack. + //! + //! This object takes ownership of \a stack and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetStack(scoped_ptr<MinidumpMemoryWriter> stack); + + //! \brief Arranges for MINIDUMP_THREAD::ThreadContext to point to the CPU + //! context to be written by \a context. + //! + //! A context is required in all MINIDUMP_THREAD objects. + //! + //! This object takes ownership of \a context and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetContext(scoped_ptr<MinidumpContextWriter> context); + + //! \brief Sets MINIDUMP_THREAD::ThreadId. + void SetThreadID(uint32_t thread_id) { thread_.ThreadId = thread_id; } + + //! \brief Sets MINIDUMP_THREAD::SuspendCount. + void SetSuspendCount(uint32_t suspend_count) { + thread_.SuspendCount = suspend_count; + } + + //! \brief Sets MINIDUMP_THREAD::PriorityClass. + void SetPriorityClass(uint32_t priority_class) { + thread_.PriorityClass = priority_class; + } + + //! \brief Sets MINIDUMP_THREAD::Priority. + void SetPriority(uint32_t priority) { thread_.Priority = priority; } + + //! \brief Sets MINIDUMP_THREAD::Teb. + void SetTEB(uint64_t teb) { thread_.Teb = teb; } + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_THREAD thread_; + scoped_ptr<MinidumpMemoryWriter> stack_; + scoped_ptr<MinidumpContextWriter> context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpThreadWriter); +}; + +//! \brief The writer for a MINIDUMP_THREAD_LIST stream in a minidump file, +//! containing a list of MINIDUMP_THREAD objects. +class MinidumpThreadListWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpThreadListWriter(); + ~MinidumpThreadListWriter() override; + + //! \brief Adds an initialized MINIDUMP_THREAD for each thread in \a + //! thread_snapshots to the MINIDUMP_THREAD_LIST. + //! + //! \param[in] thread_snapshots The thread snapshots to use as source data. + //! \param[out] thread_id_map A MinidumpThreadIDMap to be built by this + //! method. This map must be empty when this method is called. + //! + //! \note Valid in #kStateMutable. AddThread() may not be called before this + //! method, and it is not normally necessary to call AddThread() after + //! this method. + void InitializeFromSnapshot( + const std::vector<const ThreadSnapshot*>& thread_snapshots, + MinidumpThreadIDMap* thread_id_map); + + //! \brief Sets the MinidumpMemoryListWriter that each thread’s stack memory + //! region should be added to as extra memory. + //! + //! Each MINIDUMP_THREAD object can contain a reference to a + //! MinidumpMemoryWriter object that contains a snapshot of its stack memory. + //! In the overall tree of internal::MinidumpWritable objects, these + //! MinidumpMemoryWriter objects are considered children of their + //! MINIDUMP_THREAD, and are referenced by a MINIDUMP_MEMORY_DESCRIPTOR + //! contained in the MINIDUMP_THREAD. It is also possible for the same memory + //! regions to have MINIDUMP_MEMORY_DESCRIPTOR objects present in a + //! MINIDUMP_MEMORY_LIST stream. This is accomplished by calling this method, + //! which informs a MinidumpThreadListWriter that it should call + //! MinidumpMemoryListWriter::AddExtraMemory() for each extant thread stack + //! while the thread is being added in AddThread(). When this is done, the + //! MinidumpMemoryListWriter will contain a MINIDUMP_MEMORY_DESCRIPTOR + //! pointing to the thread’s stack memory in its MINIDUMP_MEMORY_LIST. Note + //! that the actual contents of the memory is only written once, as a child of + //! the MinidumpThreadWriter. The MINIDUMP_MEMORY_DESCRIPTOR objects in both + //! the MINIDUMP_THREAD and MINIDUMP_MEMORY_LIST will point to the same copy + //! of the memory’s contents. + //! + //! \note This method must be called before AddThread() is called. Threads + //! added by AddThread() prior to this method being called will not have + //! their stacks added to \a memory_list_writer as extra memory. + //! \note Valid in #kStateMutable. + void SetMemoryListWriter(MinidumpMemoryListWriter* memory_list_writer); + + //! \brief Adds a MinidumpThreadWriter to the MINIDUMP_THREAD_LIST. + //! + //! This object takes ownership of \a thread and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddThread(scoped_ptr<MinidumpThreadWriter> thread); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector<MinidumpWritable*> Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + PointerVector<MinidumpThreadWriter> threads_; + MinidumpMemoryListWriter* memory_list_writer_; // weak + MINIDUMP_THREAD_LIST thread_list_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpThreadListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc new file mode 100644 index 0000000..4e3fd5c --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
@@ -0,0 +1,720 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_thread_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <sys/types.h> + +#include <string> + +#include "base/compiler_specific.h" +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/minidump_context_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/test/minidump_context_test_util.h" +#include "minidump/test/minidump_memory_writer_test_util.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "snapshot/test/test_thread_snapshot.h" +#include "test/gtest_death_check.h" +#include "util/file/string_file.h" +#include "util/stdlib/move.h" + +namespace crashpad { +namespace test { +namespace { + +// This returns the MINIDUMP_THREAD_LIST stream in |thread_list|. If +// |memory_list| is not nullptr, a MINIDUMP_MEMORY_LIST stream is also expected +// in |file_contents|, and that stream will be returned in |memory_list|. +void GetThreadListStream(const std::string& file_contents, + const MINIDUMP_THREAD_LIST** thread_list, + const MINIDUMP_MEMORY_LIST** memory_list) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const uint32_t kExpectedStreams = memory_list ? 2 : 1; + const size_t kThreadListStreamOffset = + kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY); + const size_t kThreadsOffset = + kThreadListStreamOffset + sizeof(MINIDUMP_THREAD_LIST); + + ASSERT_GE(file_contents.size(), kThreadsOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeThreadList, directory[0].StreamType); + EXPECT_EQ(kThreadListStreamOffset, directory[0].Location.Rva); + + *thread_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>( + file_contents, directory[0].Location); + ASSERT_TRUE(thread_list); + + if (memory_list) { + ASSERT_EQ(kMinidumpStreamTypeMemoryList, directory[1].StreamType); + + *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>( + file_contents, directory[1].Location); + ASSERT_TRUE(*memory_list); + } +} + +TEST(MinidumpThreadWriter, EmptyThreadList) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter()); + + minidump_file_writer.AddStream(crashpad::move(thread_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST), + string_file.string().size()); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, nullptr)); + + EXPECT_EQ(0u, thread_list->NumberOfThreads); +} + +// The MINIDUMP_THREADs |expected| and |observed| are compared against each +// other using gtest assertions. If |stack| is not nullptr, |observed| is +// expected to contain a populated MINIDUMP_MEMORY_DESCRIPTOR in its Stack +// field, otherwise, its Stack field is expected to be zeroed out. The memory +// descriptor will be placed in |stack|. |observed| must contain a populated +// ThreadContext field. The context will be recovered from |file_contents| and +// stored in |context_base|. +void ExpectThread(const MINIDUMP_THREAD* expected, + const MINIDUMP_THREAD* observed, + const std::string& file_contents, + const MINIDUMP_MEMORY_DESCRIPTOR** stack, + const void** context_base) { + EXPECT_EQ(expected->ThreadId, observed->ThreadId); + EXPECT_EQ(expected->SuspendCount, observed->SuspendCount); + EXPECT_EQ(expected->PriorityClass, observed->PriorityClass); + EXPECT_EQ(expected->Priority, observed->Priority); + EXPECT_EQ(expected->Teb, observed->Teb); + + EXPECT_EQ(expected->Stack.StartOfMemoryRange, + observed->Stack.StartOfMemoryRange); + EXPECT_EQ(expected->Stack.Memory.DataSize, observed->Stack.Memory.DataSize); + if (stack) { + ASSERT_NE(0u, observed->Stack.Memory.DataSize); + ASSERT_NE(0u, observed->Stack.Memory.Rva); + ASSERT_GE(file_contents.size(), + observed->Stack.Memory.Rva + observed->Stack.Memory.DataSize); + *stack = &observed->Stack; + } else { + EXPECT_EQ(0u, observed->Stack.StartOfMemoryRange); + EXPECT_EQ(0u, observed->Stack.Memory.DataSize); + EXPECT_EQ(0u, observed->Stack.Memory.Rva); + } + + EXPECT_EQ(expected->ThreadContext.DataSize, observed->ThreadContext.DataSize); + ASSERT_NE(0u, observed->ThreadContext.DataSize); + ASSERT_NE(0u, observed->ThreadContext.Rva); + ASSERT_GE(file_contents.size(), + observed->ThreadContext.Rva + expected->ThreadContext.DataSize); + *context_base = &file_contents[observed->ThreadContext.Rva]; +} + +TEST(MinidumpThreadWriter, OneThread_x86_NoStack) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter()); + + const uint32_t kThreadID = 0x11111111; + const uint32_t kSuspendCount = 1; + const uint32_t kPriorityClass = 0x20; + const uint32_t kPriority = 10; + const uint64_t kTEB = 0x55555555; + const uint32_t kSeed = 123; + + auto thread_writer = make_scoped_ptr(new MinidumpThreadWriter()); + thread_writer->SetThreadID(kThreadID); + thread_writer->SetSuspendCount(kSuspendCount); + thread_writer->SetPriorityClass(kPriorityClass); + thread_writer->SetPriority(kPriority); + thread_writer->SetTEB(kTEB); + + auto context_x86_writer = make_scoped_ptr(new MinidumpContextX86Writer()); + InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); + thread_writer->SetContext(crashpad::move(context_x86_writer)); + + thread_list_writer->AddThread(crashpad::move(thread_writer)); + minidump_file_writer.AddStream(crashpad::move(thread_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) + + 1 * sizeof(MinidumpContextX86), + string_file.string().size()); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, nullptr)); + + EXPECT_EQ(1u, thread_list->NumberOfThreads); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID; + expected.SuspendCount = kSuspendCount; + expected.PriorityClass = kPriorityClass; + expected.Priority = kPriority; + expected.Teb = kTEB; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[0], + string_file.string(), + nullptr, + reinterpret_cast<const void**>(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, false)); +} + +TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter()); + + const uint32_t kThreadID = 0x22222222; + const uint32_t kSuspendCount = 2; + const uint32_t kPriorityClass = 0x30; + const uint32_t kPriority = 20; + const uint64_t kTEB = 0x5555555555555555; + const uint64_t kMemoryBase = 0x765432100000; + const size_t kMemorySize = 32; + const uint8_t kMemoryValue = 99; + const uint32_t kSeed = 456; + + auto thread_writer = make_scoped_ptr(new MinidumpThreadWriter()); + thread_writer->SetThreadID(kThreadID); + thread_writer->SetSuspendCount(kSuspendCount); + thread_writer->SetPriorityClass(kPriorityClass); + thread_writer->SetPriority(kPriority); + thread_writer->SetTEB(kTEB); + + auto memory_writer = make_scoped_ptr( + new TestMinidumpMemoryWriter(kMemoryBase, kMemorySize, kMemoryValue)); + thread_writer->SetStack(crashpad::move(memory_writer)); + + MSVC_SUPPRESS_WARNING(4316); // Object allocated on heap may not be aligned. + auto context_amd64_writer = make_scoped_ptr(new MinidumpContextAMD64Writer()); + InitializeMinidumpContextAMD64(context_amd64_writer->context(), kSeed); + thread_writer->SetContext(crashpad::move(context_amd64_writer)); + + thread_list_writer->AddThread(crashpad::move(thread_writer)); + minidump_file_writer.AddStream(crashpad::move(thread_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) + + 1 * sizeof(MinidumpContextAMD64) + kMemorySize, + string_file.string().size()); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, nullptr)); + + EXPECT_EQ(1u, thread_list->NumberOfThreads); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID; + expected.SuspendCount = kSuspendCount; + expected.PriorityClass = kPriorityClass; + expected.Priority = kPriority; + expected.Teb = kTEB; + expected.Stack.StartOfMemoryRange = kMemoryBase; + expected.Stack.Memory.DataSize = kMemorySize; + expected.ThreadContext.DataSize = sizeof(MinidumpContextAMD64); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextAMD64* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[0], + string_file.string(), + &observed_stack, + reinterpret_cast<const void**>(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue, + true)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextAMD64(kSeed, observed_context, false)); +} + +TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter()); + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + thread_list_writer->SetMemoryListWriter(memory_list_writer.get()); + + const uint32_t kThreadID0 = 1111111; + const uint32_t kSuspendCount0 = 111111; + const uint32_t kPriorityClass0 = 11111; + const uint32_t kPriority0 = 1111; + const uint64_t kTEB0 = 111; + const uint64_t kMemoryBase0 = 0x1110; + const size_t kMemorySize0 = 16; + const uint8_t kMemoryValue0 = 11; + const uint32_t kSeed0 = 1; + + auto thread_writer_0 = make_scoped_ptr(new MinidumpThreadWriter()); + thread_writer_0->SetThreadID(kThreadID0); + thread_writer_0->SetSuspendCount(kSuspendCount0); + thread_writer_0->SetPriorityClass(kPriorityClass0); + thread_writer_0->SetPriority(kPriority0); + thread_writer_0->SetTEB(kTEB0); + + auto memory_writer_0 = make_scoped_ptr( + new TestMinidumpMemoryWriter(kMemoryBase0, kMemorySize0, kMemoryValue0)); + thread_writer_0->SetStack(crashpad::move(memory_writer_0)); + + auto context_x86_writer_0 = make_scoped_ptr(new MinidumpContextX86Writer()); + InitializeMinidumpContextX86(context_x86_writer_0->context(), kSeed0); + thread_writer_0->SetContext(crashpad::move(context_x86_writer_0)); + + thread_list_writer->AddThread(crashpad::move(thread_writer_0)); + + const uint32_t kThreadID1 = 2222222; + const uint32_t kSuspendCount1 = 222222; + const uint32_t kPriorityClass1 = 22222; + const uint32_t kPriority1 = 2222; + const uint64_t kTEB1 = 222; + const uint64_t kMemoryBase1 = 0x2220; + const size_t kMemorySize1 = 32; + const uint8_t kMemoryValue1 = 22; + const uint32_t kSeed1 = 2; + + auto thread_writer_1 = make_scoped_ptr(new MinidumpThreadWriter()); + thread_writer_1->SetThreadID(kThreadID1); + thread_writer_1->SetSuspendCount(kSuspendCount1); + thread_writer_1->SetPriorityClass(kPriorityClass1); + thread_writer_1->SetPriority(kPriority1); + thread_writer_1->SetTEB(kTEB1); + + auto memory_writer_1 = make_scoped_ptr( + new TestMinidumpMemoryWriter(kMemoryBase1, kMemorySize1, kMemoryValue1)); + thread_writer_1->SetStack(crashpad::move(memory_writer_1)); + + auto context_x86_writer_1 = make_scoped_ptr(new MinidumpContextX86Writer()); + InitializeMinidumpContextX86(context_x86_writer_1->context(), kSeed1); + thread_writer_1->SetContext(crashpad::move(context_x86_writer_1)); + + thread_list_writer->AddThread(crashpad::move(thread_writer_1)); + + const uint32_t kThreadID2 = 3333333; + const uint32_t kSuspendCount2 = 333333; + const uint32_t kPriorityClass2 = 33333; + const uint32_t kPriority2 = 3333; + const uint64_t kTEB2 = 333; + const uint64_t kMemoryBase2 = 0x3330; + const size_t kMemorySize2 = 48; + const uint8_t kMemoryValue2 = 33; + const uint32_t kSeed2 = 3; + + auto thread_writer_2 = make_scoped_ptr(new MinidumpThreadWriter()); + thread_writer_2->SetThreadID(kThreadID2); + thread_writer_2->SetSuspendCount(kSuspendCount2); + thread_writer_2->SetPriorityClass(kPriorityClass2); + thread_writer_2->SetPriority(kPriority2); + thread_writer_2->SetTEB(kTEB2); + + auto memory_writer_2 = make_scoped_ptr( + new TestMinidumpMemoryWriter(kMemoryBase2, kMemorySize2, kMemoryValue2)); + thread_writer_2->SetStack(crashpad::move(memory_writer_2)); + + auto context_x86_writer_2 = make_scoped_ptr(new MinidumpContextX86Writer()); + InitializeMinidumpContextX86(context_x86_writer_2->context(), kSeed2); + thread_writer_2->SetContext(crashpad::move(context_x86_writer_2)); + + thread_list_writer->AddThread(crashpad::move(thread_writer_2)); + + minidump_file_writer.AddStream(crashpad::move(thread_list_writer)); + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST) + 3 * sizeof(MINIDUMP_THREAD) + + sizeof(MINIDUMP_MEMORY_LIST) + + 3 * sizeof(MINIDUMP_MEMORY_DESCRIPTOR) + + 3 * sizeof(MinidumpContextX86) + kMemorySize0 + kMemorySize1 + + kMemorySize2 + 12, // 12 for alignment + string_file.string().size()); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, &memory_list)); + + EXPECT_EQ(3u, thread_list->NumberOfThreads); + EXPECT_EQ(3u, memory_list->NumberOfMemoryRanges); + + { + SCOPED_TRACE("thread 0"); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID0; + expected.SuspendCount = kSuspendCount0; + expected.PriorityClass = kPriorityClass0; + expected.Priority = kPriority0; + expected.Teb = kTEB0; + expected.Stack.StartOfMemoryRange = kMemoryBase0; + expected.Stack.Memory.DataSize = kMemorySize0; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[0], + string_file.string(), + &observed_stack, + reinterpret_cast<const void**>(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue0, + false)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed0, observed_context, false)); + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[0])); + } + + { + SCOPED_TRACE("thread 1"); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID1; + expected.SuspendCount = kSuspendCount1; + expected.PriorityClass = kPriorityClass1; + expected.Priority = kPriority1; + expected.Teb = kTEB1; + expected.Stack.StartOfMemoryRange = kMemoryBase1; + expected.Stack.Memory.DataSize = kMemorySize1; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[1], + string_file.string(), + &observed_stack, + reinterpret_cast<const void**>(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue1, + false)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed1, observed_context, false)); + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[1])); + } + + { + SCOPED_TRACE("thread 2"); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID2; + expected.SuspendCount = kSuspendCount2; + expected.PriorityClass = kPriorityClass2; + expected.Priority = kPriority2; + expected.Teb = kTEB2; + expected.Stack.StartOfMemoryRange = kMemoryBase2; + expected.Stack.Memory.DataSize = kMemorySize2; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[2], + string_file.string(), + &observed_stack, + reinterpret_cast<const void**>(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue2, + true)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed2, observed_context, false)); + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[2])); + } +} + +struct InitializeFromSnapshotX86Traits { + using MinidumpContextType = MinidumpContextX86; + static void InitializeCPUContext(CPUContext* context, uint32_t seed) { + return InitializeCPUContextX86(context, seed); + } + static void ExpectMinidumpContext( + uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) { + return ExpectMinidumpContextX86(expect_seed, observed, snapshot); + } +}; + +struct InitializeFromSnapshotAMD64Traits { + using MinidumpContextType = MinidumpContextAMD64; + static void InitializeCPUContext(CPUContext* context, uint32_t seed) { + return InitializeCPUContextX86_64(context, seed); + } + static void ExpectMinidumpContext(uint32_t expect_seed, + const MinidumpContextAMD64* observed, + bool snapshot) { + return ExpectMinidumpContextAMD64(expect_seed, observed, snapshot); + } +}; + +struct InitializeFromSnapshotNoContextTraits { + using MinidumpContextType = MinidumpContextX86; + static void InitializeCPUContext(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureUnknown; + } + static void ExpectMinidumpContext(uint32_t expect_seed, + const MinidumpContextX86* observed, + bool snapshot) { + FAIL(); + } +}; + +template <typename Traits> +void RunInitializeFromSnapshotTest(bool thread_id_collision) { + using MinidumpContextType = typename Traits::MinidumpContextType; + MINIDUMP_THREAD expect_threads[3] = {}; + uint64_t thread_ids[arraysize(expect_threads)] = {}; + uint8_t memory_values[arraysize(expect_threads)] = {}; + uint32_t context_seeds[arraysize(expect_threads)] = {}; + MINIDUMP_MEMORY_DESCRIPTOR tebs[arraysize(expect_threads)] = {}; + + const size_t kTebSize = 1024; + + expect_threads[0].ThreadId = 1; + expect_threads[0].SuspendCount = 2; + expect_threads[0].Priority = 3; + expect_threads[0].Teb = 0x0123456789abcdef; + expect_threads[0].Stack.StartOfMemoryRange = 0x1000; + expect_threads[0].Stack.Memory.DataSize = 0x100; + expect_threads[0].ThreadContext.DataSize = sizeof(MinidumpContextType); + memory_values[0] = 'A'; + context_seeds[0] = 0x80000000; + tebs[0].StartOfMemoryRange = expect_threads[0].Teb; + tebs[0].Memory.DataSize = kTebSize; + + // The thread at index 1 has no stack. + expect_threads[1].ThreadId = 11; + expect_threads[1].SuspendCount = 12; + expect_threads[1].Priority = 13; + expect_threads[1].Teb = 0xfedcba9876543210; + expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType); + context_seeds[1] = 0x40000001; + tebs[1].StartOfMemoryRange = expect_threads[1].Teb; + tebs[1].Memory.DataSize = kTebSize; + + expect_threads[2].ThreadId = 21; + expect_threads[2].SuspendCount = 22; + expect_threads[2].Priority = 23; + expect_threads[2].Teb = 0x1111111111111111; + expect_threads[2].Stack.StartOfMemoryRange = 0x3000; + expect_threads[2].Stack.Memory.DataSize = 0x300; + expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType); + memory_values[2] = 'd'; + context_seeds[2] = 0x20000002; + tebs[2].StartOfMemoryRange = expect_threads[2].Teb; + tebs[2].Memory.DataSize = kTebSize; + + if (thread_id_collision) { + thread_ids[0] = 0x0123456700000001; + thread_ids[1] = 0x89abcdef00000001; + thread_ids[2] = 4; + expect_threads[0].ThreadId = 0; + expect_threads[1].ThreadId = 1; + expect_threads[2].ThreadId = 2; + } else { + thread_ids[0] = 1; + thread_ids[1] = 11; + thread_ids[2] = 22; + expect_threads[0].ThreadId = static_cast<uint32_t>(thread_ids[0]); + expect_threads[1].ThreadId = static_cast<uint32_t>(thread_ids[1]); + expect_threads[2].ThreadId = static_cast<uint32_t>(thread_ids[2]); + } + + PointerVector<TestThreadSnapshot> thread_snapshots_owner; + std::vector<const ThreadSnapshot*> thread_snapshots; + for (size_t index = 0; index < arraysize(expect_threads); ++index) { + TestThreadSnapshot* thread_snapshot = new TestThreadSnapshot(); + thread_snapshots_owner.push_back(thread_snapshot); + + thread_snapshot->SetThreadID(thread_ids[index]); + thread_snapshot->SetSuspendCount(expect_threads[index].SuspendCount); + thread_snapshot->SetPriority(expect_threads[index].Priority); + thread_snapshot->SetThreadSpecificDataAddress(expect_threads[index].Teb); + + if (expect_threads[index].Stack.Memory.DataSize) { + auto memory_snapshot = make_scoped_ptr(new TestMemorySnapshot()); + memory_snapshot->SetAddress( + expect_threads[index].Stack.StartOfMemoryRange); + memory_snapshot->SetSize(expect_threads[index].Stack.Memory.DataSize); + memory_snapshot->SetValue(memory_values[index]); + thread_snapshot->SetStack(crashpad::move(memory_snapshot)); + } + + Traits::InitializeCPUContext(thread_snapshot->MutableContext(), + context_seeds[index]); + + auto teb_snapshot = make_scoped_ptr(new TestMemorySnapshot()); + teb_snapshot->SetAddress(expect_threads[index].Teb); + teb_snapshot->SetSize(kTebSize); + teb_snapshot->SetValue(static_cast<char>('t' + index)); + thread_snapshot->AddExtraMemory(crashpad::move(teb_snapshot)); + + thread_snapshots.push_back(thread_snapshot); + } + + auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter()); + auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter()); + thread_list_writer->SetMemoryListWriter(memory_list_writer.get()); + MinidumpThreadIDMap thread_id_map; + thread_list_writer->InitializeFromSnapshot(thread_snapshots, &thread_id_map); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(crashpad::move(thread_list_writer)); + minidump_file_writer.AddStream(crashpad::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, &memory_list)); + + ASSERT_EQ(3u, thread_list->NumberOfThreads); + ASSERT_EQ(5u, memory_list->NumberOfMemoryRanges); + + size_t memory_index = 0; + for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MINIDUMP_MEMORY_DESCRIPTOR** observed_stack_p = + expect_threads[index].Stack.Memory.DataSize ? &observed_stack : nullptr; + const MinidumpContextType* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expect_threads[index], + &thread_list->Threads[index], + string_file.string(), + observed_stack_p, + reinterpret_cast<const void**>(&observed_context))); + + ASSERT_NO_FATAL_FAILURE(Traits::ExpectMinidumpContext( + context_seeds[index], observed_context, true)); + + if (observed_stack_p) { + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptorAndContents( + &expect_threads[index].Stack, + observed_stack, + string_file.string(), + memory_values[index], + false)); + + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[memory_index])); + + ++memory_index; + } + } + + for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) { + const MINIDUMP_MEMORY_DESCRIPTOR* memory = + &memory_list->MemoryRanges[memory_index]; + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptor(&tebs[index], memory)); + std::string expected_data(kTebSize, static_cast<char>('t' + index)); + std::string observed_data(&string_file.string()[memory->Memory.Rva], + memory->Memory.DataSize); + EXPECT_EQ(expected_data, observed_data); + ++memory_index; + } +} + +TEST(MinidumpThreadWriter, InitializeFromSnapshot_x86) { + RunInitializeFromSnapshotTest<InitializeFromSnapshotX86Traits>(false); +} + +TEST(MinidumpThreadWriter, InitializeFromSnapshot_AMD64) { + RunInitializeFromSnapshotTest<InitializeFromSnapshotAMD64Traits>(false); +} + +TEST(MinidumpThreadWriter, InitializeFromSnapshot_ThreadIDCollision) { + RunInitializeFromSnapshotTest<InitializeFromSnapshotX86Traits>(true); +} + +TEST(MinidumpThreadWriterDeathTest, NoContext) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter()); + + auto thread_writer = make_scoped_ptr(new MinidumpThreadWriter()); + + thread_list_writer->AddThread(crashpad::move(thread_writer)); + minidump_file_writer.AddStream(crashpad::move(thread_list_writer)); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "context_"); +} + +TEST(MinidumpThreadWriterDeathTest, InitializeFromSnapshot_NoContext) { + ASSERT_DEATH_CHECK( + RunInitializeFromSnapshotTest<InitializeFromSnapshotNoContextTraits>( + false), "context_"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_writable.cc b/third_party/crashpad/crashpad/minidump/minidump_writable.cc new file mode 100644 index 0000000..8b73906 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_writable.cc
@@ -0,0 +1,269 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_writable.h" + +#include <stdint.h> + +#include <limits> + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace { + +const size_t kMaximumAlignment = 16; + +} // namespace + +namespace crashpad { +namespace internal { + +MinidumpWritable::~MinidumpWritable() { +} + +bool MinidumpWritable::WriteEverything(FileWriterInterface* file_writer) { + DCHECK_EQ(state_, kStateMutable); + + if (!Freeze()) { + return false; + } + + DCHECK_EQ(state_, kStateFrozen); + + FileOffset offset = 0; + std::vector<MinidumpWritable*> write_sequence; + size_t size = WillWriteAtOffset(kPhaseEarly, &offset, &write_sequence); + if (size == kInvalidSize) { + return false; + } + + offset += size; + if (WillWriteAtOffset(kPhaseLate, &offset, &write_sequence) == kInvalidSize) { + return false; + } + + DCHECK_EQ(state_, kStateWritable); + DCHECK_EQ(write_sequence.front(), this); + + for (MinidumpWritable* writable : write_sequence) { + if (!writable->WritePaddingAndObject(file_writer)) { + return false; + } + } + + DCHECK_EQ(state_, kStateWritten); + + return true; +} + +void MinidumpWritable::RegisterRVA(RVA* rva) { + DCHECK_LE(state_, kStateFrozen); + + registered_rvas_.push_back(rva); +} + +void MinidumpWritable::RegisterLocationDescriptor( + MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) { + DCHECK_LE(state_, kStateFrozen); + + registered_location_descriptors_.push_back(location_descriptor); +} + +const size_t MinidumpWritable::kInvalidSize = + std::numeric_limits<size_t>::max(); + +MinidumpWritable::MinidumpWritable() + : registered_rvas_(), + registered_location_descriptors_(), + leading_pad_bytes_(0), + state_(kStateMutable) { +} + +bool MinidumpWritable::Freeze() { + DCHECK_EQ(state_, kStateMutable); + state_ = kStateFrozen; + + std::vector<MinidumpWritable*> children = Children(); + for (MinidumpWritable* child : children) { + if (!child->Freeze()) { + return false; + } + } + + return true; +} + +size_t MinidumpWritable::Alignment() { + DCHECK_GE(state_, kStateFrozen); + + return 4; +} + +std::vector<MinidumpWritable*> MinidumpWritable::Children() { + DCHECK_GE(state_, kStateFrozen); + + return std::vector<MinidumpWritable*>(); +} + +MinidumpWritable::Phase MinidumpWritable::WritePhase() { + return kPhaseEarly; +} + +size_t MinidumpWritable::WillWriteAtOffset( + Phase phase, + FileOffset* offset, + std::vector<MinidumpWritable*>* write_sequence) { + FileOffset local_offset = *offset; + CHECK_GE(local_offset, 0); + + size_t leading_pad_bytes_this_phase; + size_t size; + if (phase == WritePhase()) { + DCHECK_EQ(state_, kStateFrozen); + + // Add this object to the sequence of MinidumpWritable objects to be + // written. + write_sequence->push_back(this); + + size = SizeOfObject(); + + if (size > 0) { + // Honor this object’s request to be aligned to a specific byte boundary. + // Once the alignment is corrected, this object knows exactly what file + // offset it will be written at. + size_t alignment = Alignment(); + CHECK_LE(alignment, kMaximumAlignment); + + leading_pad_bytes_this_phase = + (alignment - (local_offset % alignment)) % alignment; + local_offset += leading_pad_bytes_this_phase; + *offset = local_offset; + } else { + // If the object is size 0, alignment is of no concern. + leading_pad_bytes_this_phase = 0; + } + leading_pad_bytes_ = leading_pad_bytes_this_phase; + + // Now that the file offset that this object will be written at is known, + // let the subclass implementation know in case it’s interested. + if (!WillWriteAtOffsetImpl(local_offset)) { + return kInvalidSize; + } + + // Populate the RVA fields in other objects that have registered to point to + // this one. Typically, a parent object will have registered to point to its + // children, but this can also occur where no parent-child relationship + // exists. + if (!registered_rvas_.empty() || + !registered_location_descriptors_.empty()) { + RVA local_rva; + if (!AssignIfInRange(&local_rva, local_offset)) { + LOG(ERROR) << "offset " << local_offset << " out of range"; + return kInvalidSize; + } + + for (RVA* rva : registered_rvas_) { + *rva = local_rva; + } + + if (!registered_location_descriptors_.empty()) { + decltype(registered_location_descriptors_[0]->DataSize) local_size; + if (!AssignIfInRange(&local_size, size)) { + LOG(ERROR) << "size " << size << " out of range"; + return kInvalidSize; + } + + for (MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor : + registered_location_descriptors_) { + location_descriptor->DataSize = local_size; + location_descriptor->Rva = local_rva; + } + } + } + + // This object is now considered writable. However, if it contains RVA or + // MINIDUMP_LOCATION_DESCRIPTOR fields, they may not be fully updated yet, + // because it’s the repsonsibility of these fields’ pointees to update them. + // Once WillWriteAtOffset has completed running for both phases on an entire + // tree, and the entire tree has moved into kStateFrozen, all RVA and + // MINIDUMP_LOCATION_DESCRIPTOR fields within that tree will be populated. + state_ = kStateWritable; + } else { + if (phase == kPhaseEarly) { + DCHECK_EQ(state_, kStateFrozen); + } else { + DCHECK_EQ(state_, kStateWritable); + } + + size = 0; + leading_pad_bytes_this_phase = 0; + } + + // Loop over children regardless of whether this object itself will write + // during this phase. An object’s children are not required to be written + // during the same phase as their parent. + std::vector<MinidumpWritable*> children = Children(); + for (MinidumpWritable* child : children) { + // Use “auto” here because it’s impossible to know whether size_t (size) or + // FileOffset (local_offset) is the wider type, and thus what type the + // result of adding these two variables will have. + auto unaligned_child_offset = local_offset + size; + FileOffset child_offset; + if (!AssignIfInRange(&child_offset, unaligned_child_offset)) { + LOG(ERROR) << "offset " << unaligned_child_offset << " out of range"; + return kInvalidSize; + } + + size_t child_size = + child->WillWriteAtOffset(phase, &child_offset, write_sequence); + if (child_size == kInvalidSize) { + return kInvalidSize; + } + + size += child_size; + } + + return leading_pad_bytes_this_phase + size; +} + +bool MinidumpWritable::WillWriteAtOffsetImpl(FileOffset offset) { + return true; +} + +bool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state_, kStateWritable); + + // The number of elements in kZeroes must be at least one less than the + // maximum Alignment() ever encountered. + const uint8_t kZeroes[kMaximumAlignment - 1] = {}; + DCHECK_LE(leading_pad_bytes_, arraysize(kZeroes)); + + if (leading_pad_bytes_) { + if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) { + return false; + } + } + + if (!WriteObject(file_writer)) { + return false; + } + + state_ = kStateWritten; + return true; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_writable.h b/third_party/crashpad/crashpad/minidump/minidump_writable.h new file mode 100644 index 0000000..9e7cf71d --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_writable.h
@@ -0,0 +1,279 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <sys/types.h> + +#include <vector> + +#include "base/basictypes.h" +#include "util/file/file_io.h" + +namespace crashpad { + +class FileWriterInterface; + +namespace internal { + +//! \brief The base class for all content that might be written to a minidump +//! file. +class MinidumpWritable { + public: + virtual ~MinidumpWritable(); + + //! \brief Writes an object and all of its children to a minidump file. + //! + //! Use this on the root object of a tree of MinidumpWritable objects, + //! typically on a MinidumpFileWriter object. + //! + //! \param[in] file_writer The file writer to receive the minidump file’s + //! content. + //! + //! \return `true` on success. `false` on failure, with an appropriate message + //! logged. + //! + //! \note Valid in #kStateMutable, and transitions the object and the entire + //! tree beneath it through all states to #kStateWritten. + //! + //! \note This method should rarely be overridden. + virtual bool WriteEverything(FileWriterInterface* file_writer); + + //! \brief Registers a file offset pointer as one that should point to the + //! object on which this method is called. + //! + //! Once the file offset at which an object will be written is known (when it + //! enters #kStateWritable), registered RVA pointers will be updated. + //! + //! \param[in] rva A pointer to storage for the file offset that should + //! contain this object’s writable file offset, once it is known. + //! + //! \note Valid in #kStateFrozen or any preceding state. + // + // This is public instead of protected because objects of derived classes need + // to be able to register their own pointers with distinct objects. + void RegisterRVA(RVA* rva); + + //! \brief Registers a location descriptor as one that should point to the + //! object on which this method is called. + //! + //! Once an object’s size and the file offset at it will be written is known + //! (when it enters #kStateFrozen), the relevant data in registered location + //! descriptors will be updated. + //! + //! \param[in] location_descriptor A pointer to a location descriptor that + //! should contain this object’s writable size and file offset, once they + //! are known. + //! + //! \note Valid in #kStateFrozen or any preceding state. + // + // This is public instead of protected because objects of derived classes need + // to be able to register their own pointers with distinct objects. + void RegisterLocationDescriptor( + MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor); + + protected: + //! \brief Identifies the state of an object. + //! + //! Objects will normally transition through each of these states as they are + //! created, populated with data, and then written to a minidump file. + enum State { + //! \brief The object’s properties can be modified. + kStateMutable = 0, + + //! \brief The object is “frozen”. + //! + //! Its properties cannot be modified. Pointers to file offsets of other + //! structures may not yet be valid. + kStateFrozen, + + //! \brief The object is writable. + //! + //! The file offset at which it will be written is known. Pointers to file + //! offsets of other structures are valid when all objects in a tree are in + //! this state. + kStateWritable, + + //! \brief The object has been written to a minidump file. + kStateWritten, + }; + + //! \brief Identifies the phase during which an object will be written to a + //! minidump file. + enum Phase { + //! \brief Objects that are written to a minidump file “early”. + //! + //! The normal sequence is for an object to write itself and then write all + //! of its children. + kPhaseEarly = 0, + + //! \brief Objects that are written to a minidump file “late”. + //! + //! Some objects, such as those capturing memory region snapshots, are + //! written to minidump files after all other objects. This “late” phase + //! identifies such objects. This is useful to improve spatial locality in + //! in minidump files in accordance with expected access patterns: unlike + //! most other data, memory snapshots are large and the entire snapshots do + //! not need to be consulted in order to process a minidump file. + kPhaseLate, + }; + + //! \brief A size value used to signal failure by methods that return + //! `size_t`. + static const size_t kInvalidSize; + + MinidumpWritable(); + + //! \brief The state of the object. + State state() const { return state_; } + + //! \brief Transitions the object from #kStateMutable to #kStateFrozen. + //! + //! The default implementation marks the object as frozen and recursively + //! calls Freeze() on all of its children. Subclasses may override this method + //! to perform processing that should only be done once callers have finished + //! populating an object with data. Typically, a subclass implementation would + //! call RegisterRVA() or RegisterLocationDescriptor() on other objects as + //! appropriate, because at the time Freeze() runs, the in-memory locations of + //! RVAs and location descriptors are known and will not change for the + //! remaining duration of an object’s lifetime. + //! + //! \return `true` on success. `false` on failure, with an appropriate message + //! logged. + virtual bool Freeze(); + + //! \brief Returns the amount of space that this object will consume when + //! written to a minidump file, in bytes, not including any leading or + //! trailing padding necessary to maintain proper alignment. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + virtual size_t SizeOfObject() = 0; + + //! \brief Returns the object’s desired byte-boundary alignment. + //! + //! The default implementation returns `4`. Subclasses may override this as + //! needed. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + virtual size_t Alignment(); + + //! \brief Returns the object’s children. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + virtual std::vector<MinidumpWritable*> Children(); + + //! \brief Returns the object’s desired write phase. + //! + //! The default implementation returns #kPhaseEarly. Subclasses may override + //! this method to alter their write phase. + //! + //! \note Valid in any state. + virtual Phase WritePhase(); + + //! \brief Prepares the object to be written at a known file offset, + //! transitioning it from #kStateFrozen to #kStateWritable. + //! + //! This method is responsible for determining the final file offset of the + //! object, which may be increased from \a offset to meet alignment + //! requirements. It calls WillWriteAtOffsetImpl() for the benefit of + //! subclasses. It populates all RVAs and location descriptors registered with + //! it via RegisterRVA() and RegisterLocationDescriptor(). It also recurses + //! into all known children. + //! + //! \param[in] phase The phase during which the object will be written. If + //! this does not match Phase(), processing is suppressed, although + //! recursive processing will still occur on all children. This addresses + //! the case where parents and children do not write in the same phase. + //! \param[in] offset The file offset at which the object will be written. The + //! offset may need to be adjusted for alignment. + //! \param[out] write_sequence This object will append itself to this list, + //! such that on return from a recursive tree of WillWriteAtOffset() + //! calls, elements of the vector will be organized in the sequence that + //! the objects will be written to the minidump file. + //! + //! \return The file size consumed by this object and all children, including + //! any padding inserted to meet alignment requirements. On failure, + //! #kInvalidSize, with an appropriate message logged. + //! + //! \note This method cannot be overridden. Subclasses that need to perform + //! processing when an object transitions to #kStateWritable should + //! implement WillWriteAtOffsetImpl(), which is called by this method. + size_t WillWriteAtOffset(Phase phase, + FileOffset* offset, + std::vector<MinidumpWritable*>* write_sequence); + + //! \brief Called once an object’s writable file offset is determined, as it + //! transitions into #kStateWritable. + //! + //! Subclasses can override this method if they need to provide additional + //! processing once their writable file offset is known. Typically, this will + //! be done by subclasses that handle certain RVAs themselves instead of using + //! the RegisterRVA() interface. + //! + //! \param[in] offset The file offset at which the object will be written. The + //! value passed to this method will already have been adjusted to meet + //! alignment requirements. + //! + //! \return `true` on success. `false` on error, indicating that the minidump + //! file should not be written. + //! + //! \note Valid in #kStateFrozen. The object will transition to + //! #kStateWritable after this method returns. + virtual bool WillWriteAtOffsetImpl(FileOffset offset); + + //! \brief Writes the object, transitioning it from #kStateWritable to + //! #kStateWritten. + //! + //! Writes any padding necessary to meet alignment requirements, and then + //! calls WriteObject() to write the object’s content. + //! + //! \param[in] file_writer The file writer to receive the object’s content. + //! + //! \return `true` on success. `false` on error with an appropriate message + //! logged. + //! + //! \note This method cannot be overridden. Subclasses must override + //! WriteObject(). + bool WritePaddingAndObject(FileWriterInterface* file_writer); + + //! \brief Writes the object’s content. + //! + //! \param[in] file_writer The file writer to receive the object’s content. + //! + //! \return `true` on success. `false` on error, indicating that the content + //! could not be written to the minidump file. + //! + //! \note Valid in #kStateWritable. The object will transition to + //! #kStateWritten after this method returns. + virtual bool WriteObject(FileWriterInterface* file_writer) = 0; + + private: + std::vector<RVA*> registered_rvas_; // weak + + // weak + std::vector<MINIDUMP_LOCATION_DESCRIPTOR*> registered_location_descriptors_; + + size_t leading_pad_bytes_; + State state_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpWritable); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_
diff --git a/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc b/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc new file mode 100644 index 0000000..38d97de3 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc
@@ -0,0 +1,837 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_writable.h" + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "gtest/gtest.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +class BaseTestMinidumpWritable : public crashpad::internal::MinidumpWritable { + public: + BaseTestMinidumpWritable() + : MinidumpWritable(), + children_(), + expected_offset_(-1), + alignment_(0), + phase_(kPhaseEarly), + has_alignment_(false), + has_phase_(false), + verified_(false) {} + + ~BaseTestMinidumpWritable() { EXPECT_TRUE(verified_); } + + void SetAlignment(size_t alignment) { + alignment_ = alignment; + has_alignment_ = true; + } + + void AddChild(BaseTestMinidumpWritable* child) { children_.push_back(child); } + + void SetPhaseLate() { + phase_ = kPhaseLate; + has_phase_ = true; + } + + void Verify() { + verified_ = true; + EXPECT_EQ(kStateWritten, state()); + for (BaseTestMinidumpWritable* child : children_) { + child->Verify(); + } + } + + protected: + bool Freeze() override { + EXPECT_EQ(kStateMutable, state()); + bool rv = MinidumpWritable::Freeze(); + EXPECT_TRUE(rv); + EXPECT_EQ(kStateFrozen, state()); + return rv; + } + + size_t Alignment() override { + EXPECT_GE(state(), kStateFrozen); + return has_alignment_ ? alignment_ : MinidumpWritable::Alignment(); + } + + std::vector<MinidumpWritable*> Children() override { + EXPECT_GE(state(), kStateFrozen); + if (!children_.empty()) { + std::vector<MinidumpWritable*> children; + for (BaseTestMinidumpWritable* child : children_) { + children.push_back(child); + } + return children; + } + return MinidumpWritable::Children(); + } + + Phase WritePhase() override { + return has_phase_ ? phase_ : MinidumpWritable::Phase(); + } + + bool WillWriteAtOffsetImpl(FileOffset offset) override { + EXPECT_EQ(state(), kStateFrozen); + expected_offset_ = offset; + bool rv = MinidumpWritable::WillWriteAtOffsetImpl(offset); + EXPECT_TRUE(rv); + return rv; + } + + bool WriteObject(FileWriterInterface* file_writer) override { + EXPECT_EQ(state(), kStateWritable); + EXPECT_EQ(expected_offset_, file_writer->Seek(0, SEEK_CUR)); + + // Subclasses must override this. + return false; + } + + private: + std::vector<BaseTestMinidumpWritable*> children_; + FileOffset expected_offset_; + size_t alignment_; + Phase phase_; + bool has_alignment_; + bool has_phase_; + bool verified_; + + DISALLOW_COPY_AND_ASSIGN(BaseTestMinidumpWritable); +}; + +class TestStringMinidumpWritable final : public BaseTestMinidumpWritable { + public: + TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {} + + ~TestStringMinidumpWritable() {} + + void SetData(const std::string& string) { data_ = string; } + + protected: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return data_.size(); + } + + bool WriteObject(FileWriterInterface* file_writer) override { + BaseTestMinidumpWritable::WriteObject(file_writer); + bool rv = file_writer->Write(&data_[0], data_.size()); + EXPECT_TRUE(rv); + return rv; + } + + private: + std::string data_; + + DISALLOW_COPY_AND_ASSIGN(TestStringMinidumpWritable); +}; + +TEST(MinidumpWritable, MinidumpWritable) { + StringFile string_file; + + { + SCOPED_TRACE("empty"); + string_file.Reset(); + TestStringMinidumpWritable string_writable; + EXPECT_TRUE(string_writable.WriteEverything(&string_file)); + EXPECT_TRUE(string_file.string().empty()); + string_writable.Verify(); + } + + { + SCOPED_TRACE("childless"); + string_file.Reset(); + TestStringMinidumpWritable string_writable; + string_writable.SetData("a"); + EXPECT_TRUE(string_writable.WriteEverything(&string_file)); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ("a", string_file.string()); + string_writable.Verify(); + } + + { + SCOPED_TRACE("parent-child"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("b"); + TestStringMinidumpWritable child; + child.SetData("c"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ(std::string("b\0\0\0c", 5), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 2"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("de"); + TestStringMinidumpWritable child; + child.SetData("f"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ(std::string("de\0\0f", 5), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 3"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("ghi"); + TestStringMinidumpWritable child; + child.SetData("j"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ(std::string("ghi\0j", 5), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 4"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("klmn"); + TestStringMinidumpWritable child; + child.SetData("o"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("klmno", string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 5"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("pqrst"); + TestStringMinidumpWritable child; + child.SetData("u"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(9u, string_file.string().size()); + EXPECT_EQ(std::string("pqrst\0\0\0u", 9), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("two children"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child_0; + child_0.SetData("child_0"); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("child_1"); + parent.AddChild(&child_1); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(23u, string_file.string().size()); + EXPECT_EQ(std::string("parent\0\0child_0\0child_1", 23), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(26u, string_file.string().size()); + EXPECT_EQ(std::string("parent\0\0child\0\0\0grandchild", 26), + string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with empty parent"); + string_file.Reset(); + TestStringMinidumpWritable parent; + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(18u, string_file.string().size()); + EXPECT_EQ(std::string("child\0\0\0grandchild", 18), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with empty child"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(18u, string_file.string().size()); + EXPECT_EQ(std::string("parent\0\0grandchild", 18), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with empty grandchild"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(13u, string_file.string().size()); + EXPECT_EQ(std::string("parent\0\0child", 13), string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with late-phase grandchild"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + grandchild.SetPhaseLate(); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(26u, string_file.string().size()); + EXPECT_EQ(std::string("parent\0\0child\0\0\0grandchild", 26), + string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with late-phase child"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + child.SetPhaseLate(); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(25u, string_file.string().size()); + EXPECT_EQ(std::string("parent\0\0grandchild\0\0child", 25), + string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("family tree"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("P.."); + TestStringMinidumpWritable child_0; + child_0.SetData("C0."); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("C1."); + parent.AddChild(&child_1); + TestStringMinidumpWritable grandchild_00; + grandchild_00.SetData("G00"); + child_0.AddChild(&grandchild_00); + TestStringMinidumpWritable grandchild_01; + grandchild_01.SetData("G01"); + child_0.AddChild(&grandchild_01); + TestStringMinidumpWritable grandchild_10; + grandchild_10.SetData("G10"); + child_1.AddChild(&grandchild_10); + TestStringMinidumpWritable grandchild_11; + grandchild_11.SetData("G11"); + child_1.AddChild(&grandchild_11); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(27u, string_file.string().size()); + EXPECT_EQ(std::string("P..\0C0.\0G00\0G01\0C1.\0G10\0G11", 27), + string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("family tree with C0 late"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("P.."); + TestStringMinidumpWritable child_0; + child_0.SetData("C0."); + child_0.SetPhaseLate(); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("C1."); + parent.AddChild(&child_1); + TestStringMinidumpWritable grandchild_00; + grandchild_00.SetData("G00"); + child_0.AddChild(&grandchild_00); + TestStringMinidumpWritable grandchild_01; + grandchild_01.SetData("G01"); + child_0.AddChild(&grandchild_01); + TestStringMinidumpWritable grandchild_10; + grandchild_10.SetData("G10"); + child_1.AddChild(&grandchild_10); + TestStringMinidumpWritable grandchild_11; + grandchild_11.SetData("G11"); + child_1.AddChild(&grandchild_11); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(27u, string_file.string().size()); + EXPECT_EQ(std::string("P..\0G00\0G01\0C1.\0G10\0G11\0C0.", 27), + string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("family tree with G0 late"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("P.."); + TestStringMinidumpWritable child_0; + child_0.SetData("C0."); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("C1."); + parent.AddChild(&child_1); + TestStringMinidumpWritable grandchild_00; + grandchild_00.SetData("G00"); + grandchild_00.SetPhaseLate(); + child_0.AddChild(&grandchild_00); + TestStringMinidumpWritable grandchild_01; + grandchild_01.SetData("G01"); + grandchild_01.SetPhaseLate(); + child_0.AddChild(&grandchild_01); + TestStringMinidumpWritable grandchild_10; + grandchild_10.SetData("G10"); + child_1.AddChild(&grandchild_10); + TestStringMinidumpWritable grandchild_11; + grandchild_11.SetData("G11"); + child_1.AddChild(&grandchild_11); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(27u, string_file.string().size()); + EXPECT_EQ(std::string("P..\0C0.\0C1.\0G10\0G11\0G00\0G01", 27), + string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("align 1"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("p"); + TestStringMinidumpWritable child; + child.SetData("c"); + child.SetAlignment(1); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(2u, string_file.string().size()); + EXPECT_EQ("pc", string_file.string()); + parent.Verify(); + } + + { + SCOPED_TRACE("align 2"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("p"); + TestStringMinidumpWritable child; + child.SetData("c"); + child.SetAlignment(2); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(3u, string_file.string().size()); + EXPECT_EQ(std::string("p\0c", 3), string_file.string()); + parent.Verify(); + } +} + +class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable { + public: + TestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {} + + ~TestRVAMinidumpWritable() {} + + void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); } + + protected: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return sizeof(rva_); + } + + bool WriteObject(FileWriterInterface* file_writer) override { + BaseTestMinidumpWritable::WriteObject(file_writer); + EXPECT_TRUE(file_writer->Write(&rva_, sizeof(rva_))); + return true; + } + + private: + RVA rva_; + + DISALLOW_COPY_AND_ASSIGN(TestRVAMinidumpWritable); +}; + +RVA RVAAtIndex(const std::string& string, size_t index) { + return *reinterpret_cast<const RVA*>(&string[index * sizeof(RVA)]); +} + +TEST(MinidumpWritable, RVA) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + TestRVAMinidumpWritable rva_writable; + EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(RVA), string_file.string().size()); + EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 0)); + rva_writable.Verify(); + } + + { + SCOPED_TRACE("self"); + string_file.Reset(); + TestRVAMinidumpWritable rva_writable; + rva_writable.SetRVA(&rva_writable); + EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(RVA), string_file.string().size()); + EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 0)); + rva_writable.Verify(); + } + + { + SCOPED_TRACE("parent-child self"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + parent.SetRVA(&parent); + TestRVAMinidumpWritable child; + child.SetRVA(&child); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(2 * sizeof(RVA), string_file.string().size()); + EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 0)); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 1)); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child only"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + TestRVAMinidumpWritable child; + parent.SetRVA(&child); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(2 * sizeof(RVA), string_file.string().size()); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 0)); + EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 1)); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child circular"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + TestRVAMinidumpWritable child; + parent.SetRVA(&child); + child.SetRVA(&parent); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(2 * sizeof(RVA), string_file.string().size()); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 0)); + EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 1)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchildren"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + TestRVAMinidumpWritable child; + parent.SetRVA(&child); + parent.AddChild(&child); + TestRVAMinidumpWritable grandchild_0; + grandchild_0.SetRVA(&child); + child.AddChild(&grandchild_0); + TestRVAMinidumpWritable grandchild_1; + grandchild_1.SetRVA(&child); + child.AddChild(&grandchild_1); + TestRVAMinidumpWritable grandchild_2; + grandchild_2.SetRVA(&child); + child.AddChild(&grandchild_2); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(5 * sizeof(RVA), string_file.string().size()); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 0)); + EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 1)); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 2)); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 3)); + EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 4)); + parent.Verify(); + } +} + +class TestLocationDescriptorMinidumpWritable final + : public BaseTestMinidumpWritable { + public: + TestLocationDescriptorMinidumpWritable() + : BaseTestMinidumpWritable(), location_descriptor_(), string_() {} + + ~TestLocationDescriptorMinidumpWritable() {} + + void SetLocationDescriptor(MinidumpWritable* other) { + other->RegisterLocationDescriptor(&location_descriptor_); + } + + void SetString(const std::string& string) { string_ = string; } + + protected: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + // NUL-terminate. + return sizeof(location_descriptor_) + string_.size() + 1; + } + + bool WriteObject(FileWriterInterface* file_writer) override { + BaseTestMinidumpWritable::WriteObject(file_writer); + WritableIoVec iov; + iov.iov_base = &location_descriptor_; + iov.iov_len = sizeof(location_descriptor_); + std::vector<WritableIoVec> iovecs(1, iov); + // NUL-terminate. + iov.iov_base = &string_[0]; + iov.iov_len = string_.size() + 1; + iovecs.push_back(iov); + EXPECT_TRUE(file_writer->WriteIoVec(&iovecs)); + return true; + } + + private: + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor_; + std::string string_; + + DISALLOW_COPY_AND_ASSIGN(TestLocationDescriptorMinidumpWritable); +}; + +struct LocationDescriptorAndData { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + char string[1]; +}; + +const LocationDescriptorAndData* LDDAtIndex(const std::string& string, + size_t index) { + return reinterpret_cast<const LocationDescriptorAndData*>(&string[index]); +} + +TEST(MinidumpWritable, LocationDescriptor) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable location_descriptor_writable; + EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); + + ASSERT_EQ(9u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(0u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + location_descriptor_writable.Verify(); + } + + { + SCOPED_TRACE("self"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable location_descriptor_writable; + location_descriptor_writable.SetLocationDescriptor( + &location_descriptor_writable); + EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); + + ASSERT_EQ(9u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(9u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + location_descriptor_writable.Verify(); + } + + { + SCOPED_TRACE("self with data"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable location_descriptor_writable; + location_descriptor_writable.SetLocationDescriptor( + &location_descriptor_writable); + location_descriptor_writable.SetString("zz"); + EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); + + ASSERT_EQ(11u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(11u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + EXPECT_STREQ("zz", ldd->string); + location_descriptor_writable.Verify(); + } + + { + SCOPED_TRACE("parent-child self"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + parent.SetLocationDescriptor(&parent); + parent.SetString("yy"); + TestLocationDescriptorMinidumpWritable child; + child.SetLocationDescriptor(&child); + child.SetString("x"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(22u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(11u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + EXPECT_STREQ("yy", ldd->string); + ldd = LDDAtIndex(string_file.string(), 12); + EXPECT_EQ(10u, ldd->location_descriptor.DataSize); + EXPECT_EQ(12u, ldd->location_descriptor.Rva); + EXPECT_STREQ("x", ldd->string); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child only"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + TestLocationDescriptorMinidumpWritable child; + parent.SetLocationDescriptor(&child); + parent.SetString("www"); + child.SetString("vv"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(23u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(11u, ldd->location_descriptor.DataSize); + EXPECT_EQ(12u, ldd->location_descriptor.Rva); + EXPECT_STREQ("www", ldd->string); + ldd = LDDAtIndex(string_file.string(), 12); + EXPECT_EQ(0u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + EXPECT_STREQ("vv", ldd->string); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child circular"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + TestLocationDescriptorMinidumpWritable child; + parent.SetLocationDescriptor(&child); + parent.SetString("uuuu"); + child.SetLocationDescriptor(&parent); + child.SetString("tttt"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(29u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(13u, ldd->location_descriptor.DataSize); + EXPECT_EQ(16u, ldd->location_descriptor.Rva); + EXPECT_STREQ("uuuu", ldd->string); + ldd = LDDAtIndex(string_file.string(), 16); + EXPECT_EQ(13u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + EXPECT_STREQ("tttt", ldd->string); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchildren"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + TestLocationDescriptorMinidumpWritable child; + parent.SetLocationDescriptor(&child); + parent.SetString("s"); + parent.AddChild(&child); + child.SetString("r"); + TestLocationDescriptorMinidumpWritable grandchild_0; + grandchild_0.SetLocationDescriptor(&child); + grandchild_0.SetString("q"); + child.AddChild(&grandchild_0); + TestLocationDescriptorMinidumpWritable grandchild_1; + grandchild_1.SetLocationDescriptor(&child); + grandchild_1.SetString("p"); + child.AddChild(&grandchild_1); + TestLocationDescriptorMinidumpWritable grandchild_2; + grandchild_2.SetLocationDescriptor(&child); + grandchild_2.SetString("o"); + child.AddChild(&grandchild_2); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(58u, string_file.string().size()); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(10u, ldd->location_descriptor.DataSize); + EXPECT_EQ(12u, ldd->location_descriptor.Rva); + EXPECT_STREQ("s", ldd->string); + ldd = LDDAtIndex(string_file.string(), 12); + EXPECT_EQ(0u, ldd->location_descriptor.DataSize); + EXPECT_EQ(0u, ldd->location_descriptor.Rva); + EXPECT_STREQ("r", ldd->string); + ldd = LDDAtIndex(string_file.string(), 24); + EXPECT_EQ(10u, ldd->location_descriptor.DataSize); + EXPECT_EQ(12u, ldd->location_descriptor.Rva); + EXPECT_STREQ("q", ldd->string); + ldd = LDDAtIndex(string_file.string(), 36); + EXPECT_EQ(10u, ldd->location_descriptor.DataSize); + EXPECT_EQ(12u, ldd->location_descriptor.Rva); + EXPECT_STREQ("p", ldd->string); + ldd = LDDAtIndex(string_file.string(), 48); + EXPECT_EQ(10u, ldd->location_descriptor.DataSize); + EXPECT_EQ(12u, ldd->location_descriptor.Rva); + EXPECT_STREQ("o", ldd->string); + parent.Verify(); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc b/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc new file mode 100644 index 0000000..f8488b4 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc
@@ -0,0 +1,61 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/minidump_writer_util.h" + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "util/stdlib/strlcpy.h" + +namespace crashpad { +namespace internal { + +// static +void MinidumpWriterUtil::AssignTimeT(uint32_t* destination, time_t source) { + if (!base::IsValueInRangeForNumericType<uint32_t>(source)) { + LOG(WARNING) << "timestamp " << source << " out of range"; + } + + *destination = static_cast<uint32_t>(source); +} + +// static +base::string16 MinidumpWriterUtil::ConvertUTF8ToUTF16(const std::string& utf8) { + base::string16 utf16; + if (!base::UTF8ToUTF16(utf8.data(), utf8.length(), &utf16)) { + LOG(WARNING) << "string " << utf8 + << " cannot be converted to UTF-16 losslessly"; + } + return utf16; +} + +// static +void MinidumpWriterUtil::AssignUTF8ToUTF16(base::char16* destination, + size_t destination_size, + const std::string& source) { + base::string16 source_utf16 = ConvertUTF8ToUTF16(source); + if (source_utf16.size() > destination_size - 1) { + LOG(WARNING) << "string " << source << " UTF-16 length " + << source_utf16.size() + << " will be truncated to UTF-16 length " + << destination_size - 1; + } + + source_utf16.resize(destination_size - 1); + c16lcpy(destination, source_utf16.c_str(), destination_size); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/minidump_writer_util.h b/third_party/crashpad/crashpad/minidump/minidump_writer_util.h new file mode 100644 index 0000000..3b2ab5e7b --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/minidump_writer_util.h
@@ -0,0 +1,90 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <time.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/strings/string16.h" + +namespace crashpad { +namespace internal { + +//! \brief A collection of utility functions used by the MinidumpWritable family +//! of classes. +class MinidumpWriterUtil final { + public: + //! \brief Assigns a `time_t` value, logging a warning if the result overflows + //! the destination buffer and will be truncated. + //! + //! \param[out] destination A pointer to the variable to be assigned to. + //! \param[in] source The value to assign. + //! + //! The minidump format uses `uint32_t` for many timestamp values, but + //! `time_t` may be wider than this. These year 2038 bugs are a limitation of + //! the minidump format. An out-of-range error will be noted with a warning, + //! but is not considered fatal. \a source will be truncated and assigned to + //! \a destination in this case. + //! + //! For `time_t` values with nonfatal overflow semantics, this function is + //! used in preference to AssignIfInRange(), which fails without performing an + //! assignment when an out-of-range condition is detected. + static void AssignTimeT(uint32_t* destination, time_t source); + + //! \brief Converts a UTF-8 string to UTF-16 and returns it. If the string + //! cannot be converted losslessly, indicating that the input is not + //! well-formed UTF-8, a warning is logged. + //! + //! \param[in] utf8 The UTF-8-encoded string to convert. + //! + //! \return The \a utf8 string, converted to UTF-16 encoding. If the + //! conversion is lossy, U+FFFD “replacement characters” will be + //! introduced. + static base::string16 ConvertUTF8ToUTF16(const std::string& utf8); + + //! \brief Converts a UTF-8 string to UTF-16 and places it into a buffer of + //! fixed size, taking care to `NUL`-terminate the buffer and not to + //! overflow it. If the string will be truncated or if it cannot be + //! converted losslessly, a warning is logged. + //! + //! Any unused portion of the \a destination buffer that is not written to by + //! the converted string will be overwritten with `NUL` UTF-16 code units, + //! thus, this function always writes \a destination_size `char16` units. + //! + //! If the conversion is lossy, U+FFFD “replacement characters” will be + //! introduced. + //! + //! \param[out] destination A pointer to the destination buffer, where the + //! UTF-16-encoded string will be written. + //! \param[in] destination_size The size of \a destination in `char16` units, + //! including space used by a `NUL` terminator. + //! \param[in] source The UTF-8-encoded input string. + static void AssignUTF8ToUTF16(base::char16* destination, + size_t destination_size, + const std::string& source); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MinidumpWriterUtil); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc new file mode 100644 index 0000000..a0e0640 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc
@@ -0,0 +1,380 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/test/minidump_context_test_util.h" + +#include "base/basictypes.h" +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "snapshot/cpu_context.h" +#include "snapshot/test/test_cpu_context.h" + +namespace crashpad { +namespace test { + +void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextX86; + return; + } + + context->context_flags = kMinidumpContextX86All; + + uint32_t value = seed; + + context->eax = value++; + context->ebx = value++; + context->ecx = value++; + context->edx = value++; + context->edi = value++; + context->esi = value++; + context->ebp = value++; + context->esp = value++; + context->eip = value++; + context->eflags = value++; + context->cs = value++ & 0xffff; + context->ds = value++ & 0xffff; + context->es = value++ & 0xffff; + context->fs = value++ & 0xffff; + context->gs = value++ & 0xffff; + context->ss = value++ & 0xffff; + + InitializeCPUContextX86Fxsave(&context->fxsave, &value); + + context->dr0 = value++; + context->dr1 = value++; + context->dr2 = value++; + context->dr3 = value++; + value += 2; // Minidumps don’t carry dr4 or dr5. + context->dr6 = value++; + context->dr7 = value++; + + // Copy the values that are aliased between the fxsave area + // (context->extended_registers) and the floating-point save area + // (context->float_save). + context->float_save.control_word = context->fxsave.fcw; + context->float_save.status_word = context->fxsave.fsw; + context->float_save.tag_word = CPUContextX86::FxsaveToFsaveTagWord( + context->fxsave.fsw, context->fxsave.ftw, context->fxsave.st_mm); + context->float_save.error_offset = context->fxsave.fpu_ip; + context->float_save.error_selector = context->fxsave.fpu_cs; + context->float_save.data_offset = context->fxsave.fpu_dp; + context->float_save.data_selector = context->fxsave.fpu_ds; + for (size_t st_mm_index = 0; + st_mm_index < arraysize(context->fxsave.st_mm); + ++st_mm_index) { + for (size_t byte = 0; + byte < arraysize(context->fxsave.st_mm[st_mm_index].st); + ++byte) { + size_t st_index = + st_mm_index * arraysize(context->fxsave.st_mm[st_mm_index].st) + byte; + context->float_save.register_area[st_index] = + context->fxsave.st_mm[st_mm_index].st[byte]; + } + } + + // Set this field last, because it has no analogue in CPUContextX86. + context->float_save.spare_0 = value++; +} + +void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextAMD64; + return; + } + + context->context_flags = kMinidumpContextAMD64All; + + uint32_t value = seed; + + context->rax = value++; + context->rbx = value++; + context->rcx = value++; + context->rdx = value++; + context->rdi = value++; + context->rsi = value++; + context->rbp = value++; + context->rsp = value++; + context->r8 = value++; + context->r9 = value++; + context->r10 = value++; + context->r11 = value++; + context->r12 = value++; + context->r13 = value++; + context->r14 = value++; + context->r15 = value++; + context->rip = value++; + context->eflags = value++; + context->cs = static_cast<uint16_t>(value++); + context->fs = static_cast<uint16_t>(value++); + context->gs = static_cast<uint16_t>(value++); + + InitializeCPUContextX86_64Fxsave(&context->fxsave, &value); + + // mxcsr appears twice, and the two values should be aliased. + context->mx_csr = context->fxsave.mxcsr; + + context->dr0 = value++; + context->dr1 = value++; + context->dr2 = value++; + context->dr3 = value++; + value += 2; // Minidumps don’t carry dr4 or dr5. + context->dr6 = value++; + context->dr7 = value++; + + // Set these fields last, because they have no analogues in CPUContextX86_64. + context->p1_home = value++; + context->p2_home = value++; + context->p3_home = value++; + context->p4_home = value++; + context->p5_home = value++; + context->p6_home = value++; + context->ds = static_cast<uint16_t>(value++); + context->es = static_cast<uint16_t>(value++); + context->ss = static_cast<uint16_t>(value++); + for (size_t index = 0; index < arraysize(context->vector_register); ++index) { + context->vector_register[index].lo = value++; + context->vector_register[index].hi = value++; + } + context->vector_control = value++; + context->debug_control = value++; + context->last_branch_to_rip = value++; + context->last_branch_from_rip = value++; + context->last_exception_to_rip = value++; + context->last_exception_from_rip = value++; +} + +namespace { + +// Using gtest assertions, compares |expected| to |observed|. This is +// templatized because the CPUContextX86::Fxsave and CPUContextX86_64::Fxsave +// are nearly identical but have different sizes for the members |xmm|, +// |reserved_4|, and |available|. +template <typename FxsaveType> +void ExpectMinidumpContextFxsave(const FxsaveType* expected, + const FxsaveType* observed) { + EXPECT_EQ(expected->fcw, observed->fcw); + EXPECT_EQ(expected->fsw, observed->fsw); + EXPECT_EQ(expected->ftw, observed->ftw); + EXPECT_EQ(expected->reserved_1, observed->reserved_1); + EXPECT_EQ(expected->fop, observed->fop); + EXPECT_EQ(expected->fpu_ip, observed->fpu_ip); + EXPECT_EQ(expected->fpu_cs, observed->fpu_cs); + EXPECT_EQ(expected->reserved_2, observed->reserved_2); + EXPECT_EQ(expected->fpu_dp, observed->fpu_dp); + EXPECT_EQ(expected->fpu_ds, observed->fpu_ds); + EXPECT_EQ(expected->reserved_3, observed->reserved_3); + EXPECT_EQ(expected->mxcsr, observed->mxcsr); + EXPECT_EQ(expected->mxcsr_mask, observed->mxcsr_mask); + for (size_t st_mm_index = 0; + st_mm_index < arraysize(expected->st_mm); + ++st_mm_index) { + SCOPED_TRACE(base::StringPrintf("st_mm_index %" PRIuS, st_mm_index)); + for (size_t byte = 0; + byte < arraysize(expected->st_mm[st_mm_index].st); + ++byte) { + EXPECT_EQ(expected->st_mm[st_mm_index].st[byte], + observed->st_mm[st_mm_index].st[byte]) << "byte " << byte; + } + for (size_t byte = 0; + byte < arraysize(expected->st_mm[st_mm_index].st_reserved); + ++byte) { + EXPECT_EQ(expected->st_mm[st_mm_index].st_reserved[byte], + observed->st_mm[st_mm_index].st_reserved[byte]) + << "byte " << byte; + } + } + for (size_t xmm_index = 0; + xmm_index < arraysize(expected->xmm); + ++xmm_index) { + SCOPED_TRACE(base::StringPrintf("xmm_index %" PRIuS, xmm_index)); + for (size_t byte = 0; byte < arraysize(expected->xmm[xmm_index]); ++byte) { + EXPECT_EQ(expected->xmm[xmm_index][byte], observed->xmm[xmm_index][byte]) + << "byte " << byte; + } + } + for (size_t byte = 0; byte < arraysize(expected->reserved_4); ++byte) { + EXPECT_EQ(expected->reserved_4[byte], observed->reserved_4[byte]) + << "byte " << byte; + } + for (size_t byte = 0; byte < arraysize(expected->available); ++byte) { + EXPECT_EQ(expected->available[byte], observed->available[byte]) + << "byte " << byte; + } +} + +} // namespace + +void ExpectMinidumpContextX86( + uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) { + MinidumpContextX86 expected; + InitializeMinidumpContextX86(&expected, expect_seed); + + EXPECT_EQ(expected.context_flags, observed->context_flags); + EXPECT_EQ(expected.dr0, observed->dr0); + EXPECT_EQ(expected.dr1, observed->dr1); + EXPECT_EQ(expected.dr2, observed->dr2); + EXPECT_EQ(expected.dr3, observed->dr3); + EXPECT_EQ(expected.dr6, observed->dr6); + EXPECT_EQ(expected.dr7, observed->dr7); + + EXPECT_EQ(expected.float_save.control_word, + observed->float_save.control_word); + EXPECT_EQ(expected.float_save.status_word, observed->float_save.status_word); + EXPECT_EQ(expected.float_save.tag_word, observed->float_save.tag_word); + EXPECT_EQ(expected.float_save.error_offset, + observed->float_save.error_offset); + EXPECT_EQ(expected.float_save.error_selector, + observed->float_save.error_selector); + EXPECT_EQ(expected.float_save.data_offset, observed->float_save.data_offset); + EXPECT_EQ(expected.float_save.data_selector, + observed->float_save.data_selector); + for (size_t index = 0; + index < arraysize(expected.float_save.register_area); + ++index) { + EXPECT_EQ(expected.float_save.register_area[index], + observed->float_save.register_area[index]) << "index " << index; + } + if (snapshot) { + EXPECT_EQ(0u, observed->float_save.spare_0); + } else { + EXPECT_EQ(expected.float_save.spare_0, observed->float_save.spare_0); + } + + EXPECT_EQ(expected.gs, observed->gs); + EXPECT_EQ(expected.fs, observed->fs); + EXPECT_EQ(expected.es, observed->es); + EXPECT_EQ(expected.ds, observed->ds); + EXPECT_EQ(expected.edi, observed->edi); + EXPECT_EQ(expected.esi, observed->esi); + EXPECT_EQ(expected.ebx, observed->ebx); + EXPECT_EQ(expected.edx, observed->edx); + EXPECT_EQ(expected.ecx, observed->ecx); + EXPECT_EQ(expected.eax, observed->eax); + EXPECT_EQ(expected.ebp, observed->ebp); + EXPECT_EQ(expected.eip, observed->eip); + EXPECT_EQ(expected.cs, observed->cs); + EXPECT_EQ(expected.eflags, observed->eflags); + EXPECT_EQ(expected.esp, observed->esp); + EXPECT_EQ(expected.ss, observed->ss); + + ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave); +} + +void ExpectMinidumpContextAMD64( + uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot) { + MinidumpContextAMD64 expected; + InitializeMinidumpContextAMD64(&expected, expect_seed); + + EXPECT_EQ(expected.context_flags, observed->context_flags); + + if (snapshot) { + EXPECT_EQ(0u, observed->p1_home); + EXPECT_EQ(0u, observed->p2_home); + EXPECT_EQ(0u, observed->p3_home); + EXPECT_EQ(0u, observed->p4_home); + EXPECT_EQ(0u, observed->p5_home); + EXPECT_EQ(0u, observed->p6_home); + } else { + EXPECT_EQ(expected.p1_home, observed->p1_home); + EXPECT_EQ(expected.p2_home, observed->p2_home); + EXPECT_EQ(expected.p3_home, observed->p3_home); + EXPECT_EQ(expected.p4_home, observed->p4_home); + EXPECT_EQ(expected.p5_home, observed->p5_home); + EXPECT_EQ(expected.p6_home, observed->p6_home); + } + + EXPECT_EQ(expected.mx_csr, observed->mx_csr); + + EXPECT_EQ(expected.cs, observed->cs); + if (snapshot) { + EXPECT_EQ(0u, observed->ds); + EXPECT_EQ(0u, observed->es); + } else { + EXPECT_EQ(expected.ds, observed->ds); + EXPECT_EQ(expected.es, observed->es); + } + EXPECT_EQ(expected.fs, observed->fs); + EXPECT_EQ(expected.gs, observed->gs); + if (snapshot) { + EXPECT_EQ(0u, observed->ss); + } else { + EXPECT_EQ(expected.ss, observed->ss); + } + + EXPECT_EQ(expected.eflags, observed->eflags); + + EXPECT_EQ(expected.dr0, observed->dr0); + EXPECT_EQ(expected.dr1, observed->dr1); + EXPECT_EQ(expected.dr2, observed->dr2); + EXPECT_EQ(expected.dr3, observed->dr3); + EXPECT_EQ(expected.dr6, observed->dr6); + EXPECT_EQ(expected.dr7, observed->dr7); + + EXPECT_EQ(expected.rax, observed->rax); + EXPECT_EQ(expected.rcx, observed->rcx); + EXPECT_EQ(expected.rdx, observed->rdx); + EXPECT_EQ(expected.rbx, observed->rbx); + EXPECT_EQ(expected.rsp, observed->rsp); + EXPECT_EQ(expected.rbp, observed->rbp); + EXPECT_EQ(expected.rsi, observed->rsi); + EXPECT_EQ(expected.rdi, observed->rdi); + EXPECT_EQ(expected.r8, observed->r8); + EXPECT_EQ(expected.r9, observed->r9); + EXPECT_EQ(expected.r10, observed->r10); + EXPECT_EQ(expected.r11, observed->r11); + EXPECT_EQ(expected.r12, observed->r12); + EXPECT_EQ(expected.r13, observed->r13); + EXPECT_EQ(expected.r14, observed->r14); + EXPECT_EQ(expected.r15, observed->r15); + EXPECT_EQ(expected.rip, observed->rip); + + ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave); + + for (size_t index = 0; index < arraysize(expected.vector_register); ++index) { + if (snapshot) { + EXPECT_EQ(0u, observed->vector_register[index].lo) << "index " << index; + EXPECT_EQ(0u, observed->vector_register[index].hi) << "index " << index; + } else { + EXPECT_EQ(expected.vector_register[index].lo, + observed->vector_register[index].lo) << "index " << index; + EXPECT_EQ(expected.vector_register[index].hi, + observed->vector_register[index].hi) << "index " << index; + } + } + + if (snapshot) { + EXPECT_EQ(0u, observed->vector_control); + EXPECT_EQ(0u, observed->debug_control); + EXPECT_EQ(0u, observed->last_branch_to_rip); + EXPECT_EQ(0u, observed->last_branch_from_rip); + EXPECT_EQ(0u, observed->last_exception_to_rip); + EXPECT_EQ(0u, observed->last_exception_from_rip); + } else { + EXPECT_EQ(expected.vector_control, observed->vector_control); + EXPECT_EQ(expected.debug_control, observed->debug_control); + EXPECT_EQ(expected.last_branch_to_rip, observed->last_branch_to_rip); + EXPECT_EQ(expected.last_branch_from_rip, observed->last_branch_from_rip); + EXPECT_EQ(expected.last_exception_to_rip, observed->last_exception_to_rip); + EXPECT_EQ(expected.last_exception_from_rip, + observed->last_exception_from_rip); + } +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.h new file mode 100644 index 0000000..14f5743 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.h
@@ -0,0 +1,75 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_ + +#include <stdint.h> + +#include "minidump/minidump_context.h" + +namespace crashpad { +namespace test { + +//! \brief Initializes a context structure for testing. +//! +//! Initialization is compatible with the initialization used by CPUContext test +//! initialization functions such as InitializeCPUContextX86() and +//! InitializeCPUContextX86_64() for identical \a seed values. +//! +//! \param[out] context The structure to initialize. +//! \param[in] seed The seed value. Initializing two context structures of the +//! same type with identical seed values should produce identical context +//! structures. Initialization with a different seed value should produce +//! a different context structure. If \a seed is `0`, \a context is zeroed +//! out entirely except for the flags field, which will identify the context +//! type. If \a seed is nonzero, \a context will be populated entirely with +//! nonzero values. +//! +//! \{ +void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed); +void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, + uint32_t seed); +//! \} + +//! \brief Verifies, via gtest assertions, that a context structure contains +//! expected values. +//! +//! \param[in] expect_seed The seed value used to initialize a context +//! structure. This is the seed value used with +//! InitializeMinidumpContext*(). +//! \param[in] observed The context structure to check. All fields of this +//! structure will be compared against the expected context structure, one +//! initialized with \a expect_seed. +//! \param[in] snapshot If `true`, compare \a observed to a context structure +//! expected to be produced from a CPUContext snapshot. If `false`, compare +//! \a observed to a native minidump context structure. CPUContext snapshot +//! structures may carry different sets of data than native minidump context +//! structures in meaningless ways. When `true`, fields not found in +//! CPUContext structures are expected to be `0`. When `false`, all fields +//! are compared. This makes it possible to test both that these fields are +//! passed through correctly by the native minidump writer and are zeroed +//! out when creating a minidump context structure from a CPUContext +//! structure. +//! \{ +void ExpectMinidumpContextX86( + uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot); +void ExpectMinidumpContextAMD64( + uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot); +//! \} + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_file_writer_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_file_writer_test_util.cc new file mode 100644 index 0000000..723e97d0 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_file_writer_test_util.cc
@@ -0,0 +1,59 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/test/minidump_file_writer_test_util.h" + +#include "gtest/gtest.h" +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +const MINIDUMP_HEADER* MinidumpHeaderAtStart( + const std::string& file_contents, + const MINIDUMP_DIRECTORY** directory) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = sizeof(MINIDUMP_HEADER); + location_descriptor.Rva = 0; + + const MINIDUMP_HEADER* header = + MinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>( + file_contents, location_descriptor); + + if (header) { + location_descriptor.DataSize = + header->NumberOfStreams * sizeof(MINIDUMP_DIRECTORY); + location_descriptor.Rva = header->StreamDirectoryRva; + *directory = MinidumpWritableAtLocationDescriptor<MINIDUMP_DIRECTORY>( + file_contents, location_descriptor); + } else { + *directory = nullptr; + } + + return header; +} + +void VerifyMinidumpHeader(const MINIDUMP_HEADER* header, + uint32_t streams, + uint32_t timestamp) { + ASSERT_TRUE(header); + ASSERT_EQ(streams, header->NumberOfStreams); + ASSERT_EQ(streams ? sizeof(MINIDUMP_HEADER) : 0u, header->StreamDirectoryRva); + EXPECT_EQ(0u, header->CheckSum); + EXPECT_EQ(timestamp, header->TimeDateStamp); + EXPECT_EQ(MiniDumpNormal, header->Flags); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_file_writer_test_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_file_writer_test_util.h new file mode 100644 index 0000000..d1952583 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_file_writer_test_util.h
@@ -0,0 +1,65 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include <string> + +namespace crashpad { +namespace test { + +//! \brief Returns the MINIDUMP_HEADER at the start of a minidump file, along +//! with the MINIDUMP_DIRECTORY it references. +//! +//! This function validates the MINIDUMP_HEADER::Signature and +//! MINIDUMP_HEADER::Version fields. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[out] directory The MINIDUMP_DIRECTORY referenced by the +//! MINIDUMP_HEADER. If the MINIDUMP_HEADER does not reference a +//! MINIDUMP_DIRECTORY, `nullptr` without raising a gtest assertion. If the +//! referenced MINIDUMP_DIRECTORY is not valid, `nullptr` with a gtest +//! assertion raised. On failure, `nullptr`. +//! +//! \return On success, the MINIDUMP_HEADER at the beginning of the minidump +//! file. On failure, raises a gtest assertion and returns `nullptr`. +const MINIDUMP_HEADER* MinidumpHeaderAtStart( + const std::string& file_contents, + const MINIDUMP_DIRECTORY** directory); + +//! \brief Verifies, via gtest assertions, that a MINIDUMP_HEADER contains +//! expected values. +//! +//! All fields in the MINIDUMP_HEADER will be evaluated except for the Signature +//! and Version fields, because those are checked by MinidumpHeaderAtStart(). +//! Most other fields are are compared to their correct default values. +//! MINIDUMP_HEADER::NumberOfStreams is compared to \a streams, and +//! MINIDUMP_HEADER::TimeDateStamp is compared to \a timestamp. Most fields are +//! checked with nonfatal EXPECT-style assertions, but +//! MINIDUMP_HEADER::NumberOfStreams and MINIDUMP_HEADER::StreamDirectoryRva are +//! checked with fatal ASSERT-style assertions, because they must be correct in +//! order for processing of the minidump to continue. +void VerifyMinidumpHeader(const MINIDUMP_HEADER* header, + uint32_t streams, + uint32_t timestamp); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_memory_writer_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_memory_writer_test_util.cc new file mode 100644 index 0000000..4944e6d --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_memory_writer_test_util.cc
@@ -0,0 +1,103 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/test/minidump_memory_writer_test_util.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { + +TestMinidumpMemoryWriter::TestMinidumpMemoryWriter(uint64_t base_address, + size_t size, + uint8_t value) + : MinidumpMemoryWriter(), + base_address_(base_address), + expected_offset_(-1), + size_(size), + value_(value) { +} + +TestMinidumpMemoryWriter::~TestMinidumpMemoryWriter() { +} + +uint64_t TestMinidumpMemoryWriter::MemoryRangeBaseAddress() const { + EXPECT_EQ(state(), kStateFrozen); + return base_address_; +} + +size_t TestMinidumpMemoryWriter::MemoryRangeSize() const { + EXPECT_GE(state(), kStateFrozen); + return size_; +} + +bool TestMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { + EXPECT_EQ(state(), kStateFrozen); + expected_offset_ = offset; + bool rv = MinidumpMemoryWriter::WillWriteAtOffsetImpl(offset); + EXPECT_TRUE(rv); + return rv; +} + +bool TestMinidumpMemoryWriter::WriteObject(FileWriterInterface* file_writer) { + EXPECT_EQ(state(), kStateWritable); + EXPECT_EQ(expected_offset_, file_writer->Seek(0, SEEK_CUR)); + + bool rv = true; + if (size_ > 0) { + std::string data(size_, value_); + rv = file_writer->Write(&data[0], size_); + EXPECT_TRUE(rv); + } + + return rv; +} + +void ExpectMinidumpMemoryDescriptor( + const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed) { + EXPECT_EQ(expected->StartOfMemoryRange, observed->StartOfMemoryRange); + EXPECT_EQ(expected->Memory.DataSize, observed->Memory.DataSize); + if (expected->Memory.Rva != 0) { + const uint32_t kMemoryAlignment = 16; + EXPECT_EQ( + (expected->Memory.Rva + kMemoryAlignment - 1) & ~(kMemoryAlignment - 1), + observed->Memory.Rva); + } +} + +void ExpectMinidumpMemoryDescriptorAndContents( + const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed, + const std::string& file_contents, + uint8_t value, + bool at_eof) { + ExpectMinidumpMemoryDescriptor(expected, observed); + + if (at_eof) { + EXPECT_EQ(file_contents.size(), + observed->Memory.Rva + observed->Memory.DataSize); + } else { + EXPECT_GE(file_contents.size(), + observed->Memory.Rva + observed->Memory.DataSize); + } + + std::string expected_data(expected->Memory.DataSize, value); + std::string observed_data(&file_contents[observed->Memory.Rva], + observed->Memory.DataSize); + EXPECT_EQ(expected_data, observed_data); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_memory_writer_test_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_memory_writer_test_util.h new file mode 100644 index 0000000..f4b97675 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_memory_writer_test_util.h
@@ -0,0 +1,105 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_ + +#include "minidump/minidump_memory_writer.h" + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> + +#include <string> + +#include "base/basictypes.h" +#include "util/file/file_writer.h" + +namespace crashpad { +namespace test { + +//! \brief A MinidumpMemoryWriter implementation used for testing. +//! +//! TestMinidumpMemoryWriter objects are created with a fixed base address and +//! size, and will write the same byte (\a value) repeatedly, \a size times. +class TestMinidumpMemoryWriter final : public MinidumpMemoryWriter { + public: + TestMinidumpMemoryWriter(uint64_t base_address, size_t size, uint8_t value); + ~TestMinidumpMemoryWriter(); + + protected: + // MinidumpMemoryWriter: + uint64_t MemoryRangeBaseAddress() const override; + size_t MemoryRangeSize() const override; + + // MinidumpWritable: + bool WillWriteAtOffsetImpl(FileOffset offset) override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + uint64_t base_address_; + FileOffset expected_offset_; + size_t size_; + uint8_t value_; + + DISALLOW_COPY_AND_ASSIGN(TestMinidumpMemoryWriter); +}; + +//! \brief Verifies, via gtest assertions, that a MINIDUMP_MEMORY_DESCRIPTOR +//! structure contains expected values. +//! +//! In \a expected and \a observed, +//! MINIDUMP_MEMORY_DESCRIPTOR::StartOfMemoryRange and +//! MINIDUMP_LOCATION_DESCRIPTOR::DataSize are compared and must match. If +//! MINIDUMP_LOCATION_DESCRIPTOR::Rva is nonzero in \a expected, the same field +//! in \a observed must match it, subject to a 16-byte alignment augmentation. +//! +//! \param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! expected values. +//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! observed values. +void ExpectMinidumpMemoryDescriptor(const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed); + +//! \brief Verifies, via gtest assertions, that a MINIDUMP_MEMORY_DESCRIPTOR +//! structure contains expected values, and that the memory region it points +//! to contains expected values assuming it was written by a +//! TestMinidumpMemoryWriter object. +//! +//! \a expected and \a observed are compared by +//! ExpectMinidumpMemoryDescriptor(). +//! +//! \param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! expected values. +//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! observed values. +//! \param[in] file_contents The contents of the minidump file in which \a +//! observed was found. The memory region referenced by \a observed will be +//! read from this string. +//! \param[in] value The \a value used to create a TestMinidumpMemoryWriter. +//! Each byte of memory in the region referenced by \a observed must be this +//! value. +//! \param[in] at_eof If `true`, the region referenced by \a observed must +//! appear at the end of \a file_contents, without any data following it. +void ExpectMinidumpMemoryDescriptorAndContents( + const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed, + const std::string& file_contents, + uint8_t value, + bool at_eof); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_rva_list_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_rva_list_test_util.cc new file mode 100644 index 0000000..ebaa8f07 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_rva_list_test_util.cc
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/test/minidump_rva_list_test_util.h" + +#include <windows.h> +#include <dbghelp.h> + +#include "minidump/minidump_extensions.h" +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +const MinidumpRVAList* MinidumpRVAListAtStart(const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = + static_cast<uint32_t>(sizeof(MinidumpRVAList) + count * sizeof(RVA)); + location_descriptor.Rva = 0; + + const MinidumpRVAList* list = + MinidumpWritableAtLocationDescriptor<MinidumpRVAList>( + file_contents, location_descriptor); + if (!list) { + return nullptr; + } + + if (list->count != count) { + EXPECT_EQ(count, list->count); + return nullptr; + } + + return list; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_rva_list_test_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_rva_list_test_util.h new file mode 100644 index 0000000..4865d01 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_rva_list_test_util.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_ + +#include <sys/types.h> + +#include <string> + +namespace crashpad { + +struct MinidumpRVAList; + +namespace test { + +//! \brief Returns the MinidumpRVAList at the start of a minidump file. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] count The number of ::RVA objects expected in the +//! MinidumpRVAList. This function will only be successful if exactly this +//! many objects are present, and if space for them exists in \a +//! file_contents. +//! +//! \return On success, the MinidumpRVAList at the beginning of the file. On +//! failure, raises a gtest assertion and returns `nullptr`. +const MinidumpRVAList* MinidumpRVAListAtStart(const std::string& file_contents, + size_t count); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_string_writer_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_string_writer_test_util.cc new file mode 100644 index 0000000..6f5ff6b --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_string_writer_test_util.cc
@@ -0,0 +1,105 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/test/minidump_string_writer_test_util.h" + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +namespace { + +template <typename T> +const T* TMinidumpStringAtRVA(const std::string& file_contents, RVA rva) { + const T* string_base = MinidumpWritableAtRVA<T>(file_contents, rva); + if (!string_base) { + return nullptr; + } + + // |Length| must indicate the ability to store an integral number of code + // units. + const size_t kCodeUnitSize = sizeof(string_base->Buffer[0]); + if (string_base->Length % kCodeUnitSize != 0) { + EXPECT_EQ(0u, string_base->Length % kCodeUnitSize); + return nullptr; + } + + // |Length| does not include space for the required NUL terminator. + MINIDUMP_LOCATION_DESCRIPTOR location; + location.DataSize = + sizeof(*string_base) + string_base->Length + kCodeUnitSize; + location.Rva = rva; + const T* string = + MinidumpWritableAtLocationDescriptor<T>(file_contents, location); + if (!string) { + return nullptr; + } + + EXPECT_EQ(string_base, string); + + // Require the NUL terminator to be NUL. + if (string->Buffer[string->Length / kCodeUnitSize] != '\0') { + EXPECT_EQ('\0', string->Buffer[string->Length / kCodeUnitSize]); + return nullptr; + } + + return string; +} + +template <typename StringType, typename MinidumpStringType> +StringType TMinidumpStringAtRVAAsString(const std::string& file_contents, + RVA rva) { + const MinidumpStringType* minidump_string = + TMinidumpStringAtRVA<MinidumpStringType>(file_contents, rva); + if (!minidump_string) { + return StringType(); + } + + StringType minidump_string_data( + reinterpret_cast<const typename StringType::value_type*>( + &minidump_string->Buffer[0]), + minidump_string->Length / sizeof(minidump_string->Buffer[0])); + return minidump_string_data; +} + +} // namespace + +const MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVA<MINIDUMP_STRING>(file_contents, rva); +} + +const MinidumpUTF8String* MinidumpUTF8StringAtRVA( + const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVA<MinidumpUTF8String>(file_contents, rva); +} + +base::string16 MinidumpStringAtRVAAsString(const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVAAsString<base::string16, MINIDUMP_STRING>( + file_contents, rva); +} + +std::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVAAsString<std::string, MinidumpUTF8String>( + file_contents, rva); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_string_writer_test_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_string_writer_test_util.h new file mode 100644 index 0000000..933ef33 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_string_writer_test_util.h
@@ -0,0 +1,104 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include <string> + +#include "base/strings/string16.h" + +namespace crashpad { + +struct MinidumpUTF8String; + +namespace test { + +//! \brief Returns a MINIDUMP_STRING located within a minidump file’s contents. +//! +//! If \a rva points outside of the range of \a file_contents, if the string has +//! an incorrect length or is not `NUL`-terminated, or if any of the string data +//! would lie outside of the range of \a file_contents, this function will fail. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MINIDUMP_STRING. +//! +//! \return On success, a pointer to the MINIDUMP_STRING in \a file_contents. On +//! failure, raises a gtest assertion and returns `nullptr`. +//! +//! \sa MinidumpStringAtRVAAsString() +//! \sa MinidumpUTF8StringAtRVA() +const MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents, + RVA rva); + +//! \brief Returns a MinidumpUTF8String located within a minidump file’s +//! contents. +//! +//! If \a rva points outside of the range of \a file_contents, if the string has +//! an incorrect length or is not `NUL`-terminated, or if any of the string data +//! would lie outside of the range of \a file_contents, this function will fail. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MinidumpUTF8String. +//! +//! \return On success, a pointer to the MinidumpUTF8String in \a file_contents. +//! On failure, raises a gtest assertion and returns `nullptr`. +//! +//! \sa MinidumpUTF8StringAtRVAAsString() +//! \sa MinidumpStringAtRVA() +const MinidumpUTF8String* MinidumpUTF8StringAtRVA( + const std::string& file_contents, + RVA rva); + +//! \brief Returns the contents of a MINIDUMP_STRING as a `string16`. +//! +//! This function uses MinidumpStringAtRVA() to obtain a MINIDUMP_STRING, and +//! returns the string data as a `string16`. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MINIDUMP_STRING. +//! +//! \return On success, the string read from \a file_writer at offset \a rva. On +//! failure, raises a gtest assertion and returns an empty string. +//! +//! \sa MinidumpUTF8StringAtRVAAsString() +base::string16 MinidumpStringAtRVAAsString(const std::string& file_contents, + RVA rva); + +//! \brief Returns the contents of a MinidumpUTF8String as a `std::string`. +//! +//! This function uses MinidumpUTF8StringAtRVA() to obtain a MinidumpUTF8String, +//! and returns the string data as a `std::string`. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MinidumpUTF8String. +//! +//! \return On success, the string read from \a file_writer at offset \a rva. On +//! failure, raises a gtest assertion and returns an empty string. +//! +//! \sa MinidumpStringAtRVAAsString() +std::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents, + RVA rva); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_writable_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_writable_test_util.cc new file mode 100644 index 0000000..c8ea4f44 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_writable_test_util.cc
@@ -0,0 +1,358 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "minidump/test/minidump_writable_test_util.h" + +#include <string> + +#include "base/strings/string16.h" +#include "gtest/gtest.h" +#include "util/file/file_writer.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { + +namespace { + +//! \brief Returns an untyped minidump object located within a minidump file’s +//! contents, where the offset of the object is known. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired object. +//! +//! \return If \a rva is within the range of \a file_contents, returns a pointer +//! into \a file_contents at offset \a rva. Otherwise, raises a gtest +//! assertion failure and returns `nullptr`. +//! +//! Do not call this function. Use the typed version, MinidumpWritableAtRVA<>(), +//! or another type-specific function. +const void* MinidumpWritableAtRVAInternal(const std::string& file_contents, + RVA rva) { + if (rva >= file_contents.size()) { + EXPECT_LT(rva, file_contents.size()); + return nullptr; + } + + return &file_contents[rva]; +} + +} // namespace + +const void* MinidumpWritableAtLocationDescriptorInternal( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + size_t expected_size, + bool allow_oversized_data) { + if (location.DataSize == 0) { + EXPECT_EQ(0u, location.Rva); + return nullptr; + } + + if (allow_oversized_data) { + if (location.DataSize < expected_size) { + EXPECT_GE(location.DataSize, expected_size); + return nullptr; + } + } else if (location.DataSize != expected_size) { + EXPECT_EQ(expected_size, location.DataSize); + return nullptr; + } + + RVA end = location.Rva + location.DataSize; + if (end > file_contents.size()) { + EXPECT_LE(end, file_contents.size()); + return nullptr; + } + + const void* rv = MinidumpWritableAtRVAInternal(file_contents, location.Rva); + + return rv; +} + +template <> +const IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const IMAGE_DEBUG_MISC* misc = + TMinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents, + location); + if (!misc) { + return nullptr; + } + + if (misc->DataType != IMAGE_DEBUG_MISC_EXENAME) { + EXPECT_EQ(implicit_cast<uint32_t>(IMAGE_DEBUG_MISC_EXENAME), + misc->DataType); + return nullptr; + } + + if (misc->Length != location.DataSize) { + EXPECT_EQ(location.DataSize, misc->Length); + return nullptr; + } + + if (misc->Unicode == 0) { + size_t string_length = misc->Length - offsetof(IMAGE_DEBUG_MISC, Data) - 1; + if (misc->Data[string_length] != '\0') { + EXPECT_EQ('\0', misc->Data[string_length]); + return nullptr; + } + } else if (misc->Unicode == 1) { + if (misc->Length % sizeof(base::char16) != 0) { + EXPECT_EQ(0u, misc->Length % sizeof(base::char16)); + return nullptr; + } + + size_t string_length = (misc->Length - offsetof(IMAGE_DEBUG_MISC, Data)) / + sizeof(base::char16) - + 1; + const base::char16* data16 = + reinterpret_cast<const base::char16*>(misc->Data); + if (data16[string_length] != '\0') { + EXPECT_EQ('\0', data16[string_length]); + return nullptr; + } + } else { + ADD_FAILURE() << "misc->Unicode " << misc->Unicode; + return nullptr; + } + + return misc; +} + +template <> +const MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const MINIDUMP_HEADER* header = + TMinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>(file_contents, + location); + if (!header) { + return nullptr; + } + + if (header->Signature != MINIDUMP_SIGNATURE) { + EXPECT_EQ(implicit_cast<uint32_t>(MINIDUMP_SIGNATURE), header->Signature); + return nullptr; + } + if (header->Version != MINIDUMP_VERSION) { + EXPECT_EQ(implicit_cast<uint32_t>(MINIDUMP_VERSION), header->Version); + return nullptr; + } + + return header; +} + +namespace { + +struct MinidumpMemoryListTraits { + using ListType = MINIDUMP_MEMORY_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_DESCRIPTOR) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfMemoryRanges; + } +}; + +struct MinidumpModuleListTraits { + using ListType = MINIDUMP_MODULE_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_MODULE) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfModules; + } +}; + +struct MinidumpThreadListTraits { + using ListType = MINIDUMP_THREAD_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfThreads; + } +}; + +struct MinidumpHandleDataStreamTraits { + using ListType = MINIDUMP_HANDLE_DATA_STREAM; + enum : size_t { kElementSize = sizeof(MINIDUMP_HANDLE_DESCRIPTOR) }; + static size_t ElementCount(const ListType* list) { + return static_cast<size_t>(list->NumberOfDescriptors); + } +}; + +struct MinidumpMemoryInfoListTraits { + using ListType = MINIDUMP_MEMORY_INFO_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) }; + static size_t ElementCount(const ListType* list) { + return static_cast<size_t>(list->NumberOfEntries); + } +}; + +struct MinidumpModuleCrashpadInfoListTraits { + using ListType = MinidumpModuleCrashpadInfoList; + enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) }; + static size_t ElementCount(const ListType* list) { + return list->count; + } +}; + +struct MinidumpSimpleStringDictionaryListTraits { + using ListType = MinidumpSimpleStringDictionary; + enum : size_t { kElementSize = sizeof(MinidumpSimpleStringDictionaryEntry) }; + static size_t ElementCount(const ListType* list) { + return list->count; + } +}; + +template <typename T> +const typename T::ListType* MinidumpListAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const typename T::ListType* list = + TMinidumpWritableAtLocationDescriptor<typename T::ListType>(file_contents, + location); + if (!list) { + return nullptr; + } + + size_t expected_size = + sizeof(typename T::ListType) + T::ElementCount(list) * T::kElementSize; + if (location.DataSize != expected_size) { + EXPECT_EQ(expected_size, location.DataSize); + return nullptr; + } + + return list; +} + +} // namespace + +template <> +const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_MEMORY_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor<MinidumpMemoryListTraits>( + file_contents, location); +} + +template <> +const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_MODULE_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor<MinidumpModuleListTraits>( + file_contents, location); +} + +template <> +const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_THREAD_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor<MinidumpThreadListTraits>( + file_contents, location); +} + +template <> +const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor< + MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>( + file_contents, location); +} + +template <> +const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor<MinidumpMemoryInfoListTraits>( + file_contents, location); +} + +template <> +const MinidumpModuleCrashpadInfoList* +MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor<MinidumpModuleCrashpadInfoListTraits>( + file_contents, location); +} + +template <> +const MinidumpSimpleStringDictionary* +MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor< + MinidumpSimpleStringDictionaryListTraits>(file_contents, location); +} + +namespace { + +template <typename T> +const T* MinidumpCVPDBAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const T* cv_pdb = + TMinidumpWritableAtLocationDescriptor<T>(file_contents, location); + if (!cv_pdb) { + return nullptr; + } + + if (cv_pdb->signature != T::kSignature) { + EXPECT_EQ(T::kSignature, cv_pdb->signature); + return nullptr; + } + + size_t string_length = location.DataSize - offsetof(T, pdb_name) - 1; + if (cv_pdb->pdb_name[string_length] != '\0') { + EXPECT_EQ('\0', cv_pdb->pdb_name[string_length]); + return nullptr; + } + + return cv_pdb; +} + +} // namespace + +template <> +const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB20>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents, + location); +} + +template <> +const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB70>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents, + location); +} + +TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value) + : MinidumpWritable(), + value_(value) { +} + +TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() { +} + +size_t TestUInt32MinidumpWritable::SizeOfObject() { + return sizeof(value_); +} + +bool TestUInt32MinidumpWritable::WriteObject(FileWriterInterface* file_writer) { + return file_writer->Write(&value_, sizeof(value_)); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_writable_test_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_writable_test_util.h new file mode 100644 index 0000000..f619028 --- /dev/null +++ b/third_party/crashpad/crashpad/minidump/test/minidump_writable_test_util.h
@@ -0,0 +1,268 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <sys/types.h> + +#include <string> + +#include "base/basictypes.h" +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class FileWriterInterface; + +namespace test { + +//! \brief Returns an untyped minidump object located within a minidump file’s +//! contents, where the offset and size of the object are known. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within +//! the minidump file of the desired object, as well as its size. +//! \param[in] expected_size The expected size of the object. If \a +//! allow_oversized_data is `true`, \a expected_size is treated as the +//! minimum size of \a location, but it is permitted to be larger. If \a +//! allow_oversized_data is `false`, the size of \a location must match +//! \a expected_size exactly. +//! \param[in] allow_oversized_data Controls whether \a expected_size is a +//! minimum limit (`true`) or an exact match is required (`false`). +//! +//! \return If the size of \a location is agrees with \a expected_size, and if +//! \a location is within the range of \a file_contents, returns a pointer +//! into \a file_contents at offset \a rva. Otherwise, raises a gtest +//! assertion failure and returns `nullptr`. +//! +//! Do not call this function. Use the typed version, +//! MinidumpWritableAtLocationDescriptor<>(), or another type-specific function. +const void* MinidumpWritableAtLocationDescriptorInternal( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + size_t expected_size, + bool allow_oversized_data); + +//! \brief A traits class defining whether a minidump object type is required to +//! appear only as a fixed-size object or if it is variable-sized. +//! +//! Variable-sized data is data referenced by a MINIDUMP_LOCATION_DESCRIPTOR +//! whose DataSize field may be larger than the size of the basic object type’s +//! structure. This can happen for types that appear only as variable-sized +//! lists, or types whose final fields are variable-sized lists or other +//! variable-sized data. +template <typename T> +struct MinidumpWritableTraits { + //! \brief `true` if \a T should be treated as a variable-sized data type, + //! where its base size is used solely as a minimum bound. `false` if \a + //! T is a fixed-sized type, which should only appear at its base size. + static const bool kAllowOversizedData = false; +}; + +#define MINIDUMP_ALLOW_OVERSIZED_DATA(x) \ + template <> \ + struct MinidumpWritableTraits<x> { \ + static const bool kAllowOversizedData = true; \ + } + +// This type appears only as a variable-sized list. +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY); + +// These types are permitted to be oversized because their final fields are +// variable-sized lists. +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary); + +// These types have final fields carrying variable-sized data (typically string +// data). +MINIDUMP_ALLOW_OVERSIZED_DATA(IMAGE_DEBUG_MISC); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String); + +// minidump_file_writer_test accesses its variable-sized test streams via a +// uint8_t*. +MINIDUMP_ALLOW_OVERSIZED_DATA(uint8_t); + +#undef MINIDUMP_ALLOW_OVERSIZED_DATA + +//! \brief Returns a typed minidump object located within a minidump file’s +//! contents, where the offset and size of the object are known. +//! +//! This function is similar to MinidumpWritableAtLocationDescriptor<>() and is +//! used to implement that function. It exists independently so that template +//! specializations are able to call this function, which provides the default +//! implementation. +//! +//! Do not call this function directly. Use +//! MinidumpWritableAtLocationDescriptor<>() instead. +template <typename T> +const T* TMinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return reinterpret_cast<const T*>( + MinidumpWritableAtLocationDescriptorInternal( + file_contents, + location, + sizeof(T), + MinidumpWritableTraits<T>::kAllowOversizedData)); +} + +//! \brief Returns a typed minidump object located within a minidump file’s +//! contents, where the offset and size of the object are known. +//! +//! This function has template specializations that perform more stringent +//! checking than the default implementation: +//! - With a MINIDUMP_HEADER template parameter, a template specialization +//! ensures that the structure’s magic number and version fields are correct. +//! - With a MINIDUMP_MEMORY_LIST, MINIDUMP_THREAD_LIST, MINIDUMP_MODULE_LIST, +//! MINIDUMP_MEMORY_INFO_LIST, or MinidumpSimpleStringDictionary template +//! parameter, template specializations ensure that the size given by \a +//! location matches the size expected of a stream containing the number of +//! elements it claims to have. +//! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70 +//! template parameter, template specializations ensure that the structure +//! has the expected format including any magic number and the `NUL`- +//! terminated string. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within +//! the minidump file of the desired object, as well as its size. +//! +//! \return If the size of \a location is at least as big as the size of the +//! requested object, and if \a location is within the range of \a +//! file_contents, returns a pointer into \a file_contents at offset \a rva. +//! Otherwise, raises a gtest assertion failure and returns `nullptr`. +//! +//! \sa MinidumpWritableAtRVA() +template <typename T> +const T* MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return TMinidumpWritableAtLocationDescriptor<T>(file_contents, location); +} + +template <> +const IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_MEMORY_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_MODULE_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_THREAD_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor< + MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor< + MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB20>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor< + CodeViewRecordPDB70>(const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MinidumpModuleCrashpadInfoList* +MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MinidumpSimpleStringDictionary* +MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +//! \brief Returns a typed minidump object located within a minidump file’s +//! contents, where the offset of the object is known. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired object. +//! +//! \return If \a rva plus the size of an object of type \a T is within the +//! range of \a file_contents, returns a pointer into \a file_contents at +//! offset \a rva. Otherwise, raises a gtest assertion failure and returns +//! `nullptr`. +//! +//! \sa MinidumpWritableAtLocationDescriptor<>() +template <typename T> +const T* MinidumpWritableAtRVA(const std::string& file_contents, RVA rva) { + MINIDUMP_LOCATION_DESCRIPTOR location; + location.DataSize = sizeof(T); + location.Rva = rva; + return MinidumpWritableAtLocationDescriptor<T>(file_contents, location); +} + +//! \brief An internal::MinidumpWritable that carries a `uint32_t` for testing. +class TestUInt32MinidumpWritable final : public internal::MinidumpWritable { + public: + //! \brief Constructs the object to write a `uint32_t` with value \a value. + explicit TestUInt32MinidumpWritable(uint32_t value); + + ~TestUInt32MinidumpWritable() override; + + protected: + // MinidumpWritable: + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + uint32_t value_; + + DISALLOW_COPY_AND_ASSIGN(TestUInt32MinidumpWritable); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/package.h b/third_party/crashpad/crashpad/package.h new file mode 100644 index 0000000..a18d7b8 --- /dev/null +++ b/third_party/crashpad/crashpad/package.h
@@ -0,0 +1,29 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_PACKAGE_H_ +#define CRASHPAD_PACKAGE_H_ + +#define PACKAGE_BUGREPORT "https://crashpad.chromium.org/bug/new" +#define PACKAGE_COPYRIGHT \ + "Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER +#define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors" +#define PACKAGE_COPYRIGHT_YEAR "2015" +#define PACKAGE_NAME "Crashpad" +#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION +#define PACKAGE_TARNAME "crashpad" +#define PACKAGE_VERSION "0.7.0" +#define PACKAGE_URL "https://crashpad.chromium.org/" + +#endif // CRASHPAD_PACKAGE_H_
diff --git a/third_party/crashpad/crashpad/snapshot/cpu_architecture.h b/third_party/crashpad/crashpad/snapshot/cpu_architecture.h new file mode 100644 index 0000000..208e98f --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/cpu_architecture.h
@@ -0,0 +1,39 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_ +#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_ + +namespace crashpad { + +//! \brief A system’s CPU architecture. +//! +//! This can be used to represent the CPU architecture of an entire system +//! as in SystemSnapshot::CPUArchitecture(). It can also be used to represent +//! the architecture of a CPUContext structure in its CPUContext::architecture +//! field without reference to external data. +enum CPUArchitecture { + //! \brief The CPU architecture is unknown. + kCPUArchitectureUnknown = 0, + + //! \brief 32-bit x86. + kCPUArchitectureX86, + + //! \brief x86_64. + kCPUArchitectureX86_64, +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_
diff --git a/third_party/crashpad/crashpad/snapshot/cpu_context.cc b/third_party/crashpad/crashpad/snapshot/cpu_context.cc new file mode 100644 index 0000000..1ff2b459 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/cpu_context.cc
@@ -0,0 +1,101 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/cpu_context.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +// static +uint16_t CPUContextX86::FxsaveToFsaveTagWord( + uint16_t fsw, + uint8_t fxsave_tag, + const CPUContextX86::X87OrMMXRegister st_mm[8]) { + enum { + kX87TagValid = 0, + kX87TagZero, + kX87TagSpecial, + kX87TagEmpty, + }; + + // The x87 tag word (in both abridged and full form) identifies physical + // registers, but |st_mm| is arranged in logical stack order. In order to map + // physical tag word bits to the logical stack registers they correspond to, + // the “stack top” value from the x87 status word is necessary. + int stack_top = (fsw >> 11) & 0x7; + + uint16_t fsave_tag = 0; + for (int physical_index = 0; physical_index < 8; ++physical_index) { + bool fxsave_bit = (fxsave_tag & (1 << physical_index)) != 0; + uint8_t fsave_bits; + + if (fxsave_bit) { + int st_index = (physical_index + 8 - stack_top) % 8; + const CPUContextX86::X87Register& st = st_mm[st_index].st; + + uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8]; + if (exponent == 0x7fff) { + // Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to + // distinguish between these, the J bit and the M bit (the most + // significant bit of |fraction|) could be consulted. + fsave_bits = kX87TagSpecial; + } else { + // The integer bit the “J bit”. + bool integer_bit = (st[7] & 0x80) != 0; + if (exponent == 0) { + uint64_t fraction = ((implicit_cast<uint64_t>(st[7]) & 0x7f) << 56) | + (implicit_cast<uint64_t>(st[6]) << 48) | + (implicit_cast<uint64_t>(st[5]) << 40) | + (implicit_cast<uint64_t>(st[4]) << 32) | + (implicit_cast<uint32_t>(st[3]) << 24) | + (st[2] << 16) | (st[1] << 8) | st[0]; + if (!integer_bit && fraction == 0) { + fsave_bits = kX87TagZero; + } else { + // Denormal (if the J bit is clear) or pseudo-denormal. + fsave_bits = kX87TagSpecial; + } + } else if (integer_bit) { + fsave_bits = kX87TagValid; + } else { + // Unnormal. + fsave_bits = kX87TagSpecial; + } + } + } else { + fsave_bits = kX87TagEmpty; + } + + fsave_tag |= (fsave_bits << (physical_index * 2)); + } + + return fsave_tag; +} + +uint64_t CPUContext::InstructionPointer() const { + switch (architecture) { + case kCPUArchitectureX86: + return x86->eip; + case kCPUArchitectureX86_64: + return x86_64->rip; + default: + NOTREACHED(); + return ~0ull; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/cpu_context.h b/third_party/crashpad/crashpad/snapshot/cpu_context.h new file mode 100644 index 0000000..bfef4d09 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/cpu_context.h
@@ -0,0 +1,219 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_ + +#include <stdint.h> + +#include "snapshot/cpu_architecture.h" + +namespace crashpad { + +//! \brief A context structure carrying 32-bit x86 CPU state. +struct CPUContextX86 { + using X87Register = uint8_t[10]; + + union X87OrMMXRegister { + struct { + X87Register st; + uint8_t st_reserved[6]; + }; + struct { + uint8_t mm_value[8]; + uint8_t mm_reserved[8]; + }; + }; + + using XMMRegister = uint8_t[16]; + + struct Fxsave { + uint16_t fcw; // FPU control word + uint16_t fsw; // FPU status word + uint8_t ftw; // abridged FPU tag word + uint8_t reserved_1; + uint16_t fop; // FPU opcode + uint32_t fpu_ip; // FPU instruction pointer offset + uint16_t fpu_cs; // FPU instruction pointer segment selector + uint16_t reserved_2; + uint32_t fpu_dp; // FPU data pointer offset + uint16_t fpu_ds; // FPU data pointer segment selector + uint16_t reserved_3; + uint32_t mxcsr; // multimedia extensions status and control register + uint32_t mxcsr_mask; // valid bits in mxcsr + X87OrMMXRegister st_mm[8]; + XMMRegister xmm[8]; + uint8_t reserved_4[176]; + uint8_t available[48]; + }; + + //! \brief Converts x87 floating-point tag words from `fxsave` (abridged, + //! 8-bit) to `fsave` (full, 16-bit) form. + //! + //! `fxsave` stores the x87 floating-point tag word in abridged 8-bit form, + //! and `fsave` stores it in full 16-bit form. Some users, notably + //! MinidumpContextX86::float_save::tag_word, require the full 16-bit form, + //! where most other contemporary code uses `fxsave` and thus the abridged + //! 8-bit form found in CPUContextX86::Fxsave::ftw. + //! + //! This function converts an abridged tag word to the full version by using + //! the abridged tag word and the contents of the registers it describes. See + //! Intel Software Developer’s Manual, Volume 2A: Instruction Set Reference + //! A-M (253666-052), 3.2 “FXSAVE”, specifically, the notes on the abridged + //! FTW and recreating the FSAVE format, and AMD Architecture Programmer’s + //! Manual, Volume 2: System Programming (24593-3.24), “FXSAVE Format for x87 + //! Tag Word”. + //! + //! \param[in] fsw The FPU status word, used to map logical \a st_mm registers + //! to their physical counterparts. This can be taken from + //! CPUContextX86::Fxsave::fsw. + //! \param[in] fxsave_tag The abridged FPU tag word. This can be taken from + //! CPUContextX86::Fxsave::ftw. + //! \param[in] st_mm The floating-point registers in logical order. This can + //! be taken from CPUContextX86::Fxsave::st_mm. + //! + //! \return The full FPU tag word. + static uint16_t FxsaveToFsaveTagWord( + uint16_t fsw, uint8_t fxsave_tag, const X87OrMMXRegister st_mm[8]); + + // Integer registers. + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; // destination index + uint32_t esi; // source index + uint32_t ebp; // base pointer + uint32_t esp; // stack pointer + uint32_t eip; // instruction pointer + uint32_t eflags; + uint16_t cs; // code segment selector + uint16_t ds; // data segment selector + uint16_t es; // extra segment selector + uint16_t fs; + uint16_t gs; + uint16_t ss; // stack segment selector + + // Floating-point and vector registers. + Fxsave fxsave; + + // Debug registers. + uint32_t dr0; + uint32_t dr1; + uint32_t dr2; + uint32_t dr3; + uint32_t dr4; // obsolete, normally an alias for dr6 + uint32_t dr5; // obsolete, normally an alias for dr7 + uint32_t dr6; + uint32_t dr7; +}; + +//! \brief A context structure carrying x86_64 CPU state. +struct CPUContextX86_64 { + using X87Register = CPUContextX86::X87Register; + using X87OrMMXRegister = CPUContextX86::X87OrMMXRegister; + using XMMRegister = CPUContextX86::XMMRegister; + + struct Fxsave { + uint16_t fcw; // FPU control word + uint16_t fsw; // FPU status word + uint8_t ftw; // abridged FPU tag word + uint8_t reserved_1; + uint16_t fop; // FPU opcode + union { + // The expression of these union members is determined by the use of + // fxsave/fxrstor or fxsave64/fxrstor64 (fxsaveq/fxrstorq). Mac OS X and + // Windows systems use the traditional fxsave/fxrstor structure. + struct { + // fxsave/fxrstor + uint32_t fpu_ip; // FPU instruction pointer offset + uint16_t fpu_cs; // FPU instruction pointer segment selector + uint16_t reserved_2; + uint32_t fpu_dp; // FPU data pointer offset + uint16_t fpu_ds; // FPU data pointer segment selector + uint16_t reserved_3; + }; + struct { + // fxsave64/fxrstor64 (fxsaveq/fxrstorq) + uint64_t fpu_ip_64; // FPU instruction pointer + uint64_t fpu_dp_64; // FPU data pointer + }; + }; + uint32_t mxcsr; // multimedia extensions status and control register + uint32_t mxcsr_mask; // valid bits in mxcsr + X87OrMMXRegister st_mm[8]; + XMMRegister xmm[16]; + uint8_t reserved_4[48]; + uint8_t available[48]; + }; + + // Integer registers. + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; // destination index + uint64_t rsi; // source index + uint64_t rbp; // base pointer + uint64_t rsp; // stack pointer + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; // instruction pointer + uint64_t rflags; + uint16_t cs; // code segment selector + uint16_t fs; + uint16_t gs; + + // Floating-point and vector registers. + Fxsave fxsave; + + // Debug registers. + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr4; // obsolete, normally an alias for dr6 + uint64_t dr5; // obsolete, normally an alias for dr7 + uint64_t dr6; + uint64_t dr7; +}; + +//! \brief A context structure capable of carrying the context of any supported +//! CPU architecture. +struct CPUContext { + //! \brief Returns the instruction pointer value from the context structure. + //! + //! This is a CPU architecture-independent method that is capable of + //! recovering the instruction pointer from any supported CPU architecture’s + //! context structure. + uint64_t InstructionPointer() const; + + //! \brief The CPU architecture of a context structure. This field controls + //! the expression of the union. + CPUArchitecture architecture; + union { + CPUContextX86* x86; + CPUContextX86_64* x86_64; + }; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc b/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc new file mode 100644 index 0000000..a5f0b6aa --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc
@@ -0,0 +1,166 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/cpu_context.h" + +#include <stdint.h> +#include <string.h> + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +enum ExponentValue { + kExponentAllZero = 0, + kExponentAllOne, + kExponentNormal, +}; + +enum FractionValue { + kFractionAllZero = 0, + kFractionNormal, +}; + +//! \brief Initializes an x87 register to a known bit pattern. +//! +//! \param[out] st_mm The x87 register to initialize. The reserved portion of +//! the register is always zeroed out. +//! \param[in] exponent_value The bit pattern to use for the exponent. If this +//! is kExponentAllZero, the sign bit will be set to `1`, and if this is +//! kExponentAllOne, the sign bit will be set to `0`. This tests that the +//! implementation doesn’t erroneously consider the sign bit to be part of +//! the exponent. This may also be kExponentNormal, indicating that the +//! exponent shall neither be all zeroes nor all ones. +//! \param[in] j_bit The value to use for the “J bit” (“integer bit”). +//! \param[in] fraction_value If kFractionAllZero, the fraction will be zeroed +//! out. If kFractionNormal, the fraction will not be all zeroes. +void SetX87Register(CPUContextX86::X87OrMMXRegister* st_mm, + ExponentValue exponent_value, + bool j_bit, + FractionValue fraction_value) { + switch (exponent_value) { + case kExponentAllZero: + st_mm->st[9] = 0x80; + st_mm->st[8] = 0; + break; + case kExponentAllOne: + st_mm->st[9] = 0x7f; + st_mm->st[8] = 0xff; + break; + case kExponentNormal: + st_mm->st[9] = 0x55; + st_mm->st[8] = 0x55; + break; + } + + uint8_t fraction_pattern = fraction_value == kFractionAllZero ? 0 : 0x55; + memset(&st_mm->st[0], fraction_pattern, 8); + + if (j_bit) { + st_mm->st[7] |= 0x80; + } else { + st_mm->st[7] &= ~0x80; + } + + memset(st_mm->st_reserved, 0, sizeof(st_mm->st_reserved)); +} + +TEST(CPUContextX86, FxsaveToFsaveTagWord) { + // The fsave tag word uses bit pattern 00 for valid, 01 for zero, 10 for + // “special”, and 11 for empty. Like the fxsave tag word, it is arranged by + // physical register. The fxsave tag word determines whether a register is + // empty, and analysis of the x87 register content distinguishes between + // valid, zero, and special. In the initializations below, comments show + // whether a register is expected to be considered valid, zero, or special, + // except where the tag word is expected to indicate that it is empty. Each + // combination appears twice: once where the fxsave tag word indicates a + // nonempty register, and once again where it indicates an empty register. + + uint16_t fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7 + uint8_t fxsave_tag = 0x0f; // physical 4-7 (logical 4-7) empty + CPUContextX86::X87OrMMXRegister st_mm[8]; + SetX87Register(&st_mm[0], kExponentNormal, false, kFractionNormal); // spec. + SetX87Register(&st_mm[1], kExponentNormal, true, kFractionNormal); // valid + SetX87Register(&st_mm[2], kExponentNormal, false, kFractionAllZero); // spec. + SetX87Register(&st_mm[3], kExponentNormal, true, kFractionAllZero); // valid + SetX87Register(&st_mm[4], kExponentNormal, false, kFractionNormal); + SetX87Register(&st_mm[5], kExponentNormal, true, kFractionNormal); + SetX87Register(&st_mm[6], kExponentNormal, false, kFractionAllZero); + SetX87Register(&st_mm[7], kExponentNormal, true, kFractionAllZero); + EXPECT_EQ(0xff22, + CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); + + fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1 + fxsave_tag = 0xf0; // physical 0-3 (logical 6-7, 0-1) empty + SetX87Register(&st_mm[0], kExponentAllZero, false, kFractionNormal); + SetX87Register(&st_mm[1], kExponentAllZero, true, kFractionNormal); + SetX87Register(&st_mm[2], kExponentAllZero, false, kFractionAllZero); // zero + SetX87Register(&st_mm[3], kExponentAllZero, true, kFractionAllZero); // spec. + SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionNormal); // spec. + SetX87Register(&st_mm[5], kExponentAllZero, true, kFractionNormal); // spec. + SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero); + SetX87Register(&st_mm[7], kExponentAllZero, true, kFractionAllZero); + EXPECT_EQ(0xa9ff, + CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); + + fsw = 5 << 11; // top = 5: logical 0-7 maps to physical 5-7, 0-4 + fxsave_tag = 0x5a; // physical 0, 2, 5, and 7 (logical 5, 0, 2, and 3) empty + SetX87Register(&st_mm[0], kExponentAllOne, false, kFractionNormal); + SetX87Register(&st_mm[1], kExponentAllOne, true, kFractionNormal); // spec. + SetX87Register(&st_mm[2], kExponentAllOne, false, kFractionAllZero); + SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionAllZero); + SetX87Register(&st_mm[4], kExponentAllOne, false, kFractionNormal); // spec. + SetX87Register(&st_mm[5], kExponentAllOne, true, kFractionNormal); + SetX87Register(&st_mm[6], kExponentAllOne, false, kFractionAllZero); // spec. + SetX87Register(&st_mm[7], kExponentAllOne, true, kFractionAllZero); // spec. + EXPECT_EQ(0xeebb, + CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); + + // This set set is just a mix of all of the possible tag types in a single + // register file. + fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0 + fxsave_tag = 0x1f; // physical 5-7 (logical 4-6) empty + SetX87Register(&st_mm[0], kExponentNormal, true, kFractionAllZero); // valid + SetX87Register(&st_mm[1], kExponentAllZero, false, kFractionAllZero); // zero + SetX87Register(&st_mm[2], kExponentAllOne, true, kFractionAllZero); // spec. + SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionNormal); // spec. + SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionAllZero); + SetX87Register(&st_mm[5], kExponentAllZero, false, kFractionAllZero); + SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero); + SetX87Register(&st_mm[7], kExponentNormal, true, kFractionNormal); // valid + EXPECT_EQ(0xfe90, + CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); + + // In this set, everything is valid. + fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7 + fxsave_tag = 0xff; // nothing empty + for (size_t index = 0; index < arraysize(st_mm); ++index) { + SetX87Register(&st_mm[index], kExponentNormal, true, kFractionAllZero); + } + EXPECT_EQ(0, CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); + + // In this set, everything is empty. The registers shouldn’t be consulted at + // all, so they’re left alone from the previous set. + fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7 + fxsave_tag = 0; // everything empty + EXPECT_EQ(0xffff, + CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc new file mode 100644 index 0000000..e19b021 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc
@@ -0,0 +1,44 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/crashpad_info_client_options.h" + +#include "base/logging.h" +#include "client/crashpad_info.h" + +namespace crashpad { + +// static +TriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + uint8_t crashpad_info_tri_state) { + switch (crashpad_info_tri_state) { + case static_cast<uint8_t>(TriState::kUnset): + return TriState::kUnset; + case static_cast<uint8_t>(TriState::kEnabled): + return TriState::kEnabled; + case static_cast<uint8_t>(TriState::kDisabled): + return TriState::kDisabled; + default: + LOG(WARNING) << "unknown TriState " + << static_cast<int>(crashpad_info_tri_state); + return TriState::kUnset; + } +} + +CrashpadInfoClientOptions::CrashpadInfoClientOptions() + : crashpad_handler_behavior(TriState::kUnset), + system_crash_reporter_forwarding(TriState::kUnset) { +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h new file mode 100644 index 0000000..2d6c35e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h
@@ -0,0 +1,64 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_ + +#include "util/misc/tri_state.h" + +namespace crashpad { + +//! \brief Options represented in a client’s CrashpadInfo structure. +//! +//! The CrashpadInfo structure is not suitable to expose client options +//! in a generic way at the snapshot level. This structure duplicates +//! option-related fields from the client structure for general use within the +//! snapshot layer and by users of this layer. +//! +//! For objects of this type corresponding to a module, option values are taken +//! from the module’s CrashpadInfo structure directly. If the module has no such +//! such structure, option values appear unset. +//! +//! For objects of this type corresponding to an entire process, option values +//! are taken from the CrashpadInfo structures of modules within the process. +//! The first module found with a set value (enabled or disabled) will provide +//! an option value for the process. Different modules may provide values for +//! different options. If no module in the process sets a value for an option, +//! the option will appear unset for the process. If no module in the process +//! has a CrashpadInfo structure, all option values will appear unset. +struct CrashpadInfoClientOptions { + public: + //! \brief Converts `uint8_t` value to a TriState value. + //! + //! The process_types layer exposes TriState as a `uint8_t` rather than an + //! enum type. This function converts these values into the equivalent enum + //! values used in the snapshot layer. + //! + //! \return The TriState equivalent of \a crashpad_info_tri_state, if it is a + //! valid TriState value. Otherwise, logs a warning and returns + //! TriState::kUnset. + static TriState TriStateFromCrashpadInfo(uint8_t crashpad_info_tri_state); + + CrashpadInfoClientOptions(); + + //! \sa CrashpadInfo::set_crashpad_handler_behavior() + TriState crashpad_handler_behavior; + + //! \sa CrashpadInfo::set_system_crash_reporter_forwarding() + TriState system_crash_reporter_forwarding; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc new file mode 100644 index 0000000..d57f1c149 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc
@@ -0,0 +1,258 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/crashpad_info_client_options.h" + +#include "base/files/file_path.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/paths.h" + +#if defined(OS_MACOSX) +#include <dlfcn.h> +#include "snapshot/mac/process_snapshot_mac.h" +#elif defined(OS_WIN) +#include <windows.h> +#include "snapshot/win/process_snapshot_win.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +TEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) { + EXPECT_EQ(TriState::kUnset, + CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0)); + EXPECT_EQ(TriState::kEnabled, + CrashpadInfoClientOptions::TriStateFromCrashpadInfo(1)); + EXPECT_EQ(TriState::kDisabled, + CrashpadInfoClientOptions::TriStateFromCrashpadInfo(2)); + + // These will produce log messages but should result in kUnset being returned. + EXPECT_EQ(TriState::kUnset, + CrashpadInfoClientOptions::TriStateFromCrashpadInfo(3)); + EXPECT_EQ(TriState::kUnset, + CrashpadInfoClientOptions::TriStateFromCrashpadInfo(4)); + EXPECT_EQ(TriState::kUnset, + CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0xff)); +} + +class ScopedUnsetCrashpadInfoOptions { + public: + explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info) + : crashpad_info_(crashpad_info) { + } + + ~ScopedUnsetCrashpadInfoOptions() { + crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); + crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + } + + private: + CrashpadInfo* crashpad_info_; + + DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions); +}; + +TEST(CrashpadInfoClientOptions, OneModule) { + // Make sure that the initial state has all values unset. +#if defined(OS_MACOSX) + ProcessSnapshotMac process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); +#elif defined(OS_WIN) + ProcessSnapshotWin process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize( + GetCurrentProcess(), ProcessSuspensionState::kRunning, 0)); +#else +#error Port. +#endif // OS_MACOSX + + CrashpadInfoClientOptions options; + process_snapshot.GetCrashpadOptions(&options); + + EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + ASSERT_TRUE(crashpad_info); + + { + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + + crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + } + + { + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + + crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); + } +} + +#if defined(OS_POSIX) +using DlHandle = void*; +#elif defined(OS_WIN) +using DlHandle = HMODULE; +#endif // OS_POSIX + +class ScopedDlHandle { + public: + explicit ScopedDlHandle(DlHandle dl_handle) + : dl_handle_(dl_handle) { + } + + ~ScopedDlHandle() { + if (dl_handle_) { +#if defined(OS_POSIX) + if (dlclose(dl_handle_) != 0) { + LOG(ERROR) << "dlclose: " << dlerror(); + } +#elif defined(OS_WIN) + if (!FreeLibrary(dl_handle_)) + PLOG(ERROR) << "FreeLibrary"; +#endif // OS_POSIX + } + } + + bool valid() const { return dl_handle_ != nullptr; } + + template <typename T> + T LookUpSymbol(const char* symbol_name) { +#if defined(OS_POSIX) + return reinterpret_cast<T>(dlsym(dl_handle_, symbol_name)); +#elif defined(OS_WIN) + return reinterpret_cast<T>(GetProcAddress(dl_handle_, symbol_name)); +#endif // OS_POSIX + } + + private: + DlHandle dl_handle_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDlHandle); +}; + +TEST(CrashpadInfoClientOptions, TwoModules) { + // Open the module, which has its own CrashpadInfo structure. +#if defined(OS_MACOSX) + const base::FilePath::StringType kDlExtension = FILE_PATH_LITERAL(".so"); +#elif defined(OS_WIN) + const base::FilePath::StringType kDlExtension = FILE_PATH_LITERAL(".dll"); +#endif + base::FilePath module_path = Paths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_snapshot_test_module") + kDlExtension); +#if defined(OS_MACOSX) + ScopedDlHandle dl_handle( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(dl_handle.valid()) << "dlopen " << module_path.value() << ": " + << dlerror(); +#elif defined(OS_WIN) + ScopedDlHandle dl_handle(LoadLibrary(module_path.value().c_str())); + ASSERT_TRUE(dl_handle.valid()) + << "LoadLibrary " << base::UTF16ToUTF8(module_path.value()) << ": " + << ErrorMessage(); +#else +#error Port. +#endif // OS_MACOSX + + // Get the function pointer from the module. This wraps GetCrashpadInfo(), but + // because it runs in the module, it returns the remote module’s CrashpadInfo + // structure. + CrashpadInfo* (*TestModule_GetCrashpadInfo)() = + dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo"); + ASSERT_TRUE(TestModule_GetCrashpadInfo); + + // Make sure that the initial state has all values unset. +#if defined(OS_MACOSX) + ProcessSnapshotMac process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); +#elif defined(OS_WIN) + ProcessSnapshotWin process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize( + GetCurrentProcess(), ProcessSuspensionState::kRunning, 0)); +#else +#error Port. +#endif // OS_MACOSX + + CrashpadInfoClientOptions options; + process_snapshot.GetCrashpadOptions(&options); + + EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + + // Get both CrashpadInfo structures. + CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo(); + ASSERT_TRUE(local_crashpad_info); + + CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo(); + ASSERT_TRUE(remote_crashpad_info); + + { + ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // When only one module sets a value, it applies to the entire process. + remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + + // When more than one module sets a value, the first one in the module list + // applies to the process. The local module should appear before the remote + // module, because the local module loaded the remote module. + local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + } + + { + ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // When only one module sets a value, it applies to the entire process. + remote_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kDisabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); + + // When more than one module sets a value, the first one in the module list + // applies to the process. The local module should appear before the remote + // module, because the local module loaded the remote module. + local_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kEnabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc new file mode 100644 index 0000000..357d35e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc
@@ -0,0 +1,47 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "build/build_config.h" +#include "client/crashpad_info.h" + +#if defined(OS_POSIX) +#define EXPORT __attribute__((visibility("default"))) +#elif defined(OS_WIN) +#include <windows.h> +#define EXPORT __declspec(dllexport) +#endif // OS_POSIX + +extern "C" { + +// Returns the module’s CrashpadInfo structure. Assuming that this file is built +// into a loadable_module with a distinct static copy of the Crashpad client +// library from the copy built into the loader of this loadable_module, this +// will return a different CrashpadInfo structure than the one that the loader +// uses. Having an extra CrashpadInfo structure makes it possible to test +// behaviors that are relevant in the presence of multiple Crashpad +// client-enabled modules. +// +// This function is used by the CrashpadInfoClientOptions.TwoModules test in +// crashpad_info_client_options_test.cc. +EXPORT crashpad::CrashpadInfo* TestModule_GetCrashpadInfo() { + return crashpad::CrashpadInfo::GetCrashpadInfo(); +} + +} // extern "C" + +#if defined(OS_WIN) +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) { + return TRUE; +} +#endif // OS_WIN
diff --git a/third_party/crashpad/crashpad/snapshot/exception_snapshot.h b/third_party/crashpad/crashpad/snapshot/exception_snapshot.h new file mode 100644 index 0000000..bd8ad8a --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/exception_snapshot.h
@@ -0,0 +1,110 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_ + +#include <stdint.h> + +#include <vector> + +namespace crashpad { + +struct CPUContext; + +//! \brief An abstract interface to a snapshot representing an exception that a +//! snapshot process sustained and triggered the snapshot being taken. +class ExceptionSnapshot { + public: + virtual ~ExceptionSnapshot() {} + + //! \brief Returns a CPUContext object corresponding to the exception thread’s + //! CPU context at the time of the exception. + //! + //! The caller does not take ownership of this object, it is scoped to the + //! lifetime of the ThreadSnapshot object that it was obtained from. + virtual const CPUContext* Context() const = 0; + + //! \brief Returns the thread identifier of the thread that triggered the + //! exception. + //! + //! This value can be compared to ThreadSnapshot::ThreadID() to associate an + //! ExceptionSnapshot object with the ThreadSnapshot that contains a snapshot + //! of the thread that triggered the exception. + virtual uint64_t ThreadID() const = 0; + + //! \brief Returns the top-level exception code identifying the exception. + //! + //! This is an operating system-specific value. + //! + //! For Mac OS X, this will be an \ref EXC_x "EXC_*" exception type, such as + //! `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions + //! processed as `EXC_CRASH` when generated from another preceding exception: + //! the original exception code will appear instead. The exception type as it + //! was received will appear at index 0 of Codes(). + //! + //! For Windows, this will be an \ref EXCEPTION_x "EXCEPTION_*" exception type + //! such as `EXCEPTION_ACCESS_VIOLATION`. + virtual uint32_t Exception() const = 0; + + //! \brief Returns the second-level exception code identifying the exception. + //! + //! This is an operating system-specific value. + //! + //! For Mac OS X, this will be the value of the exception code at index 0 as + //! received by a Mach exception handler, except: + //! * For `EXC_CRASH` exceptions generated from another preceding exception, + //! the original exception code will appear here, not the code as received + //! by the Mach exception handler. + //! * For `EXC_RESOURCE` and `EXC_GUARD` exceptions, the high 32 bits of the + //! exception code at index 0 will appear here. + //! + //! In all cases on Mac OS X, the full exception code at index 0 as it was + //! received will appear at index 1 of Codes(). + //! + //! On Windows, this will either be `0` if the exception is continuable, or + //! `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable exception. + virtual uint32_t ExceptionInfo() const = 0; + + //! \brief Returns the address that triggered the exception. + //! + //! This may be the address that caused a fault on data access, or it may be + //! the instruction pointer that contained an offending instruction. For + //! exceptions where this value cannot be determined, it will be `0`. + //! + //! For Mac OS X, this will be the value of the exception code at index 1 as + //! received by a Mach exception handler. + virtual uint64_t ExceptionAddress() const = 0; + + //! \brief Returns a series of operating system-specific exception codes. + //! + //! The precise interpretation of these codes is specific to the snapshot + //! operating system. These codes may provide a duplicate of information + //! available elsewhere, they may extend information available elsewhere, or + //! they may not be present at all. In this case, an empty vector will be + //! returned. + //! + //! For Mac OS X, this will be a vector containing the original exception type + //! and the values of `code[0]` and `code[1]` as received by a Mach exception + //! handler. + //! + //! For Windows, these are additional arguments (if any) as provided to + //! `RaiseException()`. See the documentation for `ExceptionInformation` in + //! `EXCEPTION_RECORD`. + virtual const std::vector<uint64_t>& Codes() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/handle_snapshot.cc b/third_party/crashpad/crashpad/snapshot/handle_snapshot.cc new file mode 100644 index 0000000..331b6b35 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/handle_snapshot.cc
@@ -0,0 +1,31 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/handle_snapshot.h" + +namespace crashpad { + +HandleSnapshot::HandleSnapshot() + : type_name(), + handle(0), + attributes(0), + granted_access(0), + pointer_count(0), + handle_count(0) { +} + +HandleSnapshot::~HandleSnapshot() { +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/handle_snapshot.h b/third_party/crashpad/crashpad/snapshot/handle_snapshot.h new file mode 100644 index 0000000..1346b41d --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/handle_snapshot.h
@@ -0,0 +1,56 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_ + +#include <stdint.h> + +#include <string> + +namespace crashpad { + +struct HandleSnapshot { + HandleSnapshot(); + ~HandleSnapshot(); + + //! \brief A UTF-8 string representation of the handle's type. + std::string type_name; + + //! \brief The handle's value. + uint32_t handle; + + //! \brief The attributes for the handle, e.g. `OBJ_INHERIT`, + //! `OBJ_CASE_INSENSITIVE`, etc. + uint32_t attributes; + + //! \brief The ACCESS_MASK for the handle in this process. + //! + //! See + //! http://blogs.msdn.com/b/openspecification/archive/2010/04/01/about-the-access-mask-structure.aspx + //! for more information. + uint32_t granted_access; + + //! \brief The number of kernel references to the object that this handle + //! refers to. + uint32_t pointer_count; + + //! \brief The number of open handles to the object that this handle refers + //! to. + uint32_t handle_count; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc new file mode 100644 index 0000000..8dca246 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc
@@ -0,0 +1,440 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/cpu_context_mac.h" + +#include <string.h> + +#include "base/logging.h" + +namespace crashpad { + +#if defined(ARCH_CPU_X86_FAMILY) + +namespace { + +void InitializeCPUContextX86Thread( + CPUContextX86* context, + const x86_thread_state32_t* x86_thread_state32) { + context->eax = x86_thread_state32->__eax; + context->ebx = x86_thread_state32->__ebx; + context->ecx = x86_thread_state32->__ecx; + context->edx = x86_thread_state32->__edx; + context->edi = x86_thread_state32->__edi; + context->esi = x86_thread_state32->__esi; + context->ebp = x86_thread_state32->__ebp; + context->esp = x86_thread_state32->__esp; + context->eip = x86_thread_state32->__eip; + context->eflags = x86_thread_state32->__eflags; + context->cs = x86_thread_state32->__cs; + context->ds = x86_thread_state32->__ds; + context->es = x86_thread_state32->__es; + context->fs = x86_thread_state32->__fs; + context->gs = x86_thread_state32->__gs; + context->ss = x86_thread_state32->__ss; +} + +void InitializeCPUContextX86Float( + CPUContextX86* context, const x86_float_state32_t* x86_float_state32) { + // This relies on both x86_float_state32_t and context->fxsave having + // identical (fxsave) layout. + static_assert(offsetof(x86_float_state32_t, __fpu_reserved1) - + offsetof(x86_float_state32_t, __fpu_fcw) == + sizeof(context->fxsave), + "types must be equivalent"); + + memcpy( + &context->fxsave, &x86_float_state32->__fpu_fcw, sizeof(context->fxsave)); +} + +void InitializeCPUContextX86Debug( + CPUContextX86* context, const x86_debug_state32_t* x86_debug_state32) { + context->dr0 = x86_debug_state32->__dr0; + context->dr1 = x86_debug_state32->__dr1; + context->dr2 = x86_debug_state32->__dr2; + context->dr3 = x86_debug_state32->__dr3; + context->dr4 = x86_debug_state32->__dr4; + context->dr5 = x86_debug_state32->__dr5; + context->dr6 = x86_debug_state32->__dr6; + context->dr7 = x86_debug_state32->__dr7; +} + +// Initializes |context| from the native thread state structure |state|, which +// is interpreted according to |flavor|. |state_count| must be at least the +// expected size for |flavor|. This handles the architecture-specific +// x86_THREAD_STATE32, x86_FLOAT_STATE32, and x86_DEBUG_STATE32 flavors. It also +// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE +// flavors provided that the associated structure carries 32-bit data of the +// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting +// any thread state in |context|. This returns the architecture-specific flavor +// value for the thread state that was actually set, or THREAD_STATE_NONE if no +// thread state was set. +thread_state_flavor_t InitializeCPUContextX86Flavor( + CPUContextX86* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + mach_msg_type_number_t expected_state_count; + switch (flavor) { + case x86_THREAD_STATE: + expected_state_count = x86_THREAD_STATE_COUNT; + break; + case x86_FLOAT_STATE: + expected_state_count = x86_FLOAT_STATE_COUNT; + break; + case x86_DEBUG_STATE: + expected_state_count = x86_DEBUG_STATE_COUNT; + break; + case x86_THREAD_STATE32: + expected_state_count = x86_THREAD_STATE32_COUNT; + break; + case x86_FLOAT_STATE32: + expected_state_count = x86_FLOAT_STATE32_COUNT; + break; + case x86_DEBUG_STATE32: + expected_state_count = x86_DEBUG_STATE32_COUNT; + break; + case THREAD_STATE_NONE: + expected_state_count = 0; + break; + default: + LOG(WARNING) << "unhandled flavor " << flavor; + return THREAD_STATE_NONE; + } + + if (state_count < expected_state_count) { + LOG(WARNING) << "expected state_count " << expected_state_count + << " for flavor " << flavor << ", observed " << state_count; + return THREAD_STATE_NONE; + } + + switch (flavor) { + case x86_THREAD_STATE: { + const x86_thread_state_t* x86_thread_state = + reinterpret_cast<const x86_thread_state_t*>(state); + if (x86_thread_state->tsh.flavor != x86_THREAD_STATE32) { + LOG(WARNING) << "expected flavor x86_THREAD_STATE32, observed " + << x86_thread_state->tsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86Flavor( + context, + x86_thread_state->tsh.flavor, + reinterpret_cast<ConstThreadState>(&x86_thread_state->uts.ts32), + x86_thread_state->tsh.count); + } + + case x86_FLOAT_STATE: { + const x86_float_state_t* x86_float_state = + reinterpret_cast<const x86_float_state_t*>(state); + if (x86_float_state->fsh.flavor != x86_FLOAT_STATE32) { + LOG(WARNING) << "expected flavor x86_FLOAT_STATE32, observed " + << x86_float_state->fsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86Flavor( + context, + x86_float_state->fsh.flavor, + reinterpret_cast<ConstThreadState>(&x86_float_state->ufs.fs32), + x86_float_state->fsh.count); + } + + case x86_DEBUG_STATE: { + const x86_debug_state_t* x86_debug_state = + reinterpret_cast<const x86_debug_state_t*>(state); + if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE32) { + LOG(WARNING) << "expected flavor x86_DEBUG_STATE32, observed " + << x86_debug_state->dsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86Flavor( + context, + x86_debug_state->dsh.flavor, + reinterpret_cast<ConstThreadState>(&x86_debug_state->uds.ds32), + x86_debug_state->dsh.count); + } + + case x86_THREAD_STATE32: { + const x86_thread_state32_t* x86_thread_state32 = + reinterpret_cast<const x86_thread_state32_t*>(state); + InitializeCPUContextX86Thread(context, x86_thread_state32); + return flavor; + } + + case x86_FLOAT_STATE32: { + const x86_float_state32_t* x86_float_state32 = + reinterpret_cast<const x86_float_state32_t*>(state); + InitializeCPUContextX86Float(context, x86_float_state32); + return flavor; + } + + case x86_DEBUG_STATE32: { + const x86_debug_state32_t* x86_debug_state32 = + reinterpret_cast<const x86_debug_state32_t*>(state); + InitializeCPUContextX86Debug(context, x86_debug_state32); + return flavor; + } + + case THREAD_STATE_NONE: { + // This may happen without error when called without exception-style + // flavor data, or even from an exception handler when the exception + // behavior is EXCEPTION_DEFAULT. + return flavor; + } + + default: { + NOTREACHED(); + return THREAD_STATE_NONE; + } + } +} + +void InitializeCPUContextX86_64Thread( + CPUContextX86_64* context, const x86_thread_state64_t* x86_thread_state64) { + context->rax = x86_thread_state64->__rax; + context->rbx = x86_thread_state64->__rbx; + context->rcx = x86_thread_state64->__rcx; + context->rdx = x86_thread_state64->__rdx; + context->rdi = x86_thread_state64->__rdi; + context->rsi = x86_thread_state64->__rsi; + context->rbp = x86_thread_state64->__rbp; + context->rsp = x86_thread_state64->__rsp; + context->r8 = x86_thread_state64->__r8; + context->r9 = x86_thread_state64->__r9; + context->r10 = x86_thread_state64->__r10; + context->r11 = x86_thread_state64->__r11; + context->r12 = x86_thread_state64->__r12; + context->r13 = x86_thread_state64->__r13; + context->r14 = x86_thread_state64->__r14; + context->r15 = x86_thread_state64->__r15; + context->rip = x86_thread_state64->__rip; + context->rflags = x86_thread_state64->__rflags; + context->cs = x86_thread_state64->__cs; + context->fs = x86_thread_state64->__fs; + context->gs = x86_thread_state64->__gs; +} + +void InitializeCPUContextX86_64Float( + CPUContextX86_64* context, const x86_float_state64_t* x86_float_state64) { + // This relies on both x86_float_state64_t and context->fxsave having + // identical (fxsave) layout. + static_assert(offsetof(x86_float_state64_t, __fpu_reserved1) - + offsetof(x86_float_state64_t, __fpu_fcw) == + sizeof(context->fxsave), + "types must be equivalent"); + + memcpy(&context->fxsave, + &x86_float_state64->__fpu_fcw, + sizeof(context->fxsave)); +} + +void InitializeCPUContextX86_64Debug( + CPUContextX86_64* context, const x86_debug_state64_t* x86_debug_state64) { + context->dr0 = x86_debug_state64->__dr0; + context->dr1 = x86_debug_state64->__dr1; + context->dr2 = x86_debug_state64->__dr2; + context->dr3 = x86_debug_state64->__dr3; + context->dr4 = x86_debug_state64->__dr4; + context->dr5 = x86_debug_state64->__dr5; + context->dr6 = x86_debug_state64->__dr6; + context->dr7 = x86_debug_state64->__dr7; +} + +// Initializes |context| from the native thread state structure |state|, which +// is interpreted according to |flavor|. |state_count| must be at least the +// expected size for |flavor|. This handles the architecture-specific +// x86_THREAD_STATE64, x86_FLOAT_STATE64, and x86_DEBUG_STATE64 flavors. It also +// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE +// flavors provided that the associated structure carries 64-bit data of the +// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting +// any thread state in |context|. This returns the architecture-specific flavor +// value for the thread state that was actually set, or THREAD_STATE_NONE if no +// thread state was set. +thread_state_flavor_t InitializeCPUContextX86_64Flavor( + CPUContextX86_64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + mach_msg_type_number_t expected_state_count; + switch (flavor) { + case x86_THREAD_STATE: + expected_state_count = x86_THREAD_STATE_COUNT; + break; + case x86_FLOAT_STATE: + expected_state_count = x86_FLOAT_STATE_COUNT; + break; + case x86_DEBUG_STATE: + expected_state_count = x86_DEBUG_STATE_COUNT; + break; + case x86_THREAD_STATE64: + expected_state_count = x86_THREAD_STATE64_COUNT; + break; + case x86_FLOAT_STATE64: + expected_state_count = x86_FLOAT_STATE64_COUNT; + break; + case x86_DEBUG_STATE64: + expected_state_count = x86_DEBUG_STATE64_COUNT; + break; + case THREAD_STATE_NONE: + expected_state_count = 0; + break; + default: + LOG(WARNING) << "unhandled flavor " << flavor; + return THREAD_STATE_NONE; + } + + if (state_count < expected_state_count) { + LOG(WARNING) << "expected state_count " << expected_state_count + << " for flavor " << flavor << ", observed " << state_count; + return THREAD_STATE_NONE; + } + + switch (flavor) { + case x86_THREAD_STATE: { + const x86_thread_state_t* x86_thread_state = + reinterpret_cast<const x86_thread_state_t*>(state); + if (x86_thread_state->tsh.flavor != x86_THREAD_STATE64) { + LOG(WARNING) << "expected flavor x86_THREAD_STATE64, observed " + << x86_thread_state->tsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86_64Flavor( + context, + x86_thread_state->tsh.flavor, + reinterpret_cast<ConstThreadState>(&x86_thread_state->uts.ts64), + x86_thread_state->tsh.count); + } + + case x86_FLOAT_STATE: { + const x86_float_state_t* x86_float_state = + reinterpret_cast<const x86_float_state_t*>(state); + if (x86_float_state->fsh.flavor != x86_FLOAT_STATE64) { + LOG(WARNING) << "expected flavor x86_FLOAT_STATE64, observed " + << x86_float_state->fsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86_64Flavor( + context, + x86_float_state->fsh.flavor, + reinterpret_cast<ConstThreadState>(&x86_float_state->ufs.fs64), + x86_float_state->fsh.count); + } + + case x86_DEBUG_STATE: { + const x86_debug_state_t* x86_debug_state = + reinterpret_cast<const x86_debug_state_t*>(state); + if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE64) { + LOG(WARNING) << "expected flavor x86_DEBUG_STATE64, observed " + << x86_debug_state->dsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86_64Flavor( + context, + x86_debug_state->dsh.flavor, + reinterpret_cast<ConstThreadState>(&x86_debug_state->uds.ds64), + x86_debug_state->dsh.count); + } + + case x86_THREAD_STATE64: { + const x86_thread_state64_t* x86_thread_state64 = + reinterpret_cast<const x86_thread_state64_t*>(state); + InitializeCPUContextX86_64Thread(context, x86_thread_state64); + return flavor; + } + + case x86_FLOAT_STATE64: { + const x86_float_state64_t* x86_float_state64 = + reinterpret_cast<const x86_float_state64_t*>(state); + InitializeCPUContextX86_64Float(context, x86_float_state64); + return flavor; + } + + case x86_DEBUG_STATE64: { + const x86_debug_state64_t* x86_debug_state64 = + reinterpret_cast<const x86_debug_state64_t*>(state); + InitializeCPUContextX86_64Debug(context, x86_debug_state64); + return flavor; + } + + case THREAD_STATE_NONE: { + // This may happen without error when called without exception-style + // flavor data, or even from an exception handler when the exception + // behavior is EXCEPTION_DEFAULT. + return flavor; + } + + default: { + NOTREACHED(); + return THREAD_STATE_NONE; + } + } +} + +} // namespace + +namespace internal { + +void InitializeCPUContextX86(CPUContextX86* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state32_t* x86_thread_state32, + const x86_float_state32_t* x86_float_state32, + const x86_debug_state32_t* x86_debug_state32) { + thread_state_flavor_t set_flavor = THREAD_STATE_NONE; + if (flavor != THREAD_STATE_NONE) { + set_flavor = + InitializeCPUContextX86Flavor(context, flavor, state, state_count); + } + + if (set_flavor != x86_THREAD_STATE32) { + InitializeCPUContextX86Thread(context, x86_thread_state32); + } + if (set_flavor != x86_FLOAT_STATE32) { + InitializeCPUContextX86Float(context, x86_float_state32); + } + if (set_flavor != x86_DEBUG_STATE32) { + InitializeCPUContextX86Debug(context, x86_debug_state32); + } +} + +void InitializeCPUContextX86_64(CPUContextX86_64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state64_t* x86_thread_state64, + const x86_float_state64_t* x86_float_state64, + const x86_debug_state64_t* x86_debug_state64) { + thread_state_flavor_t set_flavor = THREAD_STATE_NONE; + if (flavor != THREAD_STATE_NONE) { + set_flavor = + InitializeCPUContextX86_64Flavor(context, flavor, state, state_count); + } + + if (set_flavor != x86_THREAD_STATE64) { + InitializeCPUContextX86_64Thread(context, x86_thread_state64); + } + if (set_flavor != x86_FLOAT_STATE64) { + InitializeCPUContextX86_64Float(context, x86_float_state64); + } + if (set_flavor != x86_DEBUG_STATE64) { + InitializeCPUContextX86_64Debug(context, x86_debug_state64); + } +} + +} // namespace internal + +#endif + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h new file mode 100644 index 0000000..1d016cf --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h
@@ -0,0 +1,116 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_ + +#include <mach/mach.h> + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextX86 structure from native context structures +//! on Mac OS X. +//! +//! \a flavor, \a state, and \a state_count may be supplied by exception +//! handlers in order for the \a context parameter to be initialized by the +//! thread state received by the exception handler to the extent possible. In +//! that case, whatever thread state specified by these three parameters will +//! supersede \a x86_thread_state32, \a x86_float_state32, or \a +//! x86_debug_state32. If thread state in this format is not available, \a +//! flavor may be set to `THREAD_STATE_NONE`, and all of \a x86_thread_state32, +//! \a x86_float_state32, and \a x86_debug_state32 will be honored. +//! +//! If \a flavor, \a state, and \a state_count are provided but do not contain +//! valid values, a message will be logged and their values will be ignored as +//! though \a flavor were specified as `THREAD_STATE_NONE`. +//! +//! \param[out] context The CPUContextX86 structure to initialize. +//! \param[in] flavor The native thread state flavor of \a state. This may be +//! `x86_THREAD_STATE32`, `x86_FLOAT_STATE32`, `x86_DEBUG_STATE32`, +//! `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also +//! be `THREAD_STATE_NONE` if \a state is not supplied (and is `nullptr`). +//! \param[in] state The native thread state, which may be a casted pointer to +//! `x86_thread_state32_t`, `x86_float_state32_t`, `x86_debug_state32_t`, +//! `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This +//! parameter may be `nullptr` to not supply this data, in which case \a +//! flavor must be `THREAD_STATE_NONE`. If a “universal” structure is used, +//! it must carry 32-bit state data of the correct type. +//! \param[in] state_count The number of `natural_t`-sized (`int`-sized) units +//! in \a state. This may be 0 if \a state is `nullptr`. +//! \param[in] x86_thread_state32 The state of the thread’s integer registers. +//! \param[in] x86_float_state32 The state of the thread’s floating-point +//! registers. +//! \param[in] x86_debug_state32 The state of the thread’s debug registers. +void InitializeCPUContextX86(CPUContextX86* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state32_t* x86_thread_state32, + const x86_float_state32_t* x86_float_state32, + const x86_debug_state32_t* x86_debug_state32); + +//! \brief Initializes a CPUContextX86_64 structure from native context +//! structures on Mac OS X. +//! +//! \a flavor, \a state, and \a state_count may be supplied by exception +//! handlers in order for the \a context parameter to be initialized by the +//! thread state received by the exception handler to the extent possible. In +//! that case, whatever thread state specified by these three parameters will +//! supersede \a x86_thread_state64, \a x86_float_state64, or \a +//! x86_debug_state64. If thread state in this format is not available, \a +//! flavor may be set to `THREAD_STATE_NONE`, and all of \a x86_thread_state64, +//! \a x86_float_state64, and \a x86_debug_state64 will be honored. +//! +//! If \a flavor, \a state, and \a state_count are provided but do not contain +//! valid values, a message will be logged and their values will be ignored as +//! though \a flavor were specified as `THREAD_STATE_NONE`. +//! +//! \param[out] context The CPUContextX86_64 structure to initialize. +//! \param[in] flavor The native thread state flavor of \a state. This may be +//! `x86_THREAD_STATE64`, `x86_FLOAT_STATE64`, `x86_DEBUG_STATE64`, +//! `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also +//! be `THREAD_STATE_NONE` if \a state is not supplied (and is `nullptr`). +//! \param[in] state The native thread state, which may be a casted pointer to +//! `x86_thread_state64_t`, `x86_float_state64_t`, `x86_debug_state64_t`, +//! `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This +//! parameter may be `nullptr` to not supply this data, in which case \a +//! flavor must be `THREAD_STATE_NONE`. If a “universal” structure is used, +//! it must carry 64-bit state data of the correct type. +//! \param[in] state_count The number of `int`-sized units in \a state. This may +//! be 0 if \a state is `nullptr`. +//! \param[in] x86_thread_state64 The state of the thread’s integer registers. +//! \param[in] x86_float_state64 The state of the thread’s floating-point +//! registers. +//! \param[in] x86_debug_state64 The state of the thread’s debug registers. +void InitializeCPUContextX86_64(CPUContextX86_64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state64_t* x86_thread_state64, + const x86_float_state64_t* x86_float_state64, + const x86_debug_state64_t* x86_debug_state64); + +#endif + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc new file mode 100644 index 0000000..844c9f6 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc
@@ -0,0 +1,421 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/cpu_context_mac.h" + +#include <mach/mach.h> + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +#if defined(ARCH_CPU_X86_FAMILY) + +TEST(CPUContextMac, InitializeContextX86) { + x86_thread_state32_t x86_thread_state32 = {}; + x86_float_state32_t x86_float_state32 = {}; + x86_debug_state32_t x86_debug_state32 = {}; + x86_thread_state32.__eax = 1; + x86_float_state32.__fpu_ftw = 2; + x86_debug_state32.__dr0 = 3; + + // Test the simple case, where everything in the CPUContextX86 argument is set + // directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86(&cpu_context_x86, + THREAD_STATE_NONE, + nullptr, + 0, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } + + // Supply context in a CPU-specific “flavor” parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. Do this + // once for each of the three valid flavors. This simulates how + // InitializeCPUContextX86() might be used to initialize the context in an + // exception handler, where the exception handler may have received the + // “flavor” parameter and this context should be used to initialize the + // CPUContextX86. + + { + x86_thread_state32_t alt_x86_thread_state32 = {}; + alt_x86_thread_state32.__eax = 4; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE32, + reinterpret_cast<natural_t*>(&alt_x86_thread_state32), + x86_THREAD_STATE32_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(4u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } + + { + x86_float_state32_t alt_x86_float_state32 = {}; + alt_x86_float_state32.__fpu_ftw = 5; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_FLOAT_STATE32, + reinterpret_cast<natural_t*>(&alt_x86_float_state32), + x86_FLOAT_STATE32_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(5u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } + + { + x86_debug_state32_t alt_x86_debug_state32 = {}; + alt_x86_debug_state32.__dr0 = 6; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_DEBUG_STATE32, + reinterpret_cast<natural_t*>(&alt_x86_debug_state32), + x86_DEBUG_STATE32_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(6u, cpu_context_x86.dr0); + } + + // Supply context in a universal “flavor” parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. The + // universal format allows an exception handler to be registered to receive + // thread, float, or debug state without having to know in advance whether it + // will be receiving the state from a 32-bit or 64-bit process. For + // CPUContextX86, only the 32-bit form is supported. + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT; + x86_thread_state_3264.uts.ts32.__eax = 7; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE, + reinterpret_cast<natural_t*>(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(7u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } + + { + x86_float_state x86_float_state_3264 = {}; + x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE32; + x86_float_state_3264.fsh.count = x86_FLOAT_STATE32_COUNT; + x86_float_state_3264.ufs.fs32.__fpu_ftw = 8; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_FLOAT_STATE, + reinterpret_cast<natural_t*>(&x86_float_state_3264), + x86_FLOAT_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(8u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } + + { + x86_debug_state x86_debug_state_3264 = {}; + x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE32; + x86_debug_state_3264.dsh.count = x86_DEBUG_STATE32_COUNT; + x86_debug_state_3264.uds.ds32.__dr0 = 9; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_DEBUG_STATE, + reinterpret_cast<natural_t*>(&x86_debug_state_3264), + x86_DEBUG_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(9u, cpu_context_x86.dr0); + } + + // Supply inappropriate “flavor” contexts to test that + // InitializeCPUContextX86() detects the problem and refuses to use the + // supplied “flavor” context, falling back to the thread, float, and debug + // states. + + { + x86_thread_state64_t x86_thread_state64 = {}; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE64, + reinterpret_cast<natural_t*>(&x86_thread_state64), + x86_THREAD_STATE64_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE, + reinterpret_cast<natural_t*>(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } +} + +TEST(CPUContextMac, InitializeContextX86_64) { + x86_thread_state64_t x86_thread_state64 = {}; + x86_float_state64_t x86_float_state64 = {}; + x86_debug_state64_t x86_debug_state64 = {}; + x86_thread_state64.__rax = 10; + x86_float_state64.__fpu_ftw = 11; + x86_debug_state64.__dr0 = 12; + + // Test the simple case, where everything in the CPUContextX86_64 argument is + // set directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64(&cpu_context_x86_64, + THREAD_STATE_NONE, + nullptr, + 0, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } + + // Supply context in a CPU-specific “flavor” parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. Do this + // once for each of the three valid flavors. This simulates how + // InitializeCPUContextX86_64() might be used to initialize the context in an + // exception handler, where the exception handler may have received the + // “flavor” parameter and this context should be used to initialize the + // CPUContextX86_64. + + { + x86_thread_state64_t alt_x86_thread_state64 = {}; + alt_x86_thread_state64.__rax = 13; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE64, + reinterpret_cast<natural_t*>(&alt_x86_thread_state64), + x86_THREAD_STATE64_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(13u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } + + { + x86_float_state64_t alt_x86_float_state64 = {}; + alt_x86_float_state64.__fpu_ftw = 14; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_FLOAT_STATE64, + reinterpret_cast<natural_t*>(&alt_x86_float_state64), + x86_FLOAT_STATE64_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(14u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } + + { + x86_debug_state64_t alt_x86_debug_state64 = {}; + alt_x86_debug_state64.__dr0 = 15; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_DEBUG_STATE64, + reinterpret_cast<natural_t*>(&alt_x86_debug_state64), + x86_DEBUG_STATE64_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(15u, cpu_context_x86_64.dr0); + } + + // Supply context in a universal “flavor” parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. The + // universal format allows an exception handler to be registered to receive + // thread, float, or debug state without having to know in advance whether it + // will be receiving the state from a 32-bit or 64-bit process. For + // CPUContextX86_64, only the 64-bit form is supported. + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT; + x86_thread_state_3264.uts.ts64.__rax = 16; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE, + reinterpret_cast<natural_t*>(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(16u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } + + { + x86_float_state x86_float_state_3264 = {}; + x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE64; + x86_float_state_3264.fsh.count = x86_FLOAT_STATE64_COUNT; + x86_float_state_3264.ufs.fs64.__fpu_ftw = 17; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_FLOAT_STATE, + reinterpret_cast<natural_t*>(&x86_float_state_3264), + x86_FLOAT_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(17u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } + + { + x86_debug_state x86_debug_state_3264 = {}; + x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE64; + x86_debug_state_3264.dsh.count = x86_DEBUG_STATE64_COUNT; + x86_debug_state_3264.uds.ds64.__dr0 = 18; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_DEBUG_STATE, + reinterpret_cast<natural_t*>(&x86_debug_state_3264), + x86_DEBUG_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(18u, cpu_context_x86_64.dr0); + } + + // Supply inappropriate “flavor” contexts to test that + // InitializeCPUContextX86() detects the problem and refuses to use the + // supplied “flavor” context, falling back to the thread, float, and debug + // states. + + { + x86_thread_state32_t x86_thread_state32 = {}; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE32, + reinterpret_cast<natural_t*>(&x86_thread_state32), + x86_THREAD_STATE32_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE, + reinterpret_cast<natural_t*>(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } +} + +#endif + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc new file mode 100644 index 0000000..b5725d38e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc
@@ -0,0 +1,253 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/exception_snapshot_mac.h" + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/cpu_context_mac.h" +#include "snapshot/mac/process_reader.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_types.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotMac::ExceptionSnapshotMac() + : ExceptionSnapshot(), + context_union_(), + context_(), + codes_(), + thread_id_(0), + exception_address_(0), + exception_(0), + exception_code_0_(0), + initialized_() { +} + +ExceptionSnapshotMac::~ExceptionSnapshotMac() { +} + +bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + codes_.push_back(exception); + for (mach_msg_type_number_t code_index = 0; + code_index < code_count; + ++code_index) { + codes_.push_back(code[code_index]); + } + + exception_ = exception; + mach_exception_code_t exception_code_0 = code[0]; + + if (exception_ == EXC_CRASH) { + exception_ = ExcCrashRecoverOriginalException( + exception_code_0, &exception_code_0, nullptr); + + if (exception_ == EXC_CRASH || + exception_ == EXC_RESOURCE || + exception_ == EXC_GUARD) { + // EXC_CRASH should never be wrapped in another EXC_CRASH. + // + // EXC_RESOURCE and EXC_GUARD are software exceptions that are never + // wrapped in EXC_CRASH. The only time EXC_CRASH is generated is for + // processes exiting due to an unhandled core-generating signal or being + // killed by SIGKILL for code-signing reasons. Neither of these applies to + // EXC_RESOURCE or EXC_GUARD. See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c + // proc_prepareexit(). Receiving these exception types wrapped in + // EXC_CRASH would lose information because their code[0] uses all 64 bits + // (see below) and the code[0] recovered from EXC_CRASH only contains 20 + // significant bits. + LOG(WARNING) << base::StringPrintf( + "exception %s invalid in EXC_CRASH", + ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) + .c_str()); + } + } + + // The operations that follow put exception_code_0 (a mach_exception_code_t, + // a typedef for int64_t) into exception_code_0_ (a uint32_t). The range + // checks and bit shifts involved need the same signedness on both sides to + // work properly. + const uint64_t unsigned_exception_code_0 = exception_code_0; + + // ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is + // a 64-bit value. The best treatment for this inconsistency depends on the + // exception type. + if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) { + // All 64 bits of code[0] are significant for these exceptions. See + // <mach/exc_resource.h> for EXC_RESOURCE and 10.10 + // xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD. + // code[0] is structured similarly for these two exceptions. + // + // EXC_RESOURCE: see <kern/exc_resource.h>. The resource type and “flavor” + // together define the resource and are in the highest bits. The resource + // limit is in the lowest bits. + // + // EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c + // mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c + // fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD) + // and “flavor” (from the mach_port_guard_exception_codes or + // guard_exception_codes enums) are in the highest bits. The violating Mach + // port name or file descriptor number is in the lowest bits. + + // If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry + // 32 significant bits, and the interesting high bits will have been + // truncated. + if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) { + LOG(WARNING) << base::StringPrintf( + "behavior %s invalid for exception %s", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(), + ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) + .c_str()); + } + + // Include the more-significant information from the high bits of code[0] in + // the value to be returned by ExceptionInfo(). The full value of codes[0] + // including the less-significant lower bits is still available via Codes(). + exception_code_0_ = unsigned_exception_code_0 >> 32; + } else { + // For other exceptions, code[0]’s values never exceed 32 bits. + if (!base::IsValueInRangeForNumericType<decltype(exception_code_0_)>( + unsigned_exception_code_0)) { + LOG(WARNING) << base::StringPrintf("exception_code_0 0x%llx out of range", + unsigned_exception_code_0); + } + exception_code_0_ = unsigned_exception_code_0; + } + + const ProcessReader::Thread* thread = nullptr; + for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) { + if (exception_thread == loop_thread.port) { + thread = &loop_thread; + break; + } + } + + if (!thread) { + LOG(ERROR) << "exception_thread not found in task"; + return false; + } + + thread_id_ = thread->id; + + // Normally, the exception address is present in code[1] for EXC_BAD_ACCESS + // exceptions, but not for other types of exceptions. + bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(context_.x86_64, + flavor, + state, + state_count, + &thread->thread_context.t64, + &thread->float_context.f64, + &thread->debug_context.d64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(context_.x86, + flavor, + state, + state_count, + &thread->thread_context.t32, + &thread->float_context.f32, + &thread->debug_context.d32); + } + + // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate + // that code[1] does not (or may not) carry the exception address: + // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for + // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE) + // which collides with EXC_I386_BOUNDFLT (10.9.5 + // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS + // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c + // user_page_fault_continue() and do contain the exception address in code[1]. + if (exception_ == EXC_BAD_ACCESS && + (exception_code_0_ == EXC_I386_GPFLT || + exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { + code_1_is_exception_address = false; + } +#endif + + if (code_1_is_exception_address) { + if (process_reader->Is64Bit() && + !ExceptionBehaviorHasMachExceptionCodes(behavior)) { + // If code[1] is an address from a 64-bit process, the exception must have + // been received with MACH_EXCEPTION_CODES or the address will have been + // truncated. + LOG(WARNING) << base::StringPrintf( + "behavior %s invalid for exception %s code %d in 64-bit process", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(), + ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) + .c_str(), + exception_code_0_); + } + exception_address_ = code[1]; + } else { + exception_address_ = context_.InstructionPointer(); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotMac::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotMac::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotMac::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_; +} + +uint32_t ExceptionSnapshotMac::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_code_0_; +} + +uint64_t ExceptionSnapshotMac::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h new file mode 100644 index 0000000..ea1161c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h
@@ -0,0 +1,93 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_ + +#include <mach/mach.h> +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReader; + +namespace internal { + +//! \brief An ExceptionSnapshot of an exception sustained by a running (or +//! crashed) process on a Mac OS X system. +class ExceptionSnapshotMac final : public ExceptionSnapshot { + public: + ExceptionSnapshotMac(); + ~ExceptionSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! Other than \a process_reader, the parameters may be passed directly + //! through from a Mach exception handler. + //! + //! \param[in] process_reader A ProcessReader for the task that sustained the + //! exception. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count); + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector<uint64_t>& Codes() const override; + + private: +#if defined(ARCH_CPU_X86_FAMILY) + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; +#endif + CPUContext context_; + std::vector<uint64_t> codes_; + uint64_t thread_id_; + uint64_t exception_address_; + exception_type_t exception_; + uint32_t exception_code_0_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotMac); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc new file mode 100644 index 0000000..1e2b740 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc
@@ -0,0 +1,175 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_annotations_reader.h" + +#include <mach-o/loader.h> +#include <mach/mach.h> + +#include <utility> + +#include "base/logging.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "snapshot/mac/process_reader.h" +#include "util/mach/task_memory.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { + +MachOImageAnnotationsReader::MachOImageAnnotationsReader( + ProcessReader* process_reader, + const MachOImageReader* image_reader, + const std::string& name) + : name_(name), + process_reader_(process_reader), + image_reader_(image_reader) { +} + +std::vector<std::string> MachOImageAnnotationsReader::Vector() const { + std::vector<std::string> vector_annotations; + + ReadCrashReporterClientAnnotations(&vector_annotations); + ReadDyldErrorStringAnnotation(&vector_annotations); + + return vector_annotations; +} + +std::map<std::string, std::string> MachOImageAnnotationsReader::SimpleMap() + const { + std::map<std::string, std::string> simple_map_annotations; + + ReadCrashpadSimpleAnnotations(&simple_map_annotations); + + return simple_map_annotations; +} + +void MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations( + std::vector<std::string>* vector_annotations) const { + mach_vm_address_t crash_info_address; + const process_types::section* crash_info_section = + image_reader_->GetSectionByName( + SEG_DATA, "__crash_info", &crash_info_address); + if (!crash_info_section) { + return; + } + + process_types::crashreporter_annotations_t crash_info; + if (!crash_info.Read(process_reader_, crash_info_address)) { + LOG(WARNING) << "could not read crash info from " << name_; + return; + } + + if (crash_info.version != 4 && crash_info.version != 5) { + LOG(WARNING) << "unexpected crash info version " << crash_info.version + << " in " << name_; + return; + } + + size_t expected_size = + process_types::crashreporter_annotations_t::ExpectedSizeForVersion( + process_reader_, crash_info.version); + if (crash_info_section->size < expected_size) { + LOG(WARNING) << "small crash info section size " << crash_info_section->size + << " < " << expected_size << " for version " + << crash_info.version << " in " << name_; + return; + } + + // This number was totally made up out of nowhere, but it seems prudent to + // enforce some limit. + const size_t kMaxMessageSize = 1024; + if (crash_info.message) { + std::string message; + if (process_reader_->Memory()->ReadCStringSizeLimited( + crash_info.message, kMaxMessageSize, &message)) { + vector_annotations->push_back(message); + } else { + LOG(WARNING) << "could not read crash message in " << name_; + } + } + + if (crash_info.message2) { + std::string message; + if (process_reader_->Memory()->ReadCStringSizeLimited( + crash_info.message2, kMaxMessageSize, &message)) { + vector_annotations->push_back(message); + } else { + LOG(WARNING) << "could not read crash message 2 in " << name_; + } + } +} + +void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation( + std::vector<std::string>* vector_annotations) const { + // dyld stores its error string at the external symbol for |const char + // error_string[1024]|. See 10.9.5 dyld-239.4/src/dyld.cpp error_string. + if (image_reader_->FileType() != MH_DYLINKER) { + return; + } + + mach_vm_address_t error_string_address; + if (!image_reader_->LookUpExternalDefinedSymbol("_error_string", + &error_string_address)) { + return; + } + + std::string message; + // 1024 here is distinct from kMaxMessageSize above, because it refers to a + // precisely-sized buffer inside dyld. + if (process_reader_->Memory()->ReadCStringSizeLimited( + error_string_address, 1024, &message)) { + if (!message.empty()) { + vector_annotations->push_back(message); + } + } else { + LOG(WARNING) << "could not read dylinker error string from " << name_; + } +} + +void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations( + std::map<std::string, std::string>* simple_map_annotations) const { + process_types::CrashpadInfo crashpad_info; + if (!image_reader_->GetCrashpadInfo(&crashpad_info)) { + return; + } + + if (!crashpad_info.simple_annotations) { + return; + } + + std::vector<SimpleStringDictionary::Entry> + simple_annotations(SimpleStringDictionary::num_entries); + if (!process_reader_->Memory()->Read( + crashpad_info.simple_annotations, + simple_annotations.size() * sizeof(simple_annotations[0]), + &simple_annotations[0])) { + LOG(WARNING) << "could not read simple annotations from " << name_; + return; + } + + for (const auto& entry : simple_annotations) { + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + if (key_length) { + std::string key(entry.key, key_length); + std::string value(entry.value, strnlen(entry.value, sizeof(entry.value))); + if (!simple_map_annotations->insert(std::make_pair(key, value)).second) { + LOG(INFO) << "duplicate simple annotation " << key << " in " << name_; + } + } + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h new file mode 100644 index 0000000..f8fb1ec --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h
@@ -0,0 +1,93 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/mac/process_types.h" + +namespace crashpad { + +class MachOImageReader; +class ProcessReader; + +//! \brief A reader for annotations stored in a Mach-O image mapped into another +//! process. +//! +//! These annotations are stored for the benefit of crash reporters, and provide +//! information thought to be potentially useful for crash analysis. This class +//! can decode annotations stored in these formats: +//! - CrashpadInfo. This format is used by Crashpad clients. The “simple +//! annotations” are recovered from any module with a compatible data +//! section, and are included in the annotations returned by SimpleMap(). +//! - `CrashReporterClient.h`’s `crashreporter_annotations_t`. This format is +//! used by Apple code. The `message` and `message2` fields can be recovered +//! from any module with a compatible data section, and are included in the +//! annotations returned by Vector(). +//! - `dyld`’s `error_string`. This format is used exclusively by dyld, +//! typically for fatal errors. This string can be recovered from any +//! `MH_DYLINKER`-type module with this symbol, and is included in the +//! annotations returned by Vector(). +class MachOImageAnnotationsReader { + public: + //! \brief Constructs an object. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] image_reader The MachOImageReader for the Mach-O image file + //! contained within the remote process. + //! \param[in] name The module’s name, a string to be used in logged messages. + //! This string is for diagnostic purposes only, and may be empty. + MachOImageAnnotationsReader(ProcessReader* process_reader, + const MachOImageReader* image_reader, + const std::string& name); + + ~MachOImageAnnotationsReader() {} + + //! \brief Returns the module’s annotations that are organized as a vector of + //! strings. + std::vector<std::string> Vector() const; + + //! \brief Returns the module’s annotations that are organized as key-value + //! pairs, where all keys and values are strings. + std::map<std::string, std::string> SimpleMap() const; + + private: + // Reades crashreporter_annotations_t::message and + // crashreporter_annotations_t::message2 on behalf of Vector(). + void ReadCrashReporterClientAnnotations( + std::vector<std::string>* vector_annotations) const; + + // Reads dyld_error_string on behalf of Vector(). + void ReadDyldErrorStringAnnotation( + std::vector<std::string>* vector_annotations) const; + + // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap(). + void ReadCrashpadSimpleAnnotations( + std::map<std::string, std::string>* simple_map_annotations) const; + + std::string name_; + ProcessReader* process_reader_; // weak + const MachOImageReader* image_reader_; // weak + + DISALLOW_COPY_AND_ASSIGN(MachOImageAnnotationsReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc new file mode 100644 index 0000000..32f3b5c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
@@ -0,0 +1,411 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_annotations_reader.h" + +#include <dlfcn.h> +#include <mach/mach.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "snapshot/mac/process_reader.h" +#include "test/errors.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "test/paths.h" +#include "util/file/file_io.h" +#include "util/mac/mac_util.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { +namespace test { +namespace { + +// \return The path to crashpad_snapshot_test_module_crashy_initializer.so +std::string ModuleWithCrashyInitializer() { + return Paths::Executable().value() + "_module_crashy_initializer.so"; +} + +//! \return The path to the crashpad_snapshot_test_no_op executable. +base::FilePath NoOpExecutable() { + return base::FilePath(Paths::Executable().value() + "_no_op"); +} + +class TestMachOImageAnnotationsReader final + : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + enum TestType { + // Don’t crash, just test the CrashpadInfo interface. + kDontCrash = 0, + + // The child process should crash by calling abort(). The parent verifies + // that the system libraries set the expected annotations. + // + // This test verifies that the message field in crashreporter_annotations_t + // can be recovered. Either 10.10.2 Libc-1044.1.2/stdlib/FreeBSD/abort.c + // abort() or 10.10.2 Libc-1044.10.1/sys/_libc_fork_child.c + // _libc_fork_child() calls CRSetCrashLogMessage() to set the message field. + kCrashAbort, + + // The child process should crash at module initialization time, when dyld + // will have set an annotation matching the path of the module being + // initialized. + // + // This test exists to verify that the message2 field in + // crashreporter_annotations_t can be recovered. 10.10.2 + // dyld-353.2.1/src/ImageLoaderMachO.cpp + // ImageLoaderMachO::doInitialization() calls CRSetCrashLogMessage2() to set + // the message2 field. + kCrashModuleInitialization, + + // The child process should crash by setting DYLD_INSERT_LIBRARIES to + // contain a nonexistent library. The parent verifies that dyld sets the + // expected annotations. + kCrashDyld, + }; + + explicit TestMachOImageAnnotationsReader(TestType test_type) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + test_type_(test_type) { + } + + ~TestMachOImageAnnotationsReader() {} + + // UniversalMachExcServer::Interface: + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + EXPECT_EQ(ChildTask(), task); + + ProcessReader process_reader; + bool rv = process_reader.Initialize(task); + if (!rv) { + ADD_FAILURE(); + } else { + const std::vector<ProcessReader::Module>& modules = + process_reader.Modules(); + std::vector<std::string> all_annotations_vector; + for (const ProcessReader::Module& module : modules) { + if (module.reader) { + MachOImageAnnotationsReader module_annotations_reader( + &process_reader, module.reader, module.name); + std::vector<std::string> module_annotations_vector = + module_annotations_reader.Vector(); + all_annotations_vector.insert(all_annotations_vector.end(), + module_annotations_vector.begin(), + module_annotations_vector.end()); + } else { + EXPECT_TRUE(module.reader); + } + } + + // Mac OS X 10.6 doesn’t have support for CrashReporter annotations + // (CrashReporterClient.h), so don’t look for any special annotations in + // that version. + int mac_os_x_minor_version = MacOSXMinorVersion(); + if (mac_os_x_minor_version > 7) { + EXPECT_GE(all_annotations_vector.size(), 1u); + + std::string expected_annotation; + switch (test_type_) { + case kCrashAbort: + // The child process calls abort(), so the expected annotation + // reflects this, with a string set by 10.7.5 + // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still + // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(), + // but because abort() tests to see if a message is already set and + // something else in Libc will have set a message, this string is + // not the expectation on 10.9 or higher. Instead, after fork(), the + // child process has a message indicating that a fork() without + // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c + // _libc_fork_child(). + expected_annotation = + mac_os_x_minor_version <= 8 + ? "abort() called" + : "crashed on child side of fork pre-exec"; + break; + + case kCrashModuleInitialization: + // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp + // ImageLoaderMachO::doInitialization(). + expected_annotation = ModuleWithCrashyInitializer(); + break; + + case kCrashDyld: + // This is independent of dyld’s error_string, which is tested + // below. + expected_annotation = "dyld: launch, loading dependent libraries"; + break; + + default: + ADD_FAILURE(); + break; + } + + bool found = false; + for (const std::string& annotation : all_annotations_vector) { + // Look for the expectation as a leading susbtring, because the actual + // string that dyld uses will have the contents of the + // DYLD_INSERT_LIBRARIES environment variable appended to it on Mac + // OS X 10.10. + if (annotation.substr(0, expected_annotation.length()) == + expected_annotation) { + found = true; + break; + } + } + EXPECT_TRUE(found) << expected_annotation; + } + + // dyld exposes its error_string at least as far back as Mac OS X 10.4. + if (test_type_ == kCrashDyld) { + const char kExpectedAnnotation[] = "could not load inserted library"; + size_t expected_annotation_length = strlen(kExpectedAnnotation); + bool found = false; + for (const std::string& annotation : all_annotations_vector) { + // Look for the expectation as a leading substring, because the actual + // string will contain the library’s pathname and, on Mac OS X 10.9 + // and later, a reason. + if (annotation.substr(0, expected_annotation_length) == + kExpectedAnnotation) { + found = true; + break; + } + } + + EXPECT_TRUE(found) << kExpectedAnnotation; + } + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + + // Wait for the child process to indicate that it’s done setting up its + // annotations via the CrashpadInfo interface. + char c; + CheckedReadFile(ReadPipeHandle(), &c, sizeof(c)); + + // Verify the “simple map” annotations set via the CrashpadInfo interface. + const std::vector<ProcessReader::Module>& modules = + process_reader.Modules(); + std::map<std::string, std::string> all_annotations_simple_map; + for (const ProcessReader::Module& module : modules) { + MachOImageAnnotationsReader module_annotations_reader( + &process_reader, module.reader, module.name); + std::map<std::string, std::string> module_annotations_simple_map = + module_annotations_reader.SimpleMap(); + all_annotations_simple_map.insert(module_annotations_simple_map.begin(), + module_annotations_simple_map.end()); + } + + EXPECT_GE(all_annotations_simple_map.size(), 5u); + EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]); + EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]); + EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]); + EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]); + EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]); + + // Tell the child process that it’s permitted to crash. + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + + if (test_type_ != kDontCrash) { + // Handle the child’s crash. Further validation will be done in + // CatchMachException(). + UniversalMachExcServer universal_mach_exc_server(this); + + mach_msg_return_t mr = + MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(MACH_MSG_SUCCESS, mr) + << MachErrorMessage(mr, "MachMessageServer::Run"); + + switch (test_type_) { + case kCrashAbort: + SetExpectedChildTermination(kTerminationSignal, SIGABRT); + break; + + case kCrashModuleInitialization: + // This crash is triggered by __builtin_trap(), which shows up as + // SIGILL. + SetExpectedChildTermination(kTerminationSignal, SIGILL); + break; + + case kCrashDyld: + // dyld fatal errors result in the execution of an int3 instruction on + // x86 and a trap instruction on ARM, both of which raise SIGTRAP. + // 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error. + SetExpectedChildTermination(kTerminationSignal, SIGTRAP); + break; + + default: + FAIL(); + break; + } + } + } + + void MachMultiprocessChild() override { + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + + // This is “leaked” to crashpad_info. + SimpleStringDictionary* simple_annotations = new SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad_info->set_simple_annotations(simple_annotations); + + // Tell the parent that the environment has been set up. + char c = '\0'; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + + // Wait for the parent to indicate that it’s safe to crash. + CheckedReadFile(ReadPipeHandle(), &c, sizeof(c)); + + // Direct an exception message to the exception server running in the + // parent. + ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, + mach_task_self()); + ASSERT_TRUE(exception_ports.SetExceptionPort( + EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE)); + + switch (test_type_) { + case kDontCrash: { + break; + } + + case kCrashAbort: { + abort(); + break; + } + + case kCrashModuleInitialization: { + // Load a module that crashes while executing a module initializer. + void* dl_handle = dlopen(ModuleWithCrashyInitializer().c_str(), + RTLD_LAZY | RTLD_LOCAL); + + // This should have crashed in the dlopen(). If dlopen() failed, the + // ASSERT_NE() will show the message. If it succeeded without crashing, + // the FAIL() will fail the test. + ASSERT_NE(nullptr, dl_handle) << dlerror(); + FAIL(); + break; + } + + case kCrashDyld: { + // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist. + // Unable to load it, dyld will abort with a fatal error. + ASSERT_EQ( + 0, + setenv( + "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1)) + << ErrnoMessage("setenv"); + + // The actual executable doesn’t matter very much, because dyld won’t + // ever launch it. It just needs to be an executable that uses dyld as + // its LC_LOAD_DYLINKER (all normal executables do). A custom no-op + // executable is provided because DYLD_INSERT_LIBRARIES does not work + // with system executables on OS X 10.11 due to System Integrity + // Protection. + base::FilePath no_op_executable = NoOpExecutable(); + ASSERT_EQ(0, execl(no_op_executable.value().c_str(), + no_op_executable.BaseName().value().c_str(), + nullptr)) + << ErrnoMessage("execl"); + break; + } + + default: + break; + } + } + + TestType test_type_; + + DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader); +}; + +TEST(MachOImageAnnotationsReader, DontCrash) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kDontCrash); + test_mach_o_image_annotations_reader.Run(); +} + +TEST(MachOImageAnnotationsReader, CrashAbort) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kCrashAbort); + test_mach_o_image_annotations_reader.Run(); +} + +TEST(MachOImageAnnotationsReader, CrashModuleInitialization) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kCrashModuleInitialization); + test_mach_o_image_annotations_reader.Run(); +} + +TEST(MachOImageAnnotationsReader, CrashDyld) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kCrashDyld); + test_mach_o_image_annotations_reader.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc new file mode 100644 index 0000000..2ba9bee --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc
@@ -0,0 +1,31 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +namespace crashpad { +namespace test { +namespace { + +class CrashyClass { + public: + CrashyClass() { + __builtin_trap(); + } +}; + +// __attribute__((used)) keeps the dead code stripper away. +__attribute__((used)) CrashyClass g_crashy_object; + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc new file mode 100644 index 0000000..b895320 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc
@@ -0,0 +1,19 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <stdlib.h> + +int main(int argc, char* argv[]) { + return EXIT_SUCCESS; +}
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc new file mode 100644 index 0000000..09b7ffeb --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
@@ -0,0 +1,724 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_reader.h" + +#include <mach-o/loader.h> +#include <mach-o/nlist.h> +#include <string.h> + +#include <limits> +#include <vector> +#include <utility> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_info.h" +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "snapshot/mac/mach_o_image_symbol_table_reader.h" +#include "snapshot/mac/process_reader.h" +#include "util/mac/checked_mach_address_range.h" +#include "util/misc/implicit_cast.h" + +namespace { + +const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max(); + +} // namespace + +namespace crashpad { + +MachOImageReader::MachOImageReader() + : segments_(), + segment_map_(), + module_name_(), + module_info_(), + dylinker_name_(), + uuid_(), + address_(0), + size_(0), + slide_(0), + source_version_(0), + symtab_command_(), + dysymtab_command_(), + symbol_table_(), + id_dylib_command_(), + process_reader_(nullptr), + file_type_(0), + initialized_(), + symbol_table_initialized_() { +} + +MachOImageReader::~MachOImageReader() { +} + +bool MachOImageReader::Initialize(ProcessReader* process_reader, + mach_vm_address_t address, + const std::string& name) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + address_ = address; + module_name_ = name; + + module_info_ = + base::StringPrintf(", module %s, address 0x%llx", name.c_str(), address); + + process_types::mach_header mach_header; + if (!mach_header.Read(process_reader, address)) { + LOG(WARNING) << "could not read mach_header" << module_info_; + return false; + } + + const bool is_64_bit = process_reader->Is64Bit(); + const uint32_t kExpectedMagic = is_64_bit ? MH_MAGIC_64 : MH_MAGIC; + if (mach_header.magic != kExpectedMagic) { + LOG(WARNING) << base::StringPrintf("unexpected mach_header::magic 0x%08x", + mach_header.magic) << module_info_; + return false; + } + + switch (mach_header.filetype) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_DYLINKER: + case MH_BUNDLE: + file_type_ = mach_header.filetype; + break; + default: + LOG(WARNING) << base::StringPrintf( + "unexpected mach_header::filetype 0x%08x", + mach_header.filetype) << module_info_; + return false; + } + + const uint32_t kExpectedSegmentCommand = + is_64_bit ? LC_SEGMENT_64 : LC_SEGMENT; + const uint32_t kUnexpectedSegmentCommand = + is_64_bit ? LC_SEGMENT : LC_SEGMENT_64; + + const struct { + // Which method to call when encountering a load command matching |command|. + bool (MachOImageReader::*function)(mach_vm_address_t, const std::string&); + + // The minimum size that may be allotted to store the load command. + size_t size; + + // The load command to match. + uint32_t command; + + // True if the load command must not appear more than one time. + bool singleton; + } kLoadCommandReaders[] = { + { + &MachOImageReader::ReadSegmentCommand, + process_types::segment_command::ExpectedSize(process_reader), + kExpectedSegmentCommand, + false, + }, + { + &MachOImageReader::ReadSymTabCommand, + process_types::symtab_command::ExpectedSize(process_reader), + LC_SYMTAB, + true, + }, + { + &MachOImageReader::ReadDySymTabCommand, + process_types::symtab_command::ExpectedSize(process_reader), + LC_DYSYMTAB, + true, + }, + { + &MachOImageReader::ReadIdDylibCommand, + process_types::dylib_command::ExpectedSize(process_reader), + LC_ID_DYLIB, + true, + }, + { + &MachOImageReader::ReadDylinkerCommand, + process_types::dylinker_command::ExpectedSize(process_reader), + LC_LOAD_DYLINKER, + true, + }, + { + &MachOImageReader::ReadDylinkerCommand, + process_types::dylinker_command::ExpectedSize(process_reader), + LC_ID_DYLINKER, + true, + }, + { + &MachOImageReader::ReadUUIDCommand, + process_types::uuid_command::ExpectedSize(process_reader), + LC_UUID, + true, + }, + { + &MachOImageReader::ReadSourceVersionCommand, + process_types::source_version_command::ExpectedSize(process_reader), + LC_SOURCE_VERSION, + true, + }, + + // When reading a 64-bit process, no 32-bit segment commands should be + // present, and vice-versa. + { + &MachOImageReader::ReadUnexpectedCommand, + process_types::load_command::ExpectedSize(process_reader), + kUnexpectedSegmentCommand, + false, + }, + }; + + // This vector is parallel to the kLoadCommandReaders array, and tracks + // whether a singleton load command matching the |command| field has been + // found yet. + std::vector<uint32_t> singleton_indices(arraysize(kLoadCommandReaders), + kInvalidSegmentIndex); + + size_t offset = mach_header.Size(); + const mach_vm_address_t kLoadCommandAddressLimit = + address + offset + mach_header.sizeofcmds; + + for (uint32_t load_command_index = 0; + load_command_index < mach_header.ncmds; + ++load_command_index) { + mach_vm_address_t load_command_address = address + offset; + std::string load_command_info = base::StringPrintf(", load command %u/%u%s", + load_command_index, + mach_header.ncmds, + module_info_.c_str()); + + process_types::load_command load_command; + + // Make sure that the basic load command structure doesn’t overflow the + // space allotted for load commands. + if (load_command_address + load_command.ExpectedSize(process_reader) > + kLoadCommandAddressLimit) { + LOG(WARNING) << base::StringPrintf( + "load_command at 0x%llx exceeds sizeofcmds 0x%x", + load_command_address, + mach_header.sizeofcmds) << load_command_info; + return false; + } + + if (!load_command.Read(process_reader, load_command_address)) { + LOG(WARNING) << "could not read load_command" << load_command_info; + return false; + } + + load_command_info = base::StringPrintf(", load command 0x%x %u/%u%s", + load_command.cmd, + load_command_index, + mach_header.ncmds, + module_info_.c_str()); + + // Now that the load command’s stated size is known, make sure that it + // doesn’t overflow the space allotted for load commands. + if (load_command_address + load_command.cmdsize > + kLoadCommandAddressLimit) { + LOG(WARNING) + << base::StringPrintf( + "load_command at 0x%llx cmdsize 0x%x exceeds sizeofcmds 0x%x", + load_command_address, + load_command.cmdsize, + mach_header.sizeofcmds) << load_command_info; + return false; + } + + for (size_t reader_index = 0; + reader_index < arraysize(kLoadCommandReaders); + ++reader_index) { + if (load_command.cmd != kLoadCommandReaders[reader_index].command) { + continue; + } + + if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) { + LOG(WARNING) << base::StringPrintf( + "load command cmdsize 0x%x insufficient for 0x%zx", + load_command.cmdsize, + kLoadCommandReaders[reader_index].size) + << load_command_info; + return false; + } + + if (kLoadCommandReaders[reader_index].singleton) { + if (singleton_indices[reader_index] != kInvalidSegmentIndex) { + LOG(WARNING) << "duplicate load command at " + << singleton_indices[reader_index] << load_command_info; + return false; + } + + singleton_indices[reader_index] = load_command_index; + } + + if (!((this)->*(kLoadCommandReaders[reader_index].function))( + load_command_address, load_command_info)) { + return false; + } + + break; + } + + offset += load_command.cmdsize; + } + + // Now that the slide is known, push it into the segments. + for (MachOImageSegmentReader* segment : segments_) { + segment->SetSlide(slide_); + + // This was already checked for the unslid values while the segments were + // read, but now it’s possible to check the slid values too. The individual + // sections don’t need to be checked because they were verified to be + // contained within their respective segments when the segments were read. + mach_vm_address_t slid_segment_address = segment->Address(); + mach_vm_size_t slid_segment_size = segment->Size(); + CheckedMachAddressRange slid_segment_range( + process_reader_->Is64Bit(), slid_segment_address, slid_segment_size); + if (!slid_segment_range.IsValid()) { + LOG(WARNING) << base::StringPrintf( + "invalid slid segment range 0x%llx + 0x%llx, " + "segment ", + slid_segment_address, + slid_segment_size) << segment->Name() << module_info_; + return false; + } + } + + if (!segment_map_.count(SEG_TEXT)) { + // The __TEXT segment is required. Even a module with no executable code + // will have a __TEXT segment encompassing the Mach-O header and load + // commands. Without a __TEXT segment, |size_| will not have been computed. + LOG(WARNING) << "no " SEG_TEXT " segment" << module_info_; + return false; + } + + if (mach_header.filetype == MH_DYLIB && !id_dylib_command_) { + // This doesn’t render a module unusable, it’s just weird and worth noting. + LOG(INFO) << "no LC_ID_DYLIB" << module_info_; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const MachOImageSegmentReader* MachOImageReader::GetSegmentByName( + const std::string& segment_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const auto& iterator = segment_map_.find(segment_name); + if (iterator == segment_map_.end()) { + return nullptr; + } + + const MachOImageSegmentReader* segment = segments_[iterator->second]; + return segment; +} + +const process_types::section* MachOImageReader::GetSectionByName( + const std::string& segment_name, + const std::string& section_name, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const MachOImageSegmentReader* segment = GetSegmentByName(segment_name); + if (!segment) { + return nullptr; + } + + return segment->GetSectionByName(section_name, address); +} + +const process_types::section* MachOImageReader::GetSectionAtIndex( + size_t index, + const MachOImageSegmentReader** containing_segment, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + static_assert(NO_SECT == 0, "NO_SECT must be zero"); + if (index == NO_SECT) { + LOG(WARNING) << "section index " << index << " out of range"; + return nullptr; + } + + // Switch to a more comfortable 0-based index. + size_t local_index = index - 1; + + for (const MachOImageSegmentReader* segment : segments_) { + size_t nsects = segment->nsects(); + if (local_index < nsects) { + const process_types::section* section = + segment->GetSectionAtIndex(local_index, address); + + if (containing_segment) { + *containing_segment = segment; + } + + return section; + } + + local_index -= nsects; + } + + LOG(WARNING) << "section index " << index << " out of range"; + return nullptr; +} + +bool MachOImageReader::LookUpExternalDefinedSymbol( + const std::string& name, + mach_vm_address_t* value) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (symbol_table_initialized_.is_uninitialized()) { + InitializeSymbolTable(); + } + + if (!symbol_table_initialized_.is_valid() || !symbol_table_) { + return false; + } + + const MachOImageSymbolTableReader::SymbolInformation* symbol_info = + symbol_table_->LookUpExternalDefinedSymbol(name); + if (!symbol_info) { + return false; + } + + if (symbol_info->section == NO_SECT) { + // This is an absolute (N_ABS) symbol, which requires no further validation + // or processing. + *value = symbol_info->value; + return true; + } + + // This is a symbol defined in a particular section, so make sure that it’s + // valid for that section and fix it up for any “slide” as needed. + + mach_vm_address_t section_address; + const MachOImageSegmentReader* segment; + const process_types::section* section = + GetSectionAtIndex(symbol_info->section, &segment, §ion_address); + if (!section) { + return false; + } + + mach_vm_address_t slid_value = + symbol_info->value + (segment->SegmentSlides() ? slide_ : 0); + + // The __mh_execute_header (_MH_EXECUTE_SYM) symbol is weird. In + // position-independent executables, it shows up in the symbol table as a + // symbol in section 1, although it’s not really in that section. It points to + // the mach_header[_64], which is the beginning of the __TEXT segment, and the + // __text section normally begins after the load commands in the __TEXT + // segment. The range check below will fail for this symbol, because it’s not + // really in the section it claims to be in. See Xcode 5.1 + // ld64-236.3/src/ld/OutputFile.cpp ld::tool::OutputFile::buildSymbolTable(). + // There, ld takes symbols that refer to anything in the mach_header[_64] and + // marks them as being in section 1. Here, section 1 is treated in this same + // special way as long as it’s in the __TEXT segment that begins at the start + // of the image, which is normally the case, and as long as the symbol’s value + // is the base of the image. + // + // This only happens for PIE executables, because __mh_execute_header needs + // to slide. In non-PIE executables, __mh_execute_header is an absolute + // symbol. + CheckedMachAddressRange section_range( + process_reader_->Is64Bit(), section_address, section->size); + if (!section_range.ContainsValue(slid_value) && + !(symbol_info->section == 1 && segment->Name() == SEG_TEXT && + slid_value == Address())) { + std::string section_name_full = + MachOImageSegmentReader::SegmentAndSectionNameString(section->segname, + section->sectname); + LOG(WARNING) << base::StringPrintf( + "symbol %s (0x%llx) outside of section %s (0x%llx + " + "0x%llx)", + name.c_str(), + slid_value, + section_name_full.c_str(), + section_address, + section->size) << module_info_; + return false; + } + + *value = slid_value; + return true; +} + +uint32_t MachOImageReader::DylibVersion() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK_EQ(FileType(), implicit_cast<uint32_t>(MH_DYLIB)); + + if (id_dylib_command_) { + return id_dylib_command_->dylib_current_version; + } + + // In case this was a weird dylib without an LC_ID_DYLIB command. + return 0; +} + +void MachOImageReader::UUID(crashpad::UUID* uuid) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + memcpy(uuid, &uuid_, sizeof(uuid_)); +} + +bool MachOImageReader::GetCrashpadInfo( + process_types::CrashpadInfo* crashpad_info) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + mach_vm_address_t crashpad_info_address; + const process_types::section* crashpad_info_section = + GetSectionByName(SEG_DATA, "__crashpad_info", &crashpad_info_address); + if (!crashpad_info_section) { + return false; + } + + if (crashpad_info_section->size < + crashpad_info->ExpectedSize(process_reader_)) { + LOG(WARNING) << "small crashpad info section size " + << crashpad_info_section->size << module_info_; + return false; + } + + if (!crashpad_info->Read(process_reader_, crashpad_info_address)) { + LOG(WARNING) << "could not read crashpad info" << module_info_; + return false; + } + + if (crashpad_info->signature != CrashpadInfo::kSignature || + crashpad_info->size != crashpad_info_section->size || + crashpad_info->version < 1) { + LOG(WARNING) << "unexpected crashpad info data" << module_info_; + return false; + } + + return true; +} + +template <typename T> +bool MachOImageReader::ReadLoadCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info, + uint32_t expected_load_command_id, + T* load_command) { + if (!load_command->Read(process_reader_, load_command_address)) { + LOG(WARNING) << "could not read load command" << load_command_info; + return false; + } + + DCHECK_GE(load_command->cmdsize, load_command->Size()); + DCHECK_EQ(load_command->cmd, expected_load_command_id); + return true; +} + +bool MachOImageReader::ReadSegmentCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + MachOImageSegmentReader* segment = new MachOImageSegmentReader(); + size_t segment_index = segments_.size(); + segments_.push_back(segment); // Takes ownership. + + if (!segment->Initialize(process_reader_, + load_command_address, + load_command_info, + module_name_, + file_type_)) { + segments_.pop_back(); + return false; + } + + // At this point, the segment itself is considered valid, but if one of the + // next checks fails, it will render the module invalid. If any of the next + // checks fail, this method should return false, but it doesn’t need to bother + // removing the segment from segments_. The segment will be properly released + // when the image is destroyed, and the image won’t be usable because + // initialization won’t have completed. Most importantly, leaving the segment + // in segments_ means that no other structures (such as perhaps segment_map_) + // become inconsistent or require cleanup. + + const std::string segment_name = segment->Name(); + const auto insert_result = + segment_map_.insert(std::make_pair(segment_name, segment_index)); + if (!insert_result.second) { + LOG(WARNING) << base::StringPrintf("duplicate %s segment at %zu and %zu", + segment_name.c_str(), + insert_result.first->second, + segment_index) << load_command_info; + return false; + } + + mach_vm_size_t vmsize = segment->vmsize(); + + if (segment_name == SEG_TEXT) { + if (vmsize == 0) { + LOG(WARNING) << "zero-sized " SEG_TEXT " segment" << load_command_info; + return false; + } + + mach_vm_size_t fileoff = segment->fileoff(); + if (fileoff != 0) { + LOG(WARNING) << base::StringPrintf( + SEG_TEXT " segment has unexpected fileoff 0x%llx", + fileoff) << load_command_info; + return false; + } + + size_ = vmsize; + + // The slide is computed as the difference between the __TEXT segment’s + // preferred and actual load addresses. This is the same way that dyld + // computes slide. See 10.9.2 dyld-239.4/src/dyldInitialization.cpp + // slideOfMainExecutable(). + slide_ = address_ - segment->vmaddr(); + } + + return true; +} + +bool MachOImageReader::ReadSymTabCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info) { + symtab_command_.reset(new process_types::symtab_command()); + return ReadLoadCommand(load_command_address, + load_command_info, + LC_SYMTAB, + symtab_command_.get()); +} + +bool MachOImageReader::ReadDySymTabCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + dysymtab_command_.reset(new process_types::dysymtab_command()); + return ReadLoadCommand(load_command_address, + load_command_info, + LC_DYSYMTAB, + dysymtab_command_.get()); +} + +bool MachOImageReader::ReadIdDylibCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + if (file_type_ != MH_DYLIB) { + LOG(WARNING) << base::StringPrintf( + "LC_ID_DYLIB inappropriate in non-dylib file type 0x%x", + file_type_) << load_command_info; + return false; + } + + DCHECK(!id_dylib_command_); + id_dylib_command_.reset(new process_types::dylib_command()); + return ReadLoadCommand(load_command_address, + load_command_info, + LC_ID_DYLIB, + id_dylib_command_.get()); +} + +bool MachOImageReader::ReadDylinkerCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + if (file_type_ != MH_EXECUTE && file_type_ != MH_DYLINKER) { + LOG(WARNING) << base::StringPrintf( + "LC_LOAD_DYLINKER/LC_ID_DYLINKER inappropriate in file " + "type 0x%x", + file_type_) << load_command_info; + return false; + } + + const uint32_t kExpectedCommand = + file_type_ == MH_DYLINKER ? LC_ID_DYLINKER : LC_LOAD_DYLINKER; + process_types::dylinker_command dylinker_command; + if (!ReadLoadCommand(load_command_address, + load_command_info, + kExpectedCommand, + &dylinker_command)) { + return false; + } + + if (!process_reader_->Memory()->ReadCStringSizeLimited( + load_command_address + dylinker_command.name, + dylinker_command.cmdsize - dylinker_command.name, + &dylinker_name_)) { + LOG(WARNING) << "could not read dylinker_command name" << load_command_info; + return false; + } + + return true; +} + +bool MachOImageReader::ReadUUIDCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info) { + process_types::uuid_command uuid_command; + if (!ReadLoadCommand( + load_command_address, load_command_info, LC_UUID, &uuid_command)) { + return false; + } + + uuid_.InitializeFromBytes(uuid_command.uuid); + return true; +} + +bool MachOImageReader::ReadSourceVersionCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + process_types::source_version_command source_version_command; + if (!ReadLoadCommand(load_command_address, + load_command_info, + LC_SOURCE_VERSION, + &source_version_command)) { + return false; + } + + source_version_ = source_version_command.version; + return true; +} + +bool MachOImageReader::ReadUnexpectedCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + LOG(WARNING) << "unexpected load command" << load_command_info; + return false; +} + +void MachOImageReader::InitializeSymbolTable() const { + DCHECK(symbol_table_initialized_.is_uninitialized()); + symbol_table_initialized_.set_invalid(); + + if (!symtab_command_) { + // It’s technically valid for there to be no LC_SYMTAB, and in that case, + // any symbol lookups should fail. Mark the symbol table as valid, and + // LookUpExternalDefinedSymbol() will understand what it means when this is + // valid but symbol_table_ is not present. + symbol_table_initialized_.set_valid(); + return; + } + + // Find the __LINKEDIT segment. Technically, the symbol table can be in any + // mapped segment, but by convention, it’s in the one named __LINKEDIT. + const MachOImageSegmentReader* linkedit_segment = + GetSegmentByName(SEG_LINKEDIT); + if (!linkedit_segment) { + LOG(WARNING) << "no " SEG_LINKEDIT " segment"; + return; + } + + symbol_table_.reset(new MachOImageSymbolTableReader()); + if (!symbol_table_->Initialize(process_reader_, + symtab_command_.get(), + dysymtab_command_.get(), + linkedit_segment, + module_info_)) { + symbol_table_.reset(); + return; + } + + symbol_table_initialized_.set_valid(); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h new file mode 100644 index 0000000..7047bc6 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h
@@ -0,0 +1,354 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_ + +#include <mach/mach.h> +#include <stdint.h> + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "snapshot/mac/process_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class MachOImageSegmentReader; +class MachOImageSymbolTableReader; +class ProcessReader; + +//! \brief A reader for Mach-O images mapped into another process. +//! +//! This class is capable of reading both 32-bit (`mach_header`/`MH_MAGIC`) and +//! 64-bit (`mach_header_64`/`MH_MAGIC_64`) images based on the bitness of the +//! remote process. +//! +//! \sa MachOImageAnnotationsReader +class MachOImageReader { + public: + MachOImageReader(); + ~MachOImageReader(); + + //! \brief Reads the Mach-O image file’s load commands from another process. + //! + //! This method must only be called once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] address The address, in the remote process’ address space, + //! where the `mach_header` or `mach_header_64` at the beginning of the + //! image to be read is located. This address can be determined by reading + //! the remote process’ dyld information (see + //! snapshot/mac/process_types/dyld_images.proctype). + //! \param[in] name The module’s name, a string to be used in logged messages. + //! This string is for diagnostic purposes and to relax otherwise strict + //! parsing rules for common modules with known defects. + //! + //! \return `true` if the image was read successfully, including all load + //! commands. `false` otherwise, with an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + mach_vm_address_t address, + const std::string& name); + + //! \brief Returns the Mach-O file type. + //! + //! This value comes from the `filetype` field of the `mach_header` or + //! `mach_header_64`. Common values include `MH_EXECUTE`, `MH_DYLIB`, + //! `MH_DYLINKER`, and `MH_BUNDLE`. + uint32_t FileType() const { return file_type_; } + + //! \brief Returns the Mach-O image’s load address. + //! + //! This is the value passed as \a address to Initialize(). + mach_vm_address_t Address() const { return address_; } + + //! \brief Returns the mapped size of the Mach-O image’s `__TEXT` segment. + //! + //! Note that this is returns only the size of the `__TEXT` segment, not of + //! any other segment. This is because the interface only allows one load + //! address and size to be reported, but Mach-O image files may consist of + //! multiple discontiguous segments. By convention, the `__TEXT` segment is + //! always mapped at the beginning of a Mach-O image file, and it is the most + //! useful for the expected intended purpose of collecting data to obtain + //! stack backtraces. The implementation insists during initialization that + //! the `__TEXT` segment be mapped at the beginning of the file. + //! + //! In practice, discontiguous segments are only found for images that have + //! loaded out of the dyld shared cache, but the `__TEXT` segment’s size is + //! returned for modules that loaded with contiguous segments as well for + //! consistency. + mach_vm_size_t Size() const { return size_; } + + //! \brief Returns the Mach-O image’s “slide,” the difference between its + //! actual load address and its preferred load address. + //! + //! “Slide” is computed by subtracting the `__TEXT` segment’s preferred load + //! address from its actual load address. It will be reported as a positive + //! offset when the actual load address is greater than the preferred load + //! address. The preferred load address is taken to be the segment’s reported + //! `vmaddr` value. + mach_vm_size_t Slide() const { return slide_; } + + //! \brief Obtain segment information by segment name. + //! + //! \param[in] segment_name The name of the segment to search for, for + //! example, `"__TEXT"`. + //! + //! \return A pointer to the segment information if it was found, or `nullptr` + //! if it was not found. The caller does not take ownership; the lifetime + //! of the returned object is scoped to the lifetime of this + //! MachOImageReader object. + const MachOImageSegmentReader* GetSegmentByName( + const std::string& segment_name) const; + + //! \brief Obtain section information by segment and section name. + //! + //! \param[in] segment_name The name of the segment to search for, for + //! example, `"__TEXT"`. + //! \param[in] section_name The name of the section within the segment to + //! search for, for example, `"__text"`. + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide” into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information if it was found, or `nullptr` + //! if it was not found. The caller does not take ownership; the lifetime + //! of the returned object is scoped to the lifetime of this + //! MachOImageReader object. + //! + //! No parameter is provided for the section’s size, because it can be + //! obtained from the returned process_types::section::size field. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide” that may have occurred when the image was loaded. Use + //! \a address to obtain the section’s actual load address. + const process_types::section* GetSectionByName( + const std::string& segment_name, + const std::string& section_name, + mach_vm_address_t* address) const; + + //! \brief Obtain section information by section index. + //! + //! \param[in] index The index of the section to return, in the order that it + //! appears in the segment load commands. This is a 1-based index, + //! matching the section number values used for `nlist::n_sect`. + //! \param[out] containing_segment The segment that contains the section. + //! This parameter can be `nullptr`. The caller does not take ownership; + //! the lifetime of the returned object is scoped to the lifetime of this + //! MachOImageReader object. + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide” into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information. If \a index is out of range, + //! logs a warning and returns `nullptr`. The caller does not take + //! ownership; the lifetime of the returned object is scoped to the + //! lifetime of this MachOImageReader object. + //! + //! No parameter is provided for the section’s size, because it can be + //! obtained from the returned process_types::section::size field. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide” that may have occurred when the image was loaded. Use + //! \a address to obtain the section’s actual load address. + //! \note Unlike MachOImageSegmentReader::GetSectionAtIndex(), this method + //! accepts out-of-range values for \a index, and returns `nullptr` + //! instead of aborting execution upon encountering an out-of-range value. + //! This is because a Mach-O image file’s symbol table refers to this + //! per-module section index, and an out-of-range index in that case + //! should be treated as a data error (where the data is beyond this + //! code’s control) and handled non-fatally by reporting the error to the + //! caller. + const process_types::section* GetSectionAtIndex( + size_t index, + const MachOImageSegmentReader** containing_segment, + mach_vm_address_t* address) const; + + //! \brief Looks up a symbol in the image’s symbol table. + //! + //! This method is capable of locating external defined symbols. Specifically, + //! this method can look up symbols that have these charcteristics: + //! - `N_STAB` (debugging) and `N_PEXT` (private external) must not be set. + //! - `N_EXT` (external) must be set. + //! - The type must be `N_ABS` (absolute) or `N_SECT` (defined in section). + //! + //! `N_INDR` (indirect), `N_UNDF` (undefined), and `N_PBUD` (prebound + //! undefined) symbols cannot be located through this mechanism. + //! + //! \param[in] name The name of the symbol to look up, “mangled” or + //! “decorated” appropriately. For example, use `"_main"` to look up the + //! symbol for the C `main()` function, and use `"__Z4Funcv"` to look up + //! the symbol for the C++ `Func()` function. Contrary to `dlsym()`, the + //! leading underscore must not be stripped when using this interface. + //! \param[out] value If the lookup was successful, this will be set to the + //! value of the symbol, adjusted for any “slide” as needed. The value can + //! be used as an address in the remote process’ address space where the + //! pointee of the symbol exists in memory. + //! + //! \return `true` if the symbol lookup was successful and the symbol was + //! found. `false` otherwise, including error conditions (for which a + //! warning message will be logged), modules without symbol tables, and + //! symbol names not found in the symbol table. + //! + //! \note Symbol values returned via this interface are adjusted for “slide” + //! as appropriate, in contrast to the underlying implementation, + //! MachOImageSymbolTableReader::LookUpExternalDefinedSymbol(). + //! + //! \warning Symbols that are resolved by running symbol resolvers + //! (`.symbol_resolver`) are not properly handled by this interface. The + //! address of the symbol resolver is returned because that’s what shows + //! up in the symbol table, rather than the effective address of the + //! resolved symbol as used by dyld after running the resolver. The only + //! way to detect this situation would be to read the `LC_DYLD_INFO` or + //! `LC_DYLD_INFO_ONLY` load command if present and looking for the + //! `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER` flag, but that would just be + //! able to detect symbols with a resolver, it would not be able to + //! resolve them from out-of-process, so it’s not currently done. + bool LookUpExternalDefinedSymbol(const std::string& name, + mach_vm_address_t* value) const; + + //! \brief Returns a Mach-O dylib image’s current version. + //! + //! This information comes from the `dylib_current_version` field of a dylib’s + //! `LC_ID_DYLIB` load command. For dylibs without this load command, `0` will + //! be returned. + //! + //! This method may only be called on Mach-O images for which FileType() + //! returns `MH_DYLIB`. + uint32_t DylibVersion() const; + + //! \brief Returns a Mach-O image’s source version. + //! + //! This information comes from a Mach-O image’s `LC_SOURCE_VERSION` load + //! command. For Mach-O images without this load command, `0` will be + //! returned. + uint64_t SourceVersion() const { return source_version_; } + + //! \brief Returns a Mach-O image’s UUID. + //! + //! This information comes from a Mach-O image’s `LC_UUID` load command. For + //! Mach-O images without this load command, a zeroed-out UUID value will be + //! returned. + // + // UUID is a name in this scope (referring to this method), so the parameter’s + // type needs to be qualified with |crashpad::|. + void UUID(crashpad::UUID* uuid) const; + + //! \brief Returns the dynamic linker’s pathname. + //! + //! The dynamic linker is normally /usr/lib/dyld. + //! + //! For executable images (those with file type `MH_EXECUTE`), this is the + //! name provided in the `LC_LOAD_DYLINKER` load command, if any. For dynamic + //! linker images (those with file type `MH_DYLINKER`), this is the name + //! provided in the `LC_ID_DYLINKER` load command. In other cases, this will + //! be empty. + std::string DylinkerName() const { return dylinker_name_; } + + //! \brief Obtains the module’s CrashpadInfo structure. + //! + //! \return `true` on success, `false` on failure. If the module does not have + //! a `__crashpad_info` section, this will return `false` without logging + //! any messages. Other failures will result in messages being logged. + bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const; + + private: + // A generic helper routine for the other Read*Command() methods. + template <typename T> + bool ReadLoadCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info, + uint32_t expected_load_command_id, + T* load_command); + + // The Read*Command() methods are subroutines called by Initialize(). They are + // responsible for reading a single load command. They may update the member + // fields of their MachOImageReader object. If they can’t make sense of a load + // command, they return false. + bool ReadSegmentCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadSymTabCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadDySymTabCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadIdDylibCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadDylinkerCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadUUIDCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadSourceVersionCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadUnexpectedCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + + // Performs deferred initialization of the symbol table. Because a module’s + // symbol table is often not needed, this is not handled in Initialize(), but + // is done lazily, on-demand as needed. + // + // symbol_table_initialized_ will be transitioned to the appropriate state. If + // initialization completes successfully, this will be the valid state. + // Otherwise, it will be left in the invalid state and a warning message will + // be logged. + // + // Note that if the object contains no symbol table, symbol_table_initialized_ + // will be set to the valid state, but symbol_table_ will be nullptr. + void InitializeSymbolTable() const; + + PointerVector<MachOImageSegmentReader> segments_; + std::map<std::string, size_t> segment_map_; + std::string module_name_; + std::string module_info_; + std::string dylinker_name_; + crashpad::UUID uuid_; + mach_vm_address_t address_; + mach_vm_size_t size_; + mach_vm_size_t slide_; + uint64_t source_version_; + scoped_ptr<process_types::symtab_command> symtab_command_; + scoped_ptr<process_types::dysymtab_command> dysymtab_command_; + + // symbol_table_ (and symbol_table_initialized_) are mutable in order to + // maintain LookUpExternalDefinedSymbol() as a const interface while allowing + // lazy initialization via InitializeSymbolTable(). This is logical + // const-ness, not physical const-ness. + mutable scoped_ptr<MachOImageSymbolTableReader> symbol_table_; + + scoped_ptr<process_types::dylib_command> id_dylib_command_; + ProcessReader* process_reader_; // weak + uint32_t file_type_; + InitializationStateDcheck initialized_; + + // symbol_table_initialized_ protects symbol_table_: symbol_table_ can only + // be used when symbol_table_initialized_ is valid, although + // symbol_table_initialized_ being valid doesn’t imply that symbol_table_ is + // set. symbol_table_initialized_ will be valid without symbol_table_ being + // set in modules that have no symbol table. + mutable InitializationState symbol_table_initialized_; + + DISALLOW_COPY_AND_ASSIGN(MachOImageReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc new file mode 100644 index 0000000..b2e5fed --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc
@@ -0,0 +1,645 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_reader.h" + +#include <AvailabilityMacros.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> +#include <mach-o/dyld_images.h> +#include <mach-o/getsect.h> +#include <mach-o/ldsyms.h> +#include <mach-o/loader.h> +#include <mach-o/nlist.h> +#include <stdint.h> + +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_types.h" +#include "test/mac/dyld.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/uuid.h" + +// This file is responsible for testing MachOImageReader, +// MachOImageSegmentReader, and MachOImageSymbolTableReader. + +namespace crashpad { +namespace test { +namespace { + +// Native types and constants, in cases where the 32-bit and 64-bit versions +// are different. +#if defined(ARCH_CPU_64_BITS) +using MachHeader = mach_header_64; +const uint32_t kMachMagic = MH_MAGIC_64; +using SegmentCommand = segment_command_64; +const uint32_t kSegmentCommand = LC_SEGMENT_64; +using Section = section_64; +using Nlist = nlist_64; +#else +using MachHeader = mach_header; +const uint32_t kMachMagic = MH_MAGIC; +using SegmentCommand = segment_command; +const uint32_t kSegmentCommand = LC_SEGMENT; +using Section = section; + +// This needs to be called “struct nlist” because “nlist” without the struct +// refers to the nlist() function. +using Nlist = struct nlist; +#endif + +#if defined(ARCH_CPU_X86_64) +const int kCPUType = CPU_TYPE_X86_64; +#elif defined(ARCH_CPU_X86) +const int kCPUType = CPU_TYPE_X86; +#endif + +// Verifies that |expect_section| and |actual_section| agree. +void ExpectSection(const Section* expect_section, + const process_types::section* actual_section) { + ASSERT_TRUE(expect_section); + ASSERT_TRUE(actual_section); + + EXPECT_EQ( + MachOImageSegmentReader::SectionNameString(expect_section->sectname), + MachOImageSegmentReader::SectionNameString(actual_section->sectname)); + EXPECT_EQ( + MachOImageSegmentReader::SegmentNameString(expect_section->segname), + MachOImageSegmentReader::SegmentNameString(actual_section->segname)); + EXPECT_EQ(expect_section->addr, actual_section->addr); + EXPECT_EQ(expect_section->size, actual_section->size); + EXPECT_EQ(expect_section->offset, actual_section->offset); + EXPECT_EQ(expect_section->align, actual_section->align); + EXPECT_EQ(expect_section->reloff, actual_section->reloff); + EXPECT_EQ(expect_section->nreloc, actual_section->nreloc); + EXPECT_EQ(expect_section->flags, actual_section->flags); + EXPECT_EQ(expect_section->reserved1, actual_section->reserved1); + EXPECT_EQ(expect_section->reserved2, actual_section->reserved2); +} + +// Verifies that |expect_segment| is a valid Mach-O segment load command for the +// current system by checking its |cmd| field. Then, verifies that the +// information in |actual_segment| matches that in |expect_segment|. The +// |segname|, |vmaddr|, |vmsize|, and |fileoff| fields are examined. Each +// section within the segment is also examined by calling ExpectSection(). +// Access to each section via both MachOImageSegmentReader::GetSectionByName() +// and MachOImageReader::GetSectionByName() is verified, expecting that each +// call produces the same section. Segment and section data addresses are +// verified against data obtained by calling getsegmentdata() and +// getsectiondata(). The segment is checked to make sure that it behaves +// correctly when attempting to look up a nonexistent section by name. +// |section_index| is used to track the last-used section index in an image on +// entry, and is reset to the last-used section index on return after the +// sections are processed. This is used to test that +// MachOImageReader::GetSectionAtIndex() returns the correct result. +void ExpectSegmentCommand(const SegmentCommand* expect_segment, + const MachHeader* expect_image, + const MachOImageSegmentReader* actual_segment, + const MachOImageReader* actual_image, + size_t* section_index) { + ASSERT_TRUE(expect_segment); + ASSERT_TRUE(actual_segment); + + EXPECT_EQ(kSegmentCommand, expect_segment->cmd); + + std::string segment_name = actual_segment->Name(); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(expect_segment->segname), + segment_name); + EXPECT_EQ(expect_segment->vmaddr, actual_segment->vmaddr()); + EXPECT_EQ(expect_segment->vmsize, actual_segment->vmsize()); + EXPECT_EQ(expect_segment->fileoff, actual_segment->fileoff()); + + if (actual_segment->SegmentSlides()) { + EXPECT_EQ(actual_segment->Address(), + actual_segment->vmaddr() + actual_image->Slide()); + + unsigned long expect_segment_size; + const uint8_t* expect_segment_data = getsegmentdata( + expect_image, segment_name.c_str(), &expect_segment_size); + mach_vm_address_t expect_segment_address = + reinterpret_cast<mach_vm_address_t>(expect_segment_data); + EXPECT_EQ(expect_segment_address, actual_segment->Address()); + EXPECT_EQ(expect_segment_size, actual_segment->vmsize()); + EXPECT_EQ(actual_segment->vmsize(), actual_segment->Size()); + } else { + // getsegmentdata() doesn’t return appropriate data for the __PAGEZERO + // segment because getsegmentdata() always adjusts for slide, but the + // __PAGEZERO segment never slides, it just grows. Skip the getsegmentdata() + // check for that segment according to the same rules that the kernel uses + // to identify __PAGEZERO. See 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c + // load_segment(). + EXPECT_EQ(actual_segment->Address(), actual_segment->vmaddr()); + EXPECT_EQ(actual_segment->vmsize() + actual_image->Slide(), + actual_segment->Size()); + } + + ASSERT_EQ(expect_segment->nsects, actual_segment->nsects()); + + // Make sure that the expected load command is big enough for the number of + // sections that it claims to have, and set up a pointer to its first section + // structure. + ASSERT_EQ(sizeof(*expect_segment) + expect_segment->nsects * sizeof(Section), + expect_segment->cmdsize); + const Section* expect_sections = + reinterpret_cast<const Section*>(&expect_segment[1]); + + for (size_t index = 0; index < actual_segment->nsects(); ++index) { + const Section* expect_section = &expect_sections[index]; + const process_types::section* actual_section = + actual_segment->GetSectionAtIndex(index, nullptr); + ASSERT_NO_FATAL_FAILURE( + ExpectSection(&expect_sections[index], actual_section)); + + // Make sure that the section is accessible by GetSectionByName as well. + std::string section_name = + MachOImageSegmentReader::SectionNameString(expect_section->sectname); + const process_types::section* actual_section_by_name = + actual_segment->GetSectionByName(section_name, nullptr); + EXPECT_EQ(actual_section, actual_section_by_name); + + // Make sure that the section is accessible by the parent MachOImageReader’s + // GetSectionByName. + mach_vm_address_t actual_section_address; + const process_types::section* actual_section_from_image_by_name = + actual_image->GetSectionByName( + segment_name, section_name, &actual_section_address); + EXPECT_EQ(actual_section, actual_section_from_image_by_name); + + if (actual_segment->SegmentSlides()) { + EXPECT_EQ(actual_section_address, + actual_section->addr + actual_image->Slide()); + + unsigned long expect_section_size; + const uint8_t* expect_section_data = getsectiondata(expect_image, + segment_name.c_str(), + section_name.c_str(), + &expect_section_size); + mach_vm_address_t expect_section_address = + reinterpret_cast<mach_vm_address_t>(expect_section_data); + EXPECT_EQ(expect_section_address, actual_section_address); + EXPECT_EQ(expect_section_size, actual_section->size); + } else { + EXPECT_EQ(actual_section_address, actual_section->addr); + } + + // Test the parent MachOImageReader’s GetSectionAtIndex as well. + const MachOImageSegmentReader* containing_segment; + mach_vm_address_t actual_section_address_at_index; + const process_types::section* actual_section_from_image_at_index = + actual_image->GetSectionAtIndex(++(*section_index), + &containing_segment, + &actual_section_address_at_index); + EXPECT_EQ(actual_section, actual_section_from_image_at_index); + EXPECT_EQ(actual_segment, containing_segment); + EXPECT_EQ(actual_section_address, actual_section_address_at_index); + } + + EXPECT_EQ(nullptr, + actual_segment->GetSectionByName("NoSuchSection", nullptr)); +} + +// Walks through the load commands of |expect_image|, finding all of the +// expected segment commands. For each expected segment command, calls +// actual_image->GetSegmentByName() to obtain an actual segment command, and +// calls ExpectSegmentCommand() to compare the expected and actual segments. A +// series of by-name lookups is also performed on the segment to ensure that it +// behaves correctly when attempting to look up segment and section names that +// are not present. |test_section_indices| should be true to test +// MachOImageReader::GetSectionAtIndex() using out-of-range section indices. +// This should be tested for at least one module, but it’s very noisy in terms +// of logging output, so this knob is provided to suppress this portion of the +// test when looping over all modules. +void ExpectSegmentCommands(const MachHeader* expect_image, + const MachOImageReader* actual_image, + bool test_section_index_bounds) { + ASSERT_TRUE(expect_image); + ASSERT_TRUE(actual_image); + + // &expect_image[1] points right past the end of the mach_header[_64], to the + // start of the load commands. + const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]); + uint32_t position = 0; + size_t section_index = 0; + for (uint32_t index = 0; index < expect_image->ncmds; ++index) { + ASSERT_LT(position, expect_image->sizeofcmds); + const load_command* command = + reinterpret_cast<const load_command*>(&commands_base[position]); + ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds); + if (command->cmd == kSegmentCommand) { + ASSERT_GE(command->cmdsize, sizeof(SegmentCommand)); + const SegmentCommand* expect_segment = + reinterpret_cast<const SegmentCommand*>(command); + std::string segment_name = + MachOImageSegmentReader::SegmentNameString(expect_segment->segname); + const MachOImageSegmentReader* actual_segment = + actual_image->GetSegmentByName(segment_name); + ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommand(expect_segment, + expect_image, + actual_segment, + actual_image, + §ion_index)); + } + position += command->cmdsize; + } + EXPECT_EQ(expect_image->sizeofcmds, position); + + if (test_section_index_bounds) { + // GetSectionAtIndex uses a 1-based index. Make sure that the range is + // correct. + EXPECT_EQ(nullptr, actual_image->GetSectionAtIndex(0, nullptr, nullptr)); + EXPECT_EQ( + nullptr, + actual_image->GetSectionAtIndex(section_index + 1, nullptr, nullptr)); + } + + // Make sure that by-name lookups for names that don’t exist work properly: + // they should return nullptr. + EXPECT_FALSE(actual_image->GetSegmentByName("NoSuchSegment")); + EXPECT_FALSE(actual_image->GetSectionByName( + "NoSuchSegment", "NoSuchSection", nullptr)); + + // Make sure that there’s a __TEXT segment so that this can do a valid test of + // a section that doesn’t exist within a segment that does. + EXPECT_TRUE(actual_image->GetSegmentByName(SEG_TEXT)); + EXPECT_FALSE( + actual_image->GetSectionByName(SEG_TEXT, "NoSuchSection", nullptr)); + + // Similarly, make sure that a section name that exists in one segment isn’t + // accidentally found during a lookup for that section in a different segment. + // + // If the image has no sections (unexpected), then any section lookup should + // fail, and these initial values of test_segment and test_section are fine + // for the EXPECT_FALSE checks on GetSectionByName() below. + std::string test_segment = SEG_DATA; + std::string test_section = SECT_TEXT; + + const process_types::section* section = + actual_image->GetSectionAtIndex(1, nullptr, nullptr); + if (section) { + // Use the name of the first section in the image as the section that + // shouldn’t appear in a different segment. If the first section is in the + // __TEXT segment (as it is normally), then a section by the same name + // wouldn’t be expected in the __DATA segment. But if the first section is + // in any other segment, then it wouldn’t be expected in the __TEXT segment. + if (MachOImageSegmentReader::SegmentNameString(section->segname) == + SEG_TEXT) { + test_segment = SEG_DATA; + } else { + test_segment = SEG_TEXT; + } + test_section = + MachOImageSegmentReader::SectionNameString(section->sectname); + + // It should be possible to look up the first section by name. + EXPECT_EQ(section, + actual_image->GetSectionByName( + section->segname, section->sectname, nullptr)); + } + EXPECT_FALSE( + actual_image->GetSectionByName("NoSuchSegment", test_section, nullptr)); + EXPECT_FALSE( + actual_image->GetSectionByName(test_segment, test_section, nullptr)); + + // The __LINKEDIT segment normally does exist but doesn’t have any sections. + EXPECT_FALSE( + actual_image->GetSectionByName(SEG_LINKEDIT, "NoSuchSection", nullptr)); + EXPECT_FALSE( + actual_image->GetSectionByName(SEG_LINKEDIT, SECT_TEXT, nullptr)); +} + +// In some cases, the expected slide value for an image is unknown, because no +// reasonable API to return it is provided. When this happens, use kSlideUnknown +// to avoid checking the actual slide value against anything. +const mach_vm_size_t kSlideUnknown = std::numeric_limits<mach_vm_size_t>::max(); + +// Verifies that |expect_image| is a vaild Mach-O header for the current system +// by checking its |magic| and |cputype| fields. Then, verifies that the +// information in |actual_image| matches that in |expect_image|. The |filetype| +// field is examined, actual_image->Address() is compared to +// |expect_image_address|, and actual_image->Slide() is compared to +// |expect_image_slide|, unless |expect_image_slide| is kSlideUnknown. Various +// other attributes of |actual_image| are sanity-checked depending on the Mach-O +// file type. Finally, ExpectSegmentCommands() is called to verify all that all +// of the segments match; |test_section_index_bounds| is used as an argument to +// that function. +void ExpectMachImage(const MachHeader* expect_image, + mach_vm_address_t expect_image_address, + mach_vm_size_t expect_image_slide, + const MachOImageReader* actual_image, + bool test_section_index_bounds) { + ASSERT_TRUE(expect_image); + ASSERT_TRUE(actual_image); + + EXPECT_EQ(kMachMagic, expect_image->magic); + EXPECT_EQ(kCPUType, expect_image->cputype); + + EXPECT_EQ(expect_image->filetype, actual_image->FileType()); + EXPECT_EQ(expect_image_address, actual_image->Address()); + if (expect_image_slide != kSlideUnknown) { + EXPECT_EQ(expect_image_slide, actual_image->Slide()); + } + + const MachOImageSegmentReader* actual_text_segment = + actual_image->GetSegmentByName(SEG_TEXT); + ASSERT_TRUE(actual_text_segment); + EXPECT_EQ(expect_image_address, actual_text_segment->Address()); + EXPECT_EQ(actual_image->Size(), actual_text_segment->Size()); + EXPECT_EQ(expect_image_address - actual_text_segment->vmaddr(), + actual_image->Slide()); + + uint32_t file_type = actual_image->FileType(); + EXPECT_TRUE(file_type == MH_EXECUTE || file_type == MH_DYLIB || + file_type == MH_DYLINKER || file_type == MH_BUNDLE); + + if (file_type == MH_EXECUTE || file_type == MH_DYLINKER) { + EXPECT_EQ("/usr/lib/dyld", actual_image->DylinkerName()); + } + + // For these, just don’t crash or anything. + if (file_type == MH_DYLIB) { + actual_image->DylibVersion(); + } + actual_image->SourceVersion(); + UUID uuid; + actual_image->UUID(&uuid); + + ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommands( + expect_image, actual_image, test_section_index_bounds)); +} + +// Verifies the symbol whose Nlist structure is |entry| and whose name is |name| +// matches the value of a symbol by the same name looked up in |actual_image|. +// MachOImageReader::LookUpExternalDefinedSymbol() is used for this purpose. +// Only external defined symbols are considered, other types of symbols are +// excluded because LookUpExternalDefinedSymbol() only deals with external +// defined symbols. +void ExpectSymbol(const Nlist* entry, + const char* name, + const MachOImageReader* actual_image) { + SCOPED_TRACE(name); + + uint32_t entry_type = entry->n_type & N_TYPE; + if ((entry->n_type & N_STAB) == 0 && (entry->n_type & N_PEXT) == 0 && + (entry_type == N_ABS || entry_type == N_SECT) && + (entry->n_type & N_EXT) == 1) { + mach_vm_address_t actual_address; + ASSERT_TRUE( + actual_image->LookUpExternalDefinedSymbol(name, &actual_address)); + + // Since the nlist interface was used to read the symbol, use it to compute + // the symbol address too. This isn’t perfect, and it should be possible in + // theory to use dlsym() to get the expected address of a symbol. In + // practice, dlsym() is difficult to use when only a MachHeader* is + // available as in this function, as opposed to a void* opaque handle. It is + // possible to get a void* handle by using dladdr() to find the file name + // corresponding to the MachHeader*, and using dlopen() again on that name, + // assuming it hasn’t changed on disk since being loaded. However, even with + // that being done, dlsym() can only deal with symbols whose names begin + // with an underscore (and requires that the leading underscore be trimmed). + // dlsym() will also return different addresses for symbols that are + // resolved via symbol resolver. + mach_vm_address_t expect_address = entry->n_value; + if (entry_type == N_SECT) { + EXPECT_GE(entry->n_sect, 1u); + expect_address += actual_image->Slide(); + } else { + EXPECT_EQ(NO_SECT, entry->n_sect); + } + + EXPECT_EQ(expect_address, actual_address); + } + + // You’d think that it might be a good idea to verify that if the conditions + // above weren’t met, that the symbol didn’t show up in actual_image’s symbol + // table at all. Unfortunately, it’s possible for the same name to show up as + // both an external defined symbol and as something else, so it’s not possible + // to verify this reliably. +} + +// Locates the symbol table in |expect_image| and verifies that all of the +// external defined symbols found there are also present and have the same +// values in |actual_image|. ExpectSymbol() is used to verify the actual symbol. +void ExpectSymbolTable(const MachHeader* expect_image, + const MachOImageReader* actual_image) { + // This intentionally consults only LC_SYMTAB and not LC_DYSYMTAB so that it + // can look at the larger set of all symbols. The actual implementation being + // tested is free to consult LC_DYSYMTAB, but that’s considered an + // optimization. It’s not necessary for the test, and it’s better for the test + // to expose bugs in that optimization rather than duplicate them. + const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]); + uint32_t position = 0; + const symtab_command* symtab = nullptr; + const SegmentCommand* linkedit = nullptr; + for (uint32_t index = 0; index < expect_image->ncmds; ++index) { + ASSERT_LT(position, expect_image->sizeofcmds); + const load_command* command = + reinterpret_cast<const load_command*>(&commands_base[position]); + ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds); + if (command->cmd == LC_SYMTAB) { + ASSERT_FALSE(symtab); + ASSERT_EQ(sizeof(symtab_command), command->cmdsize); + symtab = reinterpret_cast<const symtab_command*>(command); + } else if (command->cmd == kSegmentCommand) { + ASSERT_GE(command->cmdsize, sizeof(SegmentCommand)); + const SegmentCommand* segment = + reinterpret_cast<const SegmentCommand*>(command); + std::string segment_name = + MachOImageSegmentReader::SegmentNameString(segment->segname); + if (segment_name == SEG_LINKEDIT) { + ASSERT_FALSE(linkedit); + linkedit = segment; + } + } + position += command->cmdsize; + } + + if (symtab) { + ASSERT_TRUE(linkedit); + + const char* linkedit_base = + reinterpret_cast<const char*>(linkedit->vmaddr + actual_image->Slide()); + const Nlist* nlist = reinterpret_cast<const Nlist*>( + linkedit_base + symtab->symoff - linkedit->fileoff); + const char* strtab = linkedit_base + symtab->stroff - linkedit->fileoff; + + for (uint32_t index = 0; index < symtab->nsyms; ++index) { + const Nlist* entry = nlist + index; + const char* name = strtab + entry->n_un.n_strx; + ASSERT_NO_FATAL_FAILURE(ExpectSymbol(entry, name, actual_image)); + } + } + + mach_vm_address_t ignore; + EXPECT_FALSE(actual_image->LookUpExternalDefinedSymbol("", &ignore)); + EXPECT_FALSE( + actual_image->LookUpExternalDefinedSymbol("NoSuchSymbolName", &ignore)); + EXPECT_FALSE( + actual_image->LookUpExternalDefinedSymbol("_NoSuchSymbolName", &ignore)); +} + +TEST(MachOImageReader, Self_MainExecutable) { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + const MachHeader* mh_execute_header = + reinterpret_cast<MachHeader*>(dlsym(RTLD_MAIN_ONLY, MH_EXECUTE_SYM)); + ASSERT_NE(nullptr, mh_execute_header); + mach_vm_address_t mh_execute_header_address = + reinterpret_cast<mach_vm_address_t>(mh_execute_header); + + MachOImageReader image_reader; + ASSERT_TRUE(image_reader.Initialize( + &process_reader, mh_execute_header_address, "executable")); + + EXPECT_EQ(implicit_cast<uint32_t>(MH_EXECUTE), image_reader.FileType()); + + // The main executable has image index 0. + intptr_t image_slide = _dyld_get_image_vmaddr_slide(0); + + ASSERT_NO_FATAL_FAILURE(ExpectMachImage(mh_execute_header, + mh_execute_header_address, + image_slide, + &image_reader, + true)); + + // This symbol, __mh_execute_header, is known to exist in all MH_EXECUTE + // Mach-O files. + mach_vm_address_t symbol_address; + ASSERT_TRUE(image_reader.LookUpExternalDefinedSymbol(_MH_EXECUTE_SYM, + &symbol_address)); + EXPECT_EQ(mh_execute_header_address, symbol_address); + + ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mh_execute_header, &image_reader)); +} + +TEST(MachOImageReader, Self_DyldImages) { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + uint32_t count = _dyld_image_count(); + ASSERT_GE(count, 1u); + + size_t modules_with_crashpad_info = 0; + + for (uint32_t index = 0; index < count; ++index) { + const char* image_name = _dyld_get_image_name(index); + SCOPED_TRACE(base::StringPrintf("index %u, image %s", index, image_name)); + + // _dyld_get_image_header() is poorly-declared: it’s declared as returning + // const mach_header* in both 32-bit and 64-bit environments, but in the + // 64-bit environment, it should be const mach_header_64*. + const MachHeader* mach_header = + reinterpret_cast<const MachHeader*>(_dyld_get_image_header(index)); + mach_vm_address_t image_address = + reinterpret_cast<mach_vm_address_t>(mach_header); + + MachOImageReader image_reader; + ASSERT_TRUE( + image_reader.Initialize(&process_reader, image_address, image_name)); + + uint32_t file_type = image_reader.FileType(); + if (index == 0) { + EXPECT_EQ(implicit_cast<uint32_t>(MH_EXECUTE), file_type); + } else { + EXPECT_TRUE(file_type == MH_DYLIB || file_type == MH_BUNDLE); + } + + intptr_t image_slide = _dyld_get_image_vmaddr_slide(index); + ASSERT_NO_FATAL_FAILURE(ExpectMachImage( + mach_header, image_address, image_slide, &image_reader, false)); + + ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader)); + + process_types::CrashpadInfo crashpad_info; + if (image_reader.GetCrashpadInfo(&crashpad_info)) { + ++modules_with_crashpad_info; + } + } + + EXPECT_GE(modules_with_crashpad_info, 1u); + + // Now that all of the modules have been verified, make sure that dyld itself + // can be read properly too. + const struct dyld_all_image_infos* dyld_image_infos = + _dyld_get_all_image_infos(); + ASSERT_GE(dyld_image_infos->version, 1u); + EXPECT_EQ(count, dyld_image_infos->infoArrayCount); + + if (dyld_image_infos->version >= 2) { + SCOPED_TRACE("dyld"); + + // dyld_all_image_infos::dyldImageLoadAddress is poorly-declared too. + const MachHeader* mach_header = reinterpret_cast<const MachHeader*>( + dyld_image_infos->dyldImageLoadAddress); + mach_vm_address_t image_address = + reinterpret_cast<mach_vm_address_t>(mach_header); + + MachOImageReader image_reader; + ASSERT_TRUE( + image_reader.Initialize(&process_reader, image_address, "dyld")); + + EXPECT_EQ(implicit_cast<uint32_t>(MH_DYLINKER), image_reader.FileType()); + + // There’s no good API to get dyld’s slide, so don’t bother checking it. + ASSERT_NO_FATAL_FAILURE(ExpectMachImage( + mach_header, image_address, kSlideUnknown, &image_reader, false)); + + ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader)); + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + // If dyld is new enough to record UUIDs, check the UUID of any module that + // it says has one. Note that dyld doesn’t record UUIDs of anything that + // loaded out of the shared cache, but it should at least have a UUID for the + // main executable if it has one. + if (dyld_image_infos->version >= 8 && dyld_image_infos->uuidArray) { + for (uint32_t index = 0; + index < dyld_image_infos->uuidArrayCount; + ++index) { + const dyld_uuid_info* dyld_image = &dyld_image_infos->uuidArray[index]; + SCOPED_TRACE(base::StringPrintf("uuid index %u", index)); + + // dyld_uuid_info::imageLoadAddress is poorly-declared too. + const MachHeader* mach_header = + reinterpret_cast<const MachHeader*>(dyld_image->imageLoadAddress); + mach_vm_address_t image_address = + reinterpret_cast<mach_vm_address_t>(mach_header); + + MachOImageReader image_reader; + ASSERT_TRUE( + image_reader.Initialize(&process_reader, image_address, "uuid")); + + // There’s no good way to get the image’s slide here, although the image + // should have already been checked along with its slide above, in the + // loop through all images. + ExpectMachImage( + mach_header, image_address, kSlideUnknown, &image_reader, false); + + UUID expected_uuid; + expected_uuid.InitializeFromBytes(dyld_image->imageUUID); + UUID actual_uuid; + image_reader.UUID(&actual_uuid); + EXPECT_EQ(expected_uuid, actual_uuid); + } + } +#endif +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc new file mode 100644 index 0000000..9b07e904 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc
@@ -0,0 +1,308 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_segment_reader.h" + +#include <mach-o/loader.h> + +#include <utility> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/process_reader.h" +#include "util/mac/checked_mach_address_range.h" +#include "util/mac/mac_util.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { + +namespace { + +std::string SizeLimitedCString(const char* c_string, size_t max_length) { + return std::string(c_string, strnlen(c_string, max_length)); +} + +} // namespace + +MachOImageSegmentReader::MachOImageSegmentReader() + : segment_command_(), + sections_(), + section_map_(), + slide_(0), + initialized_(), + initialized_slide_() { +} + +MachOImageSegmentReader::~MachOImageSegmentReader() { +} + +bool MachOImageSegmentReader::Initialize(ProcessReader* process_reader, + mach_vm_address_t load_command_address, + const std::string& load_command_info, + const std::string& module_name, + uint32_t file_type) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!segment_command_.Read(process_reader, load_command_address)) { + LOG(WARNING) << "could not read segment_command" << load_command_info; + return false; + } + + const uint32_t kExpectedSegmentCommand = + process_reader->Is64Bit() ? LC_SEGMENT_64 : LC_SEGMENT; + DCHECK_EQ(segment_command_.cmd, kExpectedSegmentCommand); + DCHECK_GE(segment_command_.cmdsize, segment_command_.Size()); + const size_t kSectionStructSize = + process_types::section::ExpectedSize(process_reader); + const size_t kRequiredSize = + segment_command_.Size() + segment_command_.nsects * kSectionStructSize; + if (segment_command_.cmdsize < kRequiredSize) { + LOG(WARNING) << base::StringPrintf( + "segment command cmdsize 0x%x insufficient for %u " + "section%s (0x%zx)", + segment_command_.cmdsize, + segment_command_.nsects, + segment_command_.nsects == 1 ? "" : "s", + kRequiredSize) << load_command_info; + return false; + } + + std::string segment_name = NameInternal(); + std::string segment_info = base::StringPrintf( + ", segment %s%s", segment_name.c_str(), load_command_info.c_str()); + + // This checks the unslid segment range. The slid range (as loaded into + // memory) will be checked later by MachOImageReader. + CheckedMachAddressRange segment_range(process_reader->Is64Bit(), + segment_command_.vmaddr, + segment_command_.vmsize); + if (!segment_range.IsValid()) { + LOG(WARNING) << base::StringPrintf("invalid segment range 0x%llx + 0x%llx", + segment_command_.vmaddr, + segment_command_.vmsize) << segment_info; + return false; + } + + sections_.resize(segment_command_.nsects); + if (!sections_.empty() && + !process_types::section::ReadArrayInto( + process_reader, + load_command_address + segment_command_.Size(), + segment_command_.nsects, + §ions_[0])) { + LOG(WARNING) << "could not read sections" << segment_info; + return false; + } + + for (size_t section_index = 0; + section_index < sections_.size(); + ++section_index) { + const process_types::section& section = sections_[section_index]; + std::string section_segment_name = SegmentNameString(section.segname); + std::string section_name = SectionNameString(section.sectname); + std::string section_full_name = + SegmentAndSectionNameString(section.segname, section.sectname); + + std::string section_info = base::StringPrintf(", section %s %zu/%zu%s", + section_full_name.c_str(), + section_index, + sections_.size(), + load_command_info.c_str()); + + if (section_segment_name != segment_name) { + // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted + // incorrectly on OS X 10.10 and 10.11. They have a single __TEXT segment, + // but one of the sections within it claims to belong to the __LD segment. + // This mismatch shouldn’t happen. This errant section also has the + // S_ATTR_DEBUG flag set, which shouldn’t happen unless all of the other + // sections in the segment also have this bit set (they don’t). These odd + // sections are reminiscent of unwind information stored in MH_OBJECT + // images, although cl_kernels images claim to be MH_BUNDLE. Because at + // least one cl_kernels module will commonly be found in a process, and + // sometimes more will be, tolerate this quirk. + // + // https://openradar.appspot.com/20239912 + bool ok = false; + if (file_type == MH_BUNDLE && module_name == "cl_kernels") { + int mac_os_x_minor_version = MacOSXMinorVersion(); + if ((mac_os_x_minor_version == 10 || mac_os_x_minor_version == 11) && + segment_name == SEG_TEXT && + section_segment_name == "__LD" && + section_name == "__compact_unwind" && + (section.flags & S_ATTR_DEBUG)) { + ok = true; + } + } + + if (!ok) { + LOG(WARNING) << "section.segname incorrect in segment " << segment_name + << section_info; + return false; + } + } + + CheckedMachAddressRange section_range( + process_reader->Is64Bit(), section.addr, section.size); + if (!section_range.IsValid()) { + LOG(WARNING) << base::StringPrintf( + "invalid section range 0x%llx + 0x%llx", + section.addr, + section.size) << section_info; + return false; + } + + if (!segment_range.ContainsRange(section_range)) { + LOG(WARNING) << base::StringPrintf( + "section at 0x%llx + 0x%llx outside of segment at " + "0x%llx + 0x%llx", + section.addr, + section.size, + segment_command_.vmaddr, + segment_command_.vmsize) << section_info; + return false; + } + + uint32_t section_type = (section.flags & SECTION_TYPE); + bool zero_fill = section_type == S_ZEROFILL || + section_type == S_GB_ZEROFILL || + section_type == S_THREAD_LOCAL_ZEROFILL; + + // Zero-fill section types aren’t mapped from the file, so their |offset| + // fields are irrelevant and are typically 0. + if (!zero_fill && + section.offset - segment_command_.fileoff != + section.addr - segment_command_.vmaddr) { + LOG(WARNING) << base::StringPrintf( + "section type 0x%x at 0x%llx has unexpected offset " + "0x%x in segment at 0x%llx with offset 0x%llx", + section_type, + section.addr, + section.offset, + segment_command_.vmaddr, + segment_command_.fileoff) << section_info; + return false; + } + + const auto insert_result = + section_map_.insert(std::make_pair(section_name, section_index)); + if (!insert_result.second) { + LOG(WARNING) << base::StringPrintf("duplicate section name at %zu", + insert_result.first->second) + << section_info; + return false; + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +std::string MachOImageSegmentReader::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return NameInternal(); +} + +mach_vm_address_t MachOImageSegmentReader::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); + return vmaddr() + (SegmentSlides() ? slide_ : 0); +} + +mach_vm_size_t MachOImageSegmentReader::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); + return vmsize() + (SegmentSlides() ? 0 : slide_); +} + +const process_types::section* MachOImageSegmentReader::GetSectionByName( + const std::string& section_name, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const auto& iterator = section_map_.find(section_name); + if (iterator == section_map_.end()) { + return nullptr; + } + + return GetSectionAtIndex(iterator->second, address); +} + +const process_types::section* MachOImageSegmentReader::GetSectionAtIndex( + size_t index, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + CHECK_LT(index, sections_.size()); + + const process_types::section* section = §ions_[index]; + + if (address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); + *address = section->addr + (SegmentSlides() ? slide_ : 0); + } + + return section; +} + +bool MachOImageSegmentReader::SegmentSlides() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // These are the same rules that the kernel uses to identify __PAGEZERO. See + // 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c load_segment(). + return !(segment_command_.vmaddr == 0 && segment_command_.filesize == 0 && + segment_command_.vmsize != 0 && + (segment_command_.initprot & VM_PROT_ALL) == VM_PROT_NONE && + (segment_command_.maxprot & VM_PROT_ALL) == VM_PROT_NONE); +} + +// static +std::string MachOImageSegmentReader::SegmentNameString( + const char* segment_name_c) { + // This is used to interpret the segname field of both the segment_command and + // section structures, so be sure that they’re identical. + static_assert(sizeof(process_types::segment_command::segname) == + sizeof(process_types::section::segname), + "sizes must be equal"); + + return SizeLimitedCString(segment_name_c, + sizeof(process_types::segment_command::segname)); +} + +// static +std::string MachOImageSegmentReader::SectionNameString( + const char* section_name_c) { + return SizeLimitedCString(section_name_c, + sizeof(process_types::section::sectname)); +} + +// static +std::string MachOImageSegmentReader::SegmentAndSectionNameString( + const char* segment_name_c, + const char* section_name_c) { + return base::StringPrintf("%s,%s", + SegmentNameString(segment_name_c).c_str(), + SectionNameString(section_name_c).c_str()); +} + +std::string MachOImageSegmentReader::NameInternal() const { + return SegmentNameString(segment_command_.segname); +} + +void MachOImageSegmentReader::SetSlide(mach_vm_size_t slide) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + INITIALIZATION_STATE_SET_INITIALIZING(initialized_slide_); + slide_ = slide; + INITIALIZATION_STATE_SET_VALID(initialized_slide_); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h new file mode 100644 index 0000000..4e6a97e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h
@@ -0,0 +1,262 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_ + +#include <mach/mach.h> +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/mac/process_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O +//! images mapped into another process. +//! +//! This class is capable of reading both `LC_SEGMENT` and `LC_SEGMENT_64` based +//! on the bitness of the remote process. +//! +//! A MachOImageSegmentReader will normally be instantiated by a +//! MachOImageReader. +class MachOImageSegmentReader { + public: + MachOImageSegmentReader(); + ~MachOImageSegmentReader(); + + //! \brief Reads the segment load command from another process. + //! + //! This method must only be called once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] load_command_address The address, in the remote process’ + //! address space, where the `LC_SEGMENT` or `LC_SEGMENT_64` load command + //! to be read is located. This address is determined by a Mach-O image + //! reader, such as MachOImageReader, as it walks Mach-O load commands. + //! \param[in] load_command_info A string to be used in logged messages. This + //! string is for diagnostic purposes only, and may be empty. + //! \param[in] module_name The path used to load the module. This string is + //! used to relax otherwise strict parsing rules for common modules with + //! known defects. + //! \param[in] file_type The module’s Mach-O file type. This is used to relax + //! otherwise strict parsing rules for common modules with known defects. + //! + //! \return `true` if the load command was read successfully. `false` + //! otherwise, with an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + mach_vm_address_t load_command_address, + const std::string& load_command_info, + const std::string& module_name, + uint32_t file_type); + + //! \brief Sets the image’s slide value. + //! + //! This method must only be called once on an object, after Initialize() is + //! called successfully. It must be called before Address(), Size(), + //! GetSectionByName(), or GetSectionAtIndex() can be called. + //! + //! This method is provided because slide is a property of the image that + //! cannot be determined until at least some segments have been read. As such, + //! it is not necessarily known at the time that Initialize() is called. + void SetSlide(mach_vm_size_t slide); + + //! \brief Returns the segment’s name. + //! + //! The segment’s name is taken from the load command’s `segname` field. + //! Common segment names are `"__TEXT"`, `"__DATA"`, and `"__LINKEDIT"`. + //! Symbolic constants for these common names are defined in + //! `<mach-o/loader.h>`. + std::string Name() const; + + //! \return The segment’s actual load address in memory, adjusted for any + //! “slide”. + //! + //! \note For the segment’s preferred load address, not adjusted for slide, + //! use vmaddr(). + mach_vm_address_t Address() const; + + //! \return The segment’s actual size address in memory, adjusted for any + //! growth in the case of a nonsliding segment. + //! + //! \note For the segment’s preferred size, not adjusted for growth, use + //! vmsize(). + mach_vm_address_t Size() const; + + //! \brief The segment’s preferred load address. + //! + //! \return The segment’s preferred load address as stored in the Mach-O file. + //! + //! \note This value is not adjusted for any “slide” that may have occurred + //! when the image was loaded. Use Address() for a value adjusted for + //! slide. + //! + //! \sa MachOImageReader::GetSegmentByName() + mach_vm_address_t vmaddr() const { return segment_command_.vmaddr; } + + //! \brief Returns the segment’s size as mapped into memory. + //! + //! \note For non-sliding segments, this value is not adjusted for any growth + //! that may have occurred when the image was loaded. Use Size() for a + //! value adjusted for growth. + mach_vm_size_t vmsize() const { return segment_command_.vmsize; } + + //! \brief Returns the file offset of the mapped segment in the file from + //! which it was mapped. + //! + //! The file offset is the difference between the beginning of the + //! `mach_header` or `mach_header_64` and the beginning of the segment’s + //! mapped region. For segments that are not mapped from a file (such as + //! `__PAGEZERO` segments), this will be `0`. + mach_vm_size_t fileoff() const { return segment_command_.fileoff; } + + //! \brief Returns the number of sections in the segment. + //! + //! This will return `0` for a segment without any sections, typical for + //! `__PAGEZERO` and `__LINKEDIT` segments. + //! + //! Although the Mach-O file format uses a `uint32_t` for this field, there is + //! an overall limit of 255 sections in an entire Mach-O image file (not just + //! in a single segment) imposed by the symbol table format. Symbols will not + //! be able to reference anything in a section beyond the first 255 in a + //! Mach-O image file. + uint32_t nsects() const { return segment_command_.nsects; } + + //! \brief Obtain section information by section name. + //! + //! \param[in] section_name The name of the section to search for, without the + //! leading segment name. For example, use `"__text"`, not + //! `"__TEXT,__text"` or `"__TEXT.__text"`. + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide” into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information if it was found, or `nullptr` + //! if it was not found. The caller does not take ownership; the lifetime + //! of the returned object is scoped to the lifetime of this + //! MachOImageSegmentReader object. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide” that may have occurred when the image was loaded. + //! + //! \sa MachOImageReader::GetSectionByName() + const process_types::section* GetSectionByName( + const std::string& section_name, + mach_vm_address_t* address) const; + + //! \brief Obtain section information by section index. + //! + //! \param[in] index The index of the section to return, in the order that it + //! appears in the segment load command. Unlike + //! MachOImageReader::GetSectionAtIndex(), this is a 0-based index. This + //! parameter must be in the range of valid indices aas reported by + //! nsects(). + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide” into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information. If \a index is out of range, + //! execution is aborted. The caller does not take ownership; the + //! lifetime of the returned object is scoped to the lifetime of this + //! MachOImageSegmentReader object. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide” that may have occurred when the image was loaded. + //! \note Unlike MachOImageReader::GetSectionAtIndex(), this method does not + //! accept out-of-range values for \a index, and aborts execution instead + //! of returning `nullptr` upon encountering an out-of-range value. This + //! is because this method is expected to be used in a loop that can be + //! limited to nsects() iterations, so an out-of-range error can be + //! treated more harshly as a logic error, as opposed to a data error. + //! + //! \sa MachOImageReader::GetSectionAtIndex() + const process_types::section* GetSectionAtIndex( + size_t index, + mach_vm_address_t* address) const; + + //! Returns whether the segment slides. + //! + //! Most segments slide, but the `__PAGEZERO` segment does not, it grows + //! instead. This method identifies non-sliding segments in the same way that + //! the kernel does. + bool SegmentSlides() const; + + //! \brief Returns a segment name string. + //! + //! Segment names may be 16 characters long, and are not necessarily + //! `NUL`-terminated. This function will return a segment name based on up to + //! the first 16 characters found at \a segment_name_c. + static std::string SegmentNameString(const char* segment_name_c); + + //! \brief Returns a section name string. + //! + //! Section names may be 16 characters long, and are not necessarily + //! `NUL`-terminated. This function will return a section name based on up to + //! the first 16 characters found at \a section_name_c. + static std::string SectionNameString(const char* section_name_c); + + //! \brief Returns a segment and section name string. + //! + //! A segment and section name string is composed of a segment name string + //! (see SegmentNameString()) and a section name string (see + //! SectionNameString()) separated by a comma. An example is + //! `"__TEXT,__text"`. + static std::string SegmentAndSectionNameString(const char* segment_name_c, + const char* section_name_c); + + private: + //! \brief The internal implementation of Name(). + //! + //! This is identical to Name() but does not perform the + //! InitializationStateDcheck check. It may be called during initialization + //! provided that the caller only does so after segment_command_ has been + //! read successfully. + std::string NameInternal() const; + + // The segment command data read from the remote process. + process_types::segment_command segment_command_; + + // Section structures read from the remote process in the order that they are + // given in the remote process. + std::vector<process_types::section> sections_; + + // Maps section names to indices into the sections_ vector. + std::map<std::string, size_t> section_map_; + + // The image’s slide. Note that the segment’s slide may be 0 and not the value + // of the image’s slide if SegmentSlides() is false. In that case, the + // segment is extended instead of slid, so its size as loaded will be + // increased by this value. + mach_vm_size_t slide_; + + InitializationStateDcheck initialized_; + InitializationStateDcheck initialized_slide_; + + DISALLOW_COPY_AND_ASSIGN(MachOImageSegmentReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc new file mode 100644 index 0000000..bf375af --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc
@@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_segment_reader.h" + +#include <mach-o/loader.h> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +// Most of MachOImageSegmentReader is tested as part of MachOImageReader, which +// depends on MachOImageSegmentReader to provide major portions of its +// functionality. Because MachOImageSegmentReader is difficult to use except by +// a Mach-O load command reader such as MachOImageReader, these portions +// of MachOImageSegmentReader are not tested independently. +// +// The tests here exercise the portions of MachOImageSegmentReader that are +// exposed and independently useful. + +TEST(MachOImageSegmentReader, SegmentNameString) { + // The output value should be a string of up to 16 characters, even if the + // input value is not NUL-terminated within 16 characters. + EXPECT_EQ("__TEXT", MachOImageSegmentReader::SegmentNameString("__TEXT")); + EXPECT_EQ("__OVER", MachOImageSegmentReader::SegmentNameString("__OVER")); + EXPECT_EQ("", MachOImageSegmentReader::SegmentNameString("")); + EXPECT_EQ("p", MachOImageSegmentReader::SegmentNameString("p")); + EXPECT_EQ("NoUnderChar", + MachOImageSegmentReader::SegmentNameString("NoUnderChar")); + EXPECT_EQ("0123456789abcde", + MachOImageSegmentReader::SegmentNameString("0123456789abcde")); + EXPECT_EQ("0123456789abcdef", + MachOImageSegmentReader::SegmentNameString("0123456789abcdef")); + EXPECT_EQ("gfedcba987654321", + MachOImageSegmentReader::SegmentNameString("gfedcba9876543210")); + EXPECT_EQ("hgfedcba98765432", + MachOImageSegmentReader::SegmentNameString("hgfedcba9876543210")); + + // Segment names defined in <mach-o/loader.h>. All of these should come + // through SegmentNameString() cleanly and without truncation. + const char* kSegmentTestData[] = { + SEG_TEXT, + SEG_DATA, + SEG_OBJC, + SEG_ICON, + SEG_LINKEDIT, + SEG_UNIXSTACK, + SEG_IMPORT, + }; + + for (size_t index = 0; index < arraysize(kSegmentTestData); ++index) { + EXPECT_EQ( + kSegmentTestData[index], + MachOImageSegmentReader::SegmentNameString(kSegmentTestData[index])) + << base::StringPrintf("index %zu", index); + } +} + +TEST(MachOImageSegmentReader, SectionNameString) { + // The output value should be a string of up to 16 characters, even if the + // input value is not NUL-terminated within 16 characters. + EXPECT_EQ("__text", MachOImageSegmentReader::SectionNameString("__text")); + EXPECT_EQ("__over", MachOImageSegmentReader::SectionNameString("__over")); + EXPECT_EQ("", MachOImageSegmentReader::SectionNameString("")); + EXPECT_EQ("p", MachOImageSegmentReader::SectionNameString("p")); + EXPECT_EQ("NoUnderChar", + MachOImageSegmentReader::SectionNameString("NoUnderChar")); + EXPECT_EQ("0123456789abcde", + MachOImageSegmentReader::SectionNameString("0123456789abcde")); + EXPECT_EQ("0123456789abcdef", + MachOImageSegmentReader::SectionNameString("0123456789abcdef")); + EXPECT_EQ("gfedcba987654321", + MachOImageSegmentReader::SectionNameString("gfedcba9876543210")); + EXPECT_EQ("hgfedcba98765432", + MachOImageSegmentReader::SectionNameString("hgfedcba9876543210")); + + // Section names defined in <mach-o/loader.h>. All of these should come + // through SectionNameString() cleanly and without truncation. + const char* kSectionTestData[] = { + SECT_TEXT, + SECT_FVMLIB_INIT0, + SECT_FVMLIB_INIT1, + SECT_DATA, + SECT_BSS, + SECT_COMMON, + SECT_OBJC_SYMBOLS, + SECT_OBJC_MODULES, + SECT_OBJC_STRINGS, + SECT_OBJC_REFS, + SECT_ICON_HEADER, + SECT_ICON_TIFF, + }; + + for (size_t index = 0; index < arraysize(kSectionTestData); ++index) { + EXPECT_EQ( + kSectionTestData[index], + MachOImageSegmentReader::SectionNameString(kSectionTestData[index])) + << base::StringPrintf("index %zu", index); + } +} + +TEST(MachOImageSegmentReader, SegmentAndSectionNameString) { + struct SegmentAndSectionTestData { + const char* segment; + const char* section; + const char* output; + }; + const SegmentAndSectionTestData kSegmentAndSectionTestData[] = { + {"segment", "section", "segment,section"}, + {"Segment", "Section", "Segment,Section"}, + {"SEGMENT", "SECTION", "SEGMENT,SECTION"}, + {"__TEXT", "__plain", "__TEXT,__plain"}, + {"__TEXT", "poetry", "__TEXT,poetry"}, + {"__TEXT", "Prose", "__TEXT,Prose"}, + {"__PLAIN", "__text", "__PLAIN,__text"}, + {"rich", "__text", "rich,__text"}, + {"segment", "", "segment,"}, + {"", "section", ",section"}, + {"", "", ","}, + {"0123456789abcdef", "section", "0123456789abcdef,section"}, + {"gfedcba9876543210", "section", "gfedcba987654321,section"}, + {"0123456789abcdef", "", "0123456789abcdef,"}, + {"gfedcba9876543210", "", "gfedcba987654321,"}, + {"segment", "0123456789abcdef", "segment,0123456789abcdef"}, + {"segment", "gfedcba9876543210", "segment,gfedcba987654321"}, + {"", "0123456789abcdef", ",0123456789abcdef"}, + {"", "gfedcba9876543210", ",gfedcba987654321"}, + {"0123456789abcdef", + "0123456789abcdef", + "0123456789abcdef,0123456789abcdef"}, + {"gfedcba9876543210", + "gfedcba9876543210", + "gfedcba987654321,gfedcba987654321"}, + + // Sections defined in <mach-o/loader.h>. All of these should come through + // SegmentAndSectionNameString() cleanly and without truncation. + {SEG_TEXT, SECT_TEXT, "__TEXT,__text"}, + {SEG_TEXT, SECT_FVMLIB_INIT0, "__TEXT,__fvmlib_init0"}, + {SEG_TEXT, SECT_FVMLIB_INIT1, "__TEXT,__fvmlib_init1"}, + {SEG_DATA, SECT_DATA, "__DATA,__data"}, + {SEG_DATA, SECT_BSS, "__DATA,__bss"}, + {SEG_DATA, SECT_COMMON, "__DATA,__common"}, + {SEG_OBJC, SECT_OBJC_SYMBOLS, "__OBJC,__symbol_table"}, + {SEG_OBJC, SECT_OBJC_MODULES, "__OBJC,__module_info"}, + {SEG_OBJC, SECT_OBJC_STRINGS, "__OBJC,__selector_strs"}, + {SEG_OBJC, SECT_OBJC_REFS, "__OBJC,__selector_refs"}, + {SEG_ICON, SECT_ICON_HEADER, "__ICON,__header"}, + {SEG_ICON, SECT_ICON_TIFF, "__ICON,__tiff"}, + + // These segments don’t normally have sections, but the above group tested + // the known segment names for segments that do normally have sections. + // This group does the same for segments that normally don’t. + {SEG_LINKEDIT, "", "__LINKEDIT,"}, + {SEG_UNIXSTACK, "", "__UNIXSTACK,"}, + {SEG_IMPORT, "", "__IMPORT,"}, + }; + + for (size_t index = 0; index < arraysize(kSegmentAndSectionTestData); + ++index) { + const SegmentAndSectionTestData& test = kSegmentAndSectionTestData[index]; + EXPECT_EQ(test.output, + MachOImageSegmentReader::SegmentAndSectionNameString( + test.segment, test.section)) + << base::StringPrintf("index %zu, segment %s, section %s", + index, + test.segment, + test.section); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc new file mode 100644 index 0000000..949e460 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc
@@ -0,0 +1,292 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/mach_o_image_symbol_table_reader.h" + +#include <mach-o/loader.h> +#include <mach-o/nlist.h> + +#include <utility> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringprintf.h" +#include "util/mac/checked_mach_address_range.h" +#include "util/mach/task_memory.h" + +namespace crashpad { + +namespace internal { + +//! \brief The internal implementation for MachOImageSymbolTableReader. +//! +//! Initialization is broken into more than one function that needs to share +//! data, so member variables are used. However, much of this data is irrelevant +//! after initialization is completed, so rather than doing it in +//! MachOImageSymbolTableReader, it’s handled by this class, which is a “friend” +//! of MachOImageSymbolTableReader. +class MachOImageSymbolTableReaderInitializer { + public: + MachOImageSymbolTableReaderInitializer( + ProcessReader* process_reader, + const MachOImageSegmentReader* linkedit_segment, + const std::string& module_info) + : module_info_(module_info), + linkedit_range_(), + process_reader_(process_reader), + linkedit_segment_(linkedit_segment) { + linkedit_range_.SetRange(process_reader_->Is64Bit(), + linkedit_segment->Address(), + linkedit_segment->Size()); + DCHECK(linkedit_range_.IsValid()); + } + + ~MachOImageSymbolTableReaderInitializer() {} + + //! \brief Reads the symbol table from another process. + //! + //! \sa MachOImageSymbolTableReader::Initialize() + bool Initialize(const process_types::symtab_command* symtab_command, + const process_types::dysymtab_command* dysymtab_command, + MachOImageSymbolTableReader::SymbolInformationMap* + external_defined_symbols) { + mach_vm_address_t symtab_address = + AddressForLinkEditComponent(symtab_command->symoff); + uint32_t symbol_count = symtab_command->nsyms; + size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_); + mach_vm_size_t symtab_size = symbol_count * nlist_size; + if (!IsInLinkEditSegment(symtab_address, symtab_size, "symtab")) { + return false; + } + + // If a dysymtab is present, use it to filter the symtab for just the + // portion used for extdefsym. If no dysymtab is present, the entire symtab + // will need to be consulted. + uint32_t skip_count = 0; + if (dysymtab_command) { + if (dysymtab_command->iextdefsym >= symtab_command->nsyms || + dysymtab_command->iextdefsym + dysymtab_command->nextdefsym > + symtab_command->nsyms) { + LOG(WARNING) << base::StringPrintf( + "dysymtab extdefsym %u + %u > symtab nsyms %u", + dysymtab_command->iextdefsym, + dysymtab_command->nextdefsym, + symtab_command->nsyms) << module_info_; + return false; + } + + skip_count = dysymtab_command->iextdefsym; + mach_vm_size_t skip_size = skip_count * nlist_size; + symtab_address += skip_size; + symtab_size -= skip_size; + symbol_count = dysymtab_command->nextdefsym; + } + + mach_vm_address_t strtab_address = + AddressForLinkEditComponent(symtab_command->stroff); + mach_vm_size_t strtab_size = symtab_command->strsize; + if (!IsInLinkEditSegment(strtab_address, strtab_size, "strtab")) { + return false; + } + + scoped_ptr<process_types::nlist[]> symbols( + new process_types::nlist[symtab_command->nsyms]); + if (!process_types::nlist::ReadArrayInto( + process_reader_, symtab_address, symbol_count, &symbols[0])) { + LOG(WARNING) << "could not read symbol table" << module_info_; + return false; + } + + scoped_ptr<TaskMemory::MappedMemory> string_table; + for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) { + const process_types::nlist& symbol = symbols[symbol_index]; + std::string symbol_info = base::StringPrintf(", symbol index %zu%s", + skip_count + symbol_index, + module_info_.c_str()); + bool valid_symbol = true; + if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 && + (symbol.n_type & N_EXT)) { + uint8_t symbol_type = symbol.n_type & N_TYPE; + if (symbol_type == N_ABS || symbol_type == N_SECT) { + if (symbol.n_strx >= strtab_size) { + LOG(WARNING) << base::StringPrintf( + "string at 0x%x out of bounds (0x%llx)", + symbol.n_strx, + strtab_size) << symbol_info; + return false; + } + + if (!string_table) { + string_table = process_reader_->Memory()->ReadMapped( + strtab_address, strtab_size); + if (!string_table) { + LOG(WARNING) << "could not read string table" << module_info_; + return false; + } + } + + std::string name; + if (!string_table->ReadCString(symbol.n_strx, &name)) { + LOG(WARNING) << "could not read string" << symbol_info; + return false; + } + + if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) { + LOG(WARNING) << base::StringPrintf("N_ABS symbol %s in section %u", + name.c_str(), + symbol.n_sect) << symbol_info; + return false; + } + + if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) { + LOG(WARNING) << base::StringPrintf( + "N_SECT symbol %s in section NO_SECT", + name.c_str()) << symbol_info; + return false; + } + + MachOImageSymbolTableReader::SymbolInformation this_symbol_info; + this_symbol_info.value = symbol.n_value; + this_symbol_info.section = symbol.n_sect; + if (!external_defined_symbols->insert( + std::make_pair(name, this_symbol_info)).second) { + LOG(WARNING) << "duplicate symbol " << name << symbol_info; + return false; + } + } else { + // External indirect symbols may be found in the portion of the symbol + // table used for external symbols as opposed to indirect symbols when + // the indirect symbols are also external. These can be produced by + // Xcode 5.1 ld64-236.3/src/ld/LinkEditClassic.hpp + // ld::tool::SymbolTableAtom<>::addGlobal(). Indirect symbols are not + // currently supported by this symbol table reader, so ignore them + // without failing or logging a message when encountering them. See + // https://groups.google.com/a/chromium.org/d/topic/crashpad-dev/k7QkLwO71Zo + valid_symbol = symbol_type == N_INDR; + } + } else { + valid_symbol = false; + } + if (!valid_symbol && dysymtab_command) { + LOG(WARNING) << "non-external symbol with type " << symbol.n_type + << " in extdefsym" << symbol_info; + return false; + } + } + + return true; + } + + private: + //! \brief Computes the address for data in the `__LINKEDIT` segment + //! identified by its file offset in a Mach-O image. + //! + //! \param[in] fileoff The file offset relative to the beginning of an image’s + //! `mach_header` or `mach_header_64` of the data in the `__LINKEDIT` + //! segment. + //! + //! \return The address, in the remote process’ address space, of the + //! requested data. + mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const { + return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff(); + } + + //! \brief Determines whether an address range is located within the + //! `__LINKEDIT` segment. + //! + //! \param[in] address The base address of the range to check. + //! \param[in] size The size of the range to check. + //! \param[in] tag A string that identifies the range being checked. This is + //! used only for logging. + //! + //! \return `true` if the range identified by \a address + \a size lies + //! entirely within the `__LINKEDIT` segment. `false` if that range is + //! invalid, or if that range is not contained by the `__LINKEDIT` + //! segment, with an appropriate message logged. + bool IsInLinkEditSegment(mach_vm_address_t address, + mach_vm_size_t size, + const char* tag) const { + CheckedMachAddressRange subrange(process_reader_->Is64Bit(), address, size); + if (!subrange.IsValid()) { + LOG(WARNING) << base::StringPrintf("invalid %s range (0x%llx + 0x%llx)", + tag, + address, + size) << module_info_; + return false; + } + + if (!linkedit_range_.ContainsRange(subrange)) { + LOG(WARNING) << base::StringPrintf( + "%s at 0x%llx + 0x%llx outside of " SEG_LINKEDIT + " segment at 0x%llx + 0x%llx", + tag, + address, + size, + linkedit_range_.Base(), + linkedit_range_.Size()) << module_info_; + return false; + } + + return true; + } + + std::string module_info_; + CheckedMachAddressRange linkedit_range_; + ProcessReader* process_reader_; // weak + const MachOImageSegmentReader* linkedit_segment_; // weak + + DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReaderInitializer); +}; + +} // namespace internal + +MachOImageSymbolTableReader::MachOImageSymbolTableReader() + : external_defined_symbols_(), initialized_() { +} + +MachOImageSymbolTableReader::~MachOImageSymbolTableReader() { +} + +bool MachOImageSymbolTableReader::Initialize( + ProcessReader* process_reader, + const process_types::symtab_command* symtab_command, + const process_types::dysymtab_command* dysymtab_command, + const MachOImageSegmentReader* linkedit_segment, + const std::string& module_info) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + internal::MachOImageSymbolTableReaderInitializer initializer(process_reader, + linkedit_segment, + module_info); + if (!initializer.Initialize( + symtab_command, dysymtab_command, &external_defined_symbols_)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const MachOImageSymbolTableReader::SymbolInformation* +MachOImageSymbolTableReader::LookUpExternalDefinedSymbol( + const std::string& name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const auto& iterator = external_defined_symbols_.find(name); + if (iterator == external_defined_symbols_.end()) { + return nullptr; + } + return &iterator->second; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h new file mode 100644 index 0000000..9302e7f --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h
@@ -0,0 +1,135 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_ + +#include "base/basictypes.h" + +#include <map> +#include <string> + +#include <mach/mach.h> +#include <stdint.h> + +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief A reader for symbol tables in Mach-O images mapped into another +//! process. +class MachOImageSymbolTableReader { + public: + //! \brief Information about a symbol in a module’s symbol table. + //! + //! This is a more minimal form of the `nlist` (or `nlist_64`) structure, + //! only containing the equivalent of the `n_value` and `n_sect` fields. + struct SymbolInformation { + //! \brief The address of the symbol as it exists in the symbol table, not + //! adjusted for any “slide.” + mach_vm_address_t value; + + //! \brief The 1-based section index in the module in which the symbol is + //! found. + //! + //! For symbols defined in a section (`N_SECT`), this is the section index + //! that can be passed to MachOImageReader::GetSectionAtIndex(), and \a + //! value will need to be adjusted for segment slide if the containing + //! segment slid when loaded. For absolute symbols (`N_ABS`), this will be + //! `NO_SECT` (`0`), and \a value must not be adjusted for segment slide. + uint8_t section; + }; + + // TODO(mark): Use unordered_map or a similar hash-based map? For now, + // std::map is fine because this map only stores external defined symbols, + // and there aren’t expected to be very many of those that performance would + // become a problem. std::map is also guaranteed to be part of the standard + // library, which isn’t the case for std::unordered_map, which requires the + // C++11 library. In reality, std::unordered_map does not appear to provide + // a performance advantage. It appears that the memory copies currently done + // by TaskMemory::Read() have substantially more impact on symbol table + // operations. + // + // This is public so that the type is available to + // MachOImageSymbolTableReaderInitializer. + using SymbolInformationMap = std::map<std::string, SymbolInformation>; + + MachOImageSymbolTableReader(); + ~MachOImageSymbolTableReader(); + + //! \brief Reads the symbol table from another process. + //! + //! This method must only be called once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] symtab_command The `LC_SYMTAB` load command that identifies + //! the symbol table. + //! \param[in] dysymtab_command The `LC_DYSYMTAB` load command that identifies + //! dynamic symbol information within the symbol table. This load command + //! is not present in all modules, and this parameter may be `nullptr` for + //! modules that do not have this information. When present, \a + //! dysymtab_command is an optimization that allows the symbol table + //! reader to only examine symbol table entries known to be relevant for + //! its purposes. + //! \param[in] linkedit_segment The `__LINKEDIT` segment. This segment should + //! contain the data referenced by \a symtab_command and \a + //! dysymtab_command. This may be any segment in the module, but by + //! convention, the name `__LINKEDIT` is used for this purpose. + //! \param[in] module_info A string to be used in logged messages. This string + //! is for diagnostic purposes only, and may be empty. + //! + //! \return `true` if the symbol table was read successfully. `false` + //! otherwise, with an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + const process_types::symtab_command* symtab_command, + const process_types::dysymtab_command* dysymtab_command, + const MachOImageSegmentReader* linkedit_segment, + const std::string& module_info); + + //! \brief Looks up a symbol in the image’s symbol table. + //! + //! The returned information captures the symbol as it exists in the image’s + //! symbol table, not adjusted for any “slide.” + //! + //! \param[in] name The name of the symbol to look up, “mangled” or + //! “decorated” appropriately. For example, use `"_main"` to look up the + //! symbol for the C `main()` function, and use `"__Z4Funcv"` to look up + //! the symbol for the C++ `Func()` function. + //! + //! \return A SymbolInformation* object with information about the symbol if + //! it was found, or `nullptr` if the symbol was not found or if an error + //! occurred. On error, a warning message will also be logged. The caller + //! does not take ownership; the lifetime of the returned object is scoped + //! to the lifetime of this MachOImageSymbolTableReader object. + //! + //! \note Symbol values returned via this interface are not adjusted for + //! “slide.” For slide-adjusted values, use the higher-level + //! MachOImageReader::LookUpExternalDefinedSymbol() interface. + const SymbolInformation* LookUpExternalDefinedSymbol( + const std::string& name) const; + + private: + SymbolInformationMap external_defined_symbols_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc new file mode 100644 index 0000000..4ded251 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc
@@ -0,0 +1,69 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/memory_snapshot_mac.h" + +#include "base/memory/scoped_ptr.h" +#include "util/mach/task_memory.h" + +namespace crashpad { +namespace internal { + +MemorySnapshotMac::MemorySnapshotMac() + : MemorySnapshot(), + process_reader_(nullptr), + address_(0), + size_(0), + initialized_() { +} + +MemorySnapshotMac::~MemorySnapshotMac() { +} + +void MemorySnapshotMac::Initialize(ProcessReader* process_reader, + uint64_t address, + uint64_t size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_reader_ = process_reader; + address_ = address; + size_ = size; + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +uint64_t MemorySnapshotMac::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +size_t MemorySnapshotMac::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +bool MemorySnapshotMac::Read(Delegate* delegate) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + scoped_ptr<uint8_t[]> buffer(new uint8_t[size_]); + if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) { + return false; + } + return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h new file mode 100644 index 0000000..2f19082 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h
@@ -0,0 +1,68 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include "base/basictypes.h" +#include "snapshot/mac/process_reader.h" +#include "snapshot/memory_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot of a memory region in a process on the running +//! system, when the system runs Mac OS X. +class MemorySnapshotMac final : public MemorySnapshot { + public: + MemorySnapshotMac(); + ~MemorySnapshotMac() override; + + //! \brief Initializes the object. + //! + //! Memory is read lazily. No attempt is made to read the memory snapshot data + //! until Read() is called, and the memory snapshot data is discared when + //! Read() returns. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \param[in] address The base address of the memory region to snapshot, in + //! the snapshot process’ address space. + //! \param[in] size The size of the memory region to snapshot. + void Initialize(ProcessReader* process_reader, + uint64_t address, + uint64_t size); + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + private: + ProcessReader* process_reader_; // weak + uint64_t address_; + uint64_t size_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotMac); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc new file mode 100644 index 0000000..64f04dc --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc
@@ -0,0 +1,181 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/module_snapshot_mac.h" + +#include <mach/mach.h> +#include <mach-o/loader.h> + +#include "base/files/file_path.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/mach_o_image_annotations_reader.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotMac::ModuleSnapshotMac() + : ModuleSnapshot(), + name_(), + timestamp_(0), + mach_o_image_reader_(nullptr), + process_reader_(nullptr), + initialized_() { +} + +ModuleSnapshotMac::~ModuleSnapshotMac() { +} + +bool ModuleSnapshotMac::Initialize( + ProcessReader* process_reader, + const ProcessReader::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + name_ = process_reader_module.name; + timestamp_ = process_reader_module.timestamp; + mach_o_image_reader_ = process_reader_module.reader; + if (!mach_o_image_reader_) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + process_types::CrashpadInfo crashpad_info; + if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) { + options->crashpad_handler_behavior = TriState::kUnset; + options->system_crash_reporter_forwarding = TriState::kUnset; + return; + } + + options->crashpad_handler_behavior = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.crashpad_handler_behavior); + + options->system_crash_reporter_forwarding = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.system_crash_reporter_forwarding); +} + +std::string ModuleSnapshotMac::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotMac::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return mach_o_image_reader_->Address(); +} + +uint64_t ModuleSnapshotMac::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return mach_o_image_reader_->Size(); +} + +time_t ModuleSnapshotMac::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return timestamp_; +} + +void ModuleSnapshotMac::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (mach_o_image_reader_->FileType() == MH_DYLIB) { + uint32_t dylib_version = mach_o_image_reader_->DylibVersion(); + *version_0 = (dylib_version & 0xffff0000) >> 16; + *version_1 = (dylib_version & 0x0000ff00) >> 8; + *version_2 = (dylib_version & 0x000000ff); + *version_3 = 0; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +void ModuleSnapshotMac::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // LC_SOURCE_VERSION is supposed to be interpreted as a 5-component version + // number, 24 bits for the first component and 10 for the others, per + // <mach-o/loader.h>. To preserve the full range of possible version numbers + // without data loss, map it to the 4 16-bit fields mandated by the interface + // here, which was informed by the minidump file format. + uint64_t source_version = mach_o_image_reader_->SourceVersion(); + *version_0 = (source_version & 0xffff000000000000u) >> 48; + *version_1 = (source_version & 0x0000ffff00000000u) >> 32; + *version_2 = (source_version & 0x00000000ffff0000u) >> 16; + *version_3 = source_version & 0x000000000000ffffu; +} + +ModuleSnapshot::ModuleType ModuleSnapshotMac::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + uint32_t file_type = mach_o_image_reader_->FileType(); + switch (file_type) { + case MH_EXECUTE: + return kModuleTypeExecutable; + case MH_DYLIB: + return kModuleTypeSharedLibrary; + case MH_DYLINKER: + return kModuleTypeDynamicLoader; + case MH_BUNDLE: + return kModuleTypeLoadableModule; + default: + return kModuleTypeUnknown; + } +} + +void ModuleSnapshotMac::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + mach_o_image_reader_->UUID(uuid); + *age = 0; +} + +std::string ModuleSnapshotMac::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + MachOImageAnnotationsReader annotations_reader( + process_reader_, mach_o_image_reader_, name_); + return annotations_reader.Vector(); +} + +std::map<std::string, std::string> ModuleSnapshotMac::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + MachOImageAnnotationsReader annotations_reader( + process_reader_, mach_o_image_reader_, name_); + return annotations_reader.SimpleMap(); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h new file mode 100644 index 0000000..42b1470 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h
@@ -0,0 +1,96 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/mac/process_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class MachOImageReader; +struct UUID; + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Mac OS X system. +class ModuleSnapshotMac final : public ModuleSnapshot { + public: + ModuleSnapshotMac(); + ~ModuleSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the task containing the + //! module. + //! \param[in] process_reader_module The module within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + const ProcessReader::Module& process_reader_module); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector<std::string> AnnotationsVector() const override; + std::map<std::string, std::string> AnnotationsSimpleMap() const override; + + private: + std::string name_; + time_t timestamp_; + const MachOImageReader* mach_o_image_reader_; // weak + ProcessReader* process_reader_; // weak + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMac); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc new file mode 100644 index 0000000..c7f674b --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc
@@ -0,0 +1,714 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/process_reader.h" + +#include <AvailabilityMacros.h> +#include <mach/mach_vm.h> +#include <mach-o/loader.h> + +#include <algorithm> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/mac/scoped_mach_vm.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "snapshot/mac/process_types.h" +#include "util/misc/scoped_forbid_return.h" + +namespace { + +void MachTimeValueToTimeval(const time_value& mach, timeval* tv) { + tv->tv_sec = mach.seconds; + tv->tv_usec = mach.microseconds; +} + +kern_return_t MachVMRegionRecurseDeepest(task_t task, + mach_vm_address_t* address, + mach_vm_size_t* size, + natural_t* depth, + vm_prot_t* protection, + unsigned int* user_tag) { + vm_region_submap_short_info_64 submap_info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + while (true) { + kern_return_t kr = mach_vm_region_recurse( + task, + address, + size, + depth, + reinterpret_cast<vm_region_recurse_info_t>(&submap_info), + &count); + if (kr != KERN_SUCCESS) { + return kr; + } + + if (!submap_info.is_submap) { + *protection = submap_info.protection; + *user_tag = submap_info.user_tag; + return KERN_SUCCESS; + } + + ++*depth; + } +} + +} // namespace + +namespace crashpad { + +ProcessReader::Thread::Thread() + : thread_context(), + float_context(), + debug_context(), + id(0), + stack_region_address(0), + stack_region_size(0), + thread_specific_data_address(0), + port(THREAD_NULL), + suspend_count(0), + priority(0) { +} + +ProcessReader::Module::Module() : name(), reader(nullptr), timestamp(0) { +} + +ProcessReader::Module::~Module() { +} + +ProcessReader::ProcessReader() + : process_info_(), + threads_(), + modules_(), + module_readers_(), + task_memory_(), + task_(TASK_NULL), + initialized_(), + is_64_bit_(false), + initialized_threads_(false), + initialized_modules_(false) { +} + +ProcessReader::~ProcessReader() { + for (const Thread& thread : threads_) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; + } +} + +bool ProcessReader::Initialize(task_t task) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!process_info_.InitializeFromTask(task)) { + return false; + } + + is_64_bit_ = process_info_.Is64Bit(); + + task_memory_.reset(new TaskMemory(task)); + task_ = task; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Calculate user and system time the same way the kernel does for + // getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru(). + timerclear(user_time); + timerclear(system_time); + + // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO. + // TASK_BASIC_INFO_64 is equivalent and works on earlier systems. + task_basic_info_64 task_basic_info; + mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT; + kern_return_t kr = task_info(task_, + TASK_BASIC_INFO_64, + reinterpret_cast<task_info_t>(&task_basic_info), + &task_basic_info_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64"; + return false; + } + + task_thread_times_info_data_t task_thread_times; + mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT; + kr = task_info(task_, + TASK_THREAD_TIMES_INFO, + reinterpret_cast<task_info_t>(&task_thread_times), + &task_thread_times_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES"; + return false; + } + + MachTimeValueToTimeval(task_basic_info.user_time, user_time); + MachTimeValueToTimeval(task_basic_info.system_time, system_time); + + timeval thread_user_time; + MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time); + timeval thread_system_time; + MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time); + + timeradd(user_time, &thread_user_time, user_time); + timeradd(system_time, &thread_system_time, system_time); + + return true; +} + +const std::vector<ProcessReader::Thread>& ProcessReader::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_threads_) { + InitializeThreads(); + } + + return threads_; +} + +const std::vector<ProcessReader::Module>& ProcessReader::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_modules_) { + InitializeModules(); + } + + return modules_; +} + +void ProcessReader::InitializeThreads() { + DCHECK(!initialized_threads_); + DCHECK(threads_.empty()); + + initialized_threads_ = true; + + thread_act_array_t threads; + mach_msg_type_number_t thread_count = 0; + kern_return_t kr = task_threads(task_, &threads, &thread_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_threads"; + return; + } + + // The send rights in the |threads| array won’t have their send rights managed + // by anything until they’re added to |threads_| by the loop below. Any early + // return (or exception) that happens between here and the completion of the + // loop below will leak thread port send rights. + ScopedForbidReturn threads_need_owners; + + base::mac::ScopedMachVM threads_vm( + reinterpret_cast<vm_address_t>(threads), + mach_vm_round_page(thread_count * sizeof(*threads))); + + for (size_t index = 0; index < thread_count; ++index) { + Thread thread; + thread.port = threads[index]; + +#if defined(ARCH_CPU_X86_FAMILY) + const thread_state_flavor_t kThreadStateFlavor = + Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32; + mach_msg_type_number_t thread_state_count = + Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT; + + // TODO(mark): Use the AVX variants instead of the FLOAT variants? + const thread_state_flavor_t kFloatStateFlavor = + Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32; + mach_msg_type_number_t float_state_count = + Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT; + + const thread_state_flavor_t kDebugStateFlavor = + Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32; + mach_msg_type_number_t debug_state_count = + Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT; +#endif + + kr = thread_get_state( + thread.port, + kThreadStateFlavor, + reinterpret_cast<thread_state_t>(&thread.thread_context), + &thread_state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")"; + continue; + } + + kr = thread_get_state( + thread.port, + kFloatStateFlavor, + reinterpret_cast<thread_state_t>(&thread.float_context), + &float_state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")"; + continue; + } + + kr = thread_get_state( + thread.port, + kDebugStateFlavor, + reinterpret_cast<thread_state_t>(&thread.debug_context), + &debug_state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")"; + continue; + } + + thread_basic_info basic_info; + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread.port, + THREAD_BASIC_INFO, + reinterpret_cast<thread_info_t>(&basic_info), + &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)"; + } else { + thread.suspend_count = basic_info.suspend_count; + } + + thread_identifier_info identifier_info; + count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info(thread.port, + THREAD_IDENTIFIER_INFO, + reinterpret_cast<thread_info_t>(&identifier_info), + &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)"; + } else { + thread.id = identifier_info.thread_id; + + // thread_identifier_info::thread_handle contains the base of the + // thread-specific data area, which on x86 and x86_64 is the thread’s base + // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c + // thread_info_internal() gets the value from + // machine_thread::cthread_self, which is the same value used to set the + // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c + // act_machine_switch_pcb(). + // + // This address is the internal pthread’s _pthread::tsd[], an array of + // void* values that can be indexed by pthread_key_t values. + thread.thread_specific_data_address = identifier_info.thread_handle; + } + + thread_precedence_policy precedence; + count = THREAD_PRECEDENCE_POLICY_COUNT; + boolean_t get_default = FALSE; + kr = thread_policy_get(thread.port, + THREAD_PRECEDENCE_POLICY, + reinterpret_cast<thread_policy_t>(&precedence), + &count, + &get_default); + if (kr != KERN_SUCCESS) { + MACH_LOG(INFO, kr) << "thread_policy_get"; + } else { + thread.priority = precedence.importance; + } + +#if defined(ARCH_CPU_X86_FAMILY) + mach_vm_address_t stack_pointer = Is64Bit() + ? thread.thread_context.t64.__rsp + : thread.thread_context.t32.__esp; +#endif + + thread.stack_region_address = + CalculateStackRegion(stack_pointer, &thread.stack_region_size); + + threads_.push_back(thread); + } + + threads_need_owners.Disarm(); +} + +void ProcessReader::InitializeModules() { + DCHECK(!initialized_modules_); + DCHECK(modules_.empty()); + + initialized_modules_ = true; + + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = task_info( + task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info"; + return; + } + + // TODO(mark): Deal with statically linked executables which don’t use dyld. + // This may look for the module that matches the executable path in the same + // data set that vmmap uses. + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + // The task_dyld_info_data_t struct grew in 10.7, adding the format field. + // Don’t check this field if it’s not present, which can happen when either + // the SDK used at compile time or the kernel at run time are too old and + // don’t know about it. + if (count >= TASK_DYLD_INFO_COUNT) { + const integer_t kExpectedFormat = + !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64; + if (dyld_info.all_image_info_format != kExpectedFormat) { + LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format " + << dyld_info.all_image_info_format; + DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat); + return; + } + } +#endif + + process_types::dyld_all_image_infos all_image_infos; + if (!all_image_infos.Read(this, dyld_info.all_image_info_addr)) { + LOG(WARNING) << "could not read dyld_all_image_infos"; + return; + } + + if (all_image_infos.version < 1) { + LOG(WARNING) << "unexpected dyld_all_image_infos version " + << all_image_infos.version; + return; + } + + size_t expected_size = + process_types::dyld_all_image_infos::ExpectedSizeForVersion( + this, all_image_infos.version); + if (dyld_info.all_image_info_size < expected_size) { + LOG(WARNING) << "small dyld_all_image_infos size " + << dyld_info.all_image_info_size << " < " << expected_size + << " for version " << all_image_infos.version; + return; + } + + // Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while + // dyld was loading the executable. This can happen if a required dynamic + // library was not found. Similarly, all_image_infos.infoArray may be nullptr + // if a crash occurred while dyld was updating it. + // + // TODO(mark): It may be possible to recover from these situations by looking + // through memory mappings for Mach-O images. + // + // Continue along when this situation is detected, because even without any + // images in infoArray, dyldImageLoadAddress may be set, and it may be + // possible to recover some information from dyld. + if (all_image_infos.infoArrayCount == 0) { + LOG(WARNING) << "all_image_infos.infoArrayCount is zero"; + } else if (!all_image_infos.infoArray) { + LOG(WARNING) << "all_image_infos.infoArray is nullptr"; + } + + std::vector<process_types::dyld_image_info> image_info_vector( + all_image_infos.infoArrayCount); + if (!process_types::dyld_image_info::ReadArrayInto(this, + all_image_infos.infoArray, + image_info_vector.size(), + &image_info_vector[0])) { + LOG(WARNING) << "could not read dyld_image_info array"; + return; + } + + size_t main_executable_count = 0; + bool found_dyld = false; + modules_.reserve(image_info_vector.size()); + for (const process_types::dyld_image_info& image_info : image_info_vector) { + Module module; + module.timestamp = image_info.imageFileModDate; + + if (!task_memory_->ReadCString(image_info.imageFilePath, &module.name)) { + LOG(WARNING) << "could not read dyld_image_info::imageFilePath"; + // Proceed anyway with an empty module name. + } + + scoped_ptr<MachOImageReader> reader(new MachOImageReader()); + if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) { + reader.reset(); + } + + module.reader = reader.get(); + + uint32_t file_type = reader ? reader->FileType() : 0; + + module_readers_.push_back(reader.release()); + modules_.push_back(module); + + if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress && + image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) { + found_dyld = true; + + LOG_IF(WARNING, file_type != MH_DYLINKER) + << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", + module.name.c_str(), + file_type); + } + + if (file_type == MH_EXECUTE) { + // On Mac OS X 10.6, the main executable does not normally show up at + // index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp + // notifyGDB(), the function resposible for causing + // dyld_all_image_infos::infoArray to be updated, is called. It is + // registered to be called when all dependents of an image have been + // mapped (dyld_image_state_dependents_mapped), meaning that the main + // executable won’t be added to the list until all of the libraries it + // depends on are, even though dyld begins looking at the main executable + // first. This changed in later versions of dyld, including those present + // in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from + // notifyGDB()) is registered to be called when an image itself has been + // mapped (dyld_image_state_mapped), regardless of the libraries that it + // depends on. + // + // The interface requires that the main executable be first in the list, + // so swap it into the right position. + size_t index = modules_.size() - 1; + if (main_executable_count == 0) { + std::swap(modules_[0], modules_[index]); + } else { + LOG(WARNING) << base::StringPrintf( + "multiple MH_EXECUTE modules (%s, %s)", + modules_[0].name.c_str(), + modules_[index].name.c_str()); + } + ++main_executable_count; + } + } + + LOG_IF(WARNING, main_executable_count == 0) << "no MH_EXECUTE modules"; + + // all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is + // loaded into the process’ address space as a module. Its load address is + // easily known given a sufficiently recent all_image_infos.version, but the + // timestamp and pathname are not given as they are for other modules. + // + // The timestamp is a lost cause, because the kernel doesn’t record the + // timestamp of the dynamic linker at the time it’s loaded in the same way + // that dyld records the timestamps of other modules when they’re loaded. (The + // timestamp for the main executable is also not reported and appears as 0 + // even when accessed via dyld APIs, because it’s loaded by the kernel, not by + // dyld.) + // + // The name can be determined, but it’s not as simple as hardcoding the + // default "/usr/lib/dyld" because an executable could have specified anything + // in its LC_LOAD_DYLINKER command. + if (!found_dyld && all_image_infos.version >= 2 && + all_image_infos.dyldImageLoadAddress) { + Module module; + module.timestamp = 0; + + // Examine the executable’s LC_LOAD_DYLINKER load command to find the path + // used to load dyld. + if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) { + module.name = modules_[0].reader->DylinkerName(); + } + std::string module_name = !module.name.empty() ? module.name : "(dyld)"; + + scoped_ptr<MachOImageReader> reader(new MachOImageReader()); + if (!reader->Initialize( + this, all_image_infos.dyldImageLoadAddress, module_name)) { + reader.reset(); + } + + module.reader = reader.get(); + + uint32_t file_type = reader ? reader->FileType() : 0; + + LOG_IF(WARNING, file_type != MH_DYLINKER) + << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", + module.name.c_str(), + file_type); + + if (module.name.empty() && file_type == MH_DYLINKER) { + // Look inside dyld directly to find its preferred path. + module.name = reader->DylinkerName(); + } + + if (module.name.empty()) { + module.name = "(dyld)"; + } + + // dyld is loaded in the process even if its path can’t be determined. + module_readers_.push_back(reader.release()); + modules_.push_back(module); + } +} + +mach_vm_address_t ProcessReader::CalculateStackRegion( + mach_vm_address_t stack_pointer, + mach_vm_size_t* stack_region_size) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // For pthreads, it may be possible to compute the stack region based on the + // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct + // for a thread can be located at TSD slot 0, or the known offsets of + // stackaddr and stacksize from the TSD area could be used. + mach_vm_address_t region_base = stack_pointer; + mach_vm_size_t region_size; + natural_t depth = 0; + vm_prot_t protection; + unsigned int user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest( + task_, ®ion_base, ®ion_size, &depth, &protection, &user_tag); + if (kr != KERN_SUCCESS) { + MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; + *stack_region_size = 0; + return 0; + } + + if (region_base > stack_pointer) { + // There’s nothing mapped at the stack pointer’s address. Something may have + // trashed the stack pointer. Note that this shouldn’t happen for a normal + // stack guard region violation because the guard region is mapped but has + // VM_PROT_NONE protection. + *stack_region_size = 0; + return 0; + } + + mach_vm_address_t start_address = stack_pointer; + + if ((protection & VM_PROT_READ) == 0) { + // If the region isn’t readable, the stack pointer probably points to the + // guard region. Don’t include it as part of the stack, and don’t include + // anything at any lower memory address. The code below may still possibly + // find the real stack region at a memory address higher than this region. + start_address = region_base + region_size; + } else { + // If the ABI requires a red zone, adjust the region to include it if + // possible. + LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag); + + // Regardless of whether the ABI requires a red zone, capture up to + // kExtraCaptureSize additional bytes of stack, but only if present in the + // region that was already found. + const mach_vm_size_t kExtraCaptureSize = 128; + start_address = std::max(start_address >= kExtraCaptureSize + ? start_address - kExtraCaptureSize + : start_address, + region_base); + + // Align start_address to a 16-byte boundary, which can help readers by + // ensuring that data is aligned properly. This could page-align instead, + // but that might be wasteful. + const mach_vm_size_t kDesiredAlignment = 16; + start_address &= ~(kDesiredAlignment - 1); + DCHECK_GE(start_address, region_base); + } + + region_size -= (start_address - region_base); + region_base = start_address; + + mach_vm_size_t total_region_size = region_size; + + // The stack region may have gotten split up into multiple abutting regions. + // Try to coalesce them. This frequently happens for the main thread’s stack + // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region + // is split up due to an mprotect() or vm_protect() call. + // + // Stack regions created by the kernel and the pthreads library will be marked + // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions + // with the same tag should find an entire stack region. Checking that the + // protection on individual regions is not VM_PROT_NONE should guarantee that + // this algorithm doesn’t collect map entries belonging to another thread’s + // stack: well-behaved stacks (such as those created by the kernel and the + // pthreads library) have VM_PROT_NONE guard regions at their low-address + // ends. + // + // Other stack regions may not be so well-behaved and thus if user_tag is not + // VM_MEMORY_STACK, the single region that was found is used as-is without + // trying to merge it with other adjacent regions. + if (user_tag == VM_MEMORY_STACK) { + mach_vm_address_t try_address = region_base; + mach_vm_address_t original_try_address; + + while (try_address += region_size, + original_try_address = try_address, + (kr = MachVMRegionRecurseDeepest(task_, + &try_address, + ®ion_size, + &depth, + &protection, + &user_tag) == KERN_SUCCESS) && + try_address == original_try_address && + (protection & VM_PROT_READ) != 0 && + user_tag == VM_MEMORY_STACK) { + total_region_size += region_size; + } + + if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { + // Tolerate KERN_INVALID_ADDRESS because it will be returned when there + // are no more regions in the map at or above the specified |try_address|. + MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; + } + } + + *stack_region_size = total_region_size; + return region_base; +} + +void ProcessReader::LocateRedZone(mach_vm_address_t* const start_address, + mach_vm_address_t* const region_base, + mach_vm_address_t* const region_size, + const unsigned int user_tag) { +#if defined(ARCH_CPU_X86_FAMILY) + if (Is64Bit()) { + // x86_64 has a red zone. See AMD64 ABI 0.99.6, + // http://www.x86-64.org/documentation/abi.pdf, section 3.2.2, “The Stack + // Frame”. + const mach_vm_size_t kRedZoneSize = 128; + mach_vm_address_t red_zone_base = + *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; + bool red_zone_ok = false; + if (red_zone_base >= *region_base) { + // The red zone is within the region already discovered. + red_zone_ok = true; + } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) { + // Probe to see if there’s a region immediately below the one already + // discovered. + mach_vm_address_t red_zone_region_base = red_zone_base; + mach_vm_size_t red_zone_region_size; + natural_t red_zone_depth = 0; + vm_prot_t red_zone_protection; + unsigned int red_zone_user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest(task_, + &red_zone_region_base, + &red_zone_region_size, + &red_zone_depth, + &red_zone_protection, + &red_zone_user_tag); + if (kr != KERN_SUCCESS) { + MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; + *start_address = *region_base; + } else if (red_zone_region_base + red_zone_region_size == *region_base && + (red_zone_protection & VM_PROT_READ) != 0 && + red_zone_user_tag == user_tag) { + // The region containing the red zone is immediately below the region + // already found, it’s readable (not the guard region), and it has the + // same user tag as the region already found, so merge them. + red_zone_ok = true; + *region_base -= red_zone_region_size; + *region_size += red_zone_region_size; + } + } + + if (red_zone_ok) { + // Begin capturing from the base of the red zone (but not the entire + // region that encompasses the red zone). + *start_address = red_zone_base; + } else { + // The red zone would go lower into another region in memory, but no + // region was found. Memory can only be captured to an address as low as + // the base address of the region already found. + *start_address = *region_base; + } + } +#endif +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader.h b/third_party/crashpad/crashpad/snapshot/mac/process_reader.h new file mode 100644 index 0000000..a781667 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader.h
@@ -0,0 +1,239 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_ + +#include <mach/mach.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" +#include "util/mach/task_memory.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/posix/process_info.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class MachOImageReader; + +//! \brief Accesses information about another process, identified by a Mach +//! task. +class ProcessReader { + public: + //! \brief Contains information about a thread that belongs to a task + //! (process). + struct Thread { +#if defined(ARCH_CPU_X86_FAMILY) + union ThreadContext { + x86_thread_state64_t t64; + x86_thread_state32_t t32; + }; + union FloatContext { + x86_float_state64_t f64; + x86_float_state32_t f32; + }; + union DebugContext { + x86_debug_state64_t d64; + x86_debug_state32_t d32; + }; +#endif + + Thread(); + ~Thread() {} + + ThreadContext thread_context; + FloatContext float_context; + DebugContext debug_context; + uint64_t id; + mach_vm_address_t stack_region_address; + mach_vm_size_t stack_region_size; + mach_vm_address_t thread_specific_data_address; + thread_t port; + int suspend_count; + int priority; + }; + + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this MachOImageReader is scoped to the lifetime of the + //! ProcessReader that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + const MachOImageReader* reader; + + //! \brief The module’s timestamp. + //! + //! This field will be `0` if its value cannot be determined. It can only be + //! determined for images that are loaded by dyld, so it will be `0` for the + //! main executable and for dyld itself. + time_t timestamp; + }; + + ProcessReader(); + ~ProcessReader(); + + //! \brief Initializes this object. This method must be called before any + //! other. + //! + //! \param[in] task A send right to the target task’s task port. This object + //! does not take ownership of the send right. + //! + //! \return `true` on success, indicating that this object will respond + //! validly to further method calls. `false` on failure. On failure, no + //! further method calls should be made. + bool Initialize(task_t task); + + //! \return `true` if the target task is a 64-bit process. + bool Is64Bit() const { return is_64_bit_; } + + //! \return The target task’s process ID. + pid_t ProcessID() const { return process_info_.ProcessID(); } + + //! \return The target task’s parent process ID. + pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } + + //! \brief Determines the target process’ start time. + //! + //! \param[out] start_time The time that the process started. + void StartTime(timeval* start_time) const { + process_info_.StartTime(start_time); + } + + //! \brief Determines the target process’ execution time. + //! + //! \param[out] user_time The amount of time the process has executed code in + //! user mode. + //! \param[out] system_time The amount of time the process has executed code + //! in system mode. + //! + //! \return `true` on success, `false` on failure, with a warning logged. On + //! failure, \a user_time and \a system_time will be set to represent no + //! time spent executing code in user or system mode. + bool CPUTimes(timeval* user_time, timeval* system_time) const; + + //! \return Accesses the memory of the target task. + TaskMemory* Memory() { return task_memory_.get(); } + + //! \return The threads that are in the task (process). The first element (at + //! index `0`) corresponds to the main thread. + const std::vector<Thread>& Threads(); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable, and the final element + //! corresponds to the dynamic loader, dyld. + const std::vector<Module>& Modules(); + + private: + //! Performs lazy initialization of the \a threads_ vector on behalf of + //! Threads(). + void InitializeThreads(); + + //! Performs lazy initialization of the \a modules_ vector on behalf of + //! Modules(). + void InitializeModules(); + + //! \brief Calculates the base address and size of the region used as a + //! thread’s stack. + //! + //! The region returned by this method may be formed by merging multiple + //! adjacent regions in a process’ memory map if appropriate. The base address + //! of the returned region may be lower than the \a stack_pointer passed in + //! when the ABI mandates a red zone below the stack pointer. + //! + //! \param[in] stack_pointer The stack pointer, referring to the top (lowest + //! address) of a thread’s stack. + //! \param[out] stack_region_size The size of the memory region used as the + //! thread’s stack. + //! + //! \return The base address (lowest address) of the memory region used as the + //! thread’s stack. + mach_vm_address_t CalculateStackRegion(mach_vm_address_t stack_pointer, + mach_vm_size_t* stack_region_size); + + //! \brief Adjusts the region for the red zone, if the ABI requires one. + //! + //! This method performs red zone calculation for CalculateStackRegion(). Its + //! parameters are local variables used within that method, and may be + //! modified as needed. + //! + //! Where a red zone is required, the region of memory captured for a thread’s + //! stack will be extended to include the red zone below the stack pointer, + //! provided that such memory is mapped, readable, and has the correct user + //! tag value. If these conditions cannot be met fully, as much of the red + //! zone will be captured as is possible while meeting these conditions. + //! + //! \param[inout] start_address The base address of the region to begin + //! capturing stack memory from. On entry, \a start_address is the stack + //! pointer. On return, \a start_address may be decreased to encompass a + //! red zone. + //! \param[inout] region_base The base address of the region that contains + //! stack memory. This is distinct from \a start_address in that \a + //! region_base will be page-aligned. On entry, \a region_base is the + //! base address of a region that contains \a start_address. On return, + //! if \a start_address is decremented and is outside of the region + //! originally described by \a region_base, \a region_base will also be + //! decremented appropriately. + //! \param[inout] region_size The size of the region that contains stack + //! memory. This region begins at \a region_base. On return, if \a + //! region_base is decremented, \a region_size will be incremented + //! appropriately. + //! \param[in] user_tag The Mach VM system’s user tag for the region described + //! by the initial values of \a region_base and \a region_size. The red + //! zone will only be allowed to extend out of the region described by + //! these initial values if the user tag is appropriate for stack memory + //! and the expanded region has the same user tag value. + void LocateRedZone(mach_vm_address_t* start_address, + mach_vm_address_t* region_base, + mach_vm_address_t* region_size, + unsigned int user_tag); + + ProcessInfo process_info_; + std::vector<Thread> threads_; // owns send rights + std::vector<Module> modules_; + PointerVector<MachOImageReader> module_readers_; + scoped_ptr<TaskMemory> task_memory_; + task_t task_; // weak + InitializationStateDcheck initialized_; + + // This shadows a method of process_info_, but it’s accessed so frequently + // that it’s given a first-class field to save a call and a few bit operations + // on each access. + bool is_64_bit_; + + bool initialized_threads_; + bool initialized_modules_; + + DISALLOW_COPY_AND_ASSIGN(ProcessReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc new file mode 100644 index 0000000..2cbf8ea --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc
@@ -0,0 +1,838 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/process_reader.h" + +#include <AvailabilityMacros.h> +#include <mach-o/dyld.h> +#include <mach-o/dyld_images.h> +#include <mach/mach.h> +#include <OpenCL/opencl.h> +#include <string.h> +#include <sys/stat.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "test/errors.h" +#include "test/mac/dyld.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/file/file_io.h" +#include "util/mac/mac_util.h" +#include "util/mach/mach_extensions.h" +#include "util/stdlib/pointer_container.h" +#include "util/synchronization/semaphore.h" + +#if !defined(MAC_OS_X_VERSION_10_10) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 +extern "C" { +// Redeclare a typedef whose availability (OSX 10.10) is newer than the +// deployment target. +typedef struct _cl_device_id* cl_device_id; +} // extern "C" +#endif + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcessReader, SelfBasic) { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(getpid(), process_reader.ProcessID()); + EXPECT_EQ(getppid(), process_reader.ParentProcessID()); + + const char kTestMemory[] = "Some test memory"; + char buffer[arraysize(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + reinterpret_cast<mach_vm_address_t>(kTestMemory), + sizeof(kTestMemory), + &buffer)); + EXPECT_STREQ(kTestMemory, buffer); +} + +const char kTestMemory[] = "Read me from another process"; + +class ProcessReaderChild final : public MachMultiprocess { + public: + ProcessReaderChild() : MachMultiprocess() {} + + ~ProcessReaderChild() {} + + private: + void MachMultiprocessParent() override { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(getpid(), process_reader.ParentProcessID()); + EXPECT_EQ(ChildPID(), process_reader.ProcessID()); + + FileHandle read_handle = ReadPipeHandle(); + + mach_vm_address_t address; + CheckedReadFile(read_handle, &address, sizeof(address)); + + std::string read_string; + ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string)); + EXPECT_EQ(kTestMemory, read_string); + } + + void MachMultiprocessChild() override { + FileHandle write_handle = WritePipeHandle(); + + mach_vm_address_t address = + reinterpret_cast<mach_vm_address_t>(kTestMemory); + CheckedWriteFile(write_handle, &address, sizeof(address)); + + // Wait for the parent to signal that it’s OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild); +}; + +TEST(ProcessReader, ChildBasic) { + ProcessReaderChild process_reader_child; + process_reader_child.Run(); +} + +// Returns a thread ID given a pthread_t. This wraps pthread_threadid_np() but +// that function has a cumbersome interface because it returns a success value. +// This function CHECKs success and returns the thread ID directly. +uint64_t PthreadToThreadID(pthread_t pthread) { + uint64_t thread_id; + int rv = pthread_threadid_np(pthread, &thread_id); + CHECK_EQ(rv, 0); + return thread_id; +} + +TEST(ProcessReader, SelfOneThread) { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + const std::vector<ProcessReader::Thread>& threads = process_reader.Threads(); + + // If other tests ran in this process previously, threads may have been + // created and may still be running. This check must look for at least one + // thread, not exactly one thread. + ASSERT_GE(threads.size(), 1u); + + EXPECT_EQ(PthreadToThreadID(pthread_self()), threads[0].id); + + thread_t thread_self = MachThreadSelf(); + EXPECT_EQ(thread_self, threads[0].port); + + EXPECT_EQ(0, threads[0].suspend_count); +} + +class TestThreadPool { + public: + struct ThreadExpectation { + mach_vm_address_t stack_address; + int suspend_count; + }; + + TestThreadPool() : thread_infos_() { + } + + // Resumes suspended threads, signals each thread’s exit semaphore asking it + // to exit, and joins each thread, blocking until they have all exited. + ~TestThreadPool() { + for (ThreadInfo* thread_info : thread_infos_) { + thread_t thread_port = pthread_mach_thread_np(thread_info->pthread); + while (thread_info->suspend_count > 0) { + kern_return_t kr = thread_resume(thread_port); + EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_resume"); + --thread_info->suspend_count; + } + } + + for (ThreadInfo* thread_info : thread_infos_) { + thread_info->exit_semaphore.Signal(); + } + + for (const ThreadInfo* thread_info : thread_infos_) { + int rv = pthread_join(thread_info->pthread, nullptr); + CHECK_EQ(0, rv); + } + } + + // Starts |thread_count| threads and waits on each thread’s ready semaphore, + // so that when this function returns, all threads have been started and have + // all run to the point that they’ve signalled that they are ready. + void StartThreads(size_t thread_count) { + ASSERT_TRUE(thread_infos_.empty()); + + for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) { + ThreadInfo* thread_info = new ThreadInfo(); + thread_infos_.push_back(thread_info); + + int rv = pthread_create(&thread_info->pthread, + nullptr, + ThreadMain, + thread_info); + ASSERT_EQ(0, rv); + } + + for (ThreadInfo* thread_info : thread_infos_) { + thread_info->ready_semaphore.Wait(); + } + + // If present, suspend the thread at indices 1 through 3 the same number of + // times as their index. This tests reporting of suspend counts. + for (size_t thread_index = 1; + thread_index < thread_infos_.size() && thread_index < 4; + ++thread_index) { + thread_t thread_port = + pthread_mach_thread_np(thread_infos_[thread_index]->pthread); + for (size_t suspend_count = 0; + suspend_count < thread_index; + ++suspend_count) { + kern_return_t kr = thread_suspend(thread_port); + EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_suspend"); + if (kr == KERN_SUCCESS) { + ++thread_infos_[thread_index]->suspend_count; + } + } + } + } + + uint64_t GetThreadInfo(size_t thread_index, + ThreadExpectation* expectation) { + CHECK_LT(thread_index, thread_infos_.size()); + + const ThreadInfo* thread_info = thread_infos_[thread_index]; + expectation->stack_address = thread_info->stack_address; + expectation->suspend_count = thread_info->suspend_count; + + return PthreadToThreadID(thread_info->pthread); + } + + private: + struct ThreadInfo { + ThreadInfo() + : pthread(nullptr), + stack_address(0), + ready_semaphore(0), + exit_semaphore(0), + suspend_count(0) { + } + + ~ThreadInfo() {} + + // The thread’s ID, set at the time the thread is created. + pthread_t pthread; + + // An address somewhere within the thread’s stack. The thread sets this in + // its ThreadMain(). + mach_vm_address_t stack_address; + + // The worker thread signals ready_semaphore to indicate that it’s done + // setting up its ThreadInfo structure. The main thread waits on this + // semaphore before using any data that the worker thread is responsible for + // setting. + Semaphore ready_semaphore; + + // The worker thread waits on exit_semaphore to determine when it’s safe to + // exit. The main thread signals exit_semaphore when it no longer needs the + // worker thread. + Semaphore exit_semaphore; + + // The thread’s suspend count. + int suspend_count; + }; + + static void* ThreadMain(void* argument) { + ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument); + + thread_info->stack_address = + reinterpret_cast<mach_vm_address_t>(&thread_info); + + thread_info->ready_semaphore.Signal(); + thread_info->exit_semaphore.Wait(); + + // Check this here after everything’s known to be synchronized, otherwise + // there’s a race between the parent thread storing this thread’s pthread_t + // in thread_info_pthread and this thread starting and attempting to access + // it. + CHECK_EQ(pthread_self(), thread_info->pthread); + + return nullptr; + } + + // This is a PointerVector because the address of a ThreadInfo object is + // passed to each thread’s ThreadMain(), so they cannot move around in memory. + PointerVector<ThreadInfo> thread_infos_; + + DISALLOW_COPY_AND_ASSIGN(TestThreadPool); +}; + +using ThreadMap = std::map<uint64_t, TestThreadPool::ThreadExpectation>; + +// Verifies that all of the threads in |threads|, obtained from ProcessReader, +// agree with the expectation in |thread_map|. If |tolerate_extra_threads| is +// true, |threads| is allowed to contain threads that are not listed in +// |thread_map|. This is useful when testing situations where code outside of +// the test’s control (such as system libraries) may start threads, or may have +// started threads prior to a test’s execution. +void ExpectSeveralThreads(ThreadMap* thread_map, + const std::vector<ProcessReader::Thread>& threads, + const bool tolerate_extra_threads) { + if (tolerate_extra_threads) { + ASSERT_GE(threads.size(), thread_map->size()); + } else { + ASSERT_EQ(thread_map->size(), threads.size()); + } + + for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) { + const ProcessReader::Thread& thread = threads[thread_index]; + mach_vm_address_t thread_stack_region_end = + thread.stack_region_address + thread.stack_region_size; + + const auto& iterator = thread_map->find(thread.id); + if (!tolerate_extra_threads) { + // Make sure that the thread is in the expectation map. + ASSERT_NE(thread_map->end(), iterator); + } + + if (iterator != thread_map->end()) { + EXPECT_GE(iterator->second.stack_address, thread.stack_region_address); + EXPECT_LT(iterator->second.stack_address, thread_stack_region_end); + + EXPECT_EQ(iterator->second.suspend_count, thread.suspend_count); + + // Remove the thread from the expectation map since it’s already been + // found. This makes it easy to check for duplicate thread IDs, and makes + // it easy to check that all expected threads were found. + thread_map->erase(iterator); + } + + // Make sure that this thread’s ID, stack region, and port don’t conflict + // with any other thread’s. Each thread should have a unique value for its + // ID and port, and each should have its own stack that doesn’t touch any + // other thread’s stack. + for (size_t other_thread_index = 0; + other_thread_index < threads.size(); + ++other_thread_index) { + if (thread_index == other_thread_index) { + continue; + } + + const ProcessReader::Thread& other_thread = threads[other_thread_index]; + + EXPECT_NE(thread.id, other_thread.id); + EXPECT_NE(thread.port, other_thread.port); + + mach_vm_address_t other_thread_stack_region_end = + other_thread.stack_region_address + other_thread.stack_region_size; + EXPECT_FALSE( + thread.stack_region_address >= other_thread.stack_region_address && + thread.stack_region_address < other_thread_stack_region_end); + EXPECT_FALSE( + thread_stack_region_end > other_thread.stack_region_address && + thread_stack_region_end <= other_thread_stack_region_end); + } + } + + // Make sure that each expected thread was found. + EXPECT_TRUE(thread_map->empty()); +} + +TEST(ProcessReader, SelfSeveralThreads) { + // Set up the ProcessReader here, before any other threads are running. This + // tests that the threads it returns are lazily initialized as a snapshot of + // the threads at the time of the first call to Threads(), and not at the + // time the ProcessReader was created or initialized. + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + TestThreadPool thread_pool; + const size_t kChildThreads = 16; + ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads)); + + // Build a map of all expected threads, keyed by each thread’s ID. The values + // are addresses that should lie somewhere within each thread’s stack. + ThreadMap thread_map; + const uint64_t self_thread_id = PthreadToThreadID(pthread_self()); + TestThreadPool::ThreadExpectation expectation; + expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_map); + expectation.suspend_count = 0; + thread_map[self_thread_id] = expectation; + for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) { + uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation); + + // There can’t be any duplicate thread IDs. + EXPECT_EQ(0u, thread_map.count(thread_id)); + + thread_map[thread_id] = expectation; + } + + const std::vector<ProcessReader::Thread>& threads = process_reader.Threads(); + + // Other tests that have run previously may have resulted in the creation of + // threads that still exist, so pass true for |tolerate_extra_threads|. + ExpectSeveralThreads(&thread_map, threads, true); + + // When testing in-process, verify that when this thread shows up in the + // vector, it has the expected thread port, and that this thread port only + // shows up once. + thread_t thread_self = MachThreadSelf(); + bool found_thread_self = false; + for (const ProcessReader::Thread& thread : threads) { + if (thread.port == thread_self) { + EXPECT_FALSE(found_thread_self); + found_thread_self = true; + EXPECT_EQ(self_thread_id, thread.id); + } + } + EXPECT_TRUE(found_thread_self); +} + +class ProcessReaderThreadedChild final : public MachMultiprocess { + public: + explicit ProcessReaderThreadedChild(size_t thread_count) + : MachMultiprocess(), + thread_count_(thread_count) { + } + + ~ProcessReaderThreadedChild() {} + + private: + void MachMultiprocessParent() override { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + + FileHandle read_handle = ReadPipeHandle(); + + // Build a map of all expected threads, keyed by each thread’s ID, and with + // addresses that should lie somewhere within each thread’s stack as values. + // These IDs and addresses all come from the child process via the pipe. + ThreadMap thread_map; + for (size_t thread_index = 0; + thread_index < thread_count_ + 1; + ++thread_index) { + uint64_t thread_id; + CheckedReadFile(read_handle, &thread_id, sizeof(thread_id)); + + TestThreadPool::ThreadExpectation expectation; + CheckedReadFile(read_handle, + &expectation.stack_address, + sizeof(expectation.stack_address)); + CheckedReadFile(read_handle, + &expectation.suspend_count, + sizeof(expectation.suspend_count)); + + // There can’t be any duplicate thread IDs. + EXPECT_EQ(0u, thread_map.count(thread_id)); + + thread_map[thread_id] = expectation; + } + + const std::vector<ProcessReader::Thread>& threads = process_reader.Threads(); + + // The child shouldn’t have any threads other than its main thread and the + // ones it created in its pool, so pass false for |tolerate_extra_threads|. + ExpectSeveralThreads(&thread_map, threads, false); + } + + void MachMultiprocessChild() override { + TestThreadPool thread_pool; + ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_)); + + FileHandle write_handle = WritePipeHandle(); + + // This thread isn’t part of the thread pool, but the parent will be able + // to inspect it. Write an entry for it. + uint64_t thread_id = PthreadToThreadID(pthread_self()); + + CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id)); + + TestThreadPool::ThreadExpectation expectation; + expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id); + expectation.suspend_count = 0; + + CheckedWriteFile(write_handle, + &expectation.stack_address, + sizeof(expectation.stack_address)); + CheckedWriteFile(write_handle, + &expectation.suspend_count, + sizeof(expectation.suspend_count)); + + // Write an entry for everything in the thread pool. + for (size_t thread_index = 0; + thread_index < thread_count_; + ++thread_index) { + uint64_t thread_id = + thread_pool.GetThreadInfo(thread_index, &expectation); + + CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id)); + CheckedWriteFile(write_handle, + &expectation.stack_address, + sizeof(expectation.stack_address)); + CheckedWriteFile(write_handle, + &expectation.suspend_count, + sizeof(expectation.suspend_count)); + } + + // Wait for the parent to signal that it’s OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + size_t thread_count_; + + DISALLOW_COPY_AND_ASSIGN(ProcessReaderThreadedChild); +}; + +TEST(ProcessReader, ChildOneThread) { + // The main thread plus zero child threads equals one thread. + const size_t kChildThreads = 0; + ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); + process_reader_threaded_child.Run(); +} + +TEST(ProcessReader, ChildSeveralThreads) { + const size_t kChildThreads = 64; + ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); + process_reader_threaded_child.Run(); +} + +// cl_kernels images (OpenCL kernels) are weird. They’re not ld output and don’t +// exist as files on disk. On OS X 10.10 and 10.11, their Mach-O structure isn’t +// perfect. They show up loaded into many executables, so these quirks should be +// tolerated. +// +// Create an object of this class to ensure that at least one cl_kernels image +// is present in a process, to be able to test that all of the process-reading +// machinery tolerates them. On systems where cl_kernels modules have known +// quirks, the image that an object of this class produces will also have those +// quirks. +// +// https://openradar.appspot.com/20239912 +class ScopedOpenCLNoOpKernel { + public: + ScopedOpenCLNoOpKernel() + : context_(nullptr), + program_(nullptr), + kernel_(nullptr) { + } + + ~ScopedOpenCLNoOpKernel() { + if (kernel_) { + cl_int rv = clReleaseKernel(kernel_); + EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseKernel"; + } + + if (program_) { + cl_int rv = clReleaseProgram(program_); + EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseProgram"; + } + + if (context_) { + cl_int rv = clReleaseContext(context_); + EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseContext"; + } + } + + void SetUp() { + cl_platform_id platform_id; + cl_int rv = clGetPlatformIDs(1, &platform_id, nullptr); + ASSERT_EQ(CL_SUCCESS, rv) << "clGetPlatformIDs"; + + // Use CL_DEVICE_TYPE_CPU to ensure that the kernel would execute on the + // CPU. This is the only device type that a cl_kernels image will be created + // for. + cl_device_id device_id; + rv = + clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_CPU, 1, &device_id, nullptr); + ASSERT_EQ(CL_SUCCESS, rv) << "clGetDeviceIDs"; + + context_ = clCreateContext(nullptr, 1, &device_id, nullptr, nullptr, &rv); + ASSERT_EQ(CL_SUCCESS, rv) << "clCreateContext"; + + // The goal of the program in |sources| is to produce a cl_kernels image + // that doesn’t strictly conform to Mach-O expectations. On Mac OS X 10.10, + // cl_kernels modules show up with an __LD,__compact_unwind section, showing + // up in the __TEXT segment. MachOImageSegmentReader would normally reject + // modules for this problem, but a special exception is made when this + // occurs in cl_kernels images. This portion of the test is aimed at making + // sure that this exception works correctly. + // + // A true no-op program doesn’t actually produce unwind data, so there would + // be no errant __LD,__compact_unwind section on 10.10, and the test + // wouldn’t be complete. This simple no-op, which calls a built-in function, + // does produce unwind data provided optimization is disabled. + // "-cl-opt-disable" is given to clBuildProgram() below. + const char* sources[] = { + "__kernel void NoOp(void) {barrier(CLK_LOCAL_MEM_FENCE);}", + }; + const size_t source_lengths[] = { + strlen(sources[0]), + }; + static_assert(arraysize(sources) == arraysize(source_lengths), + "arrays must be parallel"); + + program_ = clCreateProgramWithSource( + context_, arraysize(sources), sources, source_lengths, &rv); + ASSERT_EQ(CL_SUCCESS, rv) << "clCreateProgramWithSource"; + + rv = clBuildProgram( + program_, 1, &device_id, "-cl-opt-disable", nullptr, nullptr); + ASSERT_EQ(CL_SUCCESS, rv) << "clBuildProgram"; + + kernel_ = clCreateKernel(program_, "NoOp", &rv); + ASSERT_EQ(CL_SUCCESS, rv) << "clCreateKernel"; + } + + private: + cl_context context_; + cl_program program_; + cl_kernel kernel_; + + DISALLOW_COPY_AND_ASSIGN(ScopedOpenCLNoOpKernel); +}; + +// Although Mac OS X 10.6 has OpenCL and can compile and execute OpenCL code, +// OpenCL kernels that run on the CPU do not result in cl_kernels images +// appearing on that OS version. +bool ExpectCLKernels() { +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 + return true; +#else + return MacOSXMinorVersion() >= 7; +#endif +} + +TEST(ProcessReader, SelfModules) { + ScopedOpenCLNoOpKernel ensure_cl_kernels; + ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + uint32_t dyld_image_count = _dyld_image_count(); + const std::vector<ProcessReader::Module>& modules = process_reader.Modules(); + + // There needs to be at least an entry for the main executable, for a dylib, + // and for dyld. + ASSERT_GE(modules.size(), 3u); + + // dyld_image_count doesn’t include an entry for dyld itself, but |modules| + // does. + ASSERT_EQ(dyld_image_count + 1, modules.size()); + + bool found_cl_kernels = false; + for (uint32_t index = 0; index < dyld_image_count; ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %u, name %s", index, modules[index].name.c_str())); + + const char* dyld_image_name = _dyld_get_image_name(index); + EXPECT_EQ(dyld_image_name, modules[index].name); + ASSERT_TRUE(modules[index].reader); + EXPECT_EQ( + reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)), + modules[index].reader->Address()); + + if (index == 0) { + // dyld didn’t load the main executable, so it couldn’t record its + // timestamp, and it is reported as 0. + EXPECT_EQ(0, modules[index].timestamp); + } else if (modules[index].reader->FileType() == MH_BUNDLE && + modules[index].name == "cl_kernels") { + // cl_kernels doesn’t exist as a file. + EXPECT_EQ(0, modules[index].timestamp); + found_cl_kernels = true; + } else { + // Hope that the module didn’t change on disk. + struct stat stat_buf; + int rv = stat(dyld_image_name, &stat_buf); + EXPECT_EQ(0, rv) << ErrnoMessage("stat"); + if (rv == 0) { + EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp); + } + } + } + + EXPECT_EQ(ExpectCLKernels(), found_cl_kernels); + + size_t index = modules.size() - 1; + EXPECT_EQ("/usr/lib/dyld", modules[index].name); + + // dyld didn’t load itself either, so it couldn’t record its timestamp, and it + // is also reported as 0. + EXPECT_EQ(0, modules[index].timestamp); + + const struct dyld_all_image_infos* dyld_image_infos = + _dyld_get_all_image_infos(); + if (dyld_image_infos->version >= 2) { + ASSERT_TRUE(modules[index].reader); + EXPECT_EQ( + reinterpret_cast<mach_vm_address_t>( + dyld_image_infos->dyldImageLoadAddress), + modules[index].reader->Address()); + } +} + +class ProcessReaderModulesChild final : public MachMultiprocess { + public: + ProcessReaderModulesChild() : MachMultiprocess() {} + + ~ProcessReaderModulesChild() {} + + private: + void MachMultiprocessParent() override { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + + const std::vector<ProcessReader::Module>& modules = + process_reader.Modules(); + + // There needs to be at least an entry for the main executable, for a dylib, + // and for dyld. + ASSERT_GE(modules.size(), 3u); + + FileHandle read_handle = ReadPipeHandle(); + + uint32_t expect_modules; + CheckedReadFile(read_handle, &expect_modules, sizeof(expect_modules)); + + ASSERT_EQ(expect_modules, modules.size()); + + bool found_cl_kernels = false; + for (size_t index = 0; index < modules.size(); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %zu, name %s", index, modules[index].name.c_str())); + + uint32_t expect_name_length; + CheckedReadFile( + read_handle, &expect_name_length, sizeof(expect_name_length)); + + // The NUL terminator is not read. + std::string expect_name(expect_name_length, '\0'); + CheckedReadFile(read_handle, &expect_name[0], expect_name_length); + EXPECT_EQ(expect_name, modules[index].name); + + mach_vm_address_t expect_address; + CheckedReadFile(read_handle, &expect_address, sizeof(expect_address)); + ASSERT_TRUE(modules[index].reader); + EXPECT_EQ(expect_address, modules[index].reader->Address()); + + if (index == 0 || index == modules.size() - 1) { + // dyld didn’t load the main executable or itself, so it couldn’t record + // these timestamps, and they are reported as 0. + EXPECT_EQ(0, modules[index].timestamp); + } else if (modules[index].reader->FileType() == MH_BUNDLE && + modules[index].name == "cl_kernels") { + // cl_kernels doesn’t exist as a file. + EXPECT_EQ(0, modules[index].timestamp); + found_cl_kernels = true; + } else { + // Hope that the module didn’t change on disk. + struct stat stat_buf; + int rv = stat(expect_name.c_str(), &stat_buf); + EXPECT_EQ(0, rv) << ErrnoMessage("stat"); + if (rv == 0) { + EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp); + } + } + } + + EXPECT_EQ(ExpectCLKernels(), found_cl_kernels); + } + + void MachMultiprocessChild() override { + FileHandle write_handle = WritePipeHandle(); + + uint32_t dyld_image_count = _dyld_image_count(); + const struct dyld_all_image_infos* dyld_image_infos = + _dyld_get_all_image_infos(); + + uint32_t write_image_count = dyld_image_count; + if (dyld_image_infos->version >= 2) { + // dyld_image_count doesn’t include an entry for dyld itself, but one will + // be written. + ++write_image_count; + } + + CheckedWriteFile( + write_handle, &write_image_count, sizeof(write_image_count)); + + for (size_t index = 0; index < write_image_count; ++index) { + const char* dyld_image_name; + mach_vm_address_t dyld_image_address; + + if (index < dyld_image_count) { + dyld_image_name = _dyld_get_image_name(index); + dyld_image_address = + reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)); + } else { + dyld_image_name = "/usr/lib/dyld"; + dyld_image_address = reinterpret_cast<mach_vm_address_t>( + dyld_image_infos->dyldImageLoadAddress); + } + + uint32_t dyld_image_name_length = strlen(dyld_image_name); + CheckedWriteFile(write_handle, + &dyld_image_name_length, + sizeof(dyld_image_name_length)); + + // The NUL terminator is not written. + CheckedWriteFile(write_handle, dyld_image_name, dyld_image_name_length); + + CheckedWriteFile( + write_handle, &dyld_image_address, sizeof(dyld_image_address)); + } + + // Wait for the parent to signal that it’s OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild); +}; + +TEST(ProcessReader, ChildModules) { + ScopedOpenCLNoOpKernel ensure_cl_kernels; + ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); + + ProcessReaderModulesChild process_reader_modules_child; + process_reader_modules_child.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc new file mode 100644 index 0000000..b37123c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc
@@ -0,0 +1,229 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/process_snapshot_mac.h" + +#include "base/logging.h" +#include "util/misc/tri_state.h" + +namespace crashpad { + +ProcessSnapshotMac::ProcessSnapshotMac() + : ProcessSnapshot(), + system_(), + threads_(), + modules_(), + exception_(), + process_reader_(), + report_id_(), + client_id_(), + annotations_simple_map_(), + snapshot_time_(), + initialized_() { +} + +ProcessSnapshotMac::~ProcessSnapshotMac() { +} + +bool ProcessSnapshotMac::Initialize(task_t task) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + + if (!process_reader_.Initialize(task)) { + return false; + } + + system_.Initialize(&process_reader_, &snapshot_time_); + + InitializeThreads(); + InitializeModules(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSnapshotMac::InitializeException( + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK(!exception_); + + exception_.reset(new internal::ExceptionSnapshotMac()); + if (!exception_->Initialize(&process_reader_, + behavior, + exception_thread, + exception, + code, + code_count, + flavor, + state, + state_count)) { + exception_.reset(); + return false; + } + + return true; +} + +void ProcessSnapshotMac::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (internal::ModuleSnapshotMac* module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + +pid_t ProcessSnapshotMac::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ProcessID(); +} + +pid_t ProcessSnapshotMac::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ParentProcessID(); +} + +void ProcessSnapshotMac::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotMac::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotMac::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotMac::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotMac::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map<std::string, std::string>& +ProcessSnapshotMac::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotMac::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector<const ThreadSnapshot*> ProcessSnapshotMac::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<const ThreadSnapshot*> threads; + for (internal::ThreadSnapshotMac* thread : threads_) { + threads.push_back(thread); + } + return threads; +} + +std::vector<const ModuleSnapshot*> ProcessSnapshotMac::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<const ModuleSnapshot*> modules; + for (internal::ModuleSnapshotMac* module : modules_) { + modules.push_back(module); + } + return modules; +} + +const ExceptionSnapshot* ProcessSnapshotMac::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMac::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector<const MemoryMapRegionSnapshot*>(); +} + +std::vector<HandleSnapshot> ProcessSnapshotMac::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector<HandleSnapshot>(); +} + +std::vector<const MemorySnapshot*> ProcessSnapshotMac::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector<const MemorySnapshot*>(); +} + +void ProcessSnapshotMac::InitializeThreads() { + const std::vector<ProcessReader::Thread>& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReader::Thread& process_reader_thread : + process_reader_threads) { + auto thread = make_scoped_ptr(new internal::ThreadSnapshotMac()); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(thread.release()); + } + } +} + +void ProcessSnapshotMac::InitializeModules() { + const std::vector<ProcessReader::Module>& process_reader_modules = + process_reader_.Modules(); + for (const ProcessReader::Module& process_reader_module : + process_reader_modules) { + auto module = make_scoped_ptr(new internal::ModuleSnapshotMac()); + if (module->Initialize(&process_reader_, process_reader_module)) { + modules_.push_back(module.release()); + } + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h new file mode 100644 index 0000000..dd5b9c3a --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h
@@ -0,0 +1,157 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_ + +#include <mach/mach.h> +#include <sys/time.h> +#include <unistd.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/mac/exception_snapshot_mac.h" +#include "snapshot/mac/module_snapshot_mac.h" +#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/system_snapshot_mac.h" +#include "snapshot/mac/thread_snapshot_mac.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a Mac +//! OS X system. +class ProcessSnapshotMac final : public ProcessSnapshot { + public: + ProcessSnapshotMac(); + ~ProcessSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] task The task to create a snapshot from. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(task_t task); + + //! \brief Initializes the object’s exception. + //! + //! This populates the data to be returned by Exception(). The parameters may + //! be passed directly through from a Mach exception handler. + //! + //! This method must not be called until after a successful call to + //! Initialize(). + //! + //! \return `true` if the exception information could be initialized, `false` + //! otherwise with an appropriate message logged. When this method returns + //! `false`, the ProcessSnapshotMac object’s validity remains unchanged. + bool InitializeException(exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! On Mac OS X, the crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! On Mac OS X, the client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! On Mac OS X, all process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! Contrast this with module annotations, which are under the control of the + //! process being snapshotted. + void SetAnnotationsSimpleMap( + const std::map<std::string, std::string>& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ProcessSnapshot: + + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map<std::string, std::string>& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector<const ThreadSnapshot*> Threads() const override; + std::vector<const ModuleSnapshot*> Modules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override; + std::vector<HandleSnapshot> Handles() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: + // Initializes threads_ on behalf of Initialize(). + void InitializeThreads(); + + // Initializes modules_ on behalf of Initialize(). + void InitializeModules(); + + internal::SystemSnapshotMac system_; + PointerVector<internal::ThreadSnapshotMac> threads_; + PointerVector<internal::ModuleSnapshotMac> modules_; + scoped_ptr<internal::ExceptionSnapshotMac> exception_; + ProcessReader process_reader_; + UUID report_id_; + UUID client_id_; + std::map<std::string, std::string> annotations_simple_map_; + timeval snapshot_time_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMac); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types.cc new file mode 100644 index 0000000..0fc14f0 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
@@ -0,0 +1,304 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/process_types.h" + +#include <string.h> +#include <uuid/uuid.h> + +#include "base/memory/scoped_ptr.h" +#include "snapshot/mac/process_types/internal.h" +#include "util/mach/task_memory.h" + +namespace crashpad { +namespace { + +// Assign() is used by each flavor's ReadInto implementation to copy data from a +// specific struct to a generic struct. For fundamental types, the assignment +// operator suffices. For other types such as arrays, an explicit Assign +// specialization is needed, typically performing a copy. + +template <typename DestinationType, typename SourceType> +inline void Assign(DestinationType* destination, const SourceType& source) { + *destination = source; +} + +template <> +inline void Assign<process_types::internal::Reserved64Only64, + process_types::internal::Reserved64Only32>( + process_types::internal::Reserved64Only64* destination, + const process_types::internal::Reserved64Only32& source) { + // Reserved64Only32 carries no data. + *destination = 0; +} + +using CharArray16 = char[16]; +template <> +inline void Assign<CharArray16, CharArray16>(CharArray16* destination, + const CharArray16& source) { + memcpy(destination, &source, sizeof(source)); +} + +using UInt64Array16 = uint64_t[16]; +template <> +inline void Assign<UInt64Array16, UInt64Array16>(UInt64Array16* destination, + const UInt64Array16& source) { + memcpy(destination, &source, sizeof(source)); +} + +using UInt32Array16 = uint32_t[16]; +template <> +inline void Assign<UInt64Array16, UInt32Array16>(UInt64Array16* destination, + const UInt32Array16& source) { + for (size_t index = 0; index < arraysize(source); ++index) { + (*destination)[index] = source[index]; + } +} + +template <> +inline void Assign<uuid_t, uuid_t>(uuid_t* destination, const uuid_t& source) { + // uuid_t is a type alias for unsigned char[16]. + memcpy(destination, &source, sizeof(source)); +} + +} // namespace +} // namespace crashpad + +// Implement the generic crashpad::process_types::struct_name ReadInto(), which +// delegates to the templatized ReadIntoInternal(), which reads the specific +// struct type from the remote process and genericizes it. Also implement +// crashpad::process_types::internal::struct_name<> GenericizeInto(), which +// operates on each member in the struct. +#define PROCESS_TYPE_STRUCT_IMPLEMENT 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + \ + /* static */ \ + size_t struct_name::ExpectedSize(ProcessReader* process_reader) { \ + if (!process_reader->Is64Bit()) { \ + return internal::struct_name<internal::Traits32>::Size(); \ + } else { \ + return internal::struct_name<internal::Traits64>::Size(); \ + } \ + } \ + \ + /* static */ \ + bool struct_name::ReadInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic) { \ + if (!process_reader->Is64Bit()) { \ + return ReadIntoInternal<internal::struct_name<internal::Traits32> >( \ + process_reader, address, generic); \ + } else { \ + return ReadIntoInternal<internal::struct_name<internal::Traits64> >( \ + process_reader, address, generic); \ + } \ + } \ + \ + /* static */ \ + template <typename T> \ + bool struct_name::ReadIntoInternal(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic) { \ + T specific; \ + if (!specific.Read(process_reader, address)) { \ + return false; \ + } \ + specific.GenericizeInto(generic, &generic->size_); \ + return true; \ + } \ + \ + namespace internal { \ + \ + template <typename Traits> \ + void struct_name<Traits>::GenericizeInto( \ + process_types::struct_name* generic, \ + size_t* specific_size) { \ + *specific_size = Size(); + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ + Assign(&generic->member_name, member_name); + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + } \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT + +// Implement the specific crashpad::process_types::internal::struct_name<> +// ReadInto(). The default implementation simply reads the struct from the +// remote process. This is separated from other method implementations because +// some types may wish to provide custom readers. This can be done by guarding +// such types’ proctype definitions against this macro and providing custom +// implementations in snapshot/mac/process_types/custom.cc. +#define PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + \ + /* static */ \ + template <typename Traits> \ + bool struct_name<Traits>::ReadInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + struct_name<Traits>* specific) { \ + return process_reader->Memory()->Read( \ + address, sizeof(*specific), specific); \ + } \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO + +// Implement the array operations. These are separated from other method +// implementations because some types are variable-length and are never stored +// as direct-access arrays. It would be incorrect to provide reader +// implementations for such types. Types that wish to suppress array operations +// can do so by guarding their proctype definitions against this macro. +#define PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + \ + /* static */ \ + template <typename Traits> \ + bool struct_name<Traits>::ReadArrayInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name<Traits>* specific) { \ + return process_reader->Memory()->Read( \ + address, sizeof(struct_name<Traits>[count]), specific); \ + } \ + \ + } /* namespace internal */ \ + \ + /* static */ \ + bool struct_name::ReadArrayInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic) { \ + if (!process_reader->Is64Bit()) { \ + return ReadArrayIntoInternal< \ + internal::struct_name<internal::Traits32> >( \ + process_reader, address, count, generic); \ + } else { \ + return ReadArrayIntoInternal< \ + internal::struct_name<internal::Traits64> >( \ + process_reader, address, count, generic); \ + } \ + return true; \ + } \ + \ + /* static */ \ + template <typename T> \ + bool struct_name::ReadArrayIntoInternal(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic) { \ + scoped_ptr<T[]> specific(new T[count]); \ + if (!T::ReadArrayInto(process_reader, address, count, &specific[0])) { \ + return false; \ + } \ + for (size_t index = 0; index < count; ++index) { \ + specific[index].GenericizeInto(&generic[index], &generic[index].size_); \ + } \ + return true; \ + } \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY + +// Implement the generic crashpad::process_types::struct_name +// ExpectedSizeForVersion(), which delegates to the templatized +// ExpectedSizeForVersion(), which returns the expected size of a versioned +// structure given a version parameter. This is only implemented for structures +// that use PROCESS_TYPE_STRUCT_VERSIONED(), and implementations of the internal +// templatized functions must be provided in +// snapshot/mac/process_types/custom.cc. +#define PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + namespace crashpad { \ + namespace process_types { \ + \ + /* static */ \ + size_t struct_name::ExpectedSizeForVersion( \ + ProcessReader* process_reader, \ + decltype(struct_name::version_field) version) { \ + if (!process_reader->Is64Bit()) { \ + return internal::struct_name< \ + internal::Traits32>::ExpectedSizeForVersion(version); \ + } else { \ + return internal::struct_name< \ + internal::Traits64>::ExpectedSizeForVersion(version); \ + } \ + } \ + \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types.h b/third_party/crashpad/crashpad/snapshot/mac/process_types.h new file mode 100644 index 0000000..a1039dd --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types.h
@@ -0,0 +1,199 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_ + +#include <mach/mach.h> +#include <mach-o/dyld_images.h> +#include <mach-o/loader.h> +#include <stdint.h> +#include <sys/types.h> + +#include "snapshot/mac/process_reader.h" + +namespace crashpad { +namespace process_types { +namespace internal { + +// Some structure definitions differ in 32-bit and 64-bit environments by having +// additional “reserved” padding fields present only in the 64-bit environment. +// These Reserved64Only* types allow the process_types system to replicate these +// structures more precisely. +using Reserved64Only32 = char[0]; +using Reserved64Only64 = uint32_t; + +} // namespace internal +} // namespace process_types +} // namespace crashpad + +#include "snapshot/mac/process_types/traits.h" + +// Creates the traits type crashpad::process_types::internal::TraitsGeneric. +DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) + +#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS + +// Declare the crashpad::process_types::struct_name structs. These are the +// user-visible generic structs that callers will interact with. They read data +// from 32-bit or 64-bit processes by using the specific internal templatized +// structs below. +#define PROCESS_TYPE_STRUCT_DECLARE 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + struct struct_name { \ + public: \ + using Long = internal::TraitsGeneric::Long; \ + using ULong = internal::TraitsGeneric::ULong; \ + using Pointer = internal::TraitsGeneric::Pointer; \ + using IntPtr = internal::TraitsGeneric::IntPtr; \ + using UIntPtr = internal::TraitsGeneric::UIntPtr; \ + using Reserved64Only = internal::TraitsGeneric::Reserved64Only; \ + \ + /* Initializes an object with data read from |process_reader| at \ + * |address|, properly genericized. */ \ + bool Read(ProcessReader* process_reader, mach_vm_address_t address) { \ + return ReadInto(process_reader, address, this); \ + } \ + \ + /* Reads |count| objects from |process_reader| beginning at |address|, and \ + * genericizes the objects. The caller must provide storage for |count| \ + * objects in |generic|. */ \ + static bool ReadArrayInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic); \ + \ + /* Returns the size of the object that was read. This is the size of the \ + * storage in the process that the data is read from, and is not the same \ + * as the size of the generic struct. */ \ + size_t Size() const { return size_; } \ + \ + /* Similar to Size(), but computes the expected size of a structure based \ + * on the process’ bitness. This can be used prior to reading any data \ + * from a process. */ \ + static size_t ExpectedSize(ProcessReader* process_reader); \ + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ + member_type member_name __VA_ARGS__; + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + /* Similar to ExpectedSize(), but computes the expected size of a \ + * structure based on the process’ bitness and a custom value, such as a \ + * structure version number. This can be used prior to reading any data \ + * from a process. */ \ + static size_t ExpectedSizeForVersion( \ + ProcessReader* process_reader, \ + decltype(struct_name::version_field) version); + +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + private: \ + /* The static form of Read(). Populates the struct at |generic|. */ \ + static bool ReadInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic); \ + \ + template <typename T> \ + static bool ReadIntoInternal(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic); \ + template <typename T> \ + static bool ReadArrayIntoInternal(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic); \ + size_t size_; \ + }; \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_DECLARE + +// Declare the templatized crashpad::process_types::internal::struct_name<> +// structs. These are the 32-bit and 64-bit specific structs that describe the +// layout of objects in another process. This is repeated instead of being +// shared with the generic declaration above because both the generic and +// templatized specific structs need all of the struct members declared. +// +// GenericizeInto() translates a struct from the representation used in the +// remote process into the generic form. +#define PROCESS_TYPE_STRUCT_DECLARE_INTERNAL 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + template <typename Traits> \ + struct struct_name { \ + public: \ + using Long = typename Traits::Long; \ + using ULong = typename Traits::ULong; \ + using Pointer = typename Traits::Pointer; \ + using IntPtr = typename Traits::IntPtr; \ + using UIntPtr = typename Traits::UIntPtr; \ + using Reserved64Only = typename Traits::Reserved64Only; \ + \ + /* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \ + * struct above. */ \ + bool Read(ProcessReader* process_reader, mach_vm_address_t address) { \ + return ReadInto(process_reader, address, this); \ + } \ + static bool ReadArrayInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name<Traits>* specific); \ + static size_t Size() { return sizeof(struct_name<Traits>); } \ + \ + /* Translates a struct from the representation used in the remote process \ + * into the generic form. */ \ + void GenericizeInto(process_types::struct_name* generic, \ + size_t* specific_size); + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ + member_type member_name __VA_ARGS__; + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + /* ExpectedSizeForVersion() is as in the generic user-visible struct \ + * above. */ \ + static size_t ExpectedSizeForVersion( \ + decltype(struct_name::version_field) version); + +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + private: \ + /* ReadInto() is as in the generic user-visible struct above. */ \ + static bool ReadInto(ProcessReader* process_reader, \ + mach_vm_address_t address, \ + struct_name<Traits>* specific); \ + }; \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_DECLARE_INTERNAL + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype new file mode 100644 index 0000000..7ef54d5 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype
@@ -0,0 +1,26 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +#include "snapshot/mac/process_types/crashpad_info.proctype" +#include "snapshot/mac/process_types/crashreporterclient.proctype" +#include "snapshot/mac/process_types/dyld_images.proctype" +#include "snapshot/mac/process_types/loader.proctype" +#include "snapshot/mac/process_types/nlist.proctype"
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype new file mode 100644 index 0000000..cdc8247fc --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype
@@ -0,0 +1,39 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// The file corresponds to Crashpad’s client/crashpad_info.h. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +// Client Mach-O images will contain a __DATA,__crashpad_info section formatted +// according to this structure. +PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, crashpad_handler_behavior) // TriState + + // TriState + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding) + + PROCESS_TYPE_STRUCT_MEMBER(uint16_t, padding_0) + + // SimpleStringDictionary* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations) +PROCESS_TYPE_STRUCT_END(CrashpadInfo)
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype new file mode 100644 index 0000000..b40aa35 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype
@@ -0,0 +1,59 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// The name of this file was chosen based on +// http://llvm.org/svn/llvm-project/llvm/trunk/lib/Support/PrettyStackTrace.cpp. +// The name of the structure it describes was chosen based on that file as well +// as 10.9.2 cups-372.2/cups/backend/usb-darwin.c. That file also provided the +// names and types of the fields in the structure. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +// Client Mach-O images will contain a __DATA,__crash_info section formatted +// according to this structure. +// +// crashreporter_annotations_t is variable-length. Its length dictated by its +// |version| field which is always present. A custom implementation of the +// flavored ReadSpecificInto function that understands how to map this field to +// the structure’s actual size is provided in +// snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto is +// provided because dyld_all_image_infos structs are singletons in a process and +// are never present in arrays, so the functionality is unnecessary. + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ + !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + +PROCESS_TYPE_STRUCT_BEGIN(crashreporter_annotations_t) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version) // unsigned long + PROCESS_TYPE_STRUCT_VERSIONED(crashreporter_annotations_t, version) + + // Version 4 (Mac OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, signature_string) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, backtrace) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message2) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, thread) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, dialog_mode) // unsigned int + + // Version 5 (OS X 10.11) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, unknown_0) +PROCESS_TYPE_STRUCT_END(crashreporter_annotations_t) + +#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO && + // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc new file mode 100644 index 0000000..6d6fd0db --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
@@ -0,0 +1,158 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/process_types.h" + +#include <string.h> + +#include "base/logging.h" +#include "snapshot/mac/process_types/internal.h" +#include "util/mach/task_memory.h" + +namespace crashpad { +namespace process_types { +namespace internal { + +namespace { + +template <typename T> +bool ReadIntoVersioned(ProcessReader* process_reader, + mach_vm_address_t address, + T* specific) { + TaskMemory* task_memory = process_reader->Memory(); + if (!task_memory->Read( + address, sizeof(specific->version), &specific->version)) { + return false; + } + + mach_vm_size_t size = T::ExpectedSizeForVersion(specific->version); + + if (!task_memory->Read(address, size, specific)) { + return false; + } + + // Zero out the rest of the structure in case anything accesses fields without + // checking the version. + size_t remaining = sizeof(*specific) - size; + if (remaining > 0) { + char* start = reinterpret_cast<char*>(specific) + size; + memset(start, 0, remaining); + } + + return true; +} + +} // namespace + +// static +template <typename Traits> +size_t dyld_all_image_infos<Traits>::ExpectedSizeForVersion( + decltype(dyld_all_image_infos<Traits>::version) version) { + if (version >= 14) { + return sizeof(dyld_all_image_infos<Traits>); + } + if (version >= 13) { + return offsetof(dyld_all_image_infos<Traits>, reserved); + } + if (version >= 12) { + return offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID); + } + if (version >= 11) { + return offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide); + } + if (version >= 10) { + return offsetof(dyld_all_image_infos<Traits>, errorKind); + } + if (version >= 9) { + return offsetof(dyld_all_image_infos<Traits>, initialImageCount); + } + if (version >= 8) { + return offsetof(dyld_all_image_infos<Traits>, dyldAllImageInfosAddress); + } + if (version >= 7) { + return offsetof(dyld_all_image_infos<Traits>, uuidArrayCount); + } + if (version >= 6) { + return offsetof(dyld_all_image_infos<Traits>, systemOrderFlag); + } + if (version >= 5) { + return offsetof(dyld_all_image_infos<Traits>, coreSymbolicationShmPage); + } + if (version >= 3) { + return offsetof(dyld_all_image_infos<Traits>, dyldVersion); + } + if (version >= 2) { + return offsetof(dyld_all_image_infos<Traits>, jitInfo); + } + if (version >= 1) { + return offsetof(dyld_all_image_infos<Traits>, libSystemInitialized); + } + return offsetof(dyld_all_image_infos<Traits>, infoArrayCount); +} + +// static +template <typename Traits> +bool dyld_all_image_infos<Traits>::ReadInto( + ProcessReader* process_reader, + mach_vm_address_t address, + dyld_all_image_infos<Traits>* specific) { + return ReadIntoVersioned(process_reader, address, specific); +} + +// static +template <typename Traits> +size_t crashreporter_annotations_t<Traits>::ExpectedSizeForVersion( + decltype(crashreporter_annotations_t<Traits>::version) version) { + if (version >= 5) { + return sizeof(crashreporter_annotations_t<Traits>); + } + if (version >= 4) { + return offsetof(crashreporter_annotations_t<Traits>, unknown_0); + } + return offsetof(crashreporter_annotations_t<Traits>, message); +} + +// static +template <typename Traits> +bool crashreporter_annotations_t<Traits>::ReadInto( + ProcessReader* process_reader, + mach_vm_address_t address, + crashreporter_annotations_t<Traits>* specific) { + return ReadIntoVersioned(process_reader, address, specific); +} + +// Explicit template instantiation of the above. +#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ + template size_t \ + dyld_all_image_infos<Traits##lp_bits>::ExpectedSizeForVersion( \ + decltype(dyld_all_image_infos<Traits##lp_bits>::version)); \ + template bool dyld_all_image_infos<Traits##lp_bits>::ReadInto( \ + ProcessReader*, \ + mach_vm_address_t, \ + dyld_all_image_infos<Traits##lp_bits>*); \ + template size_t \ + crashreporter_annotations_t<Traits##lp_bits>::ExpectedSizeForVersion( \ + decltype(crashreporter_annotations_t<Traits##lp_bits>::version)); \ + template bool crashreporter_annotations_t<Traits##lp_bits>::ReadInto( \ + ProcessReader*, \ + mach_vm_address_t, \ + crashreporter_annotations_t<Traits##lp_bits>*); + +#include "snapshot/mac/process_types/flavors.h" + +#undef PROCESS_TYPE_FLAVOR_TRAITS + +} // namespace internal +} // namespace process_types +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype new file mode 100644 index 0000000..99be27c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype
@@ -0,0 +1,111 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file corresponds to the system’s <mach-o/dyld_images.h>. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +PROCESS_TYPE_STRUCT_BEGIN(dyld_image_info) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress) // const mach_header* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageFilePath) // const char* + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, imageFileModDate) +PROCESS_TYPE_STRUCT_END(dyld_image_info) + +PROCESS_TYPE_STRUCT_BEGIN(dyld_uuid_info) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress) // const mach_header* + PROCESS_TYPE_STRUCT_MEMBER(uuid_t, imageUUID) +PROCESS_TYPE_STRUCT_END(dyld_uuid_info) + +// dyld_all_image_infos is variable-length. Its length dictated by its |version| +// field which is always present. A custom implementation of the flavored +// ReadSpecificInto function that understands how to map this field to the +// structure’s actual size is provided in snapshot/mac/process_types/custom.cc. +// No implementation of ReadArrayInto is provided because dyld_all_image_infos +// structs are singletons in a process and are never present in arrays, so the +// functionality is unnecessary. + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ + !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + +PROCESS_TYPE_STRUCT_BEGIN(dyld_all_image_infos) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version) + PROCESS_TYPE_STRUCT_VERSIONED(dyld_all_image_infos, version) + + // Version 1 (Mac OS X 10.4) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, infoArrayCount) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, infoArray) // const dyld_image_info* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, notification) // function pointer + PROCESS_TYPE_STRUCT_MEMBER(bool, processDetachedFromSharedRegion) + + // Version 2 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(bool, libSystemInitialized) + + // This field does not appear in the system’s structure definition but is + // necessary to ensure that when building in 32-bit mode, the 64-bit version + // of the process_types structure matches the genuine 64-bit structure. This + // is required because the alignment constraints on 64-bit types are more + // stringent in 64-bit mode. + PROCESS_TYPE_STRUCT_MEMBER(Reserved64Only, alignment) + + // const mach_header* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldImageLoadAddress) + + // Version 3 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, jitInfo) // void* + + // Version 5 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldVersion) // const char* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorMessage) // const char* + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, terminationFlags) + + // Version 6 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, coreSymbolicationShmPage) // void* + + // Version 7 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, systemOrderFlag) + + // Version 8 (Mac OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, uuidArrayCount) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, uuidArray) // const dyld_uuid_info* + + // Version 9 (Mac OS X 10.7) + // dyld_all_image_infos* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldAllImageInfosAddress) + + // Version 10 (Mac OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, initialImageCount) + + // Version 11 (Mac OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, errorKind) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorClientOfDylibPath) // const char* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorTargetDylibPath) // const char* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorSymbol) // const char* + + // Version 12 (Mac OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, sharedCacheSlide) + + // Version 13 (Mac OS X 10.9) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, sharedCacheUUID, [16]) + + // Version 14 (Mac OS X 10.9) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [16]) +PROCESS_TYPE_STRUCT_END(dyld_all_image_infos) + +#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO && + // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h b/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h new file mode 100644 index 0000000..01ab2bd --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h
@@ -0,0 +1,24 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types/internal.h to produce +// process type flavor traits class declarations and by +// snapshot/mac/process_types/custom.cc to provide explicit instantiation of +// flavored implementations. + +PROCESS_TYPE_FLAVOR_TRAITS(32) +PROCESS_TYPE_FLAVOR_TRAITS(64)
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h b/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h new file mode 100644 index 0000000..fd956574 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h
@@ -0,0 +1,36 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_ + +#include "snapshot/mac/process_types.h" + +// Declare Traits32 and Traits64, flavor-specific traits classes. These are +// private traits classes not for use outside of process type internals. +// TraitsGeneric is declared in snapshot/mac/process_types.h. + +#include "snapshot/mac/process_types/traits.h" + +#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ + DECLARE_PROCESS_TYPE_TRAITS_CLASS( \ + lp_bits, lp_bits, __attribute__((aligned(lp_bits / 8)))) + +#include "snapshot/mac/process_types/flavors.h" + +#undef PROCESS_TYPE_FLAVOR_TRAITS + +#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype new file mode 100644 index 0000000..177e6f7 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype
@@ -0,0 +1,140 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file corresponds to the system’s <mach-o/loader.h>. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +PROCESS_TYPE_STRUCT_BEGIN(mach_header) // 64-bit: mach_header_64 + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, magic) + + // cputype is really cpu_type_t, a typedef for integer_t, itself a typedef for + // int. It is currently reasonable to assume that int is int32_t. + PROCESS_TYPE_STRUCT_MEMBER(int32_t, cputype) + + // cpusubtype is really cpu_subtype_t, a typedef for integer_t, itself a + // typedef for int. It is currently reasonable to assume that int is int32_t. + PROCESS_TYPE_STRUCT_MEMBER(int32_t, cpusubtype) + + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, filetype) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ncmds) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, sizeofcmds) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64Only, reserved) +PROCESS_TYPE_STRUCT_END(mach_header) + +PROCESS_TYPE_STRUCT_BEGIN(load_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) +PROCESS_TYPE_STRUCT_END(load_command) + +PROCESS_TYPE_STRUCT_BEGIN(segment_command) // 64-bit: segment_command_64 + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + + // This string is not necessarily NUL-terminated. + PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16]) + + PROCESS_TYPE_STRUCT_MEMBER(ULong, vmaddr) + PROCESS_TYPE_STRUCT_MEMBER(ULong, vmsize) + PROCESS_TYPE_STRUCT_MEMBER(ULong, fileoff) + PROCESS_TYPE_STRUCT_MEMBER(ULong, filesize) + PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, maxprot) + PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, initprot) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsects) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags) +PROCESS_TYPE_STRUCT_END(segment_command) + +PROCESS_TYPE_STRUCT_BEGIN(dylib_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + + // The following come from the dylib struct member of dylib_command. + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_name) // lc_str + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_timestamp) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_current_version) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_compatibility_version) +PROCESS_TYPE_STRUCT_END(dylib_command) + +PROCESS_TYPE_STRUCT_BEGIN(dylinker_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, name) // lc_str +PROCESS_TYPE_STRUCT_END(dylinker_command) + +PROCESS_TYPE_STRUCT_BEGIN(symtab_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, symoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsyms) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, stroff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, strsize) +PROCESS_TYPE_STRUCT_END(symtab_command) + +PROCESS_TYPE_STRUCT_BEGIN(dysymtab_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ilocalsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocalsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iextdefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextdefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iundefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nundefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, tocoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ntoc) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, modtaboff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nmodtab) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extrefsymoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrefsyms) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectsymoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nindirectsyms) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extreloff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrel) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, locreloff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocrel) +PROCESS_TYPE_STRUCT_END(dysymtab_command) + +PROCESS_TYPE_STRUCT_BEGIN(uuid_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, uuid, [16]) +PROCESS_TYPE_STRUCT_END(uuid_command) + +PROCESS_TYPE_STRUCT_BEGIN(source_version_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version) +PROCESS_TYPE_STRUCT_END(source_version_command) + +PROCESS_TYPE_STRUCT_BEGIN(section) // 64-bit: section_64 + // These strings are not necessarily NUL-terminated. + PROCESS_TYPE_STRUCT_MEMBER(char, sectname, [16]) + PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16]) + + PROCESS_TYPE_STRUCT_MEMBER(ULong, addr) + PROCESS_TYPE_STRUCT_MEMBER(ULong, size) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, offset) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, align) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reloff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nreloc) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved1) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved2) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64Only, reserved3) +PROCESS_TYPE_STRUCT_END(section)
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype new file mode 100644 index 0000000..c8989139 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype
@@ -0,0 +1,30 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file corresponds to the system’s <mach-o/nlist.h>. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +PROCESS_TYPE_STRUCT_BEGIN(nlist) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, n_strx) // n_un.n_strx + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_type) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_sect) + PROCESS_TYPE_STRUCT_MEMBER(uint16_t, n_desc) // 32-bit: int16_t + PROCESS_TYPE_STRUCT_MEMBER(ULong, n_value) +PROCESS_TYPE_STRUCT_END(nlist)
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h b/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h new file mode 100644 index 0000000..0b4d273 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h
@@ -0,0 +1,42 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types/internal.h to produce traits class definitions. + +// Things that #include this file should #undef +// DECLARE_PROCESS_TYPE_TRAITS_CLASS before #including this file again and after +// the last #include of this file. +// +// |Reserved| is used for padding fields that may be zero-length, and thus +// __VA_ARGS__, which is intended to set the alignment of the 64-bit types, is +// not used for that type alias. +#define DECLARE_PROCESS_TYPE_TRAITS_CLASS(traits_name, lp_bits, ...) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + struct Traits##traits_name { \ + using Long = int##lp_bits##_t __VA_ARGS__; \ + using ULong = uint##lp_bits##_t __VA_ARGS__; \ + using Pointer = uint##lp_bits##_t __VA_ARGS__; \ + using IntPtr = int##lp_bits##_t __VA_ARGS__; \ + using UIntPtr = uint##lp_bits##_t __VA_ARGS__; \ + using Reserved64Only = Reserved64Only##lp_bits; \ + }; \ + } \ + } \ + }
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc new file mode 100644 index 0000000..7252c5c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
@@ -0,0 +1,269 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/process_types.h" + +#include <AvailabilityMacros.h> +#include <mach/mach.h> +#include <string.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/mac/dyld.h" +#include "util/mac/mac_util.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +#define TEST_STRING(process_reader, self_view, proctype_view, field) \ + do { \ + if (self_view->field) { \ + std::string proctype_string; \ + ASSERT_TRUE(process_reader.Memory()->ReadCString(proctype_view.field, \ + &proctype_string)); \ + EXPECT_EQ(self_view->field, proctype_string); \ + } \ + } while (false) + +TEST(ProcessTypes, DyldImagesSelf) { + // Get the in-process view of dyld_all_image_infos, and check it for sanity. + const struct dyld_all_image_infos* self_image_infos = + _dyld_get_all_image_infos(); + int mac_os_x_minor_version = MacOSXMinorVersion(); + if (mac_os_x_minor_version >= 9) { + EXPECT_GE(self_image_infos->version, 13u); + } else if (mac_os_x_minor_version >= 7) { + EXPECT_GE(self_image_infos->version, 8u); + } else if (mac_os_x_minor_version >= 6) { + EXPECT_GE(self_image_infos->version, 2u); + } else { + EXPECT_GE(self_image_infos->version, 1u); + } + + EXPECT_GT(self_image_infos->infoArrayCount, 1u); + if (self_image_infos->version >= 2) { + EXPECT_TRUE(self_image_infos->libSystemInitialized); + } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (self_image_infos->version >= 9) { + EXPECT_EQ(self_image_infos, self_image_infos->dyldAllImageInfosAddress); + } +#endif + + // Get the out-of-process view of dyld_all_image_infos, and work with it + // through the process_types interface. + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = task_info(mach_task_self(), + TASK_DYLD_INFO, + reinterpret_cast<task_info_t>(&dyld_info), + &count); + ASSERT_EQ(KERN_SUCCESS, kr); + + EXPECT_EQ(reinterpret_cast<mach_vm_address_t>(self_image_infos), + dyld_info.all_image_info_addr); + EXPECT_GT(dyld_info.all_image_info_size, 1u); + + // This field is only present in the Mac OS X 10.7 SDK (at build time) and + // kernel (at run time). +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (MacOSXMinorVersion() >= 7) { +#if !defined(ARCH_CPU_64_BITS) + EXPECT_EQ(TASK_DYLD_ALL_IMAGE_INFO_32, dyld_info.all_image_info_format); +#else + EXPECT_EQ(TASK_DYLD_ALL_IMAGE_INFO_64, dyld_info.all_image_info_format); +#endif + } +#endif + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + process_types::dyld_all_image_infos proctype_image_infos; + ASSERT_TRUE(proctype_image_infos.Read(&process_reader, + dyld_info.all_image_info_addr)); + + ASSERT_EQ(self_image_infos->version, proctype_image_infos.version); + + if (proctype_image_infos.version >= 1) { + EXPECT_EQ(self_image_infos->infoArrayCount, + proctype_image_infos.infoArrayCount); + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->infoArray), + proctype_image_infos.infoArray); + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->notification), + proctype_image_infos.notification); + EXPECT_EQ(self_image_infos->processDetachedFromSharedRegion, + proctype_image_infos.processDetachedFromSharedRegion); + } + if (proctype_image_infos.version >= 2) { + EXPECT_EQ(self_image_infos->libSystemInitialized, + proctype_image_infos.libSystemInitialized); + EXPECT_EQ( + reinterpret_cast<uint64_t>(self_image_infos->dyldImageLoadAddress), + proctype_image_infos.dyldImageLoadAddress); + } + if (proctype_image_infos.version >= 3) { + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->jitInfo), + proctype_image_infos.jitInfo); + } + if (proctype_image_infos.version >= 5) { + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->dyldVersion), + proctype_image_infos.dyldVersion); + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->errorMessage), + proctype_image_infos.errorMessage); + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->terminationFlags), + proctype_image_infos.terminationFlags); + + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, dyldVersion); + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, errorMessage); + } + if (proctype_image_infos.version >= 6) { + EXPECT_EQ( + reinterpret_cast<uint64_t>(self_image_infos->coreSymbolicationShmPage), + proctype_image_infos.coreSymbolicationShmPage); + } + if (proctype_image_infos.version >= 7) { + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->systemOrderFlag), + proctype_image_infos.systemOrderFlag); + } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (proctype_image_infos.version >= 8) { + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->uuidArrayCount), + proctype_image_infos.uuidArrayCount); + } + if (proctype_image_infos.version >= 9) { + EXPECT_EQ( + reinterpret_cast<uint64_t>(self_image_infos->dyldAllImageInfosAddress), + proctype_image_infos.dyldAllImageInfosAddress); + } + if (proctype_image_infos.version >= 10) { + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->initialImageCount), + proctype_image_infos.initialImageCount); + } + if (proctype_image_infos.version >= 11) { + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->errorKind), + proctype_image_infos.errorKind); + EXPECT_EQ( + reinterpret_cast<uint64_t>(self_image_infos->errorClientOfDylibPath), + proctype_image_infos.errorClientOfDylibPath); + EXPECT_EQ( + reinterpret_cast<uint64_t>(self_image_infos->errorTargetDylibPath), + proctype_image_infos.errorTargetDylibPath); + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->errorSymbol), + proctype_image_infos.errorSymbol); + + TEST_STRING(process_reader, + self_image_infos, + proctype_image_infos, + errorClientOfDylibPath); + TEST_STRING(process_reader, + self_image_infos, + proctype_image_infos, + errorTargetDylibPath); + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, errorSymbol); + } + if (proctype_image_infos.version >= 12) { + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->sharedCacheSlide), + proctype_image_infos.sharedCacheSlide); + } +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 + if (proctype_image_infos.version >= 13) { + EXPECT_EQ(0, + memcmp(self_image_infos->sharedCacheUUID, + proctype_image_infos.sharedCacheUUID, + sizeof(self_image_infos->sharedCacheUUID))); + } + if (proctype_image_infos.version >= 14) { + for (size_t index = 0; index < arraysize(self_image_infos->reserved); + ++index) { + EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->reserved[index]), + proctype_image_infos.reserved[index]) + << "index " << index; + } + } +#endif + + if (proctype_image_infos.version >= 1) { + std::vector<process_types::dyld_image_info> proctype_image_info_vector( + proctype_image_infos.infoArrayCount); + ASSERT_TRUE(process_types::dyld_image_info::ReadArrayInto( + &process_reader, + proctype_image_infos.infoArray, + proctype_image_info_vector.size(), + &proctype_image_info_vector[0])); + + for (size_t index = 0; index < proctype_image_infos.infoArrayCount; + ++index) { + const dyld_image_info* self_image_info = + &self_image_infos->infoArray[index]; + const process_types::dyld_image_info& proctype_image_info = + proctype_image_info_vector[index]; + + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_info->imageLoadAddress), + proctype_image_info.imageLoadAddress) + << "index " << index; + EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_info->imageFilePath), + proctype_image_info.imageFilePath) + << "index " << index; + EXPECT_EQ(implicit_cast<uint64_t>(self_image_info->imageFileModDate), + proctype_image_info.imageFileModDate) + << "index " << index; + + TEST_STRING( + process_reader, self_image_info, proctype_image_info, imageFilePath); + } + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (proctype_image_infos.version >= 8) { + std::vector<process_types::dyld_uuid_info> proctype_uuid_info_vector( + proctype_image_infos.uuidArrayCount); + ASSERT_TRUE(process_types::dyld_uuid_info::ReadArrayInto( + &process_reader, + proctype_image_infos.uuidArray, + proctype_uuid_info_vector.size(), + &proctype_uuid_info_vector[0])); + + for (size_t index = 0; index < proctype_image_infos.uuidArrayCount; + ++index) { + const dyld_uuid_info* self_uuid_info = + &self_image_infos->uuidArray[index]; + const process_types::dyld_uuid_info& proctype_uuid_info = + proctype_uuid_info_vector[index]; + + EXPECT_EQ(reinterpret_cast<uint64_t>(self_uuid_info->imageLoadAddress), + proctype_uuid_info.imageLoadAddress) + << "index " << index; + EXPECT_EQ(0, + memcmp(self_uuid_info->imageUUID, + proctype_uuid_info.imageUUID, + sizeof(self_uuid_info->imageUUID))) + << "index " << index; + } + } +#endif +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc new file mode 100644 index 0000000..185372d --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc
@@ -0,0 +1,399 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/system_snapshot_mac.h" + +#include <sys/sysctl.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <time.h> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/mac/process_reader.h" +#include "util/mac/mac_util.h" +#include "util/numeric/in_range_cast.h" + +namespace crashpad { + +namespace { + +template <typename T> +T ReadIntSysctlByName(const char* name, T default_value) { + T value; + size_t value_len = sizeof(value); + if (sysctlbyname(name, &value, &value_len, nullptr, 0) != 0) { + PLOG(WARNING) << "sysctlbyname " << name; + return default_value; + } + + return value; +} + +template <typename T> +T CastIntSysctlByName(const char* name, T default_value) { + int int_value = ReadIntSysctlByName<int>(name, default_value); + return InRangeCast<T>(int_value, default_value); +} + +std::string ReadStringSysctlByName(const char* name) { + size_t buf_len; + if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) { + PLOG(WARNING) << "sysctlbyname (size) " << name; + return std::string(); + } + + if (buf_len == 0) { + return std::string(); + } + + std::string value(buf_len - 1, '\0'); + if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) { + PLOG(WARNING) << "sysctlbyname " << name; + return std::string(); + } + + return value; +} + +#if defined(ARCH_CPU_X86_FAMILY) +void CallCPUID(uint32_t leaf, + uint32_t* eax, + uint32_t* ebx, + uint32_t* ecx, + uint32_t* edx) { + asm("cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(leaf), "b"(0), "c"(0), "d"(0)); +} +#endif + +} // namespace + +namespace internal { + +SystemSnapshotMac::SystemSnapshotMac() + : SystemSnapshot(), + os_version_full_(), + os_version_build_(), + process_reader_(nullptr), + snapshot_time_(nullptr), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + os_server_(false), + initialized_() { +} + +SystemSnapshotMac::~SystemSnapshotMac() { +} + +void SystemSnapshotMac::Initialize(ProcessReader* process_reader, + const timeval* snapshot_time) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + snapshot_time_ = snapshot_time; + + // MacOSXVersion() logs its own warnings if it can’t figure anything out. It’s + // not fatal if this happens. The default values are reasonable. + std::string os_version_string; + MacOSXVersion(&os_version_major_, + &os_version_minor_, + &os_version_bugfix_, + &os_version_build_, + &os_server_, + &os_version_string); + + std::string uname_string; + utsname uts; + if (uname(&uts) != 0) { + PLOG(WARNING) << "uname"; + } else { + uname_string = base::StringPrintf( + "%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine); + } + + if (!os_version_string.empty()) { + if (!uname_string.empty()) { + os_version_full_ = base::StringPrintf( + "%s; %s", os_version_string.c_str(), uname_string.c_str()); + } else { + os_version_full_ = os_version_string; + } + } else { + os_version_full_ = uname_string; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotMac::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 + : kCPUArchitectureX86; +#else +#error port to your architecture +#endif +} + +uint32_t SystemSnapshotMac::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // machdep.cpu.family and machdep.cpu.model already take the extended family + // and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c + // cpuid_set_generic_info(). + uint16_t family = CastIntSysctlByName<uint16_t>("machdep.cpu.family", 0); + uint8_t model = CastIntSysctlByName<uint8_t>("machdep.cpu.model", 0); + uint8_t stepping = CastIntSysctlByName<uint8_t>("machdep.cpu.stepping", 0); + + return (family << 16) | (model << 8) | stepping; +#else +#error port to your architecture +#endif +} + +uint8_t SystemSnapshotMac::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return CastIntSysctlByName<uint8_t>("hw.ncpu", 1); +} + +std::string SystemSnapshotMac::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadStringSysctlByName("machdep.cpu.vendor"); +#else +#error port to your architecture +#endif +} + +void SystemSnapshotMac::CPUFrequency( + uint64_t* current_hz, uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *current_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency", 0); + *max_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency_max", 0); +} + +uint32_t SystemSnapshotMac::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadIntSysctlByName<uint32_t>("machdep.cpu.signature", 0); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotMac::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadIntSysctlByName<uint64_t>("machdep.cpu.feature_bits", 0); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadIntSysctlByName<uint64_t>("machdep.cpu.extfeature_bits", 0); +#else + NOTREACHED(); + return 0; +#endif +} + +uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // The machdep.cpu.leaf7_feature_bits sysctl isn’t supported prior to Mac OS X + // 10.7, so read this by calling cpuid directly. + // + // machdep.cpu.max_basic could be used to check whether to read the leaf, but + // that sysctl isn’t supported prior to Mac OS X 10.6, so read the maximum + // basic leaf by calling cpuid directly as well. All CPUs that Apple is known + // to have shipped should support a maximum basic leaf value of at least 0xa. + uint32_t eax, ebx, ecx, edx; + CallCPUID(0, &eax, &ebx, &ecx, &edx); + if (eax < 7) { + return 0; + } + + CallCPUID(7, &eax, &ebx, &ecx, &edx); + return ebx; +#else + NOTREACHED(); + return 0; +#endif +} + +bool SystemSnapshotMac::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // The correct way to check for denormals-as-zeros (DAZ) support is to examine + // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s + // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the + // DAZ Flag in the MXCSR Register”. Note that since this function tests for + // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would + // indicate whether DAZ is actually enabled, which is a per-thread context + // concern. + // + // All CPUs that Apple is known to have shipped should support DAZ. + + // Test for fxsave support. + uint64_t features = CPUX86Features(); + if (!(features & (UINT64_C(1) << 24))) { + return false; + } + + // Call fxsave. +#if defined(ARCH_CPU_X86) + CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {}; +#elif defined(ARCH_CPU_X86_64) + CPUContextX86_64::Fxsave fxsave __attribute__((aligned(16))) = {}; +#endif + static_assert(sizeof(fxsave) == 512, "fxsave size"); + static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28, + "mxcsr_mask offset"); + asm("fxsave %0" : "=m"(fxsave)); + + // Test the DAZ bit. + return fxsave.mxcsr_mask & (1 << 6); +#else + NOTREACHED(); + return false; +#endif +} + +SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemMacOSX; +} + +bool SystemSnapshotMac::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_server_; +} + +void SystemSnapshotMac::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotMac::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotMac::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::string model; + std::string board_id; + MacModelAndBoard(&model, &board_id); + + if (!model.empty()) { + if (!board_id.empty()) { + return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str()); + } + return model; + } + if (!board_id.empty()) { + return base::StringPrintf("(%s)", board_id.c_str()); + } + return std::string(); +} + +bool SystemSnapshotMac::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReadIntSysctlByName<int>("kern.nx", 0); +} + +void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + tm local; + PCHECK(localtime_r(&snapshot_time_->tv_sec, &local)) << "localtime_r"; + + *standard_name = tzname[0]; + if (daylight) { + // Scan forward and backward, one month at a time, looking for an instance + // when the observance of daylight saving time is different than it is in + // |local|. + long probe_gmtoff = local.tm_gmtoff; + + const int kMonthDeltas[] = + { 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, + 7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12 }; + for (size_t index = 0; index < arraysize(kMonthDeltas); ++index) { + // Look at the 15th day of each month at local noon. Set tm_isdst to -1 to + // avoid giving mktime() any hints about whether to consider daylight + // saving time in effect. mktime() accepts values of tm_mon that are + // outside of its normal range and behaves as expected: if tm_mon is -1, + // it references December of the preceding year, and if it is 12, it + // references January of the following year. + tm probe_tm = {}; + probe_tm.tm_hour = 12; + probe_tm.tm_mday = 15; + probe_tm.tm_mon = local.tm_mon + kMonthDeltas[index]; + probe_tm.tm_year = local.tm_year; + probe_tm.tm_isdst = -1; + if (mktime(&probe_tm) != -1 && probe_tm.tm_isdst != local.tm_isdst) { + probe_gmtoff = probe_tm.tm_gmtoff; + break; + } + } + + *daylight_name = tzname[1]; + if (!local.tm_isdst) { + *dst_status = kObservingStandardTime; + *standard_offset_seconds = local.tm_gmtoff; + *daylight_offset_seconds = probe_gmtoff; + } else { + *dst_status = kObservingDaylightSavingTime; + *standard_offset_seconds = probe_gmtoff; + *daylight_offset_seconds = local.tm_gmtoff; + } + } else { + *daylight_name = tzname[0]; + *dst_status = kDoesNotObserveDaylightSavingTime; + *standard_offset_seconds = local.tm_gmtoff; + *daylight_offset_seconds = local.tm_gmtoff; + } +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h new file mode 100644 index 0000000..edbd74f --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h
@@ -0,0 +1,101 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_ + +#include <stdint.h> + +#include <string> + +#include "base/basictypes.h" +#include "snapshot/system_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReader; + +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs Mac OS +//! X. +class SystemSnapshotMac final : public SystemSnapshot { + public: + SystemSnapshotMac(); + ~SystemSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \n\n + //! It seems odd that a system snapshot implementation would need a + //! ProcessReader, but some of the information reported about the system + //! depends on the process it’s being reported for. For example, the + //! architecture returned by GetCPUArchitecture() should be the + //! architecture of the process, which may be different than the native + //! architecture of the system: an x86_64 system can run both x86_64 and + //! 32-bit x86 processes. + //! \param[in] snapshot_time The time of the snapshot being taken. + //! \n\n + //! This parameter is necessary for TimeZone() to determine whether + //! daylight saving time was in effect at the time the snapshot was taken. + //! Otherwise, it would need to base its determination on the current + //! time, which may be different than the snapshot time for snapshots + //! generated around the daylight saving transition time. + void Initialize(ProcessReader* process_reader, const timeval* snapshot_time); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + std::string os_version_full_; + std::string os_version_build_; + ProcessReader* process_reader_; // weak + const timeval* snapshot_time_; // weak + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + bool os_server_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotMac); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc new file mode 100644 index 0000000..a02e94e0 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc
@@ -0,0 +1,176 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/system_snapshot_mac.h" + +#include <sys/time.h> +#include <time.h> + +#include <string> + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/mac/process_reader.h" +#include "test/errors.h" +#include "util/mac/mac_util.h" + +namespace crashpad { +namespace test { +namespace { + +// SystemSnapshotMac objects would be cumbersome to construct in each test that +// requires one, because of the repetitive and mechanical work necessary to set +// up a ProcessReader and timeval, along with the checks to verify that these +// operations succeed. This test fixture class handles the initialization work +// so that individual tests don’t have to. +class SystemSnapshotMacTest : public testing::Test { + public: + SystemSnapshotMacTest() + : Test(), + process_reader_(), + snapshot_time_(), + system_snapshot_() { + } + + const internal::SystemSnapshotMac& system_snapshot() const { + return system_snapshot_; + } + + // testing::Test: + void SetUp() override { + ASSERT_TRUE(process_reader_.Initialize(mach_task_self())); + ASSERT_EQ(0, gettimeofday(&snapshot_time_, nullptr)) + << ErrnoMessage("gettimeofday"); + system_snapshot_.Initialize(&process_reader_, &snapshot_time_); + } + + private: + ProcessReader process_reader_; + timeval snapshot_time_; + internal::SystemSnapshotMac system_snapshot_; + + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotMacTest); +}; + +TEST_F(SystemSnapshotMacTest, GetCPUArchitecture) { + CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture(); + +#if defined(ARCH_CPU_X86) + EXPECT_EQ(kCPUArchitectureX86, cpu_architecture); +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(kCPUArchitectureX86_64, cpu_architecture); +#else +#error port to your architecture +#endif +} + +TEST_F(SystemSnapshotMacTest, CPUCount) { + EXPECT_GE(system_snapshot().CPUCount(), 1); +} + +TEST_F(SystemSnapshotMacTest, CPUVendor) { + std::string cpu_vendor = system_snapshot().CPUVendor(); + +#if defined(ARCH_CPU_X86_FAMILY) + // Apple has only shipped Intel x86-family CPUs, but here’s a small nod to the + // “Hackintosh” crowd. + if (cpu_vendor != "GenuineIntel" && cpu_vendor != "AuthenticAMD") { + FAIL() << "cpu_vendor " << cpu_vendor; + } +#else +#error port to your architecture +#endif +} + +#if defined(ARCH_CPU_X86_FAMILY) + +TEST_F(SystemSnapshotMacTest, CPUX86SupportsDAZ) { + // All x86-family CPUs that Apple is known to have shipped should support DAZ. + EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ()); +} + +#endif + +TEST_F(SystemSnapshotMacTest, GetOperatingSystem) { + EXPECT_EQ(SystemSnapshot::kOperatingSystemMacOSX, + system_snapshot().GetOperatingSystem()); +} + +TEST_F(SystemSnapshotMacTest, OSVersion) { + int major; + int minor; + int bugfix; + std::string build; + system_snapshot().OSVersion(&major, &minor, &bugfix, &build); + + EXPECT_EQ(10, major); + EXPECT_EQ(MacOSXMinorVersion(), minor); + EXPECT_FALSE(build.empty()); +} + +TEST_F(SystemSnapshotMacTest, OSVersionFull) { + EXPECT_FALSE(system_snapshot().OSVersionFull().empty()); +} + +TEST_F(SystemSnapshotMacTest, MachineDescription) { + EXPECT_FALSE(system_snapshot().MachineDescription().empty()); +} + +TEST_F(SystemSnapshotMacTest, TimeZone) { + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + + system_snapshot().TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + + // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives + // seconds west of UTC. + EXPECT_EQ(-timezone, standard_offset_seconds); + + // In contemporary usage, most time zones have an integer hour offset from + // UTC, although several are at a half-hour offset, and two are at 15-minute + // offsets. Throughout history, other variations existed. See + // http://www.timeanddate.com/time/time-zones-interesting.html. + EXPECT_EQ(0, standard_offset_seconds % (15 * 60)) + << "standard_offset_seconds " << standard_offset_seconds; + + if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) { + EXPECT_EQ(standard_offset_seconds, daylight_offset_seconds); + EXPECT_EQ(standard_name, daylight_name); + } else { + EXPECT_EQ(0, daylight_offset_seconds % (15 * 60)) + << "daylight_offset_seconds " << daylight_offset_seconds; + + // In contemporary usage, dst_delta_seconds will almost always be one hour, + // except for Lord Howe Island, Australia, which uses a 30-minute + // delta. Throughout history, other variations existed. See + // http://www.timeanddate.com/time/dst/#brief. + int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; + if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { + FAIL() << "dst_delta_seconds " << dst_delta_seconds; + } + + EXPECT_NE(standard_name, daylight_name); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc new file mode 100644 index 0000000..b042f75 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
@@ -0,0 +1,119 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/mac/thread_snapshot_mac.h" + +#include "base/logging.h" +#include "snapshot/mac/cpu_context_mac.h" +#include "snapshot/mac/process_reader.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotMac::ThreadSnapshotMac() + : ThreadSnapshot(), + context_union_(), + context_(), + stack_(), + thread_id_(0), + thread_specific_data_address_(0), + thread_(MACH_PORT_NULL), + suspend_count_(0), + priority_(0), + initialized_() { +} + +ThreadSnapshotMac::~ThreadSnapshotMac() { +} + +bool ThreadSnapshotMac::Initialize( + ProcessReader* process_reader, + const ProcessReader::Thread& process_reader_thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + thread_ = process_reader_thread.port; + thread_id_ = process_reader_thread.id; + suspend_count_ = process_reader_thread.suspend_count; + priority_ = process_reader_thread.priority; + thread_specific_data_address_ = + process_reader_thread.thread_specific_data_address; + + stack_.Initialize(process_reader, + process_reader_thread.stack_region_address, + process_reader_thread.stack_region_size); + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(context_.x86_64, + THREAD_STATE_NONE, + nullptr, + 0, + &process_reader_thread.thread_context.t64, + &process_reader_thread.float_context.f64, + &process_reader_thread.debug_context.d64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(context_.x86, + THREAD_STATE_NONE, + nullptr, + 0, + &process_reader_thread.thread_context.t32, + &process_reader_thread.float_context.f32, + &process_reader_thread.debug_context.d32); + } +#endif + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotMac::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotMac::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotMac::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotMac::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return suspend_count_; +} + +int ThreadSnapshotMac::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotMac::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector<const MemorySnapshot*> ThreadSnapshotMac::ExtraMemory() const { + return std::vector<const MemorySnapshot*>(); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h new file mode 100644 index 0000000..35b77bba --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
@@ -0,0 +1,86 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_ + +#include <mach/mach.h> +#include <stdint.h> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/mac/memory_snapshot_mac.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReader; + +namespace internal { + +//! \brief A ThreadSnapshot of a thread in a running (or crashed) process on a +//! Mac OS X system. +class ThreadSnapshotMac final : public ThreadSnapshot { + public: + ThreadSnapshotMac(); + ~ThreadSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the task containing the + //! thread. + //! \param[in] process_reader_thread The thread within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + const ProcessReader::Thread& process_reader_thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_FAMILY) + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; +#endif + CPUContext context_; + MemorySnapshotMac stack_; + uint64_t thread_id_; + uint64_t thread_specific_data_address_; + thread_t thread_; + int suspend_count_; + int priority_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotMac); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_
diff --git a/third_party/crashpad/crashpad/snapshot/memory_map_region_snapshot.h b/third_party/crashpad/crashpad/snapshot/memory_map_region_snapshot.h new file mode 100644 index 0000000..4d271f7a --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/memory_map_region_snapshot.h
@@ -0,0 +1,35 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_ + +#include <windows.h> +#include <dbghelp.h> + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing a region of the +//! memory map present in the snapshot process. +class MemoryMapRegionSnapshot { + public: + virtual ~MemoryMapRegionSnapshot() {} + + //! \brief Gets a MINIDUMP_MEMORY_INFO representing the region. + virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/memory_snapshot.h b/third_party/crashpad/crashpad/snapshot/memory_snapshot.h new file mode 100644 index 0000000..47f8443 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/memory_snapshot.h
@@ -0,0 +1,77 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ + +#include <stdint.h> +#include <sys/types.h> + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing a region of memory +//! present in a snapshot process. +class MemorySnapshot { + public: + //! \brief An interface that MemorySnapshot clients must implement in order to + //! receive memory snapshot data. + //! + //! This callback-based model frees MemorySnapshot implementations from having + //! to deal with memory region ownership problems. When a memory snapshot’s + //! data is read, it will be passed to a delegate method. + class Delegate { + public: + virtual ~Delegate() {} + + //! \brief Called by MemorySnapshot::Read() to provide data requested by a + //! call to that method. + //! + //! \param[in] data A pointer to the data that was read. The callee does not + //! take ownership of this data. This data is only valid for the + //! duration of the call to this method. This parameter may be `nullptr` + //! if \a size is `0`. + //! \param[in] size The size of the data that was read. + //! + //! \return `true` on success, `false` on failure. MemoryDelegate::Read() + //! will use this as its own return value. + virtual bool MemorySnapshotDelegateRead(void* data, size_t size) = 0; + }; + + virtual ~MemorySnapshot() {} + + //! \brief The base address of the memory snapshot in the snapshot process’ + //! address space. + virtual uint64_t Address() const = 0; + + //! \brief The size of the memory snapshot. + virtual size_t Size() const = 0; + + //! \brief Calls Delegate::MemorySnapshotDelegateRead(), providing it with + //! the memory snapshot’s data. + //! + //! Implementations do not necessarily read the memory snapshot data prior to + //! this method being called. Memory snapshot data may be loaded lazily and + //! may be discarded after being passed to the delegate. This provides clean + //! memory management without burdening a snapshot implementation with the + //! requirement that it track all memory region data simultaneously. + //! + //! \return `false` on failure, otherwise, the return value of + //! Delegate::MemorySnapshotDelegateRead(), which should be `true` on + //! success and `false` on failure. + virtual bool Read(Delegate* delegate) const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc b/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc new file mode 100644 index 0000000..0d8301c1 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc
@@ -0,0 +1,86 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" + +#include <vector> +#include <utility> + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +bool ReadMinidumpSimpleStringDictionary( + FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::map<std::string, std::string>* dictionary) { + if (location.Rva == 0) { + dictionary->clear(); + return true; + } + + if (location.DataSize < sizeof(MinidumpSimpleStringDictionary)) { + LOG(ERROR) << "simple_string_dictionary size mismatch"; + return false; + } + + if (!file_reader->SeekSet(location.Rva)) { + return false; + } + + uint32_t entry_count; + if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) { + return false; + } + + if (location.DataSize != + sizeof(MinidumpSimpleStringDictionary) + + entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) { + LOG(ERROR) << "simple_string_dictionary size mismatch"; + return false; + } + + std::vector<MinidumpSimpleStringDictionaryEntry> entries(entry_count); + if (!file_reader->ReadExactly(&entries[0], + entry_count * sizeof(entries[0]))) { + return false; + } + + std::map<std::string, std::string> local_dictionary; + for (const MinidumpSimpleStringDictionaryEntry& entry : entries) { + std::string key; + if (!ReadMinidumpUTF8String(file_reader, entry.key, &key)) { + return false; + } + + std::string value; + if (!ReadMinidumpUTF8String(file_reader, entry.value, &value)) { + return false; + } + + if (!local_dictionary.insert(std::make_pair(key, value)).second) { + LOG(ERROR) << "duplicate key " << key; + return false; + } + } + + dictionary->swap(local_dictionary); + return true; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h b/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h new file mode 100644 index 0000000..633ea0f --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h
@@ -0,0 +1,42 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include <map> +#include <string> + +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a MinidumpSimpleStringDictionary from a minidump file \a +//! location in \a file_reader, and returns it in \a dictionary. +//! +//! \return `true` on success, with \a dictionary set by replacing its contents. +//! `false` on failure, with a message logged. +bool ReadMinidumpSimpleStringDictionary( + FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::map<std::string, std::string>* dictionary); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc new file mode 100644 index 0000000..9ca6ef0 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc
@@ -0,0 +1,72 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/minidump/minidump_string_list_reader.h" + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +bool ReadMinidumpStringList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector<std::string>* list) { + if (location.Rva == 0) { + list->clear(); + return true; + } + + if (location.DataSize < sizeof(MinidumpRVAList)) { + LOG(ERROR) << "string_list size mismatch"; + return false; + } + + if (!file_reader->SeekSet(location.Rva)) { + return false; + } + + uint32_t entry_count; + if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) { + return false; + } + + if (location.DataSize != + sizeof(MinidumpRVAList) + entry_count * sizeof(RVA)) { + LOG(ERROR) << "string_list size mismatch"; + return false; + } + + std::vector<RVA> rvas(entry_count); + if (!file_reader->ReadExactly(&rvas[0], entry_count * sizeof(rvas[0]))) { + return false; + } + + std::vector<std::string> local_list; + for (RVA rva : rvas) { + std::string element; + if (!ReadMinidumpUTF8String(file_reader, rva, &element)) { + return false; + } + + local_list.push_back(element); + } + + list->swap(local_list); + return true; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h new file mode 100644 index 0000000..2521605 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h
@@ -0,0 +1,42 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include <string> +#include <vector> + +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a list of MinidumpUTF8String objects in a MinidumpRVAList from +//! a minidump file \a location in \a file_reader, and returns it in \a +//! list. +//! +//! \return `true` on success, with \a list set by replacing its contents. +//! `false` on failure, with a message logged. +bool ReadMinidumpStringList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector<std::string>* list); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc new file mode 100644 index 0000000..6ea3608 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc
@@ -0,0 +1,50 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/minidump/minidump_string_reader.h" + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" + +namespace crashpad { +namespace internal { + +bool ReadMinidumpUTF8String(FileReaderInterface* file_reader, + RVA rva, + std::string* string) { + if (rva == 0) { + string->clear(); + return true; + } + + if (!file_reader->SeekSet(rva)) { + return false; + } + + uint32_t string_size; + if (!file_reader->ReadExactly(&string_size, sizeof(string_size))) { + return false; + } + + std::string local_string(string_size, '\0'); + if (!file_reader->ReadExactly(&local_string[0], string_size)) { + return false; + } + + string->swap(local_string); + return true; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h new file mode 100644 index 0000000..e5667ec2 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h
@@ -0,0 +1,40 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_ + +#include <windows.h> +#include <dbghelp.h> + +#include <string> + +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a MinidumpUTF8String from a minidump file at offset \a rva in +//! \a file_reader, and returns it in \a string. +//! +//! \return `true` on success, with \a string set. `false` on failure, with a +//! message logged. +bool ReadMinidumpUTF8String(FileReaderInterface* file_reader, + RVA rva, + std::string* string); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc new file mode 100644 index 0000000..973d3b2 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc
@@ -0,0 +1,182 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/minidump/module_snapshot_minidump.h" + +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" +#include "snapshot/minidump/minidump_string_list_reader.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotMinidump::ModuleSnapshotMinidump() + : ModuleSnapshot(), + minidump_module_(), + annotations_vector_(), + annotations_simple_map_(), + initialized_() { +} + +ModuleSnapshotMinidump::~ModuleSnapshotMinidump() { +} + +bool ModuleSnapshotMinidump::Initialize( + FileReaderInterface* file_reader, + RVA minidump_module_rva, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_module_crashpad_info_location) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!file_reader->SeekSet(minidump_module_rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_module_, sizeof(minidump_module_))) { + return false; + } + + if (!InitializeModuleCrashpadInfo(file_reader, + minidump_module_crashpad_info_location)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +std::string ModuleSnapshotMinidump::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::string(); +} + +uint64_t ModuleSnapshotMinidump::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +uint64_t ModuleSnapshotMinidump::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +time_t ModuleSnapshotMinidump::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +void ModuleSnapshotMinidump::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotMinidump::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +ModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return kModuleTypeUnknown; +} + +void ModuleSnapshotMinidump::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + *uuid = crashpad::UUID(); + *age = 0; +} + +std::string ModuleSnapshotMinidump::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::string(); +} + +std::vector<std::string> ModuleSnapshotMinidump::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_vector_; +} + +std::map<std::string, std::string> +ModuleSnapshotMinidump::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo( + FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_module_crashpad_info_location) { + if (!minidump_module_crashpad_info_location || + minidump_module_crashpad_info_location->Rva == 0) { + return true; + } + + MinidumpModuleCrashpadInfo minidump_module_crashpad_info; + if (minidump_module_crashpad_info_location->DataSize < + sizeof(minidump_module_crashpad_info)) { + LOG(ERROR) << "minidump_module_crashpad_info size mismatch"; + return false; + } + + if (!file_reader->SeekSet(minidump_module_crashpad_info_location->Rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_module_crashpad_info, + sizeof(minidump_module_crashpad_info))) { + return false; + } + + if (minidump_module_crashpad_info.version != + MinidumpModuleCrashpadInfo::kVersion) { + LOG(ERROR) << "minidump_module_crashpad_info version mismatch"; + return false; + } + + if (!ReadMinidumpStringList(file_reader, + minidump_module_crashpad_info.list_annotations, + &annotations_vector_)) { + return false; + } + + return ReadMinidumpSimpleStringDictionary( + file_reader, + minidump_module_crashpad_info.simple_annotations, + &annotations_simple_map_); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h b/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h new file mode 100644 index 0000000..bf93366 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h
@@ -0,0 +1,98 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_ + +#include <windows.h> +#include <dbghelp.h> +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ModuleSnapshot based on a module in a minidump file. +class ModuleSnapshotMinidump final : public ModuleSnapshot { + public: + ModuleSnapshotMinidump(); + ~ModuleSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! \param[in] minidump_module_rva The file offset in \a file_reader at which + //! the module’s MINIDUMP_MODULE structure is located. + //! \param[in] minidump_crashpad_module_info_location The location in \a + //! file_reader at which the module’s corresponding + //! MinidumpModuleCrashpadInfo structure is located. If no such + //! corresponding structure is available for a module, this may be + //! `nullptr`. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader, + RVA minidump_module_rva, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_crashpad_module_info_location); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector<std::string> AnnotationsVector() const override; + std::map<std::string, std::string> AnnotationsSimpleMap() const override; + + private: + // Initializes data carried in a MinidumpModuleCrashpadInfo structure on + // behalf of Initialize(). + bool InitializeModuleCrashpadInfo(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_module_crashpad_info_location); + + MINIDUMP_MODULE minidump_module_; + std::vector<std::string> annotations_vector_; + std::map<std::string, std::string> annotations_simple_map_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMinidump); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc new file mode 100644 index 0000000..6a1572a8 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
@@ -0,0 +1,346 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/minidump/process_snapshot_minidump.h" + +#include <utility> + +#include "base/memory/scoped_ptr.h" +#include "util/file/file_io.h" +#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" + +namespace crashpad { + +ProcessSnapshotMinidump::ProcessSnapshotMinidump() + : ProcessSnapshot(), + header_(), + stream_directory_(), + stream_map_(), + modules_(), + crashpad_info_(), + annotations_simple_map_(), + file_reader_(nullptr), + initialized_() { +} + +ProcessSnapshotMinidump::~ProcessSnapshotMinidump() { +} + +bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + file_reader_ = file_reader; + + if (!file_reader_->SeekSet(0)) { + return false; + } + + if (!file_reader_->ReadExactly(&header_, sizeof(header_))) { + return false; + } + + if (header_.Signature != MINIDUMP_SIGNATURE) { + LOG(ERROR) << "minidump signature mismatch"; + return false; + } + + if (header_.Version != MINIDUMP_VERSION) { + LOG(ERROR) << "minidump version mismatch"; + return false; + } + + if (!file_reader->SeekSet(header_.StreamDirectoryRva)) { + return false; + } + + stream_directory_.resize(header_.NumberOfStreams); + if (!stream_directory_.empty() && + !file_reader_->ReadExactly( + &stream_directory_[0], + header_.NumberOfStreams * sizeof(stream_directory_[0]))) { + return false; + } + + for (const MINIDUMP_DIRECTORY& directory : stream_directory_) { + const MinidumpStreamType stream_type = + static_cast<MinidumpStreamType>(directory.StreamType); + if (stream_map_.find(stream_type) != stream_map_.end()) { + LOG(ERROR) << "duplicate streams for type " << directory.StreamType; + return false; + } + + stream_map_[stream_type] = &directory.Location; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + + if (!InitializeCrashpadInfo()) { + return false; + } + + return InitializeModules(); +} + +pid_t ProcessSnapshotMinidump::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +pid_t ProcessSnapshotMinidump::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +void ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + snapshot_time->tv_sec = 0; + snapshot_time->tv_usec = 0; +} + +void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + start_time->tv_sec = 0; + start_time->tv_usec = 0; +} + +void ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + user_time->tv_sec = 0; + user_time->tv_usec = 0; + system_time->tv_sec = 0; + system_time->tv_usec = 0; +} + +void ProcessSnapshotMinidump::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = crashpad_info_.report_id; +} + +void ProcessSnapshotMinidump::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = crashpad_info_.client_id; +} + +const std::map<std::string, std::string>& +ProcessSnapshotMinidump::AnnotationsSimpleMap() const { + // TODO(mark): This method should not be const, although the interface + // currently imposes this requirement. Making it non-const would allow + // annotations_simple_map_ to be lazily constructed: InitializeCrashpadInfo() + // could be called here, and from other locations that require it, rather than + // calling it from Initialize(). + // https://crashpad.chromium.org/bug/9 + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotMinidump::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return nullptr; +} + +std::vector<const ThreadSnapshot*> ProcessSnapshotMinidump::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector<const ThreadSnapshot*>(); +} + +std::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<const ModuleSnapshot*> modules; + for (internal::ModuleSnapshotMinidump* module : modules_) { + modules.push_back(module); + } + return modules; +} + +const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return nullptr; +} + +std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMinidump::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector<const MemoryMapRegionSnapshot*>(); +} + +std::vector<HandleSnapshot> ProcessSnapshotMinidump::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector<HandleSnapshot>(); +} + +std::vector<const MemorySnapshot*> ProcessSnapshotMinidump::ExtraMemory() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector<const MemorySnapshot*>(); +} + +bool ProcessSnapshotMinidump::InitializeCrashpadInfo() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(crashpad_info_)) { + LOG(ERROR) << "crashpad_info size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) { + return false; + } + + if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) { + LOG(ERROR) << "crashpad_info version mismatch"; + return false; + } + + return internal::ReadMinidumpSimpleStringDictionary( + file_reader_, + crashpad_info_.simple_annotations, + &annotations_simple_map_); +} + +bool ProcessSnapshotMinidump::InitializeModules() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList); + if (stream_it == stream_map_.end()) { + return true; + } + + std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR> module_crashpad_info_links; + if (!InitializeModulesCrashpadInfo(&module_crashpad_info_links)) { + return false; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_MODULE_LIST)) { + LOG(ERROR) << "module_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + uint32_t module_count; + if (!file_reader_->ReadExactly(&module_count, sizeof(module_count))) { + return false; + } + + if (sizeof(MINIDUMP_MODULE_LIST) + module_count * sizeof(MINIDUMP_MODULE) != + stream_it->second->DataSize) { + LOG(ERROR) << "module_list size mismatch"; + return false; + } + + for (uint32_t module_index = 0; module_index < module_count; ++module_index) { + const RVA module_rva = stream_it->second->Rva + sizeof(module_count) + + module_index * sizeof(MINIDUMP_MODULE); + + const auto& module_crashpad_info_it = + module_crashpad_info_links.find(module_index); + const MINIDUMP_LOCATION_DESCRIPTOR* module_crashpad_info_location = + module_crashpad_info_it != module_crashpad_info_links.end() + ? &module_crashpad_info_it->second + : nullptr; + + auto module = make_scoped_ptr(new internal::ModuleSnapshotMinidump()); + if (!module->Initialize( + file_reader_, module_rva, module_crashpad_info_location)) { + return false; + } + + modules_.push_back(module.release()); + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeModulesCrashpadInfo( + std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>* + module_crashpad_info_links) { + module_crashpad_info_links->clear(); + + if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) { + return false; + } + + if (crashpad_info_.module_list.Rva == 0) { + return true; + } + + if (crashpad_info_.module_list.DataSize < + sizeof(MinidumpModuleCrashpadInfoList)) { + LOG(ERROR) << "module_crashpad_info_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(crashpad_info_.module_list.Rva)) { + return false; + } + + uint32_t crashpad_module_count; + if (!file_reader_->ReadExactly(&crashpad_module_count, + sizeof(crashpad_module_count))) { + return false; + } + + if (crashpad_info_.module_list.DataSize != + sizeof(MinidumpModuleCrashpadInfoList) + + crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) { + LOG(ERROR) << "module_crashpad_info_list size mismatch"; + return false; + } + + scoped_ptr<MinidumpModuleCrashpadInfoLink[]> minidump_links( + new MinidumpModuleCrashpadInfoLink[crashpad_module_count]); + if (!file_reader_->ReadExactly( + &minidump_links[0], + crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink))) { + return false; + } + + for (uint32_t crashpad_module_index = 0; + crashpad_module_index < crashpad_module_count; + ++crashpad_module_index) { + const MinidumpModuleCrashpadInfoLink& minidump_link = + minidump_links[crashpad_module_index]; + if (!module_crashpad_info_links + ->insert(std::make_pair(minidump_link.minidump_module_list_index, + minidump_link.location)).second) { + LOG(WARNING) + << "duplicate module_crashpad_info_list minidump_module_list_index " + << minidump_link.minidump_module_list_index; + return false; + } + } + + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h new file mode 100644 index 0000000..9384eaf --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
@@ -0,0 +1,106 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_ + +#include <sys/time.h> +#include <windows.h> +#include <dbghelp.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/minidump/module_snapshot_minidump.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot based on a minidump file. +class ProcessSnapshotMinidump final : public ProcessSnapshot { + public: + ProcessSnapshotMinidump(); + ~ProcessSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader); + + // ProcessSnapshot: + + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map<std::string, std::string>& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector<const ThreadSnapshot*> Threads() const override; + std::vector<const ModuleSnapshot*> Modules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override; + std::vector<HandleSnapshot> Handles() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: + // Initializes data carried in a MinidumpCrashpadInfo stream on behalf of + // Initialize(). + bool InitializeCrashpadInfo(); + + // Initializes data carried in a MINIDUMP_MODULE_LIST stream on behalf of + // Initialize(). + bool InitializeModules(); + + // Initializes data carried in a MinidumpModuleCrashpadInfoList structure on + // behalf of InitializeModules(). This makes use of MinidumpCrashpadInfo as + // well, so it must be called after InitializeCrashpadInfo(). + bool InitializeModulesCrashpadInfo( + std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>* + module_crashpad_info_links); + + MINIDUMP_HEADER header_; + std::vector<MINIDUMP_DIRECTORY> stream_directory_; + std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_; + PointerVector<internal::ModuleSnapshotMinidump> modules_; + MinidumpCrashpadInfo crashpad_info_; + std::map<std::string, std::string> annotations_simple_map_; + FileReaderInterface* file_reader_; // weak + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMinidump); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc new file mode 100644 index 0000000..8eb7349 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -0,0 +1,338 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/minidump/process_snapshot_minidump.h" + +#include <string.h> +#include <windows.h> +#include <dbghelp.h> + +#include "base/memory/scoped_ptr.h" +#include "gtest/gtest.h" +#include "snapshot/module_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcessSnapshotMinidump, EmptyFile) { + StringFile string_file; + ProcessSnapshotMinidump process_snapshot; + + EXPECT_FALSE(process_snapshot.Initialize(&string_file)); +} + +TEST(ProcessSnapshotMinidump, InvalidSignatureAndVersion) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_FALSE(process_snapshot.Initialize(&string_file)); +} + +TEST(ProcessSnapshotMinidump, Empty) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + UUID client_id; + process_snapshot.ClientID(&client_id); + EXPECT_EQ(UUID(), client_id); + + EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); +} + +// Writes |string| to |writer| as a MinidumpUTF8String, and returns the file +// offst of the beginning of the string. +RVA WriteString(FileWriterInterface* writer, const std::string& string) { + RVA rva = static_cast<RVA>(writer->SeekGet()); + + uint32_t string_size = static_cast<uint32_t>(string.size()); + EXPECT_TRUE(writer->Write(&string_size, sizeof(string_size))); + + // Include the trailing NUL character. + EXPECT_TRUE(writer->Write(string.c_str(), string.size() + 1)); + + return rva; +} + +// Writes |dictionary| to |writer| as a MinidumpSimpleStringDictionary, and +// populates |location| with a location descriptor identifying what was written. +void WriteMinidumpSimpleStringDictionary( + MINIDUMP_LOCATION_DESCRIPTOR* location, + FileWriterInterface* writer, + const std::map<std::string, std::string>& dictionary) { + std::vector<MinidumpSimpleStringDictionaryEntry> entries; + for (const auto& it : dictionary) { + MinidumpSimpleStringDictionaryEntry entry; + entry.key = WriteString(writer, it.first); + entry.value = WriteString(writer, it.second); + entries.push_back(entry); + } + + location->Rva = static_cast<RVA>(writer->SeekGet()); + + const uint32_t simple_string_dictionary_entries = + static_cast<uint32_t>(entries.size()); + EXPECT_TRUE(writer->Write(&simple_string_dictionary_entries, + sizeof(simple_string_dictionary_entries))); + for (const MinidumpSimpleStringDictionaryEntry& entry : entries) { + EXPECT_TRUE(writer->Write(&entry, sizeof(entry))); + } + + location->DataSize = static_cast<uint32_t>( + sizeof(simple_string_dictionary_entries) + + entries.size() * sizeof(MinidumpSimpleStringDictionaryEntry)); +} + +// Writes |strings| to |writer| as a MinidumpRVAList referencing +// MinidumpUTF8String objects, and populates |location| with a location +// descriptor identifying what was written. +void WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location, + FileWriterInterface* writer, + const std::vector<std::string>& strings) { + std::vector<RVA> rvas; + for (const std::string& string : strings) { + rvas.push_back(WriteString(writer, string)); + } + + location->Rva = static_cast<RVA>(writer->SeekGet()); + + const uint32_t string_list_entries = static_cast<uint32_t>(rvas.size()); + EXPECT_TRUE(writer->Write(&string_list_entries, sizeof(string_list_entries))); + for (RVA rva : rvas) { + EXPECT_TRUE(writer->Write(&rva, sizeof(rva))); + } + + location->DataSize = static_cast<uint32_t>(sizeof(string_list_entries) + + rvas.size() * sizeof(RVA)); +} + +TEST(ProcessSnapshotMinidump, ClientID) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + UUID client_id; + ASSERT_TRUE( + client_id.InitializeFromString("0001f4a9-d00d-5155-0a55-c0ffeec0ffee")); + + MinidumpCrashpadInfo crashpad_info = {}; + crashpad_info.version = MinidumpCrashpadInfo::kVersion; + crashpad_info.client_id = client_id; + + MINIDUMP_DIRECTORY crashpad_info_directory = {}; + crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; + crashpad_info_directory.Location.Rva = + static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info); + + header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info_directory, + sizeof(crashpad_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + UUID actual_client_id; + process_snapshot.ClientID(&actual_client_id); + EXPECT_EQ(client_id, actual_client_id); + + EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); +} + +TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MinidumpCrashpadInfo crashpad_info = {}; + crashpad_info.version = MinidumpCrashpadInfo::kVersion; + + std::map<std::string, std::string> dictionary; + dictionary["the first key"] = "THE FIRST VALUE EVER!"; + dictionary["2key"] = "a lowly second value"; + WriteMinidumpSimpleStringDictionary( + &crashpad_info.simple_annotations, &string_file, dictionary); + + MINIDUMP_DIRECTORY crashpad_info_directory = {}; + crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; + crashpad_info_directory.Location.Rva = + static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info); + + header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info_directory, + sizeof(crashpad_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + UUID client_id; + process_snapshot.ClientID(&client_id); + EXPECT_EQ(UUID(), client_id); + + const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap(); + EXPECT_EQ(dictionary, annotations_simple_map); +} + +TEST(ProcessSnapshotMinidump, Modules) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_MODULE minidump_module = {}; + uint32_t minidump_module_count = 3; + + MINIDUMP_DIRECTORY minidump_module_list_directory = {}; + minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList; + minidump_module_list_directory.Location.DataSize = + sizeof(MINIDUMP_MODULE_LIST) + + minidump_module_count * sizeof(MINIDUMP_MODULE); + minidump_module_list_directory.Location.Rva = + static_cast<RVA>(string_file.SeekGet()); + + EXPECT_TRUE( + string_file.Write(&minidump_module_count, sizeof(minidump_module_count))); + for (uint32_t minidump_module_index = 0; + minidump_module_index < minidump_module_count; + ++minidump_module_index) { + EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module))); + } + + MinidumpModuleCrashpadInfo crashpad_module_0 = {}; + crashpad_module_0.version = MinidumpModuleCrashpadInfo::kVersion; + std::map<std::string, std::string> dictionary_0; + dictionary_0["ptype"] = "browser"; + dictionary_0["pid"] = "12345"; + WriteMinidumpSimpleStringDictionary( + &crashpad_module_0.simple_annotations, &string_file, dictionary_0); + + MinidumpModuleCrashpadInfoLink crashpad_module_0_link = {}; + crashpad_module_0_link.minidump_module_list_index = 0; + crashpad_module_0_link.location.DataSize = sizeof(crashpad_module_0); + crashpad_module_0_link.location.Rva = static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_module_0, sizeof(crashpad_module_0))); + + MinidumpModuleCrashpadInfo crashpad_module_2 = {}; + crashpad_module_2.version = MinidumpModuleCrashpadInfo::kVersion; + std::map<std::string, std::string> dictionary_2; + dictionary_2["fakemodule"] = "yes"; + WriteMinidumpSimpleStringDictionary( + &crashpad_module_2.simple_annotations, &string_file, dictionary_2); + + std::vector<std::string> list_annotations_2; + list_annotations_2.push_back("first string"); + list_annotations_2.push_back("last string"); + WriteMinidumpStringList( + &crashpad_module_2.list_annotations, &string_file, list_annotations_2); + + MinidumpModuleCrashpadInfoLink crashpad_module_2_link = {}; + crashpad_module_2_link.minidump_module_list_index = 2; + crashpad_module_2_link.location.DataSize = sizeof(crashpad_module_2); + crashpad_module_2_link.location.Rva = static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2))); + + MinidumpCrashpadInfo crashpad_info = {}; + crashpad_info.version = MinidumpCrashpadInfo::kVersion; + + uint32_t crashpad_module_count = 2; + + crashpad_info.module_list.DataSize = + sizeof(MinidumpModuleCrashpadInfoList) + + crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink); + crashpad_info.module_list.Rva = static_cast<RVA>(string_file.SeekGet()); + + EXPECT_TRUE( + string_file.Write(&crashpad_module_count, sizeof(crashpad_module_count))); + EXPECT_TRUE(string_file.Write(&crashpad_module_0_link, + sizeof(crashpad_module_0_link))); + EXPECT_TRUE(string_file.Write(&crashpad_module_2_link, + sizeof(crashpad_module_2_link))); + + MINIDUMP_DIRECTORY crashpad_info_directory = {}; + crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info); + crashpad_info_directory.Location.Rva = + static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); + + header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&minidump_module_list_directory, + sizeof(minidump_module_list_directory))); + EXPECT_TRUE(string_file.Write(&crashpad_info_directory, + sizeof(crashpad_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector<const ModuleSnapshot*> modules = process_snapshot.Modules(); + ASSERT_EQ(minidump_module_count, modules.size()); + + auto annotations_simple_map = modules[0]->AnnotationsSimpleMap(); + EXPECT_EQ(dictionary_0, annotations_simple_map); + + auto annotations_vector = modules[0]->AnnotationsVector(); + EXPECT_TRUE(annotations_vector.empty()); + + annotations_simple_map = modules[1]->AnnotationsSimpleMap(); + EXPECT_TRUE(annotations_simple_map.empty()); + + annotations_vector = modules[1]->AnnotationsVector(); + EXPECT_TRUE(annotations_vector.empty()); + + annotations_simple_map = modules[2]->AnnotationsSimpleMap(); + EXPECT_EQ(dictionary_2, annotations_simple_map); + + annotations_vector = modules[2]->AnnotationsVector(); + EXPECT_EQ(list_annotations_2, annotations_vector); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/module_snapshot.h b/third_party/crashpad/crashpad/snapshot/module_snapshot.h new file mode 100644 index 0000000..a1bafd9 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/module_snapshot.h
@@ -0,0 +1,175 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing a code module +//! (binary image) loaded into a snapshot process. +class ModuleSnapshot { + public: + virtual ~ModuleSnapshot() {} + + //! \brief A module’s type. + enum ModuleType { + //! \brief The module’s type is unknown. + kModuleTypeUnknown = 0, + + //! \brief The module is a main executable. + kModuleTypeExecutable, + + //! \brief The module is a shared library. + //! + //! \sa kModuleTypeLoadableModule + kModuleTypeSharedLibrary, + + //! \brief The module is a loadable module. + //! + //! On some platforms, loadable modules are distinguished from shared + //! libraries. On these platforms, a shared library is a module that another + //! module links against directly, and a loadable module is not. Loadable + //! modules tend to be binary plug-ins. + kModuleTypeLoadableModule, + + //! \brief The module is a dynamic loader. + //! + //! This is the module responsible for loading other modules. This is + //! normally `dyld` for Mac OS X and `ld.so` for Linux and other systems + //! using ELF. + kModuleTypeDynamicLoader, + }; + + //! \brief Returns the module’s pathname. + virtual std::string Name() const = 0; + + //! \brief Returns the base address that the module is loaded at in the + //! snapshot process. + virtual uint64_t Address() const = 0; + + //! \brief Returns the size that the module occupies in the snapshot process’ + //! address space, starting at its base address. + //! + //! For Mac OS X snapshots, this method only reports the size of the `__TEXT` + //! segment, because segments may not be loaded contiguously. + virtual uint64_t Size() const = 0; + + //! \brief Returns the module’s timestamp, if known. + //! + //! The timestamp is typically the modification time of the file that provided + //! the module in `time_t` format, seconds since the POSIX epoch. If the + //! module’s timestamp is unknown, this method returns `0`. + virtual time_t Timestamp() const = 0; + + //! \brief Returns the module’s file version in the \a version_* parameters. + //! + //! If no file version can be determined, the \a version_* parameters are set + //! to `0`. + //! + //! For Mac OS X snapshots, this is taken from the module’s `LC_ID_DYLIB` load + //! command for shared libraries, and is `0` for other module types. + virtual void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const = 0; + + //! \brief Returns the module’s source version in the \a version_* parameters. + //! + //! If no source version can be determined, the \a version_* parameters are + //! set to `0`. + //! + //! For Mac OS X snapshots, this is taken from the module’s + //! `LC_SOURCE_VERSION` load command. + virtual void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const = 0; + + //! \brief Returns the module’s type. + virtual ModuleType GetModuleType() const = 0; + + //! \brief Returns the module’s UUID in the \a uuid parameter, and the age of + //! that UUID in \a age. + //! + //! A snapshot module’s UUID is taken directly from the module itself. If the + //! module does not have a UUID, the \a uuid parameter will be zeroed out. + //! + //! \a age is the number of times the UUID has been reused. This occurs on + //! Windows with incremental linking. On other platforms \a age will always be + //! `0`. + //! + //! \sa DebugFileName() + virtual void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const = 0; + + //! \brief Returns the module’s debug file info name. + //! + //! On Windows, this references the PDB file, which contains symbol + //! information held separately from the module itself. On other platforms, + //! this is normally just be the basename of the module, because the debug + //! info file’s name is not relevant even in split-debug scenarios. + //! + //! \sa UUIDAndAge() + virtual std::string DebugFileName() const = 0; + + //! \brief Returns string annotations recorded in the module. + //! + //! This method retrieves annotations recorded in a module. These annotations + //! are intended for diagnostic use, including crash analysis. A module may + //! contain multiple annotations, so they are returned in a vector. + //! + //! For Mac OS X snapshots, these annotations are found by interpreting the + //! module’s `__DATA, __crash_info` section as `crashreporter_annotations_t`. + //! System libraries using the crash reporter client interface may reference + //! annotations in this structure. Additional annotations messages may be + //! found in other locations, which may be module-specific. The dynamic linker + //! (`dyld`) can provide an annotation at its `_error_string` symbol. + //! + //! The annotations returned by this method do not duplicate those returned by + //! AnnotationsSimpleMap(). + virtual std::vector<std::string> AnnotationsVector() const = 0; + + //! \brief Returns key-value string annotations recorded in the module. + //! + //! This method retrieves annotations recorded in a module. These annotations + //! are intended for diagnostic use, including crash analysis. “Simple + //! annotations” are structured as a sequence of key-value pairs, where all + //! keys and values are strings. These are referred to in Chrome as “crash + //! keys.” + //! + //! For Mac OS X snapshots, these annotations are found by interpreting the + //! `__DATA, __crashpad_info` section as `CrashpadInfo`. Clients can use the + //! Crashpad client interface to store annotations in this structure. Most + //! annotations under the client’s direct control will be retrievable by this + //! method. For clients such as Chrome, this includes the process type. + //! + //! The annotations returned by this method do not duplicate those returned by + //! AnnotationsVector(). Additional annotations related to the process, + //! system, or snapshot producer may be obtained by calling + //! ProcessSnapshot::AnnotationsSimpleMap(). + virtual std::map<std::string, std::string> AnnotationsSimpleMap() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/process_snapshot.h b/third_party/crashpad/crashpad/snapshot/process_snapshot.h new file mode 100644 index 0000000..19d34607b --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/process_snapshot.h
@@ -0,0 +1,193 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_ + +#include <sys/time.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "snapshot/handle_snapshot.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class ExceptionSnapshot; +class MemoryMapRegionSnapshot; +class MemorySnapshot; +class ModuleSnapshot; +class SystemSnapshot; +class ThreadSnapshot; + +//! \brief An abstract interface to a snapshot representing the state of a +//! process. +//! +//! This is the top-level object in a family of Snapshot objects, because it +//! gives access to a SystemSnapshot, vectors of ModuleSnapshot and +//! ThreadSnapshot objects, and possibly an ExceptionSnapshot. In turn, +//! ThreadSnapshot and ExceptionSnapshot objects both give access to CPUContext +//! objects, and ThreadSnapshot objects also give access to MemorySnapshot +//! objects corresponding to thread stacks. +class ProcessSnapshot { + public: + virtual ~ProcessSnapshot() {} + + //! \brief Returns the snapshot process’ process ID. + virtual pid_t ProcessID() const = 0; + + //! \brief Returns the snapshot process’ parent process’ process ID. + virtual pid_t ParentProcessID() const = 0; + + //! \brief Returns the time that the snapshot was taken in \a snapshot_time. + //! + //! \param[out] snapshot_time The time that the snapshot was taken. This is + //! distinct from the time that a ProcessSnapshot object was created or + //! initialized, although it may be that time for ProcessSnapshot objects + //! representing live or recently-crashed process state. + virtual void SnapshotTime(timeval* snapshot_time) const = 0; + + //! \brief Returns the time that the snapshot process was started in \a + //! start_time. + //! + //! Normally, process uptime in wall clock time can be computed as + //! SnapshotTime() − ProcessStartTime(), but this cannot be guaranteed in + //! cases where the real-time clock has been set during the snapshot process’ + //! lifetime. + //! + //! \param[out] start_time The time that the process was started. + virtual void ProcessStartTime(timeval* start_time) const = 0; + + //! \brief Returns the snapshot process’ CPU usage times in \a user_time and + //! \a system_time. + //! + //! \param[out] user_time The time that the process has spent executing in + //! user mode. + //! \param[out] system_time The time that the process has spent executing in + //! system (kernel) mode. + virtual void ProcessCPUTimes(timeval* user_time, + timeval* system_time) const = 0; + + //! \brief Returns a %UUID identifying the event that the snapshot describes. + //! + //! This provides a stable identifier for a crash even as the report is + //! converted to different formats, provided that all formats support storing + //! a crash report ID. When a report is originally created, a report ID should + //! be assigned. From that point on, any operations involving the same report + //! should preserve the same report ID. + //! + //! If no identifier is available, this field will contain zeroes. + virtual void ReportID(UUID* client_id) const = 0; + + //! \brief Returns a %UUID identifying the client that the snapshot + //! represents. + //! + //! Client identification is within the scope of the application, but it is + //! expected that the identifier will be unique for an instance of Crashpad + //! monitoring an application or set of applications for a user. The + //! identifier shall remain stable over time. + //! + //! If no identifier is available, this field will contain zeroes. + virtual void ClientID(UUID* client_id) const = 0; + + //! \brief Returns key-value string annotations recorded for the process, + //! system, or snapshot producer. + //! + //! This method retrieves annotations recorded for a process. These + //! annotations are intended for diagnostic use, including crash analysis. + //! “Simple annotations” are structured as a sequence of key-value pairs, + //! where all keys and values are strings. These are referred to in Chrome as + //! “crash keys.” + //! + //! Annotations stored here may reflect the process, system, or snapshot + //! producer. Most annotations not under the client’s direct control will be + //! retrievable by this method. For clients such as Chrome, this includes the + //! product name and version. + //! + //! Additional per-module annotations may be obtained by calling + //! ModuleSnapshot::AnnotationsSimpleMap(). + // + // This interface currently returns a const& because all implementations store + // the data within their objects in this format, and are therefore able to + // provide this access without requiring a copy. Future implementations may + // obtain data on demand or store data in a different format, at which point + // the cost of maintaining this data in ProcessSnapshot subclass objects will + // need to be taken into account, and this interface possibly revised. + virtual const std::map<std::string, std::string>& AnnotationsSimpleMap() + const = 0; + + //! \brief Returns a SystemSnapshot reflecting the characteristics of the + //! system that ran the snapshot process at the time of the snapshot. + //! + //! \return A SystemSnapshot object. The caller does not take ownership of + //! this object, it is scoped to the lifetime of the ProcessSnapshot + //! object that it was obtained from. + virtual const SystemSnapshot* System() const = 0; + + //! \brief Returns ModuleSnapshot objects reflecting the code modules (binary + //! images) loaded into the snapshot process at the time of the snapshot. + //! + //! \return A vector of ModuleSnapshot objects. The caller does not take + //! ownership of these objects, they are scoped to the lifetime of the + //! ProcessSnapshot object that they were obtained from. + virtual std::vector<const ModuleSnapshot*> Modules() const = 0; + + //! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight + //! processes) existing in the snapshot process at the time of the + //! snapshot. + //! + //! \return A vector of ThreadSnapshot objects. The caller does not take + //! ownership of these objects, they are scoped to the lifetime of the + //! ProcessSnapshot object that they were obtained from. + virtual std::vector<const ThreadSnapshot*> Threads() const = 0; + + //! \brief Returns an ExceptionSnapshot reflecting the exception that the + //! snapshot process sustained to trigger the snapshot being taken. + //! + //! \return An ExceptionSnapshot object. The caller does not take ownership of + //! this object, it is scoped to the lifetime of the ProcessSnapshot + //! object that it was obtained from. If the snapshot is not a result of + //! an exception, returns `nullptr`. + virtual const ExceptionSnapshot* Exception() const = 0; + + //! \brief Returns MemoryMapRegionSnapshot objects reflecting the regions + //! of the memory map in the snapshot process at the time of the snapshot. + //! + //! \return A vector of MemoryMapRegionSnapshot objects. The caller does not + //! take ownership of these objects, they are scoped to the lifetime of + //! the ProcessSnapshot object that they were obtained from. + virtual std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const = 0; + + //! \brief Returns HandleSnapshot objects reflecting the open handles in the + //! snapshot process at the time of the snapshot. + //! + //! \return A vector of HandleSnapshot objects. + virtual std::vector<HandleSnapshot> Handles() const = 0; + + //! \brief Returns a vector of additional memory blocks that should be + //! included in a minidump. + //! + //! \return An vector of MemorySnapshot objects that will be included in the + //! crash dump. The caller does not take ownership of these objects, they + //! are scoped to the lifetime of the ProcessSnapshot object that they + //! were obtained from. + virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/snapshot.gyp b/third_party/crashpad/crashpad/snapshot/snapshot.gyp new file mode 100644 index 0000000..0a78e90 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/snapshot.gyp
@@ -0,0 +1,127 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_snapshot', + 'type': 'static_library', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'cpu_architecture.h', + 'cpu_context.cc', + 'cpu_context.h', + 'crashpad_info_client_options.cc', + 'crashpad_info_client_options.h', + 'exception_snapshot.h', + 'handle_snapshot.cc', + 'handle_snapshot.h', + 'mac/cpu_context_mac.cc', + 'mac/cpu_context_mac.h', + 'mac/exception_snapshot_mac.cc', + 'mac/exception_snapshot_mac.h', + 'mac/mach_o_image_annotations_reader.cc', + 'mac/mach_o_image_annotations_reader.h', + 'mac/mach_o_image_reader.cc', + 'mac/mach_o_image_reader.h', + 'mac/mach_o_image_segment_reader.cc', + 'mac/mach_o_image_segment_reader.h', + 'mac/mach_o_image_symbol_table_reader.cc', + 'mac/mach_o_image_symbol_table_reader.h', + 'mac/memory_snapshot_mac.cc', + 'mac/memory_snapshot_mac.h', + 'mac/module_snapshot_mac.cc', + 'mac/module_snapshot_mac.h', + 'mac/process_reader.cc', + 'mac/process_reader.h', + 'mac/process_snapshot_mac.cc', + 'mac/process_snapshot_mac.h', + 'mac/process_types.cc', + 'mac/process_types.h', + 'mac/process_types/all.proctype', + 'mac/process_types/crashpad_info.proctype', + 'mac/process_types/crashreporterclient.proctype', + 'mac/process_types/custom.cc', + 'mac/process_types/dyld_images.proctype', + 'mac/process_types/flavors.h', + 'mac/process_types/internal.h', + 'mac/process_types/loader.proctype', + 'mac/process_types/nlist.proctype', + 'mac/process_types/traits.h', + 'mac/system_snapshot_mac.cc', + 'mac/system_snapshot_mac.h', + 'mac/thread_snapshot_mac.cc', + 'mac/thread_snapshot_mac.h', + 'memory_snapshot.h', + 'minidump/minidump_simple_string_dictionary_reader.cc', + 'minidump/minidump_simple_string_dictionary_reader.h', + 'minidump/minidump_string_list_reader.cc', + 'minidump/minidump_string_list_reader.h', + 'minidump/minidump_string_reader.cc', + 'minidump/minidump_string_reader.h', + 'minidump/module_snapshot_minidump.cc', + 'minidump/module_snapshot_minidump.h', + 'minidump/process_snapshot_minidump.cc', + 'minidump/process_snapshot_minidump.h', + 'module_snapshot.h', + 'process_snapshot.h', + 'system_snapshot.h', + 'thread_snapshot.h', + 'win/cpu_context_win.cc', + 'win/cpu_context_win.h', + 'win/exception_snapshot_win.cc', + 'win/exception_snapshot_win.h', + 'win/memory_map_region_snapshot_win.cc', + 'win/memory_map_region_snapshot_win.h', + 'win/memory_snapshot_win.cc', + 'win/memory_snapshot_win.h', + 'win/module_snapshot_win.cc', + 'win/module_snapshot_win.h', + 'win/pe_image_annotations_reader.cc', + 'win/pe_image_annotations_reader.h', + 'win/pe_image_reader.cc', + 'win/pe_image_reader.h', + 'win/process_reader_win.cc', + 'win/process_reader_win.h', + 'win/process_snapshot_win.cc', + 'win/process_snapshot_win.h', + 'win/system_snapshot_win.cc', + 'win/system_snapshot_win.h', + 'win/thread_snapshot_win.cc', + 'win/thread_snapshot_win.h', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-lpowrprof.lib', + '-lversion.lib', + ], + }, + }], + ] + }, + ], +}
diff --git a/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp b/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp new file mode 100644 index 0000000..51274e6c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp
@@ -0,0 +1,213 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_snapshot_test_lib', + 'type': 'static_library', + 'dependencies': [ + 'snapshot.gyp:crashpad_snapshot', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'test/test_cpu_context.cc', + 'test/test_cpu_context.h', + 'test/test_exception_snapshot.cc', + 'test/test_exception_snapshot.h', + 'test/test_memory_map_region_snapshot.cc', + 'test/test_memory_map_region_snapshot.h', + 'test/test_memory_snapshot.cc', + 'test/test_memory_snapshot.h', + 'test/test_module_snapshot.cc', + 'test/test_module_snapshot.h', + 'test/test_process_snapshot.cc', + 'test/test_process_snapshot.h', + 'test/test_system_snapshot.cc', + 'test/test_system_snapshot.h', + 'test/test_thread_snapshot.cc', + 'test/test_thread_snapshot.h', + ], + }, + { + 'target_name': 'crashpad_snapshot_test', + 'type': 'executable', + 'dependencies': [ + 'crashpad_snapshot_test_module', + 'snapshot.gyp:crashpad_snapshot', + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../test/test.gyp:crashpad_test', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/gtest/gtest.gyp:gtest_main', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'cpu_context_test.cc', + 'crashpad_info_client_options_test.cc', + 'mac/cpu_context_mac_test.cc', + 'mac/mach_o_image_annotations_reader_test.cc', + 'mac/mach_o_image_reader_test.cc', + 'mac/mach_o_image_segment_reader_test.cc', + 'mac/process_reader_test.cc', + 'mac/process_types_test.cc', + 'mac/system_snapshot_mac_test.cc', + 'minidump/process_snapshot_minidump_test.cc', + 'win/cpu_context_win_test.cc', + 'win/exception_snapshot_win_test.cc', + 'win/pe_image_annotations_reader_test.cc', + 'win/pe_image_reader_test.cc', + 'win/process_reader_win_test.cc', + 'win/process_snapshot_win_test.cc', + 'win/system_snapshot_win_test.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'dependencies': [ + 'crashpad_snapshot_test_module_crashy_initializer', + 'crashpad_snapshot_test_no_op', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/OpenCL.framework', + ], + }, + }], + ['OS=="win"', { + 'dependencies': [ + 'crashpad_snapshot_test_crashing_child', + 'crashpad_snapshot_test_dump_without_crashing', + 'crashpad_snapshot_test_image_reader', + 'crashpad_snapshot_test_image_reader_module', + ], + }], + ], + }, + { + 'target_name': 'crashpad_snapshot_test_module', + 'type': 'loadable_module', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'crashpad_info_client_options_test_module.cc', + ], + }, + ], + 'conditions': [ + ['OS=="mac"', { + 'targets': [ + { + 'target_name': 'crashpad_snapshot_test_module_crashy_initializer', + 'type': 'loadable_module', + 'sources': [ + 'mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc', + ], + }, + { + 'target_name': 'crashpad_snapshot_test_no_op', + 'type': 'executable', + 'sources': [ + 'mac/mach_o_image_annotations_reader_test_no_op.cc', + ], + }, + ], + }], + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'crashpad_snapshot_test_crashing_child', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'sources': [ + 'win/crashpad_snapshot_test_crashing_child.cc', + ], + }, + { + 'target_name': 'crashpad_snapshot_test_dump_without_crashing', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'sources': [ + 'win/crashpad_snapshot_test_dump_without_crashing.cc', + ], + }, + { + 'target_name': 'crashpad_snapshot_test_image_reader', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'sources': [ + 'win/crashpad_snapshot_test_image_reader.cc', + ], + }, + { + 'target_name': 'crashpad_snapshot_test_image_reader_module', + 'type': 'loadable_module', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'sources': [ + 'win/crashpad_snapshot_test_image_reader_module.cc', + ], + 'msvs_settings': { + 'NoImportLibrary': 'true', + }, + }, + { + 'target_name': 'crashpad_snapshot_test_simple_annotations', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'sources': [ + 'win/crashpad_snapshot_test_simple_annotations.cc', + ], + }, + ], + }], + ], +}
diff --git a/third_party/crashpad/crashpad/snapshot/system_snapshot.h b/third_party/crashpad/crashpad/snapshot/system_snapshot.h new file mode 100644 index 0000000..82f5d48 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/system_snapshot.h
@@ -0,0 +1,270 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <string> + +#include "snapshot/cpu_architecture.h" + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing the state of a +//! system, comprising an operating system, CPU architecture, and various +//! other characteristics. +class SystemSnapshot { + public: + virtual ~SystemSnapshot() {} + + //! \brief A system’s operating system family. + enum OperatingSystem { + //! \brief The snapshot system’s operating system is unknown. + kOperatingSystemUnknown = 0, + + //! \brief Mac OS X. + kOperatingSystemMacOSX, + + //! \brief Windows. + kOperatingSystemWindows, + }; + + //! \brief A system’s daylight saving time status. + //! + //! The daylight saving time status is taken partially from the system’s + //! locale configuration. This determines whether daylight saving time is + //! ever observed on the system. If it is, the snapshot’s time + //! (ProcessSnapshot::SnapshotTime()) is used to determine whether the system + //! was observing daylight saving time at the time of the snapshot. + enum DaylightSavingTimeStatus { + //! \brief Daylight saving time is never observed on the snapshot system. + kDoesNotObserveDaylightSavingTime = 0, + + //! \brief Daylight saving time is observed on the snapshot system when in + //! effect, but standard time was in effect at the time of the snapshot. + kObservingStandardTime, + + //! \brief Daylight saving time is observed on the snapshot system when in + //! effect, and daylight saving time was in effect at the time of the + //! snapshot. + kObservingDaylightSavingTime, + }; + + //! \brief Returns the snapshot system’s CPU architecture. + //! + //! In some cases, a system may be able to run processes of multiple specific + //! architecture types. For example, systems based on 64-bit architectures + //! such as x86_64 are often able to run 32-bit code of another architecture + //! in the same family, such as 32-bit x86. On these systems, this method will + //! return the architecture of the process that the snapshot is associated + //! with, provided that the SystemSnapshot object was obtained from + //! ProcessSnapshot::System(). This renders one aspect of this method’s return + //! value a process attribute rather than a system attribute, but it’s defined + //! here rather than in ProcessSnapshot because the CPU architecture is a + //! better conceptual fit for the system abstraction alongside these other + //! related methods. + virtual CPUArchitecture GetCPUArchitecture() const = 0; + + //! \brief Returns the snapshot system’s CPU revision. + //! + //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU + //! family, model, and stepping ID values from `cpuid 1` `eax`. The family and + //! model values are adjusted to take the extended family and model IDs into + //! account. These values are encoded in this method’s return value with the + //! family in the high high 16 bits, the model in the next 8 bits, and the + //! stepping in the low 8 bits. + //! + //! \return A CPU architecture-specific value identifying the CPU revision. + virtual uint32_t CPURevision() const = 0; + + //! \brief Returns the total number of CPUs present in the snapshot system. + virtual uint8_t CPUCount() const = 0; + + //! \brief Returns the vendor of the snapshot system’s CPUs. + //! + //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU + //! vendor identification string as encoded in `cpuid 0` `ebx`, `edx`, and + //! `ecx`. + //! + //! \return A string identifying the vendor of the snapshot system’s CPUs. + virtual std::string CPUVendor() const = 0; + + //! \brief Returns frequency information about the snapshot system’s CPUs in + //! \current_hz and \a max_hz. + //! + //! \param[out] current_hz The snapshot system’s CPU clock frequency in Hz at + //! the time of the snapshot. + //! \param[out] max_hz The snapshot system’s maximum possible CPU clock + //! frequency. + virtual void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const = 0; + + //! \brief Returns an x86-family snapshot system’s CPU signature. + //! + //! This is the family, model, and stepping ID values as encoded in `cpuid 1` + //! `eax`. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying the CPU signature. + virtual uint32_t CPUX86Signature() const = 0; + + //! \brief Returns an x86-family snapshot system’s CPU features. + //! + //! This is the feature information as encoded in `cpuid 1` `edx` and `ecx`. + //! `edx` is placed in the low half of the return value, and `ecx` is placed + //! in the high half. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying CPU features. + //! + //! \sa CPUX86ExtendedFeatures() + //! \sa CPUX86Leaf7Features() + virtual uint64_t CPUX86Features() const = 0; + + //! \brief Returns an x86-family snapshot system’s extended CPU features. + //! + //! This is the extended feature information as encoded in `cpuid 0x80000001` + //! `edx` and `ecx`. `edx` is placed in the low half of the return value, and + //! `ecx` is placed in the high half. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying extended CPU features. + //! + //! \sa CPUX86Features() + //! \sa CPUX86Leaf7Features() + virtual uint64_t CPUX86ExtendedFeatures() const = 0; + + //! \brief Returns an x86-family snapshot system’s “leaf 7” CPU features. + //! + //! This is the “leaf 7” feature information as encoded in `cpuid 7` `ebx`. If + //! `cpuid 7` is not supported by the snapshot CPU, this returns `0`. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying “leaf 7” CPU features. + //! + //! \sa CPUX86Features() + //! \sa CPUX86ExtendedFeatures() + virtual uint32_t CPUX86Leaf7Features() const = 0; + + //! \brief Returns an x86-family snapshot system’s CPU’s support for the SSE + //! DAZ (“denormals are zeros”) mode. + //! + //! This determines whether the CPU supports DAZ mode at all, not whether this + //! mode is enabled for any particular thread. DAZ mode support is detected by + //! examining the DAZ bit in the `mxcsr_mask` field of the floating-point + //! context saved by `fxsave`. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return `true` if the snapshot system’s CPUs support the SSE DAZ mode, + //! `false` if they do not. + virtual bool CPUX86SupportsDAZ() const = 0; + + //! \brief Returns the snapshot system’s operating system family. + virtual OperatingSystem GetOperatingSystem() const = 0; + + //! \brief Returns whether the snapshot system runs a server variant of its + //! operating system. + virtual bool OSServer() const = 0; + + //! \brief Returns the snapshot system’s operating system version information + //! in \a major, \a minor, \a bugfix, and \a build. + //! + //! \param[out] major The snapshot system’s operating system’s first (major) + //! version number component. This would be `10` for Mac OS X 10.9.5, and + //! `6` for Windows 7 (NT 6.1) SP1 version 6.1.7601. + //! \param[out] minor The snapshot system’s operating system’s second (minor) + //! version number component. This would be `9` for Mac OS X 10.9.5, and + //! `1` for Windows 7 (NT 6.1) SP1 version 6.1.7601. + //! \param[out] bugfix The snapshot system’s operating system’s third (bugfix) + //! version number component. This would be `5` for Mac OS X 10.9.5, and + //! `7601` for Windows 7 (NT 6.1) SP1 version 6.1.7601. + //! \param[out] build A string further identifying an operating system + //! version. For Mac OS X 10.9.5, this would be `"13F34"`. For Windows, + //! this would be `"Service Pack 1"` if that service pack was installed. + //! For Linux and other Unix-like systems, this would be the kernel + //! version from `uname -srvm`, possibly with additional information + //! appended. On Android, the `ro.build.fingerprint` system property would + //! be appended. + virtual void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const = 0; + + //! \brief Returns the snapshot system’s full operating system version + //! information in string format. + //! + //! For Mac OS X, the string contains values from the operating system and + //! kernel. A Mac OS X 10.9.5 snapshot system would be identified as `"Mac OS + //! X 10.9.5 (13F34); Darwin 13.4.0 Darwin Kernel Version 13.4.0: Sun Aug 17 + //! 19:50:11 PDT 2014; root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64"`. + virtual std::string OSVersionFull() const = 0; + + //! \brief Returns a description of the snapshot system’s hardware in string + //! format. + //! + //! For Mac OS X, the string contains the Mac model and board ID. A mid-2014 + //! 15" MacBook Pro would be identified as `"MacBookPro11,3 + //! (Mac-2BD1B31983FE1663)"`. + virtual std::string MachineDescription() const = 0; + + //! \brief Returns the status of the NX (no-execute, or XD, execute-disable) + //! feature on the snapshot system. + //! + //! This refers to a feature that allows mapped readable pages to be marked + //! as non-executable. + //! + //! \return `true` if the snapshot system supports NX and it is enabled. + virtual bool NXEnabled() const = 0; + + //! \brief Returns time zone information from the snapshot system, based on + //! its locale configuration and real-time clock. + //! + //! \param[out] dst_status Whether the location observes daylight saving time, + //! and if so, whether it or standard time is currently being observed. + //! \param[out] standard_offset_seconds The number of seconds that the + //! location’s time zone is east (ahead) of UTC during standard time. + //! \param[out] daylight_offset_seconds The number of seconds that the + //! location’s time zone is east (ahead) of UTC during daylight saving. + //! time. + //! \param[out] standard_name The name of the time zone while standard time is + //! being observed. + //! \param[out] daylight_name The name of the time zone while daylight saving + //! time is being observed. + virtual void TimeZone(DaylightSavingTimeStatus* observes_daylight, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc b/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc new file mode 100644 index 0000000..a66d393 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc
@@ -0,0 +1,166 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_cpu_context.h" + +#include "base/basictypes.h" + +namespace crashpad { +namespace test { + +void InitializeCPUContextX86(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureX86; + + if (seed == 0) { + memset(context->x86, 0, sizeof(*context->x86)); + return; + } + + uint32_t value = seed; + + context->x86->eax = value++; + context->x86->ebx = value++; + context->x86->ecx = value++; + context->x86->edx = value++; + context->x86->edi = value++; + context->x86->esi = value++; + context->x86->ebp = value++; + context->x86->esp = value++; + context->x86->eip = value++; + context->x86->eflags = value++; + context->x86->cs = static_cast<uint16_t>(value++); + context->x86->ds = static_cast<uint16_t>(value++); + context->x86->es = static_cast<uint16_t>(value++); + context->x86->fs = static_cast<uint16_t>(value++); + context->x86->gs = static_cast<uint16_t>(value++); + context->x86->ss = static_cast<uint16_t>(value++); + InitializeCPUContextX86Fxsave(&context->x86->fxsave, &value); + context->x86->dr0 = value++; + context->x86->dr1 = value++; + context->x86->dr2 = value++; + context->x86->dr3 = value++; + context->x86->dr4 = value++; + context->x86->dr5 = value++; + context->x86->dr6 = value++; + context->x86->dr7 = value++; +} + +void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureX86_64; + + if (seed == 0) { + memset(context->x86_64, 0, sizeof(*context->x86_64)); + return; + } + + uint32_t value = seed; + + context->x86_64->rax = value++; + context->x86_64->rbx = value++; + context->x86_64->rcx = value++; + context->x86_64->rdx = value++; + context->x86_64->rdi = value++; + context->x86_64->rsi = value++; + context->x86_64->rbp = value++; + context->x86_64->rsp = value++; + context->x86_64->r8 = value++; + context->x86_64->r9 = value++; + context->x86_64->r10 = value++; + context->x86_64->r11 = value++; + context->x86_64->r12 = value++; + context->x86_64->r13 = value++; + context->x86_64->r14 = value++; + context->x86_64->r15 = value++; + context->x86_64->rip = value++; + context->x86_64->rflags = value++; + context->x86_64->cs = static_cast<uint16_t>(value++); + context->x86_64->fs = static_cast<uint16_t>(value++); + context->x86_64->gs = static_cast<uint16_t>(value++); + InitializeCPUContextX86_64Fxsave(&context->x86_64->fxsave, &value); + context->x86_64->dr0 = value++; + context->x86_64->dr1 = value++; + context->x86_64->dr2 = value++; + context->x86_64->dr3 = value++; + context->x86_64->dr4 = value++; + context->x86_64->dr5 = value++; + context->x86_64->dr6 = value++; + context->x86_64->dr7 = value++; +} + +namespace { + +// This is templatized because the CPUContextX86::Fxsave and +// CPUContextX86_64::Fxsave are nearly identical but have different sizes for +// the members |xmm|, |reserved_4|, and |available|. +template <typename FxsaveType> +void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) { + uint32_t value = *seed; + + fxsave->fcw = static_cast<uint16_t>(value++); + fxsave->fsw = static_cast<uint16_t>(value++); + fxsave->ftw = static_cast<uint8_t>(value++); + fxsave->reserved_1 = static_cast<uint8_t>(value++); + fxsave->fop = static_cast<uint16_t>(value++); + fxsave->fpu_ip = value++; + fxsave->fpu_cs = static_cast<uint16_t>(value++); + fxsave->reserved_2 = static_cast<uint16_t>(value++); + fxsave->fpu_dp = value++; + fxsave->fpu_ds = static_cast<uint16_t>(value++); + fxsave->reserved_3 = static_cast<uint16_t>(value++); + fxsave->mxcsr = value++; + fxsave->mxcsr_mask = value++; + for (size_t st_mm_index = 0; + st_mm_index < arraysize(fxsave->st_mm); + ++st_mm_index) { + for (size_t byte = 0; + byte < arraysize(fxsave->st_mm[st_mm_index].st); + ++byte) { + fxsave->st_mm[st_mm_index].st[byte] = static_cast<uint8_t>(value++); + } + for (size_t byte = 0; + byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved); + ++byte) { + fxsave->st_mm[st_mm_index].st_reserved[byte] = + static_cast<uint8_t>(value); + } + } + for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) { + for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) { + fxsave->xmm[xmm_index][byte] = static_cast<uint8_t>(value++); + } + } + for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) { + fxsave->reserved_4[byte] = static_cast<uint8_t>(value++); + } + for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) { + fxsave->available[byte] = static_cast<uint8_t>(value++); + } + + *seed = value; +} + +} // namespace + +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.h b/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.h new file mode 100644 index 0000000..0de2497 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.h
@@ -0,0 +1,67 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_ + +#include <stdint.h> + +#include "snapshot/cpu_context.h" + +namespace crashpad { +namespace test { + +//! \brief Initializes a context structure for testing. +//! +//! Initialization is compatible with the initialization used by minidump +//! context test initialization functions such as InitializeMinidumpContextX86() +//! and InitializeMinidumpContextAMD64() for identical \a seed values. +//! +//! \param[out] context The structure to initialize. +//! \param[in] seed The seed value. Initializing two context structures of the +//! same type with identical seed values should produce identical context +//! structures. Initialization with a different seed value should produce +//! a different context structure. If \a seed is `0`, \a context is zeroed +//! out entirely except for the CPUContext::architecture field, which will +//! identify the context type. If \a seed is nonzero, \a context will be +//! populated entirely with nonzero values. +//! +//! \{ +void InitializeCPUContextX86(CPUContext* context, uint32_t seed); +void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed); +//! \} + +//! \brief Initializes an `fxsave` context substructure for testing. +//! +//! \param[out] fxsave The structure to initialize. +//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of +//! the same type with identical seed values should produce identical +//! structures. Initialization with a different seed value should produce +//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed +//! out entirely. If \a seed is nonzero, \a fxsave will be populated +//! entirely with nonzero values. \a seed will be updated by this function +//! to allow the caller to perform subsequent initialization of the context +//! structure containing \a fxsave. +//! +//! \{ +void InitializeCPUContextX86Fxsave( + CPUContextX86::Fxsave* fxsave, uint32_t* seed); +void InitializeCPUContextX86_64Fxsave( + CPUContextX86_64::Fxsave* fxsave, uint32_t* seed); +//! \} + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.cc new file mode 100644 index 0000000..73d121c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.cc
@@ -0,0 +1,59 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_exception_snapshot.h" + +namespace crashpad { +namespace test { + +TestExceptionSnapshot::TestExceptionSnapshot() + : context_union_(), + context_(), + thread_id_(0), + exception_(0), + exception_info_(0), + exception_address_(0), + codes_() { + context_.x86 = &context_union_.x86; +} + +TestExceptionSnapshot::~TestExceptionSnapshot() { +} + +const CPUContext* TestExceptionSnapshot::Context() const { + return &context_; +} + +uint64_t TestExceptionSnapshot::ThreadID() const { + return thread_id_; +} + +uint32_t TestExceptionSnapshot::Exception() const { + return exception_; +} + +uint32_t TestExceptionSnapshot::ExceptionInfo() const { + return exception_info_; +} + +uint64_t TestExceptionSnapshot::ExceptionAddress() const { + return exception_address_; +} + +const std::vector<uint64_t>& TestExceptionSnapshot::Codes() const { + return codes_; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.h new file mode 100644 index 0000000..291f6c1 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.h
@@ -0,0 +1,88 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_ + +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test ExceptionSnapshot that can carry arbitrary data for testing +//! purposes. +class TestExceptionSnapshot final : public ExceptionSnapshot { + public: + TestExceptionSnapshot(); + ~TestExceptionSnapshot(); + + //! \brief Obtains a pointer to the underlying mutable CPUContext structure. + //! + //! This method is intended to be used by callers to populate the CPUContext + //! structure. + //! + //! \return The same pointer that Context() does, while treating the data as + //! mutable. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. + CPUContext* MutableContext() { return &context_; } + + void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; } + void SetException(uint32_t exception) { exception_ = exception; } + void SetExceptionInfo(uint32_t exception_information) { + exception_info_ = exception_information; + } + void SetExceptionAddress(uint64_t exception_address) { + exception_address_ = exception_address; + } + void SetCodes(const std::vector<uint64_t>& codes) { codes_ = codes; } + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector<uint64_t>& Codes() const override; + + private: + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; + CPUContext context_; + uint64_t thread_id_; + uint32_t exception_; + uint32_t exception_info_; + uint64_t exception_address_; + std::vector<uint64_t> codes_; + + DISALLOW_COPY_AND_ASSIGN(TestExceptionSnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_memory_map_region_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_memory_map_region_snapshot.cc new file mode 100644 index 0000000..53500c6 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_memory_map_region_snapshot.cc
@@ -0,0 +1,37 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_memory_map_region_snapshot.h" + +namespace crashpad { +namespace test { + +TestMemoryMapRegionSnapshot::TestMemoryMapRegionSnapshot() : memory_info_() { +} + +TestMemoryMapRegionSnapshot::~TestMemoryMapRegionSnapshot() { +} + +void TestMemoryMapRegionSnapshot::SetMindumpMemoryInfo( + const MINIDUMP_MEMORY_INFO& mmi) { + memory_info_ = mmi; +} + +const MINIDUMP_MEMORY_INFO& TestMemoryMapRegionSnapshot::AsMinidumpMemoryInfo() + const { + return memory_info_; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_memory_map_region_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_memory_map_region_snapshot.h new file mode 100644 index 0000000..e9edc5c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_memory_map_region_snapshot.h
@@ -0,0 +1,47 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/memory_map_region_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test MemoryMapRegionSnapshot that can carry arbitrary data for +//! testing purposes. +class TestMemoryMapRegionSnapshot final : public MemoryMapRegionSnapshot { + public: + TestMemoryMapRegionSnapshot(); + ~TestMemoryMapRegionSnapshot() override; + + void SetMindumpMemoryInfo(const MINIDUMP_MEMORY_INFO& mmi); + + // MemoryMapRegionSnapshot: + const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override; + + private: + MINIDUMP_MEMORY_INFO memory_info_; + + DISALLOW_COPY_AND_ASSIGN(TestMemoryMapRegionSnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_memory_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_memory_snapshot.cc new file mode 100644 index 0000000..5201be0 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_memory_snapshot.cc
@@ -0,0 +1,47 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_memory_snapshot.h" + +#include <string> + +namespace crashpad { +namespace test { + +TestMemorySnapshot::TestMemorySnapshot() + : address_(0), size_(0), value_('\0') { +} + +TestMemorySnapshot::~TestMemorySnapshot() { +} + +uint64_t TestMemorySnapshot::Address() const { + return address_; +} + +size_t TestMemorySnapshot::Size() const { + return size_; +} + +bool TestMemorySnapshot::Read(Delegate* delegate) const { + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + std::string buffer(size_, value_); + return delegate->MemorySnapshotDelegateRead(&buffer[0], size_); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_memory_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_memory_snapshot.h new file mode 100644 index 0000000..31faea4 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_memory_snapshot.h
@@ -0,0 +1,60 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include "base/basictypes.h" +#include "snapshot/memory_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test MemorySnapshot that can carry arbitrary data for testing +//! purposes. +class TestMemorySnapshot final : public MemorySnapshot { + public: + TestMemorySnapshot(); + ~TestMemorySnapshot(); + + void SetAddress(uint64_t address) { address_ = address; } + void SetSize(size_t size) { size_ = size; } + + //! \brief Sets the value to fill the test memory region with. + //! + //! \param[in] value The value to be written to \a delegate when Read() is + //! called. This value will be repeated Size() times. + void SetValue(char value) { value_ = value; } + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + private: + uint64_t address_; + size_t size_; + char value_; + + DISALLOW_COPY_AND_ASSIGN(TestMemorySnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.cc new file mode 100644 index 0000000..cfe9a1de --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.cc
@@ -0,0 +1,97 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_module_snapshot.h" + +namespace crashpad { +namespace test { + +TestModuleSnapshot::TestModuleSnapshot() + : name_(), + address_(0), + size_(0), + timestamp_(0), + file_version_(), + source_version_(), + module_type_(kModuleTypeUnknown), + age_(0), + uuid_(), + debug_file_name_(), + annotations_vector_(), + annotations_simple_map_() { +} + +TestModuleSnapshot::~TestModuleSnapshot() { +} + +std::string TestModuleSnapshot::Name() const { + return name_; +} + +uint64_t TestModuleSnapshot::Address() const { + return address_; +} + +uint64_t TestModuleSnapshot::Size() const { + return size_; +} + +time_t TestModuleSnapshot::Timestamp() const { + return timestamp_; +} + +void TestModuleSnapshot::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + *version_0 = file_version_[0]; + *version_1 = file_version_[1]; + *version_2 = file_version_[2]; + *version_3 = file_version_[3]; +} + +void TestModuleSnapshot::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + *version_0 = source_version_[0]; + *version_1 = source_version_[1]; + *version_2 = source_version_[2]; + *version_3 = source_version_[3]; +} + +ModuleSnapshot::ModuleType TestModuleSnapshot::GetModuleType() const { + return module_type_; +} + +void TestModuleSnapshot::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + *uuid = uuid_; + *age = age_; +} + +std::string TestModuleSnapshot::DebugFileName() const { + return debug_file_name_; +} + +std::vector<std::string> TestModuleSnapshot::AnnotationsVector() const { + return annotations_vector_; +} + +std::map<std::string, std::string> TestModuleSnapshot::AnnotationsSimpleMap() + const { + return annotations_simple_map_; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.h new file mode 100644 index 0000000..85a5622e6 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_module_snapshot.h
@@ -0,0 +1,119 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/module_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test ModuleSnapshot that can carry arbitrary data for testing +//! purposes. +class TestModuleSnapshot final : public ModuleSnapshot { + public: + TestModuleSnapshot(); + ~TestModuleSnapshot() override; + + void SetName(const std::string& name) { name_ = name; } + void SetAddressAndSize(uint64_t address, uint64_t size) { + address_ = address; + size_ = size; + } + void SetTimestamp(time_t timestamp) { timestamp_ = timestamp; } + void SetFileVersion(uint16_t file_version_0, + uint16_t file_version_1, + uint16_t file_version_2, + uint16_t file_version_3) { + file_version_[0] = file_version_0; + file_version_[1] = file_version_1; + file_version_[2] = file_version_2; + file_version_[3] = file_version_3; + } + void SetSourceVersion(uint16_t source_version_0, + uint16_t source_version_1, + uint16_t source_version_2, + uint16_t source_version_3) { + source_version_[0] = source_version_0; + source_version_[1] = source_version_1; + source_version_[2] = source_version_2; + source_version_[3] = source_version_3; + } + void SetModuleType(ModuleType module_type) { module_type_ = module_type; } + void SetUUIDAndAge(const crashpad::UUID& uuid, uint32_t age) { + uuid_ = uuid; + age_ = age; + } + void SetDebugFileName(const std::string& debug_file_name) { + debug_file_name_ = debug_file_name; + } + void SetAnnotationsVector( + const std::vector<std::string>& annotations_vector) { + annotations_vector_ = annotations_vector; + } + void SetAnnotationsSimpleMap( + const std::map<std::string, std::string>& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector<std::string> AnnotationsVector() const override; + std::map<std::string, std::string> AnnotationsSimpleMap() const override; + + private: + std::string name_; + uint64_t address_; + uint64_t size_; + time_t timestamp_; + uint16_t file_version_[4]; + uint16_t source_version_[4]; + ModuleType module_type_; + uint32_t age_; + crashpad::UUID uuid_; + std::string debug_file_name_; + std::vector<std::string> annotations_vector_; + std::map<std::string, std::string> annotations_simple_map_; + + DISALLOW_COPY_AND_ASSIGN(TestModuleSnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.cc new file mode 100644 index 0000000..c022dc4 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.cc
@@ -0,0 +1,123 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/exception_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" + +namespace crashpad { +namespace test { + +TestProcessSnapshot::TestProcessSnapshot() + : process_id_(0), + parent_process_id_(0), + snapshot_time_(), + process_start_time_(), + process_cpu_user_time_(), + process_cpu_system_time_(), + report_id_(), + client_id_(), + annotations_simple_map_(), + system_(), + threads_(), + modules_(), + exception_(), + memory_map_(), + handles_(), + extra_memory_() { +} + +TestProcessSnapshot::~TestProcessSnapshot() { +} + +pid_t TestProcessSnapshot::ProcessID() const { + return process_id_; +} + +pid_t TestProcessSnapshot::ParentProcessID() const { + return parent_process_id_; +} + +void TestProcessSnapshot::SnapshotTime(timeval* snapshot_time) const { + *snapshot_time = snapshot_time_; +} + +void TestProcessSnapshot::ProcessStartTime(timeval* start_time) const { + *start_time = process_start_time_; +} + +void TestProcessSnapshot::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + *user_time = process_cpu_user_time_; + *system_time = process_cpu_system_time_; +} + +void TestProcessSnapshot::ReportID(UUID* report_id) const { + *report_id = report_id_; +} + +void TestProcessSnapshot::ClientID(UUID* client_id) const { + *client_id = client_id_; +} + +const std::map<std::string, std::string>& +TestProcessSnapshot::AnnotationsSimpleMap() const { + return annotations_simple_map_; +} + +const SystemSnapshot* TestProcessSnapshot::System() const { + return system_.get(); +} + +std::vector<const ThreadSnapshot*> TestProcessSnapshot::Threads() const { + std::vector<const ThreadSnapshot*> threads; + for (const ThreadSnapshot* thread : threads_) { + threads.push_back(thread); + } + return threads; +} + +std::vector<const ModuleSnapshot*> TestProcessSnapshot::Modules() const { + std::vector<const ModuleSnapshot*> modules; + for (const ModuleSnapshot* module : modules_) { + modules.push_back(module); + } + return modules; +} + +const ExceptionSnapshot* TestProcessSnapshot::Exception() const { + return exception_.get(); +} + +std::vector<const MemoryMapRegionSnapshot*> TestProcessSnapshot::MemoryMap() + const { + std::vector<const MemoryMapRegionSnapshot*> memory_map; + for (const auto& item : memory_map_) + memory_map.push_back(item); + return memory_map; +} + +std::vector<HandleSnapshot> TestProcessSnapshot::Handles() const { + return handles_; +} + +std::vector<const MemorySnapshot*> TestProcessSnapshot::ExtraMemory() const { + std::vector<const MemorySnapshot*> extra_memory; + for (const auto& em : extra_memory_) + extra_memory.push_back(em); + return extra_memory; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.h new file mode 100644 index 0000000..666fa0a --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.h
@@ -0,0 +1,171 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_ + +#include <stdint.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/stdlib/move.h" +#include "util/misc/uuid.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace test { + +//! \brief A test ProcessSnapshot that can carry arbitrary data for testing +//! purposes. +class TestProcessSnapshot final : public ProcessSnapshot { + public: + TestProcessSnapshot(); + ~TestProcessSnapshot() override; + + void SetProcessID(pid_t process_id) { process_id_ = process_id; } + void SetParentProcessID(pid_t parent_process_id) { + parent_process_id_ = parent_process_id; + } + void SetSnapshotTime(const timeval& snapshot_time) { + snapshot_time_ = snapshot_time; + } + void SetProcessStartTime(const timeval& start_time) { + process_start_time_ = start_time; + } + void SetProcessCPUTimes(const timeval& user_time, + const timeval& system_time) { + process_cpu_user_time_ = user_time; + process_cpu_system_time_ = system_time; + } + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + void SetAnnotationsSimpleMap( + const std::map<std::string, std::string>& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + //! \brief Sets the system snapshot to be returned by System(). + //! + //! \param[in] system The system snapshot that System() will return. The + //! TestProcessSnapshot object takes ownership of \a system. + void SetSystem(scoped_ptr<SystemSnapshot> system) { + system_ = crashpad::move(system); + } + + //! \brief Adds a thread snapshot to be returned by Threads(). + //! + //! \param[in] thread The thread snapshot that will be included in Threads(). + //! The TestProcessSnapshot object takes ownership of \a thread. + void AddThread(scoped_ptr<ThreadSnapshot> thread) { + threads_.push_back(thread.release()); + } + + //! \brief Adds a module snapshot to be returned by Modules(). + //! + //! \param[in] module The module snapshot that will be included in Modules(). + //! The TestProcessSnapshot object takes ownership of \a module. + void AddModule(scoped_ptr<ModuleSnapshot> module) { + modules_.push_back(module.release()); + } + + //! \brief Sets the exception snapshot to be returned by Exception(). + //! + //! \param[in] exception The exception snapshot that Exception() will return. + //! The TestProcessSnapshot object takes ownership of \a exception. + void SetException(scoped_ptr<ExceptionSnapshot> exception) { + exception_ = crashpad::move(exception); + } + + //! \brief Adds a memory map region snapshot to be returned by MemoryMap(). + //! + //! \param[in] region The memory map region snapshot that will be included in + //! MemoryMap(). The TestProcessSnapshot object takes ownership of \a + //! region. + void AddMemoryMapRegion(scoped_ptr<MemoryMapRegionSnapshot> region) { + memory_map_.push_back(region.release()); + } + + //! \brief Adds a handle snapshot to be returned by Handles(). + //! + //! \param[in] region The handle snapshot that will be included in Handles(). + void AddHandle(const HandleSnapshot& handle) { + handles_.push_back(handle); + } + + //! \brief Add a memory snapshot to be returned by ExtraMemory(). + //! + //! \param[in] extra_memory The memory snapshot that will be included in + //! ExtraMemory(). The TestProcessSnapshot object takes ownership of \a + //! extra_memory. + void AddExtraMemory(scoped_ptr<MemorySnapshot> extra_memory) { + extra_memory_.push_back(extra_memory.release()); + } + + // ProcessSnapshot: + + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map<std::string, std::string>& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector<const ThreadSnapshot*> Threads() const override; + std::vector<const ModuleSnapshot*> Modules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override; + std::vector<HandleSnapshot> Handles() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: + pid_t process_id_; + pid_t parent_process_id_; + timeval snapshot_time_; + timeval process_start_time_; + timeval process_cpu_user_time_; + timeval process_cpu_system_time_; + UUID report_id_; + UUID client_id_; + std::map<std::string, std::string> annotations_simple_map_; + scoped_ptr<SystemSnapshot> system_; + PointerVector<ThreadSnapshot> threads_; + PointerVector<ModuleSnapshot> modules_; + scoped_ptr<ExceptionSnapshot> exception_; + PointerVector<MemoryMapRegionSnapshot> memory_map_; + std::vector<HandleSnapshot> handles_; + PointerVector<MemorySnapshot> extra_memory_; + + DISALLOW_COPY_AND_ASSIGN(TestProcessSnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_system_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_system_snapshot.cc new file mode 100644 index 0000000..0f3605cf --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_system_snapshot.cc
@@ -0,0 +1,134 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_system_snapshot.h" + +namespace crashpad { +namespace test { + +TestSystemSnapshot::TestSystemSnapshot() + : cpu_architecture_(kCPUArchitectureUnknown), + cpu_revision_(0), + cpu_count_(0), + cpu_vendor_(), + cpu_frequency_current_hz_(0), + cpu_frequency_max_hz_(0), + cpu_x86_signature_(0), + cpu_x86_features_(0), + cpu_x86_extended_features_(0), + cpu_x86_leaf_7_features_(0), + cpu_x86_supports_daz_(false), + operating_system_(kOperatingSystemUnknown), + os_server_(false), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + os_version_build_(), + os_version_full_(), + nx_enabled_(false), + machine_description_(), + time_zone_dst_status_(kDoesNotObserveDaylightSavingTime), + time_zone_standard_offset_seconds_(0), + time_zone_daylight_offset_seconds_(0), + time_zone_standard_name_(), + time_zone_daylight_name_() { +} + +TestSystemSnapshot::~TestSystemSnapshot() { +} + +CPUArchitecture TestSystemSnapshot::GetCPUArchitecture() const { + return cpu_architecture_; +} + +uint32_t TestSystemSnapshot::CPURevision() const { + return cpu_revision_; +} + +uint8_t TestSystemSnapshot::CPUCount() const { + return cpu_count_; +} + +std::string TestSystemSnapshot::CPUVendor() const { + return cpu_vendor_; +} + +void TestSystemSnapshot::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + *current_hz = cpu_frequency_current_hz_; + *max_hz = cpu_frequency_max_hz_; +} + +uint32_t TestSystemSnapshot::CPUX86Signature() const { + return cpu_x86_signature_; +} + +uint64_t TestSystemSnapshot::CPUX86Features() const { + return cpu_x86_features_; +} + +uint64_t TestSystemSnapshot::CPUX86ExtendedFeatures() const { + return cpu_x86_extended_features_; +} + +uint32_t TestSystemSnapshot::CPUX86Leaf7Features() const { + return cpu_x86_leaf_7_features_; +} + +bool TestSystemSnapshot::CPUX86SupportsDAZ() const { + return cpu_x86_supports_daz_; +} + +SystemSnapshot::OperatingSystem TestSystemSnapshot::GetOperatingSystem() const { + return operating_system_; +} + +bool TestSystemSnapshot::OSServer() const { + return os_server_; +} + +void TestSystemSnapshot::OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const { + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + *build = os_version_build_; +} + +std::string TestSystemSnapshot::OSVersionFull() const { + return os_version_full_; +} + +bool TestSystemSnapshot::NXEnabled() const { + return nx_enabled_; +} + +std::string TestSystemSnapshot::MachineDescription() const { + return machine_description_; +} + +void TestSystemSnapshot::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + *dst_status = time_zone_dst_status_; + *standard_offset_seconds = time_zone_standard_offset_seconds_; + *daylight_offset_seconds = time_zone_daylight_offset_seconds_; + *standard_name = time_zone_standard_name_; + *daylight_name = time_zone_daylight_name_; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_system_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_system_snapshot.h new file mode 100644 index 0000000..d626fae8 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_system_snapshot.h
@@ -0,0 +1,148 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_ + +#include <stdint.h> + +#include <string> + +#include "base/basictypes.h" +#include "snapshot/system_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test SystemSnapshot that can carry arbitrary data for testing +//! purposes. +class TestSystemSnapshot final : public SystemSnapshot { + public: + TestSystemSnapshot(); + ~TestSystemSnapshot() override; + + void SetCPUArchitecture(CPUArchitecture cpu_architecture) { + cpu_architecture_ = cpu_architecture; + } + void SetCPURevision(uint32_t cpu_revision) { cpu_revision_ = cpu_revision; } + void SetCPUCount(uint8_t cpu_count) { cpu_count_ = cpu_count; } + void SetCPUVendor(const std::string& cpu_vendor) { cpu_vendor_ = cpu_vendor; } + void SetCPUFrequency(uint64_t current_hz, uint64_t max_hz) { + cpu_frequency_current_hz_ = current_hz; + cpu_frequency_max_hz_ = max_hz; + } + void SetCPUX86Signature(uint32_t cpu_x86_signature) { + cpu_x86_signature_ = cpu_x86_signature; + } + void SetCPUX86Features(uint64_t cpu_x86_features) { + cpu_x86_features_ = cpu_x86_features; + } + void SetCPUX86ExtendedFeatures(uint64_t cpu_x86_extended_features) { + cpu_x86_extended_features_ = cpu_x86_extended_features; + } + void SetCPUX86Leaf7Features(uint32_t cpu_x86_leaf_7_features) { + cpu_x86_leaf_7_features_ = cpu_x86_leaf_7_features; + } + void SetCPUX86SupportsDAZ(bool cpu_x86_supports_daz) { + cpu_x86_supports_daz_ = cpu_x86_supports_daz; + } + void SetOperatingSystem(OperatingSystem operating_system) { + operating_system_ = operating_system; + } + void SetOSServer(bool os_server) { os_server_ = os_server; } + void SetOSVersion( + int major, int minor, int bugfix, const std::string& build) { + os_version_major_ = major; + os_version_minor_ = minor; + os_version_bugfix_ = bugfix; + os_version_build_ = build; + } + void SetOSVersionFull(const std::string& os_version_full) { + os_version_full_ = os_version_full; + } + void SetNXEnabled(bool nx_enabled) { nx_enabled_ = nx_enabled; } + void SetMachineDescription(const std::string& machine_description) { + machine_description_ = machine_description; + } + void SetTimeZone(DaylightSavingTimeStatus dst_status, + int standard_offset_seconds, + int daylight_offset_seconds, + const std::string& standard_name, + const std::string& daylight_name) { + time_zone_dst_status_ = dst_status; + time_zone_standard_offset_seconds_ = standard_offset_seconds; + time_zone_daylight_offset_seconds_ = daylight_offset_seconds; + time_zone_standard_name_ = standard_name; + time_zone_daylight_name_ = daylight_name; + } + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + CPUArchitecture cpu_architecture_; + uint32_t cpu_revision_; + uint8_t cpu_count_; + std::string cpu_vendor_; + uint64_t cpu_frequency_current_hz_; + uint64_t cpu_frequency_max_hz_; + uint32_t cpu_x86_signature_; + uint64_t cpu_x86_features_; + uint64_t cpu_x86_extended_features_; + uint32_t cpu_x86_leaf_7_features_; + bool cpu_x86_supports_daz_; + OperatingSystem operating_system_; + bool os_server_; + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + std::string os_version_build_; + std::string os_version_full_; + bool nx_enabled_; + std::string machine_description_; + DaylightSavingTimeStatus time_zone_dst_status_; + int time_zone_standard_offset_seconds_; + int time_zone_daylight_offset_seconds_; + std::string time_zone_standard_name_; + std::string time_zone_daylight_name_; + + DISALLOW_COPY_AND_ASSIGN(TestSystemSnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc new file mode 100644 index 0000000..621ff42 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc
@@ -0,0 +1,66 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/test/test_thread_snapshot.h" + +namespace crashpad { +namespace test { + +TestThreadSnapshot::TestThreadSnapshot() + : context_union_(), + context_(), + stack_(), + thread_id_(0), + suspend_count_(0), + priority_(0), + thread_specific_data_address_(0) { + context_.x86 = &context_union_.x86; +} + +TestThreadSnapshot::~TestThreadSnapshot() { +} + +const CPUContext* TestThreadSnapshot::Context() const { + return &context_; +} + +const MemorySnapshot* TestThreadSnapshot::Stack() const { + return stack_.get(); +} + +uint64_t TestThreadSnapshot::ThreadID() const { + return thread_id_; +} + +int TestThreadSnapshot::SuspendCount() const { + return suspend_count_; +} + +int TestThreadSnapshot::Priority() const { + return priority_; +} + +uint64_t TestThreadSnapshot::ThreadSpecificDataAddress() const { + return thread_specific_data_address_; +} + +std::vector<const MemorySnapshot*> TestThreadSnapshot::ExtraMemory() const { + std::vector<const MemorySnapshot*> extra_memory; + for (const auto& em : extra_memory_) + extra_memory.push_back(em); + return extra_memory; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h new file mode 100644 index 0000000..49a10c6 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h
@@ -0,0 +1,108 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_ + +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "snapshot/cpu_context.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/stdlib/move.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace test { + +//! \brief A test ThreadSnapshot that can carry arbitrary data for testing +//! purposes. +class TestThreadSnapshot final : public ThreadSnapshot { + public: + TestThreadSnapshot(); + ~TestThreadSnapshot(); + + //! \brief Obtains a pointer to the underlying mutable CPUContext structure. + //! + //! This method is intended to be used by callers to populate the CPUContext + //! structure. + //! + //! \return The same pointer that Context() does, while treating the data as + //! mutable. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. + CPUContext* MutableContext() { return &context_; } + + //! \brief Sets the memory region to be returned by Stack(). + //! + //! \param[in] stack The memory region that Stack() will return. The + //! TestThreadSnapshot object takes ownership of \a stack. + void SetStack(scoped_ptr<MemorySnapshot> stack) { + stack_ = crashpad::move(stack); + } + + void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; } + void SetSuspendCount(int suspend_count) { suspend_count_ = suspend_count; } + void SetPriority(int priority) { priority_ = priority; } + void SetThreadSpecificDataAddress(uint64_t thread_specific_data_address) { + thread_specific_data_address_ = thread_specific_data_address; + } + + //! \brief Add a memory snapshot to be returned by ExtraMemory(). + //! + //! \param[in] extra_memory The memory snapshot that will be included in + //! ExtraMemory(). The TestThreadSnapshot object takes ownership of \a + //! extra_memory. + void AddExtraMemory(scoped_ptr<MemorySnapshot> extra_memory) { + extra_memory_.push_back(extra_memory.release()); + } + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; + CPUContext context_; + scoped_ptr<MemorySnapshot> stack_; + uint64_t thread_id_; + int suspend_count_; + int priority_; + uint64_t thread_specific_data_address_; + PointerVector<MemorySnapshot> extra_memory_; + + DISALLOW_COPY_AND_ASSIGN(TestThreadSnapshot); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/thread_snapshot.h b/third_party/crashpad/crashpad/snapshot/thread_snapshot.h new file mode 100644 index 0000000..4d73257 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
@@ -0,0 +1,80 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_ + +#include <stdint.h> + +#include <vector> + +namespace crashpad { + +struct CPUContext; +class MemorySnapshot; + +//! \brief An abstract interface to a snapshot representing a thread +//! (lightweight process) present in a snapshot process. +class ThreadSnapshot { + public: + virtual ~ThreadSnapshot() {} + + //! \brief Returns a CPUContext object corresponding to the thread’s CPU + //! context. + //! + //! The caller does not take ownership of this object, it is scoped to the + //! lifetime of the ThreadSnapshot object that it was obtained from. + virtual const CPUContext* Context() const = 0; + + //! \brief Returns a MemorySnapshot object corresponding to the memory region + //! that contains the thread’s stack, or `nullptr` if no stack region is + //! available. + //! + //! The caller does not take ownership of this object, it is scoped to the + //! lifetime of the ThreadSnapshot object that it was obtained from. + virtual const MemorySnapshot* Stack() const = 0; + + //! \brief Returns the thread’s identifier. + //! + //! %Thread identifiers are at least unique within a process, and may be + //! unique system-wide. + virtual uint64_t ThreadID() const = 0; + + //! \brief Returns the thread’s suspend count. + //! + //! A suspend count of `0` denotes a schedulable (not suspended) thread. + virtual int SuspendCount() const = 0; + + //! \brief Returns the thread’s priority. + //! + //! Threads with higher priorities will have higher priority values. + virtual int Priority() const = 0; + + //! \brief Returns the base address of a region used to store thread-specific + //! data. + virtual uint64_t ThreadSpecificDataAddress() const = 0; + + //! \brief Returns a vector of additional memory blocks that should be + //! included in a minidump. + //! + //! \return A vector of MemorySnapshot objects that will be included in the + //! crash dump. The caller does not take ownership of these objects, they + //! are scoped to the lifetime of the ThreadSnapshot object that they + //! were obtained from. + virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/cpu_context_win.cc b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win.cc new file mode 100644 index 0000000..d4ce66d --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win.cc
@@ -0,0 +1,156 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/cpu_context_win.h" + +#include <string.h> + +#include "base/logging.h" +#include "snapshot/cpu_context.h" + +namespace crashpad { + +namespace { + +template <class T> +void CommonInitializeX86Context(const T& context, CPUContextX86* out) { + LOG_IF(ERROR, !(context.ContextFlags & WOW64_CONTEXT_i386)) + << "non-x86 context"; + memset(out, 0, sizeof(*out)); + + // We assume in this function that the WOW64_CONTEXT_* and x86 CONTEXT_* + // values for ContextFlags are identical. + + if (context.ContextFlags & WOW64_CONTEXT_CONTROL) { + out->ebp = context.Ebp; + out->eip = context.Eip; + out->cs = static_cast<uint16_t>(context.SegCs); + out->eflags = context.EFlags; + out->esp = context.Esp; + out->ss = static_cast<uint16_t>(context.SegSs); + } + + if (context.ContextFlags & WOW64_CONTEXT_INTEGER) { + out->eax = context.Eax; + out->ebx = context.Ebx; + out->ecx = context.Ecx; + out->edx = context.Edx; + out->edi = context.Edi; + out->esi = context.Esi; + } + + if (context.ContextFlags & WOW64_CONTEXT_SEGMENTS) { + out->ds = static_cast<uint16_t>(context.SegDs); + out->es = static_cast<uint16_t>(context.SegEs); + out->fs = static_cast<uint16_t>(context.SegFs); + out->gs = static_cast<uint16_t>(context.SegGs); + } + + if (context.ContextFlags & WOW64_CONTEXT_DEBUG_REGISTERS) { + out->dr0 = context.Dr0; + out->dr1 = context.Dr1; + out->dr2 = context.Dr2; + out->dr3 = context.Dr3; + // DR4 and DR5 are obsolete synonyms for DR6 and DR7, see + // https://en.wikipedia.org/wiki/X86_debug_register. + out->dr4 = context.Dr6; + out->dr5 = context.Dr7; + out->dr6 = context.Dr6; + out->dr7 = context.Dr7; + } + + if (context.ContextFlags & WOW64_CONTEXT_EXTENDED_REGISTERS) { + static_assert(sizeof(out->fxsave) == sizeof(context.ExtendedRegisters), + "types must be equivalent"); + memcpy(&out->fxsave, &context.ExtendedRegisters, sizeof(out->fxsave)); + } else if (context.ContextFlags & WOW64_CONTEXT_FLOATING_POINT) { + CHECK(false) << "TODO(scottmg): extract x87 data"; + } +} + +} // namespace + +#if defined(ARCH_CPU_64_BITS) + +void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out) { + CommonInitializeX86Context(context, out); +} + +void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) { + memset(out, 0, sizeof(*out)); + + LOG_IF(ERROR, !(context.ContextFlags & CONTEXT_AMD64)) << "non-x64 context"; + + if (context.ContextFlags & CONTEXT_CONTROL) { + out->cs = context.SegCs; + out->rflags = context.EFlags; + out->rip = context.Rip; + out->rsp = context.Rsp; + // SegSs ignored. + } + + if (context.ContextFlags & CONTEXT_INTEGER) { + out->rax = context.Rax; + out->rbx = context.Rbx; + out->rcx = context.Rcx; + out->rdx = context.Rdx; + out->rdi = context.Rdi; + out->rsi = context.Rsi; + out->rbp = context.Rbp; + out->r8 = context.R8; + out->r9 = context.R9; + out->r10 = context.R10; + out->r11 = context.R11; + out->r12 = context.R12; + out->r13 = context.R13; + out->r14 = context.R14; + out->r15 = context.R15; + } + + if (context.ContextFlags & CONTEXT_SEGMENTS) { + out->fs = context.SegFs; + out->gs = context.SegGs; + // SegDs ignored. + // SegEs ignored. + } + + if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS) { + out->dr0 = context.Dr0; + out->dr1 = context.Dr1; + out->dr2 = context.Dr2; + out->dr3 = context.Dr3; + // DR4 and DR5 are obsolete synonyms for DR6 and DR7, see + // https://en.wikipedia.org/wiki/X86_debug_register. + out->dr4 = context.Dr6; + out->dr5 = context.Dr7; + out->dr6 = context.Dr6; + out->dr7 = context.Dr7; + } + + if (context.ContextFlags & CONTEXT_FLOATING_POINT) { + static_assert(sizeof(out->fxsave) == sizeof(context.FltSave), + "types must be equivalent"); + memcpy(&out->fxsave, &context.FltSave.ControlWord, sizeof(out->fxsave)); + } +} + +#else // ARCH_CPU_64_BITS + +void InitializeX86Context(const CONTEXT& context, CPUContextX86* out) { + CommonInitializeX86Context(context, out); +} + +#endif // ARCH_CPU_64_BITS + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/cpu_context_win.h b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win.h new file mode 100644 index 0000000..a384ccb3 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win.h
@@ -0,0 +1,47 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_ + +#include <windows.h> + +#include "build/build_config.h" + +namespace crashpad { + +struct CPUContextX86; +struct CPUContextX86_64; + +#if defined(ARCH_CPU_64_BITS) || DOXYGEN + +//! \brief Initializes a CPUContextX86 structure from a native context structure +//! on Windows. +void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out); + +//! \brief Initializes a CPUContextX86_64 structure from a native context +//! structure on Windows. +void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out); + +#else // ARCH_CPU_64_BITS + +//! \brief Initializes a CPUContextX86 structure from a native context structure +//! on Windows. +void InitializeX86Context(const CONTEXT& context, CPUContextX86* out); + +#endif // ARCH_CPU_64_BITS + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc new file mode 100644 index 0000000..0c7484b --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc
@@ -0,0 +1,73 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/cpu_context_win.h" + +#include <windows.h> + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/cpu_context.h" + +namespace crashpad { +namespace test { +namespace { + +#if defined(ARCH_CPU_X86_64) + +TEST(CPUContextWin, InitializeX64Context) { + CONTEXT context = {0}; + context.Rax = 10; + context.FltSave.TagWord = 11; + context.Dr0 = 12; + context.ContextFlags = + CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS; + + // Test the simple case, where everything in the CPUContextX86_64 argument is + // set directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86_64 cpu_context_x86_64 = {}; + InitializeX64Context(context, &cpu_context_x86_64); + EXPECT_EQ(10u, cpu_context_x86_64.rax); + EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw); + EXPECT_EQ(12u, cpu_context_x86_64.dr0); + } +} + +#else + +TEST(CPUContextWin, InitializeX86Context) { + CONTEXT context = {0}; + context.ContextFlags = + CONTEXT_INTEGER | CONTEXT_EXTENDED_REGISTERS | CONTEXT_DEBUG_REGISTERS; + context.Eax = 1; + context.ExtendedRegisters[4] = 2; // FTW. + context.Dr0 = 3; + + // Test the simple case, where everything in the CPUContextX86 argument is + // set directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86 cpu_context_x86 = {}; + InitializeX86Context(context, &cpu_context_x86); + EXPECT_EQ(1u, cpu_context_x86.eax); + EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw); + EXPECT_EQ(3u, cpu_context_x86.dr0); + } +} + +#endif // ARCH_CPU_X86_64 + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc new file mode 100644 index 0000000..d4023c65 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc
@@ -0,0 +1,46 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <windows.h> + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "util/file/file_io.h" +#include "util/win/address_types.h" + +namespace { + +__declspec(noinline) crashpad::WinVMAddress CurrentAddress() { + return reinterpret_cast<crashpad::WinVMAddress>(_ReturnAddress()); +} + +} // namespace + +int wmain(int argc, wchar_t* argv[]) { + CHECK_EQ(argc, 2); + + crashpad::CrashpadClient client; + CHECK(client.SetHandlerIPCPipe(argv[1])); + CHECK(client.UseHandler()); + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + crashpad::WinVMAddress break_address = CurrentAddress(); + crashpad::CheckedWriteFile(out, &break_address, sizeof(break_address)); + + __debugbreak(); + + return 0; +}
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc new file mode 100644 index 0000000..cf5ab95 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
@@ -0,0 +1,46 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <windows.h> + +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "client/simulate_crash.h" +#include "util/file/file_io.h" +#include "util/win/address_types.h" + +namespace { + +__declspec(noinline) crashpad::WinVMAddress CurrentAddress() { + return reinterpret_cast<crashpad::WinVMAddress>(_ReturnAddress()); +} + +} // namespace + +int wmain(int argc, wchar_t* argv[]) { + CHECK_EQ(argc, 2); + + crashpad::CrashpadClient client; + CHECK(client.SetHandlerIPCPipe(argv[1])); + CHECK(client.UseHandler()); + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + crashpad::WinVMAddress current_address = CurrentAddress(); + crashpad::CheckedWriteFile(out, ¤t_address, sizeof(current_address)); + + CRASHPAD_SIMULATE_CRASH(); + + return 0; +}
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc new file mode 100644 index 0000000..0592f26 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc
@@ -0,0 +1,38 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <windows.h> + +#include "base/logging.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" + +int wmain(int argc, wchar_t* argv[]) { + CHECK_EQ(argc, 2); + + crashpad::ScopedKernelHANDLE done(CreateEvent(nullptr, true, false, argv[1])); + + PCHECK(LoadLibrary(L"crashpad_snapshot_test_image_reader_module.dll")) + << "LoadLibrary"; + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + char c = ' '; + crashpad::CheckedWriteFile(out, &c, sizeof(c)); + + CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(done.get(), INFINITE)); + + return 0; +} +
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc new file mode 100644 index 0000000..6e81de95 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc
@@ -0,0 +1,19 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <windows.h> + +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) { + return TRUE; +}
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_simple_annotations.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_simple_annotations.cc new file mode 100644 index 0000000..b66a19e1 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_simple_annotations.cc
@@ -0,0 +1,53 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <windows.h> + +#include "base/logging.h" +#include "client/crashpad_info.h" +#include "util/file/file_io.h" + +int wmain(int argc, wchar_t* argv[]) { + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + + // This is "leaked" to crashpad_info. + crashpad::SimpleStringDictionary* simple_annotations = + new crashpad::SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad_info->set_simple_annotations(simple_annotations); + + // Tell the parent that the environment has been set up. + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + char c = ' '; + crashpad::CheckedWriteFile(out, &c, sizeof(c)); + + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle"; + crashpad::CheckedReadFile(in, &c, sizeof(c)); + CHECK(c == 'd' || c == ' '); + + // If 'd' we crash with a debug break, otherwise exit normally. + if (c == 'd') + __debugbreak(); + + return 0; +}
diff --git a/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py b/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py new file mode 100755 index 0000000..f3172ba --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py
@@ -0,0 +1,315 @@ +#!/usr/bin/env python + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +import os +import platform +import random +import re +import subprocess +import sys +import tempfile +import time + +g_temp_dirs = [] + + +def MakeTempDir(): + global g_temp_dirs + new_dir = tempfile.mkdtemp() + g_temp_dirs.append(new_dir) + return new_dir + + +def CleanUpTempDirs(): + global g_temp_dirs + for d in g_temp_dirs: + subprocess.call(['rmdir', '/s', '/q', d], shell=True) + + +def FindInstalledWindowsApplication(app_path): + search_paths = [os.getenv('PROGRAMFILES(X86)'), + os.getenv('PROGRAMFILES'), + os.getenv('PROGRAMW6432'), + os.getenv('LOCALAPPDATA')] + search_paths += os.getenv('PATH', '').split(os.pathsep) + + for search_path in search_paths: + if not search_path: + continue + path = os.path.join(search_path, app_path) + if os.path.isfile(path): + return path + + return None + + +def GetCdbPath(): + """Search in some reasonable places to find cdb.exe. Searches x64 before x86 + and newer versions before older versions. + """ + possible_paths = ( + os.path.join('Windows Kits', '10', 'Debuggers', 'x64'), + os.path.join('Windows Kits', '10', 'Debuggers', 'x86'), + os.path.join('Windows Kits', '8.1', 'Debuggers', 'x64'), + os.path.join('Windows Kits', '8.1', 'Debuggers', 'x86'), + os.path.join('Windows Kits', '8.0', 'Debuggers', 'x64'), + os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'), + 'Debugging Tools For Windows (x64)', + 'Debugging Tools For Windows (x86)', + 'Debugging Tools For Windows',) + for possible_path in possible_paths: + app_path = os.path.join(possible_path, 'cdb.exe') + app_path = FindInstalledWindowsApplication(app_path) + if app_path: + return app_path + return None + + +def GetDumpFromProgram(out_dir, pipe_name, executable_name): + """Initialize a crash database, and run |executable_name| connecting to a + crash handler. If pipe_name is set, crashpad_handler will be started first. If + pipe_name is empty, the executable is responsible for starting + crashpad_handler. Returns the minidump generated by crashpad_handler for + further testing. + """ + test_database = MakeTempDir() + handler = None + + try: + if subprocess.call( + [os.path.join(out_dir, 'crashpad_database_util.exe'), '--create', + '--database=' + test_database]) != 0: + print 'could not initialize report database' + return None + + if pipe_name is not None: + handler = subprocess.Popen([ + os.path.join(out_dir, 'crashpad_handler.exe'), + '--pipe-name=' + pipe_name, + '--database=' + test_database + ]) + + # Wait until the server is ready. + printed = False + while not os.path.exists(pipe_name): + if not printed: + print 'Waiting for crashpad_handler to be ready...' + printed = True + time.sleep(0.1) + + subprocess.call([os.path.join(out_dir, executable_name), pipe_name]) + else: + subprocess.call([os.path.join(out_dir, executable_name), + os.path.join(out_dir, 'crashpad_handler.exe'), + test_database]) + + out = subprocess.check_output([ + os.path.join(out_dir, 'crashpad_database_util.exe'), + '--database=' + test_database, + '--show-completed-reports', + '--show-all-report-info', + ]) + for line in out.splitlines(): + if line.strip().startswith('Path:'): + return line.partition(':')[2].strip() + finally: + if handler: + handler.kill() + + +def GetDumpFromCrashyProgram(out_dir, pipe_name): + return GetDumpFromProgram(out_dir, pipe_name, 'crashy_program.exe') + + +def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name): + return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe') + + +def GetDumpFromZ7Program(out_dir, pipe_name): + return GetDumpFromProgram(out_dir, pipe_name, 'crashy_z7_loader.exe') + + +class CdbRun(object): + """Run cdb.exe passing it a cdb command and capturing the output. + `Check()` searches for regex patterns in sequence allowing verification of + expected output. + """ + + def __init__(self, cdb_path, dump_path, command): + # Run a command line that loads the dump, runs the specified cdb command, + # and then quits, and capturing stdout. + self.out = subprocess.check_output([ + cdb_path, + '-z', dump_path, + '-c', command + ';q' + ]) + + def Check(self, pattern, message, re_flags=0): + match_obj = re.search(pattern, self.out, re_flags) + if match_obj: + # Matched. Consume up to end of match. + self.out = self.out[match_obj.end(0):] + print 'ok - %s' % message + sys.stdout.flush() + else: + print >>sys.stderr, '-' * 80 + print >>sys.stderr, 'FAILED - %s' % message + print >>sys.stderr, '-' * 80 + print >>sys.stderr, 'did not match:\n %s' % pattern + print >>sys.stderr, '-' * 80 + print >>sys.stderr, 'remaining output was:\n %s' % self.out + print >>sys.stderr, '-' * 80 + sys.stderr.flush() + sys.exit(1) + + +def RunTests(cdb_path, + dump_path, + start_handler_dump_path, + destroyed_dump_path, + z7_dump_path, + pipe_name): + """Runs various tests in sequence. Runs a new cdb instance on the dump for + each block of tests to reduce the chances that output from one command is + confused for output from another. + """ + out = CdbRun(cdb_path, dump_path, '.ecxr') + out.Check('This dump file has an exception of interest stored in it', + 'captured exception') + out.Check( + 'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction', + 'exception at correct location') + + out = CdbRun(cdb_path, start_handler_dump_path, '.ecxr') + out.Check('This dump file has an exception of interest stored in it', + 'captured exception (using StartHandler())') + out.Check( + 'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction', + 'exception at correct location (using StartHandler())') + + out = CdbRun(cdb_path, dump_path, '!peb') + out.Check(r'PEB at', 'found the PEB') + out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved') + out.Check(r'Base TimeStamp Module', 'module list present') + pipe_name_escaped = pipe_name.replace('\\', '\\\\') + out.Check(r'CommandLine: *\'.*crashy_program.exe *' + pipe_name_escaped, + 'some PEB data is correct') + out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured', + re.IGNORECASE) + + out = CdbRun(cdb_path, dump_path, '!teb') + out.Check(r'TEB at', 'found the TEB') + out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data') + out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue') + + out = CdbRun(cdb_path, dump_path, '!gle') + out.Check('LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the ' + 'file specified.', '!gle gets last error') + out.Check('LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The ' + 'file %hs does not exist.', '!gle gets last ntstatus') + + if False: + # TODO(scottmg): Re-enable when we grab ntdll!RtlCriticalSectionList. + out = CdbRun(cdb_path, dump_path, '!locks') + out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::' + r'g_test_critical_section', 'lock was captured') + if platform.win32_ver()[0] != '7': + # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7. + out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked') + + out = CdbRun(cdb_path, dump_path, '!handle') + out.Check(r'\d+ Handles', 'captured handles') + out.Check(r'Event\s+\d+', 'capture some event handles') + out.Check(r'File\s+\d+', 'capture some file handles') + + out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2') + out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved') + out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE) + + # Check that there is no stack trace in the self-destroyed process. Confirm + # that the top is where we expect it (that's based only on IP), but subsequent + # stack entries will not be available. This confirms that we have a mostly + # valid dump, but that the stack was omitted. + out.Check(r'self_destroying_program!crashpad::`anonymous namespace\'::' + r'FreeOwnStackAndBreak.*\nquit:', + 'at correct location, no additional stack entries') + + if z7_dump_path: + out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') + out.Check('This dump file has an exception of interest stored in it', + 'captured exception in z7 module') + # Older versions of cdb display relative to exports for /Z7 modules, newer + # ones just display the offset. + out.Check(r'z7_test(!CrashMe\+0xe|\+0x100e):', + 'exception in z7 at correct location') + out.Check(r'z7_test C \(codeview symbols\) z7_test.dll', + 'expected non-pdb symbol format') + + +def main(args): + try: + if len(args) != 1: + print >>sys.stderr, 'must supply binary dir' + return 1 + + cdb_path = GetCdbPath() + if not cdb_path: + print >>sys.stderr, 'could not find cdb' + return 1 + + # Make sure we can download Windows symbols. + if not os.environ.get('_NT_SYMBOL_PATH'): + symbol_dir = MakeTempDir() + protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http' + os.environ['_NT_SYMBOL_PATH'] = ( + 'SRV*' + symbol_dir + '*' + + protocol + '://msdl.microsoft.com/download/symbols') + + pipe_name = r'\\.\pipe\end-to-end_%s_%s' % ( + os.getpid(), str(random.getrandbits(64))) + + crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name) + if not crashy_dump_path: + return 1 + + start_handler_dump_path = GetDumpFromCrashyProgram(args[0], None) + if not start_handler_dump_path: + return 1 + + destroyed_dump_path = GetDumpFromSelfDestroyingProgram(args[0], pipe_name) + if not destroyed_dump_path: + return 1 + + z7_dump_path = None + if not args[0].endswith('x64'): + z7_dump_path = GetDumpFromZ7Program(args[0], pipe_name) + if not z7_dump_path: + return 1 + + RunTests(cdb_path, + crashy_dump_path, + start_handler_dump_path, + destroyed_dump_path, + z7_dump_path, + pipe_name) + + return 0 + finally: + CleanUpTempDirs() + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win.cc new file mode 100644 index 0000000..07b1c8ea --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win.cc
@@ -0,0 +1,172 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/exception_snapshot_win.h" + +#include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/process_reader_win.h" +#include "util/win/nt_internals.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotWin::ExceptionSnapshotWin() + : ExceptionSnapshot(), + context_union_(), + context_(), + codes_(), + thread_id_(0), + exception_address_(0), + exception_flags_(0), + exception_code_(0), + initialized_() { +} + +ExceptionSnapshotWin::~ExceptionSnapshotWin() { +} + +bool ExceptionSnapshotWin::Initialize(ProcessReaderWin* process_reader, + DWORD thread_id, + WinVMAddress exception_pointers_address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + bool found_thread = false; + for (const auto& loop_thread : process_reader->Threads()) { + if (thread_id == loop_thread.id) { + found_thread = true; + break; + } + } + + if (!found_thread) { + LOG(ERROR) << "thread ID " << thread_id << " not found in process"; + return false; + } else { + thread_id_ = thread_id; + } + +#if defined(ARCH_CPU_32_BITS) + const bool is_64_bit = false; + using Context32 = CONTEXT; +#elif defined(ARCH_CPU_64_BITS) + const bool is_64_bit = process_reader->Is64Bit(); + using Context32 = WOW64_CONTEXT; + if (is_64_bit) { + CONTEXT context_record; + if (!InitializeFromExceptionPointers<EXCEPTION_RECORD64, + process_types::EXCEPTION_POINTERS64>( + *process_reader, exception_pointers_address, &context_record)) { + return false; + } + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeX64Context(context_record, context_.x86_64); + } +#endif + if (!is_64_bit) { + Context32 context_record; + if (!InitializeFromExceptionPointers<EXCEPTION_RECORD32, + process_types::EXCEPTION_POINTERS32>( + *process_reader, exception_pointers_address, &context_record)) { + return false; + } + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeX86Context(context_record, context_.x86); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotWin::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotWin::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotWin::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_code_; +} + +uint32_t ExceptionSnapshotWin::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_flags_; +} + +uint64_t ExceptionSnapshotWin::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector<uint64_t>& ExceptionSnapshotWin::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +template <class ExceptionRecordType, + class ExceptionPointersType, + class ContextType> +bool ExceptionSnapshotWin::InitializeFromExceptionPointers( + const ProcessReaderWin& process_reader, + WinVMAddress exception_pointers_address, + ContextType* context_record) { + ExceptionPointersType exception_pointers; + if (!process_reader.ReadMemory(exception_pointers_address, + sizeof(exception_pointers), + &exception_pointers)) { + LOG(ERROR) << "EXCEPTION_POINTERS read failed"; + return false; + } + if (!exception_pointers.ExceptionRecord) { + LOG(ERROR) << "null ExceptionRecord"; + return false; + } + + ExceptionRecordType first_record; + if (!process_reader.ReadMemory( + static_cast<WinVMAddress>(exception_pointers.ExceptionRecord), + sizeof(first_record), + &first_record)) { + LOG(ERROR) << "ExceptionRecord"; + return false; + } + exception_code_ = first_record.ExceptionCode; + exception_flags_ = first_record.ExceptionFlags; + exception_address_ = first_record.ExceptionAddress; + for (DWORD i = 0; i < first_record.NumberParameters; ++i) + codes_.push_back(first_record.ExceptionInformation[i]); + if (first_record.ExceptionRecord) { + // https://crashpad.chromium.org/bug/43 + LOG(WARNING) << "dropping chained ExceptionRecord"; + } + + if (!process_reader.ReadMemory( + static_cast<WinVMAddress>(exception_pointers.ContextRecord), + sizeof(*context_record), + context_record)) { + LOG(ERROR) << "ContextRecord"; + return false; + } + + return true; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win.h new file mode 100644 index 0000000..1688b125 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win.h
@@ -0,0 +1,92 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_ + +#include <stdint.h> +#include <windows.h> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/address_types.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +class ExceptionSnapshotWin final : public ExceptionSnapshot { + public: + ExceptionSnapshotWin(); + ~ExceptionSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the process that sustained + //! the exception. + //! \param[in] thread_id The thread ID in which the exception occurred. + //! \param[in] exception_pointers_address The address of an + //! `EXCEPTION_POINTERS` record in the target process, passed through from + //! the exception handler. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + DWORD thread_id, + WinVMAddress exception_pointers); + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector<uint64_t>& Codes() const override; + + private: + template <class ExceptionRecordType, + class ExceptionPointersType, + class ContextType> + bool InitializeFromExceptionPointers(const ProcessReaderWin& process_reader, + WinVMAddress exception_pointers_address, + ContextType* context_record); + +#if defined(ARCH_CPU_X86_FAMILY) + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; +#endif + CPUContext context_; + std::vector<uint64_t> codes_; + uint64_t thread_id_; + uint64_t exception_address_; + uint32_t exception_flags_; + DWORD exception_code_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc new file mode 100644 index 0000000..5c3b0609e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc
@@ -0,0 +1,276 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/exception_snapshot_win.h" + +#include <string> + +#include "base/files/file_path.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "client/crashpad_client.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_snapshot_win.h" +#include "test/paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/thread/thread.h" +#include "util/win/exception_handler_server.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_handle.h" +#include "util/win/scoped_process_suspend.h" + +namespace crashpad { +namespace test { +namespace { + +// Runs the ExceptionHandlerServer on a background thread. +class RunServerThread : public Thread { + public: + // Instantiates a thread which will invoke server->Run(delegate); + RunServerThread(ExceptionHandlerServer* server, + ExceptionHandlerServer::Delegate* delegate) + : server_(server), delegate_(delegate) {} + ~RunServerThread() override {} + + private: + // Thread: + void ThreadMain() override { server_->Run(delegate_); } + + ExceptionHandlerServer* server_; + ExceptionHandlerServer::Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(RunServerThread); +}; + +// During destruction, ensures that the server is stopped and the background +// thread joined. +class ScopedStopServerAndJoinThread { + public: + ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread) + : server_(server), thread_(thread) {} + ~ScopedStopServerAndJoinThread() { + server_->Stop(); + thread_->Join(); + } + + private: + ExceptionHandlerServer* server_; + Thread* thread_; + DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); +}; + +class CrashingDelegate : public ExceptionHandlerServer::Delegate { + public: + CrashingDelegate(HANDLE server_ready, HANDLE completed_test_event) + : server_ready_(server_ready), + completed_test_event_(completed_test_event), + break_near_(0) {} + ~CrashingDelegate() override {} + + void set_break_near(WinVMAddress break_near) { break_near_ = break_near; } + + void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } + + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override { + ScopedProcessSuspend suspend(process); + ProcessSnapshotWin snapshot; + snapshot.Initialize(process, + ProcessSuspensionState::kSuspended, + debug_critical_section_address); + snapshot.InitializeException(exception_information_address); + + // Confirm the exception record was read correctly. + EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); + EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT); + + // Verify the exception happened at the expected location with a bit of + // slop space to allow for reading the current PC before the exception + // happens. See TestCrashingChild(). + const uint64_t kAllowedOffset = 64; + EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); + EXPECT_LT(snapshot.Exception()->ExceptionAddress(), + break_near_ + kAllowedOffset); + + SetEvent(completed_test_event_); + + return snapshot.Exception()->Exception(); + } + + private: + HANDLE server_ready_; // weak + HANDLE completed_test_event_; // weak + WinVMAddress break_near_; + + DISALLOW_COPY_AND_ASSIGN(CrashingDelegate); +}; + +void TestCrashingChild(const base::string16& directory_modification) { + // Set up the registration server on a background thread. + ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); + ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); + CrashingDelegate delegate(server_ready.get(), completed.get()); + + ExceptionHandlerServer exception_handler_server(true); + std::wstring pipe_name = exception_handler_server.CreatePipe(); + RunServerThread server_thread(&exception_handler_server, &delegate); + server_thread.Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &exception_handler_server, &server_thread); + + WaitForSingleObject(server_ready.get(), INFINITE); + + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath test_executable = Paths::Executable(); + std::wstring child_test_executable = + test_executable.DirName() + .Append(directory_modification) + .Append(test_executable.BaseName().RemoveFinalExtension().value() + + L"_crashing_child.exe") + .value(); + ChildLauncher child(child_test_executable, pipe_name); + child.Start(); + + // The child tells us (approximately) where it will crash. + WinVMAddress break_near_address; + LoggingReadFile(child.stdout_read_handle(), + &break_near_address, + sizeof(break_near_address)); + delegate.set_break_near(break_near_address); + + // Wait for the child to crash and the exception information to be validated. + WaitForSingleObject(completed.get(), INFINITE); +} + +TEST(ExceptionSnapshotWinTest, ChildCrash) { + TestCrashingChild(FILE_PATH_LITERAL(".")); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) { +#ifndef NDEBUG + TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} +#endif // ARCH_CPU_64_BITS + +class SimulateDelegate : public ExceptionHandlerServer::Delegate { + public: + SimulateDelegate(HANDLE server_ready, HANDLE completed_test_event) + : server_ready_(server_ready), + completed_test_event_(completed_test_event), + dump_near_(0) {} + ~SimulateDelegate() override {} + + void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; } + + void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } + + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override { + ScopedProcessSuspend suspend(process); + ProcessSnapshotWin snapshot; + snapshot.Initialize(process, + ProcessSuspensionState::kSuspended, + debug_critical_section_address); + snapshot.InitializeException(exception_information_address); + EXPECT_TRUE(snapshot.Exception()); + EXPECT_EQ(0x517a7ed, snapshot.Exception()->Exception()); + + // Verify the dump was captured at the expected location with some slop + // space. + const uint64_t kAllowedOffset = 64; + EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(), + dump_near_); + EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(), + dump_near_ + kAllowedOffset); + + EXPECT_EQ(snapshot.Exception()->Context()->InstructionPointer(), + snapshot.Exception()->ExceptionAddress()); + + SetEvent(completed_test_event_); + + return 0; + } + + private: + HANDLE server_ready_; // weak + HANDLE completed_test_event_; // weak + WinVMAddress dump_near_; + + DISALLOW_COPY_AND_ASSIGN(SimulateDelegate); +}; + +void TestDumpWithoutCrashingChild( + const base::string16& directory_modification) { + // Set up the registration server on a background thread. + ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); + ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); + SimulateDelegate delegate(server_ready.get(), completed.get()); + + ExceptionHandlerServer exception_handler_server(true); + std::wstring pipe_name = exception_handler_server.CreatePipe(); + RunServerThread server_thread(&exception_handler_server, &delegate); + server_thread.Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &exception_handler_server, &server_thread); + + WaitForSingleObject(server_ready.get(), INFINITE); + + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath test_executable = Paths::Executable(); + std::wstring child_test_executable = + test_executable.DirName() + .Append(directory_modification) + .Append(test_executable.BaseName().RemoveFinalExtension().value() + + L"_dump_without_crashing.exe") + .value(); + ChildLauncher child(child_test_executable, pipe_name); + child.Start(); + + // The child tells us (approximately) where it will capture a dump. + WinVMAddress dump_near_address; + LoggingReadFile(child.stdout_read_handle(), + &dump_near_address, + sizeof(dump_near_address)); + delegate.set_dump_near(dump_near_address); + + // Wait for the child to crash and the exception information to be validated. + WaitForSingleObject(completed.get(), INFINITE); +} + +TEST(SimulateCrash, ChildDumpWithoutCrashing) { + TestDumpWithoutCrashingChild(FILE_PATH_LITERAL(".")); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) { +#ifndef NDEBUG + TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} +#endif // ARCH_CPU_64_BITS + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/memory_map_region_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/memory_map_region_snapshot_win.cc new file mode 100644 index 0000000..c64254c --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/memory_map_region_snapshot_win.cc
@@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/memory_map_region_snapshot_win.h" + +namespace crashpad { +namespace internal { + +MemoryMapRegionSnapshotWin::MemoryMapRegionSnapshotWin( + const MEMORY_BASIC_INFORMATION64& mbi) + : memory_info_() { + memory_info_.BaseAddress = mbi.BaseAddress; + memory_info_.AllocationBase = mbi.AllocationBase; + memory_info_.AllocationProtect = mbi.AllocationProtect; + memory_info_.RegionSize = mbi.RegionSize; + memory_info_.State = mbi.State; + memory_info_.Protect = mbi.Protect; + memory_info_.Type = mbi.Type; +} + +MemoryMapRegionSnapshotWin::~MemoryMapRegionSnapshotWin() { +} + +const MINIDUMP_MEMORY_INFO& MemoryMapRegionSnapshotWin::AsMinidumpMemoryInfo() + const { + return memory_info_; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/memory_map_region_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/memory_map_region_snapshot_win.h new file mode 100644 index 0000000..725c125 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/memory_map_region_snapshot_win.h
@@ -0,0 +1,37 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_ + +#include "snapshot/memory_map_region_snapshot.h" + +namespace crashpad { +namespace internal { + +class MemoryMapRegionSnapshotWin : public MemoryMapRegionSnapshot { + public: + explicit MemoryMapRegionSnapshotWin(const MEMORY_BASIC_INFORMATION64& mbi); + ~MemoryMapRegionSnapshotWin() override; + + virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override; + + private: + MINIDUMP_MEMORY_INFO memory_info_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc new file mode 100644 index 0000000..1861a885 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc
@@ -0,0 +1,70 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/memory_snapshot_win.h" + +#include "base/memory/scoped_ptr.h" + +namespace crashpad { +namespace internal { + +MemorySnapshotWin::MemorySnapshotWin() + : MemorySnapshot(), + process_reader_(nullptr), + address_(0), + size_(0), + initialized_() { +} + +MemorySnapshotWin::~MemorySnapshotWin() { +} + +void MemorySnapshotWin::Initialize(ProcessReaderWin* process_reader, + uint64_t address, + uint64_t size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_reader_ = process_reader; + address_ = address; + DLOG_IF(WARNING, size >= std::numeric_limits<size_t>::max()) + << "size overflow"; + size_ = static_cast<size_t>(size); + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +uint64_t MemorySnapshotWin::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +size_t MemorySnapshotWin::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +bool MemorySnapshotWin::Read(Delegate* delegate) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + scoped_ptr<uint8_t[]> buffer(new uint8_t[size_]); + if (!process_reader_->ReadMemory(address_, size_, buffer.get())) { + return false; + } + return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h new file mode 100644 index 0000000..b6d2074e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h
@@ -0,0 +1,68 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include "base/basictypes.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot of a memory region in a process on the running +//! system, when the system runs Windows. +class MemorySnapshotWin final : public MemorySnapshot { + public: + MemorySnapshotWin(); + ~MemorySnapshotWin() override; + + //! \brief Initializes the object. + //! + //! Memory is read lazily. No attempt is made to read the memory snapshot data + //! until Read() is called, and the memory snapshot data is discared when + //! Read() returns. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \param[in] address The base address of the memory region to snapshot, in + //! the snapshot process' address space. + //! \param[in] size The size of the memory region to snapshot. + void Initialize(ProcessReaderWin* process_reader, + uint64_t address, + uint64_t size); + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + private: + ProcessReaderWin* process_reader_; // weak + uint64_t address_; + size_t size_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc new file mode 100644 index 0000000..49478880 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc
@@ -0,0 +1,223 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/module_snapshot_win.h" + +#include "base/strings/utf_string_conversions.h" +#include "snapshot/win/pe_image_annotations_reader.h" +#include "snapshot/win/pe_image_reader.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" +#include "util/win/module_version.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotWin::ModuleSnapshotWin() + : ModuleSnapshot(), + name_(), + pdb_name_(), + uuid_(), + pe_image_reader_(), + process_reader_(nullptr), + timestamp_(0), + age_(0), + initialized_(), + vs_fixed_file_info_(), + initialized_vs_fixed_file_info_() { +} + +ModuleSnapshotWin::~ModuleSnapshotWin() { +} + +bool ModuleSnapshotWin::Initialize( + ProcessReaderWin* process_reader, + const ProcessInfo::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + name_ = process_reader_module.name; + timestamp_ = process_reader_module.timestamp; + pe_image_reader_.reset(new PEImageReader()); + if (!pe_image_reader_->Initialize(process_reader_, + process_reader_module.dll_base, + process_reader_module.size, + base::UTF16ToUTF8(name_))) { + return false; + } + + DWORD age_dword; + if (pe_image_reader_->DebugDirectoryInformation( + &uuid_, &age_dword, &pdb_name_)) { + static_assert(sizeof(DWORD) == sizeof(uint32_t), "unexpected age size"); + age_ = age_dword; + } else { + // If we fully supported all old debugging formats, we would want to extract + // and emit a different type of CodeView record here (as old Microsoft tools + // would do). As we don't expect to ever encounter a module that wouldn't be + // be using .PDB that we actually have symbols for, we simply set a + // plausible name here, but this will never correspond to symbols that we + // have. + pdb_name_ = base::UTF16ToUTF8(name_); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (process_reader_->Is64Bit()) + GetCrashpadOptionsInternal<process_types::internal::Traits64>(options); + else + GetCrashpadOptionsInternal<process_types::internal::Traits32>(options); +} + +std::string ModuleSnapshotWin::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::UTF16ToUTF8(name_); +} + +uint64_t ModuleSnapshotWin::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pe_image_reader_->Address(); +} + +uint64_t ModuleSnapshotWin::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pe_image_reader_->Size(); +} + +time_t ModuleSnapshotWin::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return timestamp_; +} + +void ModuleSnapshotWin::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); + if (ffi) { + *version_0 = ffi->dwFileVersionMS >> 16; + *version_1 = ffi->dwFileVersionMS & 0xffff; + *version_2 = ffi->dwFileVersionLS >> 16; + *version_3 = ffi->dwFileVersionLS & 0xffff; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +void ModuleSnapshotWin::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); + if (ffi) { + *version_0 = ffi->dwProductVersionMS >> 16; + *version_1 = ffi->dwProductVersionMS & 0xffff; + *version_2 = ffi->dwProductVersionLS >> 16; + *version_3 = ffi->dwProductVersionLS & 0xffff; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); + if (ffi) { + switch (ffi->dwFileType) { + case VFT_APP: + return ModuleSnapshot::kModuleTypeExecutable; + case VFT_DLL: + return ModuleSnapshot::kModuleTypeSharedLibrary; + case VFT_DRV: + case VFT_VXD: + return ModuleSnapshot::kModuleTypeLoadableModule; + } + } + return ModuleSnapshot::kModuleTypeUnknown; +} + +void ModuleSnapshotWin::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *uuid = uuid_; + *age = age_; +} + +std::string ModuleSnapshotWin::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pdb_name_; +} + +std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // These correspond to system-logged things on Mac. We don't currently track + // any of these on Windows, but could in the future. + // See https://crashpad.chromium.org/bug/38. + return std::vector<std::string>(); +} + +std::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + PEImageAnnotationsReader annotations_reader( + process_reader_, pe_image_reader_.get(), name_); + return annotations_reader.SimpleMap(); +} + +template <class Traits> +void ModuleSnapshotWin::GetCrashpadOptionsInternal( + CrashpadInfoClientOptions* options) { + process_types::CrashpadInfo<Traits> crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) { + options->crashpad_handler_behavior = TriState::kUnset; + options->system_crash_reporter_forwarding = TriState::kUnset; + return; + } + + options->crashpad_handler_behavior = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.crashpad_handler_behavior); + + options->system_crash_reporter_forwarding = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.system_crash_reporter_forwarding); +} + +const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (initialized_vs_fixed_file_info_.is_uninitialized()) { + initialized_vs_fixed_file_info_.set_invalid(); + if (GetModuleVersionAndType(base::FilePath(name_), &vs_fixed_file_info_)) { + initialized_vs_fixed_file_info_.set_valid(); + } + } + + return initialized_vs_fixed_file_info_.is_valid() ? &vs_fixed_file_info_ + : nullptr; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h new file mode 100644 index 0000000..92e5913 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h
@@ -0,0 +1,119 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <windows.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/process_info.h" + +namespace crashpad { + +class PEImageReader; +struct UUID; + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Windows system. +class ModuleSnapshotWin final : public ModuleSnapshot { + public: + ModuleSnapshotWin(); + ~ModuleSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the task containing the + //! module. + //! \param[in] process_reader_module The module within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + const ProcessInfo::Module& process_reader_module); + + //! \brief Returns options from the module's CrashpadInfo structure. + //! + //! \param[out] options Options set in the module's CrashpadInfo structure. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + //! \brief Returns the PEImageReader used to read this module. Only valid + //! after Initialize() is called. + const PEImageReader& pe_image_reader() const { return *pe_image_reader_; } + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector<std::string> AnnotationsVector() const override; + std::map<std::string, std::string> AnnotationsSimpleMap() const override; + + private: + template <class Traits> + void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + + // Initializes vs_fixed_file_info_ if it has not yet been initialized, and + // returns a pointer to it. Returns nullptr on failure, with a message logged + // on the first call. + const VS_FIXEDFILEINFO* VSFixedFileInfo() const; + + std::wstring name_; + std::string pdb_name_; + UUID uuid_; + scoped_ptr<PEImageReader> pe_image_reader_; + ProcessReaderWin* process_reader_; // weak + time_t timestamp_; + uint32_t age_; + InitializationStateDcheck initialized_; + + // VSFixedFileInfo() is logically const, but updates these members on the + // the call. See https://crashpad.chromium.org/bug/9. + mutable VS_FIXEDFILEINFO vs_fixed_file_info_; + mutable InitializationState initialized_vs_fixed_file_info_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc new file mode 100644 index 0000000..f2d1454 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc
@@ -0,0 +1,82 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/pe_image_annotations_reader.h" + +#include <string.h> + +#include "base/strings/utf_string_conversions.h" +#include "client/simple_string_dictionary.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +PEImageAnnotationsReader::PEImageAnnotationsReader( + ProcessReaderWin* process_reader, + const PEImageReader* pe_image_reader, + const std::wstring& name) + : name_(name), + process_reader_(process_reader), + pe_image_reader_(pe_image_reader) { +} + +std::map<std::string, std::string> PEImageAnnotationsReader::SimpleMap() const { + std::map<std::string, std::string> simple_map_annotations; + if (process_reader_->Is64Bit()) { + ReadCrashpadSimpleAnnotations<process_types::internal::Traits64>( + &simple_map_annotations); + } else { + ReadCrashpadSimpleAnnotations<process_types::internal::Traits32>( + &simple_map_annotations); + } + return simple_map_annotations; +} + +template <class Traits> +void PEImageAnnotationsReader::ReadCrashpadSimpleAnnotations( + std::map<std::string, std::string>* simple_map_annotations) const { + process_types::CrashpadInfo<Traits> crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) + return; + + if (!crashpad_info.simple_annotations) + return; + + std::vector<SimpleStringDictionary::Entry> + simple_annotations(SimpleStringDictionary::num_entries); + if (!process_reader_->ReadMemory( + crashpad_info.simple_annotations, + simple_annotations.size() * sizeof(simple_annotations[0]), + &simple_annotations[0])) { + LOG(WARNING) << "could not read simple annotations from " + << base::UTF16ToUTF8(name_); + return; + } + + for (const auto& entry : simple_annotations) { + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + if (key_length) { + std::string key(entry.key, key_length); + std::string value(entry.value, strnlen(entry.value, sizeof(entry.value))); + if (!simple_map_annotations->insert(std::make_pair(key, value)).second) { + LOG(INFO) << "duplicate simple annotation " << key << " in " + << base::UTF16ToUTF8(name_); + } + } + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.h b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.h new file mode 100644 index 0000000..3409ac8 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.h
@@ -0,0 +1,72 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_ +#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" + +namespace crashpad { + +class PEImageReader; +class ProcessReaderWin; + +//! \brief A reader of annotations stored in a PE image mapped into another +//! process. +//! +//! These annotations are stored for the benefit of crash reporters, and provide +//! information thought to be potentially useful for crash analysis. +//! +//! Currently, this class can decode information stored only in the CrashpadInfo +//! structure. This format is used by Crashpad clients. The "simple annotations" +//! are recovered from any module with a compatible data section, and are +//! included in the annotations returned by SimpleMap(). +class PEImageAnnotationsReader { + public: + //! \brief Constructs the object. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] image_reader The PEImageReader for the PE image file contained + //! within the remote process. + //! \param[in] name The module's name, a string to be used in logged messages. + //! This string is for diagnostic purposes only, and may be empty. + PEImageAnnotationsReader(ProcessReaderWin* process_reader, + const PEImageReader* pe_image_reader, + const std::wstring& name); + ~PEImageAnnotationsReader() {} + + //! \brief Returns the module's annotations that are organized as key-value + //! pairs, where all keys and values are strings. + std::map<std::string, std::string> SimpleMap() const; + + private: + // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap(). + template <class Traits> + void ReadCrashpadSimpleAnnotations( + std::map<std::string, std::string>* simple_map_annotations) const; + + std::wstring name_; + ProcessReaderWin* process_reader_; // weak + const PEImageReader* pe_image_reader_; // weak + + DISALLOW_COPY_AND_ASSIGN(PEImageAnnotationsReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc new file mode 100644 index 0000000..a37ac1e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc
@@ -0,0 +1,145 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/pe_image_annotations_reader.h" + +#include <stdlib.h> +#include <string.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/strings/utf_string_conversions.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "test/paths.h" +#include "test/win/child_launcher.h" +#include "test/win/win_multiprocess.h" +#include "util/file/file_io.h" +#include "util/win/process_info.h" + +namespace crashpad { +namespace test { +namespace { + +enum TestType { + // Don't crash, just test the CrashpadInfo interface. + kDontCrash = 0, + + // The child process should crash by __debugbreak(). + kCrashDebugBreak, +}; + +void TestAnnotationsOnCrash(TestType type, + const base::string16& directory_modification) { + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath test_executable = Paths::Executable(); + std::wstring child_test_executable = + test_executable.DirName() + .Append(directory_modification) + .Append(test_executable.BaseName().RemoveFinalExtension().value() + + L"_simple_annotations.exe") + .value(); + ChildLauncher child(child_test_executable, L""); + child.Start(); + + // Wait for the child process to indicate that it's done setting up its + // annotations via the CrashpadInfo interface. + char c; + CheckedReadFile(child.stdout_read_handle(), &c, sizeof(c)); + + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(child.process_handle(), + ProcessSuspensionState::kRunning)); + + // Verify the "simple map" annotations set via the CrashpadInfo interface. + const std::vector<ProcessInfo::Module>& modules = process_reader.Modules(); + std::map<std::string, std::string> all_annotations_simple_map; + for (const ProcessInfo::Module& module : modules) { + PEImageReader pe_image_reader; + pe_image_reader.Initialize(&process_reader, + module.dll_base, + module.size, + base::UTF16ToUTF8(module.name)); + PEImageAnnotationsReader module_annotations_reader( + &process_reader, &pe_image_reader, module.name); + std::map<std::string, std::string> module_annotations_simple_map = + module_annotations_reader.SimpleMap(); + all_annotations_simple_map.insert(module_annotations_simple_map.begin(), + module_annotations_simple_map.end()); + } + + EXPECT_GE(all_annotations_simple_map.size(), 5u); + EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]); + EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]); + EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]); + EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]); + EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]); + + // Tell the child process to continue. + DWORD expected_exit_code; + switch (type) { + case kDontCrash: + c = ' '; + expected_exit_code = 0; + break; + case kCrashDebugBreak: + c = 'd'; + expected_exit_code = STATUS_BREAKPOINT; + break; + default: + FAIL(); + } + CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c)); + + EXPECT_EQ(expected_exit_code, child.WaitForExit()); +} + +TEST(PEImageAnnotationsReader, DontCrash) { + TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL(".")); +} + +TEST(PEImageAnnotationsReader, CrashDebugBreak) { + TestAnnotationsOnCrash(kCrashDebugBreak, FILE_PATH_LITERAL(".")); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(PEImageAnnotationsReader, DontCrashWOW64) { +#ifndef NDEBUG + TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} + +TEST(PEImageAnnotationsReader, CrashDebugBreakWOW64) { +#ifndef NDEBUG + TestAnnotationsOnCrash(kCrashDebugBreak, + FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestAnnotationsOnCrash(kCrashDebugBreak, + FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} +#endif // ARCH_CPU_64_BITS + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc new file mode 100644 index 0000000..f408fb18 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc
@@ -0,0 +1,312 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/pe_image_reader.h" + +#include <string.h> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_info.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/pdb_structures.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +namespace { + +std::string RangeToString(const CheckedWinAddressRange& range) { + return base::StringPrintf("[0x%llx + 0x%llx (%s)]", + range.Base(), + range.Size(), + range.Is64Bit() ? "64" : "32"); +} + +// Map from Traits to an IMAGE_NT_HEADERSxx. +template <class Traits> +struct NtHeadersForTraits; + +template <> +struct NtHeadersForTraits<process_types::internal::Traits32> { + using type = IMAGE_NT_HEADERS32; +}; + +template <> +struct NtHeadersForTraits<process_types::internal::Traits64> { + using type = IMAGE_NT_HEADERS64; +}; + +} // namespace + +PEImageReader::PEImageReader() + : process_reader_(nullptr), + module_range_(), + module_name_(), + initialized_() { +} + +PEImageReader::~PEImageReader() { +} + +bool PEImageReader::Initialize(ProcessReaderWin* process_reader, + WinVMAddress address, + WinVMSize size, + const std::string& module_name) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + module_range_.SetRange(process_reader_->Is64Bit(), address, size); + if (!module_range_.IsValid()) { + LOG(WARNING) << "invalid module range for " << module_name << ": " + << RangeToString(module_range_); + return false; + } + module_name_ = module_name; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +template <class Traits> +bool PEImageReader::GetCrashpadInfo( + process_types::CrashpadInfo<Traits>* crashpad_info) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + IMAGE_SECTION_HEADER section; + if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>("CPADinfo", + §ion)) { + return false; + } + + if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo<Traits>)) { + LOG(WARNING) << "small crashpad info section size " + << section.Misc.VirtualSize << ", " << module_name_; + return false; + } + + WinVMAddress crashpad_info_address = Address() + section.VirtualAddress; + CheckedWinAddressRange crashpad_info_range(process_reader_->Is64Bit(), + crashpad_info_address, + section.Misc.VirtualSize); + if (!crashpad_info_range.IsValid()) { + LOG(WARNING) << "invalid range for crashpad info: " + << RangeToString(crashpad_info_range); + return false; + } + + if (!module_range_.ContainsRange(crashpad_info_range)) { + LOG(WARNING) << "crashpad info does not fall inside module " + << module_name_; + return false; + } + + if (!process_reader_->ReadMemory(crashpad_info_address, + sizeof(process_types::CrashpadInfo<Traits>), + crashpad_info)) { + LOG(WARNING) << "could not read crashpad info " << module_name_; + return false; + } + + if (crashpad_info->signature != CrashpadInfo::kSignature || + crashpad_info->version < 1) { + LOG(WARNING) << "unexpected crashpad info data " << module_name_; + return false; + } + + return true; +} + +bool PEImageReader::DebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) const { + if (process_reader_->Is64Bit()) { + return ReadDebugDirectoryInformation<IMAGE_NT_HEADERS64>( + uuid, age, pdbname); + } else { + return ReadDebugDirectoryInformation<IMAGE_NT_HEADERS32>( + uuid, age, pdbname); + } +} + +template <class NtHeadersType> +bool PEImageReader::ReadDebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) const { + NtHeadersType nt_headers; + if (!ReadNtHeaders(&nt_headers, nullptr)) + return false; + + if (nt_headers.FileHeader.SizeOfOptionalHeader < + offsetof(decltype(nt_headers.OptionalHeader), + DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]) + + sizeof(nt_headers.OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]) || + nt_headers.OptionalHeader.NumberOfRvaAndSizes <= + IMAGE_DIRECTORY_ENTRY_DEBUG) { + return false; + } + + const IMAGE_DATA_DIRECTORY& data_directory = + nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; + if (data_directory.VirtualAddress == 0 || data_directory.Size == 0) + return false; + IMAGE_DEBUG_DIRECTORY debug_directory; + if (data_directory.Size % sizeof(debug_directory) != 0) + return false; + for (size_t offset = 0; offset < data_directory.Size; + offset += sizeof(debug_directory)) { + if (!CheckedReadMemory(Address() + data_directory.VirtualAddress + offset, + sizeof(debug_directory), + &debug_directory)) { + LOG(WARNING) << "could not read data directory"; + return false; + } + + if (debug_directory.Type != IMAGE_DEBUG_TYPE_CODEVIEW) + continue; + + if (debug_directory.AddressOfRawData) { + if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) { + LOG(WARNING) << "CodeView debug entry of unexpected size"; + continue; + } + + scoped_ptr<char[]> data(new char[debug_directory.SizeOfData]); + if (!CheckedReadMemory(Address() + debug_directory.AddressOfRawData, + debug_directory.SizeOfData, + data.get())) { + LOG(WARNING) << "could not read debug directory"; + return false; + } + + if (*reinterpret_cast<DWORD*>(data.get()) != + CodeViewRecordPDB70::kSignature) { + LOG(WARNING) << "encountered non-7.0 CodeView debug record"; + continue; + } + + CodeViewRecordPDB70* codeview = + reinterpret_cast<CodeViewRecordPDB70*>(data.get()); + *uuid = codeview->uuid; + *age = codeview->age; + // This is a NUL-terminated string encoded in the codepage of the system + // where the binary was linked. We have no idea what that was, so we just + // assume ASCII. + *pdbname = std::string(reinterpret_cast<char*>(&codeview->pdb_name[0])); + return true; + } else if (debug_directory.PointerToRawData) { + // This occurs for non-PDB based debug information. We simply ignore these + // as we don't expect to encounter modules that will be in this format + // for which we'll actually have symbols. See + // https://crashpad.chromium.org/bug/47. + } + } + + return false; +} + +template <class NtHeadersType> +bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers, + WinVMAddress* nt_headers_address) const { + IMAGE_DOS_HEADER dos_header; + if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) { + LOG(WARNING) << "could not read dos header of " << module_name_; + return false; + } + + if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) { + LOG(WARNING) << "invalid e_magic in dos header of " << module_name_; + return false; + } + + WinVMAddress local_nt_headers_address = Address() + dos_header.e_lfanew; + if (!CheckedReadMemory( + local_nt_headers_address, sizeof(NtHeadersType), nt_headers)) { + LOG(WARNING) << "could not read nt headers of " << module_name_; + return false; + } + + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { + LOG(WARNING) << "invalid signature in nt headers of " << module_name_; + return false; + } + + if (nt_headers_address) + *nt_headers_address = local_nt_headers_address; + + return true; +} + +template <class NtHeadersType> +bool PEImageReader::GetSectionByName(const std::string& name, + IMAGE_SECTION_HEADER* section) const { + if (name.size() > sizeof(section->Name)) { + LOG(WARNING) << "supplied section name too long " << name; + return false; + } + + NtHeadersType nt_headers; + WinVMAddress nt_headers_address; + if (!ReadNtHeaders(&nt_headers, &nt_headers_address)) + return false; + + WinVMAddress first_section_address = + nt_headers_address + offsetof(NtHeadersType, OptionalHeader) + + nt_headers.FileHeader.SizeOfOptionalHeader; + for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) { + WinVMAddress section_address = + first_section_address + sizeof(IMAGE_SECTION_HEADER) * i; + if (!CheckedReadMemory( + section_address, sizeof(IMAGE_SECTION_HEADER), section)) { + LOG(WARNING) << "could not read section " << i << " of " << module_name_; + return false; + } + if (strncmp(reinterpret_cast<const char*>(section->Name), + name.c_str(), + sizeof(section->Name)) == 0) { + return true; + } + } + + return false; +} + +bool PEImageReader::CheckedReadMemory(WinVMAddress address, + WinVMSize size, + void* into) const { + CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size); + if (!read_range.IsValid()) { + LOG(WARNING) << "invalid read range: " << RangeToString(read_range); + return false; + } + if (!module_range_.ContainsRange(read_range)) { + LOG(WARNING) << "attempt to read outside of module " << module_name_ + << " at range: " << RangeToString(read_range); + return false; + } + return process_reader_->ReadMemory(address, size, into); +} + +// Explicit instantiations with the only 2 valid template arguments to avoid +// putting the body of the function in the header. +template bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits32>( + process_types::CrashpadInfo<process_types::internal::Traits32>* + crashpad_info) const; +template bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits64>( + process_types::CrashpadInfo<process_types::internal::Traits64>* + crashpad_info) const; + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h new file mode 100644 index 0000000..f6364d9 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h
@@ -0,0 +1,161 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_ +#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_ + +#include <windows.h> + +#include <string> + +#include "base/basictypes.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/win/address_types.h" +#include "util/win/checked_win_address_range.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace process_types { + +template <class Traits> +struct CrashpadInfo { + uint32_t signature; + uint32_t size; + uint32_t version; + uint8_t crashpad_handler_behavior; // TriState. + uint8_t system_crash_reporter_forwarding; // TriState. + uint16_t padding_0; + typename Traits::Pointer simple_annotations; +}; + +} // namespace process_types + +//! \brief A reader for PE images mapped into another process. +//! +//! This class is capable of reading both 32-bit and 64-bit images based on the +//! bitness of the remote process. +//! +//! \sa PEImageAnnotationsReader +class PEImageReader { + public: + PEImageReader(); + ~PEImageReader(); + + //! \brief Initializes the reader. + //! + //! This method must be called only once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] address The address, in the remote process' address space, + //! where the `IMAGE_DOS_HEADER` is located. + //! \param[in] size The size of the image. + //! \param[in] name The module's name, a string to be used in logged messages. + //! This string is for diagnostic purposes. + //! + //! \return `true` if the image was read successfully, `false` otherwise, with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + WinVMAddress address, + WinVMSize size, + const std::string& module_name); + + //! \brief Returns the image's load address. + //! + //! This is the value passed as \a address to Initialize(). + WinVMAddress Address() const { return module_range_.Base(); } + + //! \brief Returns the image's size. + //! + //! This is the value passed as \a size to Initialize(). + WinVMSize Size() const { return module_range_.Size(); } + + //! \brief Obtains the module's CrashpadInfo structure. + //! + //! \return `true` on success, `false` on failure. If the module does not have + //! a `CPADinfo` section, this will return `false` without logging any + //! messages. Other failures will result in messages being logged. + template <class Traits> + bool GetCrashpadInfo( + process_types::CrashpadInfo<Traits>* crashpad_info) const; + + //! \brief Obtains information from the module's debug directory, if any. + //! + //! \param[out] uuid The unique identifier of the executable/PDB. + //! \param[out] age The age field for the pdb (the number of times it's been + //! relinked). + //! \param[out] pdbname Name of the pdb file. + //! \return `true` on success, or `false` if the module has no debug directory + //! entry. + bool DebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) const; + + private: + //! \brief Implementation helper for DebugDirectoryInformation() templated by + //! `IMAGE_NT_HEADERS` type for different bitnesses. + //! + //! \return `true` on success, with the parameters set appropriately. `false` + //! on failure. This method may return `false` without logging anything in + //! the case of a module that does not contain relevant debugging + //! information but is otherwise properly structured. + template <class NtHeadersType> + bool ReadDebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) const; + + //! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image. + //! + //! \param[out] nt_headers The contents of the templated NtHeadersType + //! structure read from the remote process. + //! \param[out] nt_headers_address The address of the templated NtHeadersType + //! structure in the remote process’ address space. If this information is + //! not needed, this parameter may be `nullptr`. + //! + //! \return `true` on success, with \a nt_headers and optionally \a + //! nt_headers_address set appropriately. `false` on failure, with a + //! message logged. + template <class NtHeadersType> + bool ReadNtHeaders(NtHeadersType* nt_headers, + WinVMAddress* nt_headers_address) const; + + //! \brief Finds a given section by name in the image. + template <class NtHeadersType> + bool GetSectionByName(const std::string& name, + IMAGE_SECTION_HEADER* section) const; + + //! \brief Reads memory from target process, first checking whether the range + //! requested falls inside module_range_. + //! + //! \return `true` on success, with \a into filled out, otherwise `false` and + //! a message will be logged. + bool CheckedReadMemory(WinVMAddress address, + WinVMSize size, + void* into) const; + + ProcessReaderWin* process_reader_; // weak + CheckedWinAddressRange module_range_; + std::string module_name_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(PEImageReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader_test.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader_test.cc new file mode 100644 index 0000000..3928fee9 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader_test.cc
@@ -0,0 +1,66 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/pe_image_reader.h" + +#define PSAPI_VERSION 1 +#include <psapi.h> + +#include "gtest/gtest.h" +#include "snapshot/win/process_reader_win.h" +#include "util/win/get_function.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace crashpad { +namespace test { +namespace { + +BOOL CrashpadGetModuleInformation(HANDLE process, + HMODULE module, + MODULEINFO* module_info, + DWORD cb) { + static const auto get_module_information = + GET_FUNCTION_REQUIRED(L"psapi.dll", ::GetModuleInformation); + return get_module_information(process, module, module_info, cb); +} + +TEST(PEImageReader, DebugDirectory) { + PEImageReader pe_image_reader; + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase); + MODULEINFO module_info; + ASSERT_TRUE(CrashpadGetModuleInformation( + GetCurrentProcess(), self, &module_info, sizeof(module_info))); + EXPECT_EQ(self, module_info.lpBaseOfDll); + EXPECT_TRUE(pe_image_reader.Initialize(&process_reader, + reinterpret_cast<WinVMAddress>(self), + module_info.SizeOfImage, + "self")); + UUID uuid; + DWORD age; + std::string pdbname; + EXPECT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); + EXPECT_NE(std::string::npos, pdbname.find("crashpad_snapshot_test")); + const std::string suffix(".pdb"); + EXPECT_EQ( + 0, + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc new file mode 100644 index 0000000..373124d6 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
@@ -0,0 +1,413 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/process_reader_win.h" + +#include <winternl.h> + +#include "base/memory/scoped_ptr.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "util/win/capture_context.h" +#include "util/win/nt_internals.h" +#include "util/win/ntstatus_logging.h" +#include "util/win/process_structs.h" +#include "util/win/scoped_handle.h" +#include "util/win/time.h" + +namespace crashpad { + +namespace { + +// Gets a pointer to the process information structure after a given one, or +// null when iteration is complete, assuming they've been retrieved in a block +// via NtQuerySystemInformation(). +template <class Traits> +process_types::SYSTEM_PROCESS_INFORMATION<Traits>* NextProcess( + process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process) { + ULONG offset = process->NextEntryOffset; + if (offset == 0) + return nullptr; + return reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>( + reinterpret_cast<uint8_t*>(process) + offset); +} + +//! \brief Retrieves the SYSTEM_PROCESS_INFORMATION for a given process. +//! +//! The returned pointer points into the memory block stored by \a buffer. +//! Ownership of \a buffer is transferred to the caller. +//! +//! \return Pointer to the process' data, or nullptr if it was not found or on +//! error. On error, a message will be logged. +template <class Traits> +process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation( + HANDLE process_handle, + scoped_ptr<uint8_t[]>* buffer) { + ULONG buffer_size = 16384; + buffer->reset(new uint8_t[buffer_size]); + NTSTATUS status; + // This must be in retry loop, as we're racing with process creation on the + // system to find a buffer large enough to hold all process information. + for (int tries = 0; tries < 20; ++tries) { + status = crashpad::NtQuerySystemInformation( + SystemProcessInformation, + reinterpret_cast<void*>(buffer->get()), + buffer_size, + &buffer_size); + if (status == STATUS_BUFFER_TOO_SMALL || + status == STATUS_INFO_LENGTH_MISMATCH) { + // Add a little extra to try to avoid an additional loop iteration. We're + // racing with system-wide process creation between here and the next call + // to NtQuerySystemInformation(). + buffer_size += 4096; + buffer->reset(new uint8_t[buffer_size]); + } else { + break; + } + } + + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation"; + return nullptr; + } + + process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process = + reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>( + buffer->get()); + DWORD process_id = GetProcessId(process_handle); + for (;;) { + if (process->UniqueProcessId == process_id) + return process; + process = NextProcess(process); + if (!process) + break; + } + + LOG(ERROR) << "process " << process_id << " not found"; + return nullptr; +} + +template <class Traits> +HANDLE OpenThread( + const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) { + HANDLE handle; + ACCESS_MASK query_access = + THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION; + OBJECT_ATTRIBUTES object_attributes; + InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); + NTSTATUS status = crashpad::NtOpenThread( + &handle, query_access, &object_attributes, &thread_info.ClientId); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtOpenThread"; + return nullptr; + } + return handle; +} + +// It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a +// side-effect of returning the SuspendCount of the thread on success, so we +// fill out these two pieces of semi-unrelated data in the same function. +template <class Traits> +bool FillThreadContextAndSuspendCount(HANDLE thread_handle, + ProcessReaderWin::Thread* thread, + ProcessSuspensionState suspension_state, + bool is_64_reading_32) { + // Don't suspend the thread if it's this thread. This is really only for test + // binaries, as we won't be walking ourselves, in general. + bool is_current_thread = thread->id == + reinterpret_cast<process_types::TEB<Traits>*>( + NtCurrentTeb())->ClientId.UniqueThread; + + if (is_current_thread) { + DCHECK(suspension_state == ProcessSuspensionState::kRunning); + thread->suspend_count = 0; + DCHECK(!is_64_reading_32); + CaptureContext(&thread->context.native); + } else { + DWORD previous_suspend_count = SuspendThread(thread_handle); + if (previous_suspend_count == -1) { + PLOG(ERROR) << "SuspendThread"; + return false; + } + DCHECK(previous_suspend_count > 0 || + suspension_state == ProcessSuspensionState::kRunning); + thread->suspend_count = + previous_suspend_count - + (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0); + + memset(&thread->context, 0, sizeof(thread->context)); +#if defined(ARCH_CPU_32_BITS) + const bool is_native = true; +#elif defined(ARCH_CPU_64_BITS) + const bool is_native = !is_64_reading_32; + if (is_64_reading_32) { + thread->context.wow64.ContextFlags = CONTEXT_ALL; + if (!Wow64GetThreadContext(thread_handle, &thread->context.wow64)) { + PLOG(ERROR) << "Wow64GetThreadContext"; + return false; + } + } +#endif + if (is_native) { + thread->context.native.ContextFlags = CONTEXT_ALL; + if (!GetThreadContext(thread_handle, &thread->context.native)) { + PLOG(ERROR) << "GetThreadContext"; + return false; + } + } + + if (!ResumeThread(thread_handle)) { + PLOG(ERROR) << "ResumeThread"; + return false; + } + } + + return true; +} + +} // namespace + +ProcessReaderWin::Thread::Thread() + : context(), + id(0), + teb_address(0), + teb_size(0), + stack_region_address(0), + stack_region_size(0), + suspend_count(0), + priority_class(0), + priority(0) { +} + +ProcessReaderWin::ProcessReaderWin() + : process_(INVALID_HANDLE_VALUE), + process_info_(), + threads_(), + modules_(), + suspension_state_(), + initialized_threads_(false), + initialized_() { +} + +ProcessReaderWin::~ProcessReaderWin() { +} + +bool ProcessReaderWin::Initialize(HANDLE process, + ProcessSuspensionState suspension_state) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_ = process; + suspension_state_ = suspension_state; + process_info_.Initialize(process); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessReaderWin::ReadMemory(WinVMAddress at, + WinVMSize num_bytes, + void* into) const { + if (num_bytes == 0) + return 0; + + SIZE_T bytes_read; + if (!ReadProcessMemory(process_, + reinterpret_cast<void*>(at), + into, + base::checked_cast<SIZE_T>(num_bytes), + &bytes_read) || + num_bytes != bytes_read) { + PLOG(ERROR) << "ReadMemory at 0x" << std::hex << at << std::dec << " of " + << num_bytes << " bytes failed"; + return false; + } + return true; +} + +WinVMSize ProcessReaderWin::ReadAvailableMemory(WinVMAddress at, + WinVMSize num_bytes, + void* into) const { + if (num_bytes == 0) + return 0; + + auto ranges = process_info_.GetReadableRanges( + CheckedRange<WinVMAddress, WinVMSize>(at, num_bytes)); + + // We only read up until the first unavailable byte, so we only read from the + // first range. If we have no ranges, then no bytes were accessible anywhere + // in the range. + if (ranges.empty()) { + LOG(ERROR) << base::StringPrintf( + "range at 0x%llx, size 0x%llx completely inaccessible", at, num_bytes); + return 0; + } + + // If the start address was adjusted, we couldn't read even the first + // requested byte. + if (ranges.front().base() != at) { + LOG(ERROR) << base::StringPrintf( + "start of range at 0x%llx, size 0x%llx inaccessible", at, num_bytes); + return 0; + } + + DCHECK_LE(ranges.front().size(), num_bytes); + + // If we fail on a normal read, then something went very wrong. + if (!ReadMemory(ranges.front().base(), ranges.front().size(), into)) + return 0; + + return ranges.front().size(); +} + +bool ProcessReaderWin::StartTime(timeval* start_time) const { + FILETIME creation, exit, kernel, user; + if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) { + PLOG(ERROR) << "GetProcessTimes"; + return false; + } + *start_time = FiletimeToTimevalEpoch(creation); + return true; +} + +bool ProcessReaderWin::CPUTimes(timeval* user_time, + timeval* system_time) const { + FILETIME creation, exit, kernel, user; + if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) { + PLOG(ERROR) << "GetProcessTimes"; + return false; + } + *user_time = FiletimeToTimevalInterval(user); + *system_time = FiletimeToTimevalInterval(kernel); + return true; +} + +const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (initialized_threads_) + return threads_; + + initialized_threads_ = true; + +#if defined(ARCH_CPU_64_BITS) + ReadThreadData<process_types::internal::Traits64>(process_info_.IsWow64()); +#else + ReadThreadData<process_types::internal::Traits32>(false); +#endif + + return threads_; +} + +const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!process_info_.Modules(&modules_)) { + LOG(ERROR) << "couldn't retrieve modules"; + } + + return modules_; +} + +const ProcessInfo& ProcessReaderWin::GetProcessInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_info_; +} + +template <class Traits> +void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { + DCHECK(threads_.empty()); + + scoped_ptr<uint8_t[]> buffer; + process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = + GetProcessInformation<Traits>(process_, &buffer); + if (!process_information) + return; + + for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { + const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = + process_information->Threads[i]; + ProcessReaderWin::Thread thread; + thread.id = thread_info.ClientId.UniqueThread; + + ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); + if (!thread_handle.is_valid()) + continue; + + if (!FillThreadContextAndSuspendCount<Traits>(thread_handle.get(), + &thread, + suspension_state_, + is_64_reading_32)) { + continue; + } + + // TODO(scottmg): I believe we could reverse engineer the PriorityClass from + // the Priority, BasePriority, and + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . + // MinidumpThreadWriter doesn't handle it yet in any case, so investigate + // both of those at the same time if it's useful. + thread.priority_class = NORMAL_PRIORITY_CLASS; + + thread.priority = thread_info.Priority; + + process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info; + NTSTATUS status = crashpad::NtQueryInformationThread( + thread_handle.get(), + static_cast<THREADINFOCLASS>(ThreadBasicInformation), + &thread_basic_info, + sizeof(thread_basic_info), + nullptr); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread"; + continue; + } + + // Read the TIB (Thread Information Block) which is the first element of the + // TEB, for its stack fields. + process_types::NT_TIB<Traits> tib; + thread.teb_address = thread_basic_info.TebBaseAddress; + thread.teb_size = sizeof(process_types::TEB<Traits>); + if (ReadMemory(thread.teb_address, sizeof(tib), &tib)) { + WinVMAddress base = 0; + WinVMAddress limit = 0; + // If we're reading a WOW64 process, then the TIB we just retrieved is the + // x64 one. The first word of the x64 TIB points at the x86 TIB. See + // https://msdn.microsoft.com/en-us/library/dn424783.aspx + if (is_64_reading_32) { + process_types::NT_TIB<process_types::internal::Traits32> tib32; + thread.teb_address = tib.Wow64Teb; + thread.teb_size = + sizeof(process_types::TEB<process_types::internal::Traits32>); + if (ReadMemory(thread.teb_address, sizeof(tib32), &tib32)) { + base = tib32.StackBase; + limit = tib32.StackLimit; + } + } else { + base = tib.StackBase; + limit = tib.StackLimit; + } + + // Note, "backwards" because of direction of stack growth. + thread.stack_region_address = limit; + if (limit > base) { + LOG(ERROR) << "invalid stack range: " << base << " - " << limit; + thread.stack_region_size = 0; + } else { + thread.stack_region_size = base - limit; + } + } + threads_.push_back(thread); + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h new file mode 100644 index 0000000..90a115d --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
@@ -0,0 +1,151 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_ + +#include <sys/time.h> +#include <windows.h> + +#include <vector> + +#include "build/build_config.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/address_types.h" +#include "util/win/process_info.h" + +namespace crashpad { + +//! \brief State of process being read by ProcessReaderWin. +enum class ProcessSuspensionState : bool { + //! \brief The process has not been suspended. + kRunning, + + //! \brief The process is suspended. + kSuspended, +}; + +//! \brief Accesses information about another process, identified by a `HANDLE`. +class ProcessReaderWin { + public: + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread() {} + + union { + CONTEXT native; +#if defined(ARCH_CPU_64_BITS) + WOW64_CONTEXT wow64; +#endif + } context; + uint64_t id; + WinVMAddress teb_address; + WinVMSize teb_size; + WinVMAddress stack_region_address; + WinVMSize stack_region_size; + uint32_t suspend_count; + uint32_t priority_class; + uint32_t priority; + }; + + ProcessReaderWin(); + ~ProcessReaderWin(); + + //! \brief Initializes this object. This method must be called before any + //! other. + //! + //! \param[in] process Process handle, must have `PROCESS_QUERY_INFORMATION`, + //! `PROCESS_VM_READ`, and `PROCESS_DUP_HANDLE` access. + //! \param[in] suspension_state Whether \a process has already been suspended + //! by the caller. Typically, this will be + //! ProcessSuspensionState::kSuspended, except for testing uses and where + //! the reader is reading itself. + //! + //! \return `true` on success, indicating that this object will respond + //! validly to further method calls. `false` on failure. On failure, no + //! further method calls should be made. + //! + //! \sa ScopedProcessSuspend + bool Initialize(HANDLE process, ProcessSuspensionState suspension_state); + + //! \return `true` if the target task is a 64-bit process. + bool Is64Bit() const { return process_info_.Is64Bit(); } + + //! \brief Attempts to read \a num_bytes bytes from the target process + //! starting at address \a at into \a into. + //! + //! \return `true` if the entire region could be read, or `false` with an + //! error logged. + //! + //! \sa ReadAvailableMemory + bool ReadMemory(WinVMAddress at, WinVMSize num_bytes, void* into) const; + + //! \brief Attempts to read \a num_bytes bytes from the target process + //! starting at address \a at into \a into. If some of the specified range + //! is not accessible, reads up to the first inaccessible byte. + //! + //! \return The actual number of bytes read. + //! + //! \sa ReadMemory + WinVMSize ReadAvailableMemory(WinVMAddress at, + WinVMSize num_bytes, + void* into) const; + + //! \brief Determines the target process' start time. + //! + //! \param[out] start_time The time that the process started. + //! + //! \return `true` on success, `false` on failure, with a warning logged. + bool StartTime(timeval* start_time) const; + + //! \brief Determines the target process' execution time. + //! + //! \param[out] user_time The amount of time the process has executed code in + //! user mode. + //! \param[out] system_time The amount of time the process has executed code + //! in kernel mode. + //! + //! \return `true` on success, `false` on failure, with a warning logged. + bool CPUTimes(timeval* user_time, timeval* system_time) const; + + //! \return The threads that are in the process. The first element (at index + //! `0`) corresponds to the main thread. + const std::vector<Thread>& Threads(); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector<ProcessInfo::Module>& Modules(); + + //! \return A ProcessInfo object for the process being read. + const ProcessInfo& GetProcessInfo() const; + + private: + template <class Traits> + void ReadThreadData(bool is_64_reading_32); + + HANDLE process_; + ProcessInfo process_info_; + std::vector<Thread> threads_; + std::vector<ProcessInfo::Module> modules_; + ProcessSuspensionState suspension_state_; + bool initialized_threads_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessReaderWin); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc new file mode 100644 index 0000000..2c4660e --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
@@ -0,0 +1,207 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/process_reader_win.h" + +#include <string.h> +#include <windows.h> + +#include "gtest/gtest.h" +#include "test/win/win_multiprocess.h" +#include "util/synchronization/semaphore.h" +#include "util/thread/thread.h" +#include "util/win/scoped_process_suspend.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcessReaderWin, SelfBasic) { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(GetCurrentProcessId(), process_reader.GetProcessInfo().ProcessID()); + + const char kTestMemory[] = "Some test memory"; + char buffer[arraysize(kTestMemory)]; + ASSERT_TRUE( + process_reader.ReadMemory(reinterpret_cast<uintptr_t>(kTestMemory), + sizeof(kTestMemory), + &buffer)); + EXPECT_STREQ(kTestMemory, buffer); +} + +const char kTestMemory[] = "Read me from another process"; + +class ProcessReaderChild final : public WinMultiprocess { + public: + ProcessReaderChild() : WinMultiprocess() {} + ~ProcessReaderChild() {} + + private: + void WinMultiprocessParent() override { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildProcess(), + ProcessSuspensionState::kRunning)); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + WinVMAddress address; + CheckedReadFile(ReadPipeHandle(), &address, sizeof(address)); + + char buffer[sizeof(kTestMemory)]; + ASSERT_TRUE( + process_reader.ReadMemory(address, sizeof(kTestMemory), &buffer)); + EXPECT_EQ(0, strcmp(kTestMemory, buffer)); + } + + void WinMultiprocessChild() override { + WinVMAddress address = reinterpret_cast<WinVMAddress>(kTestMemory); + CheckedWriteFile(WritePipeHandle(), &address, sizeof(address)); + + // Wait for the parent to signal that it's OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild); +}; + +TEST(ProcessReaderWin, ChildBasic) { + WinMultiprocess::Run<ProcessReaderChild>(); +} + +TEST(ProcessReaderWin, SelfOneThread) { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + + const std::vector<ProcessReaderWin::Thread>& threads = + process_reader.Threads(); + + // If other tests ran in this process previously, threads may have been + // created and may still be running. This check must look for at least one + // thread, not exactly one thread. + ASSERT_GE(threads.size(), 1u); + + EXPECT_EQ(GetCurrentThreadId(), threads[0].id); +#if defined(ARCH_CPU_64_BITS) + EXPECT_NE(0, threads[0].context.native.Rip); +#else + EXPECT_NE(0u, threads[0].context.native.Eip); +#endif + + EXPECT_EQ(0, threads[0].suspend_count); +} + +class ProcessReaderChildThreadSuspendCount final : public WinMultiprocess { + public: + ProcessReaderChildThreadSuspendCount() : WinMultiprocess() {} + ~ProcessReaderChildThreadSuspendCount() {} + + private: + enum : unsigned int { kCreatedThreads = 3 }; + + class SleepingThread : public Thread { + public: + SleepingThread() : done_(nullptr) {} + + void SetHandle(Semaphore* done) { + done_= done; + } + + void ThreadMain() override { + done_->Wait(); + }; + + private: + Semaphore* done_; + }; + + void WinMultiprocessParent() override { + char c; + CheckedReadFile(ReadPipeHandle(), &c, sizeof(c)); + ASSERT_EQ(' ', c); + + { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildProcess(), + ProcessSuspensionState::kRunning)); + + const auto& threads = process_reader.Threads(); + ASSERT_GE(threads.size(), kCreatedThreads + 1); + for (const auto& thread : threads) + EXPECT_EQ(0u, thread.suspend_count); + } + + { + ScopedProcessSuspend suspend(ChildProcess()); + + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize( + ChildProcess(), ProcessSuspensionState::kSuspended)); + + // Confirm that thread counts are adjusted correctly for the process being + // suspended. + const auto& threads = process_reader.Threads(); + ASSERT_GE(threads.size(), kCreatedThreads + 1); + for (const auto& thread : threads) + EXPECT_EQ(0u, thread.suspend_count); + } + } + + void WinMultiprocessChild() override { + // Create three dummy threads so we can confirm we read successfully read + // more than just the main thread. + SleepingThread threads[kCreatedThreads]; + Semaphore done(0); + for (auto& thread : threads) + thread.SetHandle(&done); + for (auto& thread : threads) + thread.Start(); + + char c = ' '; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + + // Wait for the parent to signal that it's OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + + for (int i = 0; i < arraysize(threads); ++i) + done.Signal(); + for (auto& thread : threads) + thread.Join(); + } + + DISALLOW_COPY_AND_ASSIGN(ProcessReaderChildThreadSuspendCount); +}; + +TEST(ProcessReaderWin, ChildThreadSuspendCounts) { + WinMultiprocess::Run<ProcessReaderChildThreadSuspendCount>(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc new file mode 100644 index 0000000..8c184c26 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
@@ -0,0 +1,456 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/process_snapshot_win.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "snapshot/win/memory_snapshot_win.h" +#include "snapshot/win/module_snapshot_win.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/time.h" + +namespace crashpad { + +ProcessSnapshotWin::ProcessSnapshotWin() + : ProcessSnapshot(), + system_(), + threads_(), + modules_(), + exception_(), + memory_map_(), + process_reader_(), + report_id_(), + client_id_(), + annotations_simple_map_(), + snapshot_time_(), + initialized_() { +} + +ProcessSnapshotWin::~ProcessSnapshotWin() { +} + +bool ProcessSnapshotWin::Initialize( + HANDLE process, + ProcessSuspensionState suspension_state, + WinVMAddress debug_critical_section_address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetTimeOfDay(&snapshot_time_); + + if (!process_reader_.Initialize(process, suspension_state)) + return false; + + system_.Initialize(&process_reader_); + + if (process_reader_.Is64Bit()) { + InitializePebData<process_types::internal::Traits64>( + debug_critical_section_address); + } else { + InitializePebData<process_types::internal::Traits32>( + debug_critical_section_address); + } + + InitializeThreads(); + InitializeModules(); + + for (const MEMORY_BASIC_INFORMATION64& mbi : + process_reader_.GetProcessInfo().MemoryInfo()) { + memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi)); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSnapshotWin::InitializeException( + WinVMAddress exception_information_address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK(!exception_); + + ExceptionInformation exception_information; + if (!process_reader_.ReadMemory(exception_information_address, + sizeof(exception_information), + &exception_information)) { + LOG(WARNING) << "ReadMemory ExceptionInformation failed"; + return false; + } + + exception_.reset(new internal::ExceptionSnapshotWin()); + if (!exception_->Initialize(&process_reader_, + exception_information.thread_id, + exception_information.exception_pointers)) { + exception_.reset(); + return false; + } + + return true; +} + +void ProcessSnapshotWin::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (internal::ModuleSnapshotWin* module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + +pid_t ProcessSnapshotWin::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.GetProcessInfo().ProcessID(); +} + +pid_t ProcessSnapshotWin::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.GetProcessInfo().ParentProcessID(); +} + +void ProcessSnapshotWin::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotWin::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotWin::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotWin::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotWin::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map<std::string, std::string>& +ProcessSnapshotWin::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotWin::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector<const ThreadSnapshot*> ProcessSnapshotWin::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<const ThreadSnapshot*> threads; + for (internal::ThreadSnapshotWin* thread : threads_) { + threads.push_back(thread); + } + return threads; +} + +std::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<const ModuleSnapshot*> modules; + for (internal::ModuleSnapshotWin* module : modules_) { + modules.push_back(module); + } + return modules; +} + +const ExceptionSnapshot* ProcessSnapshotWin::Exception() const { + return exception_.get(); +} + +std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotWin::MemoryMap() + const { + std::vector<const MemoryMapRegionSnapshot*> memory_map; + for (const auto& item : memory_map_) + memory_map.push_back(item); + return memory_map; +} + +std::vector<HandleSnapshot> ProcessSnapshotWin::Handles() const { + std::vector<HandleSnapshot> result; + for (const auto& handle : process_reader_.GetProcessInfo().Handles()) { + HandleSnapshot snapshot; + // This is probably not strictly correct, but these are not localized so we + // expect them all to be in ASCII range anyway. This will need to be more + // carefully done if the object name is added. + snapshot.type_name = base::UTF16ToUTF8(handle.type_name); + snapshot.handle = handle.handle; + snapshot.attributes = handle.attributes; + snapshot.granted_access = handle.granted_access; + snapshot.pointer_count = handle.pointer_count; + snapshot.handle_count = handle.handle_count; + result.push_back(snapshot); + } + return result; +} + +std::vector<const MemorySnapshot*> ProcessSnapshotWin::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<const MemorySnapshot*> extra_memory; + for (const auto& em : extra_memory_) + extra_memory.push_back(em); + return extra_memory; +} + +void ProcessSnapshotWin::InitializeThreads() { + const std::vector<ProcessReaderWin::Thread>& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReaderWin::Thread& process_reader_thread : + process_reader_threads) { + auto thread = make_scoped_ptr(new internal::ThreadSnapshotWin()); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(thread.release()); + } + } +} + +void ProcessSnapshotWin::InitializeModules() { + const std::vector<ProcessInfo::Module>& process_reader_modules = + process_reader_.Modules(); + for (const ProcessInfo::Module& process_reader_module : + process_reader_modules) { + auto module = make_scoped_ptr(new internal::ModuleSnapshotWin()); + if (module->Initialize(&process_reader_, process_reader_module)) { + modules_.push_back(module.release()); + } + } +} + +template <class Traits> +void ProcessSnapshotWin::InitializePebData( + WinVMAddress debug_critical_section_address) { + WinVMAddress peb_address; + WinVMSize peb_size; + process_reader_.GetProcessInfo().Peb(&peb_address, &peb_size); + AddMemorySnapshot(peb_address, peb_size, &extra_memory_); + + process_types::PEB<Traits> peb_data; + if (!process_reader_.ReadMemory(peb_address, peb_size, &peb_data)) { + LOG(ERROR) << "ReadMemory PEB"; + return; + } + + process_types::PEB_LDR_DATA<Traits> peb_ldr_data; + AddMemorySnapshot(peb_data.Ldr, sizeof(peb_ldr_data), &extra_memory_); + if (!process_reader_.ReadMemory( + peb_data.Ldr, sizeof(peb_ldr_data), &peb_ldr_data)) { + LOG(ERROR) << "ReadMemory PEB_LDR_DATA"; + } else { + // Walk the LDR structure to retrieve its pointed-to data. + AddMemorySnapshotForLdrLIST_ENTRY( + peb_ldr_data.InLoadOrderModuleList, + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, InLoadOrderLinks), + &extra_memory_); + AddMemorySnapshotForLdrLIST_ENTRY( + peb_ldr_data.InMemoryOrderModuleList, + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, + InMemoryOrderLinks), + &extra_memory_); + AddMemorySnapshotForLdrLIST_ENTRY( + peb_ldr_data.InInitializationOrderModuleList, + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, + InInitializationOrderLinks), + &extra_memory_); + } + + process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters; + if (!process_reader_.ReadMemory(peb_data.ProcessParameters, + sizeof(process_parameters), + &process_parameters)) { + LOG(ERROR) << "ReadMemory RTL_USER_PROCESS_PARAMETERS"; + return; + } + AddMemorySnapshot( + peb_data.ProcessParameters, sizeof(process_parameters), &extra_memory_); + + AddMemorySnapshotForUNICODE_STRING( + process_parameters.CurrentDirectory.DosPath, &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.DllPath, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.ImagePathName, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.CommandLine, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.WindowTitle, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.DesktopInfo, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.ShellInfo, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.RuntimeData, + &extra_memory_); + AddMemorySnapshot( + process_parameters.Environment, + DetermineSizeOfEnvironmentBlock(process_parameters.Environment), + &extra_memory_); + + // Walk the loader lock which is directly referenced by the PEB. + ReadLock<Traits>(peb_data.LoaderLock, &extra_memory_); + + // TODO(scottmg): Use debug_critical_section_address to walk the list of + // locks (see history of this file for walking code). In some configurations + // this can walk many thousands of locks, so we may want to get some + // annotation from the client for which locks to grab. Unfortunately, without + // walking the list, the !locks command in windbg won't work because it + // requires the lock pointed to by ntdll!RtlCriticalSectionList, which we + // won't have captured. +} + +void ProcessSnapshotWin::AddMemorySnapshot( + WinVMAddress address, + WinVMSize size, + PointerVector<internal::MemorySnapshotWin>* into) { + if (size == 0) + return; + + if (!process_reader_.GetProcessInfo().LoggingRangeIsFullyReadable( + CheckedRange<WinVMAddress, WinVMSize>(address, size))) { + return; + } + + // If we have already added this exact range, don't add it again. This is + // useful for the LDR module lists which are a set of doubly-linked lists, all + // pointing to the same module name strings. + // TODO(scottmg): A more general version of this, handling overlapping, + // contained, etc. https://crashpad.chromium.org/bug/61. + for (const auto& memory_snapshot : *into) { + if (memory_snapshot->Address() == address && + memory_snapshot->Size() == size) { + return; + } + } + + internal::MemorySnapshotWin* memory_snapshot = + new internal::MemorySnapshotWin(); + memory_snapshot->Initialize(&process_reader_, address, size); + into->push_back(memory_snapshot); +} + +template <class Traits> +void ProcessSnapshotWin::AddMemorySnapshotForUNICODE_STRING( + const process_types::UNICODE_STRING<Traits>& us, + PointerVector<internal::MemorySnapshotWin>* into) { + AddMemorySnapshot(us.Buffer, us.Length, into); +} + +template <class Traits> +void ProcessSnapshotWin::AddMemorySnapshotForLdrLIST_ENTRY( + const process_types::LIST_ENTRY<Traits>& le, size_t offset_of_member, + PointerVector<internal::MemorySnapshotWin>* into) { + // Walk the doubly-linked list of entries, adding the list memory itself, as + // well as pointed-to strings. + typename Traits::Pointer last = le.Blink; + process_types::LDR_DATA_TABLE_ENTRY<Traits> entry; + typename Traits::Pointer cur = le.Flink; + for (;;) { + // |cur| is the pointer to LIST_ENTRY embedded in the LDR_DATA_TABLE_ENTRY. + // So we need to offset back to the beginning of the structure. + if (!process_reader_.ReadMemory( + cur - offset_of_member, sizeof(entry), &entry)) { + return; + } + AddMemorySnapshot(cur - offset_of_member, sizeof(entry), into); + AddMemorySnapshotForUNICODE_STRING(entry.FullDllName, into); + AddMemorySnapshotForUNICODE_STRING(entry.BaseDllName, into); + + process_types::LIST_ENTRY<Traits>* links = + reinterpret_cast<process_types::LIST_ENTRY<Traits>*>( + reinterpret_cast<unsigned char*>(&entry) + offset_of_member); + cur = links->Flink; + if (cur == last) + break; + } +} + +WinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock( + WinVMAddress start_of_environment_block) { + // http://blogs.msdn.com/b/oldnewthing/archive/2010/02/03/9957320.aspx On + // newer OSs there's no stated limit, but in practice grabbing 32k characters + // should be more than enough. + std::wstring env_block; + env_block.resize(32768); + WinVMSize bytes_read = process_reader_.ReadAvailableMemory( + start_of_environment_block, + env_block.size() * sizeof(env_block[0]), + &env_block[0]); + env_block.resize( + static_cast<unsigned int>(bytes_read / sizeof(env_block[0]))); + const wchar_t terminator[] = { 0, 0 }; + size_t at = env_block.find(std::wstring(terminator, arraysize(terminator))); + if (at != std::wstring::npos) + env_block.resize(at + arraysize(terminator)); + + return env_block.size() * sizeof(env_block[0]); +} + +template <class Traits> +void ProcessSnapshotWin::ReadLock( + WinVMAddress start, + PointerVector<internal::MemorySnapshotWin>* into) { + // We're walking the RTL_CRITICAL_SECTION_DEBUG ProcessLocksList, but starting + // from an actual RTL_CRITICAL_SECTION, so start by getting to the first + // RTL_CRITICAL_SECTION_DEBUG. + + process_types::RTL_CRITICAL_SECTION<Traits> critical_section; + if (!process_reader_.ReadMemory( + start, sizeof(critical_section), &critical_section)) { + LOG(ERROR) << "failed to read RTL_CRITICAL_SECTION"; + return; + } + + AddMemorySnapshot( + start, sizeof(process_types::RTL_CRITICAL_SECTION<Traits>), into); + + const decltype(critical_section.DebugInfo) kInvalid = + static_cast<decltype(critical_section.DebugInfo)>(-1); + if (critical_section.DebugInfo == kInvalid) + return; + + AddMemorySnapshot(critical_section.DebugInfo, + sizeof(process_types::RTL_CRITICAL_SECTION_DEBUG<Traits>), + into); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h new file mode 100644 index 0000000..a1dd723 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h
@@ -0,0 +1,195 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_ + +#include <windows.h> +#include <sys/time.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/win/exception_snapshot_win.h" +#include "snapshot/win/memory_map_region_snapshot_win.h" +#include "snapshot/win/memory_snapshot_win.h" +#include "snapshot/win/module_snapshot_win.h" +#include "snapshot/win/system_snapshot_win.h" +#include "snapshot/win/thread_snapshot_win.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/win/address_types.h" +#include "util/win/process_structs.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Windows system. +class ProcessSnapshotWin final : public ProcessSnapshot { + public: + ProcessSnapshotWin(); + ~ProcessSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process The handle to create a snapshot from. + //! \param[in] suspension_state Whether \a process has been suspended by the + //! caller. + //! \param[in] debug_critical_section_address The address in the target + //! process's address space of a `CRITICAL_SECTION` allocated with valid + //! `.DebugInfo`. Used as a starting point to walk the process's locks. + //! May be `0`. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + //! + //! \sa ScopedProcessSuspend + bool Initialize(HANDLE process, + ProcessSuspensionState suspension_state, + WinVMAddress debug_critical_section_address); + + //! \brief Initializes the object's exception. + //! + //! This populates the data to be returned by Exception(). + //! + //! This method must not be called until after a successful call to + //! Initialize(). + //! + //! \param[in] exception_information_address The address in the client + //! process's address space of an ExceptionInformation structure. + //! + //! \return `true` if the exception information could be initialized, `false` + //! otherwise with an appropriate message logged. When this method returns + //! `false`, the ProcessSnapshotWin object's validity remains unchanged. + bool InitializeException(WinVMAddress exception_information_address); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! The crash report ID is under the control of the snapshot producer, which + //! may call this method to set the report ID. If this is not done, ReportID() + //! will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! The client ID is under the control of the snapshot producer, which may + //! call this method to set the client ID. If this is not done, ClientID() + //! will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! All process annotations are under the control of the snapshot producer, + //! which may call this method to establish these annotations. Contrast this + //! with module annotations, which are under the control of the process being + //! snapshotted. + void SetAnnotationsSimpleMap( + const std::map<std::string, std::string>& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ProcessSnapshot: + + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map<std::string, std::string>& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector<const ThreadSnapshot*> Threads() const override; + std::vector<const ModuleSnapshot*> Modules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override; + std::vector<HandleSnapshot> Handles() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: + // Initializes threads_ on behalf of Initialize(). + void InitializeThreads(); + + // Initializes modules_ on behalf of Initialize(). + void InitializeModules(); + + // Initializes various memory blocks reachable from the PEB on behalf of + // Initialize(). + template <class Traits> + void InitializePebData(WinVMAddress debug_critical_section_address); + + void AddMemorySnapshot(WinVMAddress address, + WinVMSize size, + PointerVector<internal::MemorySnapshotWin>* into); + + template <class Traits> + void AddMemorySnapshotForUNICODE_STRING( + const process_types::UNICODE_STRING<Traits>& us, + PointerVector<internal::MemorySnapshotWin>* into); + + template <class Traits> + void AddMemorySnapshotForLdrLIST_ENTRY( + const process_types::LIST_ENTRY<Traits>& le, + size_t offset_of_member, + PointerVector<internal::MemorySnapshotWin>* into); + + WinVMSize DetermineSizeOfEnvironmentBlock( + WinVMAddress start_of_environment_block); + + // Starting from the address of a CRITICAL_SECTION, add a lock and, if valid, + // its .DebugInfo field to the snapshot. + template <class Traits> + void ReadLock(WinVMAddress start, + PointerVector<internal::MemorySnapshotWin>* into); + + internal::SystemSnapshotWin system_; + PointerVector<internal::MemorySnapshotWin> extra_memory_; + PointerVector<internal::ThreadSnapshotWin> threads_; + PointerVector<internal::ModuleSnapshotWin> modules_; + scoped_ptr<internal::ExceptionSnapshotWin> exception_; + PointerVector<internal::MemoryMapRegionSnapshotWin> memory_map_; + ProcessReaderWin process_reader_; + UUID report_id_; + UUID client_id_; + std::map<std::string, std::string> annotations_simple_map_; + timeval snapshot_time_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc new file mode 100644 index 0000000..0c4c343 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc
@@ -0,0 +1,107 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/process_snapshot_win.h" + +#include "base/files/file_path.h" +#include "gtest/gtest.h" +#include "snapshot/win/module_snapshot_win.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" +#include "util/win/scoped_process_suspend.h" +#include "test/paths.h" +#include "test/win/child_launcher.h" + +namespace crashpad { +namespace test { +namespace { + +void TestImageReaderChild(const base::string16& directory_modification) { + UUID done_uuid(UUID::InitializeWithNewTag{}); + ScopedKernelHANDLE done( + CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str())); + ASSERT_TRUE(done.get()); + + base::FilePath test_executable = Paths::Executable(); + std::wstring child_test_executable = + test_executable.DirName() + .Append(directory_modification) + .Append(test_executable.BaseName().RemoveFinalExtension().value() + + L"_image_reader.exe") + .value(); + ChildLauncher child(child_test_executable, done_uuid.ToString16()); + child.Start(); + + char c; + ASSERT_TRUE(LoggingReadFile(child.stdout_read_handle(), &c, sizeof(c))); + ASSERT_EQ(' ', c); + + ScopedProcessSuspend suspend(child.process_handle()); + + ProcessSnapshotWin process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize( + child.process_handle(), ProcessSuspensionState::kSuspended, 0)); + + ASSERT_GE(process_snapshot.Modules().size(), 2u); + + UUID uuid; + DWORD age; + std::string pdbname; + const std::string suffix(".pdb"); + + // Check the main .exe to see that we can retrieve its sections. + auto module = reinterpret_cast<const internal::ModuleSnapshotWin*>( + process_snapshot.Modules()[0]); + ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation( + &uuid, &age, &pdbname)); + EXPECT_NE(std::string::npos, + pdbname.find("crashpad_snapshot_test_image_reader")); + EXPECT_EQ( + 0, + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix)); + + // Check the dll it loads too. + module = reinterpret_cast<const internal::ModuleSnapshotWin*>( + process_snapshot.Modules().back()); + ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation( + &uuid, &age, &pdbname)); + EXPECT_NE(std::string::npos, + pdbname.find("crashpad_snapshot_test_image_reader_module")); + EXPECT_EQ( + 0, + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix)); + + // Tell the child it can terminate. + SetEvent(done.get()); +} + +TEST(ProcessSnapshotTest, CrashpadInfoChild) { + TestImageReaderChild(FILE_PATH_LITERAL(".")); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ProcessSnapshotTest, CrashpadInfoChildWOW64) { +#ifndef NDEBUG + TestImageReaderChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestImageReaderChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} +#endif + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc new file mode 100644 index 0000000..88827a5 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc
@@ -0,0 +1,336 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/system_snapshot_win.h" + +#include <intrin.h> +#include <powrprof.h> +#include <windows.h> +#include <winnt.h> + +#include <algorithm> +#include <utility> +#include <vector> + +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "util/win/module_version.h" + +namespace crashpad { + +namespace { + +//! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileFlags +//! value. +std::string GetStringForFileFlags(uint32_t file_flags) { + std::string result; + DCHECK_EQ(file_flags & VS_FF_INFOINFERRED, 0u); + if (file_flags & VS_FF_DEBUG) + result += "Debug,"; + if (file_flags & VS_FF_PATCHED) + result += "Patched,"; + if (file_flags & VS_FF_PRERELEASE) + result += "Prerelease,"; + if (file_flags & VS_FF_PRIVATEBUILD) + result += "Private,"; + if (file_flags & VS_FF_SPECIALBUILD) + result += "Special,"; + if (!result.empty()) + return result.substr(0, result.size() - 1); // Remove trailing comma. + return result; +} + +//! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileOS value. +std::string GetStringForFileOS(uint32_t file_os) { + // There are a variety of ancient things this could theoretically be. In + // practice, we're always going to get VOS_NT_WINDOWS32 here. + if ((file_os & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32) + return "Windows NT"; + else + return "Unknown"; +} + +} // namespace + +namespace internal { + +SystemSnapshotWin::SystemSnapshotWin() + : SystemSnapshot(), + os_version_full_(), + os_version_build_(), + process_reader_(nullptr), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + os_server_(false), + initialized_() { +} + +SystemSnapshotWin::~SystemSnapshotWin() { +} + +void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + + // We use both GetVersionEx and VerQueryValue. GetVersionEx is not trustworthy + // after Windows 8 (depending on the application manifest) so its data is used + // only to fill the os_server_ field, and the rest comes from the version + // information stamped on kernel32.dll. + OSVERSIONINFOEX version_info = {sizeof(version_info)}; + if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) { + PLOG(WARNING) << "GetVersionEx"; + } else { + const wchar_t kSystemDll[] = L"kernel32.dll"; + VS_FIXEDFILEINFO ffi; + if (GetModuleVersionAndType(base::FilePath(kSystemDll), &ffi)) { + std::string flags_string = GetStringForFileFlags(ffi.dwFileFlags); + os_server_ = version_info.wProductType != VER_NT_WORKSTATION; + std::string os_name = GetStringForFileOS(ffi.dwFileOS); + os_version_major_ = ffi.dwFileVersionMS >> 16; + os_version_minor_ = ffi.dwFileVersionMS & 0xffff; + os_version_bugfix_ = ffi.dwFileVersionLS >> 16; + os_version_build_ = + base::StringPrintf("%d", ffi.dwFileVersionLS & 0xffff); + os_version_full_ = base::StringPrintf( + "%s %d.%d.%d.%s%s", + os_name.c_str(), + os_version_major_, + os_version_minor_, + os_version_bugfix_, + os_version_build_.c_str(), + flags_string.empty() ? "" : (std::string(" (") + flags_string + ")") + .c_str()); + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotWin::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 + : kCPUArchitectureX86; +} + +uint32_t SystemSnapshotWin::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + uint32_t raw = CPUX86Signature(); + uint8_t stepping = raw & 0xf; + uint8_t model = (raw & 0xf0) >> 4; + uint8_t family = (raw & 0xf00) >> 8; + uint8_t extended_model = static_cast<uint8_t>((raw & 0xf0000) >> 16); + uint16_t extended_family = (raw & 0xff00000) >> 20; + + // For families before 15, extended_family are simply reserved bits. + if (family < 15) + extended_family = 0; + // extended_model is only used for families 6 and 15. + if (family != 6 && family != 15) + extended_model = 0; + + uint16_t adjusted_family = family + extended_family; + uint8_t adjusted_model = model + (extended_model << 4); + return (adjusted_family << 16) | (adjusted_model << 8) | stepping; +} + +uint8_t SystemSnapshotWin::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + if (!base::IsValueInRangeForNumericType<uint8_t>( + system_info.dwNumberOfProcessors)) { + LOG(WARNING) << "dwNumberOfProcessors exceeds uint8_t storage"; + } + return base::saturated_cast<uint8_t>(system_info.dwNumberOfProcessors); +} + +std::string SystemSnapshotWin::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int cpu_info[4]; + __cpuid(cpu_info, 0); + char vendor[12]; + *reinterpret_cast<int*>(vendor) = cpu_info[1]; + *reinterpret_cast<int*>(vendor + 4) = cpu_info[3]; + *reinterpret_cast<int*>(vendor + 8) = cpu_info[2]; + return std::string(vendor, sizeof(vendor)); +} + +void SystemSnapshotWin::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int num_cpus = CPUCount(); + DCHECK_GT(num_cpus, 0); + std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpus); + if (CallNtPowerInformation(ProcessorInformation, + nullptr, + 0, + &info[0], + sizeof(PROCESSOR_POWER_INFORMATION) * num_cpus) != + 0) { + *current_hz = 0; + *max_hz = 0; + return; + } + const uint64_t kMhzToHz = static_cast<uint64_t>(1E6); + *current_hz = std::max_element(info.begin(), + info.end(), + [](const PROCESSOR_POWER_INFORMATION& a, + const PROCESSOR_POWER_INFORMATION& b) { + return a.CurrentMhz < b.CurrentMhz; + })->CurrentMhz * + kMhzToHz; + *max_hz = std::max_element(info.begin(), + info.end(), + [](const PROCESSOR_POWER_INFORMATION& a, + const PROCESSOR_POWER_INFORMATION& b) { + return a.MaxMhz < b.MaxMhz; + })->MaxMhz * + kMhzToHz; +} + +uint32_t SystemSnapshotWin::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int cpu_info[4]; + // We will never run on any processors that don't support at least function 1. + __cpuid(cpu_info, 1); + return cpu_info[0]; +} + +uint64_t SystemSnapshotWin::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int cpu_info[4]; + // We will never run on any processors that don't support at least function 1. + __cpuid(cpu_info, 1); + return (static_cast<uint64_t>(cpu_info[2]) << 32) | + static_cast<uint64_t>(cpu_info[3]); +} + +uint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int cpu_info[4]; + // We will never run on any processors that don't support at least extended + // function 1. + __cpuid(cpu_info, 0x80000001); + return (static_cast<uint64_t>(cpu_info[2]) << 32) | + static_cast<uint64_t>(cpu_info[3]); +} + +uint32_t SystemSnapshotWin::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int cpu_info[4]; + + // Make sure leaf 7 can be called. + __cpuid(cpu_info, 0); + if (cpu_info[0] < 7) + return 0; + + __cpuidex(cpu_info, 7, 0); + return cpu_info[1]; +} + +bool SystemSnapshotWin::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // The correct way to check for denormals-as-zeros (DAZ) support is to examine + // mxcsr mask, which can be done with fxsave. See Intel Software Developer's + // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 "Checking for the + // DAZ Flag in the MXCSR Register". Note that since this function tests for + // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would + // indicate whether DAZ is actually enabled, which is a per-thread context + // concern. + + // Test for fxsave support. + uint64_t features = CPUX86Features(); + if (!(features & (UINT64_C(1) << 24))) { + return false; + } + + // Call fxsave. + __declspec(align(16)) uint32_t extended_registers[128]; + _fxsave(&extended_registers); + uint32_t mxcsr_mask = extended_registers[7]; + + // Test the DAZ bit. + return (mxcsr_mask & (1 << 6)) != 0; +} + +SystemSnapshot::OperatingSystem SystemSnapshotWin::GetOperatingSystem() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemWindows; +} + +bool SystemSnapshotWin::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_server_; +} + +void SystemSnapshotWin::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotWin::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotWin::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): Not sure if there's anything sensible to put here. + return std::string(); +} + +bool SystemSnapshotWin::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return !!IsProcessorFeaturePresent(PF_NX_ENABLED); +} + +void SystemSnapshotWin::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + // This returns the current time zone status rather than the status at the + // time of the snapshot. This differs from the Mac implementation. + TIME_ZONE_INFORMATION time_zone_information; + *dst_status = static_cast<DaylightSavingTimeStatus>( + GetTimeZoneInformation(&time_zone_information)); + *standard_offset_seconds = + (time_zone_information.Bias + time_zone_information.StandardBias) * -60; + *daylight_offset_seconds = + (time_zone_information.Bias + time_zone_information.DaylightBias) * -60; + *standard_name = base::UTF16ToUTF8(time_zone_information.StandardName); + *daylight_name = base::UTF16ToUTF8(time_zone_information.DaylightName); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h new file mode 100644 index 0000000..b7a22c6ad --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h
@@ -0,0 +1,96 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_ + +#include <stdint.h> +#include <sys/time.h> + +#include <string> + +#include "base/basictypes.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs Windows. +class SystemSnapshotWin final : public SystemSnapshot { + public: + SystemSnapshotWin(); + ~SystemSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! + //! It seems odd that a system snapshot implementation would need a + //! ProcessReaderWin, but some of the information reported about the + //! system depends on the process it's being reported for. For example, + //! the architecture returned by GetCPUArchitecture() should be the + //! architecture of the process, which may be different than the native + //! architecture of the system: an x86_64 system can run both x86_64 and + //! 32-bit x86 processes. + void Initialize(ProcessReaderWin* process_reader); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + std::string os_version_full_; + std::string os_version_build_; + ProcessReaderWin* process_reader_; // weak + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + bool os_server_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc new file mode 100644 index 0000000..d0caee7 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc
@@ -0,0 +1,161 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/system_snapshot_win.h" + +#include <sys/time.h> +#include <time.h> + +#include <string> + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace test { +namespace { + +class SystemSnapshotWinTest : public testing::Test { + public: + SystemSnapshotWinTest() + : Test(), + process_reader_(), + system_snapshot_() { + } + + const internal::SystemSnapshotWin& system_snapshot() const { + return system_snapshot_; + } + + // testing::Test: + void SetUp() override { + ASSERT_TRUE(process_reader_.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + system_snapshot_.Initialize(&process_reader_); + } + + private: + ProcessReaderWin process_reader_; + internal::SystemSnapshotWin system_snapshot_; + + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotWinTest); +}; + +TEST_F(SystemSnapshotWinTest, GetCPUArchitecture) { + CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture(); + +#if defined(ARCH_CPU_X86) + EXPECT_EQ(kCPUArchitectureX86, cpu_architecture); +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(kCPUArchitectureX86_64, cpu_architecture); +#endif +} + +TEST_F(SystemSnapshotWinTest, CPUCount) { + EXPECT_GE(system_snapshot().CPUCount(), 1); +} + +TEST_F(SystemSnapshotWinTest, CPUVendor) { + std::string cpu_vendor = system_snapshot().CPUVendor(); + + // There are a variety of other values, but we don't expect to run our tests + // on them. + EXPECT_TRUE(cpu_vendor == "GenuineIntel" || cpu_vendor == "AuthenticAMD"); +} + +TEST_F(SystemSnapshotWinTest, CPUX86SupportsDAZ) { + // Most SSE2+ machines support Denormals-Are-Zero. This may fail if run on + // older machines. + EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ()); +} + +TEST_F(SystemSnapshotWinTest, GetOperatingSystem) { + EXPECT_EQ(SystemSnapshot::kOperatingSystemWindows, + system_snapshot().GetOperatingSystem()); +} + +TEST_F(SystemSnapshotWinTest, OSVersion) { + int major; + int minor; + int bugfix; + std::string build; + system_snapshot().OSVersion(&major, &minor, &bugfix, &build); + + EXPECT_GE(major, 5); + if (major == 5) + EXPECT_GE(minor, 1); + if (major == 6) + EXPECT_TRUE(minor >= 0 && minor <= 3); +} + +TEST_F(SystemSnapshotWinTest, OSVersionFull) { + EXPECT_FALSE(system_snapshot().OSVersionFull().empty()); +} + +TEST_F(SystemSnapshotWinTest, MachineDescription) { + EXPECT_TRUE(system_snapshot().MachineDescription().empty()); +} + +TEST_F(SystemSnapshotWinTest, TimeZone) { + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + + system_snapshot().TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + + // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives + // seconds west of UTC. +#if _MSC_VER >= 1900 + long timezone = 0; + _get_timezone(&timezone); +#endif + EXPECT_EQ(-timezone, standard_offset_seconds); + + // In contemporary usage, most time zones have an integer hour offset from + // UTC, although several are at a half-hour offset, and two are at 15-minute + // offsets. Throughout history, other variations existed. See + // http://www.timeanddate.com/time/time-zones-interesting.html. + EXPECT_EQ(0, standard_offset_seconds % (15 * 60)) + << "standard_offset_seconds " << standard_offset_seconds; + + if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) { + EXPECT_EQ(standard_offset_seconds, daylight_offset_seconds); + EXPECT_EQ(standard_name, daylight_name); + } else { + EXPECT_EQ(0, daylight_offset_seconds % (15 * 60)) + << "daylight_offset_seconds " << daylight_offset_seconds; + + // In contemporary usage, dst_delta_seconds will almost always be one hour, + // except for Lord Howe Island, Australia, which uses a 30-minute + // delta. Throughout history, other variations existed. See + // http://www.timeanddate.com/time/dst/#brief. + int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; + if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { + FAIL() << "dst_delta_seconds " << dst_delta_seconds; + } + + EXPECT_NE(standard_name, daylight_name); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc new file mode 100644 index 0000000..9c8c9ea --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc
@@ -0,0 +1,120 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "snapshot/win/thread_snapshot_win.h" + +#include <vector> + +#include "base/logging.h" +#include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotWin::ThreadSnapshotWin() + : ThreadSnapshot(), + context_(), + stack_(), + teb_(), + thread_(), + initialized_() { +} + +ThreadSnapshotWin::~ThreadSnapshotWin() { +} + +bool ThreadSnapshotWin::Initialize( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& process_reader_thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + thread_ = process_reader_thread; + if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( + CheckedRange<WinVMAddress, WinVMSize>(thread_.stack_region_address, + thread_.stack_region_size))) { + stack_.Initialize(process_reader, + thread_.stack_region_address, + thread_.stack_region_size); + } else { + stack_.Initialize(process_reader, 0, 0); + } + + if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( + CheckedRange<WinVMAddress, WinVMSize>(thread_.teb_address, + thread_.teb_size))) { + teb_.Initialize(process_reader, thread_.teb_address, thread_.teb_size); + } else { + teb_.Initialize(process_reader, 0, 0); + } + +#if defined(ARCH_CPU_X86_64) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeX64Context(process_reader_thread.context.native, context_.x86_64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeX86Context(process_reader_thread.context.wow64, context_.x86); + } +#else + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeX86Context(process_reader_thread.context.native, context_.x86); +#endif // ARCH_CPU_X86_64 + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotWin::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotWin::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotWin::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.id; +} + +int ThreadSnapshotWin::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.suspend_count; +} + +int ThreadSnapshotWin::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.priority; +} + +uint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.teb_address; +} + +std::vector<const MemorySnapshot*> ThreadSnapshotWin::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): Ensure this region is readable, and make sure we don't + // discard the entire dump if it isn't. https://crashpad.chromium.org/bug/59 + return std::vector<const MemorySnapshot*>(1, &teb_); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h new file mode 100644 index 0000000..3f283a1 --- /dev/null +++ b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h
@@ -0,0 +1,85 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ + +#include <stdint.h> + +#include <vector> + +#include "base/basictypes.h" +#include "snapshot/cpu_context.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/win/memory_snapshot_win.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +//! \brief A ThreadSnapshot of a thread in a running (or crashed) process on a +//! Windows system. +class ThreadSnapshotWin final : public ThreadSnapshot { + public: + ThreadSnapshotWin(); + ~ThreadSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderWin for the process containing + //! the thread. + //! \param[in] process_reader_thread The thread within the ProcessReaderWin + //! for which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& process_reader_thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector<const MemorySnapshot*> ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_FAMILY) + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; +#endif + CPUContext context_; + MemorySnapshotWin stack_; + internal::MemorySnapshotWin teb_; + ProcessReaderWin::Thread thread_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_
diff --git a/third_party/crashpad/crashpad/test/errors.cc b/third_party/crashpad/crashpad/test/errors.cc new file mode 100644 index 0000000..148450625 --- /dev/null +++ b/third_party/crashpad/crashpad/test/errors.cc
@@ -0,0 +1,63 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/errors.h" + +#include <errno.h> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include "base/posix/safe_strerror.h" +#elif defined(OS_WIN) +#include <string.h> +#include <windows.h> +#endif + +namespace crashpad { +namespace test { + +std::string ErrnoMessage(int err, const std::string& base) { +#if defined(OS_POSIX) + std::string err_as_string = base::safe_strerror(errno); + const char* err_string = err_as_string.c_str(); +#elif defined(OS_WIN) + char err_string[256]; + strerror_s(err_string, errno); +#endif + return base::StringPrintf("%s%s%s (%d)", + base.c_str(), + base.empty() ? "" : ": ", + err_string, + err); +} + +std::string ErrnoMessage(const std::string& base) { + return ErrnoMessage(errno, base); +} + +#if defined(OS_WIN) +std::string ErrorMessage(const std::string& base) { + return base::StringPrintf( + "%s%s%s", + base.c_str(), + base.empty() ? "" : ": ", + logging::SystemErrorCodeToString(GetLastError()).c_str()); +} +#endif + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/errors.h b/third_party/crashpad/crashpad/test/errors.h new file mode 100644 index 0000000..17b0bea --- /dev/null +++ b/third_party/crashpad/crashpad/test/errors.h
@@ -0,0 +1,82 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_ERRORS_H_ +#define CRASHPAD_TEST_ERRORS_H_ + +#include <string> + +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +// These functions format messages in a similar way to the PLOG and PCHECK +// family of logging macros in base/logging.h. They exist to interoperate with +// gtest assertions, which don’t interoperate with logging but can be streamed +// to. +// +// Where non-test code could do: +// PCHECK(rv == 0) << "close"; +// gtest-based test code can do: +// EXPECT_EQ(0, rv) << ErrnoMessage("close"); + +//! \brief Formats an error message using an `errno` value. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! a textual and numeric description of the error. +//! +//! The message is formatted using `strerror()`. \a err may be `0` or outside of +//! the range of known error codes, and the message returned will contain the +//! string that `strerror()` uses in these cases. +//! +//! \param[in] err The error code, usable as an `errno` value. +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"Operation not permitted (1)"` if \a err has +//! the value `EPERM` on a system where this is defined to be `1`. If \a +//! base is not empty, it will be prepended to this string, separated by a +//! colon. +std::string ErrnoMessage(int err, const std::string& base = std::string()); + +//! \brief Formats an error message using `errno`. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! a textual and numeric description of the error. +//! +//! The message is formatted using `strerror()`. `errno` may be `0` or outside +//! of the range of known error codes, and the message returned will contain the +//! string that `strerror()` uses in these cases. +//! +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"Operation not permitted (1)"` if `errno` +//! has the value `EPERM` on a system where this is defined to be `1`. If +//! \a base is not empty, it will be prepended to this string, separated by +//! a colon. +std::string ErrnoMessage(const std::string& base = std::string()); + +#if defined(OS_WIN) || DOXYGEN +//! \brief Formats an error message using `GetLastError()`. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! a textual and numeric description of the error. The format is the same as +//! the `PLOG()` formatting in base. +std::string ErrorMessage(const std::string& base = std::string()); +#endif + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_ERRORS_H_
diff --git a/third_party/crashpad/crashpad/test/file.cc b/third_party/crashpad/crashpad/test/file.cc new file mode 100644 index 0000000..9c00743 --- /dev/null +++ b/third_party/crashpad/crashpad/test/file.cc
@@ -0,0 +1,66 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/file.h" + +#include <errno.h> +#include <sys/stat.h> + +#include "gtest/gtest.h" +#include "test/errors.h" + +namespace crashpad { +namespace test { + +bool FileExists(const base::FilePath& path) { +#if defined(OS_POSIX) + struct stat st; + int rv = lstat(path.value().c_str(), &st); + const char stat_function[] = "lstat"; +#elif defined(OS_WIN) + struct _stat st; + int rv = _wstat(path.value().c_str(), &st); + const char stat_function[] = "_wstat"; +#else +#error "Not implemented" +#endif + if (rv < 0) { + EXPECT_EQ(ENOENT, errno) << ErrnoMessage(stat_function) << " " + << path.value(); + return false; + } + return true; +} + +FileOffset FileSize(const base::FilePath& path) { +#if defined(OS_POSIX) + struct stat st; + int rv = lstat(path.value().c_str(), &st); + const char stat_function[] = "lstat"; +#elif defined(OS_WIN) + struct _stati64 st; + int rv = _wstati64(path.value().c_str(), &st); + const char stat_function[] = "_wstati64"; +#else +#error "Not implemented" +#endif + if (rv < 0) { + ADD_FAILURE() << ErrnoMessage(stat_function) << " " << path.value(); + return -1; + } + return st.st_size; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/file.h b/third_party/crashpad/crashpad/test/file.h new file mode 100644 index 0000000..a9987b4d --- /dev/null +++ b/third_party/crashpad/crashpad/test/file.h
@@ -0,0 +1,45 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_FILE_H_ +#define CRASHPAD_TEST_FILE_H_ + +#include "base/files/file_path.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { + +//! \brief Determines whether a file exists. +//! +//! \param[in] path The path to check for existence. +//! +//! \return `true` if \a path exists. `false` if it does not exist. If an error +//! other than “file not found” occurs when searching for \a path, returns +//! `false` with a gtest failure added. +bool FileExists(const base::FilePath& path); + +//! \brief Determines the size of a file. +//! +//! \param[in] path The path of the file to check. The file must exist. +//! +//! \return The size of the file at \a path. If the file does not exist, or an +//! error occurs when attempting to determine its size, returns `-1` with a +//! gtest failure added. +FileOffset FileSize(const base::FilePath& path); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_FILE_H_
diff --git a/third_party/crashpad/crashpad/test/gtest_death_check.h b/third_party/crashpad/crashpad/test/gtest_death_check.h new file mode 100644 index 0000000..8af50aa --- /dev/null +++ b/third_party/crashpad/crashpad/test/gtest_death_check.h
@@ -0,0 +1,55 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_GTEST_DEATH_CHECK_H_ +#define CRASHPAD_TEST_GTEST_DEATH_CHECK_H_ + +#include "base/logging.h" +#include "gtest/gtest.h" + +//! \file + +#if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \ + defined(OFFICIAL_BUILD) && \ + defined(NDEBUG)) || \ + DOXYGEN + +//! \brief Wraps the gtest `ASSERT_DEATH()` macro to make assertions about death +//! caused by `CHECK()` failures. +//! +//! In an in-Chromium build in the official configuration in Release mode, +//! `CHECK()` does not print its condition or streamed messages. In that case, +//! this macro uses an empty \a regex pattern when calling `ASSERT_DEATH()` to +//! avoid looking for any particular output on the standard error stream. In +//! other build configurations, the \a regex pattern is left intact. +#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH(statement, regex) + +//! \brief Wraps the gtest `EXPECT_DEATH()` macro to make assertions about death +//! caused by `CHECK()` failures. +//! +//! In an in-Chromium build in the official configuration in Release mode, +//! `CHECK()` does not print its condition or streamed messages. In that case, +//! this macro uses an empty \a regex pattern when calling `EXPECT_DEATH()` to +//! avoid looking for any particular output on the standard error stream. In +//! other build configurations, the \a regex pattern is left intact. +#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH(statement, regex) + +#else + +#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH(statement, "") +#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH(statement, "") + +#endif + +#endif // CRASHPAD_TEST_GTEST_DEATH_CHECK_H_
diff --git a/third_party/crashpad/crashpad/test/mac/dyld.h b/third_party/crashpad/crashpad/test/mac/dyld.h new file mode 100644 index 0000000..e436c5d1 --- /dev/null +++ b/third_party/crashpad/crashpad/test/mac/dyld.h
@@ -0,0 +1,29 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_DYLD_H_ +#define CRASHPAD_TEST_MAC_DYLD_H_ + +#include <mach-o/dyld_images.h> + +extern "C" { + +// Returns a pointer to this process’ dyld_all_image_infos structure. This is +// implemented as a non-public dyld API, declared in 10.9.2 +// dyld-239.4/include/mach-o/dyld_priv.h. +const struct dyld_all_image_infos* _dyld_get_all_image_infos(); + +} // extern "C" + +#endif // CRASHPAD_TEST_MAC_DYLD_H_
diff --git a/third_party/crashpad/crashpad/test/mac/mach_errors.cc b/third_party/crashpad/crashpad/test/mac/mach_errors.cc new file mode 100644 index 0000000..b68448f1 --- /dev/null +++ b/third_party/crashpad/crashpad/test/mac/mach_errors.cc
@@ -0,0 +1,77 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/mac/mach_errors.h" + +#include <servers/bootstrap.h> + +#include "base/strings/stringprintf.h" + +namespace { + +std::string FormatBase(const std::string& base) { + if (base.empty()) { + return std::string(); + } + + return base::StringPrintf("%s: ", base.c_str()); +} + +std::string FormatMachErrorNumber(mach_error_t mach_err) { + // For the os/kern subsystem, give the error number in decimal as in + // <mach/kern_return.h>. Otherwise, give it in hexadecimal to make it easier + // to visualize the various bits. See <mach/error.h>. + if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) { + return base::StringPrintf(" (%d)", mach_err); + } + return base::StringPrintf(" (0x%08x)", mach_err); +} + +} // namespace + +namespace crashpad { +namespace test { + +std::string MachErrorMessage(mach_error_t mach_err, const std::string& base) { + return base::StringPrintf("%s%s%s", + FormatBase(base).c_str(), + mach_error_string(mach_err), + FormatMachErrorNumber(mach_err).c_str()); +} + +std::string BootstrapErrorMessage(kern_return_t bootstrap_err, + const std::string& base) { + switch (bootstrap_err) { + case BOOTSTRAP_SUCCESS: + case BOOTSTRAP_NOT_PRIVILEGED: + case BOOTSTRAP_NAME_IN_USE: + case BOOTSTRAP_UNKNOWN_SERVICE: + case BOOTSTRAP_SERVICE_ACTIVE: + case BOOTSTRAP_BAD_COUNT: + case BOOTSTRAP_NO_MEMORY: + case BOOTSTRAP_NO_CHILDREN: + // Show known bootstrap errors in decimal because that's how they're + // defined in <servers/bootstrap.h>. + return base::StringPrintf("%s%s (%d)", + FormatBase(base).c_str(), + bootstrap_strerror(bootstrap_err), + bootstrap_err); + + default: + return MachErrorMessage(bootstrap_err, base); + } +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/mac/mach_errors.h b/third_party/crashpad/crashpad/test/mac/mach_errors.h new file mode 100644 index 0000000..2283ae77 --- /dev/null +++ b/third_party/crashpad/crashpad/test/mac/mach_errors.h
@@ -0,0 +1,70 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_MACH_ERRORS_H_ +#define CRASHPAD_TEST_MAC_MACH_ERRORS_H_ + +#include <mach/mach.h> + +#include <string> + +namespace crashpad { +namespace test { + +// These functions format messages in a similar way to the logging macros in +// base/mac/mach_logging.h. They exist to interoperate with gtest assertions, +// which don’t interoperate with logging but can be streamed to. +// +// Where non-test code could do: +// MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate"; +// gtest-based test code can do: +// EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); + +//! \brief Formats a Mach error message. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! a textual and numeric description of the error. +//! +//! \param[in] mach_err The Mach error code, which may be a `kern_return_t` or +//! related type. +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"(os/kern) invalid address (1)"` if \a +//! mach_err has the value `KERN_INVALID_ADDRESS` on a system where this is +//! defined to be 1. If \a base is not empty, it will be prepended to this +//! string, separated by a colon. +std::string MachErrorMessage(mach_error_t mach_err, + const std::string& base = std::string()); + +//! \brief Formats a bootstrap error message. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! a textual and numeric description of the error. +//! +//! \param[in] bootstrap_err The bootstrap error code. +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"Permission denied (1100)"` if \a +//! bootstrap_err has the value `BOOTSTRAP_NOT_PRIVILEGED` on a system where +//! this is defined to be 1100. If \a base is not empty, it will be +//! prepended to this string, separated by a colon. If \a bootstrap_err is +//! not a valid bootstrap error code, it will be interpreted as a Mach error +//! code in the manner of MachErrorMessage(). +std::string BootstrapErrorMessage(kern_return_t bootstrap_err, + const std::string& base = std::string()); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_MACH_ERRORS_H_
diff --git a/third_party/crashpad/crashpad/test/mac/mach_multiprocess.cc b/third_party/crashpad/crashpad/test/mac/mach_multiprocess.cc new file mode 100644 index 0000000..641eeba --- /dev/null +++ b/third_party/crashpad/crashpad/test/mac/mach_multiprocess.cc
@@ -0,0 +1,269 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/mac/mach_multiprocess.h" + +#include <AvailabilityMacros.h> +#include <bsm/libbsm.h> + +#include <string> + +#include "base/auto_reset.h" +#include "base/logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/memory/scoped_ptr.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/mac/mach_errors.h" +#include "util/file/file_io.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/random_string.h" +#include "util/misc/scoped_forbid_return.h" + +namespace { + +// The “hello” message contains a send right to the child process’ task port. +struct SendHelloMessage : public mach_msg_base_t { + mach_msg_port_descriptor_t port_descriptor; +}; + +struct ReceiveHelloMessage : public SendHelloMessage { + union { + mach_msg_trailer_t trailer; + mach_msg_audit_trailer_t audit_trailer; + }; +}; + +} // namespace + +namespace crashpad { +namespace test { + +namespace internal { + +struct MachMultiprocessInfo { + MachMultiprocessInfo() + : service_name(), + local_port(MACH_PORT_NULL), + remote_port(MACH_PORT_NULL), + child_task(TASK_NULL) { + } + + std::string service_name; + base::mac::ScopedMachReceiveRight local_port; + base::mac::ScopedMachSendRight remote_port; + base::mac::ScopedMachSendRight child_task; // valid only in parent +}; + +} // namespace internal + +MachMultiprocess::MachMultiprocess() : Multiprocess(), info_(nullptr) { +} + +void MachMultiprocess::Run() { + ASSERT_EQ(nullptr, info_); + scoped_ptr<internal::MachMultiprocessInfo> info( + new internal::MachMultiprocessInfo); + base::AutoReset<internal::MachMultiprocessInfo*> reset_info(&info_, + info.get()); + + return Multiprocess::Run(); +} + +MachMultiprocess::~MachMultiprocess() { +} + +void MachMultiprocess::PreFork() { + ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + + // Set up the parent port and register it with the bootstrap server before + // forking, so that it’s guaranteed to be there when the child attempts to + // look it up. + info_->service_name = "org.chromium.crashpad.test.mach_multiprocess."; + info_->service_name.append(RandomString()); + + info_->local_port = BootstrapCheckIn(info_->service_name); + ASSERT_TRUE(info_->local_port.is_valid()); +} + +mach_port_t MachMultiprocess::LocalPort() const { + EXPECT_TRUE(info_->local_port.is_valid()); + return info_->local_port.get(); +} + +mach_port_t MachMultiprocess::RemotePort() const { + EXPECT_TRUE(info_->remote_port.is_valid()); + return info_->remote_port.get(); +} + +task_t MachMultiprocess::ChildTask() const { + EXPECT_TRUE(info_->child_task.is_valid()); + return info_->child_task.get(); +} + +void MachMultiprocess::MultiprocessParent() { + ReceiveHelloMessage message = {}; + + kern_return_t kr = mach_msg(&message.header, + MACH_RCV_MSG | kMachMessageReceiveAuditTrailer, + 0, + sizeof(message), + info_->local_port.get(), + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); + + // Comb through the entire message, checking every field against its expected + // value. + EXPECT_EQ(MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) | + MACH_MSGH_BITS_COMPLEX, + message.header.msgh_bits); + ASSERT_EQ(sizeof(SendHelloMessage), message.header.msgh_size); + EXPECT_EQ(info_->local_port, message.header.msgh_local_port); + ASSERT_EQ(1u, message.body.msgh_descriptor_count); + EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND), + message.port_descriptor.disposition); + ASSERT_EQ(implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR), + message.port_descriptor.type); + ASSERT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0), + message.audit_trailer.msgh_trailer_type); + ASSERT_EQ(sizeof(message.audit_trailer), + message.audit_trailer.msgh_trailer_size); + EXPECT_EQ(0u, message.audit_trailer.msgh_seqno); + + // Check the audit trailer’s values for sanity. This is a little bit of + // overkill, but because the service was registered with the bootstrap server + // and other processes will be able to look it up and send messages to it, + // these checks disambiguate genuine failures later on in the test from those + // that would occur if an errant process sends a message to this service. +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 + uid_t audit_auid; + uid_t audit_euid; + gid_t audit_egid; + uid_t audit_ruid; + gid_t audit_rgid; + pid_t audit_pid; + au_asid_t audit_asid; + audit_token_to_au32(message.audit_trailer.msgh_audit, + &audit_auid, + &audit_euid, + &audit_egid, + &audit_ruid, + &audit_rgid, + &audit_pid, + &audit_asid, + nullptr); +#else + uid_t audit_auid = audit_token_to_auid(message.audit_trailer.msgh_audit); + uid_t audit_euid = audit_token_to_euid(message.audit_trailer.msgh_audit); + gid_t audit_egid = audit_token_to_egid(message.audit_trailer.msgh_audit); + uid_t audit_ruid = audit_token_to_ruid(message.audit_trailer.msgh_audit); + gid_t audit_rgid = audit_token_to_rgid(message.audit_trailer.msgh_audit); + pid_t audit_pid = audit_token_to_pid(message.audit_trailer.msgh_audit); + au_asid_t audit_asid = audit_token_to_asid(message.audit_trailer.msgh_audit); +#endif + EXPECT_EQ(geteuid(), audit_euid); + EXPECT_EQ(getegid(), audit_egid); + EXPECT_EQ(getuid(), audit_ruid); + EXPECT_EQ(getgid(), audit_rgid); + ASSERT_EQ(ChildPID(), audit_pid); + + ASSERT_EQ(ChildPID(), AuditPIDFromMachMessageTrailer(&message.trailer)); + + auditinfo_addr_t audit_info; + int rv = getaudit_addr(&audit_info, sizeof(audit_info)); + ASSERT_EQ(0, rv) << ErrnoMessage("getaudit_addr"); + EXPECT_EQ(audit_info.ai_auid, audit_auid); + EXPECT_EQ(audit_info.ai_asid, audit_asid); + + // Retrieve the remote port from the message header, and the child’s task port + // from the message body. + info_->remote_port.reset(message.header.msgh_remote_port); + info_->child_task.reset(message.port_descriptor.name); + + // Verify that the child’s task port is what it purports to be. + int mach_pid; + kr = pid_for_task(info_->child_task.get(), &mach_pid); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task"); + ASSERT_EQ(ChildPID(), mach_pid); + + MachMultiprocessParent(); + + info_->remote_port.reset(); + info_->local_port.reset(); +} + +void MachMultiprocess::MultiprocessChild() { + ScopedForbidReturn forbid_return;; + + // local_port is not valid in the forked child process. + ignore_result(info_->local_port.release()); + + info_->local_port.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_NE(kMachPortNull, info_->local_port); + + // The remote port can be obtained from the bootstrap server. + info_->remote_port = BootstrapLookUp(info_->service_name); + ASSERT_NE(kMachPortNull, info_->remote_port); + + // The “hello” message will provide the parent with its remote port, a send + // right to the child task’s local port receive right. It will also carry a + // send right to the child task’s task port. + SendHelloMessage message = {}; + message.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) | + MACH_MSGH_BITS_COMPLEX; + message.header.msgh_size = sizeof(message); + message.header.msgh_remote_port = info_->remote_port.get(); + message.header.msgh_local_port = info_->local_port.get(); + message.body.msgh_descriptor_count = 1; + message.port_descriptor.name = mach_task_self(); + message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND; + message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR; + + kern_return_t kr = mach_msg(&message.header, + MACH_SEND_MSG, + message.header.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); + + MachMultiprocessChild(); + + info_->remote_port.reset(); + info_->local_port.reset(); + + // Close the write pipe now, for cases where the parent is waiting on it to + // be closed as an indication that the child has finished. + CloseWritePipe(); + + // Wait for the parent process to close its end of the pipe. The child process + // needs to remain alive until then because the parent process will attempt to + // verify it using the task port it has access to via ChildTask(). + CheckedReadFileAtEOF(ReadPipeHandle()); + + if (testing::Test::HasFailure()) { + // Trigger the ScopedForbidReturn destructor. + return; + } + + forbid_return.Disarm(); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/mac/mach_multiprocess.h b/third_party/crashpad/crashpad/test/mac/mach_multiprocess.h new file mode 100644 index 0000000..18324d4 --- /dev/null +++ b/third_party/crashpad/crashpad/test/mac/mach_multiprocess.h
@@ -0,0 +1,120 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_ +#define CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_ + +#include <mach/mach.h> +#include <unistd.h> + +#include "base/basictypes.h" +#include "test/multiprocess.h" + +namespace crashpad { +namespace test { + +namespace internal { +struct MachMultiprocessInfo; +} // namespace internal + +//! \brief Manages a Mach-aware multiprocess test. +//! +//! This is similar to the base Multiprocess test, but adds Mach features. The +//! parent process has access to the child process’ task port. The parent and +//! child processes are able to communicate via Mach IPC: each process has a +//! receive right to its “local port” and a send right to a “remote port”, and +//! messages sent to the remote port in one process can be received on the local +//! port in the partner process. +//! +//! Subclasses are expected to implement the parent and child by overriding the +//! appropriate methods. +class MachMultiprocess : public Multiprocess { + public: + MachMultiprocess(); + + void Run(); + + protected: + ~MachMultiprocess(); + + // Multiprocess: + void PreFork() override; + + //! \brief Returns a receive right for the local port. + //! + //! This method may be called by either the parent or the child process. It + //! returns a receive right, with a corresponding send right held in the + //! opposing process. + mach_port_t LocalPort() const; + + //! \brief Returns a send right for the remote port. + //! + //! This method may be called by either the parent or the child process. It + //! returns a send right, with the corresponding receive right held in the + //! opposing process. + mach_port_t RemotePort() const; + + //! \brief Returns a send right for the child’s task port. + //! + //! This method may only be called by the parent process. + task_t ChildTask() const; + + private: + // Multiprocess: + + //! \brief Runs the parent side of the test. + //! + //! This method establishes the parent’s environment and calls + //! MachMultiprocessParent(). + //! + //! Subclasses must override MachMultiprocessParent() instead of this method. + void MultiprocessParent() final; + + //! \brief Runs the child side of the test. + //! + //! This method establishes the child’s environment and calls + //! MachMultiprocessChild(). If any failure (via fatal or nonfatal gtest + //! assertion) is detected, the child will exit with a failure status. + //! + //! Subclasses must override MachMultiprocessChild() instead of this method. + void MultiprocessChild() final; + + //! \brief The subclass-provided parent routine. + //! + //! Test failures should be reported via gtest: `EXPECT_*()`, `ASSERT_*()`, + //! `FAIL()`, etc. + //! + //! This method must not use a `wait()`-family system call to wait for the + //! child process to exit, as this is handled by the superclass. + //! + //! Subclasses must implement this method to define how the parent operates. + virtual void MachMultiprocessParent() = 0; + + //! \brief The subclass-provided child routine. + //! + //! Test failures should be reported via gtest: `EXPECT_*()`, `ASSERT_*()`, + //! `FAIL()`, etc. + //! + //! Subclasses must implement this method to define how the child operates. + virtual void MachMultiprocessChild() = 0; + + internal::MachMultiprocessInfo* info_; + + DISALLOW_COPY_AND_ASSIGN(MachMultiprocess); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_
diff --git a/third_party/crashpad/crashpad/test/mac/mach_multiprocess_test.cc b/third_party/crashpad/crashpad/test/mac/mach_multiprocess_test.cc new file mode 100644 index 0000000..858821f --- /dev/null +++ b/third_party/crashpad/crashpad/test/mac/mach_multiprocess_test.cc
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/mac/mach_multiprocess.h" + +#include <unistd.h> + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMachMultiprocess final : public MachMultiprocess { + public: + TestMachMultiprocess() : MachMultiprocess() {} + + ~TestMachMultiprocess() {} + + private: + // MachMultiprocess will have already exercised the Mach ports for IPC and the + // child task port. + void MachMultiprocessParent() override {} + + void MachMultiprocessChild() override {} + + DISALLOW_COPY_AND_ASSIGN(TestMachMultiprocess); +}; + +TEST(MachMultiprocess, MachMultiprocess) { + TestMachMultiprocess mach_multiprocess; + mach_multiprocess.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/multiprocess.h b/third_party/crashpad/crashpad/test/multiprocess.h new file mode 100644 index 0000000..bbfbca31 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess.h
@@ -0,0 +1,206 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MULTIPROCESS_H_ +#define CRASHPAD_TEST_MULTIPROCESS_H_ + +#include <sys/types.h> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { + +namespace internal { +struct MultiprocessInfo; +}; + +//! \brief Manages a multiprocess test. +//! +//! These tests are `fork()`-based. The parent and child processes are able to +//! communicate via a pair of POSIX pipes. +//! +//! Subclasses are expected to implement the parent and child by overriding the +//! appropriate methods. +//! +//! On Windows, this class is only an internal implementation detail of +//! MultiprocessExec and all tests must use that class. +class Multiprocess { + public: + //! \brief The termination type for a child process. + enum TerminationReason : bool { + //! \brief The child terminated normally. + //! + //! A normal return happens when a test returns from RunChild(), or for + //! tests that `exec()`, returns from `main()`. This also happens for tests + //! that call `exit()` or `_exit()`. + kTerminationNormal = false, + + //! \brief The child terminated by signal. + //! + //! Signal termination happens as a result of a crash, a call to `abort()`, + //! assertion failure (including gtest assertions), etc. + kTerminationSignal, + }; + + Multiprocess(); + + //! \brief Runs the test. + //! + //! This method establishes the proper testing environment by calling + //! PreFork(), then calls `fork()`. In the parent process, it calls + //! RunParent(), and in the child process, it calls RunChild(). + //! + //! This method uses gtest assertions to validate the testing environment. If + //! the testing environment cannot be set up properly, it is possible that + //! MultiprocessParent() or MultiprocessChild() will not be called. In the + //! parent process, this method also waits for the child process to exit after + //! MultiprocessParent() returns, and verifies that it exited in accordance + //! with the expectations set by SetExpectedChildTermination(). + void Run(); + + //! \brief Sets the expected termination reason and code. + //! + //! The default expected termination reasaon is + //! TerminationReason::kTerminationNormal, and the default expected + //! termination code is `EXIT_SUCCESS` (`0`). + //! + //! \param[in] reason Whether to expect the child to terminate normally or + //! as a result of a signal. + //! \param[in] code If \a reason is TerminationReason::kTerminationNormal, + //! this is the expected exit status of the child. If \a reason is + //! TerminationReason::kTerminationSignal, this is the signal that is + //! expected to kill the child. + void SetExpectedChildTermination(TerminationReason reason, int code); + + protected: + ~Multiprocess(); + + //! \brief Establishes the proper testing environment prior to forking. + //! + //! Subclasses that solely implement a test should not need to override this + //! method. Subclasses that do not implement tests but instead implement + //! additional testing features on top of this class may override this method + //! provided that they call the superclass’ implementation first as follows: + //! + //! \code + //! void PreFork() override { + //! ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + //! + //! // Place subclass-specific pre-fork code here. + //! } + //! \endcode + //! + //! Subclass implementations may signal failure by raising their own fatal + //! gtest assertions. + virtual void PreFork(); + +#if !defined(OS_WIN) + //! \brief Returns the child process’ process ID. + //! + //! This method may only be called by the parent process. + pid_t ChildPID() const; +#endif // !OS_WIN + + //! \brief Returns the read pipe’s file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to the write pipe in the partner process will appear + //! on this file handle in this process. + //! + //! It is an error to call this after CloseReadPipe() has been called. + //! + //! \return The read pipe’s file handle. + FileHandle ReadPipeHandle() const; + + //! \brief Returns the write pipe’s file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to this file handle in this process will appear on + //! the read pipe in the partner process. + //! + //! It is an error to call this after CloseWritePipe() has been called. + //! + //! \return The write pipe’s file handle. + FileHandle WritePipeHandle() const; + + //! \brief Closes the read pipe. + //! + //! This method may be called by either the parent or the child process. An + //! attempt to write to the write pipe in the partner process will fail with + //! `EPIPE` or `SIGPIPE`. ReadPipeHandle() must not be called after this. + void CloseReadPipe(); + + //! \brief Closes the write pipe. + //! + //! This method may be called by either the parent or the child process. An + //! attempt to read from the read pipe in the partner process will indicate + //! end-of-file. WritePipeHandle() must not be called after this. + void CloseWritePipe(); + + void set_info(internal::MultiprocessInfo* info) { info_ = info; } + internal::MultiprocessInfo* info() { return info_; } + + private: + //! \brief Runs the parent side of the test. + //! + //! This method establishes the parent’s environment and calls + //! MultiprocessParent(). + void RunParent(); + + //! \brief Runs the child side of the test. + //! + //! This method establishes the child’s environment, calls + //! MultiprocessChild(), and exits cleanly by calling `_exit(0)`. However, if + //! any failure (via fatal or nonfatal gtest assertion) is detected, the child + //! will exit with a failure status. + void RunChild(); + + //! \brief The subclass-provided parent routine. + //! + //! Test failures should be reported via gtest: `EXPECT_*()`, `ASSERT_*()`, + //! `FAIL()`, etc. + //! + //! This method must not use a `wait()`-family system call to wait for the + //! child process to exit, as this is handled by this class. + //! + //! Subclasses must implement this method to define how the parent operates. + virtual void MultiprocessParent() = 0; + + //! \brief The subclass-provided child routine. + //! + //! Test failures should be reported via gtest: `EXPECT_*()`, `ASSERT_*()`, + //! `FAIL()`, etc. + //! + //! Subclasses must implement this method to define how the child operates. + //! Subclasses may exit with a failure status by using `LOG(FATAL)`, + //! `abort()`, or similar. They may exit cleanly by returning from this method + //! or by calling `_exit(0)`. Under no circumstances may `exit()` be called + //! by the child without having the child process `exec()`. Use + //! MultiprocessExec if the child should call `exec()`. + virtual void MultiprocessChild() = 0; + + internal::MultiprocessInfo* info_; + int code_; + TerminationReason reason_; + + DISALLOW_COPY_AND_ASSIGN(Multiprocess); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MULTIPROCESS_H_
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec.h b/third_party/crashpad/crashpad/test/multiprocess_exec.h new file mode 100644 index 0000000..2d8f338 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_exec.h
@@ -0,0 +1,78 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MULTIPROCESS_EXEC_H_ +#define CRASHPAD_TEST_MULTIPROCESS_EXEC_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "test/multiprocess.h" + +namespace crashpad { +namespace test { + +//! \brief Manages an `exec()`-based multiprocess test. +//! +//! These tests are based on `fork()` and `exec()`. The parent process is able +//! to communicate with the child in the same manner as a base-class +//! Multiprocess parent. The read and write pipes appear in the child process on +//! stdin and stdout, respectively. +//! +//! Subclasses are expected to implement the parent in the same was as a +//! base-class Multiprocess parent. The child must be implemented in an +//! executable to be set by SetChildCommand(). +class MultiprocessExec : public Multiprocess { + public: + MultiprocessExec(); + + //! \brief Sets the command to `exec()` in the child. + //! + //! This method must be called before the test can be Run(). + //! + //! \param[in] command The executable’s pathname. + //! \param[in] arguments The command-line arguments to pass to the child + //! process in its `argv[]` vector. This vector must begin at `argv[1]`, + //! as \a command is implicitly used as `argv[0]`. This argument may be + //! `nullptr` if no command-line arguments are to be passed. + void SetChildCommand(const std::string& command, + const std::vector<std::string>* arguments); + + protected: + ~MultiprocessExec(); + + // Multiprocess: + void PreFork() override; + + private: + // Multiprocess: + void MultiprocessChild() override; + + std::string command_; + std::vector<std::string> arguments_; +#if defined(OS_POSIX) + std::vector<const char*> argv_; +#elif defined(OS_WIN) + std::wstring command_line_; +#endif // OS_POSIX + + DISALLOW_COPY_AND_ASSIGN(MultiprocessExec); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MULTIPROCESS_EXEC_H_
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc b/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc new file mode 100644 index 0000000..caf2b476 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_exec_posix.cc
@@ -0,0 +1,141 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/multiprocess_exec.h" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "base/posix/eintr_wrapper.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/misc/scoped_forbid_return.h" +#include "util/posix/close_multiple.h" + +namespace crashpad { +namespace test { + +MultiprocessExec::MultiprocessExec() + : Multiprocess(), + command_(), + arguments_(), + argv_() { +} + +void MultiprocessExec::SetChildCommand( + const std::string& command, const std::vector<std::string>* arguments) { + command_ = command; + if (arguments) { + arguments_ = *arguments; + } else { + arguments_.clear(); + } +} + +MultiprocessExec::~MultiprocessExec() { +} + +void MultiprocessExec::PreFork() { + ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + + ASSERT_FALSE(command_.empty()); + + // Build up the argv vector. This is done in PreFork() instead of + // MultiprocessChild() because although the result is only needed in the child + // process, building it is a hazardous operation in that process. + ASSERT_TRUE(argv_.empty()); + + argv_.push_back(command_.c_str()); + for (const std::string& argument : arguments_) { + argv_.push_back(argument.c_str()); + } + argv_.push_back(nullptr); +} + +void MultiprocessExec::MultiprocessChild() { + // Make sure that stdin, stdout, and stderr are FDs 0, 1, and 2, respectively. + // All FDs above this will be closed. + static_assert(STDIN_FILENO == 0, "stdin must be fd 0"); + static_assert(STDOUT_FILENO == 1, "stdout must be fd 1"); + static_assert(STDERR_FILENO == 2, "stderr must be fd 2"); + + // Move the read pipe to stdin. + FileHandle read_handle = ReadPipeHandle(); + ASSERT_NE(read_handle, STDIN_FILENO); + ASSERT_NE(read_handle, STDOUT_FILENO); + ASSERT_EQ(STDIN_FILENO, fileno(stdin)); + + int rv = fpurge(stdin); + ASSERT_EQ(0, rv) << ErrnoMessage("fpurge"); + + rv = HANDLE_EINTR(dup2(read_handle, STDIN_FILENO)); + ASSERT_EQ(STDIN_FILENO, rv) << ErrnoMessage("dup2"); + + // Move the write pipe to stdout. + FileHandle write_handle = WritePipeHandle(); + ASSERT_NE(write_handle, STDIN_FILENO); + ASSERT_NE(write_handle, STDOUT_FILENO); + ASSERT_EQ(STDOUT_FILENO, fileno(stdout)); + + // Make a copy of the original stdout file descriptor so that in case there’s + // an execv() failure, the original stdout can be restored so that gtest + // messages directed to stdout go to the right place. Mark it as + // close-on-exec, so that the child won’t see it after a successful exec(), + // but it will still be available in this process after an unsuccessful + // exec(). + int dup_orig_stdout_fd = dup(STDOUT_FILENO); + ASSERT_GE(dup_orig_stdout_fd, 0) << ErrnoMessage("dup"); + + rv = fcntl(dup_orig_stdout_fd, F_SETFD, FD_CLOEXEC); + ASSERT_NE(rv, -1) << ErrnoMessage("fcntl"); + + rv = HANDLE_EINTR(fflush(stdout)); + ASSERT_EQ(0, rv) << ErrnoMessage("fflush"); + + rv = HANDLE_EINTR(dup2(write_handle, STDOUT_FILENO)); + ASSERT_EQ(STDOUT_FILENO, rv) << ErrnoMessage("dup2"); + + CloseMultipleNowOrOnExec(STDERR_FILENO + 1, dup_orig_stdout_fd); + + // Start the new program, replacing this one. execv() has a weird declaration + // where its argv argument is declared as char* const*. In reality, the + // implementation behaves as if the argument were const char* const*, and this + // behavior is required by the standard. See + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html + // (search for “constant”). + execv(argv_[0], const_cast<char* const*>(&argv_[0])); + + // This should not normally be reached. Getting here means that execv() + // failed. + + // Be sure not to return until FAIL() is reached. + ScopedForbidReturn forbid_return; + + // Put the original stdout back. Close the copy of the write pipe FD that’s + // currently on stdout first, so that in case the dup2() that restores the + // original stdout fails, stdout isn’t left attached to the pipe when the + // FAIL() statement executes. + HANDLE_EINTR(fflush(stdout)); + IGNORE_EINTR(close(STDOUT_FILENO)); + HANDLE_EINTR(dup2(dup_orig_stdout_fd, STDOUT_FILENO)); + IGNORE_EINTR(close(dup_orig_stdout_fd)); + + forbid_return.Disarm(); + FAIL() << ErrnoMessage("execv") << ": " << argv_[0]; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec_test.cc b/third_party/crashpad/crashpad/test/multiprocess_exec_test.cc new file mode 100644 index 0000000..dc916404a --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_exec_test.cc
@@ -0,0 +1,68 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/multiprocess_exec.h" + +#include "base/basictypes.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/paths.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMultiprocessExec final : public MultiprocessExec { + public: + TestMultiprocessExec() : MultiprocessExec() {} + + ~TestMultiprocessExec() {} + + private: + void MultiprocessParent() override { + // Use Logging*File() instead of Checked*File() so that the test can fail + // gracefully with a gtest assertion if the child does not execute properly. + + char c = 'z'; + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, 1)); + + ASSERT_TRUE(LoggingReadFile(ReadPipeHandle(), &c, 1)); + EXPECT_EQ('Z', c); + } + + DISALLOW_COPY_AND_ASSIGN(TestMultiprocessExec); +}; + +TEST(MultiprocessExec, MultiprocessExec) { + TestMultiprocessExec multiprocess_exec; + base::FilePath test_executable = Paths::Executable(); +#if defined(OS_POSIX) + std::string child_test_executable = test_executable.value(); +#elif defined(OS_WIN) + std::string child_test_executable = + base::UTF16ToUTF8(test_executable.RemoveFinalExtension().value()); +#endif // OS_POSIX + child_test_executable += "_multiprocess_exec_test_child"; +#if defined(OS_WIN) + child_test_executable += ".exe"; +#endif + multiprocess_exec.SetChildCommand(child_test_executable, nullptr); + multiprocess_exec.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc b/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc new file mode 100644 index 0000000..c796e4a3 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_exec_test_child.cc
@@ -0,0 +1,84 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <algorithm> + +#if defined(__APPLE__) || defined(__linux__) +#define OS_POSIX 1 +#elif defined(_WIN32) +#define OS_WIN 1 +#endif + +#if defined(OS_POSIX) +#include <unistd.h> +#elif defined(OS_WIN) +#include <windows.h> +#endif + +int main(int argc, char* argv[]) { +#if defined(OS_POSIX) + // Make sure that there’s nothing open at any FD higher than 3. All FDs other + // than stdin, stdout, and stderr should have been closed prior to or at + // exec(). + int max_fd = std::max(static_cast<int>(sysconf(_SC_OPEN_MAX)), OPEN_MAX); + max_fd = std::max(max_fd, getdtablesize()); + for (int fd = STDERR_FILENO + 1; fd < max_fd; ++fd) { + if (close(fd) == 0 || errno != EBADF) { + abort(); + } + } + + // Read a byte from stdin, expecting it to be a specific value. + char c; + ssize_t rv = read(STDIN_FILENO, &c, 1); + if (rv != 1 || c != 'z') { + abort(); + } + + // Write a byte to stdout. + c = 'Z'; + rv = write(STDOUT_FILENO, &c, 1); + if (rv != 1) { + abort(); + } +#elif defined(OS_WIN) + // TODO(scottmg): Verify that only the handles we expect to be open, are. + + // Read a byte from stdin, expecting it to be a specific value. + char c; + DWORD bytes_read; + HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + if (!ReadFile(stdin_handle, &c, 1, &bytes_read, nullptr) || + bytes_read != 1 || c != 'z') { + abort(); + } + + // Write a byte to stdout. + c = 'Z'; + DWORD bytes_written; + if (!WriteFile( + GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &bytes_written, nullptr) || + bytes_written != 1) { + abort(); + } +#endif // OS_POSIX + + return 0; +}
diff --git a/third_party/crashpad/crashpad/test/multiprocess_exec_win.cc b/third_party/crashpad/crashpad/test/multiprocess_exec_win.cc new file mode 100644 index 0000000..2a58f84 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_exec_win.cc
@@ -0,0 +1,168 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/multiprocess_exec.h" + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "util/win/command_line.h" + +namespace crashpad { +namespace test { + +namespace internal { + +struct MultiprocessInfo { + MultiprocessInfo() {} + ScopedFileHANDLE pipe_c2p_read; + ScopedFileHANDLE pipe_c2p_write; + ScopedFileHANDLE pipe_p2c_read; + ScopedFileHANDLE pipe_p2c_write; + PROCESS_INFORMATION process_info; +}; + +} // namespace internal + +Multiprocess::Multiprocess() + : info_(nullptr), + code_(EXIT_SUCCESS), + reason_(kTerminationNormal) { +} + +void Multiprocess::Run() { + // Set up and spawn the child process. + ASSERT_NO_FATAL_FAILURE(PreFork()); + RunChild(); + + // And then run the parent actions in this process. + RunParent(); + + // Reap the child. + WaitForSingleObject(info_->process_info.hProcess, INFINITE); + CloseHandle(info_->process_info.hThread); + CloseHandle(info_->process_info.hProcess); +} + +Multiprocess::~Multiprocess() { + delete info_; +} + +void Multiprocess::PreFork() { + NOTREACHED(); +} + +FileHandle Multiprocess::ReadPipeHandle() const { + // This is the parent case, it's stdin in the child. + return info_->pipe_c2p_read.get(); +} + +FileHandle Multiprocess::WritePipeHandle() const { + // This is the parent case, it's stdout in the child. + return info_->pipe_p2c_write.get(); +} + +void Multiprocess::CloseReadPipe() { + info_->pipe_c2p_read.reset(); +} + +void Multiprocess::CloseWritePipe() { + info_->pipe_p2c_write.reset(); +} + +void Multiprocess::RunParent() { + MultiprocessParent(); + + info_->pipe_c2p_read.reset(); + info_->pipe_p2c_write.reset(); +} + +void Multiprocess::RunChild() { + MultiprocessChild(); + + info_->pipe_c2p_write.reset(); + info_->pipe_p2c_read.reset(); +} + +MultiprocessExec::MultiprocessExec() + : Multiprocess(), command_(), arguments_(), command_line_() { +} + +void MultiprocessExec::SetChildCommand( + const std::string& command, + const std::vector<std::string>* arguments) { + command_ = command; + if (arguments) { + arguments_ = *arguments; + } else { + arguments_.clear(); + } +} + +MultiprocessExec::~MultiprocessExec() { +} + +void MultiprocessExec::PreFork() { + ASSERT_FALSE(command_.empty()); + + command_line_.clear(); + AppendCommandLineArgument(base::UTF8ToUTF16(command_), &command_line_); + for (size_t i = 0; i < arguments_.size(); ++i) { + AppendCommandLineArgument(base::UTF8ToUTF16(arguments_[i]), &command_line_); + } + + // Make pipes for child-to-parent and parent-to-child communication. Mark them + // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to + // ensure that the parent sides are not inherited. + ASSERT_EQ(nullptr, info()); + set_info(new internal::MultiprocessInfo()); + + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + + HANDLE c2p_read, c2p_write; + PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0)); + PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0)); + info()->pipe_c2p_read.reset(c2p_read); + info()->pipe_c2p_write.reset(c2p_write); + + HANDLE p2c_read, p2c_write; + PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0)); + PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0)); + info()->pipe_p2c_read.reset(p2c_read); + info()->pipe_p2c_write.reset(p2c_write); +} + +void MultiprocessExec::MultiprocessChild() { + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.hStdInput = info()->pipe_p2c_read.get(); + startup_info.hStdOutput = info()->pipe_c2p_write.get(); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.dwFlags = STARTF_USESTDHANDLES; + PCHECK(CreateProcess(base::UTF8ToUTF16(command_).c_str(), + &command_line_[0], // This cannot be constant, per MSDN. + nullptr, + nullptr, + TRUE, + 0, + nullptr, + nullptr, + &startup_info, + &info()->process_info)); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/multiprocess_posix.cc b/third_party/crashpad/crashpad/test/multiprocess_posix.cc new file mode 100644 index 0000000..109ac31 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_posix.cc
@@ -0,0 +1,220 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/multiprocess.h" + +#include <signal.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> + +#include "base/auto_reset.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/misc/scoped_forbid_return.h" + +namespace crashpad { +namespace test { + +namespace internal { + +struct MultiprocessInfo { + MultiprocessInfo() + : pipe_c2p_read(-1), + pipe_c2p_write(-1), + pipe_p2c_read(-1), + pipe_p2c_write(-1), + child_pid(0) {} + + base::ScopedFD pipe_c2p_read; // child to parent + base::ScopedFD pipe_c2p_write; // child to parent + base::ScopedFD pipe_p2c_read; // parent to child + base::ScopedFD pipe_p2c_write; // parent to child + pid_t child_pid; // valid only in parent +}; + +} // namespace internal + +Multiprocess::Multiprocess() + : info_(nullptr), + code_(EXIT_SUCCESS), + reason_(kTerminationNormal) { +} + +void Multiprocess::Run() { + ASSERT_EQ(nullptr, info_); + scoped_ptr<internal::MultiprocessInfo> info(new internal::MultiprocessInfo); + base::AutoReset<internal::MultiprocessInfo*> reset_info(&info_, info.get()); + + ASSERT_NO_FATAL_FAILURE(PreFork()); + + pid_t pid = fork(); + ASSERT_GE(pid, 0) << ErrnoMessage("fork"); + + if (pid > 0) { + info_->child_pid = pid; + + RunParent(); + + // Waiting for the child happens here instead of in RunParent() because even + // if RunParent() returns early due to a gtest fatal assertion failure, the + // child should still be reaped. + + // This will make the parent hang up on the child as much as would be + // visible from the child’s perspective. The child’s side of the pipe will + // be broken, the child’s remote port will become a dead name, and an + // attempt by the child to look up the service will fail. If this weren’t + // done, the child might hang while waiting for a parent that has already + // triggered a fatal assertion failure to do something. + info.reset(); + info_ = nullptr; + + int status; + pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); + ASSERT_EQ(pid, wait_pid) << ErrnoMessage("waitpid"); + + TerminationReason reason; + int code; + std::string message; + if (WIFEXITED(status)) { + reason = kTerminationNormal; + code = WEXITSTATUS(status); + message = base::StringPrintf("Child exited with code %d, expected", code); + } else if (WIFSIGNALED(status)) { + reason = kTerminationSignal; + code = WTERMSIG(status); + message = + base::StringPrintf("Child terminated by signal %d (%s)%s, expected", + code, + strsignal(code), + WCOREDUMP(status) ? " (core dumped)" : ""); + } else { + FAIL() << "Unknown termination reason"; + } + + if (reason_ == kTerminationNormal) { + message += base::StringPrintf(" exit with code %d", code_); + } else if (reason == kTerminationSignal) { + message += base::StringPrintf(" termination by signal %d", code_); + } + + if (reason != reason_ || code != code_) { + ADD_FAILURE() << message; + } + } else { + RunChild(); + } +} + +void Multiprocess::SetExpectedChildTermination(TerminationReason reason, + int code) { + reason_ = reason; + code_ = code; +} + +Multiprocess::~Multiprocess() { +} + +void Multiprocess::PreFork() { + int pipe_fds_c2p[2]; + int rv = pipe(pipe_fds_c2p); + ASSERT_EQ(0, rv) << ErrnoMessage("pipe"); + + info_->pipe_c2p_read.reset(pipe_fds_c2p[0]); + info_->pipe_c2p_write.reset(pipe_fds_c2p[1]); + + int pipe_fds_p2c[2]; + rv = pipe(pipe_fds_p2c); + ASSERT_EQ(0, rv) << ErrnoMessage("pipe"); + + info_->pipe_p2c_read.reset(pipe_fds_p2c[0]); + info_->pipe_p2c_write.reset(pipe_fds_p2c[1]); +} + +pid_t Multiprocess::ChildPID() const { + EXPECT_NE(0, info_->child_pid); + return info_->child_pid; +} + +FileHandle Multiprocess::ReadPipeHandle() const { + int fd = info_->child_pid ? info_->pipe_c2p_read.get() + : info_->pipe_p2c_read.get(); + CHECK_NE(fd, -1); + return fd; +} + +FileHandle Multiprocess::WritePipeHandle() const { + int fd = info_->child_pid ? info_->pipe_p2c_write.get() + : info_->pipe_c2p_write.get(); + CHECK_NE(fd, -1); + return fd; +} + +void Multiprocess::CloseReadPipe() { + if (info_->child_pid) { + info_->pipe_c2p_read.reset(); + } else { + info_->pipe_p2c_read.reset(); + } +} + +void Multiprocess::CloseWritePipe() { + if (info_->child_pid) { + info_->pipe_p2c_write.reset(); + } else { + info_->pipe_c2p_write.reset(); + } +} + +void Multiprocess::RunParent() { + // The parent uses the read end of c2p and the write end of p2c. + info_->pipe_c2p_write.reset(); + info_->pipe_p2c_read.reset(); + + MultiprocessParent(); + + info_->pipe_c2p_read.reset(); + info_->pipe_p2c_write.reset(); +} + +void Multiprocess::RunChild() { + ScopedForbidReturn forbid_return; + + // The child uses the write end of c2p and the read end of p2c. + info_->pipe_c2p_read.reset(); + info_->pipe_p2c_write.reset(); + + MultiprocessChild(); + + info_->pipe_c2p_write.reset(); + info_->pipe_p2c_read.reset(); + + if (testing::Test::HasFailure()) { + // Trigger the ScopedForbidReturn destructor. + return; + } + + // In a forked child, exit() is unsafe. Use _exit() instead. + _exit(EXIT_SUCCESS); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc b/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc new file mode 100644 index 0000000..87a4238 --- /dev/null +++ b/third_party/crashpad/crashpad/test/multiprocess_posix_test.cc
@@ -0,0 +1,290 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/multiprocess.h" + +#include <stdlib.h> +#include <sys/signal.h> +#include <unistd.h> + +#include "base/basictypes.h" +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMultiprocess final : public Multiprocess { + public: + TestMultiprocess() : Multiprocess() {} + + ~TestMultiprocess() {} + + private: + // Multiprocess: + + void MultiprocessParent() override { + FileHandle read_handle = ReadPipeHandle(); + char c; + CheckedReadFile(read_handle, &c, 1); + EXPECT_EQ('M', c); + + pid_t pid; + CheckedReadFile(read_handle, &pid, sizeof(pid)); + EXPECT_EQ(pid, ChildPID()); + + c = 'm'; + CheckedWriteFile(WritePipeHandle(), &c, 1); + + // The child will close its end of the pipe and exit. Make sure that the + // parent sees EOF. + CheckedReadFileAtEOF(read_handle); + } + + void MultiprocessChild() override { + FileHandle write_handle = WritePipeHandle(); + + char c = 'M'; + CheckedWriteFile(write_handle, &c, 1); + + pid_t pid = getpid(); + CheckedWriteFile(write_handle, &pid, sizeof(pid)); + + CheckedReadFile(ReadPipeHandle(), &c, 1); + EXPECT_EQ('m', c); + } + + DISALLOW_COPY_AND_ASSIGN(TestMultiprocess); +}; + +TEST(Multiprocess, Multiprocess) { + TestMultiprocess multiprocess; + multiprocess.Run(); +} + +class TestMultiprocessUnclean final : public Multiprocess { + public: + enum TerminationType { + kExitSuccess = 0, + kExitFailure, + kExit2, + kAbort, + }; + + explicit TestMultiprocessUnclean(TerminationType type) + : Multiprocess(), + type_(type) { + if (type_ == kAbort) { + SetExpectedChildTermination(kTerminationSignal, SIGABRT); + } else { + SetExpectedChildTermination(kTerminationNormal, ExitCode()); + } + } + + ~TestMultiprocessUnclean() {} + + private: + int ExitCode() const { + return type_; + } + + // Multiprocess: + + void MultiprocessParent() override { + } + + void MultiprocessChild() override { + if (type_ == kAbort) { + abort(); + } else { + _exit(ExitCode()); + } + } + + TerminationType type_; + + DISALLOW_COPY_AND_ASSIGN(TestMultiprocessUnclean); +}; + +TEST(Multiprocess, SuccessfulExit) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitSuccess); + multiprocess.Run(); +} + +TEST(Multiprocess, UnsuccessfulExit) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitFailure); + multiprocess.Run(); +} + +TEST(Multiprocess, Exit2) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExit2); + multiprocess.Run(); +} + +TEST(Multiprocess, AbortSignal) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kAbort); + multiprocess.Run(); +} + +class TestMultiprocessClosePipe final : public Multiprocess { + public: + enum WhoCloses { + kParentCloses = 0, + kChildCloses, + }; + enum WhatCloses { + kReadCloses = 0, + kWriteCloses, + kReadAndWriteClose, + }; + + TestMultiprocessClosePipe(WhoCloses who_closes, WhatCloses what_closes) + : Multiprocess(), + who_closes_(who_closes), + what_closes_(what_closes) { + } + + ~TestMultiprocessClosePipe() {} + + private: + void VerifyInitial() { + ASSERT_NE(-1, ReadPipeHandle()); + ASSERT_NE(-1, WritePipeHandle()); + } + + // Verifies that the partner process did what it was supposed to do. This must + // only be called when who_closes_ names the partner process, not this + // process. + // + // If the partner was supposed to close its write pipe, the read pipe will be + // checked to ensure that it shows end-of-file. + // + // If the partner was supposed to close its read pipe, the write pipe will be + // checked to ensure that a checked write causes death. This can only be done + // if the partner also provides some type of signal when it has closed its + // read pipe, which is done in the form of it closing its write pipe, causing + // the read pipe in this process to show end-of-file. + void VerifyPartner() { + if (what_closes_ == kWriteCloses) { + CheckedReadFileAtEOF(ReadPipeHandle()); + } else if (what_closes_ == kReadAndWriteClose) { + CheckedReadFileAtEOF(ReadPipeHandle()); + char c = '\0'; + + // This will raise SIGPIPE. If fatal (the normal case), that will cause + // process termination. If SIGPIPE is being handled somewhere, the write + // will still fail and set errno to EPIPE, and CheckedWriteFile() will + // abort execution. Regardless of how SIGPIPE is handled, the process will + // be terminated. Because the actual termination mechanism is not known, + // no regex can be specified. + EXPECT_DEATH_CHECK(CheckedWriteFile(WritePipeHandle(), &c, 1), ""); + } + } + + void Close() { + switch (what_closes_) { + case kReadCloses: + CloseReadPipe(); + EXPECT_NE(-1, WritePipeHandle()); + EXPECT_DEATH_CHECK(ReadPipeHandle(), "fd"); + break; + case kWriteCloses: + CloseWritePipe(); + EXPECT_NE(-1, ReadPipeHandle()); + EXPECT_DEATH_CHECK(WritePipeHandle(), "fd"); + break; + case kReadAndWriteClose: + CloseReadPipe(); + CloseWritePipe(); + EXPECT_DEATH_CHECK(ReadPipeHandle(), "fd"); + EXPECT_DEATH_CHECK(WritePipeHandle(), "fd"); + break; + } + } + + // Multiprocess: + + void MultiprocessParent() override { + ASSERT_NO_FATAL_FAILURE(VerifyInitial()); + + if (who_closes_ == kParentCloses) { + Close(); + } else { + VerifyPartner(); + } + } + + void MultiprocessChild() override { + ASSERT_NO_FATAL_FAILURE(VerifyInitial()); + + if (who_closes_ == kChildCloses) { + Close(); + } else { + VerifyPartner(); + } + } + + WhoCloses who_closes_; + WhatCloses what_closes_; + + DISALLOW_COPY_AND_ASSIGN(TestMultiprocessClosePipe); +}; + +TEST(MultiprocessDeathTest, ParentClosesReadPipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kParentCloses, + TestMultiprocessClosePipe::kReadCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ParentClosesWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kParentCloses, + TestMultiprocessClosePipe::kWriteCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ParentClosesReadAndWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kParentCloses, + TestMultiprocessClosePipe::kReadAndWriteClose); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ChildClosesReadPipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kChildCloses, + TestMultiprocessClosePipe::kReadCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ChildClosesWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kChildCloses, + TestMultiprocessClosePipe::kWriteCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ChildClosesReadAndWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kChildCloses, + TestMultiprocessClosePipe::kReadAndWriteClose); + multiprocess.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/paths.cc b/third_party/crashpad/crashpad/test/paths.cc new file mode 100644 index 0000000..b5eb60b9 --- /dev/null +++ b/third_party/crashpad/crashpad/test/paths.cc
@@ -0,0 +1,100 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/paths.h" + +#include <stdlib.h> +#include <sys/stat.h> + +#include "base/logging.h" +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +namespace { + +bool IsTestDataRoot(const base::FilePath& candidate) { + const base::FilePath marker_path = + candidate.Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("paths_test_data_root.txt")); + +#if !defined(OS_WIN) + struct stat stat_buf; + int rv = stat(marker_path.value().c_str(), &stat_buf); +#else + struct _stat stat_buf; + int rv = _wstat(marker_path.value().c_str(), &stat_buf); +#endif + + return rv == 0; +} + +base::FilePath TestDataRootInternal() { +#if !defined(OS_WIN) + const char* environment_value = getenv("CRASHPAD_TEST_DATA_ROOT"); +#else // defined(OS_WIN) + const wchar_t* environment_value = _wgetenv(L"CRASHPAD_TEST_DATA_ROOT"); +#endif + + if (environment_value) { + // It was specified explicitly, so use it even if it seems incorrect. + if (!IsTestDataRoot(base::FilePath(environment_value))) { + LOG(WARNING) << "CRASHPAD_TEST_DATA_ROOT seems invalid, honoring anyway"; + } + + return base::FilePath(environment_value); + } + + // In a standalone build, the test executable is usually at + // out/{Debug,Release} relative to the Crashpad root. + const base::FilePath executable = Paths::Executable(); + base::FilePath candidate = + base::FilePath(executable.DirName() + .Append(base::FilePath::kParentDirectory) + .Append(base::FilePath::kParentDirectory)); + if (IsTestDataRoot(candidate)) { + return candidate; + } + + // In an in-Chromium build, the test executable is usually at + // out/{Debug,Release} relative to the Chromium root, and the Crashpad root is + // at third_party/crashpad/crashpad relative to the Chromium root. + candidate = candidate.Append(FILE_PATH_LITERAL("third_party")) + .Append(FILE_PATH_LITERAL("crashpad")) + .Append(FILE_PATH_LITERAL("crashpad")); + if (IsTestDataRoot(candidate)) { + return candidate; + } + + // If nothing else worked, use the current directory, issuing a warning if it + // doesn’t seem right. + if (!IsTestDataRoot(base::FilePath(base::FilePath::kCurrentDirectory))) { + LOG(WARNING) << "could not locate a valid test data root"; + } + + return base::FilePath(base::FilePath::kCurrentDirectory); +} + +} // namespace + +// static +base::FilePath Paths::TestDataRoot() { + static base::FilePath* test_data_root = + new base::FilePath(TestDataRootInternal()); + return *test_data_root; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/paths.h b/third_party/crashpad/crashpad/test/paths.h new file mode 100644 index 0000000..c143169 --- /dev/null +++ b/third_party/crashpad/crashpad/test/paths.h
@@ -0,0 +1,49 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_PATHS_H_ +#define CRASHPAD_TEST_PATHS_H_ + +#include "base/basictypes.h" +#include "base/files/file_path.h" + +namespace crashpad { +namespace test { + +//! \brief Functions to obtain paths from within tests. +class Paths { + public: + //! \brief Returns the pathname of the currently-running test executable. + static base::FilePath Executable(); + + //! \brief Returns the pathname of the test data root. + //! + //! If the `CRASHPAD_TEST_DATA_ROOT` environment variable is set, its value + //! will be returned. Otherwise, this function will attempt to locate the test + //! data root relative to the executable path. If this fails, it will fall + //! back to returning the current working directory. + //! + //! At present, the test data root is normally the root of the Crashpad source + //! tree, although this may not be the case indefinitely. This function may + //! only be used to locate test data, not for arbitrary access to source + //! files. + static base::FilePath TestDataRoot(); + + DISALLOW_IMPLICIT_CONSTRUCTORS(Paths); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_PATHS_H_
diff --git a/third_party/crashpad/crashpad/test/paths_mac.cc b/third_party/crashpad/crashpad/test/paths_mac.cc new file mode 100644 index 0000000..ce6c5f3 --- /dev/null +++ b/third_party/crashpad/crashpad/test/paths_mac.cc
@@ -0,0 +1,38 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/paths.h" + +#include <mach-o/dyld.h> +#include <stdint.h> + +#include "base/logging.h" + +namespace crashpad { +namespace test { + +// static +base::FilePath Paths::Executable() { + uint32_t executable_length = 0; + _NSGetExecutablePath(nullptr, &executable_length); + DCHECK_GT(executable_length, 1u); + std::string executable_path(executable_length, std::string::value_type()); + int rv = _NSGetExecutablePath(&executable_path[0], &executable_length); + DCHECK_EQ(rv, 0); + + return base::FilePath(executable_path); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/paths_test.cc b/third_party/crashpad/crashpad/test/paths_test.cc new file mode 100644 index 0000000..7cbca04 --- /dev/null +++ b/third_party/crashpad/crashpad/test/paths_test.cc
@@ -0,0 +1,47 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/paths.h" + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Paths, Executable) { + base::FilePath executable_path = Paths::Executable(); + base::FilePath executable_name = executable_path.BaseName(); +#if defined(OS_WIN) + EXPECT_EQ(FILE_PATH_LITERAL("crashpad_test_test.exe"), + executable_name.value()); +#else + EXPECT_EQ("crashpad_test_test", executable_name.value()); +#endif // OS_WIN +} + +TEST(Paths, TestDataRoot) { + base::FilePath test_data_root = Paths::TestDataRoot(); + ScopedFileHandle file(LoggingOpenFileForRead( + test_data_root.Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("paths_test_data_root.txt")))); + EXPECT_TRUE(file.is_valid()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/paths_test_data_root.txt b/third_party/crashpad/crashpad/test/paths_test_data_root.txt new file mode 100644 index 0000000..1ddd264 --- /dev/null +++ b/third_party/crashpad/crashpad/test/paths_test_data_root.txt
@@ -0,0 +1,16 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +This file is used by Paths::TestDataRoot() to locate the test data root. It +is present at a known path from the test data root.
diff --git a/third_party/crashpad/crashpad/test/paths_win.cc b/third_party/crashpad/crashpad/test/paths_win.cc new file mode 100644 index 0000000..95d0264 --- /dev/null +++ b/third_party/crashpad/crashpad/test/paths_win.cc
@@ -0,0 +1,30 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/paths.h" + +#include <windows.h> + +namespace crashpad { +namespace test { + +// static +base::FilePath Paths::Executable() { + wchar_t executable_path[_MAX_PATH]; + GetModuleFileName(nullptr, executable_path, sizeof(executable_path)); + return base::FilePath(executable_path); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir.cc b/third_party/crashpad/crashpad/test/scoped_temp_dir.cc new file mode 100644 index 0000000..5c151cc --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_temp_dir.cc
@@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/scoped_temp_dir.h" + +namespace crashpad { +namespace test { + +ScopedTempDir::ScopedTempDir() : path_(CreateTemporaryDirectory()) { +} + +ScopedTempDir::~ScopedTempDir() { + RecursivelyDeleteTemporaryDirectory(path()); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir.h b/third_party/crashpad/crashpad/test/scoped_temp_dir.h new file mode 100644 index 0000000..781975c --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_temp_dir.h
@@ -0,0 +1,64 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_SCOPED_TEMP_DIR_ +#define CRASHPAD_TEST_SCOPED_TEMP_DIR_ + +#include "base/basictypes.h" +#include "base/files/file_path.h" + +namespace crashpad { +namespace test { + +//! \brief A RAII object that creates a temporary directory for testing. +//! +//! Upon construction, a temporary directory will be created. Failure to create +//! the directory is fatal. On destruction, the directory and all its contents +//! will be removed. +class ScopedTempDir { + public: + ScopedTempDir(); + ~ScopedTempDir(); + + //! \brief Returns the path of the temporary directory. + //! + //! \return The temporary directory path. + const base::FilePath& path() const { return path_; } + + //! \brief Moves the temporary directory to a new temporary location. + void Rename(); + + private: + //! \brief Creates the temporary directory and asserts success of the + //! operation. + static base::FilePath CreateTemporaryDirectory(); + + //! \brief Removes all files and subdirectories at the given \a path, + //! including the \a path itself. + //! + //! Failures are recorded by gtest expectations. + //! + //! \param[in] path The path to delete, along with its contents. This must + //! reference a directory. + static void RecursivelyDeleteTemporaryDirectory(const base::FilePath& path); + + base::FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTempDir); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_SCOPED_TEMP_DIR_
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc b/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc new file mode 100644 index 0000000..34db76f --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_temp_dir_posix.cc
@@ -0,0 +1,69 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/scoped_temp_dir.h" + +#include <dirent.h> +#include <string.h> +#include <unistd.h> + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "test/errors.h" + +namespace crashpad { +namespace test { + +void ScopedTempDir::Rename() { + base::FilePath move_to = CreateTemporaryDirectory(); + PCHECK(rename(path_.value().c_str(), move_to.value().c_str()) == 0); + path_ = move_to; +} + +// static +base::FilePath ScopedTempDir::CreateTemporaryDirectory() { + char dir_template[] = "/tmp/org.chromium.crashpad.test.XXXXXX"; + PCHECK(mkdtemp(dir_template)) << "mkdtemp " << dir_template; + return base::FilePath(dir_template); +} + +// static +void ScopedTempDir::RecursivelyDeleteTemporaryDirectory( + const base::FilePath& path) { + DIR* dir = opendir(path.value().c_str()); + ASSERT_TRUE(dir) << ErrnoMessage("opendir") << " " << path.value(); + + dirent* entry; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + base::FilePath entry_path = path.Append(entry->d_name); + if (entry->d_type == DT_DIR) { + RecursivelyDeleteTemporaryDirectory(entry_path); + } else { + EXPECT_EQ(0, unlink(entry_path.value().c_str())) + << ErrnoMessage("unlink") << " " << entry_path.value(); + } + } + + EXPECT_EQ(0, closedir(dir)) + << ErrnoMessage("closedir") << " " << path.value(); + EXPECT_EQ(0, rmdir(path.value().c_str())) + << ErrnoMessage("rmdir") << " " << path.value(); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc b/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc new file mode 100644 index 0000000..ea8b067 --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc
@@ -0,0 +1,122 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/scoped_temp_dir.h" + +#include <fcntl.h> +#include <string.h> + +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/file.h" + +#if defined(OS_POSIX) +#include <unistd.h> +#elif defined(OS_WIN) +#include <direct.h> +#include <io.h> +#endif // OS_POSIX + +namespace crashpad { +namespace test { +namespace { + +void CreateFile(const base::FilePath& path) { +#if defined(OS_POSIX) + int fd = HANDLE_EINTR(creat(path.value().c_str(), 0644)); + ASSERT_GE(fd, 0) << ErrnoMessage("creat") << " " << path.value(); + ASSERT_EQ(0, IGNORE_EINTR(close(fd))) + << ErrnoMessage("close") << " " << path.value(); +#elif defined(OS_WIN) + int fd = _wcreat(path.value().c_str(), _S_IREAD | _S_IWRITE); + ASSERT_GE(fd, 0) << ErrnoMessage("_wcreat") << " " << path.value(); + ASSERT_EQ(0, _close(fd)) << ErrnoMessage("_close") << " " << path.value(); +#else +#error "Not implemented" +#endif + EXPECT_TRUE(FileExists(path)); +} + +void CreateDirectory(const base::FilePath& path) { +#if defined(OS_POSIX) + ASSERT_EQ(0, mkdir(path.value().c_str(), 0755)) + << ErrnoMessage("mkdir") << " " << path.value(); +#elif defined(OS_WIN) + ASSERT_EQ(0, _wmkdir(path.value().c_str())) + << ErrnoMessage("_wmkdir") << " " << path.value(); +#else +#error "Not implemented" +#endif + ASSERT_TRUE(FileExists(path)); +} + +TEST(ScopedTempDir, Empty) { + base::FilePath path; + { + ScopedTempDir dir; + path = dir.path(); + EXPECT_TRUE(FileExists(path)); + } + EXPECT_FALSE(FileExists(path)); +} + +TEST(ScopedTempDir, WithTwoFiles) { + base::FilePath parent, file1, file2; + + { + ScopedTempDir dir; + parent = dir.path(); + ASSERT_TRUE(FileExists(parent)); + + file1 = parent.Append(FILE_PATH_LITERAL("test1")); + CreateFile(file1); + + file2 = parent.Append(FILE_PATH_LITERAL("test 2")); + CreateFile(file2); + } + + EXPECT_FALSE(FileExists(file1)); + EXPECT_FALSE(FileExists(file2)); + EXPECT_FALSE(FileExists(parent)); +} + +TEST(ScopedTempDir, WithRecursiveDirectory) { + base::FilePath parent, file1, child_dir, file2; + + { + ScopedTempDir dir; + parent = dir.path(); + ASSERT_TRUE(FileExists(parent)); + + file1 = parent.Append(FILE_PATH_LITERAL(".first-level file")); + CreateFile(file1); + + child_dir = parent.Append(FILE_PATH_LITERAL("subdir")); + CreateDirectory(child_dir); + + file2 = child_dir.Append(FILE_PATH_LITERAL("second level file")); + CreateFile(file2); + } + + EXPECT_FALSE(FileExists(file1)); + EXPECT_FALSE(FileExists(file2)); + EXPECT_FALSE(FileExists(child_dir)); + EXPECT_FALSE(FileExists(parent)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir_win.cc b/third_party/crashpad/crashpad/test/scoped_temp_dir_win.cc new file mode 100644 index 0000000..cc4820d --- /dev/null +++ b/third_party/crashpad/crashpad/test/scoped_temp_dir_win.cc
@@ -0,0 +1,104 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/scoped_temp_dir.h" + +#include <windows.h> + +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "util/misc/random_string.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { + +namespace { + +base::FilePath GenerateCandidateName() { + wchar_t temp_path[MAX_PATH + 1]; + DWORD path_len = GetTempPath(MAX_PATH, temp_path); + PCHECK(path_len != 0) << "GetTempPath"; + base::FilePath system_temp_dir(temp_path); + base::string16 new_dir_name = base::UTF8ToUTF16(base::StringPrintf( + "crashpad.test.%d.%s", GetCurrentProcessId(), RandomString().c_str())); + return system_temp_dir.Append(new_dir_name); +} + +const int kRetries = 50; + +} // namespace + +void ScopedTempDir::Rename() { + for (int count = 0; count < kRetries; ++count) { + // Try to move to a new temporary directory with a randomly generated name. + // If the one we try exists, retry with a new name until we reach some + // limit. + base::FilePath target_path = GenerateCandidateName(); + if (MoveFileEx(path_.value().c_str(), target_path.value().c_str(), 0)) { + path_ = target_path; + return; + } + } + + CHECK(false) << "Couldn't move to a new unique temp dir"; +} + +// static +base::FilePath ScopedTempDir::CreateTemporaryDirectory() { + for (int count = 0; count < kRetries; ++count) { + // Try to create a new temporary directory with random generated name. If + // the one we generate exists, keep trying another path name until we reach + // some limit. + base::FilePath path_to_create = GenerateCandidateName(); + if (CreateDirectory(path_to_create.value().c_str(), NULL)) + return path_to_create; + } + + CHECK(false) << "Couldn't create a new unique temp dir"; + return base::FilePath(); +} + +// static +void ScopedTempDir::RecursivelyDeleteTemporaryDirectory( + const base::FilePath& path) { + const base::string16 all_files_mask(L"\\*"); + + base::string16 search_mask = path.value() + all_files_mask; + WIN32_FIND_DATA find_data; + HANDLE search_handle = FindFirstFile(search_mask.c_str(), &find_data); + if (search_handle == INVALID_HANDLE_VALUE) + ASSERT_EQ(ERROR_FILE_NOT_FOUND, GetLastError()); + do { + if (wcscmp(find_data.cFileName, L".") == 0 || + wcscmp(find_data.cFileName, L"..") == 0) { + continue; + } + base::FilePath entry_path = path.Append(find_data.cFileName); + ASSERT_FALSE(find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + RecursivelyDeleteTemporaryDirectory(entry_path); + else + EXPECT_TRUE(DeleteFile(entry_path.value().c_str())); + } while (FindNextFile(search_handle, &find_data)); + EXPECT_EQ(ERROR_NO_MORE_FILES, GetLastError()); + + EXPECT_TRUE(FindClose(search_handle)); + EXPECT_TRUE(RemoveDirectory(path.value().c_str())); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/test.gyp b/third_party/crashpad/crashpad/test/test.gyp new file mode 100644 index 0000000..e8c1709c --- /dev/null +++ b/third_party/crashpad/crashpad/test/test.gyp
@@ -0,0 +1,81 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_test', + 'type': 'static_library', + 'dependencies': [ + '../compat/compat.gyp:crashpad_compat', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'errors.cc', + 'errors.h', + 'file.cc', + 'file.h', + 'gtest_death_check.h', + 'mac/dyld.h', + 'mac/mach_errors.cc', + 'mac/mach_errors.h', + 'mac/mach_multiprocess.cc', + 'mac/mach_multiprocess.h', + 'multiprocess.h', + 'multiprocess_exec.h', + 'multiprocess_exec_posix.cc', + 'multiprocess_exec_win.cc', + 'multiprocess_posix.cc', + 'paths.cc', + 'paths.h', + 'paths_mac.cc', + 'paths_win.cc', + 'scoped_temp_dir.cc', + 'scoped_temp_dir.h', + 'scoped_temp_dir_posix.cc', + 'scoped_temp_dir_win.cc', + 'win/child_launcher.cc', + 'win/child_launcher.h', + 'win/win_child_process.cc', + 'win/win_child_process.h', + 'win/win_multiprocess.cc', + 'win/win_multiprocess.h', + ], + 'conditions': [ + ['OS=="mac"', { + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/usr/lib/libbsm.dylib', + ], + }, + }], + ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-lshell32.lib', + ], + }, + }], + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/test/test_test.gyp b/third_party/crashpad/crashpad/test/test_test.gyp new file mode 100644 index 0000000..e4405fcc --- /dev/null +++ b/third_party/crashpad/crashpad/test/test_test.gyp
@@ -0,0 +1,54 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_test_test', + 'type': 'executable', + 'dependencies': [ + 'crashpad_test_test_multiprocess_exec_test_child', + 'test.gyp:crashpad_test', + '../compat/compat.gyp:crashpad_compat', + '../third_party/gtest/gmock.gyp:gmock', + '../third_party/gtest/gmock.gyp:gmock_main', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'mac/mach_multiprocess_test.cc', + 'multiprocess_exec_test.cc', + 'multiprocess_posix_test.cc', + 'paths_test.cc', + 'scoped_temp_dir_test.cc', + 'win/win_child_process_test.cc', + 'win/win_multiprocess_test.cc', + ], + }, + { + 'target_name': 'crashpad_test_test_multiprocess_exec_test_child', + 'type': 'executable', + 'sources': [ + 'multiprocess_exec_test_child.cc', + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/test/win/child_launcher.cc b/third_party/crashpad/crashpad/test/win/child_launcher.cc new file mode 100644 index 0000000..e35f1cb --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/child_launcher.cc
@@ -0,0 +1,101 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/win/child_launcher.h" + +#include "gtest/gtest.h" +#include "util/win/command_line.h" + +namespace crashpad { +namespace test { + +ChildLauncher::ChildLauncher(const std::wstring& executable, + const std::wstring& command_line) + : executable_(executable), + command_line_(command_line), + process_handle_(), + main_thread_handle_(), + stdout_read_handle_(), + stdin_write_handle_() { +} + +ChildLauncher::~ChildLauncher() { + if (process_handle_.is_valid()) + WaitForExit(); +} + +void ChildLauncher::Start() { + ASSERT_FALSE(process_handle_.is_valid()); + ASSERT_FALSE(main_thread_handle_.is_valid()); + ASSERT_FALSE(stdout_read_handle_.is_valid()); + + // Create pipes for the stdin/stdout of the child. + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = true; + + HANDLE stdout_read; + HANDLE stdout_write; + ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)); + stdout_read_handle_.reset(stdout_read); + ScopedFileHANDLE write_handle(stdout_write); + ASSERT_TRUE( + SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0)); + + HANDLE stdin_read; + HANDLE stdin_write; + ASSERT_TRUE(CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0)); + stdin_write_handle_.reset(stdin_write); + ScopedFileHANDLE read_handle(stdin_read); + ASSERT_TRUE( + SetHandleInformation(stdin_write_handle_.get(), HANDLE_FLAG_INHERIT, 0)); + + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.hStdInput = read_handle.get(); + startup_info.hStdOutput = write_handle.get(); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.dwFlags = STARTF_USESTDHANDLES; + PROCESS_INFORMATION process_information; + std::wstring command_line; + AppendCommandLineArgument(executable_, &command_line); + command_line += L" "; + command_line += command_line_; + ASSERT_TRUE(CreateProcess(executable_.c_str(), + &command_line[0], + nullptr, + nullptr, + true, + 0, + nullptr, + nullptr, + &startup_info, + &process_information)); + // Take ownership of the two process handles returned. + main_thread_handle_.reset(process_information.hThread); + process_handle_.reset(process_information.hProcess); +} + +DWORD ChildLauncher::WaitForExit() { + EXPECT_TRUE(process_handle_.is_valid()); + EXPECT_EQ(WAIT_OBJECT_0, + WaitForSingleObject(process_handle_.get(), INFINITE)); + DWORD exit_code = 0; + EXPECT_TRUE(GetExitCodeProcess(process_handle_.get(), &exit_code)); + process_handle_.reset(); + return exit_code; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/win/child_launcher.h b/third_party/crashpad/crashpad/test/win/child_launcher.h new file mode 100644 index 0000000..22463fa --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/child_launcher.h
@@ -0,0 +1,73 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_ +#define CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_ + +#include <windows.h> + +#include <string> + +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Creates a child process for testing. Uses gtest `ASSERT_*` to +//! indicate failure. The child's output is passed through a pipe and is +//! available via stdout_read_handle(), and the child's input is attached to +//! a second pipe available via stdin_write_handle(). +class ChildLauncher { + public: + //! \brief Creates the object. \a executable will be escaped and prepended to + //! \a command_line to build the command line of the child. + ChildLauncher(const std::wstring& executable, + const std::wstring& command_line); + + ~ChildLauncher(); + + //! \brief Starts the child process, after which the handle functions below + //! will be valid. + void Start(); + + //! \brief Waits for the child process to exit. + //! + //! \return The process exit code. + DWORD WaitForExit(); + + //! \brief The child process's `HANDLE`. + HANDLE process_handle() const { return process_handle_.get(); } + + //! \brief The child process's main thread's `HANDLE`. + HANDLE main_thread_handle() const { return main_thread_handle_.get(); } + + //! \brief The read end of a pipe attached to the child's stdout. + HANDLE stdout_read_handle() const { return stdout_read_handle_.get(); } + + //! \brief The write end of a pipe attached to the child's stdin. + HANDLE stdin_write_handle() const { return stdin_write_handle_.get(); } + + private: + std::wstring executable_; + std::wstring command_line_; + ScopedKernelHANDLE process_handle_; + ScopedKernelHANDLE main_thread_handle_; + ScopedFileHANDLE stdout_read_handle_; + ScopedFileHANDLE stdin_write_handle_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_
diff --git a/third_party/crashpad/crashpad/test/win/win_child_process.cc b/third_party/crashpad/crashpad/test/win/win_child_process.cc new file mode 100644 index 0000000..1f11bc7 --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/win_child_process.cc
@@ -0,0 +1,236 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/win/win_child_process.h" + +#include <windows.h> +#include <shellapi.h> + +#include <string> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "util/stdlib/move.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" +#include "util/win/handle.h" +#include "util/win/scoped_local_alloc.h" +#include "test/paths.h" + +namespace crashpad { +namespace test { + +namespace { + +const char kIsMultiprocessChild[] = "--is-multiprocess-child"; + +bool GetSwitch(const char* switch_name, std::string* value) { + int num_args; + wchar_t** args = CommandLineToArgvW(GetCommandLine(), &num_args); + ScopedLocalAlloc scoped_args(args); // Take ownership. + if (!args) { + PLOG(FATAL) << "CommandLineToArgvW"; + return false; + } + + std::string switch_name_with_equals(switch_name); + switch_name_with_equals += "="; + for (int i = 1; i < num_args; ++i) { + const wchar_t* arg = args[i]; + std::string arg_as_utf8 = base::UTF16ToUTF8(arg); + if (arg_as_utf8.compare( + 0, switch_name_with_equals.size(), switch_name_with_equals) == 0) { + if (value) + *value = arg_as_utf8.substr(switch_name_with_equals.size()); + return true; + } + } + + return false; +} + +ScopedKernelHANDLE LaunchCommandLine(wchar_t* command_line) { + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.dwFlags = STARTF_USESTDHANDLES; + PROCESS_INFORMATION process_info; + if (!CreateProcess(Paths::Executable().value().c_str(), + &command_line[0], // This cannot be constant, per MSDN. + nullptr, + nullptr, + true, // Inherit handles. + 0, + nullptr, + nullptr, + &startup_info, + &process_info)) { + PLOG(ERROR) << "CreateProcess"; + return ScopedKernelHANDLE(); + } + if (!CloseHandle(process_info.hThread)) { + PLOG(ERROR) << "CloseHandle"; + if (!CloseHandle(process_info.hProcess)) + PLOG(ERROR) << "CloseHandle"; + return ScopedKernelHANDLE(); + } + return ScopedKernelHANDLE(process_info.hProcess); +} + +bool UnsetHandleInheritance(HANDLE handle) { + if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0)) { + PLOG(ERROR) << "SetHandleInformation"; + ADD_FAILURE() << "SetHandleInformation"; + return false; + } + return true; +} + +bool CreateInheritablePipe(ScopedFileHANDLE* read_handle, + bool read_inheritable, + ScopedFileHANDLE* write_handle, + bool write_inheritable) { + // Mark both sides as inheritable via the SECURITY_ATTRIBUTES and use + // SetHandleInformation as necessary to restrict inheritance of either side. + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = true; + + HANDLE read, write; + BOOL result = CreatePipe(&read, &write, &security_attributes, 0); + if (!result) { + PLOG(ERROR) << "CreatePipe"; + ADD_FAILURE() << "CreatePipe failed"; + return false; + } + ScopedFileHANDLE temp_read(read); + ScopedFileHANDLE temp_write(write); + + if (!read_inheritable && !UnsetHandleInheritance(temp_read.get())) + return false; + if (!write_inheritable && !UnsetHandleInheritance(temp_write.get())) + return false; + + *read_handle = crashpad::move(temp_read); + *write_handle = crashpad::move(temp_write); + + return true; +} + +} // namespace + +WinChildProcess::WinChildProcess() { + std::string switch_value; + CHECK(GetSwitch(kIsMultiprocessChild, &switch_value)); + + // Set up the handles we inherited from the parent. These are inherited from + // the parent and so are open and have the same value as in the parent. The + // values are passed to the child on the command line. + std::string left, right; + CHECK(SplitString(switch_value, '|', &left, &right)); + + // left and right were formatted as 0x%x, so they need to be converted as + // unsigned ints. + unsigned int write, read; + CHECK(StringToNumber(left, &write)); + CHECK(StringToNumber(right, &read)); + + pipe_write_.reset(IntToHandle(write)); + pipe_read_.reset(IntToHandle(read)); + + // Notify the parent that it's OK to proceed. We only need to wait to get to + // the process entry point, but this is the easiest place we can notify. + char c = ' '; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); +} + +// static +bool WinChildProcess::IsChildProcess() { + return GetSwitch(kIsMultiprocessChild, nullptr); +} + +// static +scoped_ptr<WinChildProcess::Handles> WinChildProcess::Launch() { + // Make pipes for child-to-parent and parent-to-child communication. + scoped_ptr<Handles> handles_for_parent(new Handles); + ScopedFileHANDLE read_for_child; + ScopedFileHANDLE write_for_child; + + if (!CreateInheritablePipe( + &handles_for_parent->read, false, &write_for_child, true)) { + return scoped_ptr<Handles>(); + } + + if (!CreateInheritablePipe( + &read_for_child, true, &handles_for_parent->write, false)) { + return scoped_ptr<Handles>(); + } + + // Build a command line for the child process that tells it only to run the + // current test, and to pass down the values of the pipe handles. + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + std::wstring command_line = + Paths::Executable().value() + L" " + + base::UTF8ToUTF16(base::StringPrintf("--gtest_filter=%s.%s %s=0x%x|0x%x", + test_info->test_case_name(), + test_info->name(), + kIsMultiprocessChild, + HandleToInt(write_for_child.get()), + HandleToInt(read_for_child.get()))); + + // Command-line buffer cannot be constant, per CreateProcess signature. + handles_for_parent->process = LaunchCommandLine(&command_line[0]); + if (!handles_for_parent->process.is_valid()) + return scoped_ptr<Handles>(); + + // Block until the child process has launched. CreateProcess() returns + // immediately, and test code expects process initialization to have + // completed so it can, for example, read the process memory. + char c; + if (!LoggingReadFile(handles_for_parent->read.get(), &c, sizeof(c))) { + ADD_FAILURE() << "LoggedReadFile"; + return scoped_ptr<Handles>(); + } + + if (c != ' ') { + ADD_FAILURE() << "invalid data read from child"; + return scoped_ptr<Handles>(); + } + + return crashpad::move(handles_for_parent); +} + +FileHandle WinChildProcess::ReadPipeHandle() const { + return pipe_read_.get(); +} + +FileHandle WinChildProcess::WritePipeHandle() const { + return pipe_write_.get(); +} + +void WinChildProcess::CloseReadPipe() { + pipe_read_.reset(); +} + +void WinChildProcess::CloseWritePipe() { + pipe_write_.reset(); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/win/win_child_process.h b/third_party/crashpad/crashpad/test/win/win_child_process.h new file mode 100644 index 0000000..6ca6eca --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/win_child_process.h
@@ -0,0 +1,117 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_ +#define CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Facilitates the launching of child processes from unit tests. +class WinChildProcess { + public: + //! \brief Groups handles used to communicate with, observe, and manage a + //! child process. + struct Handles { + //! \brief A handle to read from an anonymous pipe shared with the child + //! process. + ScopedFileHANDLE read; + //! \brief A handle to write to an anonymous pipe shared with the child + //! process. + ScopedFileHANDLE write; + //! \brief A handle to the child process. + ScopedKernelHANDLE process; + }; + + WinChildProcess(); + virtual ~WinChildProcess() {} + + //! \brief Returns true if the current process is a child process. + static bool IsChildProcess(); + + //! \brief Runs the child process defined by T if the current process is a + //! child process; does not return in that case. Otherwise, returns. + template <class T> + static void EntryPoint() { + if (IsChildProcess()) { + // The static_cast here will cause a compiler failure if T is not a + // subclass of WinChildProcess. It's the constructor of WinChildProcess + // that performs the pipe handshake with the parent process (without which + // we would have a hang). + T child_process; + int result = static_cast<WinChildProcess*>(&child_process)->Run(); + exit(result); + } + } + + //! \brief Launches a child process and returns the Handles for that process. + //! The process is guaranteed to be executing by the time this method + //! returns. Returns null and logs a GTest failure in case of failure. + static scoped_ptr<Handles> Launch(); + + protected: + //! \brief Returns a handle to read from an anonymous pipe shared with the + //! parent process. + //! + //! It is an error to call this after CloseReadPipe() has been called. + //! + //! \return The read pipe's file handle. + FileHandle ReadPipeHandle() const; + + //! \brief Returns a handle to write to an anonymous pipe shared with the + //! parent process. + //! + //! It is an error to call this after CloseWritePipe() has been called. + //! + //! \return The write pipe's file handle. + FileHandle WritePipeHandle() const; + + //! \brief Closes the read pipe. + //! + //! ReadPipeHandle() must not be called after this. + void CloseReadPipe(); + + //! \brief Closes the write pipe. + //! + //! An attempt to read from the read pipe in the parent process will indicate + //! end-of-file. WritePipeHandle() must not be called after this. + void CloseWritePipe(); + + private: + //! \brief The subclass-provided child routine. + //! + //! Subclasses must implement this method to define how the child operates. + //! Subclasses may exit with a failure status by using `LOG(FATAL)`, + //! `abort()`, or similar. They may also exit by returning their exit code + //! from this method. It is up to the client to observe and interpret the + //! child's exit code. + //! + //! \return The child process exit code. + virtual int Run() = 0; + + ScopedFileHANDLE pipe_read_; + ScopedFileHANDLE pipe_write_; + + DISALLOW_COPY_AND_ASSIGN(WinChildProcess); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_
diff --git a/third_party/crashpad/crashpad/test/win/win_child_process_test.cc b/third_party/crashpad/crashpad/test/win/win_child_process_test.cc new file mode 100644 index 0000000..e191dcc --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/win_child_process_test.cc
@@ -0,0 +1,84 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/win/win_child_process.h" + +#include <stdlib.h> +#include <windows.h> + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +int ReadInt(HANDLE handle) { + int value = 0; + DWORD bytes_read = 0; + EXPECT_TRUE(::ReadFile(handle, &value, sizeof(value), &bytes_read, nullptr)); + EXPECT_EQ(sizeof(value), bytes_read); + return value; +} + +void WriteInt(HANDLE handle, int value) { + DWORD bytes_written = 0; + EXPECT_TRUE( + ::WriteFile(handle, &value, sizeof(value), &bytes_written, nullptr)); + EXPECT_EQ(sizeof(value), bytes_written); +} + +class TestWinChildProcess final : public WinChildProcess { + public: + TestWinChildProcess() : WinChildProcess() {} + + ~TestWinChildProcess() {} + + private: + // WinChildProcess will have already exercised the pipes. + int Run() override { + int value = ReadInt(ReadPipeHandle()); + WriteInt(WritePipeHandle(), value); + return testing::Test::HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS; + } + + DISALLOW_COPY_AND_ASSIGN(TestWinChildProcess); +}; + +TEST(WinChildProcessTest, WinChildProcess) { + WinChildProcess::EntryPoint<TestWinChildProcess>(); + + scoped_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch(); + WriteInt(handles->write.get(), 1); + ASSERT_EQ(1, ReadInt(handles->read.get())); +} + +TEST(WinChildProcessTest, MultipleChildren) { + WinChildProcess::EntryPoint<TestWinChildProcess>(); + + scoped_ptr<WinChildProcess::Handles> handles_1 = WinChildProcess::Launch(); + scoped_ptr<WinChildProcess::Handles> handles_2 = WinChildProcess::Launch(); + scoped_ptr<WinChildProcess::Handles> handles_3 = WinChildProcess::Launch(); + + WriteInt(handles_1->write.get(), 1); + WriteInt(handles_2->write.get(), 2); + WriteInt(handles_3->write.get(), 3); + ASSERT_EQ(3, ReadInt(handles_3->read.get())); + ASSERT_EQ(2, ReadInt(handles_2->read.get())); + ASSERT_EQ(1, ReadInt(handles_1->read.get())); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/win/win_multiprocess.cc b/third_party/crashpad/crashpad/test/win/win_multiprocess.cc new file mode 100644 index 0000000..6a451f4 --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/win_multiprocess.cc
@@ -0,0 +1,80 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/win/win_multiprocess.h" + +#include <shellapi.h> + +#include "base/logging.h" +#include "base/scoped_generic.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" +#include "test/paths.h" + +namespace crashpad { +namespace test { + +WinMultiprocess::WinMultiprocess() + : exit_code_(EXIT_SUCCESS), + child_handles_(nullptr), + child_process_helper_(nullptr) {} + +WinMultiprocess::~WinMultiprocess() { +} + +void WinMultiprocess::SetExpectedChildExitCode(unsigned int exit_code) { + exit_code_ = exit_code; +} + +FileHandle WinMultiprocess::ReadPipeHandle() const { + if (child_handles_) + return child_handles_->read.get(); + CHECK(child_process_helper_); + return child_process_helper_->ReadPipeHandleForwarder(); +} + +FileHandle WinMultiprocess::WritePipeHandle() const { + if (child_handles_) + return child_handles_->write.get(); + CHECK(child_process_helper_); + return child_process_helper_->WritePipeHandleForwarder(); +} + +void WinMultiprocess::CloseReadPipe() { + if (child_handles_) { + child_handles_->read.reset(); + } else { + CHECK(child_process_helper_); + child_process_helper_->CloseReadPipeForwarder(); + } +} + +void WinMultiprocess::CloseWritePipe() { + if (child_handles_) { + child_handles_->write.reset(); + } else { + CHECK(child_process_helper_); + child_process_helper_->CloseWritePipeForwarder(); + } +} + +HANDLE WinMultiprocess::ChildProcess() const { + CHECK(child_handles_); + return child_handles_->process.get(); +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/win/win_multiprocess.h b/third_party/crashpad/crashpad/test/win/win_multiprocess.h new file mode 100644 index 0000000..906fa5dd --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/win_multiprocess.h
@@ -0,0 +1,186 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_ +#define CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "gtest/gtest.h" +#include "test/win/win_child_process.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Manages a multiprocess test on Windows. +class WinMultiprocess { + public: + WinMultiprocess(); + + //! \brief Runs the test. + //! + //! This method establishes the testing environment by respawning the process + //! as a child with additional flags. + //! + //! In the parent process, WinMultiprocessParent() is run, and in the child + //! WinMultiprocessChild(). + template <class T> + static void Run() { + ASSERT_NO_FATAL_FAILURE( + WinChildProcess::EntryPoint<ChildProcessHelper<T>>()); + // If WinChildProcess::EntryPoint returns, we are in the parent process. + scoped_ptr<WinChildProcess::Handles> child_handles = + WinChildProcess::Launch(); + ASSERT_TRUE(child_handles.get()); + T parent_process; + parent_process.child_handles_ = child_handles.get(); + static_cast<WinMultiprocess*>(&parent_process)->WinMultiprocessParent(); + + // Close our side of the handles now that we're done. The child can + // use this to know when it's safe to complete. + child_handles->read.reset(); + child_handles->write.reset(); + + // Wait for the child to complete. + ASSERT_EQ(WAIT_OBJECT_0, + WaitForSingleObject(child_handles->process.get(), INFINITE)); + + DWORD exit_code; + ASSERT_TRUE(GetExitCodeProcess(child_handles->process.get(), &exit_code)); + ASSERT_EQ(parent_process.exit_code_, exit_code); + } + + protected: + virtual ~WinMultiprocess(); + + //! \brief Sets the expected exit code of the child process. + //! + //! The default expected termination code is `EXIT_SUCCESS` (`0`). + //! + //! \param[in] code The expected exit status of the child. + void SetExpectedChildExitCode(unsigned int exit_code); + + //! \brief Returns the read pipe's file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to the write pipe in the partner process will appear + //! on this file handle in this process. + //! + //! It is an error to call this after CloseReadPipe() has been called. + //! + //! \return The read pipe's file handle. + FileHandle ReadPipeHandle() const; + + //! \brief Returns the write pipe's file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to this file handle in this process will appear on + //! the read pipe in the partner process. + //! + //! It is an error to call this after CloseWritePipe() has been called. + //! + //! \return The write pipe's file handle. + FileHandle WritePipeHandle() const; + + //! \brief Closes the read pipe. + //! + //! This method may be called by either the parent or the child process. + //! ReadPipeHandle() must not be called after this. + void CloseReadPipe(); + + //! \brief Closes the write pipe. + //! + //! This method may be called by either the parent or the child process. An + //! attempt to read from the read pipe in the partner process will indicate + //! end-of-file. WritePipeHandle() must not be called after this. + void CloseWritePipe(); + + //! \brief Returns a handle to the child process. + //! + //! This method may only be called by the parent process. + HANDLE ChildProcess() const; + + private: + // Implements an adapter to provide WinMultiprocess with access to the + // anonymous pipe handles from WinChildProcess. + class ChildProcessHelperBase : public WinChildProcess { + public: + ChildProcessHelperBase() {} + ~ChildProcessHelperBase() override {} + + void CloseWritePipeForwarder() { CloseWritePipe(); } + void CloseReadPipeForwarder() { CloseReadPipe(); } + FileHandle ReadPipeHandleForwarder() const { return ReadPipeHandle(); } + FileHandle WritePipeHandleForwarder() const { return WritePipeHandle(); } + + private: + DISALLOW_COPY_AND_ASSIGN(ChildProcessHelperBase); + }; + + // Forwards WinChildProcess::Run to T::WinMultiprocessChild. + template <class T> + class ChildProcessHelper : public ChildProcessHelperBase { + public: + ChildProcessHelper() {} + ~ChildProcessHelper() override {} + + private: + int Run() override { + T child_process; + child_process.child_process_helper_ = this; + static_cast<WinMultiprocess*>(&child_process)->WinMultiprocessChild(); + if (testing::Test::HasFailure()) + return 255; + return EXIT_SUCCESS; + } + + DISALLOW_COPY_AND_ASSIGN(ChildProcessHelper); + }; + + //! \brief The subclass-provided parent routine. + //! + //! Test failures should be reported via gtest: `EXPECT_*()`, `ASSERT_*()`, + //! `FAIL()`, etc. + //! + //! This method need not use `WaitForSingleObject()`-family call to wait for + //! the child process to exit, as this is handled by this class. + //! + //! Subclasses must implement this method to define how the parent operates. + virtual void WinMultiprocessParent() = 0; + + //! \brief The subclass-provided child routine. + //! + //! Test failures should be reported via gtest: `EXPECT_*()`, `ASSERT_*()`, + //! `FAIL()`, etc. + //! + //! Subclasses must implement this method to define how the child operates. + //! Subclasses may exit with a failure status by using `LOG(FATAL)`, + //! `abort()`, or similar. They may exit cleanly by returning from this + //! method. + virtual void WinMultiprocessChild() = 0; + + unsigned int exit_code_; + WinChildProcess::Handles* child_handles_; + ChildProcessHelperBase* child_process_helper_; + + DISALLOW_COPY_AND_ASSIGN(WinMultiprocess); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_
diff --git a/third_party/crashpad/crashpad/test/win/win_multiprocess_test.cc b/third_party/crashpad/crashpad/test/win/win_multiprocess_test.cc new file mode 100644 index 0000000..19af08e --- /dev/null +++ b/third_party/crashpad/crashpad/test/win/win_multiprocess_test.cc
@@ -0,0 +1,84 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "test/win/win_multiprocess.h" + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +template <int ExitCode> +class TestWinMultiprocess final : public WinMultiprocess { + public: + TestWinMultiprocess() {} + + private: + // WinMultiprocess will have already exercised the pipes. + void WinMultiprocessParent() override { SetExpectedChildExitCode(ExitCode); } + + void WinMultiprocessChild() override { + exit(ExitCode); + } + + DISALLOW_COPY_AND_ASSIGN(TestWinMultiprocess); +}; + +class TestWinMultiprocessChildAsserts final : public WinMultiprocess { + public: + TestWinMultiprocessChildAsserts() {} + + private: + void WinMultiprocessParent() override { SetExpectedChildExitCode(255); } + void WinMultiprocessChild() override { + ASSERT_FALSE(true); + } + + DISALLOW_COPY_AND_ASSIGN(TestWinMultiprocessChildAsserts); +}; + +class TestWinMultiprocessChildExpects final : public WinMultiprocess { + public: + TestWinMultiprocessChildExpects() {} + + private: + void WinMultiprocessParent() override { SetExpectedChildExitCode(255); } + void WinMultiprocessChild() override { + EXPECT_FALSE(true); + } + + DISALLOW_COPY_AND_ASSIGN(TestWinMultiprocessChildExpects); +}; + +TEST(WinMultiprocess, WinMultiprocess) { + WinMultiprocess::Run<TestWinMultiprocess<0>>(); +} + +TEST(WinMultiprocess, WinMultiprocessNonSuccessExitCode) { + WinMultiprocess::Run<TestWinMultiprocess<100>>(); +} + +TEST(WinMultiprocessChildFails, ChildExpectFailure) { + WinMultiprocess::Run<TestWinMultiprocessChildExpects>(); +} + +TEST(WinMultiprocessChildFails, ChildAssertFailure) { + WinMultiprocess::Run<TestWinMultiprocessChildAsserts>(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad b/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad new file mode 100644 index 0000000..a2aadf7 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad
@@ -0,0 +1,44 @@ +Name: Apple cctools +Short Name: cctools +URL: https://opensource.apple.com/source/cctools/ +URL: https://opensource.apple.com/tarballs/cctools/ +Version: 855 (from Xcode 5.1) +License: APSL 2.0 +License File: cctools/APPLE_LICENSE +Security Critical: no + +Description: +cctools contains portions of Apple’s compiler toolchain, including common tools +like ar, as, nm, strings, and strip, and platform-specific tools like lipo and +otool. It also contains support libraries such as libmacho, which contains +interfaces for dealing with Mach-O images. + +libmacho is available on Mac OS X as a runtime library that is part of +libSystem, but versions of libmacho included in operating system versions prior +to Mac OS X 10.7 did not include the getsectiondata() and getsegmentdata() +functions. This library is present here to provide implementations of these +functions for systems that do not have them. + +Crashpad code is not expected to use this library directly. It should use the +getsectiondata() and getsegmentdata() wrappers in compat, which will use +system-provided implementations if present at runtime, and will otherwise fall +back to the implementations in this library. + +Local Modifications: + - Only cctools/APPLE_LICENSE, cctools/libmacho/getsecbyname.c, and + cctools/include/mach-o/getsect.h are included. + - getsecbyname.c and getsect.h have been trimmed to remove everything other + than the getsectiondata() and getsegmentdata() functions. The #include guards + in getsect.h have been made unique. + - getsectiondata() is renamed to crashpad_getsectiondata(), and + getsegmentdata() is renamed to crashpad_getsegmentdata(). + - These functions are only declared and defined if the deployment target is + older than 10.7. This library is not needed otherwise, because in that case, + the system always provides implementations in runtime libraries. + - Originally, each of these two functions were implemented twice: once for + 32-bit code and once for 64-bit code. Aside from the types and constants + used, the two implementations were completely identical. This has been + simplified to have a shared implementation that relies on local typedefs and + constants being defined properly. This change was only made in + getsecbyname.c. getsect.h was not changed to avoid leaking new definitions + beyond this header.
diff --git a/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp b/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp new file mode 100644 index 0000000..7e0dfd4 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp
@@ -0,0 +1,34 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'targets': [ + { + 'target_name': 'apple_cctools', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../..', + ], + }, + 'sources': [ + 'cctools/include/mach-o/getsect.h', + 'cctools/libmacho/getsecbyname.c', + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE b/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE
@@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +This file contains Original Code and/or Modifications of Original Code +as defined in and that are subject to the Apple Public Source License +Version 2.0 (the 'License'). You may not use this file except in +compliance with the License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this +file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +Please see the License for the specific language governing rights and +limitations under the License."
diff --git a/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h b/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h new file mode 100644 index 0000000..639b8061 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h
@@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ +#define CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ + +#include <AvailabilityMacros.h> + +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 + +#include <stdint.h> +#include <mach-o/loader.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __LP64__ +/* + * Runtime interfaces for 32-bit Mach-O programs. + */ +extern uint8_t *crashpad_getsectiondata( + const struct mach_header *mhp, + const char *segname, + const char *sectname, + unsigned long *size); + +extern uint8_t *crashpad_getsegmentdata( + const struct mach_header *mhp, + const char *segname, + unsigned long *size); + +#else /* defined(__LP64__) */ +/* + * Runtime interfaces for 64-bit Mach-O programs. + */ +extern uint8_t *crashpad_getsectiondata( + const struct mach_header_64 *mhp, + const char *segname, + const char *sectname, + unsigned long *size); + +extern uint8_t *crashpad_getsegmentdata( + const struct mach_header_64 *mhp, + const char *segname, + unsigned long *size); + +#endif /* defined(__LP64__) */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 */ + +#endif /* CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ */
diff --git a/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c b/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c new file mode 100644 index 0000000..1db1cbec --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c
@@ -0,0 +1,133 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "third_party/apple_cctools/cctools/include/mach-o/getsect.h" + +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 + +#include <string.h> + +#ifndef __LP64__ +typedef struct mach_header mach_header_32_64; +typedef struct segment_command segment_command_32_64; +typedef struct section section_32_64; +#define LC_SEGMENT_32_64 LC_SEGMENT +#else /* defined(__LP64__) */ +typedef struct mach_header_64 mach_header_32_64; +typedef struct segment_command_64 segment_command_32_64; +typedef struct section_64 section_32_64; +#define LC_SEGMENT_32_64 LC_SEGMENT_64 +#endif /* defined(__LP64__) */ + +/* + * This routine returns the a pointer to the section contents of the named + * section in the named segment if it exists in the image pointed to by the + * mach header. Otherwise it returns zero. + */ + +uint8_t * +crashpad_getsectiondata( +const mach_header_32_64 *mhp, +const char *segname, +const char *sectname, +unsigned long *size) +{ + segment_command_32_64 *sgp, *zero; + section_32_64 *sp, *find; + uint32_t i, j; + + zero = 0; + find = 0; + sp = 0; + sgp = (segment_command_32_64 *) + ((char *)mhp + sizeof(mach_header_32_64)); + for(i = 0; i < mhp->ncmds; i++){ + if(sgp->cmd == LC_SEGMENT_32_64){ + if(zero == 0 && sgp->fileoff == 0 && sgp->nsects != 0){ + zero = sgp; + if(find != 0) + goto done; + } + if(find == 0 && + strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0){ + sp = (section_32_64 *)((char *)sgp + + sizeof(segment_command_32_64)); + for(j = 0; j < sgp->nsects; j++){ + if(strncmp(sp->sectname, sectname, + sizeof(sp->sectname)) == 0 && + strncmp(sp->segname, segname, + sizeof(sp->segname)) == 0){ + find = sp; + if(zero != 0) + goto done; + } + sp = (section_32_64 *)((char *)sp + + sizeof(section_32_64)); + } + } + } + sgp = (segment_command_32_64 *)((char *)sgp + sgp->cmdsize); + } + return(0); +done: + *size = sp->size; + return((uint8_t *)((uintptr_t)mhp - zero->vmaddr + sp->addr)); +} + +uint8_t * +crashpad_getsegmentdata( +const mach_header_32_64 *mhp, +const char *segname, +unsigned long *size) +{ + segment_command_32_64 *sgp, *zero, *find; + uint32_t i; + + zero = 0; + find = 0; + sgp = (segment_command_32_64 *) + ((char *)mhp + sizeof(mach_header_32_64)); + for(i = 0; i < mhp->ncmds; i++){ + if(sgp->cmd == LC_SEGMENT_32_64){ + if(zero == 0 && sgp->fileoff == 0 && sgp->nsects != 0){ + zero = sgp; + if(find != 0) + goto done; + } + if(find == 0 && + strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0){ + find = sgp; + if(zero != 0) + goto done; + } + } + sgp = (segment_command_32_64 *)((char *)sgp + sgp->cmdsize); + } + return(0); +done: + *size = sgp->vmsize; + return((uint8_t *)((uintptr_t)mhp - zero->vmaddr + sgp->vmaddr)); +} + +#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 */
diff --git a/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE b/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE new file mode 100644 index 0000000..71fe6fd --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE
@@ -0,0 +1,335 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. By +downloading or using this software, you are agreeing to be bound by the terms +of this License. If you do not or cannot agree to the terms of this License, +please do not download or use the software. + +Apple Note: In January 2007, Apple changed its corporate name from "Apple +Computer, Inc." to "Apple Inc." This change has been reflected below and +copyright years updated, but no other changes have been made to the APSL 2.0. + +1. General; Definitions. This License applies to any program or other +work which Apple Inc. ("Apple") makes publicly available and which contains a +notice placed by Apple identifying such program or work as "Original Code" and +stating that it is subject to the terms of this Apple Public Source License +version 2.0 ("License"). As used in this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the +grantor of rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to Apple and (ii) that cover subject matter contained in +the Original Code, but only to the extent necessary to use, reproduce and/or +distribute the Original Code without infringement; and (b) in the case where +You are the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to You and (ii) that cover subject matter in +Your Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or contributes to +the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the combination +of Original Code and any Modifications, and/or any respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise +make Covered Code available, directly or indirectly, to anyone other than You; +and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way +to provide a service, including but not limited to delivery of content, through +electronic communication with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change to, +the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous Modifications, +and/or any respective portions thereof. When code is released as a series of +files, a Modification is: (a) any addition to or deletion from the contents of +a file containing Covered Code; and/or (b) any new file or other representation +of computer program statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other work as +originally made available by Apple under this License, including the Source +Code of any updates or upgrades to such programs or works made available by +Apple under this License, and that has been expressly identified by Apple as +such in the header file(s) of such work; and (b) the object code compiled from +such Source Code and originally made available by Apple under this License + +1.8 "Source Code" means the human readable form of a program or other work +that is suitable for making modifications to it, including all modules it +contains, plus any associated interface definition files, scripts used to +control compilation and installation of an executable (object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising rights +under this License. For legal entities, "You" or "Your" includes any entity +which controls, is controlled by, or is under common control with, You, where +"control" means (a) the power, direct or indirect, to cause the direction or +management of such entity, whether by contract or otherwise, or (b) ownership +of fifty percent (50%) or more of the outstanding shares or beneficial +ownership of such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms and +conditions of this License, Apple hereby grants You, effective on the date You +accept this License and download the Original Code, a world-wide, royalty-free, +non-exclusive license, to the extent of Apple's Applicable Patent Rights and +copyrights covering the Original Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, internally +distribute within Your organization, and Externally Deploy verbatim, unmodified +copies of the Original Code, for commercial or non-commercial purposes, +provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as they appear +in the Original Code, and keep intact all notices in the Original Code that +refer to this License; and + +(b) You must include a copy of this License with every copy of Source Code +of Covered Code and documentation You distribute or Externally Deploy, and You +may not offer or impose any terms on such Source Code that alter or restrict +this License or the recipients' rights hereunder, except as permitted under +Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial or +non-commercial purposes, provided that in each instance You also meet all of +these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to the +Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the notice +in Exhibit A in each file of the Source Code of all Your Modifications, and +cause the modified files to carry prominent notices stating that You changed +the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make Source Code +of all Your Externally Deployed Modifications either available to those to whom +You have Externally Deployed Your Modifications, or publicly available. Source +Code of Your Externally Deployed Modifications must be released under the terms +set forth in this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve (12) +months from the date of initial External Deployment, whichever is longer. You +should preferably distribute the Source Code of Your Externally Deployed +Modifications electronically (e.g. download from a web site). + +2.3 Distribution of Executable Versions. In addition, if You Externally +Deploy Covered Code (Original Code and/or Modifications) in object code, +executable form only, You must include a prominent notice, in the code itself +as well as in related documentation, stating that Source Code of the Covered +Code is available under the terms of this License with information on how and +where to obtain such Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that although +Apple and each Contributor grants the licenses to their respective portions of +the Covered Code set forth herein, no assurances are provided by Apple or any +Contributor that the Covered Code does not infringe the patent or other +intellectual property rights of any other entity. Apple and each Contributor +disclaim any liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, You hereby assume sole +responsibility to secure any other intellectual property rights needed, if any. +For example, if a third party patent license is required to allow You to +distribute the Covered Code, it is Your responsibility to acquire that license +before distributing the Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the licenses +granted to You under this License, You hereby grant to any person or entity +receiving or distributing Covered Code under this License a non-exclusive, +royalty-free, perpetual, irrevocable license, under Your Applicable Patent +Rights and other intellectual property rights (other than patent) owned or +controlled by You, to use, reproduce, display, perform, modify, sublicense, +distribute and Externally Deploy Your Modifications of the same scope and +extent as Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered Code +with other code not governed by the terms of this License and distribute the +Larger Work as a single product. In each such instance, You must make sure the +requirements of this License are fulfilled for the Covered Code or any portion +thereof. + +5. Limitations on Patent License. Except as expressly stated in Section +2, no other patent rights, express or implied, are granted by Apple herein. +Modifications and/or Larger Works may require additional patent licenses from +Apple which Apple may grant in its sole discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other rights +consistent with the scope of the license granted herein ("Additional Terms") to +one or more recipients of Covered Code. However, You may do so only on Your own +behalf and as Your sole responsibility, and not on behalf of Apple or any +Contributor. You must obtain the recipient's agreement that any such Additional +Terms are offered by You alone, and You hereby agree to indemnify, defend and +hold Apple and every Contributor harmless for any liability incurred by or +claims asserted against Apple or such Contributor by reason of any such +Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new versions +of this License from time to time. Each version will be given a distinguishing +version number. Once Original Code has been published under a particular +version of this License, You may continue to use it under the terms of that +version. You may also choose to use such Original Code under the terms of any +subsequent version of this License published by Apple. No one other than Apple +has the right to modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered Code may +contain errors that could cause failures or loss of data, and may be incomplete +or contain inaccuracies. You expressly acknowledge and agree that use of the +Covered Code, or any portion thereof, is at Your sole and entire risk. THE +COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF +ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" +FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF +SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF +QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH +CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE +COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR +REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR +WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED +REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge +that the Covered Code is not intended for use in the operation of nuclear +facilities, aircraft navigation, communication systems, or air traffic control +machines in which case the failure of the Covered Code could lead to death, +personal injury, or severe physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR +YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER +UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS +LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL +PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF +LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT +APPLY TO YOU. In no event shall Apple's total liability to You for all damages +(other than as may be required by applicable law) under this License exceed the +amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Mac", "Mac OS", "QuickTime", "QuickTime +Streaming Server" or any other trademarks, service marks, logos or trade names +belonging to Apple (collectively "Apple Marks") or to any trademark, service +mark, logo or trade name belonging to any Contributor. You agree not to use +any Apple Marks in or as part of the name of products derived from the Original +Code or to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times with +Apple's third party trademark usage guidelines which are posted at +http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, each +Contributor retains all rights, title and interest in and to any Modifications +made by such Contributor. Apple retains all rights, title and interest in and +to the Original Code and any Modifications made by or on behalf of Apple +("Apple Modifications"), and such Apple Modifications will not be automatically +subject to this License. Apple may, at its sole discretion, choose to license +such Apple Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with any +term(s) of this License and fail to cure such breach within 30 days of becoming +aware of such breach; +(b) immediately in the event of the circumstances described in Section +13.5(b); or +(c) automatically without notice from Apple if You, at any time during the +term of this License, commence an action for patent infringement against Apple; +provided that Apple did not first commence an action for patent infringement +against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately stop +any further use, reproduction, modification, sublicensing and distribution of +the Covered Code. All sublicenses to the Covered Code which have been properly +granted prior to termination shall survive any termination of this License. +Provisions which, by their nature, should remain in effect beyond the +termination of this License shall survive, including but not limited to +Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other +for compensation, indemnity or damages of any sort solely as a result of +terminating this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in the +Covered Code include only those rights customarily provided to the public as +defined in this License. This customary commercial license in technical data +and software is provided in accordance with FAR 12.211 (Technical Data) and +12.212 (Computer Software) and, for Department of Defense purchases, DFAR +252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in +Commercial Computer Software or Computer Software Documentation). Accordingly, +all U.S. Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of legal +association between or among You, Apple or any Contributor, and You will not +represent to the contrary, whether expressly, by implication, appearance or +otherwise. + +13.3 Independent Development. Nothing in this License will impair Apple's +right to acquire, license, develop, have others develop for it, market and/or +distribute technology or products that perform the same or similar functions +as, or otherwise compete with, Modifications, Larger Works, technology or +products that You may develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce +any provision of this License will not be deemed a waiver of future enforcement +of that or any other provision. Any law or regulation which provides that the +language of a contract shall be construed against the drafter will not apply to +this License. + +13.5 Severability. (a) If for any reason a court of competent jurisdiction +finds any provision of this License, or portion thereof, to be unenforceable, +that provision of the License will be enforced to the maximum extent +permissible so as to effect the economic benefits and intent of the parties, +and the remainder of this License will continue in full force and effect. (b) +Notwithstanding the foregoing, if applicable law prohibits or restricts You +from fully and/or specifically complying with Sections 2 and/or 3 or prevents +the enforceability of either of those Sections, this License will immediately +terminate and You must immediately discontinue any use of the Covered Code and +destroy all copies of it that are in your possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution between +You and Apple relating to this License shall take place in the Northern +District of California, and You and Apple hereby consent to the personal +jurisdiction of, and venue in, the state and federal courts within that +District with respect to this License. The application of the United Nations +Convention on Contracts for the International Sale of Goods is expressly +excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the entire +agreement between the parties with respect to the subject matter hereof. This +License shall be governed by the laws of the United States and the State of +California, except that body of California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following clause +applies: The parties hereby confirm that they have requested that this License +and all related documents be drafted in English. Les parties ont exigé que le +présent contrat et tous les documents connexes soient rédigés en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. + +This file contains Original Code and/or Modifications of Original Code as +defined in and that are subject to the Apple Public Source License Version 2.0 +(the 'License'). You may not use this file except in compliance with the +License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS +OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT +LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the +specific language governing rights and limitations under the License." +
diff --git a/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h b/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h new file mode 100644 index 0000000..bf12c92 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h
@@ -0,0 +1,205 @@ +/* + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* CFStreamAbstract.h + Copyright (c) 2000-2009, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAMABSTRACT__) +#define __COREFOUNDATION_CFSTREAMABSTRACT__ 1 + +#include <CoreFoundation/CFStream.h> + +CF_EXTERN_C_BEGIN + +/* During a stream's lifetime, the open callback will be called once, followed by any number of openCompleted calls (until openCompleted returns TRUE). Then any number of read/canRead or write/canWrite calls, then a single close call. copyProperty can be called at any time. prepareAsynch will be called exactly once when the stream's client is first configured. + + Expected semantics: + - open reserves any system resources that are needed. The stream may start the process of opening, returning TRUE immediately and setting openComplete to FALSE. When the open completes, _CFStreamSignalEvent should be called passing kCFStreamOpenCompletedEvent. openComplete should be set to TRUE only if the open operation completed in its entirety. + - openCompleted will only be called after open has been called, but before any kCFStreamOpenCompletedEvent has been received. Return TRUE, setting error.code to 0, if the open operation has completed. Return TRUE, setting error to the correct error code and domain if the open operation completed, but failed. Return FALSE if the open operation is still in-progress. If your open ever fails to complete (i.e. sets openComplete to FALSE), you must be implement the openCompleted callback. + - read should read into the given buffer, returning the number of bytes successfully read. read must block until at least one byte is available, but should not block until the entire buffer is filled; zero should only be returned if end-of-stream is encountered. atEOF should be set to true if the EOF is encountered, false otherwise. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values. + - getBuffer is an optimization to return an internal buffer of bytes read from the stream, and may return NULL. getBuffer itself may be NULL if the concrete implementation does not wish to provide an internal buffer. If implemented, it should set numBytesRead to the number of bytes available in the internal buffer (but should not exceed maxBytesToRead) and return a pointer to the base of the bytes. + - canRead will only be called once openCompleted reports that the stream has been successfully opened (or the initial open call succeeded). It should return whether there are bytes that can be read without blocking. + - write should write the bytes in the given buffer to the device, returning the number of bytes successfully written. write must block until at least one byte is written. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values. + - close should close the device, releasing any reserved system resources. close cannot fail (it may be called to abort the stream), and may be called at any time after open has been called. It will only be called once. + - copyProperty should return the value for the given property, or NULL if none exists. Composite streams (streams built on top of other streams) should take care to call CFStreamCopyProperty on the base stream if they do not recognize the property given, to give the underlying stream a chance to respond. + + In all cases, errors returned by reference will be initialized to NULL by the caller, and if they are set to non-NULL, will + be released by the caller +*/ + +typedef struct { + CFIndex version; /* == 2 */ + + void *(*create)(CFReadStreamRef stream, void *info); + void (*finalize)(CFReadStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info); + + Boolean (*open)(CFReadStreamRef stream, CFErrorRef *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFErrorRef *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFErrorRef *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, CFErrorRef *error, void *info); + void (*close)(CFReadStreamRef stream, void *info); + + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + + void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacks; + +typedef struct { + CFIndex version; /* == 2 */ + + void *(*create)(CFWriteStreamRef stream, void *info); + void (*finalize)(CFWriteStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info); + + Boolean (*open)(CFWriteStreamRef stream, CFErrorRef *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFErrorRef *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, CFErrorRef *error, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + + void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacks; + +// Primitive creation mechanisms. +CF_EXPORT +CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const CFReadStreamCallBacks *callbacks, void *info); +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreate(CFAllocatorRef alloc, const CFWriteStreamCallBacks *callbacks, void *info); + +/* All the functions below can only be called when you are sure the stream in question was created via + CFReadStreamCreate() or CFWriteStreamCreate(), above. They are NOT safe for toll-free bridged objects, + so the caller must be sure the argument passed is not such an object. */ + +// To be called by the concrete stream implementation (the callbacks) when an event occurs. error may be NULL if event != kCFStreamEventErrorOccurred +// error should be a CFErrorRef if the callbacks are version 2 or later; otherwise it should be a (CFStreamError *). +CF_EXPORT +void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, const void *error); +CF_EXPORT +void CFWriteStreamSignalEvent(CFWriteStreamRef stream, CFStreamEventType event, const void *error); + +// These require that the stream allow the run loop to run once before delivering the event to its client. +// See the comment above CFRead/WriteStreamSignalEvent for interpretation of the error argument. +CF_EXPORT +void _CFReadStreamSignalEventDelayed(CFReadStreamRef stream, CFStreamEventType event, const void *error); +CF_EXPORT +void _CFWriteStreamSignalEventDelayed(CFWriteStreamRef stream, CFStreamEventType event, const void *error); + +CF_EXPORT +void _CFReadStreamClearEvent(CFReadStreamRef stream, CFStreamEventType event); +// Write variant not currently needed +//CF_EXPORT +//void _CFWriteStreamClearEvent(CFWriteStreamRef stream, CFStreamEventType event); + +// Convenience for concrete implementations to extract the info pointer given the stream. +CF_EXPORT +void *CFReadStreamGetInfoPointer(CFReadStreamRef stream); +CF_EXPORT +void *CFWriteStreamGetInfoPointer(CFWriteStreamRef stream); + +// Returns the client info pointer currently set on the stream. These should probably be made public one day. +CF_EXPORT +void *_CFReadStreamGetClient(CFReadStreamRef readStream); +CF_EXPORT +void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream); + +// Returns an array of the runloops and modes on which the stream is currently scheduled +CF_EXPORT +CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream); +CF_EXPORT +CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream); + +/* Deprecated versions; here for backwards compatibility. */ +typedef struct { + CFIndex version; /* == 1 */ + void *(*create)(CFReadStreamRef stream, void *info); + void (*finalize)(CFReadStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info); + Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, void *info); + void (*close)(CFReadStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacksV1; + +typedef struct { + CFIndex version; /* == 1 */ + void *(*create)(CFWriteStreamRef stream, void *info); + void (*finalize)(CFWriteStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info); + Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacksV1; + +typedef struct { + CFIndex version; /* == 0 */ + Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, void *info); + void (*close)(CFReadStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacksV0; + +typedef struct { + CFIndex version; /* == 0 */ + Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacksV0; + +CF_EXTERN_C_END + +#endif /* ! __COREFOUNDATION_CFSTREAMABSTRACT__ */
diff --git a/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad b/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad new file mode 100644 index 0000000..b6c6cfa --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad
@@ -0,0 +1,15 @@ +Name: Apple CF-Lite +Short Name: CF +URL: https://opensource.apple.com/source/CF/ +URL: https://opensource.apple.com/tarballs/CF/ +Version: 550.43 (from Mac OS X 10.6.8) +License: APSL 2.0 +License File: APPLE_LICENSE +Security Critical: no + +Description: +CF-Lite is an open-source version of the CoreFoundation framework. This +contains non-public but stable header files. + +Local Modifications: + - Only CFStreamAbstract.h is included.
diff --git a/third_party/crashpad/crashpad/third_party/getopt/LICENSE b/third_party/crashpad/crashpad/third_party/getopt/LICENSE new file mode 100644 index 0000000..4444b12 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/getopt/LICENSE
@@ -0,0 +1,5 @@ +Copyright (C) 1997 Gregory Pietsch + +[These files] are hereby placed in the public domain without restrictions. Just +give the author credit, don't claim you wrote it or prevent anyone else from +using it.
diff --git a/third_party/crashpad/crashpad/third_party/getopt/README.crashpad b/third_party/crashpad/crashpad/third_party/getopt/README.crashpad new file mode 100644 index 0000000..9b2b832 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/getopt/README.crashpad
@@ -0,0 +1,15 @@ +Name: Gregory Pietsch getopt +Short Name: getopt +URL: https://sourceware.org/ml/newlib/2005/msg00758.html +License: Public domain +License File: LICENSE +Security Critical: no + +Description: +A public domain implementation of getopt. + +Local Modifications: +- Minor compilation fixes applied for Windows. +- Add copy of copyright (Public domain) to the top of both files for Chromium's + checklicenses step. +- Compiled as .cc, and wrapped in namespace crashpad.
diff --git a/third_party/crashpad/crashpad/third_party/getopt/getopt.cc b/third_party/crashpad/crashpad/third_party/getopt/getopt.cc new file mode 100644 index 0000000..1f6eaff --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/getopt/getopt.cc
@@ -0,0 +1,422 @@ +/* +Copyright (C) 1997 Gregory Pietsch + +[These files] are hereby placed in the public domain without restrictions. Just +give the author credit, don't claim you wrote it or prevent anyone else from +using it. +*/ + +/**************************************************************************** + +getopt.c - Read command line options + +AUTHOR: Gregory Pietsch +CREATED Fri Jan 10 21:13:05 1997 + +DESCRIPTION: + +The getopt() function parses the command line arguments. Its arguments argc +and argv are the argument count and array as passed to the main() function +on program invocation. The argument optstring is a list of available option +characters. If such a character is followed by a colon (`:'), the option +takes an argument, which is placed in optarg. If such a character is +followed by two colons, the option takes an optional argument, which is +placed in optarg. If the option does not take an argument, optarg is NULL. + +The external variable optind is the index of the next array element of argv +to be processed; it communicates from one call to the next which element to +process. + +The getopt_long() function works like getopt() except that it also accepts +long options started by two dashes `--'. If these take values, it is either +in the form + +--arg=value + + or + +--arg value + +It takes the additional arguments longopts which is a pointer to the first +element of an array of type GETOPT_LONG_OPTION_T. The last element of the +array has to be filled with NULL for the name field. + +The longind pointer points to the index of the current long option relative +to longopts if it is non-NULL. + +The getopt() function returns the option character if the option was found +successfully, `:' if there was a missing parameter for one of the options, +`?' for an unknown option character, and EOF for the end of the option list. + +The getopt_long() function's return value is described in the header file. + +The function getopt_long_only() is identical to getopt_long(), except that a +plus sign `+' can introduce long options as well as `--'. + +The following describes how to deal with options that follow non-option +argv-elements. + +If the caller did not specify anything, the default is REQUIRE_ORDER if the +environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. + +REQUIRE_ORDER means don't recognize them as options; stop option processing +when the first non-option is seen. This is what Unix does. This mode of +operation is selected by either setting the environment variable +POSIXLY_CORRECT, or using `+' as the first character of the optstring +parameter. + +PERMUTE is the default. We permute the contents of ARGV as we scan, so that +eventually all the non-options are at the end. This allows options to be +given in any order, even with programs that were not written to expect this. + +RETURN_IN_ORDER is an option available to programs that were written to +expect options and other argv-elements in any order and that care about the +ordering of the two. We describe each non-option argv-element as if it were +the argument of an option with character code 1. Using `-' as the first +character of the optstring parameter selects this mode of operation. + +The special argument `--' forces an end of option-scanning regardless of the +value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause +getopt() and friends to return EOF with optind != argc. + +COPYRIGHT NOTICE AND DISCLAIMER: + +Copyright (C) 1997 Gregory Pietsch + +This file and the accompanying getopt.h header file are hereby placed in the +public domain without restrictions. Just give the author credit, don't +claim you wrote it or prevent anyone else from using it. + +Gregory Pietsch's current e-mail address: +gpietsch@comcast.net +****************************************************************************/ + +/* include files */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef GETOPT_H +#include "getopt.h" +#endif + +namespace crashpad { + +/* macros */ + +/* types */ +typedef enum GETOPT_ORDERING_T +{ + PERMUTE, + RETURN_IN_ORDER, + REQUIRE_ORDER +} GETOPT_ORDERING_T; + +/* globally-defined variables */ +char *optarg = NULL; +int optind = 0; +int opterr = 1; +int optopt = '?'; + +/* functions */ + +/* reverse_argv_elements: reverses num elements starting at argv */ +static void +reverse_argv_elements (char **argv, int num) +{ + int i; + char *tmp; + + for (i = 0; i < (num >> 1); i++) + { + tmp = argv[i]; + argv[i] = argv[num - i - 1]; + argv[num - i - 1] = tmp; + } +} + +/* permute: swap two blocks of argv-elements given their lengths */ +static void +permute (char **argv, int len1, int len2) +{ + reverse_argv_elements (argv, len1); + reverse_argv_elements (argv, len1 + len2); + reverse_argv_elements (argv, len2); +} + +/* is_option: is this argv-element an option or the end of the option list? */ +static int +is_option (char *argv_element, int only) +{ + return ((argv_element == NULL) + || (argv_element[0] == '-') || (only && argv_element[0] == '+')); +} + +/* getopt_internal: the function that does all the dirty work */ +static int +getopt_internal (int argc, char **argv, char *shortopts, + GETOPT_LONG_OPTION_T * longopts, int *longind, int only) +{ + GETOPT_ORDERING_T ordering = PERMUTE; + static size_t optwhere = 0; + size_t permute_from = 0; + int num_nonopts = 0; + int optindex = 0; + size_t match_chars = 0; + char *possible_arg = NULL; + int longopt_match = -1; + int has_arg = -1; + char *cp = NULL; + int arg_next = 0; + + /* first, deal with silly parameters and easy stuff */ + if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL)) + return (optopt = '?'); + if (optind >= argc || argv[optind] == NULL) + return EOF; + if (strcmp (argv[optind], "--") == 0) + { + optind++; + return EOF; + } + /* if this is our first time through */ + if (optind == 0) { + optind = 1; + optwhere = 1; + } + + /* define ordering */ + if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+')) + { + ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER; + shortopts++; + } + else + ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE; + + /* + * based on ordering, find our next option, if we're at the beginning of + * one + */ + if (optwhere == 1) + { + switch (ordering) + { + case PERMUTE: + permute_from = optind; + num_nonopts = 0; + while (!is_option (argv[optind], only)) + { + optind++; + num_nonopts++; + } + if (argv[optind] == NULL) + { + /* no more options */ + optind = (int)permute_from; + return EOF; + } + else if (strcmp (argv[optind], "--") == 0) + { + /* no more options, but have to get `--' out of the way */ + permute (argv + permute_from, num_nonopts, 1); + optind = (int)(permute_from + 1); + return EOF; + } + break; + case RETURN_IN_ORDER: + if (!is_option (argv[optind], only)) + { + optarg = argv[optind++]; + return (optopt = 1); + } + break; + case REQUIRE_ORDER: + if (!is_option (argv[optind], only)) + return EOF; + break; + } + } + /* we've got an option, so parse it */ + + /* first, is it a long option? */ + if (longopts != NULL + && (memcmp (argv[optind], "--", 2) == 0 + || (only && argv[optind][0] == '+')) && optwhere == 1) + { + /* handle long options */ + if (memcmp (argv[optind], "--", 2) == 0) + optwhere = 2; + longopt_match = -1; + possible_arg = strchr (argv[optind] + optwhere, '='); + if (possible_arg == NULL) + { + /* no =, so next argv might be arg */ + match_chars = strlen (argv[optind]); + possible_arg = argv[optind] + match_chars; + match_chars = match_chars - optwhere; + } + else + match_chars = (possible_arg - argv[optind]) - optwhere; + for (optindex = 0; longopts[optindex].name != NULL; optindex++) + { + if (memcmp (argv[optind] + optwhere, + longopts[optindex].name, match_chars) == 0) + { + /* do we have an exact match? */ + if (match_chars == strlen (longopts[optindex].name)) + { + longopt_match = optindex; + break; + } + /* do any characters match? */ + else + { + if (longopt_match < 0) + longopt_match = optindex; + else + { + /* we have ambiguous options */ + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous " + "(could be `--%s' or `--%s')\n", + argv[0], + argv[optind], + longopts[longopt_match].name, + longopts[optindex].name); + return (optopt = '?'); + } + } + } + } + if (longopt_match >= 0) + has_arg = longopts[longopt_match].has_arg; + } + /* if we didn't find a long option, is it a short option? */ + if (longopt_match < 0 && shortopts != NULL) + { + cp = strchr (shortopts, argv[optind][optwhere]); + if (cp == NULL) + { + /* couldn't find option in shortopts */ + if (opterr) + fprintf (stderr, + "%s: invalid option -- `-%c'\n", + argv[0], argv[optind][optwhere]); + optwhere++; + if (argv[optind][optwhere] == '\0') + { + optind++; + optwhere = 1; + } + return (optopt = '?'); + } + has_arg = ((cp[1] == ':') + ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument); + possible_arg = argv[optind] + optwhere + 1; + optopt = *cp; + } + /* get argument and reset optwhere */ + arg_next = 0; + switch (has_arg) + { + case OPTIONAL_ARG: + if (*possible_arg == '=') + possible_arg++; + if (*possible_arg != '\0') + { + optarg = possible_arg; + optwhere = 1; + } + else + optarg = NULL; + break; + case required_argument: + if (*possible_arg == '=') + possible_arg++; + if (*possible_arg != '\0') + { + optarg = possible_arg; + optwhere = 1; + } + else if (optind + 1 >= argc) + { + if (opterr) + { + fprintf (stderr, "%s: argument required for option `", argv[0]); + if (longopt_match >= 0) + fprintf (stderr, "--%s'\n", longopts[longopt_match].name); + else + fprintf (stderr, "-%c'\n", *cp); + } + optind++; + return (optopt = ':'); + } + else + { + optarg = argv[optind + 1]; + arg_next = 1; + optwhere = 1; + } + break; + case no_argument: + if (longopt_match < 0) + { + optwhere++; + if (argv[optind][optwhere] == '\0') + optwhere = 1; + } + else + optwhere = 1; + optarg = NULL; + break; + } + + /* do we have to permute or otherwise modify optind? */ + if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0) + { + permute (argv + permute_from, num_nonopts, 1 + arg_next); + optind = (int)(permute_from + 1 + arg_next); + } + else if (optwhere == 1) + optind = optind + 1 + arg_next; + + /* finally return */ + if (longopt_match >= 0) + { + if (longind != NULL) + *longind = longopt_match; + if (longopts[longopt_match].flag != NULL) + { + *(longopts[longopt_match].flag) = longopts[longopt_match].val; + return 0; + } + else + return longopts[longopt_match].val; + } + else + return optopt; +} + +int +getopt (int argc, char **argv, char *optstring) +{ + return getopt_internal (argc, argv, optstring, NULL, NULL, 0); +} + +int +getopt_long (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind) +{ + return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0); +} + +int +getopt_long_only (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind) +{ + return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1); +} + +} // namespace crashpad + +/* end of file GETOPT.C */
diff --git a/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp b/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp new file mode 100644 index 0000000..5644584 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp
@@ -0,0 +1,35 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../../build/crashpad.gypi', + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'getopt', + 'type': 'static_library', + 'sources': [ + 'getopt.cc', + 'getopt.h', + ], + }, + ], + }, { + 'targets': [] + }] + ], +}
diff --git a/third_party/crashpad/crashpad/third_party/getopt/getopt.h b/third_party/crashpad/crashpad/third_party/getopt/getopt.h new file mode 100644 index 0000000..282a489e --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/getopt/getopt.h
@@ -0,0 +1,63 @@ +/* +Copyright (C) 1997 Gregory Pietsch + +[These files] are hereby placed in the public domain without restrictions. Just +give the author credit, don't claim you wrote it or prevent anyone else from +using it. +*/ + +#ifndef GETOPT_H +#define GETOPT_H + +/* include files needed by this include file */ + +/* macros defined by this include file */ +#define no_argument 0 +#define required_argument 1 +#define OPTIONAL_ARG 2 + +/* types defined by this include file */ + +namespace crashpad { + +/* GETOPT_LONG_OPTION_T: The type of long option */ +typedef struct GETOPT_LONG_OPTION_T +{ + const char *name; /* the name of the long option */ + int has_arg; /* one of the above macros */ + int *flag; /* determines if getopt_long() returns a + * value for a long option; if it is + * non-NULL, 0 is returned as a function + * value and the value of val is stored in + * the area pointed to by flag. Otherwise, + * val is returned. */ + int val; /* determines the value to return if flag is + * NULL. */ +} GETOPT_LONG_OPTION_T; + +typedef GETOPT_LONG_OPTION_T option; + +/* externally-defined variables */ +extern char *optarg; +extern int optind; +extern int opterr; +extern int optopt; + +/* function prototypes */ +int getopt(int argc, char** argv, char* optstring); +int getopt_long(int argc, + char** argv, + const char* shortopts, + const GETOPT_LONG_OPTION_T* longopts, + int* longind); +int getopt_long_only(int argc, + char** argv, + const char* shortopts, + const GETOPT_LONG_OPTION_T* longopts, + int* longind); + +} // namespace crashpad + +#endif /* GETOPT_H */ + +/* END OF FILE getopt.h */
diff --git a/third_party/crashpad/crashpad/third_party/gtest/README.crashpad b/third_party/crashpad/crashpad/third_party/gtest/README.crashpad new file mode 100644 index 0000000..b862eda6 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/gtest/README.crashpad
@@ -0,0 +1,15 @@ +Name: Google Test (googletest) +Short Name: gtest +URL: https://github.com/google/googletest/ +Revision: See DEPS +License: BSD 3-clause +License File: gtest/googletest/LICENSE +Security Critical: no + +Description: +Google Test (Google C++ Testing Framework) is Google’s framework for writing C++ +tests on a variety of platforms. It includes Google Mock, an extension for +writing and using C++ mock classes. + +Local Modifications: +None
diff --git a/third_party/crashpad/crashpad/third_party/gtest/gmock.gyp b/third_party/crashpad/crashpad/third_party/gtest/gmock.gyp new file mode 100644 index 0000000..d553bcfd --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/gtest/gmock.gyp
@@ -0,0 +1,219 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../../build/crashpad_in_chromium.gypi', + ], + 'conditions': [ + ['crashpad_in_chromium==0', { + 'target_defaults': { + # gmock relies heavily on objects with static storage duration. + 'xcode_settings': { + 'WARNING_CFLAGS!': [ + '-Wexit-time-destructors', + ], + }, + 'cflags!': [ + '-Wexit-time-destructors', + ], + }, + + 'targets': [ + { + 'target_name': 'gmock', + 'type': 'static_library', + 'dependencies': [ + 'gtest.gyp:gtest', + ], + 'include_dirs': [ + 'gtest/googlemock', + 'gtest/googlemock/include', + ], + 'sources': [ + 'gtest/googlemock/include/gmock/gmock-actions.h', + 'gtest/googlemock/include/gmock/gmock-cardinalities.h', + 'gtest/googlemock/include/gmock/gmock-generated-actions.h', + 'gtest/googlemock/include/gmock/gmock-generated-function-mockers.h', + 'gtest/googlemock/include/gmock/gmock-generated-matchers.h', + 'gtest/googlemock/include/gmock/gmock-generated-nice-strict.h', + 'gtest/googlemock/include/gmock/gmock-matchers.h', + 'gtest/googlemock/include/gmock/gmock-more-actions.h', + 'gtest/googlemock/include/gmock/gmock-more-matchers.h', + 'gtest/googlemock/include/gmock/gmock-spec-builders.h', + 'gtest/googlemock/include/gmock/gmock.h', + 'gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h', + 'gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h', + 'gtest/googlemock/include/gmock/internal/custom/gmock-port.h', + 'gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h', + 'gtest/googlemock/include/gmock/internal/gmock-internal-utils.h', + 'gtest/googlemock/include/gmock/internal/gmock-port.h', + 'gtest/googlemock/src/gmock-all.cc', + 'gtest/googlemock/src/gmock-cardinalities.cc', + 'gtest/googlemock/src/gmock-internal-utils.cc', + 'gtest/googlemock/src/gmock-matchers.cc', + 'gtest/googlemock/src/gmock-spec-builders.cc', + 'gtest/googlemock/src/gmock.cc', + ], + 'sources!': [ + 'gtest/googlemock/src/gmock-all.cc', + ], + + 'direct_dependent_settings': { + 'include_dirs': [ + 'gtest/googlemock/include', + ], + 'conditions': [ + ['clang!=0', { + # The MOCK_METHODn() macros do not specify “override”, which + # triggers this warning in users: “error: 'Method' overrides a + # member function but is not marked 'override' + # [-Werror,-Winconsistent-missing-override]”. Suppress these + # warnings, and add -Wno-unknown-warning-option because only + # recent versions of clang (trunk r220703 and later, version + # 3.6 and later) recognize it. + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'WARNING_CFLAGS': [ + '-Wno-inconsistent-missing-override', + '-Wno-unknown-warning-option', + ], + }, + }], + ['OS=="linux"', { + 'cflags': [ + '-Wno-inconsistent-missing-override', + '-Wno-unknown-warning-option', + ], + }], + ], + }], + ], + }, + 'export_dependent_settings': [ + 'gtest.gyp:gtest', + ], + }, + { + 'target_name': 'gmock_main', + 'type': 'static_library', + 'dependencies': [ + 'gmock', + 'gtest.gyp:gtest', + ], + 'sources': [ + 'gtest/googlemock/src/gmock_main.cc', + ], + }, + { + 'target_name': 'gmock_test_executable', + 'type': 'none', + 'dependencies': [ + 'gmock', + 'gtest.gyp:gtest', + ], + 'direct_dependent_settings': { + 'type': 'executable', + 'include_dirs': [ + 'gtest/googlemock', + ], + }, + 'export_dependent_settings': [ + 'gmock', + 'gtest.gyp:gtest', + ], + }, + { + 'target_name': 'gmock_all_test', + 'dependencies': [ + 'gmock_test_executable', + 'gmock_main', + ], + 'include_dirs': [ + 'gtest/googletest', + ], + 'sources': [ + 'gtest/googlemock/test/gmock-actions_test.cc', + 'gtest/googlemock/test/gmock-cardinalities_test.cc', + 'gtest/googlemock/test/gmock-generated-actions_test.cc', + 'gtest/googlemock/test/gmock-generated-function-mockers_test.cc', + 'gtest/googlemock/test/gmock-generated-internal-utils_test.cc', + 'gtest/googlemock/test/gmock-generated-matchers_test.cc', + 'gtest/googlemock/test/gmock-internal-utils_test.cc', + 'gtest/googlemock/test/gmock-matchers_test.cc', + 'gtest/googlemock/test/gmock-more-actions_test.cc', + 'gtest/googlemock/test/gmock-nice-strict_test.cc', + 'gtest/googlemock/test/gmock-port_test.cc', + 'gtest/googlemock/test/gmock-spec-builders_test.cc', + 'gtest/googlemock/test/gmock_test.cc', + ], + }, + { + 'target_name': 'gmock_link_test', + 'dependencies': [ + 'gmock_test_executable', + 'gmock_main', + ], + 'sources': [ + 'gtest/googlemock/test/gmock_link_test.cc', + 'gtest/googlemock/test/gmock_link_test.h', + 'gtest/googlemock/test/gmock_link2_test.cc', + ], + }, + { + 'target_name': 'gmock_stress_test', + 'dependencies': [ + 'gmock_test_executable', + ], + 'sources': [ + 'gtest/googlemock/test/gmock_stress_test.cc', + ], + }, + { + 'target_name': 'gmock_all_tests', + 'type': 'none', + 'dependencies': [ + 'gmock_all_test', + 'gmock_link_test', + 'gmock_stress_test', + ], + }, + ], + }, { # else: crashpad_in_chromium!=0 + 'targets': [ + { + 'target_name': 'gmock', + 'type': 'none', + 'dependencies': [ + '<(DEPTH)/testing/gmock.gyp:gmock', + ], + 'export_dependent_settings': [ + '<(DEPTH)/testing/gmock.gyp:gmock', + ], + }, + { + 'target_name': 'gmock_main', + 'type': 'none', + 'dependencies': [ + '<(DEPTH)/testing/gmock.gyp:gmock_main', + ], + 'export_dependent_settings': [ + '<(DEPTH)/testing/gmock.gyp:gmock_main', + ], + }, + ], + }], + ], +}
diff --git a/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp b/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp new file mode 100644 index 0000000..2ac6ca20 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp
@@ -0,0 +1,264 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../../build/crashpad_in_chromium.gypi', + ], + 'conditions': [ + ['crashpad_in_chromium==0', { + 'target_defaults': { + # gtest relies heavily on objects with static storage duration. + 'xcode_settings': { + 'WARNING_CFLAGS!': [ + '-Wexit-time-destructors', + ], + }, + 'cflags!': [ + '-Wexit-time-destructors', + ], + }, + + 'targets': [ + { + 'target_name': 'gtest', + 'type': 'static_library', + 'include_dirs': [ + 'gtest/googletest', + 'gtest/googletest/include', + ], + 'sources': [ + 'gtest/googletest/include/gtest/gtest-death-test.h', + 'gtest/googletest/include/gtest/gtest-message.h', + 'gtest/googletest/include/gtest/gtest-param-test.h', + 'gtest/googletest/include/gtest/gtest-printers.h', + 'gtest/googletest/include/gtest/gtest-spi.h', + 'gtest/googletest/include/gtest/gtest-test-part.h', + 'gtest/googletest/include/gtest/gtest-typed-test.h', + 'gtest/googletest/include/gtest/gtest.h', + 'gtest/googletest/include/gtest/gtest_pred_impl.h', + 'gtest/googletest/include/gtest/gtest_prod.h', + 'gtest/googletest/include/gtest/internal/custom/gtest-port.h', + 'gtest/googletest/include/gtest/internal/custom/gtest-printers.h', + 'gtest/googletest/include/gtest/internal/custom/gtest.h', + 'gtest/googletest/include/gtest/internal/gtest-death-test-internal.h', + 'gtest/googletest/include/gtest/internal/gtest-filepath.h', + 'gtest/googletest/include/gtest/internal/gtest-internal.h', + 'gtest/googletest/include/gtest/internal/gtest-linked_ptr.h', + 'gtest/googletest/include/gtest/internal/gtest-param-util-generated.h', + 'gtest/googletest/include/gtest/internal/gtest-param-util.h', + 'gtest/googletest/include/gtest/internal/gtest-port-arch.h', + 'gtest/googletest/include/gtest/internal/gtest-port.h', + 'gtest/googletest/include/gtest/internal/gtest-string.h', + 'gtest/googletest/include/gtest/internal/gtest-tuple.h', + 'gtest/googletest/include/gtest/internal/gtest-type-util.h', + 'gtest/googletest/src/gtest-all.cc', + 'gtest/googletest/src/gtest-death-test.cc', + 'gtest/googletest/src/gtest-filepath.cc', + 'gtest/googletest/src/gtest-internal-inl.h', + 'gtest/googletest/src/gtest-port.cc', + 'gtest/googletest/src/gtest-printers.cc', + 'gtest/googletest/src/gtest-test-part.cc', + 'gtest/googletest/src/gtest-typed-test.cc', + 'gtest/googletest/src/gtest.cc', + ], + 'sources!': [ + 'gtest/googletest/src/gtest-all.cc', + ], + + 'direct_dependent_settings': { + 'include_dirs': [ + 'gtest/googletest/include', + ], + }, + }, + { + 'target_name': 'gtest_main', + 'type': 'static_library', + 'dependencies': [ + 'gtest', + ], + 'sources': [ + 'gtest/googletest/src/gtest_main.cc', + ], + }, + { + 'target_name': 'gtest_test_executable', + 'type': 'none', + 'dependencies': [ + 'gtest', + ], + 'direct_dependent_settings': { + 'type': 'executable', + 'include_dirs': [ + 'gtest/googletest', + ], + }, + 'export_dependent_settings': [ + 'gtest', + ], + }, + { + 'target_name': 'gtest_all_test', + 'dependencies': [ + 'gtest_test_executable', + 'gtest_main', + ], + 'sources': [ + 'gtest/googletest/test/gtest-death-test_test.cc', + 'gtest/googletest/test/gtest-filepath_test.cc', + 'gtest/googletest/test/gtest-linked_ptr_test.cc', + 'gtest/googletest/test/gtest-message_test.cc', + 'gtest/googletest/test/gtest-options_test.cc', + 'gtest/googletest/test/gtest-port_test.cc', + 'gtest/googletest/test/gtest-printers_test.cc', + 'gtest/googletest/test/gtest-test-part_test.cc', + 'gtest/googletest/test/gtest-typed-test2_test.cc', + 'gtest/googletest/test/gtest-typed-test_test.cc', + 'gtest/googletest/test/gtest-typed-test_test.h', + 'gtest/googletest/test/gtest_main_unittest.cc', + 'gtest/googletest/test/gtest_pred_impl_unittest.cc', + 'gtest/googletest/test/gtest_prod_test.cc', + 'gtest/googletest/test/gtest_unittest.cc', + 'gtest/googletest/test/production.cc', + 'gtest/googletest/test/production.h', + ], + }, + { + 'target_name': 'gtest_environment_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest_environment_test.cc', + ], + }, + { + 'target_name': 'gtest_listener_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest-listener_test.cc', + ], + }, + { + 'target_name': 'gtest_no_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest_no_test_unittest.cc', + ], + }, + { + 'target_name': 'gtest_param_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest-param-test2_test.cc', + 'gtest/googletest/test/gtest-param-test_test.cc', + 'gtest/googletest/test/gtest-param-test_test.h', + ], + }, + { + 'target_name': 'gtest_premature_exit_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest_premature_exit_test.cc', + ], + }, + { + 'target_name': 'gtest_repeat_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest_repeat_test.cc', + ], + }, + { + 'target_name': 'gtest_sole_header_test', + 'dependencies': [ + 'gtest_test_executable', + 'gtest_main', + ], + 'sources': [ + 'gtest/googletest/test/gtest_sole_header_test.cc', + ], + }, + { + 'target_name': 'gtest_stress_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest_stress_test.cc', + ], + }, + { + 'target_name': 'gtest_unittest_api_test', + 'dependencies': [ + 'gtest_test_executable', + ], + 'sources': [ + 'gtest/googletest/test/gtest-unittest-api_test.cc', + ], + }, + { + 'target_name': 'gtest_all_tests', + 'type': 'none', + 'dependencies': [ + 'gtest_all_test', + 'gtest_environment_test', + 'gtest_listener_test', + 'gtest_no_test', + 'gtest_param_test', + 'gtest_premature_exit_test', + 'gtest_repeat_test', + 'gtest_sole_header_test', + 'gtest_stress_test', + 'gtest_unittest_api_test', + ], + }, + ], + }, { # else: crashpad_in_chromium!=0 + 'targets': [ + { + 'target_name': 'gtest', + 'type': 'none', + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'export_dependent_settings': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + }, + { + 'target_name': 'gtest_main', + 'type': 'none', + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest_main', + ], + 'export_dependent_settings': [ + '<(DEPTH)/testing/gtest.gyp:gtest_main', + ], + }, + ], + }], + ], +}
diff --git a/third_party/crashpad/crashpad/third_party/gyp/README.crashpad b/third_party/crashpad/crashpad/third_party/gyp/README.crashpad new file mode 100644 index 0000000..18eb1649 --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/gyp/README.crashpad
@@ -0,0 +1,13 @@ +Name: GYP (Generate Your Projects) +Short Name: gyp +URL: https://gyp.gsrc.io/ +Revision: See DEPS +License: BSD 3-clause +License File: gyp/LICENSE +Security Critical: no + +Description: +GYP is used to generate build files. + +Local Modifications: +None
diff --git a/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad b/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad new file mode 100644 index 0000000..1fa3c4d --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad
@@ -0,0 +1,17 @@ +Name: mini_chromium +Short Name: mini_chromium +URL: https://chromium.googlesource.com/chromium/mini_chromium/ +Revision: See DEPS +License: BSD 3-clause +License File: mini_chromium/LICENSE +Security Critical: yes + +Description: +mini_chromium is a small collection of useful low-level (“base”) routines from +the Chromium open-source project at http://www.chromium.org/. Chromium is +large, sprawling, full of dependencies, and a web browser. mini_chromium is +small, self-contained, and a library. mini_chromium is especially useful as a +dependency of other code that wishes to use Chromium’s base routines. + +Local Modifications: +None
diff --git a/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp b/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp new file mode 100644 index 0000000..56f897d --- /dev/null +++ b/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp
@@ -0,0 +1,46 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../../build/crashpad_in_chromium.gypi', + ], + 'targets': [ + { + # To support both Crashpad’s standalone build and its in-Chromium build, + # Crashpad code depending on base should do so through this shim, which + # will either get base from mini_chromium or Chromium depending on the + # build type. + 'target_name': 'base', + 'type': 'none', + 'conditions': [ + ['crashpad_in_chromium==0', { + 'dependencies': [ + 'mini_chromium/base/base.gyp:base', + ], + 'export_dependent_settings': [ + 'mini_chromium/base/base.gyp:base', + ], + }, { # else: crashpad_in_chromium!=0 + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + ], + 'export_dependent_settings': [ + '<(DEPTH)/base/base.gyp:base', + ], + }], + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/tools/crashpad_database_util.ad b/third_party/crashpad/crashpad/tools/crashpad_database_util.ad new file mode 100644 index 0000000..78107ee3 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/crashpad_database_util.ad
@@ -0,0 +1,163 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += crashpad_database_util(1) + +== Name + +crashpad_database_util - Operate on Crashpad crash report databases + +== Synopsis + +[verse] +*crashpad_database_util* ['OPTION…'] + +== Description + +Operates on Crashpad crash report databases. The database’s settings can be +queried and modified, and information about crash reports stored in the +database can be displayed. + +When this program is requested to both show and set information in a single +invocation, all “show” operations will be completed prior to beginning any “set” +operation. + +Programs that use the Crashpad client library directly will not normally use +this tool, but may use the database through the programmatic interfaces in the +client library. This tool exists to allow developers to manipulate a Crashpad +database. + +== Options +*--create*:: +Creates the database identified by *--database* if it does not exist, provided +that the parent directory of 'PATH' exists. + +*-d*, *--database*='PATH':: +Use 'PATH' as the path to the Crashpad crash report database. This option is +required. The database must already exist unless *--create* is also specified. + +*--show-client-id*:: +Show the client ID stored in the database’s settings. The client ID is formatted +as a UUID. The client ID is set when the database is created. +man_link:crashpad_handler[8] retrieves the client ID and stores it in crash +reports as they are written. + +*--show-uploads-enabled*:: +Show the status of the uploads-enabled bit stored in the database’s settings. +man_link:crashpad_handler[8] does not upload reports when this bit is false. +This bit is false when a database is created, and is under an application’s +control via the Crashpad client library interface. ++ +See also *--set-uploads-enabled*. + +*--show-last-upload-attempt-time*:: +Show the last-upload-attempt time stored in the database’s settings. This value +is +0+, meaning “never,” when the database is created. +man_link:crashpad_handler[8] consults this value before attempting an upload to +implement its rate-limiting behavior. The database updates this value whenever +an upload is attempted. ++ +See also *--set-last-upload-attempt-time*. + +*--show-pending-reports*:: +Show reports eligible for upload. + +*--show-completed-reports*:: +Show reports not eligible for upload. A report is moved from the “pending” state +to the “completed” state by man_link:crashpad_handler[8]. This may happen when a +report is successfully uploaded, when a report is not uploaded because uploads +are disabled, or when a report upload attempt fails and will not be retried. + +*--show-all-report-info*:: +With *--show-pending-reports* or *--show-completed-reports*, show all metadata +for each report displayed. Without this option, only report IDs will be shown. + +*--show-report*='UUID':: +Show a report from the database looked up by its identifier, 'UUID', which must +be formatted in string representation per RFC 4122 §3. All metadata for each +report found via a *--show-report* option will be shown. If 'UUID' is not found, +the string +"not found"+ will be printed. If this program is only requested to +show a single report and it is not found, it will treat this as a failure for +the purposes of determining its exit status. This option may appear multiple +times. + +*--set-report-uploads-enabled*='BOOL':: +Enable or disable report upload in the database’s settings. 'BOOL' is a string +representation of a boolean value, such as +"0"+ or +"true"+. ++ +See also *--show-uploads-enabled*. + +*--set-last-upload-attempt-time*='TIME':: +Set the last-upload-attempt time in the database’s settings. 'TIME' is a string +representation of a time, which may be in 'yyyy-mm-dd hh:mm:ss' format, a +numeric +time_t+ value, or the special string +"never"+. ++ +See also *--show-last-upload-attempt-time*. + +*--new-report*='PATH':: +Submit a new report located at 'PATH' to the database. If 'PATH' is +"-"+, the +new report will be read from standard input. The new report will be in the +“pending” state. The UUID assigned to the new report will be printed. This +option may appear multiple times. + +*--utc*:: +When showing times, do so in UTC as opposed to the local time zone. When setting +times, interpret ambiguous time strings in UTC as opposed to the local time +zone. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Shows all crash reports in a crash report database that are in the “completed” +state. +[subs="quotes"] +---- +$ *crashpad_database_util --database /tmp/crashpad_database \ + --show-completed-reports* +23f9512b-63e1-4ead-9dcd-e2e21fbccc68 +4bfca440-039f-4bc6-bbd4-6933cef5efd4 +56caeff8-b61a-43b2-832d-9e796e6e4a50 +---- + +Disables report upload in a crash report database’s settings, and then verifies +that the change was made. +[subs="quotes"] +---- +$ *crashpad_database_util --database /tmp/crashpad_database \ + --set-uploads-enabled false* +$ *crashpad_database_util --database /tmp/crashpad_database \ + --show-uploads-enabled* +false +---- + +== Exit Status + +*0*:: +Success. + +*1*:: +Failure, with a message printed to the standard error stream. + +== See Also + +man_link:crashpad_handler[8] + +include::../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/tools/crashpad_database_util.cc b/third_party/crashpad/crashpad/tools/crashpad_database_util.cc new file mode 100644 index 0000000..50666f2 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/crashpad_database_util.cc
@@ -0,0 +1,615 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <time.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/crash_report_database.h" +#include "client/settings.h" +#include "tools/tool_support.h" +#include "util/file/file_io.h" +#include "util/file/file_reader.h" +#include "util/stdlib/move.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace { + +void Usage(const base::FilePath& me) { + fprintf(stderr, +"Usage: %" PRFilePath " [OPTION]... PID\n" +"Operate on Crashpad crash report databases.\n" +"\n" +" --create allow database at PATH to be created\n" +" -d, --database=PATH operate on the crash report database at PATH\n" +" --show-client-id show the client ID\n" +" --show-uploads-enabled show whether uploads are enabled\n" +" --show-last-upload-attempt-time\n" +" show the last-upload-attempt time\n" +" --show-pending-reports show reports eligible for upload\n" +" --show-completed-reports show reports not eligible for upload\n" +" --show-all-report-info with --show-*-reports, show more information\n" +" --show-report=UUID show report stored under UUID\n" +" --set-uploads-enabled=BOOL enable or disable uploads\n" +" --set-last-upload-attempt-time=TIME\n" +" set the last-upload-attempt time to TIME\n" +" --new-report=PATH submit a new report at PATH, or - for stdin\n" +" --utc show and set UTC times instead of local\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.value().c_str()); + ToolSupport::UsageTail(me); +} + +struct Options { + std::vector<UUID> show_reports; + std::vector<base::FilePath> new_report_paths; + const char* database; + const char* set_last_upload_attempt_time_string; + time_t set_last_upload_attempt_time; + bool create; + bool show_client_id; + bool show_uploads_enabled; + bool show_last_upload_attempt_time; + bool show_pending_reports; + bool show_completed_reports; + bool show_all_report_info; + bool set_uploads_enabled; + bool has_set_uploads_enabled; + bool utc; +}; + +// Converts |string| to |boolean|, returning true if a conversion could be +// performed, and false without setting |boolean| if no conversion could be +// performed. Various string representations of a boolean are recognized +// case-insensitively. +bool StringToBool(const char* string, bool* boolean) { + const char* const kFalseWords[] = { + "0", + "false", + "no", + "off", + "disabled", + "clear", + }; + const char* const kTrueWords[] = { + "1", + "true", + "yes", + "on", + "enabled", + "set", + }; + + for (size_t index = 0; index < arraysize(kFalseWords); ++index) { + if (strcasecmp(string, kFalseWords[index]) == 0) { + *boolean = false; + return true; + } + } + + for (size_t index = 0; index < arraysize(kTrueWords); ++index) { + if (strcasecmp(string, kTrueWords[index]) == 0) { + *boolean = true; + return true; + } + } + + return false; +} + +// Converts |boolean| to a string, either "true" or "false". +std::string BoolToString(bool boolean) { + return std::string(boolean ? "true" : "false"); +} + +// Converts |string| to |time|, returning true if a conversion could be +// performed, and false without setting |boolean| if no conversion could be +// performed. Various time formats are recognized, including several string +// representations and a numeric time_t representation. The special string +// "never" is recognized as |string| and converts to a |time| value of 0. |utc|, +// when true, causes |string| to be interpreted as a UTC time rather than a +// local time when the time zone is ambiguous. +bool StringToTime(const char* string, time_t* time, bool utc) { + if (strcasecmp(string, "never") == 0) { + *time = 0; + return true; + } + + const char* end = string + strlen(string); + + const char* const kFormats[] = { + "%Y-%m-%d %H:%M:%S %Z", + "%Y-%m-%d %H:%M:%S", + "%+", + }; + + for (size_t index = 0; index < arraysize(kFormats); ++index) { + tm time_tm; + const char* strptime_result = strptime(string, kFormats[index], &time_tm); + if (strptime_result == end) { + if (utc) { + *time = timegm(&time_tm); + } else { + *time = mktime(&time_tm); + } + + return true; + } + } + + char* end_result; + errno = 0; + long long strtoll_result = strtoll(string, &end_result, 0); + if (end_result == end && errno == 0 && + base::IsValueInRangeForNumericType<time_t>(strtoll_result)) { + *time = strtoll_result; + return true; + } + + return false; +} + +// Converts |time_tt| to a string, and returns it. |utc| determines whether the +// converted time will reference local time or UTC. If |time_tt| is 0, the +// string "never" will be returned as a special case. +std::string TimeToString(time_t time_tt, bool utc) { + if (time_tt == 0) { + return std::string("never"); + } + + tm time_tm; + if (utc) { + gmtime_r(&time_tt, &time_tm); + } else { + localtime_r(&time_tt, &time_tm); + } + + char string[64]; + CHECK_NE( + strftime(string, arraysize(string), "%Y-%m-%d %H:%M:%S %Z", &time_tm), + 0u); + + return std::string(string); +} + +// Shows information about a single |report|. |space_count| is the number of +// spaces to print before each line that is printed. |utc| determines whether +// times should be shown in UTC or the local time zone. +void ShowReport(const CrashReportDatabase::Report& report, + size_t space_count, + bool utc) { + std::string spaces(space_count, ' '); + + printf("%sPath: %" PRFilePath "\n", + spaces.c_str(), + report.file_path.value().c_str()); + if (!report.id.empty()) { + printf("%sRemote ID: %s\n", spaces.c_str(), report.id.c_str()); + } + printf("%sCreation time: %s\n", + spaces.c_str(), + TimeToString(report.creation_time, utc).c_str()); + printf("%sUploaded: %s\n", + spaces.c_str(), + BoolToString(report.uploaded).c_str()); + printf("%sLast upload attempt time: %s\n", + spaces.c_str(), + TimeToString(report.last_upload_attempt_time, utc).c_str()); + printf("%sUpload attempts: %d\n", spaces.c_str(), report.upload_attempts); +} + +// Shows information about a vector of |reports|. |space_count| is the number of +// spaces to print before each line that is printed. |options| will be consulted +// to determine whether to show expanded information +// (options.show_all_report_info) and what time zone to use when showing +// expanded information (options.utc). +void ShowReports(const std::vector<CrashReportDatabase::Report>& reports, + size_t space_count, + const Options& options) { + std::string spaces(space_count, ' '); + const char* colon = options.show_all_report_info ? ":" : ""; + + for (const CrashReportDatabase::Report& report : reports) { + printf("%s%s%s\n", spaces.c_str(), report.uuid.ToString().c_str(), colon); + if (options.show_all_report_info) { + ShowReport(report, space_count + 2, options.utc); + } + } +} + +int DatabaseUtilMain(int argc, char* argv[]) { + const base::FilePath argv0( + ToolSupport::CommandLineArgumentToFilePathStringType(argv[0])); + const base::FilePath me(argv0.BaseName()); + + enum OptionFlags { + // “Short” (single-character) options. + kOptionDatabase = 'd', + + // Long options without short equivalents. + kOptionLastChar = 255, + kOptionCreate, + kOptionShowClientID, + kOptionShowUploadsEnabled, + kOptionShowLastUploadAttemptTime, + kOptionShowPendingReports, + kOptionShowCompletedReports, + kOptionShowAllReportInfo, + kOptionShowReport, + kOptionSetUploadsEnabled, + kOptionSetLastUploadAttemptTime, + kOptionNewReport, + kOptionUTC, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + const option long_options[] = { + {"create", no_argument, nullptr, kOptionCreate}, + {"database", required_argument, nullptr, kOptionDatabase}, + {"show-client-id", no_argument, nullptr, kOptionShowClientID}, + {"show-uploads-enabled", no_argument, nullptr, kOptionShowUploadsEnabled}, + {"show-last-upload-attempt-time", + no_argument, + nullptr, + kOptionShowLastUploadAttemptTime}, + {"show-pending-reports", no_argument, nullptr, kOptionShowPendingReports}, + {"show-completed-reports", + no_argument, + nullptr, + kOptionShowCompletedReports}, + {"show-all-report-info", no_argument, nullptr, kOptionShowAllReportInfo}, + {"show-report", required_argument, nullptr, kOptionShowReport}, + {"set-uploads-enabled", + required_argument, + nullptr, + kOptionSetUploadsEnabled}, + {"set-last-upload-attempt-time", + required_argument, + nullptr, + kOptionSetLastUploadAttemptTime}, + {"new-report", required_argument, nullptr, kOptionNewReport}, + {"utc", no_argument, nullptr, kOptionUTC}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + Options options = {}; + + int opt; + while ((opt = getopt_long(argc, argv, "d:", long_options, nullptr)) != -1) { + switch (opt) { + case kOptionCreate: { + options.create = true; + break; + } + case kOptionDatabase: { + options.database = optarg; + break; + } + case kOptionShowClientID: { + options.show_client_id = true; + break; + } + case kOptionShowUploadsEnabled: { + options.show_uploads_enabled = true; + break; + } + case kOptionShowLastUploadAttemptTime: { + options.show_last_upload_attempt_time = true; + break; + } + case kOptionShowPendingReports: { + options.show_pending_reports = true; + break; + } + case kOptionShowCompletedReports: { + options.show_completed_reports = true; + break; + } + case kOptionShowAllReportInfo: { + options.show_all_report_info = true; + break; + } + case kOptionShowReport: { + UUID uuid; + if (!uuid.InitializeFromString(optarg)) { + ToolSupport::UsageHint(me, "--show-report requires a UUID"); + return EXIT_FAILURE; + } + options.show_reports.push_back(uuid); + break; + } + case kOptionSetUploadsEnabled: { + if (!StringToBool(optarg, &options.set_uploads_enabled)) { + ToolSupport::UsageHint(me, "--set-uploads-enabled requires a BOOL"); + return EXIT_FAILURE; + } + options.has_set_uploads_enabled = true; + break; + } + case kOptionSetLastUploadAttemptTime: { + options.set_last_upload_attempt_time_string = optarg; + break; + } + case kOptionNewReport: { + options.new_report_paths.push_back(base::FilePath( + ToolSupport::CommandLineArgumentToFilePathStringType(optarg))); + break; + } + case kOptionUTC: { + options.utc = true; + break; + } + case kOptionHelp: { + Usage(me); + return EXIT_SUCCESS; + } + case kOptionVersion: { + ToolSupport::Version(me); + return EXIT_SUCCESS; + } + default: { + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + } + } + argc -= optind; + argv += optind; + + if (!options.database) { + ToolSupport::UsageHint(me, "--database is required"); + return EXIT_FAILURE; + } + + // This conversion couldn’t happen in the option-processing loop above because + // it depends on options.utc, which may have been set after + // options.set_last_upload_attempt_time_string. + if (options.set_last_upload_attempt_time_string) { + if (!StringToTime(options.set_last_upload_attempt_time_string, + &options.set_last_upload_attempt_time, + options.utc)) { + ToolSupport::UsageHint(me, + "--set-last-upload-attempt-time requires a TIME"); + return EXIT_FAILURE; + } + } + + // --new-report is treated as a show operation because it produces output. + const size_t show_operations = options.show_client_id + + options.show_uploads_enabled + + options.show_last_upload_attempt_time + + options.show_pending_reports + + options.show_completed_reports + + options.show_reports.size() + + options.new_report_paths.size(); + const size_t set_operations = + options.has_set_uploads_enabled + + (options.set_last_upload_attempt_time_string != nullptr); + + if ((options.create ? 1 : 0) + show_operations + set_operations == 0) { + ToolSupport::UsageHint(me, "nothing to do"); + return EXIT_FAILURE; + } + + scoped_ptr<CrashReportDatabase> database; + base::FilePath database_path = base::FilePath( + ToolSupport::CommandLineArgumentToFilePathStringType(options.database)); + if (options.create) { + database = CrashReportDatabase::Initialize(database_path); + } else { + database = CrashReportDatabase::InitializeWithoutCreating(database_path); + } + if (!database) { + return EXIT_FAILURE; + } + + Settings* settings = database->GetSettings(); + + // Handle the “show” options before the “set” options so that when they’re + // specified together, the “show” option reflects the initial state. + + if (options.show_client_id) { + UUID client_id; + if (!settings->GetClientID(&client_id)) { + return EXIT_FAILURE; + } + + const char* prefix = (show_operations > 1) ? "Client ID: " : ""; + + printf("%s%s\n", prefix, client_id.ToString().c_str()); + } + + if (options.show_uploads_enabled) { + bool uploads_enabled; + if (!settings->GetUploadsEnabled(&uploads_enabled)) { + return EXIT_FAILURE; + } + + const char* prefix = (show_operations > 1) ? "Uploads enabled: " : ""; + + printf("%s%s\n", prefix, BoolToString(uploads_enabled).c_str()); + } + + if (options.show_last_upload_attempt_time) { + time_t last_upload_attempt_time; + if (!settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) { + return EXIT_FAILURE; + } + + const char* prefix = + (show_operations > 1) ? "Last upload attempt time: " : ""; + + printf("%s%s (%ld)\n", + prefix, + TimeToString(last_upload_attempt_time, options.utc).c_str(), + static_cast<long>(last_upload_attempt_time)); + } + + if (options.show_pending_reports) { + std::vector<CrashReportDatabase::Report> pending_reports; + if (database->GetPendingReports(&pending_reports) != + CrashReportDatabase::kNoError) { + return EXIT_FAILURE; + } + + if (show_operations > 1) { + printf("Pending reports:\n"); + } + + ShowReports(pending_reports, show_operations > 1 ? 2 : 0, options); + } + + if (options.show_completed_reports) { + std::vector<CrashReportDatabase::Report> completed_reports; + if (database->GetCompletedReports(&completed_reports) != + CrashReportDatabase::kNoError) { + return EXIT_FAILURE; + } + + if (show_operations > 1) { + printf("Completed reports:\n"); + } + + ShowReports(completed_reports, show_operations > 1 ? 2 : 0, options); + } + + for (const UUID& uuid : options.show_reports) { + CrashReportDatabase::Report report; + CrashReportDatabase::OperationStatus status = + database->LookUpCrashReport(uuid, &report); + if (status == CrashReportDatabase::kNoError) { + if (show_operations > 1) { + printf("Report %s:\n", uuid.ToString().c_str()); + } + ShowReport(report, show_operations > 1 ? 2 : 0, options.utc); + } else if (status == CrashReportDatabase::kReportNotFound) { + // If only asked to do one thing, a failure to find the single requested + // report should result in a failure exit status. + if (show_operations + set_operations == 1) { + fprintf( + stderr, "%" PRFilePath ": Report not found\n", me.value().c_str()); + return EXIT_FAILURE; + } + printf("Report %s not found\n", uuid.ToString().c_str()); + } else { + return EXIT_FAILURE; + } + } + + if (options.has_set_uploads_enabled && + !settings->SetUploadsEnabled(options.set_uploads_enabled)) { + return EXIT_FAILURE; + } + + if (options.set_last_upload_attempt_time_string && + !settings->SetLastUploadAttemptTime( + options.set_last_upload_attempt_time)) { + return EXIT_FAILURE; + } + + for (const base::FilePath new_report_path : options.new_report_paths) { + scoped_ptr<FileReaderInterface> file_reader; + + bool is_stdin = false; + if (new_report_path.value() == FILE_PATH_LITERAL("-")) { + is_stdin = true; + file_reader.reset(new WeakStdioFileReader(stdin)); + } else { + scoped_ptr<FileReader> file_path_reader(new FileReader()); + if (!file_path_reader->Open(new_report_path)) { + return EXIT_FAILURE; + } + + file_reader = crashpad::move(file_path_reader); + } + + CrashReportDatabase::NewReport* new_report; + CrashReportDatabase::OperationStatus status = + database->PrepareNewCrashReport(&new_report); + if (status != CrashReportDatabase::kNoError) { + return EXIT_FAILURE; + } + + CrashReportDatabase::CallErrorWritingCrashReport + call_error_writing_crash_report(database.get(), new_report); + + char buf[4096]; + FileOperationResult read_result; + do { + read_result = file_reader->Read(buf, sizeof(buf)); + if (read_result < 0) { + return EXIT_FAILURE; + } + if (read_result > 0 && + !LoggingWriteFile(new_report->handle, buf, read_result)) { + return EXIT_FAILURE; + } + } while (read_result == sizeof(buf)); + + call_error_writing_crash_report.Disarm(); + + UUID uuid; + status = database->FinishedWritingCrashReport(new_report, &uuid); + if (status != CrashReportDatabase::kNoError) { + return EXIT_FAILURE; + } + + file_reader.reset(); + if (is_stdin) { + if (fclose(stdin) == EOF) { + STDIO_PLOG(ERROR) << "fclose"; + } + } + + const char* prefix = (show_operations > 1) ? "New report ID: " : ""; + printf("%s%s\n", prefix, uuid.ToString().c_str()); + } + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +#if defined(OS_POSIX) +int main(int argc, char* argv[]) { + return crashpad::DatabaseUtilMain(argc, argv); +} +#elif defined(OS_WIN) +int wmain(int argc, wchar_t* argv[]) { + return crashpad::ToolSupport::Wmain(argc, argv, crashpad::DatabaseUtilMain); +} +#endif // OS_POSIX
diff --git a/third_party/crashpad/crashpad/tools/generate_dump.ad b/third_party/crashpad/crashpad/tools/generate_dump.ad new file mode 100644 index 0000000..d8a53c8d --- /dev/null +++ b/third_party/crashpad/crashpad/tools/generate_dump.ad
@@ -0,0 +1,96 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += generate_dump(1) + +== Name + +generate_dump - Generate a minidump file containing a snapshot of a running +process + +== Synopsis + +[verse] +*generate_dump* ['OPTION…'] 'PID' + +== Description + +Generates a minidump file containing a snapshot of a running process whose +process identifier is 'PID'. By default, the target process will be suspended +while the minidump is generated, and the minidump file will be written to ++minidump.PID+. After the minidump file is generated, the target process resumes +running. + +The minidump file will contain information about the process, its threads, its +modules, and the system. It will not contain any exception information because +it will be generated from a live running process, not as a result of an +exception occurring. + +This program uses +task_for_pid()+ to access the process’ task port. This +operation may be restricted to use by the superuser, executables signed by an +authority trusted by the system, and processes otherwise permitted by +taskgated(8). Consequently, this program must normally either be signed or be +invoked by root. It is possible to install this program as a setuid root +executable to overcome this limitation. + +This program is similar to the gcore(1) program available on some operating +systems. + +== Options + +*-r*, *--no-suspend*:: +The target process will continue running while the minidump file is generated. +Normally, the target process is suspended during this operation, which +guarantees that the minidump file will contain an atomic snapshot of the +process. ++ +This option may be useful when attempting to generate a minidump from a process +that dump generation has an interprocess dependency on, such as a system server +like launchd(8) or opendirectoryd(8). Deadlock could occur if any portion of the +dump generation operation blocks while waiting for a response from one of these +servers while they are suspended. + +*-o*, *--output*='FILE':: +The minidump will be written to 'FILE' instead of +minidump.PID+. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Generate a minidump file in +/tmp/minidump+ containing a snapshot of the process +with PID 1234. +[subs="quotes"] +---- +$ *generate_dump --output=/tmp/minidump 1234* +---- + +== Exit Status + +*0*:: +Success. + +*1*:: +Failure, with a message printed to the standard error stream. + +== See Also + +man_link:catch_exception_tool[1] + +include::../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/tools/generate_dump.cc b/third_party/crashpad/crashpad/tools/generate_dump.cc new file mode 100644 index 0000000..23b21ef --- /dev/null +++ b/third_party/crashpad/crashpad/tools/generate_dump.cc
@@ -0,0 +1,230 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <string> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "minidump/minidump_file_writer.h" +#include "tools/tool_support.h" +#include "util/file/file_writer.h" +#include "util/posix/drop_privileges.h" +#include "util/stdlib/string_number_conversion.h" + +#if defined(OS_MACOSX) +#include <mach/mach.h> +#include <unistd.h> + +#include "base/mac/scoped_mach_port.h" +#include "snapshot/mac/process_snapshot_mac.h" +#include "util/mach/scoped_task_suspend.h" +#include "util/mach/task_for_pid.h" +#elif defined(OS_WIN) +#include "base/strings/utf_string_conversions.h" +#include "snapshot/win/process_snapshot_win.h" +#include "util/win/scoped_process_suspend.h" +#include "util/win/xp_compat.h" +#endif // OS_MACOSX + +namespace crashpad { +namespace { + +struct Options { + std::string dump_path; + pid_t pid; + bool suspend; +}; + +void Usage(const base::FilePath& me) { + fprintf(stderr, +"Usage: %" PRFilePath " [OPTION]... PID\n" +"Generate a minidump file containing a snapshot of a running process.\n" +"\n" +" -r, --no-suspend don't suspend the target process during dump generation\n" +" -o, --output=FILE write the minidump to FILE instead of minidump.PID\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.value().c_str()); + ToolSupport::UsageTail(me); +} + +int GenerateDumpMain(int argc, char* argv[]) { + const base::FilePath argv0( + ToolSupport::CommandLineArgumentToFilePathStringType(argv[0])); + const base::FilePath me(argv0.BaseName()); + + enum OptionFlags { + // “Short” (single-character) options. + kOptionOutput = 'o', + kOptionNoSuspend = 'r', + + // Long options without short equivalents. + kOptionLastChar = 255, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + Options options = {}; + options.suspend = true; + + const option long_options[] = { + {"no-suspend", no_argument, nullptr, kOptionNoSuspend}, + {"output", required_argument, nullptr, kOptionOutput}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "o:r", long_options, nullptr)) != -1) { + switch (opt) { + case kOptionOutput: + options.dump_path = optarg; + break; + case kOptionNoSuspend: + options.suspend = false; + break; + case kOptionHelp: + Usage(me); + return EXIT_SUCCESS; + case kOptionVersion: + ToolSupport::Version(me); + return EXIT_SUCCESS; + default: + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (argc != 1) { + ToolSupport::UsageHint(me, "PID is required"); + return EXIT_FAILURE; + } + + if (!StringToNumber(argv[0], &options.pid) || options.pid <= 0) { + fprintf(stderr, + "%" PRFilePath ": invalid PID: %s\n", + me.value().c_str(), + argv[0]); + return EXIT_FAILURE; + } + +#if defined(OS_MACOSX) + task_t task = TaskForPID(options.pid); + if (task == TASK_NULL) { + return EXIT_FAILURE; + } + base::mac::ScopedMachSendRight task_owner(task); + + // This tool may have been installed as a setuid binary so that TaskForPID() + // could succeed. Drop any privileges now that they’re no longer necessary. + DropPrivileges(); + + if (options.pid == getpid()) { + if (options.suspend) { + LOG(ERROR) << "cannot suspend myself"; + return EXIT_FAILURE; + } + LOG(WARNING) << "operating on myself"; + } +#elif defined(OS_WIN) + ScopedKernelHANDLE process( + OpenProcess(kXPProcessAllAccess, false, options.pid)); + if (!process.is_valid()) { + PLOG(ERROR) << "could not open process " << options.pid; + return EXIT_FAILURE; + } +#endif // OS_MACOSX + + if (options.dump_path.empty()) { + options.dump_path = base::StringPrintf("minidump.%d", options.pid); + } + + { +#if defined(OS_MACOSX) + scoped_ptr<ScopedTaskSuspend> suspend; + if (options.suspend) { + suspend.reset(new ScopedTaskSuspend(task)); + } +#elif defined(OS_WIN) + scoped_ptr<ScopedProcessSuspend> suspend; + if (options.suspend) { + suspend.reset(new ScopedProcessSuspend(process.get())); + } +#endif // OS_MACOSX + +#if defined(OS_MACOSX) + ProcessSnapshotMac process_snapshot; + if (!process_snapshot.Initialize(task)) { + return EXIT_FAILURE; + } +#elif defined(OS_WIN) + ProcessSnapshotWin process_snapshot; + if (!process_snapshot.Initialize(process.get(), + options.suspend + ? ProcessSuspensionState::kSuspended + : ProcessSuspensionState::kRunning, + 0)) { + return EXIT_FAILURE; + } +#endif // OS_MACOSX + + FileWriter file_writer; + base::FilePath dump_path( + ToolSupport::CommandLineArgumentToFilePathStringType( + options.dump_path)); + if (!file_writer.Open(dump_path, + FileWriteMode::kTruncateOrCreate, + FilePermissions::kWorldReadable)) { + return EXIT_FAILURE; + } + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + if (!minidump.WriteEverything(&file_writer)) { + file_writer.Close(); + if (unlink(options.dump_path.c_str()) != 0) { + PLOG(ERROR) << "unlink"; + } + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +#if defined(OS_POSIX) +int main(int argc, char* argv[]) { + return crashpad::GenerateDumpMain(argc, argv); +} +#elif defined(OS_WIN) +int wmain(int argc, wchar_t* argv[]) { + return crashpad::ToolSupport::Wmain(argc, argv, crashpad::GenerateDumpMain); +} +#endif // OS_POSIX
diff --git a/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad b/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad new file mode 100644 index 0000000..a639de4c --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad
@@ -0,0 +1,117 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += catch_exception_tool(1) + +== Name + +catch_exception_tool - Catch Mach exceptions and display information about them + +== Synopsis + +[verse] +*catch_exception_tool* *-m* 'SERVICE' ['OPTION…'] + +== Description + +Runs a Mach exception server registered with the bootstrap server under the name +'SERVICE'. The exception server is capable of receiving exceptions for +“behavior” values of +EXCEPTION_DEFAULT+, +EXCEPTION_STATE+, and ++EXCEPTION_STATE_IDENTITY+, with or without +MACH_EXCEPTION_CODES+ set. + +== Options + +*-f*, *--file*='FILE':: +Information about the exception will be appended to 'FILE' instead of the +standard output stream. + +*-m*, *--mach-service*='SERVICE':: +Check in with the bootstrap server under the name 'SERVICE'. This service name +may already be reserved with the bootstrap server in cases where this tool is +started by launchd(8) as a result of a message being sent to a service declared +in a job’s +MachServices+ dictionary (see launchd.plist(5)). The service name +may also be completely unknown to the system. + +*-p*, *--persistent*:: +Continue processing exceptions after the first one. The default mode is +one-shot, where this tool exits after processing the first exception. + +*-t*, *--timeout*='TIMEOUT':: +Run for a maximum of 'TIMEOUT' seconds. Specify +0+ to request non-blocking +operation, in which the tool exits immediately if no exception is received. In +*--persistent* mode, 'TIMEOUT' applies to the overall duration that this tool +will run, not to the processing of individual exceptions. When *--timeout* is +not specified, this tool will block indefinitely while waiting for an exception. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Run a one-shot blocking exception server registered with the bootstrap server +under the name +svc+: +[subs="quotes"] +---- +$ *catch_exception_tool --mach-service=svc --file=out &* +[1] 1233 +$ *exception_port_tool --set-handler=handler=bootstrap:svc crasher* +Illegal instruction: 4 +[1]+ Done catch_exception_tool --mach-service=svc --file=out +$ *cat out* +catch_exception_tool: +behavior EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES, +pid 1234, thread 56789, exception EXC_CRASH, codes[2] 0x4200001, 0, +original exception EXC_BAD_INSTRUCTION, original code[0] 1, +signal SIGILL +---- + +Run an on-demand exception server started by launchd(5) available via the +bootstrap server under the name +svc+: +[subs="quotes"] +---- +$ *on_demand_service_tool --load --label=catch_exception \ + --mach-service=svc \ + $(which catch_exception_tool) --mach-service=svc \ + --file=/tmp/out --persistent --timeout=0* +$ *exception_port_tool --set-handler=handler=bootstrap:svc crasher* +Illegal instruction: 4 +$ *on_demand_service_tool --unload --label=catch_exception* +$ *cat /tmp/out* +catch_exception_tool: +behavior EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES, +pid 2468, thread 13579, exception EXC_CRASH, codes[2] 0x4200001, 0, +original exception EXC_BAD_INSTRUCTION, original code[0] 1, +signal SIGILL +---- + +== Exit Status + +*0*:: +Success. In *--persistent* mode with a *--timeout* set, it is considered +successful if at least one exception was caught when the timer expires. + +*1*:: +Failure, with a message printed to the standard error stream. + +== See Also + +man_link:exception_port_tool[1], +man_link:on_demand_service_tool[1] + +include::../../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc b/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc new file mode 100644 index 0000000..32c6aed --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc
@@ -0,0 +1,327 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <getopt.h> +#include <libgen.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "tools/tool_support.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_types.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/posix/symbolic_constants_posix.h" +#include "util/stdlib/string_number_conversion.h" + +namespace crashpad { +namespace { + +struct Options { + std::string file_path; + std::string mach_service; + FILE* file; + int timeout_secs; + bool has_timeout; + MachMessageServer::Persistent persistent; +}; + +class ExceptionServer : public UniversalMachExcServer::Interface { + public: + ExceptionServer(const Options& options, + const std::string& me, + int* exceptions_handled) + : UniversalMachExcServer::Interface(), + options_(options), + me_(me), + exceptions_handled_(exceptions_handled) {} + + // UniversalMachExcServer::Interface: + virtual kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + ++*exceptions_handled_; + + fprintf(options_.file, + "%s: behavior %s", + me_.c_str(), + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str()); + + kern_return_t kr; + if (ExceptionBehaviorHasIdentity(behavior)) { + // It’s not possible to call pid_for_task() once EXC_CORPSE_NOTIFY has + // been generated. It is possible to obtain the process ID by mapping the + // corpse kcdata area from the task’s address space at code[0] (size + // code[1]) and locating TASK_CRASHINFO_PID within that area. This area + // also includes TASK_CRASHINFO_CRASHED_THREADID which could be used + // instead of thread_info() below, and TASK_CRASHINFO_EXCEPTION_CODES + // which could be used to recover the exception codes passed to the + // EXC_CRASH handler. None of this is currently done because corpses are a + // new 10.11-only feature. See 10.11 <corpses/task_corpse.h> and + // <kern/kern_cdata.h>. + if (exception != EXC_CORPSE_NOTIFY) { + pid_t pid; + kr = pid_for_task(task, &pid); + if (kr != KERN_SUCCESS) { + fprintf(options_.file, "\n"); + fflush(options_.file); + MACH_LOG(ERROR, kr) << "pid_for_task"; + return KERN_FAILURE; + } + fprintf(options_.file, ", pid %d", pid); + } + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info(thread, + THREAD_IDENTIFIER_INFO, + reinterpret_cast<thread_info_t>(&identifier_info), + &count); + if (kr != KERN_SUCCESS) { + fprintf(options_.file, "\n"); + fflush(options_.file); + MACH_LOG(ERROR, kr) << "thread_info"; + return KERN_FAILURE; + } + fprintf(options_.file, ", thread %lld", identifier_info.thread_id); + } + + fprintf( + options_.file, + ", exception %s, codes[%d]", + ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(), + code_count); + + for (size_t index = 0; index < code_count; ++index) { + fprintf(options_.file, + "%s %#llx", + index != 0 ? "," : "", + code[index]); + } + + if (exception == EXC_CRASH) { + mach_exception_code_t original_code_0; + int signal; + exception_type_t original_exception = + ExcCrashRecoverOriginalException(code[0], &original_code_0, &signal); + fprintf(options_.file, + ", original exception %s, original code[0] %lld, signal %s", + ExceptionToString(original_exception, + kUseFullName | kUnknownIsNumeric).c_str(), + original_code_0, + SignalToString(signal, kUseFullName | kUnknownIsNumeric).c_str()); + } + + if (ExceptionBehaviorHasState(behavior)) { + std::string flavor_string = + ThreadStateFlavorToString(*flavor, kUseFullName | kUnknownIsNumeric); + fprintf(options_.file, + ", flavor %s, old_state_count %d", + flavor_string.c_str(), + old_state_count); + } + + fprintf(options_.file, "\n"); + fflush(options_.file); + + if (exception != EXC_CRASH && exception != kMachExceptionSimulated) { + // Find another handler. + return KERN_FAILURE; + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + private: + const Options& options_; + const std::string& me_; + int* exceptions_handled_; +}; + +void Usage(const std::string& me) { + fprintf(stderr, +"Usage: %s -m SERVICE [OPTION]...\n" +"Catch Mach exceptions and display information about them.\n" +"\n" +" -f, --file=FILE append information to FILE instead of stdout\n" +" -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n" +" -p, --persistent continue processing exceptions after the first\n" +" -t, --timeout=TIMEOUT run for a maximum of TIMEOUT seconds\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.c_str()); + ToolSupport::UsageTail(me); +} + +int CatchExceptionToolMain(int argc, char* argv[]) { + const std::string me(basename(argv[0])); + + enum OptionFlags { + // “Short” (single-character) options. + kOptionFile = 'f', + kOptionMachService = 'm', + kOptionPersistent = 'p', + kOptionTimeout = 't', + + // Long options without short equivalents. + kOptionLastChar = 255, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + Options options = {}; + + const option long_options[] = { + {"file", required_argument, nullptr, kOptionFile}, + {"mach-service", required_argument, nullptr, kOptionMachService}, + {"persistent", no_argument, nullptr, kOptionPersistent}, + {"timeout", required_argument, nullptr, kOptionTimeout}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "f:m:pt:", long_options, nullptr)) != + -1) { + switch (opt) { + case kOptionFile: + options.file_path = optarg; + break; + case kOptionMachService: + options.mach_service = optarg; + break; + case kOptionPersistent: + options.persistent = MachMessageServer::kPersistent; + break; + case kOptionTimeout: + if (!StringToNumber(optarg, &options.timeout_secs) || + options.timeout_secs < 0) { + ToolSupport::UsageHint(me, "-t requires a zero or positive TIMEOUT"); + return EXIT_FAILURE; + } + options.has_timeout = true; + break; + case kOptionHelp: + Usage(me); + return EXIT_SUCCESS; + case kOptionVersion: + ToolSupport::Version(me); + return EXIT_SUCCESS; + default: + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (options.mach_service.empty()) { + ToolSupport::UsageHint(me, "-m is required"); + return EXIT_FAILURE; + } + + base::mac::ScopedMachReceiveRight + service_port(BootstrapCheckIn(options.mach_service)); + if (service_port == kMachPortNull) { + return EXIT_FAILURE; + } + + base::ScopedFILE file_owner; + if (options.file_path.empty()) { + options.file = stdout; + } else { + file_owner.reset(fopen(options.file_path.c_str(), "a")); + if (!file_owner.get()) { + PLOG(ERROR) << "fopen " << options.file_path; + return EXIT_FAILURE; + } + options.file = file_owner.get(); + } + + int exceptions_handled = 0; + ExceptionServer exception_server(options, me, &exceptions_handled); + UniversalMachExcServer universal_mach_exc_server(&exception_server); + + // Assume that if persistent mode has been requested, it’s desirable to ignore + // large messages and keep running. + MachMessageServer::ReceiveLarge receive_large = + (options.persistent == MachMessageServer::kPersistent) + ? MachMessageServer::kReceiveLargeIgnore + : MachMessageServer::kReceiveLargeError; + + mach_msg_timeout_t timeout_ms; + if (!options.has_timeout) { + timeout_ms = kMachMessageTimeoutWaitIndefinitely; + } else if (options.timeout_secs == 0) { + timeout_ms = kMachMessageTimeoutNonblocking; + } else { + timeout_ms = options.timeout_secs * 1000; + } + + mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server, + service_port.get(), + MACH_MSG_OPTION_NONE, + options.persistent, + receive_large, + timeout_ms); + if (mr == MACH_RCV_TIMED_OUT && options.has_timeout && options.persistent && + exceptions_handled) { + // This is not an error: when exiting on timeout during persistent + // processing and at least one exception was handled, it’s considered a + // success. + } else if (mr != MACH_MSG_SUCCESS) { + MACH_LOG(ERROR, mr) << "MachMessageServer::Run"; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int main(int argc, char* argv[]) { + return crashpad::CatchExceptionToolMain(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad b/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad new file mode 100644 index 0000000..e102cc00 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad
@@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += exception_port_tool(1) + +== Name + +exception_port_tool - Show and change Mach exception ports + +== Synopsis + +[verse] +*exception_port_tool* ['OPTION…'] ['COMMAND' ['ARG…']] + +== Description + +Shows Mach exception ports registered for a thread, task, or host target with a +*--show-** option, changes Mach exception ports with *--set-handler*, shows +changes with a *--show-new-** option, and executes 'COMMAND' along with any +arguments specified ('ARG…') with the changed exception ports in effect. + +== Options +*-s*, *--set-handler*='DESCRIPTION':: +Set an exception port to 'DESCRIPTION'. This option may appear zero, one, or +more times. ++ +'DESCRIPTION' is formatted as a comma-separated sequence of tokens, where each +token consists of a key and value separated by an equals sign. These keys are +recognized: ++ +*target*='TARGET'::: +'TARGET' defines which target’s exception ports to set: *host*, *task*, or +*thread*. The default value of 'TARGET' is *task*. Operations on *host* are +restricted to the superuser. ++ +*mask*='MASK'::: +'MASK' defines the mask of exception types to handle, from ++<mach/exception_types.h>+. This can be *BAD_ACCESS*, *BAD_INSTRUCTION*, +*ARITHMETIC*, *EMULATION*, *SOFTWARE*, *BREAKPOINT*, *SYSCALL*, *MACH_SYSCALL*, +*RPC_ALERT*, *CRASH*, *RESOURCE*, *GUARD*, or *CORPSE_NOTIFY*. Different +exception types may be combined by combining them with pipe characters (*|*). +The special value *ALL* includes each exception type except for *CRASH*. To +truly specify all exception types including *CRASH*, use *ALL|CRASH*. The +default value of 'MASK' is *CRASH*. ++ +*behavior*='BEHAVIOR'::: +'BEHAVIOR' defines the specific exception handler routine to be called when an +exception occurs. This can be *DEFAULT*, *STATE*, or *STATE_IDENTITY*. *MACH* +may also be specified by combining them with pipe characters (*|*). The most +complete set of exception information is provided with *STATE_IDENTITY|MACH*. +Not all exception servers implement all possible behaviors. The default value of +'BEHAVIOR' is *DEFAULT|MACH*. ++ +*flavor*='FLAVOR'::: +For state-carrying values of 'BEHAVIOR' (those including *STATE* or +*STATE_IDENTITY*), 'FLAVOR' specifies the architecture-specific thread state +flavor to be provided to the exception handler. For the x86 family, this can be +*THREAD*, *THREAD32*, *THREAD64*, *FLOAT*, *FLOAT32*, *FLOAT64*, *DEBUG*, +*DEBUG32*, or *DEBUG64*. The default value of 'FLAVOR' is *NONE*, which is not +valid for state-carrying values of 'BEHAVIOR'. ++ +*handler*='HANDLER'::: +'HANDLER' defines the exception handler. *NULL* indicates that any existing +exception port should be cleared. 'HANDLER' may also take the form +*bootstrap*:__SERVICE__, which will look 'SERVICE' up with the bootstrap server +and set that service as the exception handler. The default value of 'HANDLER' is +*NULL*. + +*--show-bootstrap*='SERVICE':: +Looks up 'SERVICE' with the bootstrap server and shows it. Normally, the handler +port values displayed by the other *--show-** options are meaningless +handles, but by comparing them to the port values for known bootstrap services, +it is possible to verify that they are set as intended. + +*-p*, *--pid*='PID':: +For operations on the task target, including *--set-handler* with 'TARGET' set +to *task*, *--show-task*, and *--show-new-task*, operates on the task associated +with process id 'PID' instead of the current task associated with the tool. When +this option is supplied, 'COMMAND' must not be specified. ++ +This option uses +task_for_pid()+ to access the process’ task port. This +operation may be restricted to use by the superuser, executables signed by an +authority trusted by the system, and processes otherwise permitted by +taskgated(8). Consequently, this program must normally either be signed or be +invoked by root to use this option. It is possible to install this program as a +setuid root executable to overcome this limitation. + +*-h*, *--show-host*:: +Shows the original host exception ports before making any changes requested by +*--set-handler*. This option is restricted to the superuser. + +*-t*, *--show-task*:: +Shows the original task exception ports before making any changes requested by +*--set-handler*. + +*--show-thread*:: +Shows the original thread exception ports before making any changes requested by +*--set-handler*. + +*-H*, *--show-new-host*:: +Shows the modified host exception ports after making any changes requested by +*--set-handler*. This option is restricted to the superuser. + +*-T*, *--show-new-task*:: +Shows the modified task exception ports after making any changes requested by +*--set-handler*. + +*--show-new-thread*:: +Shows the modified thread exception ports after making any changes requested by +*--set-handler*. + +*-n*, *--numeric*:: +For *--show-** options, all values will be displayed numerically only. The +default is to decode numeric values and display them symbolically as well. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Sets a new task-level exception handler for +EXC_CRASH+-type exceptions to the +handler registered with the bootstrap server as +svc+, showing the task-level +exception ports before and after the change. The old and new exception handlers +are verified by their service names as registered with the bootstrap server. +With the new task-level exception ports in effect, a program is run. +[subs="quotes"] +---- +$ *exception_port_tool --show-task --show-new-task \ + --show-bootstrap=com.apple.ReportCrash --show-bootstrap=svc \ + --set-handler=behavior=DEFAULT,handler=bootstrap:svc crash* +service com.apple.ReportCrash 0xe03 +service svc 0x1003 +task exception port 0, mask 0x400 (CRASH), port 0xe03, +behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD) +new task exception port 0, mask 0x400 (CRASH), port 0x1003, +behavior 0x1 (DEFAULT), flavor 13 (NONE) +Illegal instruction: 4 +---- + +Shows the task-level exception ports for the process with PID 1234. This +requires superuser permissions or the approval of taskgated(8). +[subs="quotes"] +---- +# *exception_port_tool --pid=1234 --show-task* +task exception port 0, mask 0x4e +(BAD_ACCESS|BAD_INSTRUCTION|ARITHMETIC|BREAKPOINT), port 0x1503, +behavior 0x1 (DEFAULT), flavor 13 (NONE) +task exception port 1, mask 0x1c00 (CRASH|RESOURCE|GUARD), +port 0x1403, behavior 0x80000003 (STATE_IDENTITY|MACH), +flavor 7 (THREAD) +---- + +== Exit Status + +*0*:: +Success. + +*125*:: +Failure, with a message printed to the standard error stream. + +*126*:: +The program specified by 'COMMAND' was found, but could not be invoked. + +*127*:: +The program specified by 'COMMAND' could not be found. + +== See Also + +man_link:catch_exception_tool[1], +man_link:on_demand_service_tool[1] + +include::../../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc b/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc new file mode 100644 index 0000000..b873e68 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc
@@ -0,0 +1,591 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <mach/mach.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "tools/tool_support.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/mach/task_for_pid.h" +#include "util/posix/drop_privileges.h" +#include "util/stdlib/string_number_conversion.h" + +namespace crashpad { +namespace { + +//! \brief Manages a pool of Mach send rights, deallocating all send rights upon +//! destruction. +//! +//! This class effectively implements what a vector of +//! base::mac::ScopedMachSendRight objects would be. +//! +//! The various “show” operations performed by this program display Mach ports +//! by their names as they are known in this task. For this to be useful, rights +//! to the same ports must have consistent names across successive calls. This +//! cannot be guaranteed if the rights are deallocated as soon as they are used, +//! because if that deallocation causes the task to lose its last right to a +//! port, subsequently regaining a right to the same port would cause it to be +//! known by a new name in this task. +//! +//! Instead of immediately deallocating send rights that are used for display, +//! they can be added to this pool. The pool collects send rights, ensuring that +//! they remain alive in this task, and that subsequent calls that obtain the +//! same rights cause them to be known by the same name. All rights are +//! deallocated upon destruction. +class MachSendRightPool { + public: + MachSendRightPool() + : send_rights_() { + } + + ~MachSendRightPool() { + for (mach_port_t send_right : send_rights_) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), send_right); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; + } + } + + //! \brief Adds a send right to the pool. + //! + //! \param[in] send_right The send right to be added. The pool object takes + //! its own reference to the send right, which remains valid until the + //! pool object is destroyed. The caller remains responsible for its + //! reference to the send right. + //! + //! It is possible and in fact likely that one pool will wind up owning the + //! same send right multiple times. This is acceptable, because send rights + //! are reference-counted. + void AddSendRight(mach_port_t send_right) { + kern_return_t kr = mach_port_mod_refs(mach_task_self(), + send_right, + MACH_PORT_RIGHT_SEND, + 1); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_mod_refs"; + + send_rights_.push_back(send_right); + } + + private: + std::vector<mach_port_t> send_rights_; + + DISALLOW_COPY_AND_ASSIGN(MachSendRightPool); +}; + +struct ExceptionHandlerDescription { + ExceptionPorts::TargetType target_type; + exception_mask_t mask; + exception_behavior_t behavior; + thread_state_flavor_t flavor; + std::string handler; +}; + +const char kHandlerNull[] = "NULL"; +const char kHandlerBootstrapColon[] = "bootstrap:"; + +// Populates |description| based on a textual representation in +// |handler_string_ro|, returning true on success and false on failure (parse +// error). The --help string describes the format of |handler_string_ro|. +// Briefly, it is a comma-separated string that allows the members of +// |description| to be specified as "field=value". Values for "target" can be +// "host", "task", or "thread"; values for "handler" are of the form +// "bootstrap:service_name" where service_name will be looked up with the +// bootstrap server; and values for the other fields are interpreted by +// SymbolicConstantsMach. +bool ParseHandlerString(const char* handler_string_ro, + ExceptionHandlerDescription* description) { + const char kTargetEquals[] = "target="; + const char kMaskEquals[] = "mask="; + const char kBehaviorEquals[] = "behavior="; + const char kFlavorEquals[] = "flavor="; + const char kHandlerEquals[] = "handler="; + + std::string handler_string(handler_string_ro); + char* handler_string_c = &handler_string[0]; + + char* token; + while ((token = strsep(&handler_string_c, ",")) != nullptr) { + if (strncmp(token, kTargetEquals, strlen(kTargetEquals)) == 0) { + const char* value = token + strlen(kTargetEquals); + if (strcmp(value, "host") == 0) { + description->target_type = ExceptionPorts::kTargetTypeHost; + } else if (strcmp(value, "task") == 0) { + description->target_type = ExceptionPorts::kTargetTypeTask; + } else if (strcmp(value, "thread") == 0) { + description->target_type = ExceptionPorts::kTargetTypeThread; + } else { + return false; + } + } else if (strncmp(token, kMaskEquals, strlen(kMaskEquals)) == 0) { + const char* value = token + strlen(kMaskEquals); + if (!StringToExceptionMask( + value, + kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr, + &description->mask)) { + return false; + } + } else if (strncmp(token, kBehaviorEquals, strlen(kBehaviorEquals)) == 0) { + const char* value = token + strlen(kBehaviorEquals); + if (!StringToExceptionBehavior( + value, + kAllowFullName | kAllowShortName | kAllowNumber, + &description->behavior)) { + return false; + } + } else if (strncmp(token, kFlavorEquals, strlen(kFlavorEquals)) == 0) { + const char* value = token + strlen(kFlavorEquals); + if (!StringToThreadStateFlavor( + value, + kAllowFullName | kAllowShortName | kAllowNumber, + &description->flavor)) { + return false; + } + } else if (strncmp(token, kHandlerEquals, strlen(kHandlerEquals)) == 0) { + const char* value = token + strlen(kHandlerEquals); + if (strcmp(value, kHandlerNull) != 0 && + strncmp(value, + kHandlerBootstrapColon, + strlen(kHandlerBootstrapColon)) != 0) { + return false; + } + description->handler = std::string(value); + } else { + return false; + } + } + + return true; +} + +// ShowExceptionPorts() shows handlers as numeric mach_port_t values, which are +// opaque and meaningless on their own. ShowBootstrapService() can be used to +// look up a service with the bootstrap server by name and show its mach_port_t +// value, which can then be associated with handlers shown by +// ShowExceptionPorts(). Any send rights obtained by this function are added to +// |mach_send_right_pool|. +void ShowBootstrapService(const std::string& service_name, + MachSendRightPool* mach_send_right_pool) { + base::mac::ScopedMachSendRight service_port(BootstrapLookUp(service_name)); + if (service_port == kMachPortNull) { + return; + } + + mach_send_right_pool->AddSendRight(service_port.get()); + + printf("service %s %#x\n", service_name.c_str(), service_port.get()); +} + +// Prints information about all exception ports known for |exception_ports|. If +// |numeric| is true, all information is printed in numeric form, otherwise, it +// will be converted to symbolic constants where possible by +// SymbolicConstantsMach. If |is_new| is true, information will be presented as +// “new exception ports”, indicating that they show the state of the exception +// ports after SetExceptionPort() has been called. Any send rights obtained by +// this function are added to |mach_send_right_pool|. +void ShowExceptionPorts(const ExceptionPorts& exception_ports, + bool numeric, + bool is_new, + MachSendRightPool* mach_send_right_pool) { + const char* target_name = exception_ports.TargetTypeName(); + + ExceptionPorts::ExceptionHandlerVector handlers; + if (!exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers)) { + return; + } + + const char* age_name = is_new ? "new " : ""; + + if (handlers.empty()) { + printf("no %s%s exception ports\n", age_name, target_name); + } + + for (size_t port_index = 0; port_index < handlers.size(); ++port_index) { + mach_send_right_pool->AddSendRight(handlers[port_index].port); + + if (numeric) { + printf( + "%s%s exception port %zu, mask %#x, port %#x, " + "behavior %#x, flavor %u\n", + age_name, + target_name, + port_index, + handlers[port_index].mask, + handlers[port_index].port, + handlers[port_index].behavior, + handlers[port_index].flavor); + } else { + std::string mask_string = ExceptionMaskToString( + handlers[port_index].mask, kUseShortName | kUnknownIsEmpty | kUseOr); + if (mask_string.empty()) { + mask_string.assign("?"); + } + + std::string behavior_string = ExceptionBehaviorToString( + handlers[port_index].behavior, kUseShortName | kUnknownIsEmpty); + if (behavior_string.empty()) { + behavior_string.assign("?"); + } + + std::string flavor_string = ThreadStateFlavorToString( + handlers[port_index].flavor, kUseShortName | kUnknownIsEmpty); + if (flavor_string.empty()) { + flavor_string.assign("?"); + } + + printf( + "%s%s exception port %zu, mask %#x (%s), port %#x, " + "behavior %#x (%s), flavor %u (%s)\n", + age_name, + target_name, + port_index, + handlers[port_index].mask, + mask_string.c_str(), + handlers[port_index].port, + handlers[port_index].behavior, + behavior_string.c_str(), + handlers[port_index].flavor, + flavor_string.c_str()); + } + } +} + +// Sets the exception port for |target_port|, a send right to a thread, task, or +// host port, to |description|, which identifies what type of port |target_port| +// is and describes an exception port to be set. Returns true on success. +// +// This function may be called more than once if setting different handlers is +// desired. +bool SetExceptionPort(const ExceptionHandlerDescription* description, + mach_port_t target_port) { + base::mac::ScopedMachSendRight service_port; + if (description->handler.compare( + 0, strlen(kHandlerBootstrapColon), kHandlerBootstrapColon) == 0) { + const char* service_name = + description->handler.c_str() + strlen(kHandlerBootstrapColon); + service_port = BootstrapLookUp(service_name); + if (service_port == kMachPortNull) { + return false; + } + + // The service port doesn’t need to be added to a MachSendRightPool because + // it’s not used for display at all. ScopedMachSendRight is sufficient. + } else if (description->handler != kHandlerNull) { + return false; + } + + ExceptionPorts exception_ports(description->target_type, target_port); + if (!exception_ports.SetExceptionPort(description->mask, + service_port.get(), + description->behavior, + description->flavor)) { + return false; + } + + return true; +} + +void Usage(const std::string& me) { + fprintf(stderr, +"Usage: %s [OPTION]... [COMMAND [ARG]...]\n" +"View and change Mach exception ports, and run COMMAND if supplied.\n" +"\n" +" -s, --set-handler=DESCRIPTION set an exception port to DESCRIPTION, see below\n" +" --show-bootstrap=SERVICE look up and display a service registered with\n" +" the bootstrap server\n" +" -p, --pid=PID operate on PID instead of the current task\n" +" -h, --show-host display original host exception ports\n" +" -t, --show-task display original task exception ports\n" +" --show-thread display original thread exception ports\n" +" -H, --show-new-host display modified host exception ports\n" +" -T, --show-new-task display modified task exception ports\n" +" --show-new-thread display modified thread exception ports\n" +" -n, --numeric display values numerically, not symbolically\n" +" --help display this help and exit\n" +" --version output version information and exit\n" +"\n" +"Any operations on host exception ports require superuser permissions.\n" +"\n" +"DESCRIPTION is formatted as a comma-separated sequence of tokens, where each\n" +"token consists of a key and value separated by an equals sign. Available keys:\n" +" target which target's exception ports to set: host, task, or thread\n" +" mask the mask of exception types to handle: CRASH, ALL, or others\n" +" behavior the specific exception handler routine to call: DEFAULT, STATE,\n" +" or STATE_IDENTITY, possibly with MACH_EXCEPTION_CODES.\n" +" flavor the thread state flavor passed to the handler: architecture-specific\n" +" handler the exception handler: NULL or bootstrap:SERVICE, indicating that\n" +" the handler should be looked up with the bootstrap server\n" +"The default DESCRIPTION is\n" +" target=task,mask=CRASH,behavior=DEFAULT|MACH,flavor=NONE,handler=NULL\n", + me.c_str()); + ToolSupport::UsageTail(me); +} + +int ExceptionPortToolMain(int argc, char* argv[]) { + const std::string me(basename(argv[0])); + + enum ExitCode { + kExitSuccess = EXIT_SUCCESS, + + // To differentiate this tool’s errors from errors in the programs it execs, + // use a high exit code for ordinary failures instead of EXIT_FAILURE. This + // is the same rationale for using the distinct exit codes for exec + // failures. + kExitFailure = 125, + + // Like env, use exit code 126 if the program was found but could not be + // invoked, and 127 if it could not be found. + // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html + kExitExecFailure = 126, + kExitExecENOENT = 127, + }; + + enum OptionFlags { + // “Short” (single-character) options. + kOptionSetPort = 's', + kOptionPid = 'p', + kOptionShowHost = 'h', + kOptionShowTask = 't', + kOptionShowNewHost = 'H', + kOptionShowNewTask = 'T', + kOptionNumeric = 'n', + + // Long options without short equivalents. + kOptionLastChar = 255, + kOptionShowBootstrap, + kOptionShowThread, + kOptionShowNewThread, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + struct { + std::vector<const char*> show_bootstrap; + std::vector<ExceptionHandlerDescription> set_handler; + pid_t pid; + task_t alternate_task; + bool show_host; + bool show_task; + bool show_thread; + bool show_new_host; + bool show_new_task; + bool show_new_thread; + bool numeric; + } options = {}; + + const option long_options[] = { + {"set-handler", required_argument, nullptr, kOptionSetPort}, + {"show-bootstrap", required_argument, nullptr, kOptionShowBootstrap}, + {"pid", required_argument, nullptr, kOptionPid}, + {"show-host", no_argument, nullptr, kOptionShowHost}, + {"show-task", no_argument, nullptr, kOptionShowTask}, + {"show-thread", no_argument, nullptr, kOptionShowThread}, + {"show-new-host", no_argument, nullptr, kOptionShowNewHost}, + {"show-new-task", no_argument, nullptr, kOptionShowNewTask}, + {"show-new-thread", no_argument, nullptr, kOptionShowNewThread}, + {"numeric", no_argument, nullptr, kOptionNumeric}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "+s:p:htHTn", long_options, nullptr)) != + -1) { + switch (opt) { + case kOptionSetPort: { + options.set_handler.push_back({}); + ExceptionHandlerDescription* description = &options.set_handler.back(); + description->target_type = ExceptionPorts::kTargetTypeTask; + description->mask = EXC_MASK_CRASH; + description->behavior = EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES; + description->flavor = THREAD_STATE_NONE; + description->handler = "NULL"; + if (!ParseHandlerString(optarg, description)) { + fprintf(stderr, + "%s: invalid exception handler: %s\n", + me.c_str(), + optarg); + return kExitFailure; + } + break; + } + case kOptionShowBootstrap: + options.show_bootstrap.push_back(optarg); + break; + case kOptionPid: + if (!StringToNumber(optarg, &options.pid)) { + fprintf(stderr, "%s: invalid pid: %s\n", me.c_str(), optarg); + return kExitFailure; + } + break; + case kOptionShowHost: + options.show_host = true; + break; + case kOptionShowTask: + options.show_task = true; + break; + case kOptionShowThread: + options.show_thread = true; + break; + case kOptionShowNewHost: + options.show_new_host = true; + break; + case kOptionShowNewTask: + options.show_new_task = true; + break; + case kOptionShowNewThread: + options.show_new_thread = true; + break; + case kOptionNumeric: + options.numeric = true; + break; + case kOptionHelp: + Usage(me); + return kExitSuccess; + case kOptionVersion: + ToolSupport::Version(me); + return kExitSuccess; + default: + ToolSupport::UsageHint(me, nullptr); + return kExitFailure; + } + } + argc -= optind; + argv += optind; + + if (options.show_bootstrap.empty() && !options.show_host && + !options.show_task && !options.show_thread && + options.set_handler.empty() && argc == 0) { + ToolSupport::UsageHint(me, "nothing to do"); + return kExitFailure; + } + + base::mac::ScopedMachSendRight alternate_task_owner; + if (options.pid) { + if (argc) { + ToolSupport::UsageHint(me, "cannot combine -p with COMMAND"); + return kExitFailure; + } + + options.alternate_task = TaskForPID(options.pid); + if (options.alternate_task == TASK_NULL) { + return kExitFailure; + } + alternate_task_owner.reset(options.alternate_task); + } + + // This tool may have been installed as a setuid binary so that TaskForPID() + // could succeed. Drop any privileges now that they’re no longer necessary. + DropPrivileges(); + + MachSendRightPool mach_send_right_pool; + + // Show bootstrap services requested. + for (const char* service : options.show_bootstrap) { + ShowBootstrapService(service, &mach_send_right_pool); + } + + // Show the original exception ports. + if (options.show_host) { + ShowExceptionPorts( + ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL), + options.numeric, + false, + &mach_send_right_pool); + } + if (options.show_task) { + ShowExceptionPorts( + ExceptionPorts(ExceptionPorts::kTargetTypeTask, options.alternate_task), + options.numeric, + false, + &mach_send_right_pool); + } + if (options.show_thread) { + ShowExceptionPorts( + ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL), + options.numeric, + false, + &mach_send_right_pool); + } + + if (!options.set_handler.empty()) { + // Set new exception handlers. + for (ExceptionHandlerDescription description : options.set_handler) { + if (!SetExceptionPort( + &description, + description.target_type == ExceptionPorts::kTargetTypeTask + ? options.alternate_task + : TASK_NULL)) { + return kExitFailure; + } + } + + // Show changed exception ports. + if (options.show_new_host) { + ShowExceptionPorts( + ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL), + options.numeric, + true, + &mach_send_right_pool); + } + if (options.show_new_task) { + ShowExceptionPorts( + ExceptionPorts(ExceptionPorts::kTargetTypeTask, + options.alternate_task), + options.numeric, + true, + &mach_send_right_pool); + } + if (options.show_new_thread) { + ShowExceptionPorts( + ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL), + options.numeric, + true, + &mach_send_right_pool); + } + } + + if (argc) { + // Using the remaining arguments, start a new program with the new set of + // exception ports in effect. + execvp(argv[0], argv); + PLOG(ERROR) << "execvp " << argv[0]; + return errno == ENOENT ? kExitExecENOENT : kExitExecFailure; + } + + return kExitSuccess; +} + +} // namespace +} // namespace crashpad + +int main(int argc, char* argv[]) { + return crashpad::ExceptionPortToolMain(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad b/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad new file mode 100644 index 0000000..5db4713f --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad
@@ -0,0 +1,102 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += on_demand_service_tool(1) + +== Name + +on_demand_service_tool - Load and unload on-demand Mach services registered with +launchd(8) + +== Synopsis + +[verse] +*on_demand_service_tool* *-L* *-l* 'LABEL' ['OPTION…'] 'COMMAND' ['ARG…'] +*on_demand_service_tool* *-U* *-l* 'LABEL' + +== Description + +On-demand services may be registered with launchd(8) by using the *--load* form. +One or more service names may be registered with the bootstrap server by +specifying *--mach-service*. When a Mach message is sent to any of these +services, launchd(8) will invoke 'COMMAND' along with any arguments specified +('ARG…'). 'COMMAND' must be an absolute pathname. + +The *--unload* form unregisters jobs registered with launchd(8). + +== Options + +*-L*, *--load*:: +Registers a job with launchd(8). *--label*='LABEL' and 'COMMAND' are required. +This operation may also be referred to as “load” or “submit”. + +*-U*, *--unload*:: +Unregisters a job with launchd(8). *--label*='LABEL' is required. This operation +may also be referred to as “unload” or “remove”. + +*-l*, *--label*='LABEL':: +'LABEL' is used as the job label to identify the job to launchd(8). 'LABEL' must +be unique within a launchd(8) context. + +*-m*, *--mach-service*='SERVICE':: +In conjunction with *--load*, registers 'SERVICE' with the bootstrap server. +Clients will be able to obtain a send right by looking up the 'SERVICE' name +with the bootstrap server. When a message is sent to such a Mach port, +launchd(8) will invoke 'COMMAND' along with any arguments specified ('ARG…') if +it is not running. This forms the “on-demand” nature referenced by this tool’s +name. This option may appear zero, one, or more times. 'SERVICE' must be unique +within a bootstrap context. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Registers an on-demand server that will execute man_link:catch_exception_tool[1] +when a Mach message is sent to a Mach port obtained from the bootstrap server by +looking up the name +svc+: +[subs="quotes"] +---- +$ *on_demand_service_tool --load --label=catch_exception \ + --mach-service=svc \ + $(which catch_exception_tool) --mach-service=svc \ + --file=/tmp/out --persistent --timeout=0* +---- + +Unregisters the on-demand server installed above: +[subs="quotes"] +---- +$ *on_demand_service_tool --unload --label=catch_exception* +---- + +== Exit Status + +*0*:: +Success. + +*1*:: +Failure, with a message printed to the standard error stream. + +== See Also + +man_link:catch_exception_tool[1], +man_link:exception_port_tool[1], +launchctl(1) + +include::../../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm b/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm new file mode 100644 index 0000000..c6b6952 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm
@@ -0,0 +1,197 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <CoreFoundation/CoreFoundation.h> +#import <Foundation/Foundation.h> +#include <getopt.h> +#include <launch.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "base/mac/foundation_util.h" +#include "base/strings/sys_string_conversions.h" +#include "tools/tool_support.h" +#include "util/mac/service_management.h" +#include "util/stdlib/objc.h" + +namespace crashpad { +namespace { + +void Usage(const std::string& me) { + fprintf(stderr, +"Usage: %s -L -l LABEL [OPTION]... COMMAND [ARG]...\n" +" %s -U -l LABEL\n" +"Load and unload on-demand Mach services from launchd.\n" +"\n" +" -L, --load load (submit) the job identified by --label;\n" +" COMMAND must be specified\n" +" -U, --unload unload (remove) the job identified by --label\n" +" -l, --label=LABEL identify the job to launchd with LABEL\n" +" -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.c_str(), + me.c_str()); + ToolSupport::UsageTail(me); +} + +int OnDemandServiceToolMain(int argc, char* argv[]) { + const std::string me(basename(argv[0])); + + enum Operation { + kOperationUnknown = 0, + kOperationLoadJob, + kOperationUnloadJob, + }; + + enum OptionFlags { + // “Short” (single-character) options. + kOptionLoadJob = 'L', + kOptionUnloadJob = 'U', + kOptionJobLabel = 'l', + kOptionMachService = 'm', + + // Long options without short equivalents. + kOptionLastChar = 255, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + struct { + Operation operation; + std::string job_label; + std::vector<std::string> mach_services; + } options = {}; + + const option long_options[] = { + {"load", no_argument, nullptr, kOptionLoadJob}, + {"unload", no_argument, nullptr, kOptionUnloadJob}, + {"label", required_argument, nullptr, kOptionJobLabel}, + {"mach-service", required_argument, nullptr, kOptionMachService}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "+LUl:m:", long_options, nullptr)) != + -1) { + switch (opt) { + case kOptionLoadJob: + options.operation = kOperationLoadJob; + break; + case kOptionUnloadJob: + options.operation = kOperationUnloadJob; + break; + case kOptionJobLabel: + options.job_label = optarg; + break; + case kOptionMachService: + options.mach_services.push_back(optarg); + break; + case kOptionHelp: + Usage(me); + return EXIT_SUCCESS; + case kOptionVersion: + ToolSupport::Version(me); + return EXIT_SUCCESS; + default: + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (options.job_label.empty()) { + ToolSupport::UsageHint(me, "must provide -l"); + return EXIT_FAILURE; + } + + switch (options.operation) { + case kOperationLoadJob: { + if (argc == 0) { + ToolSupport::UsageHint(me, "must provide COMMAND with -L"); + return EXIT_FAILURE; + } + + @autoreleasepool { + NSString* job_label = base::SysUTF8ToNSString(options.job_label); + + NSMutableArray* command = [NSMutableArray arrayWithCapacity:argc]; + for (int index = 0; index < argc; ++index) { + NSString* argument = base::SysUTF8ToNSString(argv[index]); + [command addObject:argument]; + } + + NSDictionary* job_dictionary = @{ + @LAUNCH_JOBKEY_LABEL : job_label, + @LAUNCH_JOBKEY_PROGRAMARGUMENTS : command, + }; + + if (!options.mach_services.empty()) { + NSMutableDictionary* mach_services = [NSMutableDictionary + dictionaryWithCapacity:options.mach_services.size()]; + for (std::string mach_service : options.mach_services) { + NSString* mach_service_ns = base::SysUTF8ToNSString(mach_service); + [mach_services setObject:@YES forKey:mach_service_ns]; + } + + NSMutableDictionary* mutable_job_dictionary = + [[job_dictionary mutableCopy] autorelease]; + [mutable_job_dictionary setObject:mach_services + forKey:@LAUNCH_JOBKEY_MACHSERVICES]; + job_dictionary = mutable_job_dictionary; + } + + CFDictionaryRef job_dictionary_cf = + base::mac::NSToCFCast(job_dictionary); + if (!ServiceManagementSubmitJob(job_dictionary_cf)) { + fprintf(stderr, "%s: failed to submit job\n", me.c_str()); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; + } + + case kOperationUnloadJob: { + if (!ServiceManagementRemoveJob(options.job_label, true)) { + fprintf(stderr, "%s: failed to remove job\n", me.c_str()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + + default: { + ToolSupport::UsageHint(me, "must provide -L or -U"); + return EXIT_FAILURE; + } + } +} + +} // namespace +} // namespace crashpad + +int main(int argc, char* argv[]) { + return crashpad::OnDemandServiceToolMain(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad b/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad new file mode 100644 index 0000000..129488a --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad
@@ -0,0 +1,112 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +:doctype: manpage + += run_with_crashpad(1) + +== Name + +run_with_crashpad - Run a program with a Crashpad exception handler + +== Synopsis + +[verse] +*run_with_crashpad* ['OPTION…'] 'COMMAND' ['ARG…'] + +== Description + +Starts a Crashpad exception handler server such as crashpad_handler(8) and +becomes its client, setting an exception port referencing the handler. Then, +executes 'COMMAND' along with any arguments specified ('ARG…') with the new +exception port in effect. + +The exception port is configured to receive exceptions of type +EXC_CRASH+, ++EXC_RESOURCE+, and +EXC_GUARD+. The exception behavior is configured as ++EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES+. The thread state flavor is +set to +MACHINE_THREAD_STATE+. + +Programs that use the Crashpad client library directly will not normally use +this tool. This tool exists to allow programs that are unaware of Crashpad to be +run with a Crashpad exception handler. + +== Options +*-h*, *--handler*='HANDLER':: +Invoke 'HANDLER' as the Crashpad handler program instead of the default, +*crashpad_handler*. + +*--annotation*='KEY=VALUE':: +Passed to the Crashpad handler program as an *--annotation* argument. + +*--database*='PATH':: +Passed to the Crashpad handler program as its *--database* argument. + +*--url*='URL':: +Passed to the Crashpad handler program as its *--url* argument. + +*-a*, *--argument*='ARGUMENT':: +Invokes the Crashpad handler program with 'ARGUMENT' as one of its arguments. +This option may appear zero, one, or more times. If this program has a specific +option such as *--database* matching the desired Crashpad handler program +option, the specific option should be used in preference to *--argument*. +Regardless of this option’s presence, the handler will always be invoked with +the necessary arguments to perform a handshake. + +*--help*:: +Display help and exit. + +*--version*:: +Output version information and exit. + +== Examples + +Starts a Crashpad exception handler server by its default name, +*crashpad_handler*, and runs a program with this handler in effect. +[subs="quotes"] +---- +$ *run_with_crashpad --database=/tmp/crashpad_database crash* +Illegal instruction: 4 +---- + +Starts a Crashpad exception handler server at a nonstandard path, and runs +man_link:exception_port_tool[1] to show the task-level exception ports. +[subs="quotes"] +---- +$ *run_with_crashpad --handler=/tmp/crashpad_handler \ + --database=/tmp/crashpad_database exception_port_tool \ + --show-task* +task exception port 0, mask 0x1c00 (CRASH|RESOURCE|GUARD), port +0x30b, behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD) +---- + +== Exit Status + +*0*:: +Success. + +*125*:: +Failure, with a message printed to the standard error stream. + +*126*:: +The program specified by 'COMMAND' was found, but could not be invoked. + +*127*:: +The program specified by 'COMMAND' could not be found. + +== See Also + +man_link:crashpad_handler[8], +man_link:exception_port_tool[1] + +include::../../doc/support/man_footer.ad[]
diff --git a/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc b/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc new file mode 100644 index 0000000..58337aa --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc
@@ -0,0 +1,190 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "tools/tool_support.h" +#include "util/stdlib/map_insert.h" +#include "util/string/split_string.h" + +namespace crashpad { +namespace { + +void Usage(const std::string& me) { + fprintf(stderr, +"Usage: %s [OPTION]... COMMAND [ARG]...\n" +"Start a Crashpad handler and have it handle crashes from COMMAND.\n" +"\n" +" -h, --handler=HANDLER invoke HANDLER instead of crashpad_handler\n" +" --annotation=KEY=VALUE passed to the handler as an --annotation argument\n" +" --database=PATH passed to the handler as its --database argument\n" +" --url=URL passed to the handler as its --url argument\n" +" -a, --argument=ARGUMENT invoke the handler with ARGUMENT\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.c_str()); + ToolSupport::UsageTail(me); +} + +int RunWithCrashpadMain(int argc, char* argv[]) { + const std::string me(basename(argv[0])); + + enum ExitCode { + kExitSuccess = EXIT_SUCCESS, + + // To differentiate this tool’s errors from errors in the programs it execs, + // use a high exit code for ordinary failures instead of EXIT_FAILURE. This + // is the same rationale for using the distinct exit codes for exec + // failures. + kExitFailure = 125, + + // Like env, use exit code 126 if the program was found but could not be + // invoked, and 127 if it could not be found. + // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html + kExitExecFailure = 126, + kExitExecENOENT = 127, + }; + + enum OptionFlags { + // “Short” (single-character) options. + kOptionHandler = 'h', + kOptionArgument = 'a', + + // Long options without short equivalents. + kOptionLastChar = 255, + kOptionAnnotation, + kOptionDatabase, + kOptionURL, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + const option long_options[] = { + {"handler", required_argument, nullptr, kOptionHandler}, + {"annotation", required_argument, nullptr, kOptionAnnotation}, + {"database", required_argument, nullptr, kOptionDatabase}, + {"url", required_argument, nullptr, kOptionURL}, + {"argument", required_argument, nullptr, kOptionArgument}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + struct { + std::string handler; + std::map<std::string, std::string> annotations; + std::string database; + std::string url; + std::vector<std::string> arguments; + } options = {}; + options.handler = "crashpad_handler"; + + int opt; + while ((opt = getopt_long(argc, argv, "+a:h:", long_options, nullptr)) != + -1) { + switch (opt) { + case kOptionHandler: { + options.handler = optarg; + break; + } + case kOptionAnnotation: { + std::string key; + std::string value; + if (!SplitString(optarg, '=', &key, &value)) { + ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE"); + return EXIT_FAILURE; + } + std::string old_value; + if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) { + LOG(WARNING) << "duplicate key " << key << ", discarding value " + << old_value; + } + break; + } + case kOptionDatabase: { + options.database = optarg; + break; + } + case kOptionURL: { + options.url = optarg; + break; + } + case kOptionArgument: { + options.arguments.push_back(optarg); + break; + } + case kOptionHelp: { + Usage(me); + return kExitSuccess; + } + case kOptionVersion: { + ToolSupport::Version(me); + return kExitSuccess; + } + default: { + ToolSupport::UsageHint(me, nullptr); + return kExitFailure; + } + } + } + argc -= optind; + argv += optind; + + if (!argc) { + ToolSupport::UsageHint(me, "COMMAND is required"); + return kExitFailure; + } + + // Start the handler process and direct exceptions to it. + CrashpadClient crashpad_client; + if (!crashpad_client.StartHandler(base::FilePath(options.handler), + base::FilePath(options.database), + options.url, + options.annotations, + options.arguments, + false)) { + return kExitFailure; + } + + if (!crashpad_client.UseHandler()) { + return kExitFailure; + } + + // Using the remaining arguments, start a new program with the new exception + // port in effect. + execvp(argv[0], argv); + PLOG(ERROR) << "execvp " << argv[0]; + return errno == ENOENT ? kExitExecENOENT : kExitExecFailure; +} + +} // namespace +} // namespace crashpad + +int main(int argc, char* argv[]) { + return crashpad::RunWithCrashpadMain(argc, argv); +}
diff --git a/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist b/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist new file mode 100644 index 0000000..faebead --- /dev/null +++ b/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>SecTaskAccess</key> + <array> + <string>allowed</string> + </array> +</dict> +</plist>
diff --git a/third_party/crashpad/crashpad/tools/tool_support.cc b/third_party/crashpad/crashpad/tools/tool_support.cc new file mode 100644 index 0000000..5d3592bc --- /dev/null +++ b/third_party/crashpad/crashpad/tools/tool_support.cc
@@ -0,0 +1,101 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "tools/tool_support.h" + +#include <stdio.h> + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "package.h" + +namespace crashpad { + +// static +void ToolSupport::Version(const base::FilePath& me) { + fprintf(stderr, + "%" PRFilePath " (%s) %s\n%s\n", + me.value().c_str(), + PACKAGE_NAME, + PACKAGE_VERSION, + PACKAGE_COPYRIGHT); +} + +// static +void ToolSupport::UsageTail(const base::FilePath& me) { + fprintf(stderr, + "\nReport %" PRFilePath " bugs to\n%s\n%s home page: <%s>\n", + me.value().c_str(), + PACKAGE_BUGREPORT, + PACKAGE_NAME, + PACKAGE_URL); +} + +// static +void ToolSupport::UsageHint(const base::FilePath& me, const char* hint) { + if (hint) { + fprintf(stderr, "%" PRFilePath ": %s\n", me.value().c_str(), hint); + } + fprintf(stderr, + "Try '%" PRFilePath " --help' for more information.\n", + me.value().c_str()); +} + +#if defined(OS_POSIX) +// static +void ToolSupport::Version(const std::string& me) { + Version(base::FilePath(me)); +} + +// static +void ToolSupport::UsageTail(const std::string& me) { + UsageTail(base::FilePath(me)); +} + +// static +void ToolSupport::UsageHint(const std::string& me, const char* hint) { + UsageHint(base::FilePath(me), hint); +} +#endif // OS_POSIX + +#if defined(OS_WIN) + +// static +int ToolSupport::Wmain(int argc, wchar_t* argv[], int (*entry)(int, char* [])) { + scoped_ptr<char* []> argv_as_utf8(new char* [argc + 1]); + std::vector<std::string> storage; + storage.reserve(argc); + for (int i = 0; i < argc; ++i) { + storage.push_back(base::UTF16ToUTF8(argv[i])); + argv_as_utf8[i] = &storage[i][0]; + } + argv_as_utf8[argc] = nullptr; + return entry(argc, argv_as_utf8.get()); +} + +#endif // OS_WIN + +// static +base::FilePath::StringType ToolSupport::CommandLineArgumentToFilePathStringType( + const base::StringPiece& path) { +#if defined(OS_POSIX) + return path.as_string(); +#elif defined(OS_WIN) + return base::UTF8ToUTF16(path); +#endif // OS_POSIX +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/tools/tool_support.h b/third_party/crashpad/crashpad/tools/tool_support.h new file mode 100644 index 0000000..9099691 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/tool_support.h
@@ -0,0 +1,82 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TOOLS_TOOL_SUPPORT_H_ +#define CRASHPAD_TOOLS_TOOL_SUPPORT_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" + +namespace crashpad { + +//! \brief Common functions used by command line tools. +class ToolSupport { + public: + //! \brief Handles `--version`. + //! + //! \param[in] me The tool’s name, the basename of `argv[0]`. + static void Version(const base::FilePath& me); + + //! \brief Prints the footer for `--help`. + //! + //! \param[in] me The tool’s name, the basename of `argv[0]`. + static void UsageTail(const base::FilePath& me); + + //! \brief Suggests using `--help` when a command line tool can’t make sense + //! of its arguments. + //! + //! \param[in] me The tool’s name, the basename of `argv[0]`. + static void UsageHint(const base::FilePath& me, const char* hint); + +#if defined(OS_POSIX) || DOXYGEN + //! \copydoc Version + static void Version(const std::string& me); + + //! \copydoc UsageTail + static void UsageTail(const std::string& me); + + //! \copydoc UsageHint + static void UsageHint(const std::string& me, const char* hint); +#endif // OS_POSIX + +#if defined(OS_WIN) || DOXYGEN + //! \brief Converts \a argv `wchar_t` UTF-16 to UTF-8, and passes onwards to a + //! UTF-8 entry point. + //! + //! \return The return value of \a entry. + static int Wmain(int argc, wchar_t* argv[], int (*entry)(int, char*[])); +#endif // OS_WIN + + //! \brief Converts a command line argument to the string type suitable for + //! base::FilePath. + //! + //! On POSIX, this is a no-op. On Windows, assumes that Wmain() was used, and + //! the input argument was converted from UTF-16 in a `wchar_t*` to UTF-8 in a + //! `char*`. This undoes that transformation. + //! + //! \sa Wmain() + static base::FilePath::StringType CommandLineArgumentToFilePathStringType( + const base::StringPiece& arg); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ToolSupport); +}; + +} // namespace crashpad + +#endif // CRASHPAD_TOOLS_TOOL_SUPPORT_H_
diff --git a/third_party/crashpad/crashpad/tools/tools.gyp b/third_party/crashpad/crashpad/tools/tools.gyp new file mode 100644 index 0000000..4876300 --- /dev/null +++ b/third_party/crashpad/crashpad/tools/tools.gyp
@@ -0,0 +1,193 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_tool_support', + 'type': 'static_library', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'tool_support.cc', + 'tool_support.h', + ], + }, + { + 'target_name': 'crashpad_database_util', + 'type': 'executable', + 'dependencies': [ + 'crashpad_tool_support', + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'crashpad_database_util.cc', + ], + }, + { + 'target_name': 'generate_dump', + 'type': 'executable', + 'dependencies': [ + 'crashpad_tool_support', + '../compat/compat.gyp:crashpad_compat', + '../minidump/minidump.gyp:crashpad_minidump', + '../snapshot/snapshot.gyp:crashpad_snapshot', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'generate_dump.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-sectcreate', + '__TEXT', + '__info_plist', + '<(sectaskaccess_info_plist)' + ], + }, + }], + ], + } + ], + 'conditions': [ + ['OS=="mac"', { + 'variables': { + # Programs that use task_for_pid() can indicate to taskgated(8) in their + # Info.plist that they are allowed to call that function. In order for + # this to work, the programs in question must be signed by an authority + # trusted by the system. Signing is beyond the scope of the build, but + # the key to make this work is placed in Info.plist to enable the + # desired behavior once the tools that require this access are signed. + # + # The tools built here are flat-file executables, and are not bundled. + # To have an Info.plist, they must have a special __TEXT,__info_plist + # section. This section is created at link time. + # + # The Info.plist for this purpose is mac/sectaskaccess_info.plist and is + # referenced by OTHER_LDFLAGS. ninja runs the link step from the output + # directory such as out/Release, and requires a relative path from that + # directory. Xcode runs the link step from the directory of the + # .xcodeproj, which is the directory of the .gyp file. + 'conditions': [ + ['GENERATOR=="ninja"', { + 'sectaskaccess_info_plist': '<!(pwd)/mac/sectaskaccess_info.plist', + }, { # else: GENERATOR!="ninja" + 'sectaskaccess_info_plist': 'mac/sectaskaccess_info.plist', + }], + ], + }, + + 'targets': [ + { + 'target_name': 'catch_exception_tool', + 'type': 'executable', + 'dependencies': [ + 'crashpad_tool_support', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'mac/catch_exception_tool.cc', + ], + }, + { + 'target_name': 'exception_port_tool', + 'type': 'executable', + 'dependencies': [ + 'crashpad_tool_support', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'mac/exception_port_tool.cc', + ], + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-sectcreate', + '__TEXT', + '__info_plist', + '<(sectaskaccess_info_plist)' + ], + }, + }, + { + 'target_name': 'on_demand_service_tool', + 'type': 'executable', + 'dependencies': [ + 'crashpad_tool_support', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework', + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + ], + }, + 'sources': [ + 'mac/on_demand_service_tool.mm', + ], + }, + { + 'target_name': 'run_with_crashpad', + 'type': 'executable', + 'dependencies': [ + 'crashpad_tool_support', + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'mac/run_with_crashpad.cc', + ], + }, + ], + }], + ], +}
diff --git a/third_party/crashpad/crashpad/util/file/file_io.cc b/third_party/crashpad/crashpad/util/file/file_io.cc new file mode 100644 index 0000000..98eb340 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_io.cc
@@ -0,0 +1,74 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_io.h" + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" + +namespace crashpad { + +bool LoggingReadFile(FileHandle file, void* buffer, size_t size) { + FileOperationResult expect = base::checked_cast<FileOperationResult>(size); + FileOperationResult rv = ReadFile(file, buffer, size); + if (rv < 0) { + PLOG(ERROR) << "read"; + return false; + } + if (rv != expect) { + LOG(ERROR) << "read: expected " << expect << ", observed " << rv; + return false; + } + + return true; +} + +bool LoggingWriteFile(FileHandle file, const void* buffer, size_t size) { + FileOperationResult expect = base::checked_cast<FileOperationResult>(size); + FileOperationResult rv = WriteFile(file, buffer, size); + if (rv < 0) { + PLOG(ERROR) << "write"; + return false; + } + if (rv != expect) { + LOG(ERROR) << "write: expected " << expect << ", observed " << rv; + return false; + } + + return true; +} + +void CheckedReadFile(FileHandle file, void* buffer, size_t size) { + CHECK(LoggingReadFile(file, buffer, size)); +} + +void CheckedWriteFile(FileHandle file, const void* buffer, size_t size) { + CHECK(LoggingWriteFile(file, buffer, size)); +} + +void CheckedReadFileAtEOF(FileHandle file) { + char c; + FileOperationResult rv = ReadFile(file, &c, 1); + if (rv < 0) { + PCHECK(rv == 0) << "read"; + } else { + CHECK_EQ(rv, 0) << "read"; + } +} + +void CheckedCloseFile(FileHandle file) { + CHECK(LoggingCloseFile(file)); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_io.h b/third_party/crashpad/crashpad/util/file/file_io.h new file mode 100644 index 0000000..55909415 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_io.h
@@ -0,0 +1,347 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_FILE_FILE_IO_H_ +#define CRASHPAD_UTIL_FILE_FILE_IO_H_ + +#include <sys/types.h> + +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include "base/files/scoped_file.h" +#elif defined(OS_WIN) +#include <windows.h> +#include "util/win/scoped_handle.h" +#endif + +//! \file + +#if defined(OS_POSIX) || DOXYGEN + +//! \brief A `PLOG()` macro usable for standard input/output error conditions. +//! +//! The `PLOG()` macro uses `errno` on POSIX and is appropriate to report +//! errors from standard input/output functions. On Windows, `PLOG()` uses +//! `GetLastError()`, and cannot be used to report errors from standard +//! input/output functions. This macro uses `PLOG()` when appropriate for +//! standard I/O functions, and `LOG()` otherwise. +#define STDIO_PLOG(x) PLOG(x) + +#else + +#define STDIO_PLOG(x) LOG(x) +#define fseeko(file, offset, whence) _fseeki64(file, offset, whence) +#define ftello(file) _ftelli64(file) + +#endif + +namespace base { +class FilePath; +} // namespace base + +namespace crashpad { + +#if defined(OS_POSIX) || DOXYGEN + +//! \brief Platform-specific alias for a low-level file handle. +using FileHandle = int; + +//! \brief Platform-specific alias for a position in an open file. +using FileOffset = off_t; + +//! \brief Scoped wrapper of a FileHandle. +using ScopedFileHandle = base::ScopedFD; + +//! \brief A value that can never be a valid FileHandle. +const FileHandle kInvalidFileHandle = -1; + +using FileOperationResult = ssize_t; + +#elif defined(OS_WIN) + +using FileHandle = HANDLE; +using FileOffset = LONGLONG; +using ScopedFileHandle = ScopedFileHANDLE; +using FileOperationResult = LONG_PTR; + +const FileHandle kInvalidFileHandle = INVALID_HANDLE_VALUE; + +#endif + +//! \brief Determines the mode that LoggingOpenFileForWrite() uses. +enum class FileWriteMode { + //! \brief Opens the file if it exists, or fails if it does not. + kReuseOrFail, + + //! \brief Opens the file if it exists, or creates a new file. + kReuseOrCreate, + + //! \brief Creates a new file. If the file already exists, it will be + //! overwritten. + kTruncateOrCreate, + + //! \brief Creates a new file. If the file already exists, the open will fail. + kCreateOrFail, +}; + +//! \brief Determines the permissions bits for files created on POSIX systems. +enum class FilePermissions : bool { + //! \brief Equivalent to `0600`. + kOwnerOnly, + + //! \brief Equivalent to `0644`. + kWorldReadable, +}; + +//! \brief Determines the locking mode that LoggingLockFile() uses. +enum class FileLocking : bool { + //! \brief Equivalent to `flock()` with `LOCK_SH`. + kShared, + + //! \brief Equivalent to `flock()` with `LOCK_EX`. + kExclusive, +}; + +//! \brief Reads from a file, retrying when interrupted on POSIX or following a +//! short read. +//! +//! This function reads into \a buffer, stopping only when \a size bytes have +//! been read or when end-of-file has been reached. On Windows, reading from +//! sockets is not currently supported. +//! +//! \return The number of bytes read and placed into \a buffer, or `-1` on +//! error, with `errno` or `GetLastError()` set appropriately. On error, a +//! portion of \a file may have been read into \a buffer. +//! +//! \sa WriteFile +//! \sa LoggingReadFile +//! \sa CheckedReadFile +//! \sa CheckedReadFileAtEOF +FileOperationResult ReadFile(FileHandle file, void* buffer, size_t size); + +//! \brief Writes to a file, retrying when interrupted or following a short +//! write on POSIX. +//! +//! This function writes to \a file, stopping only when \a size bytes have been +//! written. +//! +//! \return The number of bytes written from \a buffer, or `-1` on error, with +//! `errno` or `GetLastError()` set appropriately. On error, a portion of +//! \a buffer may have been written to \a file. +//! +//! \sa ReadFile +//! \sa LoggingWriteFile +//! \sa CheckedWriteFile +FileOperationResult WriteFile(FileHandle file, const void* buffer, size_t size); + +//! \brief Wraps ReadFile(), ensuring that exactly \a size bytes are read. +//! +//! \return `true` on success. If \a size is out of the range of possible +//! ReadFile() return values, if the underlying ReadFile() fails, or if +//! other than \a size bytes were read, this function logs a message and +//! returns `false`. +//! +//! \sa LoggingWriteFile +//! \sa ReadFile +//! \sa CheckedReadFile +//! \sa CheckedReadFileAtEOF +bool LoggingReadFile(FileHandle file, void* buffer, size_t size); + +//! \brief Wraps WriteFile(), ensuring that exactly \a size bytes are written. +//! +//! \return `true` on success. If \a size is out of the range of possible +//! WriteFile() return values, if the underlying WriteFile() fails, or if +//! other than \a size bytes were written, this function logs a message and +//! returns `false`. +//! +//! \sa LoggingReadFile +//! \sa WriteFile +//! \sa CheckedWriteFile +bool LoggingWriteFile(FileHandle file, const void* buffer, size_t size); + +//! \brief Wraps ReadFile(), ensuring that exactly \a size bytes are read. +//! +//! If \a size is out of the range of possible ReadFile() return values, if the +//! underlying ReadFile() fails, or if other than \a size bytes were read, this +//! function causes execution to terminate without returning. +//! +//! \sa CheckedWriteFile +//! \sa ReadFile +//! \sa LoggingReadFile +//! \sa CheckedReadFileAtEOF +void CheckedReadFile(FileHandle file, void* buffer, size_t size); + +//! \brief Wraps WriteFile(), ensuring that exactly \a size bytes are written. +//! +//! If \a size is out of the range of possible WriteFile() return values, if the +//! underlying WriteFile() fails, or if other than \a size bytes were written, +//! this function causes execution to terminate without returning. +//! +//! \sa CheckedReadFile +//! \sa WriteFile +//! \sa LoggingWriteFile +void CheckedWriteFile(FileHandle file, const void* buffer, size_t size); + +//! \brief Wraps ReadFile(), ensuring that it indicates end-of-file. +//! +//! Attempts to read a single byte from \a file, expecting no data to be read. +//! If the underlying ReadFile() fails, or if a byte actually is read, this +//! function causes execution to terminate without returning. +//! +//! \sa CheckedReadFile +//! \sa ReadFile +void CheckedReadFileAtEOF(FileHandle file); + +//! \brief Wraps `open()` or `CreateFile()`, opening an existing file for +//! reading. +//! +//! \return The newly opened FileHandle, or an invalid FileHandle on failure. +//! +//! \sa ScopedFileHandle +//! \sa OpenFileForWrite +//! \sa OpenFileForReadAndWrite +//! \sa LoggingOpenFileForRead +FileHandle OpenFileForRead(const base::FilePath& path); + +//! \brief Wraps `open()` or `CreateFile()`, creating a file for output. +//! +//! \a mode determines the style (truncate, reuse, etc.) that is used to open +//! the file. On POSIX, \a permissions determines the value that is passed as +//! `mode` to `open()`. On Windows, the file is always opened in binary mode +//! (that is, no CRLF translation). On Windows, the file is opened for sharing, +//! see LoggingLockFile() and LoggingUnlockFile() to control concurrent access. +//! +//! \return The newly opened FileHandle, or an invalid FileHandle on failure. +//! +//! \sa ScopedFileHandle +//! \sa OpenFileForRead +//! \sa OpenFileForReadAndWrite +//! \sa LoggingOpenFileForWrite +FileHandle OpenFileForWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions); + +//! \brief Wraps `open()` or `CreateFile()`, creating a file for both input and +//! output. +//! +//! \a mode determines the style (truncate, reuse, etc.) that is used to open +//! the file. On POSIX, \a permissions determines the value that is passed as +//! `mode` to `open()`. On Windows, the file is always opened in binary mode +//! (that is, no CRLF translation). On Windows, the file is opened for sharing, +//! see LoggingLockFile() and LoggingUnlockFile() to control concurrent access. +//! +//! \return The newly opened FileHandle, or an invalid FileHandle on failure. +//! +//! \sa ScopedFileHandle +//! \sa OpenFileForRead +//! \sa OpenFileForWrite +//! \sa LoggingOpenFileForReadAndWrite +FileHandle OpenFileForReadAndWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions); + +//! \brief Wraps OpenFileForRead(), logging an error if the operation fails. +//! +//! \return The newly opened FileHandle, or an invalid FileHandle on failure. +//! +//! \sa ScopedFileHandle +//! \sa LoggingOpenFileForWrite +//! \sa LoggingOpenFileForReadAndWrite +FileHandle LoggingOpenFileForRead(const base::FilePath& path); + +//! \brief Wraps OpenFileForWrite(), logging an error if the operation fails. +//! +//! \return The newly opened FileHandle, or an invalid FileHandle on failure. +//! +//! \sa ScopedFileHandle +//! \sa LoggingOpenFileForRead +//! \sa LoggingOpenFileForReadAndWrite +FileHandle LoggingOpenFileForWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions); + +//! \brief Wraps OpenFileForReadAndWrite(), logging an error if the operation +//! fails. +//! +//! \return The newly opened FileHandle, or an invalid FileHandle on failure. +//! +//! \sa ScopedFileHandle +//! \sa LoggingOpenFileForRead +//! \sa LoggingOpenFileForWrite +FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions); + +//! \brief Locks the given \a file using `flock()` on POSIX or `LockFileEx()` on +//! Windows. +//! +//! It is an error to attempt to lock a file in a different mode when it is +//! already locked. This call will block until the lock is acquired. The +//! entire file is locked. +//! +//! If \a locking is FileLocking::kShared, \a file must have been opened for +//! reading, and if it's FileLocking::kExclusive, \a file must have been opened +//! for writing. +//! +//! \param[in] file The open file handle to be locked. +//! \param[in] locking Controls whether the lock is a shared reader lock, or an +//! exclusive writer lock. +//! +//! \return `true` on success, or `false` and a message will be logged. +bool LoggingLockFile(FileHandle file, FileLocking locking); + +//! \brief Unlocks a file previously locked with LoggingLockFile(). +//! +//! It is an error to attempt to unlock a file that was not previously locked. +//! A previously-locked file should be unlocked before closing the file handle, +//! otherwise on some OSs the lock may not be released immediately. +//! +//! \param[in] file The open locked file handle to be unlocked. +//! +//! \return `true` on success, or `false` and a message will be logged. +bool LoggingUnlockFile(FileHandle file); + +//! \brief Wraps `lseek()` or `SetFilePointerEx()`. Logs an error if the +//! operation fails. +//! +//! Repositions the offset of the open \a file to the specified \a offset, +//! relative to \a whence. \a whence must be one of `SEEK_SET`, `SEEK_CUR`, or +//! `SEEK_END`, and is interpreted in the usual way. +//! +//! \return The resulting offset in bytes from the beginning of the file, or +//! `-1` on failure. +FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence); + +//! \brief Truncates the given \a file to zero bytes in length. +//! +//! \return `true` on success, or `false`, and a message will be logged. +bool LoggingTruncateFile(FileHandle file); + +//! \brief Wraps `close()` or `CloseHandle()`, logging an error if the operation +//! fails. +//! +//! \return On success, `true` is returned. On failure, an error is logged and +//! `false` is returned. +bool LoggingCloseFile(FileHandle file); + +//! \brief Wraps `close()` or `CloseHandle()`, ensuring that it succeeds. +//! +//! If the underlying function fails, this function causes execution to +//! terminate without returning. +void CheckedCloseFile(FileHandle file); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FILE_FILE_IO_H_
diff --git a/third_party/crashpad/crashpad/util/file/file_io_posix.cc b/third_party/crashpad/crashpad/util/file/file_io_posix.cc new file mode 100644 index 0000000..ce08a39 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
@@ -0,0 +1,190 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_io.h" + +#include <fcntl.h> +#include <sys/file.h> +#include <unistd.h> + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/posix/eintr_wrapper.h" + +namespace { + +struct ReadTraits { + using VoidBufferType = void*; + using CharBufferType = char*; + static crashpad::FileOperationResult Operate(int fd, + CharBufferType buffer, + size_t size) { + return read(fd, buffer, size); + } +}; + +struct WriteTraits { + using VoidBufferType = const void*; + using CharBufferType = const char*; + static crashpad::FileOperationResult Operate(int fd, + CharBufferType buffer, + size_t size) { + return write(fd, buffer, size); + } +}; + +template <typename Traits> +crashpad::FileOperationResult +ReadOrWrite(int fd, typename Traits::VoidBufferType buffer, size_t size) { + typename Traits::CharBufferType buffer_c = + reinterpret_cast<typename Traits::CharBufferType>(buffer); + + crashpad::FileOperationResult total_bytes = 0; + while (size > 0) { + crashpad::FileOperationResult bytes = + HANDLE_EINTR(Traits::Operate(fd, buffer_c, size)); + if (bytes < 0) { + return bytes; + } else if (bytes == 0) { + break; + } + + buffer_c += bytes; + size -= bytes; + total_bytes += bytes; + } + + return total_bytes; +} + +} // namespace + +namespace crashpad { + +namespace { + +FileHandle OpenFileForOutput(int rdwr_or_wronly, + const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + DCHECK(rdwr_or_wronly & (O_RDWR | O_WRONLY)); + DCHECK_EQ(rdwr_or_wronly & ~(O_RDWR | O_WRONLY), 0); + + int flags = rdwr_or_wronly; + + switch (mode) { + case FileWriteMode::kReuseOrFail: + break; + case FileWriteMode::kReuseOrCreate: + flags |= O_CREAT; + break; + case FileWriteMode::kTruncateOrCreate: + flags |= O_CREAT | O_TRUNC; + break; + case FileWriteMode::kCreateOrFail: + flags |= O_CREAT | O_EXCL; + break; + } + + return HANDLE_EINTR( + open(path.value().c_str(), + flags, + permissions == FilePermissions::kWorldReadable ? 0644 : 0600)); +} + +} // namespace + +FileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) { + return ReadOrWrite<ReadTraits>(file, buffer, size); +} + +FileOperationResult WriteFile(FileHandle file, + const void* buffer, + size_t size) { + return ReadOrWrite<WriteTraits>(file, buffer, size); +} + +FileHandle OpenFileForRead(const base::FilePath& path) { + return HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); +} + +FileHandle OpenFileForWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + return OpenFileForOutput(O_WRONLY, path, mode, permissions); +} + +FileHandle OpenFileForReadAndWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + return OpenFileForOutput(O_RDWR, path, mode, permissions); +} + +FileHandle LoggingOpenFileForRead(const base::FilePath& path) { + FileHandle fd = OpenFileForRead(path); + PLOG_IF(ERROR, fd < 0) << "open " << path.value(); + return fd; +} + +FileHandle LoggingOpenFileForWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + FileHandle fd = OpenFileForWrite(path, mode, permissions); + PLOG_IF(ERROR, fd < 0) << "open " << path.value(); + return fd; +} + +FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + FileHandle fd = OpenFileForReadAndWrite(path, mode, permissions); + PLOG_IF(ERROR, fd < 0) << "open " << path.value(); + return fd; +} + +bool LoggingLockFile(FileHandle file, FileLocking locking) { + int operation = (locking == FileLocking::kShared) ? LOCK_SH : LOCK_EX; + int rv = HANDLE_EINTR(flock(file, operation)); + PLOG_IF(ERROR, rv != 0) << "flock"; + return rv == 0; +} + +bool LoggingUnlockFile(FileHandle file) { + int rv = flock(file, LOCK_UN); + PLOG_IF(ERROR, rv != 0) << "flock"; + return rv == 0; +} + +FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) { + off_t rv = lseek(file, offset, whence); + PLOG_IF(ERROR, rv < 0) << "lseek"; + return rv; +} + +bool LoggingTruncateFile(FileHandle file) { + if (HANDLE_EINTR(ftruncate(file, 0)) != 0) { + PLOG(ERROR) << "ftruncate"; + return false; + } + return true; +} + +bool LoggingCloseFile(FileHandle file) { + int rv = IGNORE_EINTR(close(file)); + PLOG_IF(ERROR, rv != 0) << "close"; + return rv == 0; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_io_test.cc b/third_party/crashpad/crashpad/util/file/file_io_test.cc new file mode 100644 index 0000000..77529ea5 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_io_test.cc
@@ -0,0 +1,311 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_io.h" + +#include "base/atomicops.h" +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/file.h" +#include "test/scoped_temp_dir.h" +#include "util/misc/implicit_cast.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +void TestOpenFileForWrite(FileHandle (*opener)(const base::FilePath&, + FileWriteMode, + FilePermissions)) { + ScopedTempDir temp_dir; + base::FilePath file_path_1 = + temp_dir.path().Append(FILE_PATH_LITERAL("file_1")); + ASSERT_FALSE(FileExists(file_path_1)); + + ScopedFileHandle file_handle(opener(file_path_1, + FileWriteMode::kReuseOrFail, + FilePermissions::kWorldReadable)); + EXPECT_EQ(kInvalidFileHandle, file_handle); + EXPECT_FALSE(FileExists(file_path_1)); + + file_handle.reset(opener(file_path_1, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_1)); + EXPECT_EQ(0, FileSize(file_path_1)); + + file_handle.reset(opener(file_path_1, + FileWriteMode::kReuseOrCreate, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_1)); + EXPECT_EQ(0, FileSize(file_path_1)); + + const char data = '%'; + EXPECT_TRUE(LoggingWriteFile(file_handle.get(), &data, sizeof(data))); + + // Close file_handle to ensure that the write is flushed to disk. + file_handle.reset(); + EXPECT_EQ(implicit_cast<FileOffset>(sizeof(data)), FileSize(file_path_1)); + + file_handle.reset(opener(file_path_1, + FileWriteMode::kReuseOrCreate, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_1)); + EXPECT_EQ(implicit_cast<FileOffset>(sizeof(data)), FileSize(file_path_1)); + + file_handle.reset(opener(file_path_1, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)); + EXPECT_EQ(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_1)); + EXPECT_EQ(implicit_cast<FileOffset>(sizeof(data)), FileSize(file_path_1)); + + file_handle.reset(opener(file_path_1, + FileWriteMode::kReuseOrFail, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_1)); + EXPECT_EQ(implicit_cast<FileOffset>(sizeof(data)), FileSize(file_path_1)); + + file_handle.reset(opener(file_path_1, + FileWriteMode::kTruncateOrCreate, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_1)); + EXPECT_EQ(0, FileSize(file_path_1)); + + base::FilePath file_path_2 = + temp_dir.path().Append(FILE_PATH_LITERAL("file_2")); + ASSERT_FALSE(FileExists(file_path_2)); + + file_handle.reset(opener(file_path_2, + FileWriteMode::kTruncateOrCreate, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_2)); + EXPECT_EQ(0, FileSize(file_path_2)); + + base::FilePath file_path_3 = + temp_dir.path().Append(FILE_PATH_LITERAL("file_3")); + ASSERT_FALSE(FileExists(file_path_3)); + + file_handle.reset(opener(file_path_3, + FileWriteMode::kReuseOrCreate, + FilePermissions::kWorldReadable)); + EXPECT_NE(kInvalidFileHandle, file_handle); + EXPECT_TRUE(FileExists(file_path_3)); + EXPECT_EQ(0, FileSize(file_path_3)); +} + +TEST(FileIO, OpenFileForWrite) { + TestOpenFileForWrite(OpenFileForWrite); +} + +TEST(FileIO, OpenFileForReadAndWrite) { + TestOpenFileForWrite(OpenFileForReadAndWrite); +} + +TEST(FileIO, LoggingOpenFileForWrite) { + TestOpenFileForWrite(LoggingOpenFileForWrite); +} + +TEST(FileIO, LoggingOpenFileForReadAndWrite) { + TestOpenFileForWrite(LoggingOpenFileForReadAndWrite); +} + +enum class ReadOrWrite : bool { + kRead, + kWrite, +}; + +void FileShareModeTest(ReadOrWrite first, ReadOrWrite second) { + ScopedTempDir temp_dir; + base::FilePath shared_file = + temp_dir.path().Append(FILE_PATH_LITERAL("shared_file")); + { + // Create an empty file to work on. + ScopedFileHandle create( + LoggingOpenFileForWrite(shared_file, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + } + + auto handle1 = ScopedFileHandle( + (first == ReadOrWrite::kRead) + ? LoggingOpenFileForRead(shared_file) + : LoggingOpenFileForWrite(shared_file, + FileWriteMode::kReuseOrCreate, + FilePermissions::kOwnerOnly)); + ASSERT_NE(handle1, kInvalidFileHandle); + auto handle2 = ScopedFileHandle( + (second == ReadOrWrite::kRead) + ? LoggingOpenFileForRead(shared_file) + : LoggingOpenFileForWrite(shared_file, + FileWriteMode::kReuseOrCreate, + FilePermissions::kOwnerOnly)); + EXPECT_NE(handle2, kInvalidFileHandle); + + EXPECT_NE(handle1.get(), handle2.get()); +} + +TEST(FileIO, FileShareMode_Read_Read) { + FileShareModeTest(ReadOrWrite::kRead, ReadOrWrite::kRead); +} + +TEST(FileIO, FileShareMode_Read_Write) { + FileShareModeTest(ReadOrWrite::kRead, ReadOrWrite::kWrite); +} + +TEST(FileIO, FileShareMode_Write_Read) { + FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kRead); +} + +TEST(FileIO, FileShareMode_Write_Write) { + FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kWrite); +} + +TEST(FileIO, MultipleSharedLocks) { + ScopedTempDir temp_dir; + base::FilePath shared_file = + temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock")); + + { + // Create an empty file to lock. + ScopedFileHandle create( + LoggingOpenFileForWrite(shared_file, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + } + + auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(shared_file)); + ASSERT_NE(handle1, kInvalidFileHandle); + EXPECT_TRUE(LoggingLockFile(handle1.get(), FileLocking::kShared)); + + auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(shared_file)); + ASSERT_NE(handle1, kInvalidFileHandle); + EXPECT_TRUE(LoggingLockFile(handle2.get(), FileLocking::kShared)); + + EXPECT_TRUE(LoggingUnlockFile(handle1.get())); + EXPECT_TRUE(LoggingUnlockFile(handle2.get())); +} + +class LockingTestThread : public Thread { + public: + LockingTestThread() + : file_(), lock_type_(), iterations_(), actual_iterations_() {} + + void Init(FileHandle file, + FileLocking lock_type, + int iterations, + base::subtle::Atomic32* actual_iterations) { + ASSERT_NE(file, kInvalidFileHandle); + file_ = ScopedFileHandle(file); + lock_type_ = lock_type; + iterations_ = iterations; + actual_iterations_ = actual_iterations; + } + + private: + void ThreadMain() override { + for (int i = 0; i < iterations_; ++i) { + EXPECT_TRUE(LoggingLockFile(file_.get(), lock_type_)); + base::subtle::NoBarrier_AtomicIncrement(actual_iterations_, 1); + EXPECT_TRUE(LoggingUnlockFile(file_.get())); + } + } + + ScopedFileHandle file_; + FileLocking lock_type_; + int iterations_; + base::subtle::Atomic32* actual_iterations_; + + DISALLOW_COPY_AND_ASSIGN(LockingTestThread); +}; + +void LockingTest(FileLocking main_lock, FileLocking other_locks) { + ScopedTempDir temp_dir; + base::FilePath shared_file = + temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock")); + + { + // Create an empty file to lock. + ScopedFileHandle create( + LoggingOpenFileForWrite(shared_file, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + } + + auto initial = ScopedFileHandle( + (main_lock == FileLocking::kShared) + ? LoggingOpenFileForRead(shared_file) + : LoggingOpenFileForWrite(shared_file, + FileWriteMode::kReuseOrCreate, + FilePermissions::kOwnerOnly)); + ASSERT_NE(initial, kInvalidFileHandle); + ASSERT_TRUE(LoggingLockFile(initial.get(), main_lock)); + + base::subtle::Atomic32 actual_iterations = 0; + + LockingTestThread threads[20]; + int expected_iterations = 0; + for (size_t index = 0; index < arraysize(threads); ++index) { + int iterations_for_this_thread = static_cast<int>(index * 10); + threads[index].Init( + (other_locks == FileLocking::kShared) + ? LoggingOpenFileForRead(shared_file) + : LoggingOpenFileForWrite(shared_file, + FileWriteMode::kReuseOrCreate, + FilePermissions::kOwnerOnly), + other_locks, + iterations_for_this_thread, + &actual_iterations); + expected_iterations += iterations_for_this_thread; + + ASSERT_NO_FATAL_FAILURE(threads[index].Start()); + } + + base::subtle::Atomic32 result = + base::subtle::NoBarrier_Load(&actual_iterations); + EXPECT_EQ(0, result); + + ASSERT_TRUE(LoggingUnlockFile(initial.get())); + + for (auto& t : threads) + t.Join(); + + result = base::subtle::NoBarrier_Load(&actual_iterations); + EXPECT_EQ(expected_iterations, result); +} + +TEST(FileIO, ExclusiveVsExclusives) { + LockingTest(FileLocking::kExclusive, FileLocking::kExclusive); +} + +TEST(FileIO, ExclusiveVsShareds) { + LockingTest(FileLocking::kExclusive, FileLocking::kShared); +} + +TEST(FileIO, SharedVsExclusives) { + LockingTest(FileLocking::kShared, FileLocking::kExclusive); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_io_win.cc b/third_party/crashpad/crashpad/util/file/file_io_win.cc new file mode 100644 index 0000000..def2a5a --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_io_win.cc
@@ -0,0 +1,239 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_io.h" + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/utf_string_conversions.h" + +namespace { + +bool IsSocketHandle(HANDLE file) { + if (GetFileType(file) == FILE_TYPE_PIPE) { + // FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous + // pipe. If we are unable to retrieve the pipe information, we know it's a + // socket. + return !GetNamedPipeInfo(file, NULL, NULL, NULL, NULL); + } + return false; +} + +} // namespace + +namespace crashpad { + +namespace { + +FileHandle OpenFileForOutput(DWORD access, + const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + DCHECK(access & GENERIC_WRITE); + DCHECK_EQ(access & ~(GENERIC_READ | GENERIC_WRITE), 0u); + + DWORD disposition = 0; + switch (mode) { + case FileWriteMode::kReuseOrFail: + disposition = OPEN_EXISTING; + break; + case FileWriteMode::kReuseOrCreate: + disposition = OPEN_ALWAYS; + break; + case FileWriteMode::kTruncateOrCreate: + disposition = CREATE_ALWAYS; + break; + case FileWriteMode::kCreateOrFail: + disposition = CREATE_NEW; + break; + } + return CreateFile(path.value().c_str(), + access, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + disposition, + FILE_ATTRIBUTE_NORMAL, + nullptr); +} + +} // namespace + +// TODO(scottmg): Handle > DWORD sized writes if necessary. + +FileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) { + DCHECK(!IsSocketHandle(file)); + DWORD size_dword = base::checked_cast<DWORD>(size); + DWORD total_read = 0; + char* buffer_c = reinterpret_cast<char*>(buffer); + while (size_dword > 0) { + DWORD bytes_read; + BOOL success = ::ReadFile(file, buffer_c, size_dword, &bytes_read, nullptr); + if (!success) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + // When reading a pipe and the write handle has been closed, ReadFile + // fails with ERROR_BROKEN_PIPE, but only once all pending data has been + // read. + break; + } else if (GetLastError() != ERROR_MORE_DATA) { + return -1; + } + } else if (bytes_read == 0 && GetFileType(file) != FILE_TYPE_PIPE) { + // Zero bytes read for a file indicates reaching EOF. Zero bytes read from + // a pipe indicates only that there was a zero byte WriteFile issued on + // the other end, so continue reading. + break; + } + + buffer_c += bytes_read; + size_dword -= bytes_read; + total_read += bytes_read; + } + return total_read; +} + +FileOperationResult WriteFile(FileHandle file, + const void* buffer, + size_t size) { + // TODO(scottmg): This might need to handle the limit for pipes across a + // network in the future. + DWORD size_dword = base::checked_cast<DWORD>(size); + DWORD bytes_written; + BOOL rv = ::WriteFile(file, buffer, size_dword, &bytes_written, nullptr); + if (!rv) + return -1; + CHECK_EQ(bytes_written, size_dword); + return bytes_written; +} + +FileHandle OpenFileForRead(const base::FilePath& path) { + return CreateFile(path.value().c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + 0, + nullptr); +} + +FileHandle OpenFileForWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + return OpenFileForOutput(GENERIC_WRITE, path, mode, permissions); +} + +FileHandle OpenFileForReadAndWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + return OpenFileForOutput( + GENERIC_READ | GENERIC_WRITE, path, mode, permissions); +} + +FileHandle LoggingOpenFileForRead(const base::FilePath& path) { + FileHandle file = OpenFileForRead(path); + PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE) + << "CreateFile " << base::UTF16ToUTF8(path.value()); + return file; +} + +FileHandle LoggingOpenFileForWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + FileHandle file = OpenFileForWrite(path, mode, permissions); + PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE) + << "CreateFile " << base::UTF16ToUTF8(path.value()); + return file; +} + +FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path, + FileWriteMode mode, + FilePermissions permissions) { + FileHandle file = OpenFileForReadAndWrite(path, mode, permissions); + PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE) + << "CreateFile " << base::UTF16ToUTF8(path.value()); + return file; +} + +bool LoggingLockFile(FileHandle file, FileLocking locking) { + DWORD flags = + (locking == FileLocking::kExclusive) ? LOCKFILE_EXCLUSIVE_LOCK : 0; + + // Note that the `Offset` fields of overlapped indicate the start location for + // locking (beginning of file in this case), and `hEvent` must be also be set + // to 0. + OVERLAPPED overlapped = {0}; + if (!LockFileEx(file, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) { + PLOG(ERROR) << "LockFileEx"; + return false; + } + return true; +} + +bool LoggingUnlockFile(FileHandle file) { + // Note that the `Offset` fields of overlapped indicate the start location for + // locking (beginning of file in this case), and `hEvent` must be also be set + // to 0. + OVERLAPPED overlapped = {0}; + if (!UnlockFileEx(file, 0, MAXDWORD, MAXDWORD, &overlapped)) { + PLOG(ERROR) << "UnlockFileEx"; + return false; + } + return true; +} + +FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) { + DWORD method = 0; + switch (whence) { + case SEEK_SET: + method = FILE_BEGIN; + break; + case SEEK_CUR: + method = FILE_CURRENT; + break; + case SEEK_END: + method = FILE_END; + break; + default: + NOTREACHED(); + break; + } + + LARGE_INTEGER distance_to_move; + distance_to_move.QuadPart = offset; + LARGE_INTEGER new_offset; + BOOL result = SetFilePointerEx(file, distance_to_move, &new_offset, method); + if (!result) { + PLOG(ERROR) << "SetFilePointerEx"; + return -1; + } + return new_offset.QuadPart; +} + +bool LoggingTruncateFile(FileHandle file) { + if (LoggingSeekFile(file, 0, SEEK_SET) != 0) + return false; + if (!SetEndOfFile(file)) { + PLOG(ERROR) << "SetEndOfFile"; + return false; + } + return true; +} + +bool LoggingCloseFile(FileHandle file) { + BOOL rv = CloseHandle(file); + PLOG_IF(ERROR, !rv) << "CloseHandle"; + return !!rv; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_reader.cc b/third_party/crashpad/crashpad/util/file/file_reader.cc new file mode 100644 index 0000000..14d06c7 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_reader.cc
@@ -0,0 +1,136 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_reader.h" + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "build/build_config.h" + +namespace crashpad { + +bool FileReaderInterface::ReadExactly(void* data, size_t size) { + FileOperationResult expect = base::checked_cast<FileOperationResult>(size); + FileOperationResult rv = Read(data, size); + if (rv < 0) { + // Read() will have logged its own error. + return false; + } else if (rv != expect) { + LOG(ERROR) << "ReadExactly(): expected " << expect << ", observed " << rv; + return false; + } + + return true; +} + +WeakFileHandleFileReader::WeakFileHandleFileReader(FileHandle file_handle) + : file_handle_(file_handle) { +} + +WeakFileHandleFileReader::~WeakFileHandleFileReader() { +} + +FileOperationResult WeakFileHandleFileReader::Read(void* data, size_t size) { + DCHECK_NE(file_handle_, kInvalidFileHandle); + + // Don’t use LoggingReadFile(), which insists on a full read and only returns + // a bool. This method permits short reads and returns the number of bytes + // read. + base::checked_cast<FileOperationResult>(size); + FileOperationResult rv = ReadFile(file_handle_, data, size); + if (rv < 0) { + PLOG(ERROR) << "read"; + return -1; + } + + return rv; +} + +FileOffset WeakFileHandleFileReader::Seek(FileOffset offset, int whence) { + DCHECK_NE(file_handle_, kInvalidFileHandle); + return LoggingSeekFile(file_handle_, offset, whence); +} + +FileReader::FileReader() + : file_(), + weak_file_handle_file_reader_(kInvalidFileHandle) { +} + +FileReader::~FileReader() { +} + +bool FileReader::Open(const base::FilePath& path) { + CHECK(!file_.is_valid()); + file_.reset(LoggingOpenFileForRead(path)); + if (!file_.is_valid()) { + return false; + } + + weak_file_handle_file_reader_.set_file_handle(file_.get()); + return true; +} + +void FileReader::Close() { + CHECK(file_.is_valid()); + + weak_file_handle_file_reader_.set_file_handle(kInvalidFileHandle); + file_.reset(); +} + +FileOperationResult FileReader::Read(void* data, size_t size) { + DCHECK(file_.is_valid()); + return weak_file_handle_file_reader_.Read(data, size); +} + +FileOffset FileReader::Seek(FileOffset offset, int whence) { + DCHECK(file_.is_valid()); + return weak_file_handle_file_reader_.Seek(offset, whence); +} + +WeakStdioFileReader::WeakStdioFileReader(FILE* file) + : file_(file) { +} + +WeakStdioFileReader::~WeakStdioFileReader() { +} + +FileOperationResult WeakStdioFileReader::Read(void* data, size_t size) { + DCHECK(file_); + + size_t rv = fread(data, 1, size, file_); + if (rv < size && ferror(file_)) { + STDIO_PLOG(ERROR) << "fread"; + return -1; + } + + return rv; +} + +FileOffset WeakStdioFileReader::Seek(FileOffset offset, int whence) { + DCHECK(file_); + if (fseeko(file_, offset, whence) == -1) { + STDIO_PLOG(ERROR) << "fseeko"; + return -1; + } + + FileOffset new_offset = ftello(file_); + if (new_offset == -1) { + STDIO_PLOG(ERROR) << "ftello"; + return -1; + } + + return new_offset; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_reader.h b/third_party/crashpad/crashpad/util/file/file_reader.h new file mode 100644 index 0000000..5d77b87 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_reader.h
@@ -0,0 +1,181 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_FILE_FILE_READER_H_ +#define CRASHPAD_UTIL_FILE_FILE_READER_H_ + +#include <stdio.h> +#include <sys/types.h> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "util/file/file_io.h" +#include "util/file/file_seeker.h" + +namespace crashpad { + +//! \brief An interface to read to files and other file-like objects with +//! semantics matching the underlying platform (POSIX or Windows). +class FileReaderInterface : public virtual FileSeekerInterface { + public: + virtual ~FileReaderInterface() {} + + //! \brief Wraps ReadFile(), or provides an implementation with identical + //! semantics. + //! + //! \return The number of bytes actually read if the operation succeeded, + //! which may be `0` or any positive value less than or equal to \a size. + //! `-1` if the operation failed, with an error message logged. + virtual FileOperationResult Read(void* data, size_t size) = 0; + + //! \brief Wraps Read(), ensuring that the read succeeded and exactly \a size + //! bytes were read. + //! + //! Semantically, this behaves as LoggingReadFile(). + //! + //! \return `true` if the operation succeeded, `false` if it failed, with an + //! error message logged. Short reads are treated as failures. + bool ReadExactly(void* data, size_t size); +}; + +//! \brief A file reader backed by a FileHandle. +//! +//! FileReader requires users to provide a FilePath to open, but this class +//! accepts an already-open FileHandle instead. Like FileReader, this class may +//! read from a filesystem-based file, but unlike FileReader, this class is not +//! responsible for opening or closing the file. Users of this class must ensure +//! that the file handle is closed appropriately elsewhere. Objects of this +//! class may be used to read from file handles not associated with +//! filesystem-based files, although special attention should be paid to the +//! Seek() method, which may not function on file handles that do not refer to +//! disk-based files. +//! +//! This class is expected to be used when other code is responsible for +//! opening files and already provides file handles. +class WeakFileHandleFileReader : public FileReaderInterface { + public: + explicit WeakFileHandleFileReader(FileHandle file_handle); + ~WeakFileHandleFileReader() override; + + // FileReaderInterface: + FileOperationResult Read(void* data, size_t size) override; + + // FileSeekerInterface: + + //! \copydoc FileReaderInterface::Seek() + //! + //! \note This method is only guaranteed to function on file handles referring + //! to disk-based files. + FileOffset Seek(FileOffset offset, int whence) override; + + private: + void set_file_handle(FileHandle file_handle) { file_handle_ = file_handle; } + + FileHandle file_handle_; // weak + + // FileReader uses this class as its internal implementation, and it needs to + // be able to call set_file_handle(). FileReader cannot initialize a + // WeakFileHandleFileReader with a correct file descriptor at the time of + // construction because no file descriptor will be available until + // FileReader::Open() is called. + friend class FileReader; + + DISALLOW_COPY_AND_ASSIGN(WeakFileHandleFileReader); +}; + +//! \brief A file reader implementation that wraps traditional system file +//! operations on files accessed through the filesystem. +class FileReader : public FileReaderInterface { + public: + FileReader(); + ~FileReader() override; + + // FileReaderInterface: + + //! \brief Wraps LoggingOpenFileForRead(). + //! + //! \return `true` if the operation succeeded, `false` if it failed, with an + //! error message logged. + //! + //! \note After a successful call, this method cannot be called again until + //! after Close(). + bool Open(const base::FilePath& path); + + //! \brief Wraps CheckedCloseHandle(). + //! + //! \note It is only valid to call this method on an object that has had a + //! successful Open() that has not yet been matched by a subsequent call + //! to this method. + void Close(); + + // FileReaderInterface: + + //! \copydoc FileReaderInterface::Read() + //! + //! \note It is only valid to call this method between a successful Open() and + //! a Close(). + FileOperationResult Read(void* data, size_t size) override; + + // FileSeekerInterface: + + //! \copydoc FileReaderInterface::Seek() + //! + //! \note It is only valid to call this method between a successful Open() and + //! a Close(). + FileOffset Seek(FileOffset offset, int whence) override; + + private: + ScopedFileHandle file_; + WeakFileHandleFileReader weak_file_handle_file_reader_; + + DISALLOW_COPY_AND_ASSIGN(FileReader); +}; + +//! \brief A file reader backed by a standard input/output `FILE*`. +//! +//! This class accepts an already-open `FILE*`. It is not responsible for +//! opening or closing this `FILE*`. Users of this class must ensure that the +//! `FILE*` is closed appropriately elsewhere. Objects of this class may be used +//! to read from `FILE*` objects not associated with filesystem-based files, +//! although special attention should be paid to the Seek() method, which may +//! not function on `FILE*` objects that do not refer to disk-based files. +//! +//! This class is expected to be used when other code is responsible for +//! opening `FILE*` objects and already provides `FILE*` objects. A good use +//! would be a WeakStdioFileReader for `stdin`. +class WeakStdioFileReader : public FileReaderInterface { + public: + explicit WeakStdioFileReader(FILE* file); + ~WeakStdioFileReader() override; + + // FileReaderInterface: + FileOperationResult Read(void* data, size_t size) override; + + // FileSeekerInterface: + + //! \copydoc FileReaderInterface::Seek() + //! + //! \note This method is only guaranteed to function on `FILE*` objects + //! referring to disk-based files. + FileOffset Seek(FileOffset offset, int whence) override; + + private: + FILE* file_; // weak + + DISALLOW_COPY_AND_ASSIGN(WeakStdioFileReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FILE_FILE_READER_H_
diff --git a/third_party/crashpad/crashpad/util/file/file_seeker.cc b/third_party/crashpad/crashpad/util/file/file_seeker.cc new file mode 100644 index 0000000..a38e2f91 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_seeker.cc
@@ -0,0 +1,37 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_seeker.h" + +#include "base/logging.h" + +namespace crashpad { + +FileOffset FileSeekerInterface::SeekGet() { + return Seek(0, SEEK_CUR); +} + +bool FileSeekerInterface::SeekSet(FileOffset offset) { + FileOffset rv = Seek(offset, SEEK_SET); + if (rv < 0) { + // Seek() will have logged its own error. + return false; + } else if (rv != offset) { + LOG(ERROR) << "SeekSet(): expected " << offset << ", observed " << rv; + return false; + } + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_seeker.h b/third_party/crashpad/crashpad/util/file/file_seeker.h new file mode 100644 index 0000000..a7cff908 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_seeker.h
@@ -0,0 +1,54 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_FILE_FILE_SEEKER_H_ +#define CRASHPAD_UTIL_FILE_FILE_SEEKER_H_ + +#include "util/file/file_io.h" + +namespace crashpad { + +//! \brief An interface to seek in files and other file-like objects with +//! semantics matching the underlying platform (POSIX or Windows). +class FileSeekerInterface { + public: + //! \brief Wraps LoggingSeekFile() or provides an alternate implementation + //! with identical semantics. + //! + //! \return The return value of LoggingSeekFile(). `-1` on failure, + //! with an error message logged. + virtual FileOffset Seek(FileOffset offset, int whence) = 0; + + //! \brief Wraps Seek(), using `SEEK_CUR` to obtain the file’s current + //! position. + //! + //! \return The file’s current position on success. `-1` on failure, with an + //! error message logged. + FileOffset SeekGet(); + + //! \brief Wraps Seek(), using `SEEK_SET`, ensuring that the seek succeeded + //! and the file is positioned as desired. + //! + //! \return `true` if the operation succeeded, `false` if it failed, with an + //! error message logged. A failure to reposition the file as desired is + //! treated as a failure. + bool SeekSet(FileOffset offset); + + protected: + ~FileSeekerInterface() {} +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FILE_FILE_SEEKER_H_
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.cc b/third_party/crashpad/crashpad/util/file/file_writer.cc new file mode 100644 index 0000000..00a323e5 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_writer.cc
@@ -0,0 +1,179 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/file_writer.h" + +#include <algorithm> + +#include <limits.h> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "build/build_config.h" +#include "util/misc/implicit_cast.h" + +#if defined(OS_POSIX) +#include <sys/uio.h> +#include <unistd.h> +#include "base/posix/eintr_wrapper.h" +#endif // OS_POSIX + +namespace crashpad { + +#if defined(OS_POSIX) +// Ensure type compatibility between WritableIoVec and iovec. +static_assert(sizeof(WritableIoVec) == sizeof(iovec), "WritableIoVec size"); +static_assert(offsetof(WritableIoVec, iov_base) == offsetof(iovec, iov_base), + "WritableIoVec base offset"); +static_assert(offsetof(WritableIoVec, iov_len) == offsetof(iovec, iov_len), + "WritableIoVec len offset"); +#endif // OS_POSIX + +WeakFileHandleFileWriter::WeakFileHandleFileWriter(FileHandle file_handle) + : file_handle_(file_handle) { +} + +WeakFileHandleFileWriter::~WeakFileHandleFileWriter() { +} + +bool WeakFileHandleFileWriter::Write(const void* data, size_t size) { + DCHECK_NE(file_handle_, kInvalidFileHandle); + return LoggingWriteFile(file_handle_, data, size); +} + +bool WeakFileHandleFileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) { + DCHECK_NE(file_handle_, kInvalidFileHandle); + +#if defined(OS_POSIX) + + ssize_t size = 0; + for (const WritableIoVec& iov : *iovecs) { + // TODO(mark): Check to avoid overflow of ssize_t, and fail with EINVAL. + size += iov.iov_len; + } + + // Get an iovec*, because that’s what writev wants. The only difference + // between WritableIoVec and iovec is that WritableIoVec’s iov_base is a + // pointer to a const buffer, where iovec’s iov_base isn’t. writev doesn’t + // actually write to the data, so this cast is safe here. iovec’s iov_base is + // non-const because the same structure is used for readv and writev, and + // readv needs to write to the buffer that iov_base points to. + iovec* iov = reinterpret_cast<iovec*>(&(*iovecs)[0]); + size_t remaining_iovecs = iovecs->size(); + + while (size > 0) { + size_t writev_iovec_count = + std::min(remaining_iovecs, implicit_cast<size_t>(IOV_MAX)); + ssize_t written = + HANDLE_EINTR(writev(file_handle_, iov, writev_iovec_count)); + if (written < 0) { + PLOG(ERROR) << "writev"; + return false; + } else if (written == 0) { + LOG(ERROR) << "writev: returned 0"; + return false; + } + + size -= written; + DCHECK_GE(size, 0); + + if (size == 0) { + remaining_iovecs = 0; + break; + } + + while (written > 0) { + size_t wrote_this_iovec = + std::min(implicit_cast<size_t>(written), iov->iov_len); + written -= wrote_this_iovec; + if (wrote_this_iovec < iov->iov_len) { + iov->iov_base = + reinterpret_cast<char*>(iov->iov_base) + wrote_this_iovec; + iov->iov_len -= wrote_this_iovec; + } else { + ++iov; + --remaining_iovecs; + } + } + } + + DCHECK_EQ(remaining_iovecs, 0u); + +#else // !OS_POSIX + + for (const WritableIoVec& iov : *iovecs) { + if (!Write(iov.iov_base, iov.iov_len)) + return false; + } + +#endif // OS_POSIX + +#ifndef NDEBUG + // The interface says that |iovecs| is not sacred, so scramble it to make sure + // that nobody depends on it. + memset(&(*iovecs)[0], 0xa5, sizeof((*iovecs)[0]) * iovecs->size()); +#endif + + return true; +} + +FileOffset WeakFileHandleFileWriter::Seek(FileOffset offset, int whence) { + DCHECK_NE(file_handle_, kInvalidFileHandle); + return LoggingSeekFile(file_handle_, offset, whence); +} + +FileWriter::FileWriter() + : file_(), + weak_file_handle_file_writer_(kInvalidFileHandle) { +} + +FileWriter::~FileWriter() { +} + +bool FileWriter::Open(const base::FilePath& path, + FileWriteMode write_mode, + FilePermissions permissions) { + CHECK(!file_.is_valid()); + file_.reset(LoggingOpenFileForWrite(path, write_mode, permissions)); + if (!file_.is_valid()) { + return false; + } + + weak_file_handle_file_writer_.set_file_handle(file_.get()); + return true; +} + +void FileWriter::Close() { + CHECK(file_.is_valid()); + + weak_file_handle_file_writer_.set_file_handle(kInvalidFileHandle); + file_.reset(); +} + +bool FileWriter::Write(const void* data, size_t size) { + DCHECK(file_.is_valid()); + return weak_file_handle_file_writer_.Write(data, size); +} + +bool FileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) { + DCHECK(file_.is_valid()); + return weak_file_handle_file_writer_.WriteIoVec(iovecs); +} + +FileOffset FileWriter::Seek(FileOffset offset, int whence) { + DCHECK(file_.is_valid()); + return weak_file_handle_file_writer_.Seek(offset, whence); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/file_writer.h b/third_party/crashpad/crashpad/util/file/file_writer.h new file mode 100644 index 0000000..a0d547e --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/file_writer.h
@@ -0,0 +1,172 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_FILE_FILE_WRITER_H_ +#define CRASHPAD_UTIL_FILE_FILE_WRITER_H_ + +#include <sys/types.h> + +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "util/file/file_io.h" +#include "util/file/file_seeker.h" + +namespace crashpad { + +//! \brief A version of `iovec` with a `const` #iov_base field. +//! +//! This structure is intended to be used for write operations. +// +// Type compatibility with iovec is tested with static assertions in the +// implementation file. +struct WritableIoVec { + //! \brief The base address of a memory region for output. + const void* iov_base; + + //! \brief The size of the memory pointed to by #iov_base. + size_t iov_len; +}; + +//! \brief An interface to write to files and other file-like objects with +//! semantics matching the underlying platform (POSIX or Windows). +class FileWriterInterface : public virtual FileSeekerInterface { + public: + virtual ~FileWriterInterface() {} + + //! \brief Wraps LoggingWriteFile(), or provides an implementation with + //! identical semantics. + //! + //! \return `true` if the operation succeeded, `false` if it failed, with an + //! error message logged. + virtual bool Write(const void* data, size_t size) = 0; + + //! \brief Wraps `writev()` on POSIX or provides an alternate implementation + //! with identical semantics. This method will write entire buffers, + //! continuing after a short write or after being interrupted. On + //! non-POSIX this is a simple wrapper around Write(). + //! + //! \return `true` if the operation succeeded, `false` if it failed, with an + //! error message logged. + //! + //! \note The contents of \a iovecs are undefined when this method returns. + virtual bool WriteIoVec(std::vector<WritableIoVec>* iovecs) = 0; +}; + +//! \brief A file writer backed by a FileHandle. +//! +//! FileWriter requires users to provide a FilePath to open, but this class +//! accepts an already-open FileHandle instead. Like FileWriter, this class may +//! write to a filesystem-based file, but unlike FileWriter, this class is not +//! responsible for creating or closing the file. Users of this class must +//! ensure that the file handle is closed appropriately elsewhere. Objects of +//! this class may be used to write to file handles not associated with +//! filesystem-based files, although special attention should be paid to the +//! Seek() method, which may not function on file handles that do not refer to +//! disk-based files. +//! +//! This class is expected to be used when other code is responsible for +//! creating files and already provides file handles. +class WeakFileHandleFileWriter : public FileWriterInterface { + public: + explicit WeakFileHandleFileWriter(FileHandle file_handle); + ~WeakFileHandleFileWriter() override; + + // FileWriterInterface: + bool Write(const void* data, size_t size) override; + bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override; + + // FileSeekerInterface: + + //! \copydoc FileWriterInterface::Seek() + //! + //! \note This method is only guaranteed to function on file handles referring + //! to disk-based files. + FileOffset Seek(FileOffset offset, int whence) override; + + private: + void set_file_handle(FileHandle file_handle) { file_handle_ = file_handle; } + + FileHandle file_handle_; // weak + + // FileWriter uses this class as its internal implementation, and it needs to + // be able to call set_file_handle(). FileWriter cannot initialize a + // WeakFileHandleFileWriter with a correct file descriptor at the time of + // construction because no file descriptor will be available until + // FileWriter::Open() is called. + friend class FileWriter; + + DISALLOW_COPY_AND_ASSIGN(WeakFileHandleFileWriter); +}; + +//! \brief A file writer implementation that wraps traditional system file +//! operations on files accessed through the filesystem. +class FileWriter : public FileWriterInterface { + public: + FileWriter(); + ~FileWriter() override; + + // FileWriterInterface: + + //! \brief Wraps LoggingOpenFileForWrite(). + //! + //! \return `true` if the operation succeeded, `false` if it failed, with an + //! error message logged. + //! + //! \note After a successful call, this method cannot be called again until + //! after Close(). + bool Open(const base::FilePath& path, + FileWriteMode write_mode, + FilePermissions permissions); + + //! \brief Wraps CheckedCloseHandle(). + //! + //! \note It is only valid to call this method on an object that has had a + //! successful Open() that has not yet been matched by a subsequent call + //! to this method. + void Close(); + + // FileWriterInterface: + + //! \copydoc FileWriterInterface::Write() + //! + //! \note It is only valid to call this method between a successful Open() and + //! a Close(). + bool Write(const void* data, size_t size) override; + + //! \copydoc FileWriterInterface::WriteIoVec() + //! + //! \note It is only valid to call this method between a successful Open() and + //! a Close(). + bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override; + + // FileSeekerInterface: + + //! \copydoc FileWriterInterface::Seek() + //! + //! \note It is only valid to call this method between a successful Open() and + //! a Close(). + FileOffset Seek(FileOffset offset, int whence) override; + + private: + ScopedFileHandle file_; + WeakFileHandleFileWriter weak_file_handle_file_writer_; + + DISALLOW_COPY_AND_ASSIGN(FileWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FILE_FILE_WRITER_H_
diff --git a/third_party/crashpad/crashpad/util/file/string_file.cc b/third_party/crashpad/crashpad/util/file/string_file.cc new file mode 100644 index 0000000..1c74b3f --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/string_file.cc
@@ -0,0 +1,173 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/string_file.h" + +#include <string.h> + +#include <algorithm> +#include <limits> + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "util/misc/implicit_cast.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +StringFile::StringFile() : string_(), offset_(0) { +} + +StringFile::~StringFile() { +} + +void StringFile::SetString(const std::string& string) { + CHECK_LE( + string.size(), + implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max())); + string_ = string; + offset_ = 0; +} + +void StringFile::Reset() { + string_.clear(); + offset_ = 0; +} + +FileOperationResult StringFile::Read(void* data, size_t size) { + DCHECK(offset_.IsValid()); + + const size_t offset = offset_.ValueOrDie(); + if (offset >= string_.size()) { + return 0; + } + + const size_t nread = std::min(size, string_.size() - offset); + + base::CheckedNumeric<FileOperationResult> new_offset = offset_; + new_offset += nread; + if (!new_offset.IsValid()) { + LOG(ERROR) << "Read(): file too large"; + return -1; + } + + memcpy(data, &string_[offset], nread); + offset_ = new_offset; + + return nread; +} + +bool StringFile::Write(const void* data, size_t size) { + DCHECK(offset_.IsValid()); + + const size_t offset = offset_.ValueOrDie(); + if (offset > string_.size()) { + string_.resize(offset); + } + + base::CheckedNumeric<FileOperationResult> new_offset = offset_; + new_offset += size; + if (!new_offset.IsValid()) { + LOG(ERROR) << "Write(): file too large"; + return false; + } + + string_.replace(offset, size, reinterpret_cast<const char*>(data), size); + offset_ = new_offset; + + return true; +} + +bool StringFile::WriteIoVec(std::vector<WritableIoVec>* iovecs) { + DCHECK(offset_.IsValid()); + + if (iovecs->empty()) { + LOG(ERROR) << "WriteIoVec(): no iovecs"; + return false; + } + + // Avoid writing anything at all if it would cause an overflow. + base::CheckedNumeric<FileOperationResult> new_offset = offset_; + for (const WritableIoVec& iov : *iovecs) { + new_offset += iov.iov_len; + if (!new_offset.IsValid()) { + LOG(ERROR) << "WriteIoVec(): file too large"; + return false; + } + } + + for (const WritableIoVec& iov : *iovecs) { + if (!Write(iov.iov_base, iov.iov_len)) { + return false; + } + } + +#ifndef NDEBUG + // The interface says that |iovecs| is not sacred, so scramble it to make sure + // that nobody depends on it. + memset(&(*iovecs)[0], 0xa5, sizeof((*iovecs)[0]) * iovecs->size()); +#endif + + return true; +} + +FileOffset StringFile::Seek(FileOffset offset, int whence) { + DCHECK(offset_.IsValid()); + + size_t base_offset; + + switch (whence) { + case SEEK_SET: + base_offset = 0; + break; + + case SEEK_CUR: + base_offset = offset_.ValueOrDie(); + break; + + case SEEK_END: + base_offset = string_.size(); + break; + + default: + LOG(ERROR) << "Seek(): invalid whence " << whence; + return -1; + } + + FileOffset base_offset_fileoffset; + if (!AssignIfInRange(&base_offset_fileoffset, base_offset)) { + LOG(ERROR) << "Seek(): base_offset " << base_offset + << " invalid for FileOffset"; + return -1; + } + base::CheckedNumeric<FileOffset> new_offset(base_offset_fileoffset); + new_offset += offset; + if (!new_offset.IsValid()) { + LOG(ERROR) << "Seek(): new_offset invalid"; + return -1; + } + FileOffset new_offset_fileoffset = new_offset.ValueOrDie(); + size_t new_offset_sizet; + if (!AssignIfInRange(&new_offset_sizet, new_offset_fileoffset)) { + LOG(ERROR) << "Seek(): new_offset " << new_offset_fileoffset + << " invalid for size_t"; + return -1; + } + + offset_ = new_offset_sizet; + + return offset_.ValueOrDie(); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/file/string_file.h b/third_party/crashpad/crashpad/util/file/string_file.h new file mode 100644 index 0000000..05f57072 --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/string_file.h
@@ -0,0 +1,81 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_FILE_STRING_FILE_H_ +#define CRASHPAD_UTIL_FILE_STRING_FILE_H_ + +#include <sys/types.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/numerics/safe_math.h" +#include "util/file/file_io.h" +#include "util/file/file_reader.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +//! \brief A file reader and writer backed by a virtual file, as opposed to a +//! file on disk or other operating system file descriptor-based file. +//! +//! The virtual file is a buffer in memory. This class is convenient for use +//! with other code that normally expects to read or write files, when it is +//! impractical or inconvenient to read or write a file. It is expected that +//! tests, in particular, will benefit from using this class. +class StringFile : public FileReaderInterface, public FileWriterInterface { + public: + StringFile(); + ~StringFile() override; + + //! \brief Returns a string containing the virtual file’s contents. + const std::string& string() const { return string_; } + + //! \brief Sets the virtual file’s contents to \a string, and resets its file + //! position to `0`. + void SetString(const std::string& string); + + //! \brief Resets the virtual file’s contents to be empty, and resets its file + //! position to `0`. + void Reset(); + + // FileReaderInterface: + FileOperationResult Read(void* data, size_t size) override; + + // FileWriterInterface: + bool Write(const void* data, size_t size) override; + bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override; + + // FileSeekerInterface: + FileOffset Seek(FileOffset offset, int whence) override; + + private: + //! \brief The virtual file’s contents. + std::string string_; + + //! \brief The file offset of the virtual file. + //! + //! \note This is stored in a `size_t` to match the characteristics of + //! #string_, the `std::string` used to store the virtual file’s contents. + //! This type will have different characteristics than the `off_t` used to + //! report file offsets. The implementation must take care when converting + //! between these distinct types. + base::CheckedNumeric<size_t> offset_; + + DISALLOW_COPY_AND_ASSIGN(StringFile); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FILE_STRING_FILE_H_
diff --git a/third_party/crashpad/crashpad/util/file/string_file_test.cc b/third_party/crashpad/crashpad/util/file/string_file_test.cc new file mode 100644 index 0000000..0990daf --- /dev/null +++ b/third_party/crashpad/crashpad/util/file/string_file_test.cc
@@ -0,0 +1,504 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/file/string_file.h" + +#include <string.h> + +#include <algorithm> +#include <limits> + +#include "gtest/gtest.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(StringFile, EmptyFile) { + StringFile string_file; + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.Write("", 0)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + char c = '6'; + EXPECT_EQ(0, string_file.Read(&c, 1)); + EXPECT_EQ('6', c); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.string().empty()); +} + +TEST(StringFile, OneByteFile) { + StringFile string_file; + + EXPECT_TRUE(string_file.Write("a", 1)); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ("a", string_file.string()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + char c = '6'; + EXPECT_EQ(1, string_file.Read(&c, 1)); + EXPECT_EQ('a', c); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(0, string_file.Read(&c, 1)); + EXPECT_EQ('a', c); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ("a", string_file.string()); + + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_TRUE(string_file.Write("b", 1)); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ("b", string_file.string()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_EQ(1, string_file.Read(&c, 1)); + EXPECT_EQ('b', c); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(0, string_file.Read(&c, 1)); + EXPECT_EQ('b', c); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ("b", string_file.string()); + + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_TRUE(string_file.Write("\0", 1)); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ('\0', string_file.string()[0]); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ('\0', string_file.string()[0]); +} + +TEST(StringFile, SetString) { + char kString1[] = "Four score"; + StringFile string_file; + string_file.SetString(kString1); + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + char buf[5] = "****"; + EXPECT_EQ(4, string_file.Read(buf, 4)); + EXPECT_STREQ("Four", buf); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(static_cast<FileOffset>(strlen(kString1)), + string_file.Seek(0, SEEK_END)); + EXPECT_EQ(kString1, string_file.string()); + + char kString2[] = "and seven years ago"; + EXPECT_EQ(4, string_file.Seek(4, SEEK_SET)); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + string_file.SetString(kString2); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(4, string_file.Read(buf, 4)); + EXPECT_STREQ("and ", buf); + EXPECT_EQ(static_cast<FileOffset>(strlen(kString2)), + string_file.Seek(0, SEEK_END)); + EXPECT_EQ(kString2, string_file.string()); + + char kString3[] = "our fathers"; + EXPECT_EQ(4, string_file.Seek(4, SEEK_SET)); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + string_file.SetString(kString3); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(4, string_file.Read(buf, 4)); + EXPECT_STREQ("our ", buf); + EXPECT_EQ(static_cast<FileOffset>(strlen(kString3)), + string_file.Seek(0, SEEK_END)); + EXPECT_EQ(kString3, string_file.string()); +} + +TEST(StringFile, ReadExactly) { + StringFile string_file; + string_file.SetString("1234567"); + char buf[4] = "***"; + EXPECT_TRUE(string_file.ReadExactly(buf, 3)); + EXPECT_STREQ("123", buf); + EXPECT_TRUE(string_file.ReadExactly(buf, 3)); + EXPECT_STREQ("456", buf); + EXPECT_FALSE(string_file.ReadExactly(buf, 3)); +} + +TEST(StringFile, Reset) { + StringFile string_file; + + EXPECT_TRUE(string_file.Write("abc", 3)); + EXPECT_EQ(3u, string_file.string().size()); + EXPECT_EQ("abc", string_file.string()); + EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR)); + char buf[10] = "*********"; + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_EQ(3, string_file.Read(&buf, 10)); + EXPECT_STREQ("abc******", buf); + EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE(string_file.string().empty()); + + string_file.Reset(); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("de", 2)); + EXPECT_EQ(2u, string_file.string().size()); + EXPECT_EQ("de", string_file.string()); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_EQ(2, string_file.Read(&buf, 10)); + EXPECT_STREQ("dec******", buf); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE(string_file.string().empty()); + + string_file.Reset(); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("fghi", 4)); + EXPECT_EQ(4u, string_file.string().size()); + EXPECT_EQ("fghi", string_file.string()); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_EQ(2, string_file.Read(&buf, 2)); + EXPECT_STREQ("fgc******", buf); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(2, string_file.Read(&buf, 2)); + EXPECT_STREQ("hic******", buf); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE(string_file.string().empty()); + + string_file.Reset(); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + // Test resetting after a sparse write. + EXPECT_EQ(1, string_file.Seek(1, SEEK_SET)); + EXPECT_TRUE(string_file.Write("j", 1)); + EXPECT_EQ(2u, string_file.string().size()); + EXPECT_EQ(std::string("\0j", 2), string_file.string()); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE(string_file.string().empty()); + + string_file.Reset(); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); +} + +TEST(StringFile, WriteInvalid) { + StringFile string_file; + + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + EXPECT_FALSE(string_file.Write( + "", + implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max()) + + 1)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("a", 1)); + EXPECT_FALSE(string_file.Write( + "", + implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max()))); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ("a", string_file.string()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); +} + +TEST(StringFile, WriteIoVec) { + StringFile string_file; + + std::vector<WritableIoVec> iovecs; + WritableIoVec iov; + iov.iov_base = ""; + iov.iov_len = 0; + iovecs.push_back(iov); + EXPECT_TRUE(string_file.WriteIoVec(&iovecs)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + iovecs.clear(); + iov.iov_base = "a"; + iov.iov_len = 1; + iovecs.push_back(iov); + EXPECT_TRUE(string_file.WriteIoVec(&iovecs)); + EXPECT_EQ(1u, string_file.string().size()); + EXPECT_EQ("a", string_file.string()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + + iovecs.clear(); + iovecs.push_back(iov); + EXPECT_TRUE(string_file.WriteIoVec(&iovecs)); + EXPECT_EQ(2u, string_file.string().size()); + EXPECT_EQ("aa", string_file.string()); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + + iovecs.clear(); + iovecs.push_back(iov); + iov.iov_base = "bc"; + iov.iov_len = 2; + iovecs.push_back(iov); + EXPECT_TRUE(string_file.WriteIoVec(&iovecs)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("aaabc", string_file.string()); + EXPECT_EQ(5, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("def", 3)); + EXPECT_EQ(8u, string_file.string().size()); + EXPECT_EQ("aaabcdef", string_file.string()); + EXPECT_EQ(8, string_file.Seek(0, SEEK_CUR)); + + iovecs.clear(); + iov.iov_base = "ghij"; + iov.iov_len = 4; + iovecs.push_back(iov); + iov.iov_base = "klmno"; + iov.iov_len = 5; + iovecs.push_back(iov); + EXPECT_TRUE(string_file.WriteIoVec(&iovecs)); + EXPECT_EQ(17u, string_file.string().size()); + EXPECT_EQ("aaabcdefghijklmno", string_file.string()); + EXPECT_EQ(17, string_file.Seek(0, SEEK_CUR)); + + string_file.Reset(); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + iovecs.clear(); + iov.iov_base = "abcd"; + iov.iov_len = 4; + iovecs.resize(16, iov); + EXPECT_TRUE(string_file.WriteIoVec(&iovecs)); + EXPECT_EQ(64u, string_file.string().size()); + EXPECT_EQ("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", + string_file.string()); + EXPECT_EQ(64, string_file.Seek(0, SEEK_CUR)); +} + +TEST(StringFile, WriteIoVecInvalid) { + StringFile string_file; + + std::vector<WritableIoVec> iovecs; + EXPECT_FALSE(string_file.WriteIoVec(&iovecs)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + + WritableIoVec iov; + EXPECT_EQ(1, string_file.Seek(1, SEEK_CUR)); + iov.iov_base = "a"; + iov.iov_len = std::numeric_limits<FileOperationResult>::max(); + iovecs.push_back(iov); + EXPECT_FALSE(string_file.WriteIoVec(&iovecs)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + + iovecs.clear(); + iov.iov_base = "a"; + iov.iov_len = 1; + iovecs.push_back(iov); + iov.iov_len = std::numeric_limits<FileOperationResult>::max() - 1; + iovecs.push_back(iov); + EXPECT_FALSE(string_file.WriteIoVec(&iovecs)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); +} + +TEST(StringFile, Seek) { + StringFile string_file; + + EXPECT_TRUE(string_file.Write("abcd", 4)); + EXPECT_EQ(4u, string_file.string().size()); + EXPECT_EQ("abcd", string_file.string()); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_TRUE(string_file.Write("efgh", 4)); + EXPECT_EQ(4u, string_file.string().size()); + EXPECT_EQ("efgh", string_file.string()); + EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_TRUE(string_file.Write("ijk", 3)); + EXPECT_EQ(4u, string_file.string().size()); + EXPECT_EQ("ijkh", string_file.string()); + EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(0, string_file.Seek(0, SEEK_SET)); + EXPECT_TRUE(string_file.Write("lmnop", 5)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("lmnop", string_file.string()); + EXPECT_EQ(5, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(1, string_file.Seek(1, SEEK_SET)); + EXPECT_TRUE(string_file.Write("q", 1)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("lqnop", string_file.string()); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(1, string_file.Seek(-1, SEEK_CUR)); + EXPECT_TRUE(string_file.Write("r", 1)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("lrnop", string_file.string()); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("s", 1)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("lrsop", string_file.string()); + EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(2, string_file.Seek(-1, SEEK_CUR)); + EXPECT_TRUE(string_file.Write("t", 1)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("lrtop", string_file.string()); + EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(4, string_file.Seek(-1, SEEK_END)); + EXPECT_TRUE(string_file.Write("u", 1)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("lrtou", string_file.string()); + EXPECT_EQ(5, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(0, string_file.Seek(-5, SEEK_END)); + EXPECT_TRUE(string_file.Write("v", 1)); + EXPECT_EQ(5u, string_file.string().size()); + EXPECT_EQ("vrtou", string_file.string()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(5, string_file.Seek(0, SEEK_END)); + EXPECT_TRUE(string_file.Write("w", 1)); + EXPECT_EQ(6u, string_file.string().size()); + EXPECT_EQ("vrtouw", string_file.string()); + EXPECT_EQ(6, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(8, string_file.Seek(2, SEEK_END)); + EXPECT_EQ(6u, string_file.string().size()); + EXPECT_EQ("vrtouw", string_file.string()); + + EXPECT_EQ(6, string_file.Seek(0, SEEK_END)); + EXPECT_TRUE(string_file.Write("x", 1)); + EXPECT_EQ(7u, string_file.string().size()); + EXPECT_EQ("vrtouwx", string_file.string()); + EXPECT_EQ(7, string_file.Seek(0, SEEK_CUR)); +} + +TEST(StringFile, SeekSparse) { + StringFile string_file; + + EXPECT_EQ(3, string_file.Seek(3, SEEK_SET)); + EXPECT_TRUE(string_file.string().empty()); + EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("abc", 3)); + EXPECT_EQ(6u, string_file.string().size()); + EXPECT_EQ(std::string("\0\0\0abc", 6), string_file.string()); + EXPECT_EQ(6, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(9, string_file.Seek(3, SEEK_END)); + EXPECT_EQ(6u, string_file.string().size()); + EXPECT_EQ(9, string_file.Seek(0, SEEK_CUR)); + char c; + EXPECT_EQ(0, string_file.Read(&c, 1)); + EXPECT_EQ(9, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(6u, string_file.string().size()); + EXPECT_TRUE(string_file.Write("def", 3)); + EXPECT_EQ(12u, string_file.string().size()); + EXPECT_EQ(std::string("\0\0\0abc\0\0\0def", 12), string_file.string()); + EXPECT_EQ(12, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(7, string_file.Seek(-5, SEEK_END)); + EXPECT_EQ(12u, string_file.string().size()); + EXPECT_EQ(7, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.Write("g", 1)); + EXPECT_EQ(12u, string_file.string().size()); + EXPECT_EQ(std::string("\0\0\0abc\0g\0def", 12), string_file.string()); + EXPECT_EQ(8, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(15, string_file.Seek(7, SEEK_CUR)); + EXPECT_EQ(12u, string_file.string().size()); + EXPECT_EQ(15, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.Write("hij", 3)); + EXPECT_EQ(18u, string_file.string().size()); + EXPECT_EQ(std::string("\0\0\0abc\0g\0def\0\0\0hij", 18), + string_file.string()); + EXPECT_EQ(18, string_file.Seek(0, SEEK_CUR)); + + EXPECT_EQ(1, string_file.Seek(-17, SEEK_CUR)); + EXPECT_EQ(18u, string_file.string().size()); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.Write("k", 1)); + EXPECT_EQ(18u, string_file.string().size()); + EXPECT_EQ(std::string("\0k\0abc\0g\0def\0\0\0hij", 18), string_file.string()); + EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR)); + + EXPECT_TRUE(string_file.Write("l", 1)); + EXPECT_TRUE(string_file.Write("mnop", 4)); + EXPECT_EQ(18u, string_file.string().size()); + EXPECT_EQ(std::string("\0klmnopg\0def\0\0\0hij", 18), string_file.string()); + EXPECT_EQ(7, string_file.Seek(0, SEEK_CUR)); +} + +TEST(StringFile, SeekInvalid) { + StringFile string_file; + + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + EXPECT_EQ(1, string_file.Seek(1, SEEK_SET)); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_LT(string_file.Seek(-1, SEEK_SET), 0); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_LT(string_file.Seek(std::numeric_limits<FileOperationResult>::min(), + SEEK_SET), + 0); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_LT(string_file.Seek(std::numeric_limits<FileOffset>::min(), SEEK_SET), + 0); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.string().empty()); + + static_assert(SEEK_SET != 3 && SEEK_CUR != 3 && SEEK_END != 3, + "3 must be invalid for whence"); + EXPECT_LT(string_file.Seek(0, 3), 0); + + string_file.Reset(); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.string().empty()); + + const FileOffset kMaxOffset = static_cast<FileOffset>( + std::min(implicit_cast<uint64_t>(std::numeric_limits<FileOffset>::max()), + implicit_cast<uint64_t>(std::numeric_limits<size_t>::max()))); + + EXPECT_EQ(kMaxOffset, string_file.Seek(kMaxOffset, SEEK_SET)); + EXPECT_EQ(kMaxOffset, string_file.Seek(0, SEEK_CUR)); + EXPECT_LT(string_file.Seek(1, SEEK_CUR), 0); + + EXPECT_EQ(1, string_file.Seek(1, SEEK_SET)); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_LT(string_file.Seek(kMaxOffset, SEEK_CUR), 0); +} + +TEST(StringFile, SeekSet) { + StringFile string_file; + EXPECT_TRUE(string_file.SeekSet(1)); + EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR)); + EXPECT_TRUE(string_file.SeekSet(10)); + EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE(string_file.SeekSet(-1)); + EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE( + string_file.SeekSet(std::numeric_limits<FileOperationResult>::min())); + EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR)); + EXPECT_FALSE(string_file.SeekSet(std::numeric_limits<FileOffset>::min())); + EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h b/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h new file mode 100644 index 0000000..e64e3d1 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h
@@ -0,0 +1,38 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_ +#define CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_ + +#include <mach/mach.h> + +#include "util/numeric/checked_address_range.h" + +namespace crashpad { + +//! \brief Ensures that a range, composed of a base and a size, does not +//! overflow the pointer type of the process it describes a range in. +//! +//! This class checks bases of type `mach_vm_address_t` and sizes of type +//! `mach_vm_address_t` against a process whose pointer type is either 32 or 64 +//! bits wide. +//! +//! Aside from varying the overall range on the basis of a process’ pointer type +//! width, this class functions very similarly to CheckedRange. +using CheckedMachAddressRange = + internal::CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
diff --git a/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc b/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc new file mode 100644 index 0000000..702715d --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc
@@ -0,0 +1,256 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/checked_mach_address_range.h" + +#include <mach/mach.h> + +#include <limits> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +enum Validity { + kInvalid = false, + kValid, + kValid64Invalid32, +}; + +bool ExpectationForValidity32(Validity validity) { + return validity == kValid; +} + +bool ExpectationForValidity64(Validity validity) { + return validity == kValid || validity == kValid64Invalid32; +} + +TEST(CheckedMachAddressRange, IsValid) { + const struct TestData { + mach_vm_address_t base; + mach_vm_size_t size; + Validity validity; + } kTestData[] = { + {0, 0, kValid}, + {0, 1, kValid}, + {0, 2, kValid}, + {0, 0x7fffffff, kValid}, + {0, 0x80000000, kValid}, + {0, 0xfffffffe, kValid}, + {0, 0xffffffff, kValid}, + {0, 0xffffffffffffffff, kValid64Invalid32}, + {1, 0, kValid}, + {1, 1, kValid}, + {1, 2, kValid}, + {1, 0x7fffffff, kValid}, + {1, 0x80000000, kValid}, + {1, 0xfffffffe, kValid}, + {1, 0xffffffff, kValid64Invalid32}, + {1, 0xfffffffffffffffe, kValid64Invalid32}, + {1, 0xffffffffffffffff, kInvalid}, + {0x7fffffff, 0, kValid}, + {0x7fffffff, 1, kValid}, + {0x7fffffff, 2, kValid}, + {0x7fffffff, 0x7fffffff, kValid}, + {0x7fffffff, 0x80000000, kValid}, + {0x7fffffff, 0xfffffffe, kValid64Invalid32}, + {0x7fffffff, 0xffffffff, kValid64Invalid32}, + {0x80000000, 0, kValid}, + {0x80000000, 1, kValid}, + {0x80000000, 2, kValid}, + {0x80000000, 0x7fffffff, kValid}, + {0x80000000, 0x80000000, kValid64Invalid32}, + {0x80000000, 0xfffffffe, kValid64Invalid32}, + {0x80000000, 0xffffffff, kValid64Invalid32}, + {0xfffffffe, 0, kValid}, + {0xfffffffe, 1, kValid}, + {0xfffffffe, 2, kValid64Invalid32}, + {0xfffffffe, 0x7fffffff, kValid64Invalid32}, + {0xfffffffe, 0x80000000, kValid64Invalid32}, + {0xfffffffe, 0xfffffffe, kValid64Invalid32}, + {0xfffffffe, 0xffffffff, kValid64Invalid32}, + {0xffffffff, 0, kValid}, + {0xffffffff, 1, kValid64Invalid32}, + {0xffffffff, 2, kValid64Invalid32}, + {0xffffffff, 0x7fffffff, kValid64Invalid32}, + {0xffffffff, 0x80000000, kValid64Invalid32}, + {0xffffffff, 0xfffffffe, kValid64Invalid32}, + {0xffffffff, 0xffffffff, kValid64Invalid32}, + {0x7fffffffffffffff, 0, kValid64Invalid32}, + {0x7fffffffffffffff, 1, kValid64Invalid32}, + {0x7fffffffffffffff, 2, kValid64Invalid32}, + {0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32}, + {0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32}, + {0x7fffffffffffffff, 0x8000000000000001, kInvalid}, + {0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid}, + {0x7fffffffffffffff, 0xffffffffffffffff, kInvalid}, + {0x8000000000000000, 0, kValid64Invalid32}, + {0x8000000000000000, 1, kValid64Invalid32}, + {0x8000000000000000, 2, kValid64Invalid32}, + {0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32}, + {0x8000000000000000, 0x8000000000000000, kInvalid}, + {0x8000000000000000, 0x8000000000000001, kInvalid}, + {0x8000000000000000, 0xfffffffffffffffe, kInvalid}, + {0x8000000000000000, 0xffffffffffffffff, kInvalid}, + {0xfffffffffffffffe, 0, kValid64Invalid32}, + {0xfffffffffffffffe, 1, kValid64Invalid32}, + {0xfffffffffffffffe, 2, kInvalid}, + {0xffffffffffffffff, 0, kValid64Invalid32}, + {0xffffffffffffffff, 1, kInvalid}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx", + index, + testcase.base, + testcase.size)); + + CheckedMachAddressRange range_32(false, testcase.base, testcase.size); + EXPECT_EQ(ExpectationForValidity32(testcase.validity), range_32.IsValid()); + + CheckedMachAddressRange range_64(true, testcase.base, testcase.size); + EXPECT_EQ(ExpectationForValidity64(testcase.validity), range_64.IsValid()); + } +} + +TEST(CheckedMachAddressRange, ContainsValue) { + const struct TestData { + mach_vm_address_t value; + bool expectation; + } kTestData[] = { + {0, false}, + {1, false}, + {0x1fff, false}, + {0x2000, true}, + {0x2001, true}, + {0x2ffe, true}, + {0x2fff, true}, + {0x3000, false}, + {0x3001, false}, + {0x7fffffff, false}, + {0x80000000, false}, + {0x80000001, false}, + {0x80001fff, false}, + {0x80002000, false}, + {0x80002001, false}, + {0x80002ffe, false}, + {0x80002fff, false}, + {0x80003000, false}, + {0x80003001, false}, + {0xffffcfff, false}, + {0xffffdfff, false}, + {0xffffefff, false}, + {0xffffffff, false}, + {0x100000000, false}, + {0xffffffffffffffff, false}, + }; + + CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000); + ASSERT_TRUE(parent_range_32.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE( + base::StringPrintf("index %zu, value 0x%llx", index, testcase.value)); + + EXPECT_EQ(testcase.expectation, + parent_range_32.ContainsValue(testcase.value)); + } + + CheckedMachAddressRange parent_range_64(true, 0x100000000, 0x1000); + ASSERT_TRUE(parent_range_64.IsValid()); + EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff)); + EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000)); + EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001)); + EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff)); + EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000)); +} + +TEST(CheckedMachAddressRange, ContainsRange) { + const struct TestData { + mach_vm_address_t base; + mach_vm_size_t size; + bool expectation; + } kTestData[] = { + {0, 0, false}, + {0, 1, false}, + {0x2000, 0x1000, true}, + {0, 0x2000, false}, + {0x3000, 0x1000, false}, + {0x1800, 0x1000, false}, + {0x2800, 0x1000, false}, + {0x2000, 0x800, true}, + {0x2800, 0x800, true}, + {0x2400, 0x800, true}, + {0x2800, 0, true}, + {0x2000, 0xffffdfff, false}, + {0x2800, 0xffffd7ff, false}, + {0x3000, 0xffffcfff, false}, + {0xfffffffe, 1, false}, + {0xffffffff, 0, false}, + {0x1fff, 0, false}, + {0x2000, 0, true}, + {0x2001, 0, true}, + {0x2fff, 0, true}, + {0x3000, 0, true}, + {0x3001, 0, false}, + {0x1fff, 1, false}, + {0x2000, 1, true}, + {0x2001, 1, true}, + {0x2fff, 1, true}, + {0x3000, 1, false}, + {0x3001, 1, false}, + }; + + CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000); + ASSERT_TRUE(parent_range_32.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx", + index, + testcase.base, + testcase.size)); + + CheckedMachAddressRange child_range_32(false, testcase.base, testcase.size); + ASSERT_TRUE(child_range_32.IsValid()); + EXPECT_EQ(testcase.expectation, + parent_range_32.ContainsRange(child_range_32)); + } + + CheckedMachAddressRange parent_range_64(true, 0x100000000, 0x1000); + ASSERT_TRUE(parent_range_64.IsValid()); + + CheckedMachAddressRange child_range_64(true, 0xffffffff, 2); + EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64)); + + child_range_64.SetRange(true, 0x100000000, 2); + EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64)); + + child_range_64.SetRange(true, 0x100000ffe, 2); + EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64)); + + child_range_64.SetRange(true, 0x100000fff, 2); + EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/launchd.h b/third_party/crashpad/crashpad/util/mac/launchd.h new file mode 100644 index 0000000..03f8bf0 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/launchd.h
@@ -0,0 +1,147 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MAC_LAUNCHD_H_ +#define CRASHPAD_UTIL_MAC_LAUNCHD_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <launch.h> + +namespace crashpad { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +//! \{ +//! \brief Wraps the `<launch.h>` function of the same name. +//! +//! The Mac OS X 10.10 SDK deprecates `<launch.h>`, although the functionality +//! it provides is still useful. These wrappers allow the deprecated functions +//! to be called without triggering deprecated-declaration warnings. + +inline launch_data_t LaunchDataAlloc(launch_data_type_t type) { + return launch_data_alloc(type); +} + +inline launch_data_type_t LaunchDataGetType(const launch_data_t data) { + return launch_data_get_type(data); +} + +inline void LaunchDataFree(launch_data_t data) { + return launch_data_free(data); +} + +inline bool LaunchDataDictInsert(launch_data_t dict, + const launch_data_t value, + const char* key) { + return launch_data_dict_insert(dict, value, key); +} + +inline launch_data_t LaunchDataDictLookup(const launch_data_t dict, + const char* key) { + return launch_data_dict_lookup(dict, key); +} + +inline size_t LaunchDataDictGetCount(launch_data_t dict) { + return launch_data_dict_get_count(dict); +} + +inline bool LaunchDataArraySetIndex(launch_data_t array, + const launch_data_t value, + size_t index) { + return launch_data_array_set_index(array, value, index); +} + +inline launch_data_t LaunchDataArrayGetIndex(launch_data_t array, + size_t index) { + return launch_data_array_get_index(array, index); +} + +inline size_t LaunchDataArrayGetCount(launch_data_t array) { + return launch_data_array_get_count(array); +} + +inline launch_data_t LaunchDataNewInteger(long long integer) { + return launch_data_new_integer(integer); +} + +inline launch_data_t LaunchDataNewBool(bool boolean) { + return launch_data_new_bool(boolean); +} + +inline launch_data_t LaunchDataNewReal(double real) { + return launch_data_new_real(real); +} + +inline launch_data_t LaunchDataNewString(const char* string) { + return launch_data_new_string(string); +} + +inline launch_data_t LaunchDataNewOpaque(const void* opaque, size_t size) { + return launch_data_new_opaque(opaque, size); +} + +inline long long LaunchDataGetInteger(const launch_data_t data) { + return launch_data_get_integer(data); +} + +inline bool LaunchDataGetBool(const launch_data_t data) { + return launch_data_get_bool(data); +} + +inline double LaunchDataGetReal(const launch_data_t data) { + return launch_data_get_real(data); +} + +inline const char* LaunchDataGetString(const launch_data_t data) { + return launch_data_get_string(data); +} + +inline void* LaunchDataGetOpaque(const launch_data_t data) { + return launch_data_get_opaque(data); +} + +inline size_t LaunchDataGetOpaqueSize(const launch_data_t data) { + return launch_data_get_opaque_size(data); +} + +inline int LaunchDataGetErrno(const launch_data_t data) { + return launch_data_get_errno(data); +} + +inline launch_data_t LaunchMsg(const launch_data_t data) { + return launch_msg(data); +} + +//! \} + +#pragma clang diagnostic pop + +//! \brief Converts a Core Foundation-type property list to a launchd-type +//! `launch_data_t`. +//! +//! \param[in] property_cf The Core Foundation-type property list to convert. +//! +//! \return The converted launchd-type `launch_data_t`. The caller takes +//! ownership of the returned value. On error, returns `nullptr`. +//! +//! \note This function handles all `CFPropertyListRef` types except for +//! `CFDateRef`, because there’s no `launch_data_type_t` analogue. Not all +//! types supported in a launchd-type `launch_data_t` have +//! `CFPropertyListRef` analogues. +launch_data_t CFPropertyToLaunchData(CFPropertyListRef property_cf); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MAC_LAUNCHD_H_
diff --git a/third_party/crashpad/crashpad/util/mac/launchd.mm b/third_party/crashpad/crashpad/util/mac/launchd.mm new file mode 100644 index 0000000..1b72355 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/launchd.mm
@@ -0,0 +1,141 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/launchd.h" + +#import <Foundation/Foundation.h> + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_launch_data.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/strings/sys_string_conversions.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +launch_data_t CFPropertyToLaunchData(CFPropertyListRef property_cf) { + @autoreleasepool { + // This function mixes Foundation and Core Foundation access to property + // list elements according to which is more convenient and correct for any + // specific task. + + launch_data_t data_launch = nullptr; + CFTypeID type_id_cf = CFGetTypeID(property_cf); + + if (type_id_cf == CFDictionaryGetTypeID()) { + NSDictionary* dictionary_ns = base::mac::CFToNSCast( + base::mac::CFCastStrict<CFDictionaryRef>(property_cf)); + base::mac::ScopedLaunchData dictionary_launch( + LaunchDataAlloc(LAUNCH_DATA_DICTIONARY)); + + for (NSString* key in dictionary_ns) { + if (![key isKindOfClass:[NSString class]]) { + return nullptr; + } + + CFPropertyListRef value_cf = + implicit_cast<CFPropertyListRef>([dictionary_ns objectForKey:key]); + launch_data_t value_launch = CFPropertyToLaunchData(value_cf); + if (!value_launch) { + return nullptr; + } + + LaunchDataDictInsert(dictionary_launch, value_launch, [key UTF8String]); + } + + data_launch = dictionary_launch.release(); + + } else if (type_id_cf == CFArrayGetTypeID()) { + NSArray* array_ns = base::mac::CFToNSCast( + base::mac::CFCastStrict<CFArrayRef>(property_cf)); + base::mac::ScopedLaunchData array_launch( + LaunchDataAlloc(LAUNCH_DATA_ARRAY)); + size_t index = 0; + + for (id element_ns in array_ns) { + CFPropertyListRef element_cf = + implicit_cast<CFPropertyListRef>(element_ns); + launch_data_t element_launch = CFPropertyToLaunchData(element_cf); + if (!element_launch) { + return nullptr; + } + + LaunchDataArraySetIndex(array_launch, element_launch, index++); + } + + data_launch = array_launch.release(); + + } else if (type_id_cf == CFNumberGetTypeID()) { + CFNumberRef number_cf = base::mac::CFCastStrict<CFNumberRef>(property_cf); + NSNumber* number_ns = base::mac::CFToNSCast(number_cf); + switch (CFNumberGetType(number_cf)) { + case kCFNumberSInt8Type: + case kCFNumberSInt16Type: + case kCFNumberSInt32Type: + case kCFNumberSInt64Type: + case kCFNumberCharType: + case kCFNumberShortType: + case kCFNumberIntType: + case kCFNumberLongType: + case kCFNumberLongLongType: + case kCFNumberCFIndexType: + case kCFNumberNSIntegerType: { + data_launch = LaunchDataNewInteger([number_ns longLongValue]); + break; + } + + case kCFNumberFloat32Type: + case kCFNumberFloat64Type: + case kCFNumberFloatType: + case kCFNumberDoubleType: { + data_launch = LaunchDataNewReal([number_ns doubleValue]); + break; + } + + default: { return nullptr; } + } + + } else if (type_id_cf == CFBooleanGetTypeID()) { + CFBooleanRef boolean_cf = + base::mac::CFCastStrict<CFBooleanRef>(property_cf); + data_launch = LaunchDataNewBool(CFBooleanGetValue(boolean_cf)); + + } else if (type_id_cf == CFStringGetTypeID()) { + NSString* string_ns = base::mac::CFToNSCast( + base::mac::CFCastStrict<CFStringRef>(property_cf)); + + // -fileSystemRepresentation might be more correct than -UTF8String, + // because these strings can hold paths. The analogous function in + // launchctl, CF2launch_data() (10.9.4 + // launchd-842.92.1/support/launchctl.c), uses UTF-8 instead of filesystem + // encoding, so do the same here. Note that there’s another occurrence of + // -UTF8String above, used for dictionary keys. + data_launch = LaunchDataNewString([string_ns UTF8String]); + + } else if (type_id_cf == CFDataGetTypeID()) { + NSData* data_ns = base::mac::CFToNSCast( + base::mac::CFCastStrict<CFDataRef>(property_cf)); + data_launch = LaunchDataNewOpaque([data_ns bytes], [data_ns length]); + } else { + base::ScopedCFTypeRef<CFStringRef> type_name_cf( + CFCopyTypeIDDescription(type_id_cf)); + DLOG(ERROR) << "unable to convert CFTypeID " << type_id_cf << " (" + << base::SysCFStringRefToUTF8(type_name_cf) << ")"; + } + + return data_launch; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/launchd_test.mm b/third_party/crashpad/crashpad/util/mac/launchd_test.mm new file mode 100644 index 0000000..8f60fc9 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/launchd_test.mm
@@ -0,0 +1,302 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/launchd.h" + +#import <Foundation/Foundation.h> +#include <launch.h> +#include <string.h> + +#include <cmath> +#include <limits> + +#include "base/basictypes.h" +#include "base/mac/scoped_launch_data.h" +#include "gtest/gtest.h" +#include "util/stdlib/objc.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Launchd, CFPropertyToLaunchData_Integer) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSNumber* integer_nses[] = { + @0, + @1, + @-1, + @0x7f, + @0x80, + @0xff, + @0x0100, + @0x7fff, + @0x8000, + @0xffff, + @0x00010000, + @0x7fffffff, + @0x80000000, + @0xffffffff, + @0x1000000000000000, + @0x7fffffffffffffff, + @0x8000000000000000, + @0xffffffffffffffff, + @0x0123456789abcdef, + @0xfedcba9876543210, + }; + + for (size_t index = 0; index < arraysize(integer_nses); ++index) { + NSNumber* integer_ns = integer_nses[index]; + launch_data.reset(CFPropertyToLaunchData(integer_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_INTEGER, LaunchDataGetType(launch_data)); + EXPECT_EQ([integer_ns longLongValue], LaunchDataGetInteger(launch_data)) + << "index " << index; + } + } +} + +TEST(Launchd, CFPropertyToLaunchData_FloatingPoint) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSNumber* double_nses[] = { + @0.0, + @1.0, + @-1.0, + [NSNumber numberWithFloat:std::numeric_limits<float>::min()], + [NSNumber numberWithFloat:std::numeric_limits<float>::max()], + [NSNumber numberWithDouble:std::numeric_limits<double>::min()], + [NSNumber numberWithDouble:std::numeric_limits<double>::max()], + @3.1415926535897932, + [NSNumber numberWithDouble:std::numeric_limits<double>::infinity()], + [NSNumber numberWithDouble:std::numeric_limits<double>::quiet_NaN()], + [NSNumber numberWithDouble:std::numeric_limits<double>::signaling_NaN()], + }; + + for (size_t index = 0; index < arraysize(double_nses); ++index) { + NSNumber* double_ns = double_nses[index]; + launch_data.reset(CFPropertyToLaunchData(double_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_REAL, LaunchDataGetType(launch_data)); + double expected_double_value = [double_ns doubleValue]; + double observed_double_value = LaunchDataGetReal(launch_data); + bool expected_is_nan = std::isnan(expected_double_value); + EXPECT_EQ(expected_is_nan, std::isnan(observed_double_value)); + if (!expected_is_nan) { + EXPECT_DOUBLE_EQ(expected_double_value, observed_double_value) + << "index " << index; + } + } + } +} + +TEST(Launchd, CFPropertyToLaunchData_Boolean) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSNumber* bool_nses[] = { + @NO, + @YES, + }; + + for (size_t index = 0; index < arraysize(bool_nses); ++index) { + NSNumber* bool_ns = bool_nses[index]; + launch_data.reset(CFPropertyToLaunchData(bool_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_BOOL, LaunchDataGetType(launch_data)); + if ([bool_ns boolValue]) { + EXPECT_TRUE(LaunchDataGetBool(launch_data)); + } else { + EXPECT_FALSE(LaunchDataGetBool(launch_data)); + } + } + } +} + +TEST(Launchd, CFPropertyToLaunchData_String) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSString* string_nses[] = { + @"", + @"string", + @"Üñîçø∂é", + }; + + for (size_t index = 0; index < arraysize(string_nses); ++index) { + NSString* string_ns = string_nses[index]; + launch_data.reset(CFPropertyToLaunchData(string_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_data)); + EXPECT_STREQ([string_ns UTF8String], LaunchDataGetString(launch_data)); + } + } +} + +TEST(Launchd, CFPropertyToLaunchData_Data) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + const uint8_t data_c[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 3, 2}; + NSData* data_ns = [NSData dataWithBytes:data_c length:sizeof(data_c)]; + launch_data.reset(CFPropertyToLaunchData(data_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_OPAQUE, LaunchDataGetType(launch_data)); + EXPECT_EQ(sizeof(data_c), LaunchDataGetOpaqueSize(launch_data)); + EXPECT_EQ(0, + memcmp(LaunchDataGetOpaque(launch_data), data_c, sizeof(data_c))); + } +} + +TEST(Launchd, CFPropertyToLaunchData_Dictionary) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSDictionary* dictionary_ns = @{ + @"key" : @"value", + }; + + launch_data.reset(CFPropertyToLaunchData(dictionary_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_DICTIONARY, LaunchDataGetType(launch_data)); + EXPECT_EQ([dictionary_ns count], LaunchDataDictGetCount(launch_data)); + + launch_data_t launch_lookup_data = LaunchDataDictLookup(launch_data, "key"); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data)); + EXPECT_STREQ("value", LaunchDataGetString(launch_lookup_data)); + } +} + +TEST(Launchd, CFPropertyToLaunchData_Array) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSArray* array_ns = @[ @"element_1", @"element_2", ]; + + launch_data.reset(CFPropertyToLaunchData(array_ns)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_ARRAY, LaunchDataGetType(launch_data)); + EXPECT_EQ([array_ns count], LaunchDataArrayGetCount(launch_data)); + + launch_data_t launch_lookup_data = LaunchDataArrayGetIndex(launch_data, 0); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data)); + EXPECT_STREQ("element_1", LaunchDataGetString(launch_lookup_data)); + + launch_lookup_data = LaunchDataArrayGetIndex(launch_data, 1); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data)); + EXPECT_STREQ("element_2", LaunchDataGetString(launch_lookup_data)); + } +} + +TEST(Launchd, CFPropertyToLaunchData_NSDate) { + // Test that NSDate conversions fail as advertised. There’s no type for + // storing date values in a launch_data_t. + + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSDate* date = [NSDate date]; + launch_data.reset(CFPropertyToLaunchData(date)); + EXPECT_FALSE(launch_data); + + NSDictionary* date_dictionary = @{ + @"key" : @"value", + @"date" : date, + }; + launch_data.reset(CFPropertyToLaunchData(date_dictionary)); + EXPECT_FALSE(launch_data); + + NSArray* date_array = @[ @"string_1", date, @"string_2", ]; + launch_data.reset(CFPropertyToLaunchData(date_array)); + EXPECT_FALSE(launch_data); + } +} + +TEST(Launchd, CFPropertyToLaunchData_RealWorldJobDictionary) { + @autoreleasepool { + base::mac::ScopedLaunchData launch_data; + + NSDictionary* job_dictionary = @{ + @LAUNCH_JOBKEY_LABEL : @"com.example.job.rebooter", + @LAUNCH_JOBKEY_ONDEMAND : @YES, + @LAUNCH_JOBKEY_PROGRAMARGUMENTS : + @[ @"/bin/bash", @"-c", @"/sbin/reboot", ], + @LAUNCH_JOBKEY_MACHSERVICES : @{ + @"com.example.service.rebooter" : @YES, + }, + }; + + launch_data.reset(CFPropertyToLaunchData(job_dictionary)); + ASSERT_TRUE(launch_data.get()); + ASSERT_EQ(LAUNCH_DATA_DICTIONARY, LaunchDataGetType(launch_data)); + EXPECT_EQ(4u, LaunchDataDictGetCount(launch_data)); + + launch_data_t launch_lookup_data = + LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_LABEL); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data)); + EXPECT_STREQ("com.example.job.rebooter", + LaunchDataGetString(launch_lookup_data)); + + launch_lookup_data = + LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_ONDEMAND); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_BOOL, LaunchDataGetType(launch_lookup_data)); + EXPECT_TRUE(LaunchDataGetBool(launch_lookup_data)); + + launch_lookup_data = + LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_PROGRAMARGUMENTS); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_ARRAY, LaunchDataGetType(launch_lookup_data)); + EXPECT_EQ(3u, LaunchDataArrayGetCount(launch_lookup_data)); + + launch_data_t launch_sublookup_data = + LaunchDataArrayGetIndex(launch_lookup_data, 0); + ASSERT_TRUE(launch_sublookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_sublookup_data)); + EXPECT_STREQ("/bin/bash", LaunchDataGetString(launch_sublookup_data)); + + launch_sublookup_data = LaunchDataArrayGetIndex(launch_lookup_data, 1); + ASSERT_TRUE(launch_sublookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_sublookup_data)); + EXPECT_STREQ("-c", LaunchDataGetString(launch_sublookup_data)); + + launch_sublookup_data = LaunchDataArrayGetIndex(launch_lookup_data, 2); + ASSERT_TRUE(launch_sublookup_data); + ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_sublookup_data)); + EXPECT_STREQ("/sbin/reboot", LaunchDataGetString(launch_sublookup_data)); + + launch_lookup_data = + LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_MACHSERVICES); + ASSERT_TRUE(launch_lookup_data); + ASSERT_EQ(LAUNCH_DATA_DICTIONARY, LaunchDataGetType(launch_lookup_data)); + EXPECT_EQ(1u, LaunchDataDictGetCount(launch_lookup_data)); + + launch_sublookup_data = LaunchDataDictLookup( + launch_lookup_data, "com.example.service.rebooter"); + ASSERT_TRUE(launch_sublookup_data); + ASSERT_EQ(LAUNCH_DATA_BOOL, LaunchDataGetType(launch_sublookup_data)); + EXPECT_TRUE(LaunchDataGetBool(launch_sublookup_data)); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/mac_util.cc b/third_party/crashpad/crashpad/util/mac/mac_util.cc new file mode 100644 index 0000000..a792e51 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/mac_util.cc
@@ -0,0 +1,282 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/mac_util.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/utsname.h> + +#include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_ioobject.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" + +extern "C" { +// Private CoreFoundation internals. See 10.9.2 CF-855.14/CFPriv.h and +// CF-855.14/CFUtilities.c. These are marked for weak import because they’re +// private and subject to change. + +#define WEAK_IMPORT __attribute__((weak_import)) + +// Don’t call these functions directly, call them through the +// TryCFCopy*VersionDictionary() helpers to account for the possibility that +// they may not be present at runtime. +CFDictionaryRef _CFCopySystemVersionDictionary() WEAK_IMPORT; +CFDictionaryRef _CFCopyServerVersionDictionary() WEAK_IMPORT; + +// Don’t use these constants with CFDictionaryGetValue() directly, use them with +// the TryCFDictionaryGetValue() wrapper to account for the possibility that +// they may not be present at runtime. +extern const CFStringRef _kCFSystemVersionProductNameKey WEAK_IMPORT; +extern const CFStringRef _kCFSystemVersionProductVersionKey WEAK_IMPORT; +extern const CFStringRef _kCFSystemVersionProductVersionExtraKey WEAK_IMPORT; +extern const CFStringRef _kCFSystemVersionBuildVersionKey WEAK_IMPORT; + +#undef WEAK_IMPORT + +} // extern "C" + +namespace { + +// Returns the running system’s Darwin major version. Don’t call this, it’s an +// implementation detail and its result is meant to be cached by +// MacOSXMinorVersion(). +// +// This is very similar to Chromium’s base/mac/mac_util.mm +// DarwinMajorVersionInternal(). +int DarwinMajorVersion() { + // base::OperatingSystemVersionNumbers calls Gestalt(), which is a + // higher-level function than is needed. It might perform unnecessary + // operations. On 10.6, it was observed to be able to spawn threads (see + // http://crbug.com/53200). It might also read files or perform other blocking + // operations. Actually, nobody really knows for sure just what Gestalt() + // might do, or what it might be taught to do in the future. + // + // uname(), on the other hand, is implemented as a simple series of sysctl() + // system calls to obtain the relevant data from the kernel. The data is + // compiled right into the kernel, so no threads or blocking or other funny + // business is necessary. + + utsname uname_info; + int rv = uname(&uname_info); + PCHECK(rv == 0) << "uname"; + + DCHECK_EQ(strcmp(uname_info.sysname, "Darwin"), 0) << "unexpected sysname " + << uname_info.sysname; + + char* dot = strchr(uname_info.release, '.'); + CHECK(dot); + + int darwin_major_version = 0; + CHECK(base::StringToInt( + base::StringPiece(uname_info.release, dot - uname_info.release), + &darwin_major_version)); + + return darwin_major_version; +} + +// Helpers for the weak-imported private CoreFoundation internals. + +CFDictionaryRef TryCFCopySystemVersionDictionary() { + if (_CFCopySystemVersionDictionary) { + return _CFCopySystemVersionDictionary(); + } + return nullptr; +} + +CFDictionaryRef TryCFCopyServerVersionDictionary() { + if (_CFCopyServerVersionDictionary) { + return _CFCopyServerVersionDictionary(); + } + return nullptr; +} + +const void* TryCFDictionaryGetValue(CFDictionaryRef dictionary, + const void* value) { + if (value) { + return CFDictionaryGetValue(dictionary, value); + } + return nullptr; +} + +// Converts |version| to a triplet of version numbers on behalf of +// MacOSXVersion(). Returns true on success. If |version| does not have the +// expected format, returns false. |version| must be in the form "10.9.2" or +// just "10.9". In the latter case, |bugfix| will be set to 0. +bool StringToVersionNumbers(const std::string& version, + int* major, + int* minor, + int* bugfix) { + size_t first_dot = version.find_first_of('.'); + if (first_dot == 0 || first_dot == std::string::npos || + first_dot == version.length() - 1) { + LOG(ERROR) << "version has unexpected format"; + return false; + } + if (!base::StringToInt(base::StringPiece(&version[0], first_dot), major)) { + LOG(ERROR) << "version has unexpected format"; + return false; + } + + size_t second_dot = version.find_first_of('.', first_dot + 1); + if (second_dot == version.length() - 1) { + LOG(ERROR) << "version has unexpected format"; + return false; + } else if (second_dot == std::string::npos) { + second_dot = version.length(); + } + + if (!base::StringToInt(base::StringPiece(&version[first_dot + 1], + second_dot - first_dot - 1), + minor)) { + LOG(ERROR) << "version has unexpected format"; + return false; + } + + if (second_dot == version.length()) { + *bugfix = 0; + } else if (!base::StringToInt( + base::StringPiece(&version[second_dot + 1], + version.length() - second_dot - 1), + bugfix)) { + LOG(ERROR) << "version has unexpected format"; + return false; + } + + return true; +} + +std::string IORegistryEntryDataPropertyAsString(io_registry_entry_t entry, + CFStringRef key) { + base::ScopedCFTypeRef<CFTypeRef> property( + IORegistryEntryCreateCFProperty(entry, key, kCFAllocatorDefault, 0)); + CFDataRef data = base::mac::CFCast<CFDataRef>(property); + if (data && CFDataGetLength(data) > 0) { + return reinterpret_cast<const char*>(CFDataGetBytePtr(data)); + } + + return std::string(); +} + +} // namespace + +namespace crashpad { + +int MacOSXMinorVersion() { + // The Darwin major version is always 4 greater than the Mac OS X minor + // version for Darwin versions beginning with 6, corresponding to Mac OS X + // 10.2. + static int mac_os_x_minor_version = DarwinMajorVersion() - 4; + DCHECK(mac_os_x_minor_version >= 2); + return mac_os_x_minor_version; +} + +bool MacOSXVersion(int* major, + int* minor, + int* bugfix, + std::string* build, + bool* server, + std::string* version_string) { + base::ScopedCFTypeRef<CFDictionaryRef> dictionary( + TryCFCopyServerVersionDictionary()); + if (dictionary) { + *server = true; + } else { + dictionary.reset(TryCFCopySystemVersionDictionary()); + if (!dictionary) { + LOG(ERROR) << "_CFCopySystemVersionDictionary failed"; + return false; + } + *server = false; + } + + bool success = true; + + CFStringRef version_cf = base::mac::CFCast<CFStringRef>( + TryCFDictionaryGetValue(dictionary, _kCFSystemVersionProductVersionKey)); + std::string version; + if (!version_cf) { + LOG(ERROR) << "version_cf not found"; + success = false; + } else { + version = base::SysCFStringRefToUTF8(version_cf); + success &= StringToVersionNumbers(version, major, minor, bugfix); + } + + CFStringRef build_cf = base::mac::CFCast<CFStringRef>( + TryCFDictionaryGetValue(dictionary, _kCFSystemVersionBuildVersionKey)); + if (!build_cf) { + LOG(ERROR) << "build_cf not found"; + success = false; + } else { + build->assign(base::SysCFStringRefToUTF8(build_cf)); + } + + CFStringRef product_cf = base::mac::CFCast<CFStringRef>( + TryCFDictionaryGetValue(dictionary, _kCFSystemVersionProductNameKey)); + std::string product; + if (!product_cf) { + LOG(ERROR) << "product_cf not found"; + success = false; + } else { + product = base::SysCFStringRefToUTF8(product_cf); + } + + // This key is not required, and in fact is normally not present. + CFStringRef extra_cf = base::mac::CFCast<CFStringRef>(TryCFDictionaryGetValue( + dictionary, _kCFSystemVersionProductVersionExtraKey)); + std::string extra; + if (extra_cf) { + extra = base::SysCFStringRefToUTF8(extra_cf); + } + + if (!product.empty() || !version.empty() || !build->empty()) { + if (!extra.empty()) { + version_string->assign(base::StringPrintf("%s %s %s (%s)", + product.c_str(), + version.c_str(), + extra.c_str(), + build->c_str())); + } else { + version_string->assign(base::StringPrintf( + "%s %s (%s)", product.c_str(), version.c_str(), build->c_str())); + } + } + + return success; +} + +void MacModelAndBoard(std::string* model, std::string* board_id) { + base::mac::ScopedIOObject<io_service_t> platform_expert( + IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("IOPlatformExpertDevice"))); + if (platform_expert) { + model->assign( + IORegistryEntryDataPropertyAsString(platform_expert, CFSTR("model"))); + board_id->assign(IORegistryEntryDataPropertyAsString(platform_expert, + CFSTR("board-id"))); + } else { + model->clear(); + board_id->clear(); + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/mac_util.h b/third_party/crashpad/crashpad/util/mac/mac_util.h new file mode 100644 index 0000000..9cedfdf --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/mac_util.h
@@ -0,0 +1,73 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MAC_MAC_UTIL_H_ +#define CRASHPAD_UTIL_MAC_MAC_UTIL_H_ + +#include <string> + +namespace crashpad { + +//! \brief Returns the version of the running operating system. +//! +//! \return The minor version of the operating system, such as `9` for Mac OS X +//! 10.9.2. +//! +//! \note This is similar to the base::mac::IsOS*() family of functions, but +//! is provided for situations where the caller needs to obtain version +//! information beyond what is provided by Chromium’s base, or for when the +//! caller needs the actual minor version value. +int MacOSXMinorVersion(); + +//! \brief Returns the version of the running operating system. +//! +//! All parameters are required. No parameter may be `nullptr`. +//! +//! \param[out] major The major version of the operating system, such as `10` +//! for Mac OS X 10.9.2. +//! \param[out] minor The major version of the operating system, such as `9` for +//! Mac OS X 10.9.2. +//! \param[out] bugfix The bugfix version of the operating system, such as `2` +//! for Mac OS X 10.9.2. +//! \param[out] build The operating system’s build string, such as "13C64" for +//! Mac OS X 10.9.2. +//! \param[out] server `true` for a Mac OS X Server installation, `false` +//! otherwise (for a desktop/laptop, client, or workstation system). +//! \param[out] version_string A string representing the full operating system +//! version, such as `"Mac OS X 10.9.2 (13C64)"`. +//! +//! \return `true` on success, `false` on failure, with an error message logged. +//! A failure is considered to have occurred if any element could not be +//! determined. When this happens, their values will be untouched, but other +//! values that could be determined will still be set properly. +bool MacOSXVersion(int* major, + int* minor, + int* bugfix, + std::string* build, + bool* server, + std::string* version_string); + +//! \brief Returns the model name and board ID of the running system. +//! +//! \param[out] model The system’s model name. A mid-2012 15" MacBook Pro would +//! report “MacBookPro10,1”. +//! \param[out] board_id The system’s board ID. A mid-2012 15" MacBook Pro would +//! report “Mac-C3EC7CD22292981F”. +//! +//! If a value cannot be determined, its string is cleared. +void MacModelAndBoard(std::string* model, std::string* board_id); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MAC_MAC_UTIL_H_
diff --git a/third_party/crashpad/crashpad/util/mac/mac_util_test.mm b/third_party/crashpad/crashpad/util/mac/mac_util_test.mm new file mode 100644 index 0000000..5b41b420 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/mac_util_test.mm
@@ -0,0 +1,143 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/mac_util.h" + +#import <Foundation/Foundation.h> +#include <stdlib.h> + +#include <string> + +#include "base/mac/scoped_nsobject.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +#ifdef __GLIBCXX__ +// When C++ exceptions are disabled, libstdc++ from GCC 4.2 defines |try| and +// |catch| so as to allow exception-expecting C++ code to build properly when +// language support for exceptions is not present. These macros interfere with +// the use of |@try| and |@catch| in Objective-C files such as this one. +// Undefine these macros here, after everything has been #included, since there +// will be no C++ uses and only Objective-C uses from this point on. +#undef try +#undef catch +#endif + +namespace crashpad { +namespace test { +namespace { + +// Runs /usr/bin/sw_vers with a single argument, |argument|, and places the +// command’s standard output into |output| after stripping the trailing newline. +// Fatal gtest assertions report tool failures, which the caller should check +// for with ASSERT_NO_FATAL_FAILURE() or testing::Test::HasFatalFailure(). +void SwVers(NSString* argument, std::string* output) { + @autoreleasepool { + base::scoped_nsobject<NSPipe> pipe([[NSPipe alloc] init]); + base::scoped_nsobject<NSTask> task([[NSTask alloc] init]); + [task setStandardOutput:pipe]; + [task setLaunchPath:@"/usr/bin/sw_vers"]; + [task setArguments:@[ argument ]]; + + @try { + [task launch]; + } + @catch (NSException* exception) { + FAIL() << [[exception name] UTF8String] << ": " + << [[exception reason] UTF8String]; + } + + NSData* data = [[pipe fileHandleForReading] readDataToEndOfFile]; + [task waitUntilExit]; + + ASSERT_EQ(NSTaskTerminationReasonExit, [task terminationReason]); + ASSERT_EQ(EXIT_SUCCESS, [task terminationStatus]); + + output->assign(reinterpret_cast<const char*>([data bytes]), [data length]); + + EXPECT_EQ('\n', output->at(output->size() - 1)); + output->resize(output->size() - 1); + } +} + +TEST(MacUtil, MacOSXVersion) { + int major; + int minor; + int bugfix; + std::string build; + bool server; + std::string version_string; + ASSERT_TRUE( + MacOSXVersion(&major, &minor, &bugfix, &build, &server, &version_string)); + + std::string version; + if (bugfix) { + version = base::StringPrintf("%d.%d.%d", major, minor, bugfix); + } else { + // 10.x.0 releases report their version string as simply 10.x. + version = base::StringPrintf("%d.%d", major, minor); + } + + std::string expected_product_version; + ASSERT_NO_FATAL_FAILURE( + SwVers(@"-productVersion", &expected_product_version)); + + EXPECT_EQ(expected_product_version, version); + + std::string expected_build_version; + ASSERT_NO_FATAL_FAILURE(SwVers(@"-buildVersion", &expected_build_version)); + + EXPECT_EQ(expected_build_version, build); + + std::string expected_product_name; + ASSERT_NO_FATAL_FAILURE(SwVers(@"-productName", &expected_product_name)); + + // Look for a space after the product name in the complete version string. + expected_product_name += ' '; + EXPECT_EQ(0u, version_string.find(expected_product_name)); +} + +TEST(MacUtil, MacOSXMinorVersion) { + // Make sure that MacOSXMinorVersion() and MacOSXVersion() agree. The two have + // their own distinct implementations, and the latter was checked against + // sw_vers above. + int major; + int minor; + int bugfix; + std::string build; + bool server; + std::string version_string; + ASSERT_TRUE( + MacOSXVersion(&major, &minor, &bugfix, &build, &server, &version_string)); + + EXPECT_EQ(minor, MacOSXMinorVersion()); +} + +TEST(MacUtil, MacModelAndBoard) { + // There’s not much that can be done to test these, so just make sure they’re + // not empty. The model could be compared against the parsed output of + // “system_profiler SPHardwareDataType”, but the board doesn’t show up + // anywhere other than the I/O Registry, and that’s exactly how + // MacModelAndBoard() gets the data, so it wouldn’t be a very useful test. + std::string model; + std::string board; + MacModelAndBoard(&model, &board); + + EXPECT_FALSE(model.empty()); + EXPECT_FALSE(board.empty()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/service_management.cc b/third_party/crashpad/crashpad/util/mac/service_management.cc new file mode 100644 index 0000000..9940006ad --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/service_management.cc
@@ -0,0 +1,136 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/service_management.h" + +#include <errno.h> +#include <launch.h> + +#include "base/mac/scoped_launch_data.h" +#include "util/mac/launchd.h" +#include "util/misc/clock.h" + +namespace crashpad { + +namespace { + +launch_data_t LaunchDataDictionaryForJob(const std::string& label) { + base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY)); + LaunchDataDictInsert( + request, LaunchDataNewString(label.c_str()), LAUNCH_KEY_GETJOB); + + base::mac::ScopedLaunchData response(LaunchMsg(request)); + if (LaunchDataGetType(response) != LAUNCH_DATA_DICTIONARY) { + return nullptr; + } + + return response.release(); +} + +} // namespace + +bool ServiceManagementSubmitJob(CFDictionaryRef job_cf) { + base::mac::ScopedLaunchData job_launch(CFPropertyToLaunchData(job_cf)); + if (!job_launch.get()) { + return false; + } + + base::mac::ScopedLaunchData jobs(LaunchDataAlloc(LAUNCH_DATA_ARRAY)); + LaunchDataArraySetIndex(jobs, job_launch.release(), 0); + + base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY)); + LaunchDataDictInsert(request, jobs.release(), LAUNCH_KEY_SUBMITJOB); + + base::mac::ScopedLaunchData response(LaunchMsg(request)); + if (LaunchDataGetType(response) != LAUNCH_DATA_ARRAY) { + return false; + } + + if (LaunchDataArrayGetCount(response) != 1) { + return false; + } + + launch_data_t response_element = LaunchDataArrayGetIndex(response, 0); + if (LaunchDataGetType(response_element) != LAUNCH_DATA_ERRNO) { + return false; + } + + int err = LaunchDataGetErrno(response_element); + if (err != 0) { + return false; + } + + return true; +} + +bool ServiceManagementRemoveJob(const std::string& label, bool wait) { + base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY)); + LaunchDataDictInsert( + request, LaunchDataNewString(label.c_str()), LAUNCH_KEY_REMOVEJOB); + + base::mac::ScopedLaunchData response(LaunchMsg(request)); + if (LaunchDataGetType(response) != LAUNCH_DATA_ERRNO) { + return false; + } + + int err = LaunchDataGetErrno(response); + if (err == EINPROGRESS) { + if (wait) { + // TODO(mark): Use a kqueue to wait for the process to exit. To avoid a + // race, the kqueue would need to be set up prior to asking launchd to + // remove the job. Even so, the job’s PID may change between the time it’s + // obtained and the time the kqueue is set up, so this is nontrivial. + do { + SleepNanoseconds(1E5); // 100 microseconds + } while (ServiceManagementIsJobLoaded(label)); + } + + return true; + } + + if (err != 0) { + return false; + } + + return true; +} + +bool ServiceManagementIsJobLoaded(const std::string& label) { + base::mac::ScopedLaunchData dictionary(LaunchDataDictionaryForJob(label)); + if (!dictionary) { + return false; + } + + return true; +} + +pid_t ServiceManagementIsJobRunning(const std::string& label) { + base::mac::ScopedLaunchData dictionary(LaunchDataDictionaryForJob(label)); + if (!dictionary) { + return 0; + } + + launch_data_t pid = LaunchDataDictLookup(dictionary, LAUNCH_JOBKEY_PID); + if (!pid) { + return 0; + } + + if (LaunchDataGetType(pid) != LAUNCH_DATA_INTEGER) { + return 0; + } + + return LaunchDataGetInteger(pid); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/service_management.h b/third_party/crashpad/crashpad/util/mac/service_management.h new file mode 100644 index 0000000..802e5449 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/service_management.h
@@ -0,0 +1,81 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT_H_ +#define CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <unistd.h> + +#include <string> + +namespace crashpad { + +//! \brief Submits a job to the user launchd domain as in `SMJobSubmit()`. +//! +//! \param[in] job_cf A dictionary describing a job. +//! +//! \return `true` if the job was submitted successfully, otherwise `false`. +//! +//! \note This function is provided because `SMJobSubmit()` is deprecated in Mac +//! OS X 10.10. It may or may not be implemented using `SMJobSubmit()` from +//! `ServiceManagement.framework`. +bool ServiceManagementSubmitJob(CFDictionaryRef job_cf); + +//! \brief Removes a job from the user launchd domain as in `SMJobRemove()`. +//! +//! \param[in] label The label for the job to remove. +//! \param[in] wait `true` if this function should block, waiting for the job to +//! be removed. `false` if the job may be removed asynchronously. +//! +//! \return `true` if the job was removed successfully or if an asynchronous +//! attempt to remove the job was started successfully, otherwise `false`. +//! +//! \note This function is provided because `SMJobRemove()` is deprecated in Mac +//! OS X 10.10. On Mac OS X 10.10, observed in DP8 14A361c, it also blocks +//! for far too long (`_block_until_job_exits()` contains a one-second +//! `sleep()`, filed as radar 18398683) and does not signal failure via its +//! return value when asked to remove a nonexistent job (filed as radar +//! 18268941). +bool ServiceManagementRemoveJob(const std::string& label, bool wait); + +//! \brief Determines whether a specified job is loaded in the user launchd +//! domain. +//! +//! \param[in] label The label for the job to look up. +//! +//! \return `true` if the job is loaded, otherwise `false`. +//! +//! \note A loaded job is not necessarily presently running, nor has it +//! necessarily ever run in the past. +//! \note This function is provided because `SMJobCopyDictionary()` is +//! deprecated in Mac OS X 10.10. It may or may not be implemented using +//! `SMJobCopyDictionary()` from `ServiceManagement.framework`. +bool ServiceManagementIsJobLoaded(const std::string& label); + +//! \brief Determines whether a specified job is running in the user launchd +//! domain. +//! +//! \param[in] label The label for the job to look up. +//! +//! \return The job’s process ID if running, otherwise `0`. +//! +//! \note This function is provided because `SMJobCopyDictionary()` is +//! deprecated in Mac OS X 10.10. It may or may not be implemented using +//! `SMJobCopyDictionary()` from `ServiceManagement.framework`. +pid_t ServiceManagementIsJobRunning(const std::string& label); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT
diff --git a/third_party/crashpad/crashpad/util/mac/service_management_test.mm b/third_party/crashpad/crashpad/util/mac/service_management_test.mm new file mode 100644 index 0000000..cdfd082 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/service_management_test.mm
@@ -0,0 +1,160 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/service_management.h" + +#import <Foundation/Foundation.h> +#include <launch.h> + +#include <string> +#include <vector> + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "gtest/gtest.h" +#include "util/misc/clock.h" +#include "util/misc/random_string.h" +#include "util/posix/process_info.h" +#include "util/stdlib/objc.h" + +namespace crashpad { +namespace test { +namespace { + +// Ensures that the process with the specified PID is running, identifying it by +// requiring that its argv[argc - 1] compare equal to last_arg. +void ExpectProcessIsRunning(pid_t pid, std::string& last_arg) { + ProcessInfo process_info; + ASSERT_TRUE(process_info.Initialize(pid)); + + // The process may not have called exec yet, so loop with a small delay while + // looking for the cookie. + int outer_tries = 10; + std::vector<std::string> job_argv; + while (outer_tries--) { + // If the process is in the middle of calling exec, process_info.Arguments() + // may fail. Loop with a small retry delay while waiting for the expected + // successful call. + int inner_tries = 10; + bool success; + do { + success = process_info.Arguments(&job_argv); + if (success) { + break; + } + if (inner_tries > 0) { + SleepNanoseconds(1E6); // 1 millisecond + } + } while (inner_tries--); + ASSERT_TRUE(success); + + ASSERT_FALSE(job_argv.empty()); + if (job_argv.back() == last_arg) { + break; + } + + if (outer_tries > 0) { + SleepNanoseconds(1E6); // 1 millisecond + } + } + + ASSERT_FALSE(job_argv.empty()); + EXPECT_EQ(last_arg, job_argv.back()); +} + +// Ensures that the process with the specified PID is not running. Because the +// PID may be reused for another process, a process is only treated as running +// if its argv[argc - 1] compares equal to last_arg. +void ExpectProcessIsNotRunning(pid_t pid, std::string& last_arg) { + // The process may not have exited yet, so loop with a small delay while + // checking that it has exited. + int tries = 10; + std::vector<std::string> job_argv; + while (tries--) { + ProcessInfo process_info; + if (!process_info.Initialize(pid) || !process_info.Arguments(&job_argv)) { + // The PID was not found. + return; + } + + // The PID was found. It may have been recycled for another process. Make + // sure that the cookie isn’t found. + ASSERT_FALSE(job_argv.empty()); + if (job_argv.back() != last_arg) { + break; + } + + if (tries > 0) { + SleepNanoseconds(1E6); // 1 millisecond + } + } + + ASSERT_FALSE(job_argv.empty()); + EXPECT_NE(last_arg, job_argv.back()); +} + +TEST(ServiceManagement, SubmitRemoveJob) { + @autoreleasepool { + const std::string cookie = RandomString(); + + std::string shell_script = + base::StringPrintf("sleep 10; echo %s", cookie.c_str()); + NSString* shell_script_ns = base::SysUTF8ToNSString(shell_script); + + const char kJobLabel[] = "org.chromium.crashpad.test.service_management"; + NSDictionary* job_dictionary_ns = @{ + @LAUNCH_JOBKEY_LABEL : @"org.chromium.crashpad.test.service_management", + @LAUNCH_JOBKEY_RUNATLOAD : @YES, + @LAUNCH_JOBKEY_PROGRAMARGUMENTS : + @[ @"/bin/sh", @"-c", shell_script_ns, ], + }; + CFDictionaryRef job_dictionary_cf = + base::mac::NSToCFCast(job_dictionary_ns); + + // The job may be left over from a failed previous run. + if (ServiceManagementIsJobLoaded(kJobLabel)) { + EXPECT_TRUE(ServiceManagementRemoveJob(kJobLabel, true)); + } + + EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel)); + ASSERT_FALSE(ServiceManagementIsJobRunning(kJobLabel)); + + // Submit the job. + ASSERT_TRUE(ServiceManagementSubmitJob(job_dictionary_cf)); + EXPECT_TRUE(ServiceManagementIsJobLoaded(kJobLabel)); + + // launchd started the job because RunAtLoad is true. + pid_t job_pid = ServiceManagementIsJobRunning(kJobLabel); + ASSERT_GT(job_pid, 0); + + ExpectProcessIsRunning(job_pid, shell_script); + + // Remove the job. + ASSERT_TRUE(ServiceManagementRemoveJob(kJobLabel, true)); + EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel)); + EXPECT_EQ(0, ServiceManagementIsJobRunning(kJobLabel)); + + // Now that the job is unloaded, a subsequent attempt to unload it should be + // an error. + EXPECT_FALSE(ServiceManagementRemoveJob(kJobLabel, false)); + + ExpectProcessIsNotRunning(job_pid, shell_script); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/xattr.cc b/third_party/crashpad/crashpad/util/mac/xattr.cc new file mode 100644 index 0000000..cb72e06e --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/xattr.cc
@@ -0,0 +1,150 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/xattr.h" + +#include <errno.h> +#include <stdint.h> +#include <sys/xattr.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.h" + +namespace crashpad { + +XattrStatus ReadXattr(const base::FilePath& file, + const base::StringPiece& name, + std::string* value) { + // First get the size of the attribute value. + ssize_t buffer_size = getxattr(file.value().c_str(), name.data(), nullptr, + 0, 0, 0); + if (buffer_size < 0) { + if (errno == ENOATTR) + return XattrStatus::kNoAttribute; + PLOG(ERROR) << "getxattr size " << name << " on file " << file.value(); + return XattrStatus::kOtherError; + } + + // Resize the buffer and read into it. + value->resize(buffer_size); + if (!value->empty()) { + ssize_t bytes_read = getxattr(file.value().c_str(), name.data(), + &(*value)[0], value->size(), + 0, 0); + if (bytes_read < 0) { + PLOG(ERROR) << "getxattr " << name << " on file " << file.value(); + return XattrStatus::kOtherError; + } + DCHECK_EQ(bytes_read, buffer_size); + } + + return XattrStatus::kOK; +} + +bool WriteXattr(const base::FilePath& file, + const base::StringPiece& name, + const std::string& value) { + int rv = setxattr(file.value().c_str(), name.data(), value.c_str(), + value.length(), 0, 0); + PLOG_IF(ERROR, rv != 0) << "setxattr " << name << " on file " + << file.value(); + return rv == 0; +} + +XattrStatus ReadXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool* value) { + std::string tmp; + XattrStatus status; + if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK) + return status; + if (tmp == "1") { + *value = true; + return XattrStatus::kOK; + } else if (tmp == "0") { + *value = false; + return XattrStatus::kOK; + } else { + LOG(ERROR) << "ReadXattrBool " << name << " on file " << file.value() + << " could not be interpreted as boolean"; + return XattrStatus::kOtherError; + } +} + +bool WriteXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool value) { + return WriteXattr(file, name, (value ? "1" : "0")); +} + +XattrStatus ReadXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int* value) { + std::string tmp; + XattrStatus status; + if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK) + return status; + if (!base::StringToInt(tmp, value)) { + LOG(ERROR) << "ReadXattrInt " << name << " on file " << file.value() + << " could not be converted to an int"; + return XattrStatus::kOtherError; + } + return XattrStatus::kOK; +} + +bool WriteXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int value) { + std::string tmp = base::StringPrintf("%d", value); + return WriteXattr(file, name, tmp); +} + +XattrStatus ReadXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t* value) { + // time_t on OS X is defined as a long, but it will be read into an + // int64_t here, since there is no string conversion method for long. + std::string tmp; + XattrStatus status; + if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK) + return status; + + int64_t encoded_value; + if (!base::StringToInt64(tmp, &encoded_value)) { + LOG(ERROR) << "ReadXattrTimeT " << name << " on file " << file.value() + << " could not be converted to an int"; + return XattrStatus::kOtherError; + } + + *value = base::saturated_cast<time_t>(encoded_value); + if (!base::IsValueInRangeForNumericType<time_t>(encoded_value)) { + LOG(ERROR) << "ReadXattrTimeT " << name << " on file " << file.value() + << " read over-sized value and will saturate"; + return XattrStatus::kOtherError; + } + + return XattrStatus::kOK; +} + +bool WriteXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t value) { + std::string tmp = base::StringPrintf("%ld", value); + return WriteXattr(file, name, tmp); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mac/xattr.h b/third_party/crashpad/crashpad/util/mac/xattr.h new file mode 100644 index 0000000..3e14f67 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/xattr.h
@@ -0,0 +1,97 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MAC_XATTR_H_ +#define CRASHPAD_UTIL_MAC_XATTR_H_ + +#include <time.h> + +#include <string> + +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" + +namespace crashpad { + +//! \brief The result code for a ReadXattr operation. +enum class XattrStatus { + //! \brief No error occured. No message is logged. + kOK = 0, + + //! \brief The attribute does not exist. No message is logged. + kNoAttribute, + + //! \brief An error occurred and an error message was logged. + kOtherError, +}; + +//! \brief Reads an extended attribute on a file. +//! +//! \param[in] file The path to the file. +//! \param[in] name The name of the extended attribute to read. +//! \param[out] value The value of the attribute. +//! +//! \return XattrStatus +XattrStatus ReadXattr(const base::FilePath& file, + const base::StringPiece& name, + std::string* value); + +//! \brief Writes an extended attribute on a file. +//! +//! \param[in] file The path to the file. +//! \param[in] name The name of the extended attribute to write. +//! \param[in] value The value of the attribute. +//! +//! \return `true` if the write was successful. `false` on error, with a message +//! logged. +bool WriteXattr(const base::FilePath& file, + const base::StringPiece& name, + const std::string& value); + +//! \copydoc ReadXattr +//! +//! Only the values `"0"` and `"1"`, for `false` and `true` respectively, are +//! valid conversions. +XattrStatus ReadXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool* value); + +//! \copydoc WriteXattr +bool WriteXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool value); + +//! \copydoc ReadXattr +XattrStatus ReadXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int* value); + +//! \copydoc WriteXattr +bool WriteXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int value); + +//! \copydoc ReadXattr +XattrStatus ReadXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t* value); + +//! \copydoc WriteXattr +bool WriteXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t value); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MAC_XATTR_H_
diff --git a/third_party/crashpad/crashpad/util/mac/xattr_test.cc b/third_party/crashpad/crashpad/util/mac/xattr_test.cc new file mode 100644 index 0000000..a156a0a --- /dev/null +++ b/third_party/crashpad/crashpad/util/mac/xattr_test.cc
@@ -0,0 +1,122 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mac/xattr.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <limits> + +#include "base/files/scoped_file.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" + +namespace crashpad { +namespace test { +namespace { + +class Xattr : public testing::Test { + protected: + // testing::Test: + + void SetUp() override { + path_ = temp_dir_.path().Append("xattr_file"); + base::ScopedFD tmp(HANDLE_EINTR( + open(path_.value().c_str(), O_CREAT | O_TRUNC, 0644))); + EXPECT_GE(tmp.get(), 0) << ErrnoMessage("open"); + } + + void TearDown() override { + EXPECT_EQ(0, unlink(path_.value().c_str())) << ErrnoMessage("unlink"); + } + + const base::FilePath& path() const { return path_; } + + private: + ScopedTempDir temp_dir_; + base::FilePath path_; +}; + +const char kKey[] = "com.google.crashpad.test"; + +TEST_F(Xattr, ReadNonExistentXattr) { + std::string value; + EXPECT_EQ(XattrStatus::kNoAttribute, ReadXattr(path(), kKey, &value)); +} + +TEST_F(Xattr, WriteAndReadString) { + std::string value = "hello world"; + EXPECT_TRUE(WriteXattr(path(), kKey, value)); + + std::string actual; + EXPECT_EQ(XattrStatus::kOK, ReadXattr(path(), kKey, &actual)); + EXPECT_EQ(value, actual); +} + +TEST_F(Xattr, WriteAndReadVeryLongString) { + std::string value(533, 'A'); + EXPECT_TRUE(WriteXattr(path(), kKey, value)); + + std::string actual; + EXPECT_EQ(XattrStatus::kOK, ReadXattr(path(), kKey, &actual)); + EXPECT_EQ(value, actual); +} + +TEST_F(Xattr, WriteAndReadBool) { + EXPECT_TRUE(WriteXattrBool(path(), kKey, true)); + bool actual = false; + EXPECT_EQ(XattrStatus::kOK, ReadXattrBool(path(), kKey, &actual)); + EXPECT_TRUE(actual); + + EXPECT_TRUE(WriteXattrBool(path(), kKey, false)); + EXPECT_EQ(XattrStatus::kOK, ReadXattrBool(path(), kKey, &actual)); + EXPECT_FALSE(actual); +} + +TEST_F(Xattr, WriteAndReadInt) { + int expected = 42; + int actual; + + EXPECT_TRUE(WriteXattrInt(path(), kKey, expected)); + EXPECT_EQ(XattrStatus::kOK, ReadXattrInt(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); + + expected = std::numeric_limits<int>::max(); + EXPECT_TRUE(WriteXattrInt(path(), kKey, expected)); + EXPECT_EQ(XattrStatus::kOK, ReadXattrInt(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); +} + +TEST_F(Xattr, WriteAndReadTimeT) { + time_t expected = time(nullptr); + time_t actual; + + EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected)); + EXPECT_EQ(XattrStatus::kOK, ReadXattrTimeT(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); + + expected = std::numeric_limits<time_t>::max(); + EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected)); + EXPECT_EQ(XattrStatus::kOK, ReadXattrTimeT(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/child_port.defs b/third_party/crashpad/crashpad/util/mach/child_port.defs new file mode 100644 index 0000000..b227f00f --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port.defs
@@ -0,0 +1,62 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <mach/mach_types.defs> +#include <mach/std_types.defs> + +// child_port provides an interface for port rights to be transferred between +// tasks. Its expected usage is for processes to be able to pass port rights +// across IPC boundaries. A child process may wish to give its parent a copy of +// of a send right to its own task port, or a parent process may wish to give a +// receive right to a child process that implements a server. +// +// This Mach subsystem defines the lowest-level interface for these rights to +// be transferred. Most users will not user this interface directly, but will +// use ChildPortHandshake, which builds on this interface by providing client +// and server implementations, along with a protocol for establishing +// communication in a parent-child process relationship. +subsystem child_port 10011; + +serverprefix handle_; + +type child_port_server_t = mach_port_t; +type child_port_token_t = uint64_t; + +import "util/mach/child_port_types.h"; + +// Sends a Mach port right across an IPC boundary. +// +// server[in]: The server to send the port right to. +// token[in]: A random opaque token, generated by the server and communicated to +// the client through some secure means such as a shared pipe. The client +// includes the token in its request to prove its authenticity to the +// server. This parameter is necessary for instances where the server must +// publish its service broadly, such as via the bootstrap server. When this +// is done, anyone with access to the bootstrap server will be able to gain +// rights to communicate with |server|, and |token| serves as a shared +// secret allowing the server to verify that it has received a request from +// the intended client. |server| will reject requests with an invalid +// |token|. +// port[in]: A port right to transfer to the server. +// +// Return value: As this is a “simpleroutine”, the server does not respond to +// the client request, and the client does not block waiting for a response +// after sending its request. The return value is MACH_MSG_SUCCESS if the +// request was queued for the server, without any indication of whether the +// server considered the request valid or took any action. On data +// validation or mach_msg() failure, another code will be returned +// indicating the nature of the error. +simpleroutine child_port_check_in(server: child_port_server_t; + token: child_port_token_t; + port: mach_port_poly_t);
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc b/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc new file mode 100644 index 0000000..3fe4c44 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc
@@ -0,0 +1,450 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/child_port_handshake.h" + +#include <errno.h> +#include <pthread.h> +#include <sys/event.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <algorithm> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/posix/eintr_wrapper.h" +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" +#include "util/file/file_io.h" +#include "util/mach/child_port.h" +#include "util/mach/child_port_server.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/misc/implicit_cast.h" +#include "util/stdlib/move.h" +#include "util/misc/random_string.h" + +namespace crashpad { +namespace { + +class ChildPortHandshakeServer final : public ChildPortServer::Interface { + public: + ChildPortHandshakeServer(); + ~ChildPortHandshakeServer(); + + mach_port_t RunServer(base::ScopedFD server_write_fd, + ChildPortHandshake::PortRightType port_right_type); + + private: + // ChildPortServer::Interface: + kern_return_t HandleChildPortCheckIn(child_port_server_t server, + child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type, + const mach_msg_trailer_t* trailer, + bool* destroy_request) override; + + child_port_token_t token_; + mach_port_t port_; + mach_msg_type_name_t right_type_; + bool checked_in_; + + DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeServer); +}; + +ChildPortHandshakeServer::ChildPortHandshakeServer() + : token_(0), + port_(MACH_PORT_NULL), + right_type_(MACH_MSG_TYPE_PORT_NONE), + checked_in_(false) { +} + +ChildPortHandshakeServer::~ChildPortHandshakeServer() { +} + +mach_port_t ChildPortHandshakeServer::RunServer( + base::ScopedFD server_write_fd, + ChildPortHandshake::PortRightType port_right_type) { + DCHECK_EQ(port_, kMachPortNull); + DCHECK(!checked_in_); + DCHECK(server_write_fd.is_valid()); + + // Initialize the token and share it with the client via the pipe. + token_ = base::RandUint64(); + if (!LoggingWriteFile(server_write_fd.get(), &token_, sizeof(token_))) { + LOG(WARNING) << "no client check-in"; + return MACH_PORT_NULL; + } + + // Create a unique name for the bootstrap service mapping. Make it unguessable + // to prevent outsiders from grabbing the name first, which would cause + // bootstrap_check_in() to fail. + uint64_t thread_id; + errno = pthread_threadid_np(pthread_self(), &thread_id); + PCHECK(errno == 0) << "pthread_threadid_np"; + std::string service_name = base::StringPrintf( + "org.chromium.crashpad.child_port_handshake.%d.%llu.%s", + getpid(), + thread_id, + RandomString().c_str()); + + // Check the new service in with the bootstrap server, obtaining a receive + // right for it. + base::mac::ScopedMachReceiveRight server_port(BootstrapCheckIn(service_name)); + CHECK(server_port.is_valid()); + + // Share the service name with the client via the pipe. + uint32_t service_name_length = service_name.size(); + if (!LoggingWriteFile(server_write_fd.get(), + &service_name_length, + sizeof(service_name_length))) { + LOG(WARNING) << "no client check-in"; + return MACH_PORT_NULL; + } + + if (!LoggingWriteFile( + server_write_fd.get(), service_name.c_str(), service_name_length)) { + LOG(WARNING) << "no client check-in"; + return MACH_PORT_NULL; + } + + // A kqueue cannot monitor a raw Mach receive right with EVFILT_MACHPORT. It + // requires a port set. Create a new port set and add the receive right to it. + base::mac::ScopedMachPortSet server_port_set( + NewMachPort(MACH_PORT_RIGHT_PORT_SET)); + CHECK(server_port_set.is_valid()); + + kern_return_t kr = mach_port_insert_member( + mach_task_self(), server_port.get(), server_port_set.get()); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; + + // Set up a kqueue to monitor both the server’s receive right and the write + // side of the pipe. Messages from the client will be received via the receive + // right, and the pipe will show EOF if the client closes its read side + // prematurely. + base::ScopedFD kq(kqueue()); + PCHECK(kq != -1) << "kqueue"; + + struct kevent changelist[2]; + EV_SET(&changelist[0], + server_port_set.get(), + EVFILT_MACHPORT, + EV_ADD | EV_CLEAR, + 0, + 0, + nullptr); + EV_SET(&changelist[1], + server_write_fd.get(), + EVFILT_WRITE, + EV_ADD | EV_CLEAR, + 0, + 0, + nullptr); + int rv = HANDLE_EINTR( + kevent(kq.get(), changelist, arraysize(changelist), nullptr, 0, nullptr)); + PCHECK(rv != -1) << "kevent"; + + ChildPortServer child_port_server(this); + + bool blocking = true; + DCHECK(!checked_in_); + while (!checked_in_) { + DCHECK_EQ(port_, kMachPortNull); + + // Get a kevent from the kqueue. Block while waiting for an event unless the + // write pipe has arrived at EOF, in which case the kevent() should be + // nonblocking. Although the client sends its check-in message before + // closing the read side of the pipe, this organization allows the events to + // be delivered out of order and the check-in message will still be + // processed. + struct kevent event; + const timespec nonblocking_timeout = {}; + const timespec* timeout = blocking ? nullptr : &nonblocking_timeout; + rv = HANDLE_EINTR(kevent(kq.get(), nullptr, 0, &event, 1, timeout)); + PCHECK(rv != -1) << "kevent"; + + if (rv == 0) { + // Non-blocking kevent() with no events to return. + DCHECK(!blocking); + LOG(WARNING) << "no client check-in"; + return MACH_PORT_NULL; + } + + DCHECK_EQ(rv, 1); + + if (event.flags & EV_ERROR) { + // kevent() may have put its error here. + errno = event.data; + PLOG(FATAL) << "kevent"; + } + + switch (event.filter) { + case EVFILT_MACHPORT: { + // There’s something to receive on the port set. + DCHECK_EQ(event.ident, server_port_set.get()); + + // Run the message server in an inner loop instead of using + // MachMessageServer::kPersistent. This allows the loop to exit as soon + // as child_port_ is set, even if other messages are queued. This needs + // to drain all messages, because the use of edge triggering (EV_CLEAR) + // means that if more than one message is in the queue when kevent() + // returns, no more notifications will be generated. + while (!checked_in_) { + // If a proper message is received from child_port_check_in(), + // this will call HandleChildPortCheckIn(). + mach_msg_return_t mr = + MachMessageServer::Run(&child_port_server, + server_port_set.get(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeIgnore, + kMachMessageTimeoutNonblocking); + if (mr == MACH_RCV_TIMED_OUT) { + break; + } else if (mr != MACH_MSG_SUCCESS) { + MACH_LOG(ERROR, mr) << "MachMessageServer::Run"; + return MACH_PORT_NULL; + } + } + break; + } + + case EVFILT_WRITE: + // The write pipe is ready to be written to, or it’s at EOF. The former + // case is uninteresting, but a notification for this may be presented + // because the write pipe will be ready to be written to, at the latest, + // when the client reads its messages from the read side of the same + // pipe. Ignore that case. Multiple notifications for that situation + // will not be generated because edge triggering (EV_CLEAR) is used + // above. + DCHECK_EQ(implicit_cast<int>(event.ident), server_write_fd.get()); + if (event.flags & EV_EOF) { + // There are no readers attached to the write pipe. The client has + // closed its side of the pipe. There can be one last shot at + // receiving messages, in case the check-in message is delivered + // out of order, after the EOF notification. + blocking = false; + } + break; + + default: + NOTREACHED(); + break; + } + } + + if (port_ == MACH_PORT_NULL) { + return MACH_PORT_NULL; + } + + bool mismatch = false; + switch (port_right_type) { + case ChildPortHandshake::PortRightType::kReceiveRight: + if (right_type_ != MACH_MSG_TYPE_PORT_RECEIVE) { + LOG(ERROR) << "expected receive right, observed " << right_type_; + mismatch = true; + } + break; + case ChildPortHandshake::PortRightType::kSendRight: + if (right_type_ != MACH_MSG_TYPE_PORT_SEND && + right_type_ != MACH_MSG_TYPE_PORT_SEND_ONCE) { + LOG(ERROR) << "expected send or send-once right, observed " + << right_type_; + mismatch = true; + } + break; + } + + if (mismatch) { + MachMessageDestroyReceivedPort(port_, right_type_); + port_ = MACH_PORT_NULL; + return MACH_PORT_NULL; + } + + mach_port_t port = MACH_PORT_NULL; + std::swap(port_, port); + return port; +} + +kern_return_t ChildPortHandshakeServer::HandleChildPortCheckIn( + child_port_server_t server, + const child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type, + const mach_msg_trailer_t* trailer, + bool* destroy_request) { + DCHECK_EQ(port_, kMachPortNull); + DCHECK(!checked_in_); + + if (token != token_) { + // If the token’s not correct, someone’s attempting to spoof the legitimate + // client. + LOG(WARNING) << "ignoring incorrect token"; + *destroy_request = true; + } else { + checked_in_ = true; + + if (right_type != MACH_MSG_TYPE_PORT_RECEIVE && + right_type != MACH_MSG_TYPE_PORT_SEND && + right_type != MACH_MSG_TYPE_PORT_SEND_ONCE) { + // The message needs to carry a receive, send, or send-once right. + LOG(ERROR) << "invalid right type " << right_type; + *destroy_request = true; + } else { + // Communicate the child port and right type back to the RunServer(). + // *destroy_request is left at false, because RunServer() needs the right + // to remain intact. It gives ownership of the right to its caller. + port_ = port; + right_type_ = right_type; + } + } + + // This is a MIG simpleroutine, there is no reply message. + return MIG_NO_REPLY; +} + +} // namespace + +ChildPortHandshake::ChildPortHandshake() + : client_read_fd_(), + server_write_fd_() { + // Use socketpair() instead of pipe(). There is no way to suppress SIGPIPE on + // pipes in Mac OS X 10.6, because the F_SETNOSIGPIPE fcntl() command was not + // introduced until 10.7. + int pipe_fds[2]; + PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fds) == 0) + << "socketpair"; + + client_read_fd_.reset(pipe_fds[0]); + server_write_fd_.reset(pipe_fds[1]); + + // Simulate pipe() semantics by shutting down the “wrong” sides of the socket. + PCHECK(shutdown(server_write_fd_.get(), SHUT_RD) == 0) << "shutdown SHUT_RD"; + PCHECK(shutdown(client_read_fd_.get(), SHUT_WR) == 0) << "shutdown SHUT_WR"; + + // SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes + // to fail with EPIPE instead. + const int value = 1; + PCHECK(setsockopt(server_write_fd_.get(), + SOL_SOCKET, + SO_NOSIGPIPE, + &value, + sizeof(value)) == 0) << "setsockopt"; +} + +ChildPortHandshake::~ChildPortHandshake() { +} + +base::ScopedFD ChildPortHandshake::ClientReadFD() { + DCHECK(client_read_fd_.is_valid()); + return crashpad::move(client_read_fd_); +} + +base::ScopedFD ChildPortHandshake::ServerWriteFD() { + DCHECK(server_write_fd_.is_valid()); + return crashpad::move(server_write_fd_); +} + +mach_port_t ChildPortHandshake::RunServer(PortRightType port_right_type) { + client_read_fd_.reset(); + return RunServerForFD(crashpad::move(server_write_fd_), port_right_type); +} + +bool ChildPortHandshake::RunClient(mach_port_t port, + mach_msg_type_name_t right_type) { + server_write_fd_.reset(); + return RunClientForFD(crashpad::move(client_read_fd_), port, right_type); +} + +// static +mach_port_t ChildPortHandshake::RunServerForFD(base::ScopedFD server_write_fd, + PortRightType port_right_type) { + ChildPortHandshakeServer server; + return server.RunServer(crashpad::move(server_write_fd), port_right_type); +} + +// static +bool ChildPortHandshake::RunClientForFD(base::ScopedFD client_read_fd, + mach_port_t port, + mach_msg_type_name_t right_type) { + DCHECK(client_read_fd.is_valid()); + + // Read the token and the service name from the read side of the pipe. + child_port_token_t token; + std::string service_name; + if (!RunClientInternal_ReadPipe( + client_read_fd.get(), &token, &service_name)) { + return false; + } + + // Look up the server and check in with it by providing the token and port. + return RunClientInternal_SendCheckIn(service_name, token, port, right_type); +} + +// static +bool ChildPortHandshake::RunClientInternal_ReadPipe(int client_read_fd, + child_port_token_t* token, + std::string* service_name) { + // Read the token from the pipe. + if (!LoggingReadFile(client_read_fd, token, sizeof(*token))) { + return false; + } + + // Read the service name from the pipe. + uint32_t service_name_length; + if (!LoggingReadFile( + client_read_fd, &service_name_length, sizeof(service_name_length))) { + return false; + } + + service_name->resize(service_name_length); + if (!service_name->empty() && + !LoggingReadFile( + client_read_fd, &(*service_name)[0], service_name_length)) { + return false; + } + + return true; +} + +// static +bool ChildPortHandshake::RunClientInternal_SendCheckIn( + const std::string& service_name, + child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type) { + // Get a send right to the server by looking up the service with the bootstrap + // server by name. + base::mac::ScopedMachSendRight server_port(BootstrapLookUp(service_name)); + if (server_port == kMachPortNull) { + return false; + } + + // Check in with the server. + kern_return_t kr = child_port_check_in( + server_port.get(), token, port, right_type); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "child_port_check_in"; + return false; + } + + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_handshake.h b/third_party/crashpad/crashpad/util/mach/child_port_handshake.h new file mode 100644 index 0000000..f738cda --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_handshake.h
@@ -0,0 +1,327 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_ +#define CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_ + +#include <mach/mach.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/files/scoped_file.h" +#include "util/mach/child_port_types.h" + +namespace crashpad { + +namespace test { +namespace { +class ChildPortHandshakeTest; +} // namespace +} // namespace test + +//! \brief Implements a handshake protocol that allows processes to exchange +//! port rights. +//! +//! Ordinarily, there is no way for parent and child processes to exchange port +//! rights, outside of the rights that children inherit from their parents. +//! These include task-special ports and exception ports, but all of these have +//! system-defined uses, and cannot reliably be replaced: in a multi-threaded +//! parent, it is impossible to temporarily change an inheritable port while +//! maintaining a guarantee that another thread will not attempt to use it, and +//! in children, it difficult to guarantee that nothing will attempt to use an +//! inheritable port before it can be replaced with the correct one. This latter +//! concern is becoming increasingly more pronounced as system libraries perform +//! more operations that rely on an inherited port in module initializers. +//! +//! The protocol implemented by this class involves a server that runs in one +//! process. The server is published with the bootstrap server, which the other +//! process has access to because the bootstrap port is one of the inherited +//! task-special ports. The two processes also share a pipe, which the server +//! can write to and the client can read from. The server will write a random +//! token to this pipe, along with the name under which its service has been +//! registered with the bootstrap server. The client can then obtain a send +//! right to this service with `bootstrap_look_up()`, and send a check-in +//! message containing the token value and the port right of its choice by +//! calling `child_port_check_in()`. +//! +//! The inclusion of the token authenticates the client to the server. This is +//! necessary because the service is published with the bootstrap server, which +//! opens up access to it to more than the intended client. Because the token is +//! passed to the client by a shared pipe, it constitutes a shared secret not +//! known by other processes that may have incidental access to the server. The +//! ChildPortHandshake server considers its randomly-generated token valid until +//! a client checks in with it. This mechanism is used instead of examining the +//! request message’s audit trailer to verify the sender’s process ID because in +//! some process architectures, it may be impossible to verify the client’s +//! process ID. +//! +//! The shared pipe serves another purpose: the server monitors it for an +//! end-of-file (no readers) condition. Once detected, it will stop its blocking +//! wait for a client to check in. This mechanism was also chosen for its +//! ability to function properly in diverse process architectures. +//! +//! This class can be used to allow a child process to provide its parent with a +//! send right to its task port, in cases where it is desirable for the parent +//! to have such access. It can also be used to allow a parent process to +//! transfer a receive right to a child process that implements the server for +//! that right, or for a child process to establish its own server and provide +//! its parent with a send right to that server, for cases where a service is +//! provided and it is undesirable or impossible to provide it via the bootstrap +//! or launchd interfaces. +//! +//! Example parent process, running a client that sends a receive right to its +//! child: +//! \code +//! ChildPortHandshake child_port_handshake; +//! base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); +//! std::string server_write_fd_string = +//! base::StringPrintf("%d", server_write_fd.get()); +//! +//! pid_t pid = fork(); +//! if (pid == 0) { +//! // Child +//! +//! // Close all file descriptors above STDERR_FILENO except for +//! // server_write_fd. Let the child know what file descriptor to use for +//! // server_write_fd by passing it as argv[1]. Example code for the child +//! // process is below. +//! CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get()); +//! execlp("./child", "child", server_write_fd_string.c_str(), nullptr); +//! } +//! +//! // Parent +//! +//! // Close the child’s end of the pipe. +//! server_write_fd.reset(); +//! +//! // Make a new Mach receive right. +//! base::mac::ScopedMachReceiveRight +//! receive_right(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); +//! +//! // Make a send right corresponding to the receive right. +//! mach_port_t send_right; +//! mach_msg_type_name_t send_right_type; +//! mach_port_extract_right(mach_task_self(), +//! receive_right.get(), +//! MACH_MSG_TYPE_MAKE_SEND, +//! &send_right, +//! &send_right_type); +//! base::mac::ScopedMachSendRight send_right_owner(send_right); +//! +//! // Send the receive right to the child process, retaining the send right +//! // for use in the parent process. +//! if (child_port_handshake.RunClient(receive_right.get(), +//! MACH_MSG_TYPE_MOVE_RECEIVE)) { +//! ignore_result(receive_right.release()); +//! } +//! \endcode +//! +//! Example child process, running a server that receives a receive right from +//! its parent: +//! \code +//! int main(int argc, char* argv[]) { +//! // The parent passed server_write_fd in argv[1]. +//! base::ScopedFD server_write_fd(atoi(argv[1])); +//! +//! // Obtain a receive right from the parent process. +//! base::mac::ScopedMachReceiveRight receive_right( +//! ChildPortHandshake::RunServerForFD( +//! crashpad::move(server_write_fd), +//! ChildPortHandshake::PortRightType::kReceiveRight)); +//! } +//! \endcode +class ChildPortHandshake { + public: + //! \brief Controls whether a receive or send right is expected to be + //! obtained from the client by the server’s call to RunServer(). + enum class PortRightType { + //! \brief The server expects to receive a receive right. + kReceiveRight = 0, + + //! \brief The server expects to receive a send or send-once right. + kSendRight, + }; + + ChildPortHandshake(); + ~ChildPortHandshake(); + + //! \brief Obtains the “read” side of the pipe, to be used by the client. + //! + //! This file descriptor must be passed to RunClientForFD(). + //! + //! \return The file descriptor that the client should read from. + base::ScopedFD ClientReadFD(); + + //! \brief Obtains the “write” side of the pipe, to be used by the server. + //! + //! This file descriptor must be passed to RunServerForFD(). + //! + //! \return The file descriptor that the server should write to. + base::ScopedFD ServerWriteFD(); + + //! \brief Runs the server. + //! + //! This method closes the “read” side of the pipe in-process, so that the + //! client process holds the only file descriptor that can read from the pipe. + //! It then calls RunServerForFD() using the “write” side of the pipe. If + //! ClientReadFD() has already been called in the server process, the caller + //! must ensure that the file descriptor returned by ClientReadFD() is closed + //! prior to calling this method. + mach_port_t RunServer(PortRightType port_right_type); + + //! \brief Runs the client. + //! + //! This method closes the “write” side of the pipe in-process, so that the + //! server process holds the only file descriptor that can write to the pipe. + //! It then calls RunClientForFD() using the “read” side of the pipe. If + //! ServerWriteFD() has already been called in the client process, the caller + //! must ensure that the file descriptor returned by ServerWriteFD() is closed + //! prior to calling this method. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool RunClient(mach_port_t port, mach_msg_type_name_t right_type); + + //! \brief Runs the server. + //! + //! If a ChildPortHandshake object is available, don’t call this static + //! function. Instead, call RunServer(), which wraps this function. When using + //! this function, the caller is responsible for ensuring that the client + //! “read” side of the pipe is closed in the server process prior to calling + //! this function. + //! + //! This function performs these tasks: + //! - Creates a random token and sends it via the pipe. + //! - Checks its service in with the bootstrap server, and sends the name + //! of its bootstrap service mapping via the pipe. + //! - Simultaneously receives messages on its Mach server and monitors the + //! pipe for end-of-file. This is a blocking operation. + //! - When a Mach message is received, calls HandleChildPortCheckIn() to + //! interpret and validate it, and if the message is valid, returns the + //! port right extracted from the message. If the message is not valid, + //! this method will continue waiting for a valid message. Valid messages + //! are properly formatted and have the correct token. The right carried in + //! a valid message will be returned. If a message is not valid, this + //! method will continue waiting for pipe EOF or a valid message. + //! - When notified of pipe EOF, returns `MACH_PORT_NULL`. + //! - Regardless of return value, destroys the server’s receive right and + //! closes the pipe. + //! + //! \param[in] port_right_type The port right type expected to be received + //! from the client. If the port right received from the client does not + //! match the expected type, the received port right will be destroyed, + //! and `MACH_PORT_NULL` will be returned. + //! + //! \return On success, the port right provided by the client. The caller + //! takes ownership of this right. On failure, `MACH_PORT_NULL`, + //! indicating that the client did not check in properly before + //! terminating, where termination is detected by detecting that the read + //! side of the shared pipe has closed. On failure, a message indicating + //! the nature of the failure will be logged. + static mach_port_t RunServerForFD(base::ScopedFD server_write_fd, + PortRightType port_right_type); + + //! \brief Runs the client. + //! + //! If a ChildPortHandshake object is available, don’t call this static + //! function. Instead, call RunClient(), which wraps this function. When using + //! this function, the caller is responsible for ensuring that the server + //! “write” side of the pipe is closed in the client process prior to calling + //! this function. + //! + //! This function performs these tasks: + //! - Reads the token from the pipe. + //! - Reads the bootstrap service name from the pipe. + //! - Obtains a send right to the server by calling `bootstrap_look_up()`. + //! - Sends a check-in message to the server by calling + //! `child_port_check_in()`, providing the token and the user-supplied port + //! right. + //! - Deallocates the send right to the server, and closes the pipe. + //! + //! There is no return value because `child_port_check_in()` is a MIG + //! `simpleroutine`, and the server does not send a reply. This allows + //! check-in to occur without blocking to wait for a reply. + //! + //! \param[in] pipe_read The “read” side of the pipe shared with the server + //! process. This function takes ownership of this file descriptor, and + //! will close it prior to returning. + //! \param[in] port The port right that will be passed to the server by + //! `child_port_check_in()`. + //! \param[in] right_type The right type to furnish the server with. If \a + //! port is a send right, this can be `MACH_MSG_TYPE_COPY_SEND` or + //! `MACH_MSG_TYPE_MOVE_SEND`. If \a port is a send-once right, this can + //! be `MACH_MSG_TYPE_MOVE_SEND_ONCE`. If \a port is a receive right, this + //! can be `MACH_MSG_TYPE_MAKE_SEND`, `MACH_MSG_TYPE_MAKE_SEND_ONCE`, or + //! `MACH_MSG_TYPE_MOVE_RECEIVE`. + //! + //! \return `true` on success, `false` on failure with a message logged. On + //! failure, the port right corresponding to a \a right_type of + //! `MACH_MSG_TYPE_MOVE_*` is not consumed, and the caller must dispose of + //! the right if necessary. + static bool RunClientForFD(base::ScopedFD client_read_fd, + mach_port_t port, + mach_msg_type_name_t right_type); + + private: + //! \brief Runs the read-from-pipe portion of the client’s side of the + //! handshake. This is an implementation detail of RunClient and is only + //! exposed for testing purposes. + //! + //! When using this function and RunClientInternal_SendCheckIn(), the caller + //! is responsible for closing \a pipe_read at an appropriate time, normally + //! after calling RunClientInternal_SendCheckIn(). + //! + //! \param[in] pipe_read The “read” side of the pipe shared with the server + //! process. + //! \param[out] token The token value read from \a pipe_read. + //! \param[out] service_name The service name as registered with the bootstrap + //! server, read from \a pipe_read. + //! + //! \return `true` on success, `false` on failure with a message logged. + static bool RunClientInternal_ReadPipe(int pipe_read, + child_port_token_t* token, + std::string* service_name); + + //! \brief Runs the check-in portion of the client’s side of the handshake. + //! This is an implementation detail of RunClient and is only exposed for + //! testing purposes. + //! + //! When using this RunClientInternal_ReadPipe() and this function, the caller + //! is responsible for closing the “read” side of the pipe at an appropriate + //! time, normally after calling this function. + //! + //! \param[in] service_name The service name as registered with the bootstrap + //! server, to be looked up with `bootstrap_look_up()`. + //! \param[in] token The token value to provide during check-in. + //! \param[in] port The port that will be passed to the server by + //! `child_port_check_in()`. + //! \param[in] right_type The right type to furnish the server with. + //! + //! \return `true` on success, `false` on failure with a message logged. + static bool RunClientInternal_SendCheckIn(const std::string& service_name, + child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type); + + base::ScopedFD client_read_fd_; + base::ScopedFD server_write_fd_; + + friend class test::ChildPortHandshakeTest; + + DISALLOW_COPY_AND_ASSIGN(ChildPortHandshake); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc b/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc new file mode 100644 index 0000000..f77107b5 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc
@@ -0,0 +1,382 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/child_port_handshake.h" + +#include "base/mac/scoped_mach_port.h" +#include "gtest/gtest.h" +#include "test/multiprocess.h" +#include "util/mach/child_port_types.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace test { +namespace { + +class ChildPortHandshakeTest : public Multiprocess { + public: + enum class ClientProcess { + // The child runs the client and the parent runs the server. + kChildClient = 0, + + // The parent runs the client and the child runs the server. + kParentClient, + }; + + enum class TestType { + // The client checks in with the server, transferring a receive right. + kClientChecksIn_ReceiveRight = 0, + + // In this test, the client checks in with the server normally. It sends a + // copy of its bootstrap port to the server, because both parent and child + // should have the same bootstrap port, allowing for verification. + kClientChecksIn_SendRight, + + // The client checks in with the server, transferring a send-once right. + kClientChecksIn_SendOnceRight, + + // In this test, the client reads from its pipe, and subsequently exits + // without checking in. This tests that the server properly detects that it + // has lost its client after sending instructions to it via the pipe, while + // waiting for a check-in message. + kClientDoesNotCheckIn, + + // In this test, the client exits without checking in. This tests that the + // server properly detects that it has lost a client. Whether or not the + // client closes the pipe before the server writes to it is a race, and the + // server needs to be able to detect client loss in both cases, so the + // ClientDoesNotCheckIn_ReadsPipe and NoClient tests also exist to test + // these individual cases more deterministically. + kClientDoesNotCheckIn_ReadsPipe, + + // In this test, the client checks in with the server with an incorrect + // token value and a copy of its own task port. The server should reject the + // message because of the invalid token, and return MACH_PORT_NULL to its + // caller. + kTokenIncorrect, + + // In this test, the client checks in with the server with an incorrect + // token value and a copy of its own task port, and subsequently, the + // correct token value and a copy of its bootstrap port. The server should + // reject the first because of the invalid token, but it should continue + // waiting for a message with a valid token as long as the pipe remains + // open. It should wind wind up returning the bootstrap port, allowing for + // verification. + kTokenIncorrectThenCorrect, + + // The server dies. The failure should be reported in the client. This test + // type is only compatible with ClientProcess::kParentClient. + kServerDies, + }; + + ChildPortHandshakeTest(ClientProcess client_process, TestType test_type) + : Multiprocess(), + child_port_handshake_(), + client_process_(client_process), + test_type_(test_type) { + } + + ~ChildPortHandshakeTest() { + } + + private: + void RunServer() { + if (test_type_ == TestType::kServerDies) { + return; + } + + base::mac::ScopedMachReceiveRight receive_right; + base::mac::ScopedMachSendRight send_right; + if (test_type_ == TestType::kClientChecksIn_ReceiveRight) { + receive_right.reset(child_port_handshake_.RunServer( + ChildPortHandshake::PortRightType::kReceiveRight)); + } else { + send_right.reset(child_port_handshake_.RunServer( + ChildPortHandshake::PortRightType::kSendRight)); + } + + switch (test_type_) { + case TestType::kClientChecksIn_ReceiveRight: + EXPECT_TRUE(receive_right.is_valid()); + break; + + case TestType::kClientChecksIn_SendRight: + case TestType::kTokenIncorrectThenCorrect: + EXPECT_EQ(bootstrap_port, send_right); + break; + + case TestType::kClientChecksIn_SendOnceRight: + EXPECT_TRUE(send_right.is_valid()); + EXPECT_NE(bootstrap_port, send_right); + break; + + case TestType::kClientDoesNotCheckIn: + case TestType::kClientDoesNotCheckIn_ReadsPipe: + case TestType::kTokenIncorrect: + EXPECT_FALSE(send_right.is_valid()); + break; + + case TestType::kServerDies: + // This was special-cased as an early return above. + FAIL(); + break; + } + } + + void RunClient() { + switch (test_type_) { + case TestType::kClientChecksIn_SendRight: { + ASSERT_TRUE(child_port_handshake_.RunClient(bootstrap_port, + MACH_MSG_TYPE_COPY_SEND)); + break; + } + + case TestType::kClientChecksIn_ReceiveRight: { + mach_port_t receive_right = NewMachPort(MACH_PORT_RIGHT_RECEIVE); + ASSERT_TRUE(child_port_handshake_.RunClient( + receive_right, MACH_MSG_TYPE_MOVE_RECEIVE)); + break; + } + + case TestType::kClientChecksIn_SendOnceRight: { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(child_port_handshake_.RunClient( + receive_right.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE)); + break; + } + + case TestType::kClientDoesNotCheckIn: { + child_port_handshake_.ServerWriteFD().reset(); + child_port_handshake_.ClientReadFD().reset(); + break; + } + + case TestType::kClientDoesNotCheckIn_ReadsPipe: { + // Don’t run the standard client routine. Instead, drain the pipe, which + // will get the parent to the point that it begins waiting for a + // check-in message. Then, exit. The pipe is drained using the same + // implementation that the real client would use. + child_port_handshake_.ServerWriteFD().reset(); + base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD(); + child_port_token_t token; + std::string service_name; + ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( + client_read_fd.get(), &token, &service_name)); + break; + } + + case TestType::kTokenIncorrect: { + // Don’t run the standard client routine. Instead, read the token and + // service name, mutate the token, and then check in with the bad token. + // The parent should reject the message. + child_port_handshake_.ServerWriteFD().reset(); + base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD(); + child_port_token_t token; + std::string service_name; + ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( + client_read_fd.get(), &token, &service_name)); + child_port_token_t bad_token = ~token; + ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( + service_name, + bad_token, + mach_task_self(), + MACH_MSG_TYPE_COPY_SEND)); + break; + } + + case TestType::kTokenIncorrectThenCorrect: { + // Don’t run the standard client routine. Instead, read the token and + // service name. Mutate the token, and check in with the bad token, + // expecting the parent to reject the message. Then, check in with the + // correct token, expecting the parent to accept it. + child_port_handshake_.ServerWriteFD().reset(); + base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD(); + child_port_token_t token; + std::string service_name; + ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( + client_read_fd.release(), &token, &service_name)); + child_port_token_t bad_token = ~token; + ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( + service_name, + bad_token, + mach_task_self(), + MACH_MSG_TYPE_COPY_SEND)); + ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( + service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND)); + break; + } + + case TestType::kServerDies: { + ASSERT_EQ(ClientProcess::kParentClient, client_process_); + ASSERT_FALSE(child_port_handshake_.RunClient(bootstrap_port, + MACH_MSG_TYPE_COPY_SEND)); + break; + } + } + } + + // Multiprocess: + + void MultiprocessParent() override { + switch (client_process_) { + case ClientProcess::kChildClient: + RunServer(); + break; + case ClientProcess::kParentClient: + RunClient(); + break; + } + } + + void MultiprocessChild() override { + switch (client_process_) { + case ClientProcess::kChildClient: + RunClient(); + break; + case ClientProcess::kParentClient: + RunServer(); + break; + } + } + + private: + ChildPortHandshake child_port_handshake_; + ClientProcess client_process_; + TestType test_type_; + + DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeTest); +}; + +TEST(ChildPortHandshake, ChildClientChecksIn_ReceiveRight) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight); + test.Run(); +} + +TEST(ChildPortHandshake, ChildClientChecksIn_SendRight) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight); + test.Run(); +} + +TEST(ChildPortHandshake, ChildClientChecksIn_SendOnceRight) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight); + test.Run(); +} + +TEST(ChildPortHandshake, ChildClientDoesNotCheckIn) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn); + test.Run(); +} + +TEST(ChildPortHandshake, ChildClientDoesNotCheckIn_ReadsPipe) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe); + test.Run(); +} + +TEST(ChildPortHandshake, ChildClientTokenIncorrect) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kTokenIncorrect); + test.Run(); +} + +TEST(ChildPortHandshake, ChildClientTokenIncorrectThenCorrect) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kChildClient, + ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientChecksIn_ReceiveRight) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientChecksIn_SendRight) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientChecksIn_SendOnceRight) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientDoesNotCheckIn) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientDoesNotCheckIn_ReadsPipe) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientTokenIncorrect) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kTokenIncorrect); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientTokenIncorrectThenCorrect) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect); + test.Run(); +} + +TEST(ChildPortHandshake, ParentClientServerDies) { + ChildPortHandshakeTest test( + ChildPortHandshakeTest::ClientProcess::kParentClient, + ChildPortHandshakeTest::TestType::kServerDies); + test.Run(); +} + +TEST(ChildPortHandshake, NoClient) { + // In this test, the client never checks in with the server because it never + // even runs. This tests that the server properly detects that it has no + // client at all, and does not terminate execution with an error such as + // “broken pipe” when attempting to send instructions to the client. This test + // is similar to kClientDoesNotCheckIn, but because there’s no client at all, + // the server is guaranteed to see that its pipe partner is gone. + ChildPortHandshake child_port_handshake; + base::mac::ScopedMachSendRight child_port(child_port_handshake.RunServer( + ChildPortHandshake::PortRightType::kSendRight)); + EXPECT_FALSE(child_port.is_valid()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_server.cc b/third_party/crashpad/crashpad/util/mach/child_port_server.cc new file mode 100644 index 0000000..4fc723a --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_server.cc
@@ -0,0 +1,122 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/child_port_server.h" + +#include "base/logging.h" +#include "util/mach/child_portServer.h" +#include "util/mach/mach_message.h" + +extern "C" { + +// This function is not used, and is in fact obsoleted by the other +// functionality implemented in this file. The standard MIG-generated +// child_port_server() (in child_portServer.c) server dispatch routine usable +// with the standard mach_msg_server() function calls out to this function. +// child_port_server() is unused and is replaced by the more flexible +// ChildPortServer, but the linker still needs to see this function definition. + +kern_return_t handle_child_port_check_in(child_port_server_t server, + child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type) { + NOTREACHED(); + return KERN_FAILURE; +} + +} // extern "C" + +namespace { + +// There is no predefined constant for this. +enum MachMessageID : mach_msg_id_t { + kMachMessageIDChildPortCheckIn = 10011, +}; + +// The MIG-generated __MIG_check__Request__*() functions are not declared as +// accepting const data, but they could have been because they in fact do not +// modify the data. This wrapper function is provided to bridge the const gap +// between the code in this file, which is const-correct and treats request +// message data as const, and the generated function. + +kern_return_t MIGCheckRequestChildPortCheckIn( + const __Request__child_port_check_in_t* in_request) { + using Request = __Request__child_port_check_in_t; + return __MIG_check__Request__child_port_check_in_t( + const_cast<Request*>(in_request)); +} + +} // namespace + +namespace crashpad { + +ChildPortServer::ChildPortServer(ChildPortServer::Interface* interface) + : MachMessageServer::Interface(), + interface_(interface) { +} + +bool ChildPortServer::MachMessageServerFunction( + const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) { + PrepareMIGReplyFromRequest(in_header, out_header); + + const mach_msg_trailer_t* in_trailer = + MachMessageTrailerFromHeader(in_header); + + switch (in_header->msgh_id) { + case kMachMessageIDChildPortCheckIn: { + // child_port_check_in(), handle_child_port_check_in(). + using Request = __Request__child_port_check_in_t; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = MIGCheckRequestChildPortCheckIn(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = __Reply__child_port_check_in_t; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->HandleChildPortCheckIn(in_header->msgh_local_port, + in_request->token, + in_request->port.name, + in_request->port.disposition, + in_trailer, + destroy_complex_request); + return true; + } + + default: { + SetMIGReplyError(out_header, MIG_BAD_ID); + return false; + } + } +} + +std::set<mach_msg_id_t> ChildPortServer::MachMessageServerRequestIDs() { + const mach_msg_id_t request_ids[] = {kMachMessageIDChildPortCheckIn}; + return std::set<mach_msg_id_t>(&request_ids[0], + &request_ids[arraysize(request_ids)]); +} + +mach_msg_size_t ChildPortServer::MachMessageServerRequestSize() { + return sizeof(__RequestUnion__handle_child_port_subsystem); +} + +mach_msg_size_t ChildPortServer::MachMessageServerReplySize() { + return sizeof(__ReplyUnion__handle_child_port_subsystem); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_server.h b/third_party/crashpad/crashpad/util/mach/child_port_server.h new file mode 100644 index 0000000..bc42cc0 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_server.h
@@ -0,0 +1,77 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_ +#define CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_ + +#include <mach/mach.h> + +#include <set> + +#include "base/basictypes.h" +#include "util/mach/child_port_types.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { + +//! \brief A server interface for the `child_port` Mach subsystem. +class ChildPortServer : public MachMessageServer::Interface { + public: + //! \brief An interface that the request message that is a part of the + //! `child_port` Mach subsystem can be dispatched to. + class Interface { + public: + //! \brief Handles check-ins sent by `child_port_check_in()`. + //! + //! This behaves equivalently to a `handle_child_port_check_in()` function + //! used with `child_port_server()`. + //! + //! \param[in] trailer The trailer received with the request message. + //! \param[out] destroy_request `true` if the request message is to be + //! destroyed even when this method returns success. See + //! MachMessageServer::Interface. + virtual kern_return_t HandleChildPortCheckIn( + child_port_server_t server, + const child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type, + const mach_msg_trailer_t* trailer, + bool* destroy_request) = 0; + + protected: + ~Interface() {} + }; + + //! \brief Constructs an object of this class. + //! + //! \param[in] interface The interface to dispatch requests to. Weak. + explicit ChildPortServer(Interface* interface); + + // MachMessageServer::Interface: + bool MachMessageServerFunction(const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) override; + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override; + mach_msg_size_t MachMessageServerRequestSize() override; + mach_msg_size_t MachMessageServerReplySize() override; + + private: + Interface* interface_; // weak + + DISALLOW_COPY_AND_ASSIGN(ChildPortServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc b/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc new file mode 100644 index 0000000..7a8dbf77 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc
@@ -0,0 +1,137 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/child_port_server.h" + +#include <string.h> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +using testing::Eq; +using testing::Pointee; +using testing::Return; + +// Fake Mach ports. These aren’t used as ports in these tests, they’re just used +// as cookies to make sure that the correct values get passed to the correct +// places. +const mach_port_t kServerLocalPort = 0x05050505; +const mach_port_t kCheckInPort = 0x06060606; + +// Other fake values. +const mach_msg_type_name_t kCheckInPortRightType = MACH_MSG_TYPE_PORT_SEND; +const child_port_token_t kCheckInToken = 0xfedcba9876543210; + +// The definition of the request structure from child_port.h isn’t available +// here. It needs custom initialization code, so duplicate the expected +// definition of the structure from child_port.h here in this file, and provide +// the initialization code as a method in true object-oriented fashion. + +struct __attribute__((packed, aligned(4))) ChildPortCheckInRequest { + ChildPortCheckInRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND) | MACH_MSGH_BITS_COMPLEX; + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = MACH_PORT_NULL; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 10011; + msgh_body.msgh_descriptor_count = 1; + port.name = kCheckInPort; + port.disposition = kCheckInPortRightType; + port.type = MACH_MSG_PORT_DESCRIPTOR; + NDR = NDR_record; + token = kCheckInToken; + } + + mach_msg_header_t Head; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t port; + NDR_record_t NDR; + child_port_token_t token; + mach_msg_trailer_t trailer; +}; + +struct MIGReply : public mig_reply_error_t { + MIGReply() { + memset(this, 0x5a, sizeof(*this)); + RetCode = KERN_FAILURE; + } + + void Verify() { + EXPECT_EQ(implicit_cast<mach_msg_bits_t>(MACH_MSGH_BITS(0, 0)), + Head.msgh_bits); + EXPECT_EQ(sizeof(*this), Head.msgh_size); + EXPECT_EQ(kMachPortNull, Head.msgh_remote_port); + EXPECT_EQ(kMachPortNull, Head.msgh_local_port); + EXPECT_EQ(10111, Head.msgh_id); + EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); + EXPECT_EQ(MIG_NO_REPLY, RetCode); + } +}; + +class MockChildPortServerInterface : public ChildPortServer::Interface { + public: + MOCK_METHOD6(HandleChildPortCheckIn, + kern_return_t(child_port_server_t server, + const child_port_token_t token, + mach_port_t port, + mach_msg_type_name_t right_type, + const mach_msg_trailer_t* trailer, + bool* destroy_request)); +}; + +TEST(ChildPortServer, MockChildPortCheckIn) { + MockChildPortServerInterface server_interface; + ChildPortServer server(&server_interface); + + std::set<mach_msg_id_t> expect_request_ids; + expect_request_ids.insert(10011); // There is no constant for this. + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + + ChildPortCheckInRequest request; + EXPECT_EQ(request.Head.msgh_size, server.MachMessageServerRequestSize()); + + MIGReply reply; + EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize()); + + EXPECT_CALL(server_interface, + HandleChildPortCheckIn(kServerLocalPort, + kCheckInToken, + kCheckInPort, + kCheckInPortRightType, + Eq(&request.trailer), + Pointee(Eq(false)))) + .WillOnce(Return(MIG_NO_REPLY)) + .RetiresOnSaturation(); + + bool destroy_request = false; + EXPECT_TRUE(server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_request)); + EXPECT_FALSE(destroy_request); + + reply.Verify(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_types.h b/third_party/crashpad/crashpad/util/mach/child_port_types.h new file mode 100644 index 0000000..d0760a7 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/child_port_types.h
@@ -0,0 +1,26 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_ +#define CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_ + +#include <mach/mach.h> +#include <stdint.h> + +// This file is #included by C (non-C++) files, and must remain strictly C. + +typedef mach_port_t child_port_server_t; +typedef uint64_t child_port_token_t; + +#endif // CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_
diff --git a/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc new file mode 100644 index 0000000..abbe5bc --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc
@@ -0,0 +1,86 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/composite_mach_message_server.h" + +#include <algorithm> +#include <utility> + +#include "base/logging.h" +#include "util/mach/mach_message.h" + +namespace crashpad { + +CompositeMachMessageServer::CompositeMachMessageServer() + : MachMessageServer::Interface(), + handler_map_(), + request_size_(sizeof(mach_msg_header_t)), + reply_size_(sizeof(mig_reply_error_t)) { +} + +CompositeMachMessageServer::~CompositeMachMessageServer() { +} + +void CompositeMachMessageServer::AddHandler( + MachMessageServer::Interface* handler) { + // Other cycles would be invalid as well, but they aren’t currently checked. + DCHECK_NE(handler, this); + + std::set<mach_msg_id_t> request_ids = handler->MachMessageServerRequestIDs(); + for (mach_msg_id_t request_id : request_ids) { + std::pair<HandlerMap::const_iterator, bool> result = + handler_map_.insert(std::make_pair(request_id, handler)); + CHECK(result.second) << "duplicate request ID " << request_id; + } + + request_size_ = + std::max(request_size_, handler->MachMessageServerRequestSize()); + reply_size_ = std::max(reply_size_, handler->MachMessageServerReplySize()); +} + +bool CompositeMachMessageServer::MachMessageServerFunction( + const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) { + HandlerMap::const_iterator iterator = handler_map_.find(in->msgh_id); + if (iterator == handler_map_.end()) { + // Do what MIG-generated server routines do when they can’t dispatch a + // message. + PrepareMIGReplyFromRequest(in, out); + SetMIGReplyError(out, MIG_BAD_ID); + return false; + } + + MachMessageServer::Interface* handler = iterator->second; + return handler->MachMessageServerFunction(in, out, destroy_complex_request); +} + +std::set<mach_msg_id_t> +CompositeMachMessageServer::MachMessageServerRequestIDs() { + std::set<mach_msg_id_t> request_ids; + for (const auto& entry : handler_map_) { + request_ids.insert(entry.first); + } + return request_ids; +} + +mach_msg_size_t CompositeMachMessageServer::MachMessageServerRequestSize() { + return request_size_; +} + +mach_msg_size_t CompositeMachMessageServer::MachMessageServerReplySize() { + return reply_size_; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h new file mode 100644 index 0000000..6da957b --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h
@@ -0,0 +1,103 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_ +#define CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_ + +#include <mach/mach.h> + +#include <map> +#include <set> + +#include "base/basictypes.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { + +//! \brief Adapts multiple MachMessageServer::Interface implementations for +//! simultaneous use in a single MachMessageServer::Run() call. +//! +//! This class implements a MachMessageServer::Interface that contains other +//! other MachMessageServer::Interface objects. +//! +//! In some situations, it may be desirable for a Mach message server to handle +//! messages from distinct MIG subsystems with distinct +//! MachMessageServer::Interface implementations. This may happen if a single +//! receive right is shared for multiple subsystems, or if distinct receive +//! rights are combined in a Mach port set. In these cases, this class performs +//! a first-level demultiplexing to forward request messages to the proper +//! subsystem-level demultiplexers. +class CompositeMachMessageServer : public MachMessageServer::Interface { + public: + CompositeMachMessageServer(); + ~CompositeMachMessageServer(); + + //! \brief Adds a handler that messages can be dispatched to based on request + //! message ID. + //! + //! \param[in] handler A MachMessageServer handler. Ownership of this object + //! is not taken. Cycles must not be created between objects. It is + //! invalid to add an object as its own handler. + //! + //! If \a handler claims to support any request ID that this object is already + //! able to handle, execution will be terminated. + void AddHandler(MachMessageServer::Interface* handler); + + // MachMessageServer::Interface: + + //! \copydoc MachMessageServer::Interface::MachMessageServerFunction() + //! + //! This implementation forwards the message to an appropriate handler added + //! by AddHandler() on the basis of the \a in request message’s message ID. + //! If no appropriate handler exists, the \a out reply message is treated as + //! a `mig_reply_error_t`, its return code is set to `MIG_BAD_ID`, and `false` + //! is returned. + bool MachMessageServerFunction(const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) override; + + //! \copydoc MachMessageServer::Interface::MachMessageServerRequestIDs() + //! + //! This implementation returns the set of all request message Mach message + //! IDs of all handlers added by AddHandler(). + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override; + + //! \copydoc MachMessageServer::Interface::MachMessageServerRequestSize() + //! + //! This implementation returns the maximum request message size of all + //! handlers added by AddHandler(). If no handlers are present, returns the + //! size of `mach_msg_header_t`, the minimum size of a MIG request message + //! that can be received for demultiplexing purposes. + mach_msg_size_t MachMessageServerRequestSize() override; + + //! \copydoc MachMessageServer::Interface::MachMessageServerReplySize() + //! + //! This implementation returns the maximum reply message size of all handlers + //! added by AddHandler(). If no handlers are present, returns the size of + //! `mig_reply_error_t`, the minimum size of a MIG reply message. + mach_msg_size_t MachMessageServerReplySize() override; + + private: + using HandlerMap = std::map<mach_msg_id_t, MachMessageServer::Interface*>; + + HandlerMap handler_map_; // weak + mach_msg_size_t request_size_; + mach_msg_size_t reply_size_; + + DISALLOW_COPY_AND_ASSIGN(CompositeMachMessageServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_
diff --git a/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc new file mode 100644 index 0000000..0af8206 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc
@@ -0,0 +1,304 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/composite_mach_message_server.h" + +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" +#include "util/mach/mach_message.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(CompositeMachMessageServer, Empty) { + CompositeMachMessageServer server; + + EXPECT_TRUE(server.MachMessageServerRequestIDs().empty()); + + mach_msg_empty_rcv_t request = {}; + EXPECT_EQ(sizeof(request.header), server.MachMessageServerRequestSize()); + + mig_reply_error_t reply = {}; + EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize()); + + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); +} + +class TestMachMessageHandler : public MachMessageServer::Interface { + public: + TestMachMessageHandler() + : MachMessageServer::Interface(), + request_ids_(), + request_size_(0), + reply_size_(0), + return_code_(KERN_FAILURE), + return_value_(false), + destroy_complex_request_(false) { + } + + ~TestMachMessageHandler() { + } + + void SetReturnCodes(bool return_value, + kern_return_t return_code, + bool destroy_complex_request) { + return_value_ = return_value; + return_code_ = return_code; + destroy_complex_request_ = destroy_complex_request; + } + + void AddRequestID(mach_msg_id_t request_id) { + request_ids_.insert(request_id); + } + + void SetRequestSize(mach_msg_size_t request_size) { + request_size_ = request_size; + } + + void SetReplySize(mach_msg_size_t reply_size) { + reply_size_ = reply_size; + } + + // MachMessageServer::Interface: + + bool MachMessageServerFunction(const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) override { + EXPECT_NE(request_ids_.end(), request_ids_.find(in->msgh_id)); + + *destroy_complex_request = destroy_complex_request_; + PrepareMIGReplyFromRequest(in, out); + SetMIGReplyError(out, return_code_); + return return_value_; + } + + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override { + return request_ids_; + } + + mach_msg_size_t MachMessageServerRequestSize() override { + return request_size_; + } + + mach_msg_size_t MachMessageServerReplySize() override { + return reply_size_; + } + + private: + std::set<mach_msg_id_t> request_ids_; + mach_msg_size_t request_size_; + mach_msg_size_t reply_size_; + kern_return_t return_code_; + bool return_value_; + bool destroy_complex_request_; + + DISALLOW_COPY_AND_ASSIGN(TestMachMessageHandler); +}; + +TEST(CompositeMachMessageServer, HandlerDoesNotHandle) { + TestMachMessageHandler handler; + + CompositeMachMessageServer server; + server.AddHandler(&handler); + + EXPECT_TRUE(server.MachMessageServerRequestIDs().empty()); + + mach_msg_empty_rcv_t request = {}; + EXPECT_EQ(sizeof(request.header), server.MachMessageServerRequestSize()); + + mig_reply_error_t reply = {}; + EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize()); + + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); +} + +TEST(CompositeMachMessageServer, OneHandler) { + const mach_msg_id_t kRequestID = 100; + const mach_msg_size_t kRequestSize = 256; + const mach_msg_size_t kReplySize = 128; + const kern_return_t kReturnCode = KERN_SUCCESS; + + TestMachMessageHandler handler; + handler.AddRequestID(kRequestID); + handler.SetRequestSize(kRequestSize); + handler.SetReplySize(kReplySize); + handler.SetReturnCodes(true, kReturnCode, true); + + CompositeMachMessageServer server; + + // The chosen request and reply sizes must be larger than the defaults for + // that portion fo the test to be valid. + EXPECT_GT(kRequestSize, server.MachMessageServerRequestSize()); + EXPECT_GT(kReplySize, server.MachMessageServerReplySize()); + + server.AddHandler(&handler); + + std::set<mach_msg_id_t> expect_request_ids; + expect_request_ids.insert(kRequestID); + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + + EXPECT_EQ(kRequestSize, server.MachMessageServerRequestSize()); + EXPECT_EQ(kReplySize, server.MachMessageServerReplySize()); + + mach_msg_empty_rcv_t request = {}; + mig_reply_error_t reply = {}; + + // Send a message with an unknown request ID. + request.header.msgh_id = 0; + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); + + // Send a message with a known request ID. + request.header.msgh_id = kRequestID; + EXPECT_TRUE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode, reply.RetCode); + EXPECT_TRUE(destroy_complex_request); +} + +TEST(CompositeMachMessageServer, ThreeHandlers) { + const mach_msg_id_t kRequestIDs0[] = {5}; + const kern_return_t kReturnCode0 = KERN_SUCCESS; + + const mach_msg_id_t kRequestIDs1[] = {4, 7}; + const kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE; + + const mach_msg_id_t kRequestIDs2[] = {10, 0, 20}; + const mach_msg_size_t kRequestSize2 = 6144; + const mach_msg_size_t kReplySize2 = 16384; + const kern_return_t kReturnCode2 = KERN_NOT_RECEIVER; + + TestMachMessageHandler handlers[3]; + std::set<mach_msg_id_t> expect_request_ids; + + for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) { + const mach_msg_id_t request_id = kRequestIDs0[index]; + handlers[0].AddRequestID(request_id); + expect_request_ids.insert(request_id); + } + handlers[0].SetRequestSize(sizeof(mach_msg_header_t)); + handlers[0].SetReplySize(sizeof(mig_reply_error_t)); + handlers[0].SetReturnCodes(true, kReturnCode0, false); + + for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) { + const mach_msg_id_t request_id = kRequestIDs1[index]; + handlers[1].AddRequestID(request_id); + expect_request_ids.insert(request_id); + } + handlers[1].SetRequestSize(100); + handlers[1].SetReplySize(200); + handlers[1].SetReturnCodes(false, kReturnCode1, true); + + for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) { + const mach_msg_id_t request_id = kRequestIDs2[index]; + handlers[2].AddRequestID(request_id); + expect_request_ids.insert(request_id); + } + handlers[2].SetRequestSize(kRequestSize2); + handlers[2].SetReplySize(kReplySize2); + handlers[2].SetReturnCodes(true, kReturnCode2, true); + + CompositeMachMessageServer server; + + // The chosen request and reply sizes must be larger than the defaults for + // that portion fo the test to be valid. + EXPECT_GT(kRequestSize2, server.MachMessageServerRequestSize()); + EXPECT_GT(kReplySize2, server.MachMessageServerReplySize()); + + server.AddHandler(&handlers[0]); + server.AddHandler(&handlers[1]); + server.AddHandler(&handlers[2]); + + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + + EXPECT_EQ(kRequestSize2, server.MachMessageServerRequestSize()); + EXPECT_EQ(kReplySize2, server.MachMessageServerReplySize()); + + mach_msg_empty_rcv_t request = {}; + mig_reply_error_t reply = {}; + + // Send a message with an unknown request ID. + request.header.msgh_id = 100; + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); + + // Send messages with known request IDs. + + for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) { + request.header.msgh_id = kRequestIDs0[index]; + SCOPED_TRACE(base::StringPrintf( + "handler 0, index %zu, id %d", index, request.header.msgh_id)); + + EXPECT_TRUE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode0, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); + } + + for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) { + request.header.msgh_id = kRequestIDs1[index]; + SCOPED_TRACE(base::StringPrintf( + "handler 1, index %zu, id %d", index, request.header.msgh_id)); + + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode1, reply.RetCode); + EXPECT_TRUE(destroy_complex_request); + } + + for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) { + request.header.msgh_id = kRequestIDs2[index]; + SCOPED_TRACE(base::StringPrintf( + "handler 2, index %zu, id %d", index, request.header.msgh_id)); + + EXPECT_TRUE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode2, reply.RetCode); + EXPECT_TRUE(destroy_complex_request); + } +} + +// CompositeMachMessageServer can’t deal with two handlers that want to handle +// the same request ID. +TEST(CompositeMachMessageServerDeathTest, DuplicateRequestID) { + const mach_msg_id_t kRequestID = 400; + + TestMachMessageHandler handlers[2]; + handlers[0].AddRequestID(kRequestID); + handlers[1].AddRequestID(kRequestID); + + CompositeMachMessageServer server; + + server.AddHandler(&handlers[0]); + EXPECT_DEATH_CHECK(server.AddHandler(&handlers[1]), "duplicate request ID"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc b/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc new file mode 100644 index 0000000..2495a60 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc
@@ -0,0 +1,129 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exc_client_variants.h" + +#include <vector> + +#include "base/logging.h" +#include "util/mach/exc.h" +#include "util/mach/mach_exc.h" + +namespace crashpad { + +kern_return_t UniversalExceptionRaise(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count) { + // This function is similar to 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c + // exception_deliver() as far as the delivery logic is concerned. Unlike + // exception_deliver(), this function does not get or set thread states for + // behavior values that require this, as that is left to the caller to do if + // needed. + + std::vector<exception_data_type_t> small_code_vector; + exception_data_t small_code = nullptr; + if ((behavior & MACH_EXCEPTION_CODES) == 0 && code_count) { + small_code_vector.reserve(code_count); + for (size_t code_index = 0; code_index < code_count; ++code_index) { + small_code_vector.push_back(code[code_index]); + } + small_code = &small_code_vector[0]; + } + + // The *exception_raise*() family has bad declarations. Their code and + // old_state arguments aren’t pointers to const data, although they should be. + // The generated stubs in excUser.c and mach_excUser.c make it clear that the + // data is never modified, and these parameters could be declared with const + // appropriately. The uses of const_cast below are thus safe. + + switch (behavior) { + case EXCEPTION_DEFAULT: + return exception_raise( + exception_port, thread, task, exception, small_code, code_count); + + case EXCEPTION_STATE: + return exception_raise_state(exception_port, + exception, + small_code, + code_count, + flavor, + const_cast<thread_state_t>(old_state), + old_state_count, + new_state, + new_state_count); + + case EXCEPTION_STATE_IDENTITY: + return exception_raise_state_identity( + exception_port, + thread, + task, + exception, + small_code, + code_count, + flavor, + const_cast<thread_state_t>(old_state), + old_state_count, + new_state, + new_state_count); + + case EXCEPTION_DEFAULT | kMachExceptionCodes: + return mach_exception_raise(exception_port, + thread, + task, + exception, + const_cast<mach_exception_data_type_t*>(code), + code_count); + + case EXCEPTION_STATE | kMachExceptionCodes: + return mach_exception_raise_state( + exception_port, + exception, + const_cast<mach_exception_data_type_t*>(code), + code_count, + flavor, + const_cast<thread_state_t>(old_state), + old_state_count, + new_state, + new_state_count); + + case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes: + return mach_exception_raise_state_identity( + exception_port, + thread, + task, + exception, + const_cast<mach_exception_data_type_t*>(code), + code_count, + flavor, + const_cast<thread_state_t>(old_state), + old_state_count, + new_state, + new_state_count); + + default: + NOTREACHED(); + return KERN_INVALID_ARGUMENT; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exc_client_variants.h b/third_party/crashpad/crashpad/util/mach/exc_client_variants.h new file mode 100644 index 0000000..d96e689 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exc_client_variants.h
@@ -0,0 +1,85 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_ +#define CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_ + +#include <mach/mach.h> + +#include "util/mach/mach_extensions.h" + +namespace crashpad { + +//! \brief Calls the appropriate `*exception_raise*()` function for the +//! specified \a behavior. +//! +//! The function called will be `exception_raise()` for `EXCEPTION_DEFAULT`, +//! `exception_raise_state()` for `EXCEPTION_STATE`, or +//! `exception_raise_state_identity()` for `EXCEPTION_STATE_IDENTITY`. If +//! `MACH_EXCEPTION_CODES` is also set, the function called will instead be +//! `mach_exception_raise()`, `mach_exception_raise_state()` or +//! `mach_exception_raise_state_identity()`, respectively. +//! +//! This function does not fetch the existing thread state for \a behavior +//! values that require a thread state. The caller must provide the existing +//! thread state in the \a flavor, \a old_state, and \a old_state_count +//! parameters for \a behavior values that require a thread state. Thread states +//! may be obtained by calling `thread_get_state()` if needed. Similarly, this +//! function does not do anything with the new thread state returned for these +//! \a behavior values. Callers that wish to make use of the new thread state +//! may do so by using the returned \a flavor, \a new_state, and \a +//! new_state_count values. Thread states may be set by calling +//! `thread_set_state()` if needed. +//! +//! \a thread and \a task are only used when \a behavior indicates that the +//! exception message will carry identity information, when it has the value +//! value `EXCEPTION_DEFAULT` or `EXCEPTION_STATE_IDENTITY`, possibly with +//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused +//! and may be set to `THREAD_NULL` and `TASK_NULL`, respectively. +//! +//! \a flavor, \a old_state, \a old_state_count, \a new_state, and \a +//! new_state_count are only used when \a behavior indicates that the exception +//! message will carry thread state information, when it has the value +//! `EXCEPTION_STATE` or `EXCEPTION_STATE_IDENTITY`, possibly with +//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused +//! and may be set to `0` (\a old_state_count) or `nullptr` (the remaining +//! parameters). +//! +//! \param[in] behavior The exception behavior, which dictates which function +//! will be called. It is an error to call this function with an invalid +//! value for \a behavior. +//! \param[in] code If \behavior indicates a behavior without +//! `MACH_EXCEPTION_CODES`, the elements of \a code will be truncated in +//! order to be passed to the appropriate exception handler. +//! +//! All other parameters are treated equivalently to their treatment by the +//! `*exception_raise*()` family of functions. +//! +//! \return The return value of the function called. +kern_return_t UniversalExceptionRaise(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_
diff --git a/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc b/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc new file mode 100644 index 0000000..49bf78c3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc
@@ -0,0 +1,296 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exc_client_variants.h" + +#include <mach/mach.h> +#include <pthread.h> +#include <string.h> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +class TestExcClientVariants : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + TestExcClientVariants(exception_behavior_t behavior, bool all_fields) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + behavior_(behavior), + all_fields_(all_fields), + handled_(false) { + ++exception_; + ++exception_code_; + ++exception_subcode_; + } + + // UniversalMachExcServer::Interface: + + virtual kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + EXPECT_FALSE(handled_); + handled_ = true; + + EXPECT_EQ(behavior_, behavior); + EXPECT_EQ(LocalPort(), exception_port); + + if (HasIdentity()) { + EXPECT_NE(THREAD_NULL, thread); + EXPECT_EQ(ChildTask(), task); + } else { + EXPECT_EQ(THREAD_NULL, thread); + EXPECT_EQ(TASK_NULL, task); + } + + mach_exception_code_t expect_code = exception_code_; + mach_exception_subcode_t expect_subcode = exception_subcode_; + if ((behavior & MACH_EXCEPTION_CODES) == 0) { + expect_code = implicit_cast<exception_data_type_t>(expect_code); + expect_subcode = implicit_cast<exception_data_type_t>(expect_subcode); + } + + EXPECT_EQ(exception_, exception); + EXPECT_EQ(2u, code_count); + + // The code_count check above would ideally use ASSERT_EQ so that the next + // conditionals would not be necessary, but ASSERT_* requires a function + // returning type void, and the interface dictates otherwise here. + if (code_count >= 1) { + EXPECT_EQ(expect_code, code[0]); + } + if (code_count >= 2) { + EXPECT_EQ(expect_subcode, code[1]); + } + + if (HasState()) { + EXPECT_EQ(exception_ + 10, *flavor); + EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, old_state_count); + EXPECT_NE(nullptr, old_state); + EXPECT_EQ(implicit_cast<mach_msg_type_number_t>(THREAD_STATE_MAX), + *new_state_count); + EXPECT_NE(nullptr, new_state); + + for (size_t index = 0; index < old_state_count; ++index) { + EXPECT_EQ(index, old_state[index]); + } + + // Use a flavor known to be different from the incoming flavor, for a test + // of the “out” side of the inout flavor parameter. + *flavor = exception_ + 20; + *new_state_count = MACHINE_THREAD_STATE_COUNT; + + // Send a new state back to the client. + for (size_t index = 0; index < *new_state_count; ++index) { + new_state[index] = MACHINE_THREAD_STATE_COUNT - index; + } + } else { + EXPECT_EQ(THREAD_STATE_NONE, *flavor); + EXPECT_EQ(0u, old_state_count); + EXPECT_EQ(nullptr, old_state); + EXPECT_EQ(0u, *new_state_count); + EXPECT_EQ(nullptr, new_state); + } + + return KERN_SUCCESS; + } + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + UniversalMachExcServer universal_mach_exc_server(this); + + kern_return_t kr = + MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "MachMessageServer::Run"); + + EXPECT_TRUE(handled_); + } + + void MachMultiprocessChild() override { + const exception_type_t exception = exception_; + const mach_exception_data_type_t code[] = { + exception_code_, + exception_subcode_ + }; + + thread_t thread = THREAD_NULL; + task_t task = TASK_NULL; + if (all_fields_ || HasIdentity()) { + thread = MachThreadSelf(); + task = mach_task_self(); + } + + thread_state_flavor_t flavor; + thread_state_flavor_t* flavor_p = nullptr; + natural_t old_state[MACHINE_THREAD_STATE_COUNT]; + thread_state_t old_state_p = nullptr; + mach_msg_type_number_t old_state_count = 0; + natural_t new_state[THREAD_STATE_MAX]; + thread_state_t new_state_p = nullptr; + mach_msg_type_number_t new_state_count; + mach_msg_type_number_t* new_state_count_p = nullptr; + if (all_fields_ || HasState()) { + // Pick a different flavor each time based on the value of exception_. + // These aren’t real flavors, it’s just for testing. + flavor = exception_ + 10; + flavor_p = &flavor; + for (size_t index = 0; index < arraysize(old_state); ++index) { + old_state[index] = index; + } + old_state_p = reinterpret_cast<thread_state_t>(&old_state); + old_state_count = arraysize(old_state); + + // new_state and new_state_count are out parameters that the server should + // never see or use, so set them to bogus values. The call to the server + // should overwrite these. + memset(new_state, 0xa5, sizeof(new_state)); + new_state_p = reinterpret_cast<thread_state_t>(&new_state); + new_state_count = 0x5a; + new_state_count_p = &new_state_count; + } + + EXPECT_EQ(KERN_SUCCESS, UniversalExceptionRaise(behavior_, + RemotePort(), + thread, + task, + exception, + code, + arraysize(code), + flavor_p, + old_state_p, + old_state_count, + new_state_p, + new_state_count_p)); + + if (HasState()) { + // Verify the out parameters. + + EXPECT_EQ(exception_ + 20, flavor); + EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, new_state_count); + + for (size_t index = 0; index < new_state_count; ++index) { + EXPECT_EQ(MACHINE_THREAD_STATE_COUNT - index, new_state[index]); + } + } + } + + bool HasIdentity() const { + return ExceptionBehaviorHasIdentity(behavior_); + } + + bool HasState() const { + return ExceptionBehaviorHasState(behavior_); + } + + // The behavior to test. + exception_behavior_t behavior_; + + // If false, only fields required for the current value of behavior_ are set + // in a call to UniversalExceptionRaise(). The thread and task fields are only + // set for identity-carrying behaviors, and the flavor and state fields are + // only set for state-carrying behaviors. If true, all fields are set + // regardless of the behavior. Testing in both ways verifies that + // UniversalExceptionRaise() can tolerate the null arguments documented as + // usable when the behavior allows it, and that it ignores these arguments + // even when set when the behavior does not make use of them. + bool all_fields_; + + // true if an exception message was handled. + bool handled_; + + // These fields will increment for each instantiation of the test class. + static exception_type_t exception_; + static mach_exception_code_t exception_code_; + static mach_exception_subcode_t exception_subcode_; + + DISALLOW_COPY_AND_ASSIGN(TestExcClientVariants); +}; + +exception_type_t TestExcClientVariants::exception_ = 0; + +// exception_code_ and exception_subcode_ are always large enough to require +// 64 bits, so that when the 32-bit-only variants not using MACH_EXCEPITON_CODES +// are tested, the code and subcode fields can be checked for proper truncation. +mach_exception_code_t TestExcClientVariants::exception_code_ = 0x100000000; +mach_exception_subcode_t TestExcClientVariants::exception_subcode_ = + 0xffffffff00000000; + +TEST(ExcClientVariants, UniversalExceptionRaise) { + const exception_behavior_t kBehaviors[] = { + EXCEPTION_DEFAULT, + EXCEPTION_STATE, + EXCEPTION_STATE_IDENTITY, + kMachExceptionCodes | EXCEPTION_DEFAULT, + kMachExceptionCodes | EXCEPTION_STATE, + kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, + }; + + for (size_t index = 0; index < arraysize(kBehaviors); ++index) { + exception_behavior_t behavior = kBehaviors[index]; + SCOPED_TRACE(base::StringPrintf("index %zu, behavior %d", index, behavior)); + + { + SCOPED_TRACE("all_fields = false"); + + TestExcClientVariants test_exc_client_variants(behavior, false); + test_exc_client_variants.Run(); + } + + { + SCOPED_TRACE("all_fields = true"); + + TestExcClientVariants test_exc_client_variants(behavior, true); + test_exc_client_variants.Run(); + } + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc b/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc new file mode 100644 index 0000000..c5a74a3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc
@@ -0,0 +1,801 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exc_server_variants.h" + +#include <AvailabilityMacros.h> +#include <string.h> + +#include <algorithm> +#include <vector> + +#include "base/logging.h" +#include "util/mac/mac_util.h" +#include "util/mach/composite_mach_message_server.h" +#include "util/mach/exc.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/excServer.h" +#include "util/mach/mach_exc.h" +#include "util/mach/mach_excServer.h" +#include "util/mach/mach_message.h" + +extern "C" { + +// These six functions are not used, and are in fact obsoleted by the other +// functionality implemented in this file. The standard MIG-generated exc_server +// (in excServer.c) and mach_exc_server (in mach_excServer.c) server dispatch +// routines usable with the standard mach_msg_server() function call out to +// these functions. exc_server() and mach_exc_server() are unused and are +// replaced by the more flexible ExcServer and MachExcServer, but the linker +// still needs to see these six function definitions. + +kern_return_t catch_exception_raise(exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t catch_exception_raise_state( + exception_handler_t exception_port, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + thread_state_t old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t catch_exception_raise_state_identity( + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + thread_state_t old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t catch_mach_exception_raise(exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t code_count) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t catch_mach_exception_raise_state( + exception_handler_t exception_port, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + thread_state_t old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t catch_mach_exception_raise_state_identity( + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + thread_state_t old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count) { + NOTREACHED(); + return KERN_FAILURE; +} + +} // extern "C" + +namespace crashpad { + +namespace { + +// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with +// the exc subsystem. +struct ExcTraits { + using ExceptionCode = exception_data_type_t; + + using RequestUnion = __RequestUnion__exc_subsystem; + using ReplyUnion = __ReplyUnion__exc_subsystem; + + using ExceptionRaiseRequest = __Request__exception_raise_t; + using ExceptionRaiseStateRequest = __Request__exception_raise_state_t; + using ExceptionRaiseStateIdentityRequest = + __Request__exception_raise_state_identity_t; + + using ExceptionRaiseReply = __Reply__exception_raise_t; + using ExceptionRaiseStateReply = __Reply__exception_raise_state_t; + using ExceptionRaiseStateIdentityReply = + __Reply__exception_raise_state_identity_t; + + // The MIG-generated __MIG_check__Request__*() functions are not declared as + // accepting const data, but they could have been because they in fact do not + // modify the data. + + static kern_return_t MIGCheckRequestExceptionRaise( + const ExceptionRaiseRequest* in_request) { + return __MIG_check__Request__exception_raise_t( + const_cast<ExceptionRaiseRequest*>(in_request)); + } + + static kern_return_t MIGCheckRequestExceptionRaiseState( + const ExceptionRaiseStateRequest* in_request, + const ExceptionRaiseStateRequest** in_request_1) { + return __MIG_check__Request__exception_raise_state_t( + const_cast<ExceptionRaiseStateRequest*>(in_request), + const_cast<ExceptionRaiseStateRequest**>(in_request_1)); + } + + static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity( + const ExceptionRaiseStateIdentityRequest* in_request, + const ExceptionRaiseStateIdentityRequest** in_request_1) { + return __MIG_check__Request__exception_raise_state_identity_t( + const_cast<ExceptionRaiseStateIdentityRequest*>(in_request), + const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1)); + } + + // There are no predefined constants for these. + static const mach_msg_id_t kMachMessageIDExceptionRaise = 2401; + static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2402; + static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2403; + + static const exception_behavior_t kExceptionBehavior = 0; +}; + +// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with +// the mach_exc subsystem. +struct MachExcTraits { + using ExceptionCode = mach_exception_data_type_t; + + using RequestUnion = __RequestUnion__mach_exc_subsystem; + using ReplyUnion = __ReplyUnion__mach_exc_subsystem; + + using ExceptionRaiseRequest = __Request__mach_exception_raise_t; + using ExceptionRaiseStateRequest = __Request__mach_exception_raise_state_t; + using ExceptionRaiseStateIdentityRequest = + __Request__mach_exception_raise_state_identity_t; + + using ExceptionRaiseReply = __Reply__mach_exception_raise_t; + using ExceptionRaiseStateReply = __Reply__mach_exception_raise_state_t; + using ExceptionRaiseStateIdentityReply = + __Reply__mach_exception_raise_state_identity_t; + + // The MIG-generated __MIG_check__Request__*() functions are not declared as + // accepting const data, but they could have been because they in fact do not + // modify the data. + + static kern_return_t MIGCheckRequestExceptionRaise( + const ExceptionRaiseRequest* in_request) { + return __MIG_check__Request__mach_exception_raise_t( + const_cast<ExceptionRaiseRequest*>(in_request)); + } + + static kern_return_t MIGCheckRequestExceptionRaiseState( + const ExceptionRaiseStateRequest* in_request, + const ExceptionRaiseStateRequest** in_request_1) { + return __MIG_check__Request__mach_exception_raise_state_t( + const_cast<ExceptionRaiseStateRequest*>(in_request), + const_cast<ExceptionRaiseStateRequest**>(in_request_1)); + } + + static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity( + const ExceptionRaiseStateIdentityRequest* in_request, + const ExceptionRaiseStateIdentityRequest** in_request_1) { + return __MIG_check__Request__mach_exception_raise_state_identity_t( + const_cast<ExceptionRaiseStateIdentityRequest*>(in_request), + const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1)); + } + + // There are no predefined constants for these. + static const mach_msg_id_t kMachMessageIDExceptionRaise = 2405; + static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2406; + static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2407; + + static const exception_behavior_t kExceptionBehavior = MACH_EXCEPTION_CODES; +}; + +//! \brief A server interface for the `exc` or `mach_exc` Mach subsystems. +template <typename Traits> +class ExcServer : public MachMessageServer::Interface { + public: + //! \brief An interface that the different request messages that are a part of + //! the `exc` or `mach_exc` Mach subsystems can be dispatched to. + class Interface { + public: + //! \brief Handles exceptions raised by `exception_raise()` or + //! `mach_exception_raise()`. + //! + //! This behaves equivalently to a `catch_exception_raise()` function used + //! with `exc_server()`, or a `catch_mach_exception_raise()` function used + //! with `mach_exc_server()`. + //! + //! \param[in] trailer The trailer received with the request message. + //! \param[out] destroy_request `true` if the request message is to be + //! destroyed even when this method returns success. See + //! MachMessageServer::Interface. + virtual kern_return_t CatchExceptionRaise( + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + const mach_msg_trailer_t* trailer, + bool* destroy_request) = 0; + + //! \brief Handles exceptions raised by `exception_raise_state()` or + //! `mach_exception_raise_state()`. + //! + //! This behaves equivalently to a `catch_exception_raise_state()` function + //! used with `exc_server()`, or a `catch_mach_exception_raise_state()` + //! function used with `mach_exc_server()`. + //! + //! There is no \a destroy_request parameter because, unlike + //! CatchExceptionRaise() and CatchExceptionRaiseStateIdentity(), the + //! request message is not complex (it does not carry the \a thread or \a + //! task port rights) and thus there is nothing to destroy. + //! + //! \param[in] trailer The trailer received with the request message. + virtual kern_return_t CatchExceptionRaiseState( + exception_handler_t exception_port, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer) = 0; + + //! \brief Handles exceptions raised by `exception_raise_state_identity()` + //! or `mach_exception_raise_state_identity()`. + //! + //! This behaves equivalently to a `catch_exception_raise_state_identity()` + //! function used with `exc_server()`, or a + //! `catch_mach_exception_raise_state_identity()` function used with + //! `mach_exc_server()`. + //! + //! \param[in] trailer The trailer received with the request message. + //! \param[out] destroy_request `true` if the request message is to be + //! destroyed even when this method returns success. See + //! MachMessageServer::Interface. + virtual kern_return_t CatchExceptionRaiseStateIdentity( + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_request) = 0; + + protected: + ~Interface() {} + }; + + //! \brief Constructs an object of this class. + //! + //! \param[in] interface The interface to dispatch requests to. Weak. + explicit ExcServer(Interface* interface) + : MachMessageServer::Interface(), interface_(interface) {} + + // MachMessageServer::Interface: + + bool MachMessageServerFunction(const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) override; + + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override { + const mach_msg_id_t request_ids[] = { + Traits::kMachMessageIDExceptionRaise, + Traits::kMachMessageIDExceptionRaiseState, + Traits::kMachMessageIDExceptionRaiseStateIdentity, + }; + return std::set<mach_msg_id_t>(&request_ids[0], + &request_ids[arraysize(request_ids)]); + } + + mach_msg_size_t MachMessageServerRequestSize() override { + return sizeof(typename Traits::RequestUnion); + } + + mach_msg_size_t MachMessageServerReplySize() override { + return sizeof(typename Traits::ReplyUnion); + } + + private: + Interface* interface_; // weak + + DISALLOW_COPY_AND_ASSIGN(ExcServer); +}; + +template <typename Traits> +bool ExcServer<Traits>::MachMessageServerFunction( + const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) { + PrepareMIGReplyFromRequest(in_header, out_header); + + const mach_msg_trailer_t* in_trailer = + MachMessageTrailerFromHeader(in_header); + + switch (in_header->msgh_id) { + case Traits::kMachMessageIDExceptionRaise: { + // exception_raise(), catch_exception_raise(), mach_exception_raise(), + // catch_mach_exception_raise(). + using Request = typename Traits::ExceptionRaiseRequest; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = Traits::MIGCheckRequestExceptionRaise(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = typename Traits::ExceptionRaiseReply; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->CatchExceptionRaise(in_header->msgh_local_port, + in_request->thread.name, + in_request->task.name, + in_request->exception, + in_request->code, + in_request->codeCnt, + in_trailer, + destroy_complex_request); + if (out_reply->RetCode != KERN_SUCCESS) { + return true; + } + + out_header->msgh_size = sizeof(*out_reply); + return true; + } + + case Traits::kMachMessageIDExceptionRaiseState: { + // exception_raise_state(), catch_exception_raise_state(), + // mach_exception_raise_state(), catch_mach_exception_raise_state(). + using Request = typename Traits::ExceptionRaiseStateRequest; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + + // in_request_1 is used for the portion of the request after the codes, + // which in theory can be variable-length. The check function will set it. + const Request* in_request_1; + kern_return_t kr = + Traits::MIGCheckRequestExceptionRaiseState(in_request, &in_request_1); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = typename Traits::ExceptionRaiseStateReply; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->flavor = in_request_1->flavor; + out_reply->new_stateCnt = arraysize(out_reply->new_state); + out_reply->RetCode = + interface_->CatchExceptionRaiseState(in_header->msgh_local_port, + in_request->exception, + in_request->code, + in_request->codeCnt, + &out_reply->flavor, + in_request_1->old_state, + in_request_1->old_stateCnt, + out_reply->new_state, + &out_reply->new_stateCnt, + in_trailer); + if (out_reply->RetCode != KERN_SUCCESS) { + return true; + } + + out_header->msgh_size = + sizeof(*out_reply) - sizeof(out_reply->new_state) + + sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt; + return true; + } + + case Traits::kMachMessageIDExceptionRaiseStateIdentity: { + // exception_raise_state_identity(), + // catch_exception_raise_state_identity(), + // mach_exception_raise_state_identity(), + // catch_mach_exception_raise_state_identity(). + using Request = typename Traits::ExceptionRaiseStateIdentityRequest; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + + // in_request_1 is used for the portion of the request after the codes, + // which in theory can be variable-length. The check function will set it. + const Request* in_request_1; + kern_return_t kr = Traits::MIGCheckRequestExceptionRaiseStateIdentity( + in_request, &in_request_1); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = typename Traits::ExceptionRaiseStateIdentityReply; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->flavor = in_request_1->flavor; + out_reply->new_stateCnt = arraysize(out_reply->new_state); + out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity( + in_header->msgh_local_port, + in_request->thread.name, + in_request->task.name, + in_request->exception, + in_request->code, + in_request->codeCnt, + &out_reply->flavor, + in_request_1->old_state, + in_request_1->old_stateCnt, + out_reply->new_state, + &out_reply->new_stateCnt, + in_trailer, + destroy_complex_request); + if (out_reply->RetCode != KERN_SUCCESS) { + return true; + } + + out_header->msgh_size = + sizeof(*out_reply) - sizeof(out_reply->new_state) + + sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt; + return true; + } + + default: { + SetMIGReplyError(out_header, MIG_BAD_ID); + return false; + } + } +} + +//! \brief A server interface for the `exc` or `mach_exc` Mach subsystems, +//! simplified to have only a single interface method needing +//! implementation. +template <typename Traits> +class SimplifiedExcServer final : public ExcServer<Traits>, + public ExcServer<Traits>::Interface { + public: + //! \brief An interface that the different request messages that are a part of + //! the `exc` or `mach_exc` Mach subsystems can be dispatched to. + class Interface { + public: + //! \brief Handles exceptions raised by `exception_raise()`, + //! `exception_raise_state()`, and `exception_raise_state_identity()`; + //! or `mach_exception_raise()`, `mach_exception_raise_state()`, and + //! `mach_exception_raise_state_identity()`. + //! + //! For convenience in implementation, these different “behaviors” of + //! exception messages are all mapped to a single interface method. The + //! exception’s original “behavior” is specified in the \a behavior + //! parameter. Only parameters that were supplied in the request message + //! are populated, other parameters are set to reasonable default values. + //! + //! The meanings of most parameters are identical to that of + //! ExcServer<>::Interface::CatchExceptionRaiseStateIdentity(). + //! + //! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or + //! `EXCEPTION_STATE_IDENTITY`, identifying which exception request + //! message was processed and thus which other parameters are valid. + //! When used with the `mach_exc` subsystem, `MACH_EXCEPTION_CODES` will + //! be ORed in to this parameter. + virtual kern_return_t CatchException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) = 0; + + protected: + ~Interface() {} + }; + + //! \brief Constructs an object of this class. + //! + //! \param[in] interface The interface to dispatch requests to. Weak. + explicit SimplifiedExcServer(Interface* interface) + : ExcServer<Traits>(this), + ExcServer<Traits>::Interface(), + interface_(interface) {} + + // ExcServer::Interface: + + kern_return_t CatchExceptionRaise(exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + const mach_msg_trailer_t* trailer, + bool* destroy_request) override { + thread_state_flavor_t flavor = THREAD_STATE_NONE; + mach_msg_type_number_t new_state_count = 0; + return interface_->CatchException( + Traits::kExceptionBehavior | EXCEPTION_DEFAULT, + exception_port, + thread, + task, + exception, + code_count ? code : nullptr, + code_count, + &flavor, + nullptr, + 0, + nullptr, + &new_state_count, + trailer, + destroy_request); + } + + kern_return_t CatchExceptionRaiseState( + exception_handler_t exception_port, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer) override { + bool destroy_complex_request = false; + return interface_->CatchException( + Traits::kExceptionBehavior | EXCEPTION_STATE, + exception_port, + THREAD_NULL, + TASK_NULL, + exception, + code_count ? code : nullptr, + code_count, + flavor, + old_state_count ? old_state : nullptr, + old_state_count, + new_state_count ? new_state : nullptr, + new_state_count, + trailer, + &destroy_complex_request); + } + + kern_return_t CatchExceptionRaiseStateIdentity( + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const typename Traits::ExceptionCode* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_request) override { + return interface_->CatchException( + Traits::kExceptionBehavior | EXCEPTION_STATE_IDENTITY, + exception_port, + thread, + task, + exception, + code_count ? code : nullptr, + code_count, + flavor, + old_state_count ? old_state : nullptr, + old_state_count, + new_state_count ? new_state : nullptr, + new_state_count, + trailer, + destroy_request); + } + + private: + Interface* interface_; // weak + + DISALLOW_COPY_AND_ASSIGN(SimplifiedExcServer); +}; + +} // namespace + +namespace internal { + +class UniversalMachExcServerImpl final + : public CompositeMachMessageServer, + public SimplifiedExcServer<ExcTraits>::Interface, + public SimplifiedExcServer<MachExcTraits>::Interface { + public: + explicit UniversalMachExcServerImpl( + UniversalMachExcServer::Interface* interface) + : CompositeMachMessageServer(), + SimplifiedExcServer<ExcTraits>::Interface(), + SimplifiedExcServer<MachExcTraits>::Interface(), + exc_server_(this), + mach_exc_server_(this), + interface_(interface) { + AddHandler(&exc_server_); + AddHandler(&mach_exc_server_); + } + + ~UniversalMachExcServerImpl() {} + + // SimplifiedExcServer<ExcTraits>::Interface: + kern_return_t CatchException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) { + std::vector<mach_exception_data_type_t> mach_codes; + mach_codes.reserve(code_count); + for (size_t index = 0; index < code_count; ++index) { + mach_codes.push_back(code[index]); + } + + return interface_->CatchMachException(behavior, + exception_port, + thread, + task, + exception, + code_count ? &mach_codes[0] : nullptr, + code_count, + flavor, + old_state_count ? old_state : nullptr, + old_state_count, + new_state_count ? new_state : nullptr, + new_state_count, + trailer, + destroy_complex_request); + } + + // SimplifiedExcServer<MachExcTraits>::Interface: + kern_return_t CatchException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) { + return interface_->CatchMachException(behavior, + exception_port, + thread, + task, + exception, + code_count ? code : nullptr, + code_count, + flavor, + old_state_count ? old_state : nullptr, + old_state_count, + new_state_count ? new_state : nullptr, + new_state_count, + trailer, + destroy_complex_request); + } + + private: + SimplifiedExcServer<ExcTraits> exc_server_; + SimplifiedExcServer<MachExcTraits> mach_exc_server_; + UniversalMachExcServer::Interface* interface_; // weak + + DISALLOW_COPY_AND_ASSIGN(UniversalMachExcServerImpl); +}; + +} // namespace internal + +UniversalMachExcServer::UniversalMachExcServer( + UniversalMachExcServer::Interface* interface) + : MachMessageServer::Interface(), + impl_(new internal::UniversalMachExcServerImpl(interface)) { +} + +UniversalMachExcServer::~UniversalMachExcServer() { +} + +bool UniversalMachExcServer::MachMessageServerFunction( + const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) { + return impl_->MachMessageServerFunction( + in_header, out_header, destroy_complex_request); +} + +std::set<mach_msg_id_t> UniversalMachExcServer::MachMessageServerRequestIDs() { + return impl_->MachMessageServerRequestIDs(); +} + +mach_msg_size_t UniversalMachExcServer::MachMessageServerRequestSize() { + return impl_->MachMessageServerRequestSize(); +} + +mach_msg_size_t UniversalMachExcServer::MachMessageServerReplySize() { + return impl_->MachMessageServerReplySize(); +} + +kern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception, + exception_behavior_t behavior, + bool set_thread_state) { + if (exception == EXC_CRASH +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 + && MacOSXMinorVersion() >= 11 +#endif + ) { + return KERN_SUCCESS; + } + + if (!set_thread_state && ExceptionBehaviorHasState(behavior)) { + return MACH_RCV_PORT_DIED; + } + + return KERN_SUCCESS; +} + +void ExcServerCopyState(exception_behavior_t behavior, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count) { + if (ExceptionBehaviorHasState(behavior)) { + *new_state_count = std::min(old_state_count, *new_state_count); + memcpy(new_state, old_state, *new_state_count * sizeof(old_state[0])); + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exc_server_variants.h b/third_party/crashpad/crashpad/util/mach/exc_server_variants.h new file mode 100644 index 0000000..95d1c12 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exc_server_variants.h
@@ -0,0 +1,220 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_ +#define CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_ + +#include <mach/mach.h> + +#include <set> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { + +namespace internal { +class UniversalMachExcServerImpl; +} // namespace internal + +//! \brief A server interface for the `exc` and `mach_exc` Mach subsystems, +//! unified to handle exceptions delivered to either subsystem, and +//! simplified to have only a single interface method needing +//! implementation. +//! +//! The `<mach/exc.defs>` and `<mach/mach_exc.defs>` interfaces are identical, +//! except that the latter allows for 64-bit exception codes, and is requested +//! by setting the MACH_EXCEPTION_CODES behavior bit associated with an +//! exception port. +//! +//! UniversalMachExcServer operates by translating messages received in the +//! `exc` subsystem to a variant that is compatible with the `mach_exc` +//! subsystem. This involves changing the format of \a code, the exception code +//! field, from `exception_data_type_t` to `mach_exception_data_type_t`. +class UniversalMachExcServer final : public MachMessageServer::Interface { + public: + //! \brief An interface that the different request messages that are a part of + //! the `exc` and `mach_exc` Mach subsystems can be dispatched to. + class Interface { + public: + //! \brief Handles exceptions raised by `exception_raise()`, + //! `exception_raise_state()`, `exception_raise_state_identity()`, + //! `mach_exception_raise()`, `mach_exception_raise_state()`, and + //! `mach_exception_raise_state_identity()`. + //! + //! For convenience in implementation, these different “behaviors” of + //! exception messages are all mapped to a single interface method. The + //! exception’s original “behavior” is specified in the \a behavior + //! parameter. Only parameters that were supplied in the request message + //! are populated, other parameters are set to reasonable default values. + //! + //! This behaves equivalently to a `catch_exception_raise_state_identity()` + //! function used with `exc_server()`, or a + //! `catch_mach_exception_raise_state_identity()` function used with + //! `mach_exc_server()`. The meanings of most parameters are identical to + //! their meanings to these functions. + //! + //! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, + //! or `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` + //! ORed in. This identifies which exception request message was + //! processed and thus which other parameters are valid. + //! \param[in] trailer The trailer received with the request message. + //! \param[out] destroy_request `true` if the request message is to be + //! destroyed even when this method returns success. See + //! MachMessageServer::Interface. + virtual kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) = 0; + + protected: + ~Interface() {} + }; + + //! \brief Constructs an object of this class. + //! + //! \param[in] interface The interface to dispatch requests to. Weak. + explicit UniversalMachExcServer(Interface* interface); + + ~UniversalMachExcServer(); + + // MachMessageServer::Interface: + bool MachMessageServerFunction(const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) override; + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override; + mach_msg_size_t MachMessageServerRequestSize() override; + mach_msg_size_t MachMessageServerReplySize() override; + + private: + scoped_ptr<internal::UniversalMachExcServerImpl> impl_; + + DISALLOW_COPY_AND_ASSIGN(UniversalMachExcServer); +}; + +//! \brief Computes an approriate successful return value for an exception +//! handler function. +//! +//! For exception handlers that respond to state-carrying behaviors, when the +//! handler is called by the kernel (as it is normally), the kernel will attempt +//! to set a new thread state when the exception handler returns successfully. +//! Other code that mimics the kernel’s exception-delivery semantics may +//! implement the same or similar behavior. In some situations, it is +//! undesirable to set a new thread state. If the exception handler were to +//! return unsuccessfully, however, the kernel would continue searching for an +//! exception handler at a wider (task or host) scope. This may also be +//! undesirable. +//! +//! If such exception handlers return `MACH_RCV_PORT_DIED`, the kernel will not +//! set a new thread state and will also not search for another exception +//! handler. See 10.9.4 `xnu-2422.110.17/osfmk/kern/exception.c`. +//! `exception_deliver()` will only set a new thread state if the handler’s +//! return code was `MACH_MSG_SUCCESS` (a synonym for `KERN_SUCCESS`), and +//! subsequently, `exception_triage()` will not search for a new handler if the +//! handler’s return code was `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`. +//! +//! This function allows exception handlers to compute an appropriate return +//! code to influence their caller (the kernel) in the desired way with respect +//! to setting a new thread state while suppressing the caller’s subsequent +//! search for other exception handlers. An exception handler should return the +//! value returned by this function. +//! +//! This function is useful even for `EXC_CRASH` handlers, where returning +//! `KERN_SUCCESS` and allowing the kernel to set a new thread state has been +//! observed to cause a perceptible and unnecessary waste of time. The victim +//! task in an `EXC_CRASH` handler is already being terminated and is no longer +//! schedulable, so there is no point in setting the states of any of its +//! threads. +//! +//! On OS X 10.11, the `MACH_RCV_PORT_DIED` mechanism cannot be used with an +//! `EXC_CRASH` handler without triggering an undesirable `EXC_CORPSE_NOTIFY` +//! exception. In that case, `KERN_SUCCESS` is always returned. Because this +//! function may return `KERN_SUCCESS` for a state-carrying exception, it is +//! important to ensure that the state returned by a state-carrying exception +//! handler is valid, because it will be passed to `thread_set_status()`. +//! ExcServerCopyState() may be used to achieve this. +//! +//! \param[in] exception The exception type passed to the exception handler. +//! This may be taken directly from the \a exception parameter of +//! internal::SimplifiedExcServer::Interface::CatchException(), for example. +//! \param[in] behavior The behavior of the exception handler as invoked. This +//! may be taken directly from the \a behavior parameter of +//! internal::SimplifiedExcServer::Interface::CatchException(), for example. +//! \param[in] set_thread_state `true` if the handler would like its caller to +//! set the new thread state using the \a flavor, \a new_state, and \a +//! new_state_count out parameters. This can only happen when \a behavior is +//! a state-carrying behavior. +//! +//! \return `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`. `KERN_SUCCESS` is used when +//! \a behavior is not a state-carrying behavior, or when it is a +//! state-carrying behavior and \a set_thread_state is `true`, or for +//! `EXC_CRASH` exceptions on OS X 10.11 and later. Otherwise, +//! `MACH_RCV_PORT_DIED` is used. +kern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception, + exception_behavior_t behavior, + bool set_thread_state); + +//! \brief Copies the old state to the new state for state-carrying exceptions. +//! +//! When the kernel sends a state-carrying exception request and the response is +//! successful (`MACH_MSG_SUCCESS`, a synonym for `KERN_SUCCESS`), it will set +//! a new thread state based on \a new_state and \a new_state_count. To ease +//! initialization of the new state, this function copies \a old_state and +//! \a old_state_count. This is only done if \a behavior indicates a +//! state-carrying exception. +//! +//! \param[in] behavior The behavior of the exception handler as invoked. This +//! may be taken directly from the \a behavior parameter of +//! internal::SimplifiedExcServer::Interface::CatchException(), for example. +//! \param[in] old_state The original state value. This may be taken directly +//! from the \a old_state parameter of +//! internal::SimplifiedExcServer::Interface::CatchException(), for example. +//! \param[in] old_state_count The number of significant `natural_t` words in \a +//! old_state. This may be taken directly from the \a old_state_count +//! parameter of internal::SimplifiedExcServer::Interface::CatchException(), +//! for example. +//! \param[out] new_state The state value to be set. This may be taken directly +//! from the \a new_state parameter of +//! internal::SimplifiedExcServer::Interface::CatchException(), for example. +//! This parameter is untouched if \a behavior is not state-carrying. +//! \param[inout] new_state_count On entry, the number of `natural_t` words +//! available to be written to in \a new_state. On return, the number of +//! significant `natural_t` words in \a new_state. This may be taken +//! directly from the \a new_state_count parameter of +//! internal::SimplifiedExcServer::Interface::CatchException(), for example. +//! This parameter is untouched if \a behavior is not state-carrying. If \a +//! \a behavior is state-carrying, this parameter should be at least as +//! large as \a old_state_count. +void ExcServerCopyState(exception_behavior_t behavior, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_
diff --git a/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc b/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc new file mode 100644 index 0000000..68674b6 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc
@@ -0,0 +1,1342 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exc_server_variants.h" + +#include <mach/mach.h> +#include <string.h> + +#include "base/strings/stringprintf.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/mac/mac_util.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_types.h" +#include "util/mach/mach_message.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +using testing::DefaultValue; +using testing::Eq; +using testing::Pointee; +using testing::Return; + +// Fake Mach ports. These aren’t used as ports in these tests, they’re just used +// as cookies to make sure that the correct values get passed to the correct +// places. +const mach_port_t kClientRemotePort = 0x01010101; +const mach_port_t kServerLocalPort = 0x02020202; +const thread_t kExceptionThreadPort = 0x03030303; +const task_t kExceptionTaskPort = 0x04040404; + +// Other fake exception values. +const exception_type_t kExceptionType = EXC_BAD_ACCESS; + +// Test using an exception code with the high bit set to ensure that it gets +// promoted to the wider mach_exception_data_type_t type as a signed quantity. +const exception_data_type_t kTestExceptonCodes[] = { + KERN_PROTECTION_FAILURE, + implicit_cast<exception_data_type_t>(0xfedcba98), +}; + +const mach_exception_data_type_t kTestMachExceptionCodes[] = { + KERN_PROTECTION_FAILURE, + implicit_cast<mach_exception_data_type_t>(0xfedcba9876543210), +}; + +const thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE; +const mach_msg_type_number_t kThreadStateFlavorCount = + MACHINE_THREAD_STATE_COUNT; + +void InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor, + mach_port_t port) { + descriptor->name = port; + descriptor->disposition = MACH_MSG_TYPE_PORT_SEND; + descriptor->type = MACH_MSG_PORT_DESCRIPTOR; +} + +// The definitions of the request and reply structures from mach_exc.h aren’t +// available here. They need custom initialization code, and the reply +// structures need verification code too, so duplicate the expected definitions +// of the structures from both exc.h and mach_exc.h here in this file, and +// provide the initialization and verification code as methods in true +// object-oriented fashion. + +struct __attribute__((packed, aligned(4))) ExceptionRaiseRequest { + ExceptionRaiseRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) | + MACH_MSGH_BITS_COMPLEX; + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = kClientRemotePort; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 2401; + msgh_body.msgh_descriptor_count = 2; + InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); + InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); + NDR = NDR_record; + exception = kExceptionType; + codeCnt = 2; + code[0] = kTestExceptonCodes[0]; + code[1] = kTestExceptonCodes[1]; + } + + mach_msg_header_t Head; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + integer_t code[2]; + mach_msg_trailer_t trailer; +}; + +struct __attribute__((packed, aligned(4))) ExceptionRaiseReply { + ExceptionRaiseReply() { + memset(this, 0x5a, sizeof(*this)); + RetCode = KERN_FAILURE; + } + + // Verify accepts a |behavior| parameter because the same message format and + // verification function is used for ExceptionRaiseReply and + // MachExceptionRaiseReply. Knowing which behavior is expected allows the + // message ID to be checked. + void Verify(exception_behavior_t behavior) { + EXPECT_EQ(implicit_cast<mach_msg_bits_t>( + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)), + Head.msgh_bits); + EXPECT_EQ(sizeof(*this), Head.msgh_size); + EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); + EXPECT_EQ(kMachPortNull, Head.msgh_local_port); + switch (behavior) { + case EXCEPTION_DEFAULT: + EXPECT_EQ(2501, Head.msgh_id); + break; + case EXCEPTION_DEFAULT | kMachExceptionCodes: + EXPECT_EQ(2505, Head.msgh_id); + break; + default: + ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id " + << Head.msgh_id; + break; + } + EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); + EXPECT_EQ(KERN_SUCCESS, RetCode); + } + + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; +}; + +struct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest { + ExceptionRaiseStateRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND); + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = kClientRemotePort; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 2402; + NDR = NDR_record; + exception = kExceptionType; + codeCnt = 2; + code[0] = kTestExceptonCodes[0]; + code[1] = kTestExceptonCodes[1]; + flavor = kThreadStateFlavor; + old_stateCnt = kThreadStateFlavorCount; + + // Adjust the message size for the data that it’s actually carrying, which + // may be smaller than the maximum that it can carry. + Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); + } + + // Because the message size has been adjusted, the trailer may not appear in + // its home member variable. This computes the actual address of the trailer. + const mach_msg_trailer_t* Trailer() const { + return MachMessageTrailerFromHeader(&Head); + } + + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + integer_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[THREAD_STATE_MAX]; + mach_msg_trailer_t trailer; +}; + +struct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply { + ExceptionRaiseStateReply() { + memset(this, 0x5a, sizeof(*this)); + RetCode = KERN_FAILURE; + } + + // Verify accepts a |behavior| parameter because the same message format and + // verification function is used for ExceptionRaiseStateReply, + // ExceptionRaiseStateIdentityReply, MachExceptionRaiseStateReply, and + // MachExceptionRaiseStateIdentityReply. Knowing which behavior is expected + // allows the message ID to be checked. + void Verify(exception_behavior_t behavior) { + EXPECT_EQ(implicit_cast<mach_msg_bits_t>( + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)), + Head.msgh_bits); + EXPECT_EQ(sizeof(*this), Head.msgh_size); + EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); + EXPECT_EQ(kMachPortNull, Head.msgh_local_port); + switch (behavior) { + case EXCEPTION_STATE: + EXPECT_EQ(2502, Head.msgh_id); + break; + case EXCEPTION_STATE_IDENTITY: + EXPECT_EQ(2503, Head.msgh_id); + break; + case EXCEPTION_STATE | kMachExceptionCodes: + EXPECT_EQ(2506, Head.msgh_id); + break; + case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes: + EXPECT_EQ(2507, Head.msgh_id); + break; + default: + ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id " + << Head.msgh_id; + break; + } + EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); + EXPECT_EQ(KERN_SUCCESS, RetCode); + EXPECT_EQ(kThreadStateFlavor, flavor); + EXPECT_EQ(arraysize(new_state), new_stateCnt); + } + + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[THREAD_STATE_MAX]; +}; + +struct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest { + ExceptionRaiseStateIdentityRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) | + MACH_MSGH_BITS_COMPLEX; + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = kClientRemotePort; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 2403; + msgh_body.msgh_descriptor_count = 2; + InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); + InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); + NDR = NDR_record; + exception = kExceptionType; + codeCnt = 2; + code[0] = kTestExceptonCodes[0]; + code[1] = kTestExceptonCodes[1]; + flavor = kThreadStateFlavor; + old_stateCnt = kThreadStateFlavorCount; + + // Adjust the message size for the data that it’s actually carrying, which + // may be smaller than the maximum that it can carry. + Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); + } + + // Because the message size has been adjusted, the trailer may not appear in + // its home member variable. This computes the actual address of the trailer. + const mach_msg_trailer_t* Trailer() const { + return MachMessageTrailerFromHeader(&Head); + } + + mach_msg_header_t Head; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + integer_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[THREAD_STATE_MAX]; + mach_msg_trailer_t trailer; +}; + +// The reply messages for exception_raise_state and +// exception_raise_state_identity are identical. +using ExceptionRaiseStateIdentityReply = ExceptionRaiseStateReply; + +struct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest { + MachExceptionRaiseRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) | + MACH_MSGH_BITS_COMPLEX; + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = kClientRemotePort; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 2405; + msgh_body.msgh_descriptor_count = 2; + InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); + InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); + NDR = NDR_record; + exception = kExceptionType; + codeCnt = 2; + code[0] = kTestMachExceptionCodes[0]; + code[1] = kTestMachExceptionCodes[1]; + } + + mach_msg_header_t Head; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + mach_msg_trailer_t trailer; +}; + +// The reply messages for exception_raise and mach_exception_raise are +// identical. +using MachExceptionRaiseReply = ExceptionRaiseReply; + +struct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest { + MachExceptionRaiseStateRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND); + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = kClientRemotePort; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 2406; + NDR = NDR_record; + exception = kExceptionType; + codeCnt = 2; + code[0] = kTestMachExceptionCodes[0]; + code[1] = kTestMachExceptionCodes[1]; + flavor = kThreadStateFlavor; + old_stateCnt = kThreadStateFlavorCount; + + // Adjust the message size for the data that it’s actually carrying, which + // may be smaller than the maximum that it can carry. + Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); + } + + // Because the message size has been adjusted, the trailer may not appear in + // its home member variable. This computes the actual address of the trailer. + const mach_msg_trailer_t* Trailer() const { + return MachMessageTrailerFromHeader(&Head); + } + + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[THREAD_STATE_MAX]; + mach_msg_trailer_t trailer; +}; + +// The reply messages for exception_raise_state and mach_exception_raise_state +// are identical. +using MachExceptionRaiseStateReply = ExceptionRaiseStateReply; + +struct __attribute__((packed, + aligned(4))) MachExceptionRaiseStateIdentityRequest { + MachExceptionRaiseStateIdentityRequest() { + memset(this, 0xa5, sizeof(*this)); + Head.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) | + MACH_MSGH_BITS_COMPLEX; + Head.msgh_size = sizeof(*this) - sizeof(trailer); + Head.msgh_remote_port = kClientRemotePort; + Head.msgh_local_port = kServerLocalPort; + Head.msgh_id = 2407; + msgh_body.msgh_descriptor_count = 2; + InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); + InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); + NDR = NDR_record; + exception = kExceptionType; + codeCnt = 2; + code[0] = kTestMachExceptionCodes[0]; + code[1] = kTestMachExceptionCodes[1]; + flavor = kThreadStateFlavor; + old_stateCnt = kThreadStateFlavorCount; + + // Adjust the message size for the data that it’s actually carrying, which + // may be smaller than the maximum that it can carry. + Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); + } + + // Because the message size has been adjusted, the trailer may not appear in + // its home member variable. This computes the actual address of the trailer. + const mach_msg_trailer_t* Trailer() const { + return MachMessageTrailerFromHeader(&Head); + } + + mach_msg_header_t Head; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[THREAD_STATE_MAX]; + mach_msg_trailer_t trailer; +}; + +// The reply messages for exception_raise_state_identity and +// mach_exception_raise_state_identity are identical. +using MachExceptionRaiseStateIdentityReply = ExceptionRaiseStateIdentityReply; + +// InvalidRequest and BadIDErrorReply are used to test that +// UniversalMachExcServer deals appropriately with messages that it does not +// understand: messages with an unknown Head.msgh_id. + +struct InvalidRequest : public mach_msg_empty_send_t { + explicit InvalidRequest(mach_msg_id_t id) { + memset(this, 0xa5, sizeof(*this)); + header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND); + header.msgh_size = sizeof(*this); + header.msgh_remote_port = kClientRemotePort; + header.msgh_local_port = kServerLocalPort; + header.msgh_id = id; + } +}; + +struct BadIDErrorReply : public mig_reply_error_t { + BadIDErrorReply() { + memset(this, 0x5a, sizeof(*this)); + RetCode = KERN_FAILURE; + } + + void Verify(mach_msg_id_t id) { + EXPECT_EQ(implicit_cast<mach_msg_bits_t>( + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)), + Head.msgh_bits); + EXPECT_EQ(sizeof(*this), Head.msgh_size); + EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); + EXPECT_EQ(kMachPortNull, Head.msgh_local_port); + EXPECT_EQ(id + 100, Head.msgh_id); + EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); + EXPECT_EQ(MIG_BAD_ID, RetCode); + } +}; + +class MockUniversalMachExcServer : public UniversalMachExcServer::Interface { + public: + struct ConstExceptionCodes { + const mach_exception_data_type_t* code; + mach_msg_type_number_t code_count; + }; + struct ThreadStateAndCount { + thread_state_t state; + mach_msg_type_number_t* state_count; + }; + struct ConstThreadStateAndCount { + ConstThreadState state; + mach_msg_type_number_t* state_count; + }; + + // UniversalMachExcServer::Interface: + + // CatchMachException is the method to mock, but it has 13 parameters, and + // gmock can only mock methods with up to 10 parameters. Coalesce some related + // parameters together into structs, and call a mocked method. + virtual kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + const ConstExceptionCodes exception_codes = {code, code_count}; + const ConstThreadStateAndCount old_thread_state = {old_state, + &old_state_count}; + ThreadStateAndCount new_thread_state = {new_state, new_state_count}; + return MockCatchMachException(behavior, + exception_port, + thread, + task, + exception, + &exception_codes, + flavor, + &old_thread_state, + &new_thread_state, + trailer); + } + + MOCK_METHOD10(MockCatchMachException, + kern_return_t(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const ConstExceptionCodes* exception_codes, + thread_state_flavor_t* flavor, + const ConstThreadStateAndCount* old_thread_state, + ThreadStateAndCount* new_thread_state, + const mach_msg_trailer_t* trailer)); +}; + +// Matcher for ConstExceptionCodes, testing that it carries 2 codes matching +// code_0 and code_1. +MATCHER_P2(AreExceptionCodes, code_0, code_1, "") { + if (!arg) { + return false; + } + + if (arg->code_count == 2 && arg->code[0] == code_0 && + arg->code[1] == code_1) { + return true; + } + + *result_listener << "codes ("; + for (size_t index = 0; index < arg->code_count; ++index) { + *result_listener << arg->code[index]; + if (index < arg->code_count - 1) { + *result_listener << ", "; + } + } + *result_listener << ")"; + + return false; +} + +// Matcher for ThreadStateAndCount and ConstThreadStateAndCount, testing that +// *state_count is present and matches the specified value. If 0 is specified +// for the count, |state| must be nullptr (not present), otherwise |state| must +// not be nullptr (present). +MATCHER_P(IsThreadStateAndCount, state_count, "") { + if (!arg) { + return false; + } + if (!arg->state_count) { + *result_listener << "state_count nullptr"; + return false; + } + if (*(arg->state_count) != state_count) { + *result_listener << "*state_count " << *(arg->state_count); + return false; + } + if (state_count) { + if (!arg->state) { + *result_listener << "*state_count " << state_count << ", state nullptr"; + return false; + } + } else { + if (arg->state) { + *result_listener << "*state_count 0, state non-nullptr (" << arg->state + << ")"; + return false; + } + } + return true; +} + +template <typename T> +class ScopedDefaultValue { + public: + explicit ScopedDefaultValue(const T& default_value) { + DefaultValue<T>::Set(default_value); + } + + ~ScopedDefaultValue() { DefaultValue<T>::Clear(); } +}; + +TEST(ExcServerVariants, MockExceptionRaise) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2401)); // There is no constant for this. + + ExceptionRaiseRequest request; + EXPECT_LE(request.Head.msgh_size, + universal_mach_exc_server.MachMessageServerRequestSize()); + + ExceptionRaiseReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + const exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT; + + EXPECT_CALL(server, + MockCatchMachException(kExceptionBehavior, + kServerLocalPort, + kExceptionThreadPort, + kExceptionTaskPort, + kExceptionType, + AreExceptionCodes(kTestExceptonCodes[0], + kTestExceptonCodes[1]), + Pointee(Eq(THREAD_STATE_NONE)), + IsThreadStateAndCount(0u), + IsThreadStateAndCount(0u), + Eq(&request.trailer))) + .WillOnce(Return(KERN_SUCCESS)) + .RetiresOnSaturation(); + + bool destroy_complex_request = false; + EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + EXPECT_TRUE(destroy_complex_request); + + reply.Verify(kExceptionBehavior); +} + +TEST(ExcServerVariants, MockExceptionRaiseState) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2402)); // There is no constant for this. + + ExceptionRaiseStateRequest request; + EXPECT_LE(request.Head.msgh_size, + universal_mach_exc_server.MachMessageServerRequestSize()); + + ExceptionRaiseStateReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE; + + EXPECT_CALL( + server, + MockCatchMachException( + kExceptionBehavior, + kServerLocalPort, + THREAD_NULL, + TASK_NULL, + kExceptionType, + AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]), + Pointee(Eq(kThreadStateFlavor)), + IsThreadStateAndCount(kThreadStateFlavorCount), + IsThreadStateAndCount(arraysize(reply.new_state)), + Eq(request.Trailer()))) + .WillOnce(Return(KERN_SUCCESS)) + .RetiresOnSaturation(); + + bool destroy_complex_request = false; + EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + + // The request wasn’t complex, so nothing got a chance to change the value of + // this variable. + EXPECT_FALSE(destroy_complex_request); + + reply.Verify(kExceptionBehavior); +} + +TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2403)); // There is no constant for this. + + ExceptionRaiseStateIdentityRequest request; + EXPECT_LE(request.Head.msgh_size, + universal_mach_exc_server.MachMessageServerRequestSize()); + + ExceptionRaiseStateIdentityReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY; + + EXPECT_CALL( + server, + MockCatchMachException( + kExceptionBehavior, + kServerLocalPort, + kExceptionThreadPort, + kExceptionTaskPort, + kExceptionType, + AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]), + Pointee(Eq(kThreadStateFlavor)), + IsThreadStateAndCount(kThreadStateFlavorCount), + IsThreadStateAndCount(arraysize(reply.new_state)), + Eq(request.Trailer()))) + .WillOnce(Return(KERN_SUCCESS)) + .RetiresOnSaturation(); + + bool destroy_complex_request = false; + EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + EXPECT_TRUE(destroy_complex_request); + + reply.Verify(kExceptionBehavior); +} + +TEST(ExcServerVariants, MockMachExceptionRaise) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2405)); // There is no constant for this. + + MachExceptionRaiseRequest request; + EXPECT_LE(request.Head.msgh_size, + universal_mach_exc_server.MachMessageServerRequestSize()); + + MachExceptionRaiseReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + const exception_behavior_t kExceptionBehavior = + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES; + + EXPECT_CALL( + server, + MockCatchMachException(kExceptionBehavior, + kServerLocalPort, + kExceptionThreadPort, + kExceptionTaskPort, + kExceptionType, + AreExceptionCodes(kTestMachExceptionCodes[0], + kTestMachExceptionCodes[1]), + Pointee(Eq(THREAD_STATE_NONE)), + IsThreadStateAndCount(0u), + IsThreadStateAndCount(0u), + Eq(&request.trailer))) + .WillOnce(Return(KERN_SUCCESS)) + .RetiresOnSaturation(); + + bool destroy_complex_request = false; + EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + EXPECT_TRUE(destroy_complex_request); + + reply.Verify(kExceptionBehavior); +} + +TEST(ExcServerVariants, MockMachExceptionRaiseState) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2406)); // There is no constant for this. + + MachExceptionRaiseStateRequest request; + EXPECT_LE(request.Head.msgh_size, + universal_mach_exc_server.MachMessageServerRequestSize()); + + MachExceptionRaiseStateReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + const exception_behavior_t kExceptionBehavior = + EXCEPTION_STATE | MACH_EXCEPTION_CODES; + + EXPECT_CALL( + server, + MockCatchMachException(kExceptionBehavior, + kServerLocalPort, + THREAD_NULL, + TASK_NULL, + kExceptionType, + AreExceptionCodes(kTestMachExceptionCodes[0], + kTestMachExceptionCodes[1]), + Pointee(Eq(kThreadStateFlavor)), + IsThreadStateAndCount(kThreadStateFlavorCount), + IsThreadStateAndCount(arraysize(reply.new_state)), + Eq(request.Trailer()))) + .WillOnce(Return(KERN_SUCCESS)) + .RetiresOnSaturation(); + + bool destroy_complex_request = false; + EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + + // The request wasn’t complex, so nothing got a chance to change the value of + // this variable. + EXPECT_FALSE(destroy_complex_request); + + reply.Verify(kExceptionBehavior); +} + +TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2407)); // There is no constant for this. + + MachExceptionRaiseStateIdentityRequest request; + EXPECT_LE(request.Head.msgh_size, + universal_mach_exc_server.MachMessageServerRequestSize()); + + MachExceptionRaiseStateIdentityReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + const exception_behavior_t kExceptionBehavior = + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES; + + EXPECT_CALL( + server, + MockCatchMachException(kExceptionBehavior, + kServerLocalPort, + kExceptionThreadPort, + kExceptionTaskPort, + kExceptionType, + AreExceptionCodes(kTestMachExceptionCodes[0], + kTestMachExceptionCodes[1]), + Pointee(Eq(kThreadStateFlavor)), + IsThreadStateAndCount(kThreadStateFlavorCount), + IsThreadStateAndCount(arraysize(reply.new_state)), + Eq(request.Trailer()))) + .WillOnce(Return(KERN_SUCCESS)) + .RetiresOnSaturation(); + + bool destroy_complex_request = false; + EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + EXPECT_TRUE(destroy_complex_request); + + reply.Verify(kExceptionBehavior); +} + +TEST(ExcServerVariants, MockUnknownID) { + ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + // Make sure that a message with an unknown ID is handled appropriately. + // UniversalMachExcServer should not dispatch the message to + // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply. + + const mach_msg_id_t unknown_ids[] = { + // Reasonable things to check. + -101, + -100, + -99, + -1, + 0, + 1, + 99, + 100, + 101, + + // Invalid IDs right around valid ones. + 2400, + 2404, + 2408, + + // Valid and invalid IDs in the range used for replies, not requests. + 2500, + 2501, + 2502, + 2503, + 2504, + 2505, + 2506, + 2507, + 2508, + }; + + for (size_t index = 0; index < arraysize(unknown_ids); ++index) { + mach_msg_id_t id = unknown_ids[index]; + + SCOPED_TRACE(base::StringPrintf("unknown id %d", id)); + + std::set<mach_msg_id_t> ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_EQ(ids.end(), ids.find(id)); + + InvalidRequest request(id); + EXPECT_LE(sizeof(request), + universal_mach_exc_server.MachMessageServerRequestSize()); + + BadIDErrorReply reply; + EXPECT_LE(sizeof(reply), + universal_mach_exc_server.MachMessageServerReplySize()); + + bool destroy_complex_request = false; + EXPECT_FALSE(universal_mach_exc_server.MachMessageServerFunction( + reinterpret_cast<mach_msg_header_t*>(&request), + reinterpret_cast<mach_msg_header_t*>(&reply), + &destroy_complex_request)); + + // The request wasn’t handled, nothing got a chance to change the value of + // this variable. MachMessageServer would destroy the request if it was + // complex, regardless of what was done to this variable, because the + // return code was not KERN_SUCCESS or MIG_NO_REPLY. + EXPECT_FALSE(destroy_complex_request); + + reply.Verify(id); + } +} + +TEST(ExcServerVariants, MachMessageServerRequestIDs) { + std::set<mach_msg_id_t> expect_request_ids; + + // There are no constants for these. + expect_request_ids.insert(2401); + expect_request_ids.insert(2402); + expect_request_ids.insert(2403); + expect_request_ids.insert(2405); + expect_request_ids.insert(2406); + expect_request_ids.insert(2407); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + EXPECT_EQ(expect_request_ids, + universal_mach_exc_server.MachMessageServerRequestIDs()); +} + +class TestExcServerVariants : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + TestExcServerVariants(exception_behavior_t behavior, + thread_state_flavor_t flavor, + mach_msg_type_number_t state_count) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + behavior_(behavior), + flavor_(flavor), + state_count_(state_count), + handled_(false) {} + + // UniversalMachExcServer::Interface: + + virtual kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + EXPECT_FALSE(handled_); + handled_ = true; + + EXPECT_EQ(behavior_, behavior); + + EXPECT_EQ(LocalPort(), exception_port); + + if (ExceptionBehaviorHasIdentity(behavior)) { + EXPECT_NE(THREAD_NULL, thread); + EXPECT_EQ(ChildTask(), task); + } else { + EXPECT_EQ(THREAD_NULL, thread); + EXPECT_EQ(TASK_NULL, task); + } + + EXPECT_EQ(EXC_CRASH, exception); + EXPECT_EQ(2u, code_count); + + // The exception and code_count checks above would ideally use ASSERT_EQ so + // that the next conditional would not be necessary, but ASSERT_* requires a + // function returning type void, and the interface dictates otherwise here. + if (exception == EXC_CRASH && code_count >= 1) { + int signal; + ExcCrashRecoverOriginalException(code[0], nullptr, &signal); + SetExpectedChildTermination(kTerminationSignal, signal); + } + + const bool has_state = ExceptionBehaviorHasState(behavior); + if (has_state) { + EXPECT_EQ(flavor_, *flavor); + EXPECT_EQ(state_count_, old_state_count); + EXPECT_NE(nullptr, old_state); + EXPECT_EQ(implicit_cast<mach_msg_type_number_t>(THREAD_STATE_MAX), + *new_state_count); + EXPECT_NE(nullptr, new_state); + } else { + EXPECT_EQ(THREAD_STATE_NONE, *flavor); + EXPECT_EQ(0u, old_state_count); + EXPECT_EQ(nullptr, old_state); + EXPECT_EQ(0u, *new_state_count); + EXPECT_EQ(nullptr, new_state); + } + + EXPECT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0), + trailer->msgh_trailer_type); + EXPECT_EQ(REQUESTED_TRAILER_SIZE(kMachMessageOptions), + trailer->msgh_trailer_size); + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + UniversalMachExcServer universal_mach_exc_server(this); + + kern_return_t kr = + MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + kMachMessageOptions, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "MachMessageServer::Run"); + + EXPECT_TRUE(handled_); + } + + void MachMultiprocessChild() override { + // Set the parent as the exception handler for EXC_CRASH. + kern_return_t kr = task_set_exception_ports( + mach_task_self(), EXC_MASK_CRASH, RemotePort(), behavior_, flavor_); + ASSERT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "task_set_exception_ports"); + + // Now crash. + __builtin_trap(); + } + + exception_behavior_t behavior_; + thread_state_flavor_t flavor_; + mach_msg_type_number_t state_count_; + bool handled_; + + static const mach_msg_option_t kMachMessageOptions = + MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0); + + DISALLOW_COPY_AND_ASSIGN(TestExcServerVariants); +}; + +TEST(ExcServerVariants, ExceptionRaise) { + TestExcServerVariants test_exc_server_variants( + EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0); + test_exc_server_variants.Run(); +} + +TEST(ExcServerVariants, ExceptionRaiseState) { + TestExcServerVariants test_exc_server_variants( + EXCEPTION_STATE, MACHINE_THREAD_STATE, MACHINE_THREAD_STATE_COUNT); + test_exc_server_variants.Run(); +} + +TEST(ExcServerVariants, ExceptionRaiseStateIdentity) { + TestExcServerVariants test_exc_server_variants(EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE, + MACHINE_THREAD_STATE_COUNT); + test_exc_server_variants.Run(); +} + +TEST(ExcServerVariants, MachExceptionRaise) { + TestExcServerVariants test_exc_server_variants( + MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0); + test_exc_server_variants.Run(); +} + +TEST(ExcServerVariants, MachExceptionRaiseState) { + TestExcServerVariants test_exc_server_variants( + MACH_EXCEPTION_CODES | EXCEPTION_STATE, + MACHINE_THREAD_STATE, + MACHINE_THREAD_STATE_COUNT); + test_exc_server_variants.Run(); +} + +TEST(ExcServerVariants, MachExceptionRaiseStateIdentity) { + TestExcServerVariants test_exc_server_variants( + MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE, + MACHINE_THREAD_STATE_COUNT); + test_exc_server_variants.Run(); +} + +TEST(ExcServerVariants, ThreadStates) { + // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of + // the other thread state flavors that are expected to work. + + struct TestData { + thread_state_flavor_t flavor; + mach_msg_type_number_t count; + }; + const TestData test_data[] = { +#if defined(ARCH_CPU_X86_FAMILY) + // For the x86 family, exception handlers can only properly receive the + // thread, float, and exception state flavors. There’s a bug in the kernel + // that causes it to call thread_getstatus() (a wrapper for the more + // familiar thread_get_state()) with an incorrect state buffer size + // parameter when delivering an exception. 10.9.4 + // xnu-2422.110.17/osfmk/kern/exception.c exception_deliver() uses the + // _MachineStateCount[] array indexed by the flavor number to obtain the + // buffer size. 10.9.4 xnu-2422.110.17/osfmk/i386/pcb.c contains the + // definition of this array for the x86 family. The slots corresponding to + // thread, float, and exception state flavors in both native-width (32- + // and 64-bit) and universal are correct, but the remaining elements in + // the array are not. This includes elements that would correspond to + // debug and AVX state flavors, so these cannot be tested here. + // + // When machine_thread_get_state() (the machine-specific implementation of + // thread_get_state()) encounters an undersized buffer as reported by the + // buffer size parameter, it returns KERN_INVALID_ARGUMENT, which causes + // exception_deliver() to not actually deliver the exception and instead + // return that error code to exception_triage() as well. + // + // This bug is filed as radar 18312067. + // + // Additionaly, the AVX state flavors are also not tested because they’re + // not available on all CPUs and OS versions. +#if defined(ARCH_CPU_X86) + {x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT}, + {x86_FLOAT_STATE32, x86_FLOAT_STATE32_COUNT}, + {x86_EXCEPTION_STATE32, x86_EXCEPTION_STATE32_COUNT}, +#endif +#if defined(ARCH_CPU_X86_64) + {x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT}, + {x86_FLOAT_STATE64, x86_FLOAT_STATE64_COUNT}, + {x86_EXCEPTION_STATE64, x86_EXCEPTION_STATE64_COUNT}, +#endif + {x86_THREAD_STATE, x86_THREAD_STATE_COUNT}, + {x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT}, + {x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT}, +#else +#error Port this test to your CPU architecture. +#endif + }; + + for (size_t index = 0; index < arraysize(test_data); ++index) { + const TestData& test = test_data[index]; + SCOPED_TRACE( + base::StringPrintf("index %zu, flavor %d", index, test.flavor)); + + TestExcServerVariants test_exc_server_variants( + MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, + test.flavor, + test.count); + test_exc_server_variants.Run(); + } +} + +TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) { + const kern_return_t prefer_not_set_thread_state = + MacOSXMinorVersion() < 11 ? MACH_RCV_PORT_DIED : KERN_SUCCESS; + + struct TestData { + exception_type_t exception; + exception_behavior_t behavior; + bool set_thread_state; + kern_return_t kr; + }; + const TestData kTestData[] = { + {EXC_CRASH, EXCEPTION_DEFAULT, false, KERN_SUCCESS}, + {EXC_CRASH, EXCEPTION_STATE, false, prefer_not_set_thread_state}, + {EXC_CRASH, EXCEPTION_STATE_IDENTITY, false, prefer_not_set_thread_state}, + {EXC_CRASH, kMachExceptionCodes | EXCEPTION_DEFAULT, false, KERN_SUCCESS}, + {EXC_CRASH, + kMachExceptionCodes | EXCEPTION_STATE, + false, + prefer_not_set_thread_state}, + {EXC_CRASH, + kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, + false, + prefer_not_set_thread_state}, + {EXC_CRASH, EXCEPTION_DEFAULT, true, KERN_SUCCESS}, + {EXC_CRASH, EXCEPTION_STATE, true, KERN_SUCCESS}, + {EXC_CRASH, EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS}, + {EXC_CRASH, kMachExceptionCodes | EXCEPTION_DEFAULT, true, KERN_SUCCESS}, + {EXC_CRASH, kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS}, + {EXC_CRASH, + kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, + true, + KERN_SUCCESS}, + {EXC_BAD_ACCESS, EXCEPTION_DEFAULT, false, KERN_SUCCESS}, + {EXC_BAD_INSTRUCTION, EXCEPTION_STATE, false, MACH_RCV_PORT_DIED}, + {EXC_ARITHMETIC, EXCEPTION_STATE_IDENTITY, false, MACH_RCV_PORT_DIED}, + {EXC_EMULATION, + kMachExceptionCodes | EXCEPTION_DEFAULT, + false, + KERN_SUCCESS}, + {EXC_SOFTWARE, + kMachExceptionCodes | EXCEPTION_STATE, + false, + MACH_RCV_PORT_DIED}, + {EXC_BREAKPOINT, + kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, + false, + MACH_RCV_PORT_DIED}, + {EXC_SYSCALL, EXCEPTION_DEFAULT, true, KERN_SUCCESS}, + {EXC_MACH_SYSCALL, EXCEPTION_STATE, true, KERN_SUCCESS}, + {EXC_RPC_ALERT, EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS}, + {EXC_RESOURCE, + kMachExceptionCodes | EXCEPTION_DEFAULT, + true, + KERN_SUCCESS}, + {EXC_GUARD, kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS}, + {EXC_CORPSE_NOTIFY, + kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, + true, + KERN_SUCCESS}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& test_data = kTestData[index]; + SCOPED_TRACE( + base::StringPrintf("index %zu, behavior %d, set_thread_state %s", + index, + test_data.behavior, + test_data.set_thread_state ? "true" : "false")); + + EXPECT_EQ(test_data.kr, + ExcServerSuccessfulReturnValue(test_data.exception, + test_data.behavior, + test_data.set_thread_state)); + } +} + +TEST(ExcServerVariants, ExcServerCopyState) { + const natural_t old_state[] = {1, 2, 3, 4, 5}; + natural_t new_state[10] = {}; + + const mach_msg_type_number_t old_state_count = arraysize(old_state); + mach_msg_type_number_t new_state_count = arraysize(new_state); + + // EXCEPTION_DEFAULT (with or without MACH_EXCEPTION_CODES) is not + // state-carrying. new_state and new_state_count should be untouched. + ExcServerCopyState(EXCEPTION_DEFAULT, + old_state, + old_state_count, + new_state, + &new_state_count); + EXPECT_EQ(arraysize(new_state), new_state_count); + for (size_t i = 0; i < arraysize(new_state); ++i) { + EXPECT_EQ(0u, new_state[i]) << "i " << i; + } + + ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, + old_state, + old_state_count, + new_state, + &new_state_count); + EXPECT_EQ(arraysize(new_state), new_state_count); + for (size_t i = 0; i < arraysize(new_state); ++i) { + EXPECT_EQ(0u, new_state[i]) << "i " << i; + } + + // This is a state-carrying exception where old_state_count is small. + mach_msg_type_number_t copy_limit = 2; + ExcServerCopyState( + EXCEPTION_STATE, old_state, copy_limit, new_state, &new_state_count); + EXPECT_EQ(copy_limit, new_state_count); + for (size_t i = 0; i < copy_limit; ++i) { + EXPECT_EQ(old_state[i], new_state[i]) << "i " << i; + } + for (size_t i = copy_limit; i < arraysize(new_state); ++i) { + EXPECT_EQ(0u, new_state[i]) << "i " << i; + } + + // This is a state-carrying exception where new_state_count is small. + copy_limit = 3; + new_state_count = copy_limit; + ExcServerCopyState(EXCEPTION_STATE_IDENTITY, + old_state, + old_state_count, + new_state, + &new_state_count); + EXPECT_EQ(copy_limit, new_state_count); + for (size_t i = 0; i < copy_limit; ++i) { + EXPECT_EQ(old_state[i], new_state[i]) << "i " << i; + } + for (size_t i = copy_limit; i < arraysize(new_state); ++i) { + EXPECT_EQ(0u, new_state[i]) << "i " << i; + } + + // This is a state-carrying exception where all of old_state is copied to + // new_state, which is large enough to receive it and then some. + new_state_count = arraysize(new_state); + ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, + old_state, + old_state_count, + new_state, + &new_state_count); + EXPECT_EQ(old_state_count, new_state_count); + for (size_t i = 0; i < arraysize(old_state); ++i) { + EXPECT_EQ(old_state[i], new_state[i]) << "i " << i; + } + for (size_t i = arraysize(old_state); i < arraysize(new_state); ++i) { + EXPECT_EQ(0u, new_state[i]) << "i " << i; + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc b/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc new file mode 100644 index 0000000..eb2a80c --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc
@@ -0,0 +1,39 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exception_behaviors.h" + +namespace crashpad { + +bool ExceptionBehaviorHasState(exception_behavior_t behavior) { + const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior); + return basic_behavior == EXCEPTION_STATE || + basic_behavior == EXCEPTION_STATE_IDENTITY; +} + +bool ExceptionBehaviorHasIdentity(exception_behavior_t behavior) { + const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior); + return basic_behavior == EXCEPTION_DEFAULT || + basic_behavior == EXCEPTION_STATE_IDENTITY; +} + +bool ExceptionBehaviorHasMachExceptionCodes(exception_behavior_t behavior) { + return (behavior & MACH_EXCEPTION_CODES) != 0; +} + +exception_behavior_t ExceptionBehaviorBasic(exception_behavior_t behavior) { + return behavior & ~MACH_EXCEPTION_CODES; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exception_behaviors.h b/third_party/crashpad/crashpad/util/mach/exception_behaviors.h new file mode 100644 index 0000000..14b5448 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_behaviors.h
@@ -0,0 +1,94 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_ +#define CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_ + +#include <mach/mach.h> + +namespace crashpad { + +//! \brief Determines whether \a behavior indicates an exception behavior that +//! carries thread state information. +//! +//! When this function returns `true`, an exception message of \a behavior will +//! carry thread state information. Its \a flavor, \a old_state, \a +//! old_state_count, \a new_state, and \a new_state_count fields will be valid. +//! When this function returns `false`, these fields will not be valid. +//! +//! Exception behaviors that carry thread state information are +//! `EXCEPTION_STATE` and `EXCEPTION_STATE_IDENTITY`. `MACH_EXCEPTION_CODES` may +//! also be set. These behaviors correspond to `exception_raise_state()`, +//! `exception_raise_state_identity()`, `mach_exception_raise_state()`, and +//! `mach_exception_raise_state_identity()`. +//! +//! \param[in] behavior An exception behavior value. +//! +//! \return `true` if \a behavior is `EXCEPTION_STATE` or +//! `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` also +//! set. +bool ExceptionBehaviorHasState(exception_behavior_t behavior); + +//! \brief Determines whether \a behavior indicates an exception behavior that +//! carries thread and task identities. +//! +//! When this function returns `true`, an exception message of \a behavior will +//! carry thread and task identities in the form of send rights to the thread +//! and task ports. Its \a thread and \a task fields will be valid. When this +//! function returns `false`, these fields will not be valid. +//! +//! Exception behaviors that carry thread and task identity information are +//! `EXCEPTION_DEFAULT` and `EXCEPTION_STATE_IDENTITY`. `MACH_EXCEPTION_CODES` +//! may also be set. These behaviors correspond to `exception_raise()`, +//! `exception_raise_state_identity()`, `mach_exception_raise()`, and +//! `mach_exception_raise_state_identity()`. +//! +//! \param[in] behavior An exception behavior value. +//! +//! \return `true` if \a behavior is `EXCEPTION_DEFAULT` or +//! `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` also +//! set. +bool ExceptionBehaviorHasIdentity(exception_behavior_t behavior); + +//! \brief Determines whether \a behavior indicates an exception behavior that +//! carries 64-bit exception codes (“Mach exception codes”). +//! +//! When this function returns `true`, an exception message of \a behavior will +//! carry 64-bit exception codes of type `mach_exception_code_t` in its \a code +//! field. When this function returns `false`, the exception message will carry +//! 32-bit exception codes of type `exception_data_type_t` in its \a code field. +//! +//! Exception behaviors that carry 64-bit exception codes are those that have +//! `MACH_EXCEPTION_CODES` set. These behaviors correspond to +//! `mach_exception_raise()`, `mach_exception_raise_state()`, and +//! `mach_exception_raise_state_identity()`. +//! +//! \param[in] behavior An exception behavior value. +//! +//! \return `true` if `MACH_EXCEPTION_CODES` is set in \a behavior. +bool ExceptionBehaviorHasMachExceptionCodes(exception_behavior_t behavior); + +//! \brief Returns the basic behavior value of \a behavior, its value without +//! `MACH_EXCEPTION_CODES` set. +//! +//! \param[in] behavior An exception behavior value. +//! +//! \return `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or +//! `EXCEPTION_STATE_IDENTITY`, assuming \a behavior was a correct exception +//! behavior value. +exception_behavior_t ExceptionBehaviorBasic(exception_behavior_t behavior); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_
diff --git a/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc b/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc new file mode 100644 index 0000000..cbde600 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc
@@ -0,0 +1,72 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exception_behaviors.h" + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ExceptionBehaviors, ExceptionBehaviors) { + struct TestData { + exception_behavior_t behavior; + bool state; + bool identity; + bool mach_exception_codes; + exception_behavior_t basic_behavior; + }; + const TestData kTestData[] = { + {EXCEPTION_DEFAULT, false, true, false, EXCEPTION_DEFAULT}, + {EXCEPTION_STATE, true, false, false, EXCEPTION_STATE}, + {EXCEPTION_STATE_IDENTITY, true, true, false, EXCEPTION_STATE_IDENTITY}, + {kMachExceptionCodes | EXCEPTION_DEFAULT, + false, + true, + true, + EXCEPTION_DEFAULT}, + {kMachExceptionCodes | EXCEPTION_STATE, + true, + false, + true, + EXCEPTION_STATE}, + {kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, + true, + true, + true, + EXCEPTION_STATE_IDENTITY}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& test_data = kTestData[index]; + SCOPED_TRACE(base::StringPrintf( + "index %zu, behavior %d", index, test_data.behavior)); + + EXPECT_EQ(test_data.state, ExceptionBehaviorHasState(test_data.behavior)); + EXPECT_EQ(test_data.identity, + ExceptionBehaviorHasIdentity(test_data.behavior)); + EXPECT_EQ(test_data.mach_exception_codes, + ExceptionBehaviorHasMachExceptionCodes(test_data.behavior)); + EXPECT_EQ(test_data.basic_behavior, + ExceptionBehaviorBasic(test_data.behavior)); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exception_ports.cc b/third_party/crashpad/crashpad/util/mach/exception_ports.cc new file mode 100644 index 0000000..ef535b4 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_ports.cc
@@ -0,0 +1,155 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exception_ports.h" + +#include "base/logging.h" +#include "base/mac/mach_logging.h" + +namespace crashpad { + +ExceptionPorts::ExceptionHandlerVector::ExceptionHandlerVector() + : vector_() { +} + +ExceptionPorts::ExceptionHandlerVector::~ExceptionHandlerVector() { + Deallocate(); +} + +void ExceptionPorts::ExceptionHandlerVector::clear() { + Deallocate(); + vector_.clear(); +} + +void ExceptionPorts::ExceptionHandlerVector::Deallocate() { + for (ExceptionHandler& exception_handler : vector_) { + if (exception_handler.port != MACH_PORT_NULL) { + kern_return_t kr = + mach_port_deallocate(mach_task_self(), exception_handler.port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; + } + } +} + +ExceptionPorts::ExceptionPorts(TargetType target_type, mach_port_t target_port) + : target_port_(target_port), dealloc_target_port_(false) { + switch (target_type) { + case kTargetTypeHost: + get_exception_ports_ = host_get_exception_ports; + set_exception_ports_ = host_set_exception_ports; + target_name_ = "host"; + if (target_port_ == HOST_NULL) { + target_port_ = mach_host_self(); + dealloc_target_port_ = true; + } + break; + + case kTargetTypeTask: + get_exception_ports_ = task_get_exception_ports; + set_exception_ports_ = task_set_exception_ports; + target_name_ = "task"; + if (target_port_ == TASK_NULL) { + target_port_ = mach_task_self(); + // Don’t deallocate mach_task_self(). + } + break; + + case kTargetTypeThread: + get_exception_ports_ = thread_get_exception_ports; + set_exception_ports_ = thread_set_exception_ports; + target_name_ = "thread"; + if (target_port_ == THREAD_NULL) { + target_port_ = mach_thread_self(); + dealloc_target_port_ = true; + } + break; + + default: + NOTREACHED(); + get_exception_ports_ = nullptr; + set_exception_ports_ = nullptr; + target_name_ = nullptr; + target_port_ = MACH_PORT_NULL; + break; + } +} + +ExceptionPorts::~ExceptionPorts() { + if (dealloc_target_port_) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), target_port_); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; + } +} + +bool ExceptionPorts::GetExceptionPorts(exception_mask_t mask, + ExceptionHandlerVector* handlers) const { + // <mach/mach_types.defs> says that these arrays have room for 32 elements, + // despite EXC_TYPES_COUNT only being as low as 11 (in the 10.6 SDK), and + // later operating system versions have defined more exception types. The + // generated task_get_exception_ports() in taskUser.c expects there to be room + // for 32. + const int kMaxPorts = 32; + + // task_get_exception_ports() doesn’t actually use the initial value of + // handler_count, but 10.9.4 + // xnu-2422.110.17/osfmk/man/task_get_exception_ports.html says it does. Humor + // the documentation. + mach_msg_type_number_t handler_count = kMaxPorts; + + exception_mask_t masks[kMaxPorts]; + exception_handler_t ports[kMaxPorts]; + exception_behavior_t behaviors[kMaxPorts]; + thread_state_flavor_t flavors[kMaxPorts]; + + kern_return_t kr = get_exception_ports_( + target_port_, mask, masks, &handler_count, ports, behaviors, flavors); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << TargetTypeName() << "_get_exception_ports"; + return false; + } + + handlers->clear(); + for (mach_msg_type_number_t index = 0; index < handler_count; ++index) { + if (ports[index] != MACH_PORT_NULL) { + ExceptionHandler handler; + handler.mask = masks[index]; + handler.port = ports[index]; + handler.behavior = behaviors[index]; + handler.flavor = flavors[index]; + handlers->push_back(handler); + } + } + + return true; +} + +bool ExceptionPorts::SetExceptionPort(exception_mask_t mask, + exception_handler_t port, + exception_behavior_t behavior, + thread_state_flavor_t flavor) const { + kern_return_t kr = + set_exception_ports_(target_port_, mask, port, behavior, flavor); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << TargetTypeName() << "_set_exception_ports"; + return false; + } + + return true; +} + +const char* ExceptionPorts::TargetTypeName() const { + return target_name_; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exception_ports.h b/third_party/crashpad/crashpad/util/mach/exception_ports.h new file mode 100644 index 0000000..7f23c5e5 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_ports.h
@@ -0,0 +1,218 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_ +#define CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_ + +#include <mach/mach.h> + +#include <vector> + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief A better interface to `*_get_exception_ports()` and +//! `*_set_exception_ports()`. +//! +//! The same generic interface can be used to operate on host, task, and thread +//! exception ports. The “get” interface is superior to the system’s native +//! interface because it keeps related data about a single exception handler +//! together in one struct, rather than separating it into four parallel arrays. +class ExceptionPorts { + public: + //! \brief Various entities which can have their own exception ports set. + enum TargetType { + //! \brief The host exception target. + //! + //! `host_get_exception_ports()` and `host_set_exception_ports()` will be + //! used. If no target port is explicitly provided, `mach_host_self()` will + //! be used as the target port. `mach_host_self()` is the only target port + //! for this type that is expected to function properly. + //! + //! \note Operations on this target type are not expected to succeed as + //! non-root, because `mach_host_self()` doesn’t return the privileged + //! `host_priv` port to non-root users, and this is the target port + //! that’s required for `host_get_exception_ports()` and + //! `host_set_exception_ports()`. + kTargetTypeHost = 0, + + //! \brief A task exception target. + //! + //! `task_get_exception_ports()` and `task_set_exception_ports()` will be + //! used. If no target port is explicitly provided, `mach_task_self()` will + //! be used as the target port. + kTargetTypeTask, + + //! \brief A thread exception target. + //! + //! `thread_get_exception_ports()` and `thread_set_exception_ports()` will + //! be used. If no target port is explicitly provided, `mach_thread_self()` + //! will be used as the target port. + kTargetTypeThread, + }; + + //! \brief Information about a registered exception handler. + struct ExceptionHandler { + //! \brief A mask specifying the exception types to direct to \a port, + //! containing `EXC_MASK_*` values. + exception_mask_t mask; + + //! \brief A send right to a Mach port that will handle exceptions of the + //! types indicated in \a mask. + exception_handler_t port; + + //! \brief The “behavior” that the exception handler at \a port implements: + //! `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or + //! `EXCEPTION_STATE_IDENTITY`, possibly combined with + //! `MACH_EXCEPTION_CODES`. + exception_behavior_t behavior; + + //! \brief The thread state flavor that the exception handler at \a port + //! will receive and possibly modify. This member has no effect for \a + //! \a behavior values that indicate a “default” behavior. + thread_state_flavor_t flavor; + }; + + //! \brief Wraps `std::vector<ExceptionHandler>`, providing proper cleanup of + //! the send rights contained in each element’s ExceptionHandler::port. + //! + //! Upon destruction or clear(), an object of this class will deallocate all + //! send rights it contains. Otherwise, it is an interface-compatible drop-in + //! replacement for `std::vector<ExceptionHandler>`. Note that non-`const` + //! mutators are not provided to avoid accidental Mach right leaks. + class ExceptionHandlerVector { + public: + using VectorType = std::vector<ExceptionHandler>; + + ExceptionHandlerVector(); + ~ExceptionHandlerVector(); + + VectorType::const_iterator begin() const { return vector_.begin(); } + VectorType::const_iterator end() const { return vector_.end(); } + VectorType::size_type size() const { return vector_.size(); } + bool empty() const { return vector_.empty(); } + VectorType::const_reference operator[](VectorType::size_type index) const { + return vector_[index]; + } + void push_back(VectorType::value_type& value) { vector_.push_back(value); } + void clear(); + + private: + void Deallocate(); + + VectorType vector_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerVector); + }; + + //! \brief Constructs an interface object to get or set exception ports on a + //! host, task, or thread port. + //! + //! \param[in] target_type The type of target on which the exception ports are + //! to be get or set: #kTargetTypeHost, #kTargetTypeTask, or or + //! #kTargetTypeThread. The correct functions for + //! `*_get_exception_ports()` and `*_set_exception_ports()` will be used. + //! \param[in] target_port The target on which to call + //! `*_get_exception_ports()` or `*_set_exception_ports()`. The target + //! port must be a send right to a port of the type specified in \a + //! target_type. In this case, ownership of \a target_port is not given to + //! the new ExceptionPorts object. \a target_port may also be + //! `HOST_NULL`, `TASK_NULL`, or `THREAD_NULL`, in which case + //! `mach_host_self()`, `mach_task_self()`, or `mach_thread_self()` will + //! be used as the target port depending on the value of \a target_type. + //! In this case, ownership of the target port will be managed + //! appropriately for \a target_type. + ExceptionPorts(TargetType target_type, mach_port_t target_port); + + ~ExceptionPorts(); + + //! \brief Calls `*_get_exception_ports()` on the target. + //! + //! \param[in] mask The exception mask, containing the `EXC_MASK_*` values to + //! be looked up and returned in \a handlers. + //! \param[out] handlers The exception handlers registered for \a target_port + //! to handle exceptions indicated in \a mask. If no execption port is + //! registered for a bit in \a mask, \a handlers will not contain an entry + //! corresponding to that bit. This is a departure from the + //! `*_get_exception_ports()` functions, which may return a handler whose + //! port is set to `EXCEPTION_PORT_NULL` in this case. On failure, this + //! argument is untouched. + //! + //! \return `true` if `*_get_exception_ports()` returned `KERN_SUCCESS`, with + //! \a handlers set appropriately. `false` otherwise, with an appropriate + //! message logged. + bool GetExceptionPorts(exception_mask_t mask, + ExceptionHandlerVector* handlers) const; + + //! \brief Calls `*_set_exception_ports()` on the target. + //! + //! \param[in] mask A mask specifying the exception types to direct to \a + //! port, containing `EXC_MASK_*` values. + //! \param[in] port A send right to a Mach port that will handle exceptions + //! sustained by \a target_port of the types indicated in \a mask. The + //! send right is copied, not consumed, by this call. + //! \param[in] behavior The “behavior” that the exception handler at \a port + //! implements: `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or + //! `EXCEPTION_STATE_IDENTITY`, possibly combined with + //! `MACH_EXCEPTION_CODES`. + //! \param[in] flavor The thread state flavor that the exception handler at \a + //! port expects to receive and possibly modify. This argument has no + //! effect for \a behavior values that indicate a “default” behavior. + //! + //! \return `true` if `*_set_exception_ports()` returned `KERN_SUCCESS`. + //! `false` otherwise, with an appropriate message logged. + bool SetExceptionPort(exception_mask_t mask, + exception_handler_t port, + exception_behavior_t behavior, + thread_state_flavor_t flavor) const; + + //! \brief Returns a string identifying the target type. + //! + //! \return `"host"`, `"task"`, or `"thread"`, as appropriate. + const char* TargetTypeName() const; + + private: + using GetExceptionPortsType = kern_return_t(*)(mach_port_t, + exception_mask_t, + exception_mask_array_t, + mach_msg_type_number_t*, + exception_handler_array_t, + exception_behavior_array_t, + exception_flavor_array_t); + + using SetExceptionPortsType = kern_return_t(*)(mach_port_t, + exception_mask_t, + exception_handler_t, + exception_behavior_t, + thread_state_flavor_t); + + GetExceptionPortsType get_exception_ports_; + SetExceptionPortsType set_exception_ports_; + const char* target_name_; + mach_port_t target_port_; + + // If true, target_port_ will be deallocated in the destructor. This will + // always be false when the user provides a non-null target_port to the + // constructor. It will also be false when target_type is kTargetTypeTask, + // even with a TASK_NULL target_port, because it is incorrect to deallocate + // the result of mach_task_self(). + bool dealloc_target_port_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionPorts); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
diff --git a/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc b/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc new file mode 100644 index 0000000..e6e1d56dc --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc
@@ -0,0 +1,612 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exception_ports.h" + +#include <mach/mach.h> +#include <pthread.h> +#include <signal.h> +#include <unistd.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/file/file_io.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_types.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/misc/scoped_forbid_return.h" +#include "util/synchronization/semaphore.h" + +namespace crashpad { +namespace test { +namespace { + +// Calls GetExceptionPorts() on its |exception_ports| argument to look up the +// EXC_MASK_CRASH handler. If |expect_port| is not MACH_PORT_NULL, it expects to +// find a handler for this mask whose port matches |expect_port| and whose +// behavior matches |expect_behavior| exactly. In this case, if +// |expect_behavior| is a state-carrying behavior, the looked-up thread state +// flavor is expected to be MACHINE_THREAD_STATE, otherwise, it is expected to +// be THREAD_STATE_NONE. If |expect_port| is MACH_PORT_NULL, no handler for +// EXC_MASK_CRASH is expected to be found. +// +// A second GetExceptionPorts() lookup is also performed on a wider exception +// mask, EXC_MASK_ALL | EXC_MASK_CRASH. The EXC_MASK_CRASH handler’s existence +// and properties from this second lookup are validated in the same way. +// +// This function uses gtest EXPECT_* and ASSERT_* macros to perform its +// validation. +void TestGetExceptionPorts(const ExceptionPorts& exception_ports, + mach_port_t expect_port, + exception_behavior_t expect_behavior) { + const exception_mask_t kExceptionMask = EXC_MASK_CRASH; + + thread_state_flavor_t expect_flavor = (expect_behavior == EXCEPTION_DEFAULT) + ? THREAD_STATE_NONE + : MACHINE_THREAD_STATE; + + ExceptionPorts::ExceptionHandlerVector crash_handler; + ASSERT_TRUE( + exception_ports.GetExceptionPorts(kExceptionMask, &crash_handler)); + + if (expect_port != MACH_PORT_NULL) { + ASSERT_EQ(1u, crash_handler.size()); + + EXPECT_EQ(kExceptionMask, crash_handler[0].mask); + EXPECT_EQ(expect_port, crash_handler[0].port); + EXPECT_EQ(expect_behavior, crash_handler[0].behavior); + EXPECT_EQ(expect_flavor, crash_handler[0].flavor); + } else { + EXPECT_TRUE(crash_handler.empty()); + } + + ExceptionPorts::ExceptionHandlerVector handlers; + ASSERT_TRUE(exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers)); + + EXPECT_GE(handlers.size(), crash_handler.size()); + bool found = false; + for (const ExceptionPorts::ExceptionHandler& handler : handlers) { + if ((handler.mask & kExceptionMask) != 0) { + EXPECT_FALSE(found); + found = true; + EXPECT_EQ(expect_port, handler.port); + EXPECT_EQ(expect_behavior, handler.behavior); + EXPECT_EQ(expect_flavor, handler.flavor); + } + } + + if (expect_port != MACH_PORT_NULL) { + EXPECT_TRUE(found); + } else { + EXPECT_FALSE(found); + } +} + +class TestExceptionPorts : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + // Which entities to set exception ports for. + enum SetOn { + kSetOnTaskOnly = 0, + kSetOnTaskAndThreads, + }; + + // Where to call ExceptionPorts::SetExceptionPort() from. + enum SetType { + // Call it from the child process on itself. + kSetInProcess = 0, + + // Call it from the parent process on the child. + kSetOutOfProcess, + }; + + // Which thread in the child process is expected to crash. + enum WhoCrashes { + kNobodyCrashes = 0, + kMainThreadCrashes, + kOtherThreadCrashes, + }; + + TestExceptionPorts(SetOn set_on, SetType set_type, WhoCrashes who_crashes) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + set_on_(set_on), + set_type_(set_type), + who_crashes_(who_crashes), + handled_(false) {} + + SetOn set_on() const { return set_on_; } + SetType set_type() const { return set_type_; } + WhoCrashes who_crashes() const { return who_crashes_; } + + // UniversalMachExcServer::Interface: + + virtual kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + EXPECT_FALSE(handled_); + handled_ = true; + + // To be able to distinguish between which handler was actually triggered, + // the different handlers are registered with different behavior values. + exception_behavior_t expect_behavior; + if (set_on_ == kSetOnTaskOnly) { + expect_behavior = EXCEPTION_DEFAULT; + } else if (who_crashes_ == kMainThreadCrashes) { + expect_behavior = EXCEPTION_STATE; + } else if (who_crashes_ == kOtherThreadCrashes) { + expect_behavior = EXCEPTION_STATE_IDENTITY; + } else { + NOTREACHED(); + expect_behavior = 0; + } + + EXPECT_EQ(expect_behavior, behavior); + + EXPECT_EQ(LocalPort(), exception_port); + + EXPECT_EQ(EXC_CRASH, exception); + EXPECT_EQ(2u, code_count); + + // The exception and code_count checks above would ideally use ASSERT_EQ so + // that the next conditional would not be necessary, but ASSERT_* requires a + // function returning type void, and the interface dictates otherwise here. + if (exception == EXC_CRASH && code_count >= 1) { + int signal; + ExcCrashRecoverOriginalException(code[0], nullptr, &signal); + + // The child crashed with __builtin_trap(), which shows up as SIGILL. + EXPECT_EQ(SIGILL, signal); + + SetExpectedChildTermination(kTerminationSignal, signal); + } + + EXPECT_EQ(0, AuditPIDFromMachMessageTrailer(trailer)); + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + private: + class Child { + public: + explicit Child(TestExceptionPorts* test_exception_ports) + : test_exception_ports_(test_exception_ports), + thread_(), + init_semaphore_(0), + crash_semaphore_(0) {} + + ~Child() {} + + void Run() { + ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask, + TASK_NULL); + ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread, + THREAD_NULL); + + mach_port_t remote_port = test_exception_ports_->RemotePort(); + + // Set the task’s and this thread’s exception ports, if appropriate. + if (test_exception_ports_->set_type() == kSetInProcess) { + ASSERT_TRUE(self_task_ports.SetExceptionPort( + EXC_MASK_CRASH, remote_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE)); + + if (test_exception_ports_->set_on() == kSetOnTaskAndThreads) { + ASSERT_TRUE(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH, + remote_port, + EXCEPTION_STATE, + MACHINE_THREAD_STATE)); + } + } + + int rv_int = pthread_create(&thread_, nullptr, ThreadMainThunk, this); + ASSERT_EQ(0, rv_int); + + // Wait for the new thread to be ready. + init_semaphore_.Wait(); + + // Tell the parent process that everything is set up. + char c = '\0'; + CheckedWriteFile(test_exception_ports_->WritePipeHandle(), &c, 1); + + // Wait for the parent process to say that its end is set up. + CheckedReadFile(test_exception_ports_->ReadPipeHandle(), &c, 1); + EXPECT_EQ('\0', c); + + // Regardless of where ExceptionPorts::SetExceptionPort() ran, + // ExceptionPorts::GetExceptionPorts() can always be tested in-process. + { + SCOPED_TRACE("task"); + TestGetExceptionPorts(self_task_ports, remote_port, EXCEPTION_DEFAULT); + } + + { + SCOPED_TRACE("main_thread"); + mach_port_t thread_handler = + (test_exception_ports_->set_on() == kSetOnTaskAndThreads) + ? remote_port + : MACH_PORT_NULL; + TestGetExceptionPorts( + self_thread_ports, thread_handler, EXCEPTION_STATE); + } + + // Let the other thread know it’s safe to proceed. + crash_semaphore_.Signal(); + + // If this thread is the one that crashes, do it. + if (test_exception_ports_->who_crashes() == kMainThreadCrashes) { + Crash(); + } + + // Reap the other thread. + rv_int = pthread_join(thread_, nullptr); + ASSERT_EQ(0, rv_int); + } + + private: + // Calls ThreadMain(). + static void* ThreadMainThunk(void* argument) { + Child* self = reinterpret_cast<Child*>(argument); + return self->ThreadMain(); + } + + // Runs the “other” thread. + void* ThreadMain() { + ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread, + THREAD_NULL); + mach_port_t remote_port = test_exception_ports_->RemotePort(); + + // Set this thread’s exception handler, if appropriate. + if (test_exception_ports_->set_type() == kSetInProcess && + test_exception_ports_->set_on() == kSetOnTaskAndThreads) { + CHECK(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH, + remote_port, + EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE)); + } + + // Let the main thread know that this thread is ready. + init_semaphore_.Signal(); + + // Wait for the main thread to signal that it’s safe to proceed. + crash_semaphore_.Wait(); + + // Regardless of where ExceptionPorts::SetExceptionPort() ran, + // ExceptionPorts::GetExceptionPorts() can always be tested in-process. + { + SCOPED_TRACE("other_thread"); + mach_port_t thread_handler = + (test_exception_ports_->set_on() == kSetOnTaskAndThreads) + ? remote_port + : MACH_PORT_NULL; + TestGetExceptionPorts( + self_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY); + } + + // If this thread is the one that crashes, do it. + if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) { + Crash(); + } + + return nullptr; + } + + static void Crash() { + __builtin_trap(); + } + + // The parent object. + TestExceptionPorts* test_exception_ports_; // weak + + // The “other” thread. + pthread_t thread_; + + // The main thread waits on this for the other thread to start up and + // perform its own initialization. + Semaphore init_semaphore_; + + // The child thread waits on this for the parent thread to indicate that the + // child can test its exception ports and possibly crash, as appropriate. + Semaphore crash_semaphore_; + + DISALLOW_COPY_AND_ASSIGN(Child); + }; + + // MachMultiprocess: + + void MachMultiprocessParent() override { + // Wait for the child process to be ready. It needs to have all of its + // threads set up before proceeding if in kSetOutOfProcess mode. + char c; + CheckedReadFile(ReadPipeHandle(), &c, 1); + EXPECT_EQ('\0', c); + + mach_port_t local_port = LocalPort(); + + // Get an ExceptionPorts object for the task and each of its threads. + ExceptionPorts task_ports(ExceptionPorts::kTargetTypeTask, ChildTask()); + EXPECT_STREQ("task", task_ports.TargetTypeName()); + + // Hopefully the threads returned by task_threads() are in order, with the + // main thread first and the other thread second. This is currently always + // the case, although nothing guarantees that it will remain so. + thread_act_array_t threads; + mach_msg_type_number_t thread_count = 0; + kern_return_t kr = task_threads(ChildTask(), &threads, &thread_count); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "task_threads"); + + ScopedForbidReturn threads_need_owners; + ASSERT_EQ(2u, thread_count); + base::mac::ScopedMachSendRight main_thread(threads[0]); + base::mac::ScopedMachSendRight other_thread(threads[1]); + threads_need_owners.Disarm(); + + ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread, + main_thread.get()); + ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread, + other_thread.get()); + EXPECT_STREQ("thread", main_thread_ports.TargetTypeName()); + EXPECT_STREQ("thread", other_thread_ports.TargetTypeName()); + + if (set_type_ == kSetOutOfProcess) { + // Test ExceptionPorts::SetExceptionPorts() being called from + // out-of-process. + // + // local_port is only a receive right, but a send right is needed for + // ExceptionPorts::SetExceptionPort(). Make a send right, which can be + // deallocated once the calls to ExceptionPorts::SetExceptionPort() are + // done. + kr = mach_port_insert_right( + mach_task_self(), local_port, local_port, MACH_MSG_TYPE_MAKE_SEND); + ASSERT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_insert_right"); + base::mac::ScopedMachSendRight send_owner(local_port); + + ASSERT_TRUE(task_ports.SetExceptionPort( + EXC_MASK_CRASH, local_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE)); + + if (set_on_ == kSetOnTaskAndThreads) { + ASSERT_TRUE(main_thread_ports.SetExceptionPort( + EXC_MASK_CRASH, local_port, EXCEPTION_STATE, MACHINE_THREAD_STATE)); + + ASSERT_TRUE( + other_thread_ports.SetExceptionPort(EXC_MASK_CRASH, + local_port, + EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE)); + } + } + + // Regardless of where ExceptionPorts::SetExceptionPort() ran, + // ExceptionPorts::GetExceptionPorts() can always be tested out-of-process. + { + SCOPED_TRACE("task"); + TestGetExceptionPorts(task_ports, local_port, EXCEPTION_DEFAULT); + } + + mach_port_t thread_handler = + (set_on_ == kSetOnTaskAndThreads) ? local_port : MACH_PORT_NULL; + + { + SCOPED_TRACE("main_thread"); + TestGetExceptionPorts(main_thread_ports, thread_handler, EXCEPTION_STATE); + } + + { + SCOPED_TRACE("other_thread"); + TestGetExceptionPorts( + other_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY); + } + + // Let the child process know that everything in the parent process is set + // up. + c = '\0'; + CheckedWriteFile(WritePipeHandle(), &c, 1); + + if (who_crashes_ != kNobodyCrashes) { + UniversalMachExcServer universal_mach_exc_server(this); + + const mach_msg_timeout_t kTimeoutMs = 50; + kern_return_t kr = + MachMessageServer::Run(&universal_mach_exc_server, + local_port, + kMachMessageReceiveAuditTrailer, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kTimeoutMs); + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "MachMessageServer::Run"); + + EXPECT_TRUE(handled_); + } + + // Wait for the child process to exit or terminate, as indicated by it + // closing its pipe. This keeps LocalPort() alive in the child as + // RemotePort(), for the child’s use in its TestGetExceptionPorts(). + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + void MachMultiprocessChild() override { + Child child(this); + child.Run(); + } + + SetOn set_on_; + SetType set_type_; + WhoCrashes who_crashes_; + + // true if an exception message was handled. + bool handled_; + + DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts); +}; + +TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_NoCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskOnly, + TestExceptionPorts::kSetInProcess, + TestExceptionPorts::kNobodyCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_MainThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskOnly, + TestExceptionPorts::kSetInProcess, + TestExceptionPorts::kMainThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_OtherThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskOnly, + TestExceptionPorts::kSetInProcess, + TestExceptionPorts::kOtherThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_NoCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskAndThreads, + TestExceptionPorts::kSetInProcess, + TestExceptionPorts::kNobodyCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_MainThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskAndThreads, + TestExceptionPorts::kSetInProcess, + TestExceptionPorts::kMainThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, + TaskAndThreadExceptionPorts_SetInProcess_OtherThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskAndThreads, + TestExceptionPorts::kSetInProcess, + TestExceptionPorts::kOtherThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_NoCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskOnly, + TestExceptionPorts::kSetOutOfProcess, + TestExceptionPorts::kNobodyCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_MainThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskOnly, + TestExceptionPorts::kSetOutOfProcess, + TestExceptionPorts::kMainThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_OtherThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskOnly, + TestExceptionPorts::kSetOutOfProcess, + TestExceptionPorts::kOtherThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetOutOfProcess_NoCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskAndThreads, + TestExceptionPorts::kSetOutOfProcess, + TestExceptionPorts::kNobodyCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, + TaskAndThreadExceptionPorts_SetOutOfProcess_MainThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskAndThreads, + TestExceptionPorts::kSetOutOfProcess, + TestExceptionPorts::kMainThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, + TaskAndThreadExceptionPorts_SetOutOfProcess_OtherThreadCrash) { + TestExceptionPorts test_exception_ports( + TestExceptionPorts::kSetOnTaskAndThreads, + TestExceptionPorts::kSetOutOfProcess, + TestExceptionPorts::kOtherThreadCrashes); + test_exception_ports.Run(); +} + +TEST(ExceptionPorts, HostExceptionPorts) { + // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to + // make sure that TargetTypeName() returns the right string, and that the + // underlying host_get_exception_ports() function appears to be called by + // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance + // that the test is being run as root, just look for KERN_SUCCESS. + // host_set_exception_ports() is not tested, because if the test were running + // as root and the call succeeded, it would have global effects. + + const bool expect_success = geteuid() == 0; + + base::mac::ScopedMachSendRight host(mach_host_self()); + ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost, + host.get()); + EXPECT_STREQ("host", explicit_host_ports.TargetTypeName()); + + ExceptionPorts::ExceptionHandlerVector explicit_handlers; + bool rv = + explicit_host_ports.GetExceptionPorts(ExcMaskValid(), &explicit_handlers); + EXPECT_EQ(expect_success, rv); + + ExceptionPorts implicit_host_ports(ExceptionPorts::kTargetTypeHost, + HOST_NULL); + EXPECT_STREQ("host", implicit_host_ports.TargetTypeName()); + + ExceptionPorts::ExceptionHandlerVector implicit_handlers; + rv = + implicit_host_ports.GetExceptionPorts(ExcMaskValid(), &implicit_handlers); + EXPECT_EQ(expect_success, rv); + + EXPECT_EQ(explicit_handlers.size(), implicit_handlers.size()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exception_types.cc b/third_party/crashpad/crashpad/util/mach/exception_types.cc new file mode 100644 index 0000000..0199b31 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_types.cc
@@ -0,0 +1,212 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exception_types.h" + +#include <Availability.h> +#include <AvailabilityMacros.h> +#include <dlfcn.h> +#include <errno.h> +#include <libproc.h> +#include <kern/exc_resource.h> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "util/mac/mac_util.h" + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 + +extern "C" { + +// proc_get_wakemon_params() is present in the Mac OS X 10.9 SDK, but no +// declaration is provided. This provides a declaration and marks it for weak +// import if the deployment target is below 10.9. +int proc_get_wakemon_params(pid_t pid, int* rate_hz, int* flags) + __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +// Redeclare the method without the availability annotation to suppress the +// -Wpartial-availability warning. +int proc_get_wakemon_params(pid_t pid, int* rate_hz, int* flags); + +} // extern "C" + +#else + +namespace { + +using ProcGetWakemonParamsType = int (*)(pid_t, int*, int*); + +// The SDK doesn’t have proc_get_wakemon_params() to link against, even with +// weak import. This function returns a function pointer to it if it exists at +// runtime, or nullptr if it doesn’t. proc_get_wakemon_params() is looked up in +// the same module that provides proc_pidinfo(). +ProcGetWakemonParamsType GetProcGetWakemonParams() { + Dl_info dl_info; + if (!dladdr(reinterpret_cast<void*>(proc_pidinfo), &dl_info)) { + return nullptr; + } + + void* dl_handle = + dlopen(dl_info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + if (!dl_handle) { + return nullptr; + } + + ProcGetWakemonParamsType proc_get_wakemon_params = + reinterpret_cast<ProcGetWakemonParamsType>( + dlsym(dl_handle, "proc_get_wakemon_params")); + return proc_get_wakemon_params; +} + +} // namespace + +#endif + +namespace { + +// Wraps proc_get_wakemon_params(), calling it if the system provides it. It’s +// present on Mac OS X 10.9 and later. If it’s not available, sets errno to +// ENOSYS and returns -1. +int ProcGetWakemonParams(pid_t pid, int* rate_hz, int* flags) { +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 + // proc_get_wakemon_params() isn’t in the SDK. Look it up dynamically. + static ProcGetWakemonParamsType proc_get_wakemon_params = + GetProcGetWakemonParams(); +#endif + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 + // proc_get_wakemon_params() is definitely available if the deployment target + // is 10.9 or newer. + if (!proc_get_wakemon_params) { + errno = ENOSYS; + return -1; + } +#endif + + return proc_get_wakemon_params(pid, rate_hz, flags); +} + +} // namespace + +namespace crashpad { + +exception_type_t ExcCrashRecoverOriginalException( + mach_exception_code_t code_0, + mach_exception_code_t* original_code_0, + int* signal) { + // 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c proc_prepareexit() sets code[0] + // based on the signal value, original exception type, and low 20 bits of the + // original code[0] before calling xnu-2422.110.17/osfmk/kern/exception.c + // task_exception_notify() to raise an EXC_CRASH. + // + // The list of core-generating signals (as used in proc_prepareexit()’s call + // to hassigprop()) is in 10.9.4 xnu-2422.110.17/bsd/sys/signalvar.h sigprop: + // entires with SA_CORE are in the set. These signals are SIGQUIT, SIGILL, + // SIGTRAP, SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, and SIGSYS. Processes + // killed for code-signing reasons will be killed by SIGKILL and are also + // eligible for EXC_CRASH handling, but processes killed by SIGKILL for other + // reasons are not. + if (signal) { + *signal = (code_0 >> 24) & 0xff; + } + + if (original_code_0) { + *original_code_0 = code_0 & 0xfffff; + } + + return (code_0 >> 20) & 0xf; +} + +bool IsExceptionNonfatalResource(exception_type_t exception, + mach_exception_code_t code_0, + pid_t pid) { + if (exception != EXC_RESOURCE) { + return false; + } + + const int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(code_0); + const int resource_flavor = EXC_RESOURCE_DECODE_FLAVOR(code_0); + + if (resource_type == RESOURCE_TYPE_CPU && + (resource_flavor == FLAVOR_CPU_MONITOR || + resource_flavor == FLAVOR_CPU_MONITOR_FATAL)) { + // These exceptions may be fatal. They are not fatal by default at task + // creation but can be made fatal by calling proc_rlimit_control() with + // RLIMIT_CPU_USAGE_MONITOR as the second argument and CPUMON_MAKE_FATAL set + // in the flags. + if (MacOSXMinorVersion() >= 10) { + // In Mac OS X 10.10, the exception code indicates whether the exception + // is fatal. See 10.10 xnu-2782.1.97/osfmk/kern/thread.c + // THIS_THREAD_IS_CONSUMING_TOO_MUCH_CPU__SENDING_EXC_RESOURCE(). + return resource_flavor == FLAVOR_CPU_MONITOR; + } + + // In Mac OS X 10.9, there’s no way to determine whether the exception is + // fatal. Unlike RESOURCE_TYPE_WAKEUPS below, there’s no way to determine + // this outside the kernel. proc_rlimit_control()’s RLIMIT_CPU_USAGE_MONITOR + // is the only interface to modify CPUMON_MAKE_FATAL, but it’s only able to + // set this bit, not obtain its current value. + // + // Default to assuming that these exceptions are nonfatal. They are nonfatal + // by default and no users of proc_rlimit_control() were found on 10.9.5 + // 13F1066 in /System and /usr outside of Metadata.framework and associated + // tools. + return true; + } + + if (resource_type == RESOURCE_TYPE_WAKEUPS && + resource_flavor == FLAVOR_WAKEUPS_MONITOR) { + // These exceptions may be fatal. They are not fatal by default at task + // creation, but can be made fatal by calling proc_rlimit_control() with + // RLIMIT_WAKEUPS_MONITOR as the second argument and WAKEMON_MAKE_FATAL set + // in the flags. + // + // proc_get_wakemon_params() (which calls + // through to proc_rlimit_control() with RLIMIT_WAKEUPS_MONITOR) determines + // whether these exceptions are fatal. See 10.10 + // xnu-2782.1.97/osfmk/kern/task.c + // THIS_PROCESS_IS_CAUSING_TOO_MANY_WAKEUPS__SENDING_EXC_RESOURCE(). + // + // If proc_get_wakemon_params() fails, default to assuming that these + // exceptions are nonfatal. They are nonfatal by default and no users of + // proc_rlimit_control() were found on 10.9.5 13F1066 in /System and /usr + // outside of Metadata.framework and associated tools. + int wm_rate; + int wm_flags; + int rv = ProcGetWakemonParams(pid, &wm_rate, &wm_flags); + if (rv < 0) { + PLOG(WARNING) << "ProcGetWakemonParams"; + return true; + } + + return !(wm_flags & WAKEMON_MAKE_FATAL); + } + + if (resource_type == RESOURCE_TYPE_MEMORY && + resource_flavor == FLAVOR_HIGH_WATERMARK) { + // These exceptions are never fatal. See 10.10 + // xnu-2782.1.97/osfmk/kern/task.c + // THIS_PROCESS_CROSSED_HIGH_WATERMARK__SENDING_EXC_RESOURCE(). + return true; + } + + // Treat unknown exceptions as fatal. This is the conservative approach: it + // may result in more crash reports being generated, but the type-flavor + // combinations can be evaluated to determine appropriate handling. + LOG(WARNING) << "unknown resource type " << resource_type << " flavor " + << resource_flavor; + return false; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/exception_types.h b/third_party/crashpad/crashpad/util/mach/exception_types.h new file mode 100644 index 0000000..834f212 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_types.h
@@ -0,0 +1,79 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_ +#define CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_ + +#include <mach/mach.h> +#include <sys/types.h> + +namespace crashpad { + +//! \brief Recovers the original exception, first exception code, and signal +//! from the encoded form of the first exception code delivered with +//! `EXC_CRASH` exceptions. +//! +//! `EXC_CRASH` exceptions are generated when the kernel has committed to +//! terminating a process as a result of a core-generating POSIX signal and, for +//! hardware exceptions, an earlier Mach exception. Information about this +//! earlier exception and signal is made available to the `EXC_CRASH` handler +//! via its `code[0]` parameter. This function recovers the original exception, +//! the value of `code[0]` from the original exception, and the value of the +//! signal responsible for process termination. +//! +//! \param[in] code_0 The first exception code (`code[0]`) passed to a Mach +//! exception handler in an `EXC_CRASH` exception. It is invalid to call +//! this function with an exception code from any exception other than +//! `EXC_CRASH`. +//! \param[out] original_code_0 The first exception code (`code[0]`) passed to +//! the Mach exception handler for a hardware exception that resulted in the +//! generation of a POSIX signal that caused process termination. If the +//! signal that caused termination was not sent as a result of a hardware +//! exception, this will be `0`. Callers that do not need this value may +//! pass `nullptr`. +//! \param[out] signal The POSIX signal that caused process termination. Callers +//! that do not need this value may pass `nullptr`. +//! +//! \return The original exception for a hardware exception that resulted in the +//! generation of a POSIX signal that caused process termination. If the +//! signal that caused termination was not sent as a result of a hardware +//! exception, this will be `0`. +exception_type_t ExcCrashRecoverOriginalException( + mach_exception_code_t code_0, + mach_exception_code_t* original_code_0, + int* signal); + +//! \brief Determines whether an exception is a non-fatal `EXC_RESOURCE`. +//! +//! \param[in] exception The exception type as received by a Mach exception +//! handler. +//! \param[in] code_0 The first exception code (`code[0]`) as received by a +//! Mach exception handler. +//! \param[in] pid The process ID that the exception occurred in. In some cases, +//! process may need to be queried to determine whether an `EXC_RESOURCE` +//! exception is fatal. +//! +//! \return `true` if the exception is a non-fatal `EXC_RESOURCE`. `false` +//! otherwise. If the exception is `EXC_RESOURCE` of a recognized type but +//! it is not possible to determine whether it is fatal, returns `true` +//! under the assumption that all known `EXC_RESOURCE` exceptions are +//! non-fatal by default. If the exception is not `EXC_RESOURCE` or is an +//! unknown `EXC_RESOURCE` type, returns `false`. +bool IsExceptionNonfatalResource(exception_type_t exception, + mach_exception_code_t code_0, + pid_t pid); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
diff --git a/third_party/crashpad/crashpad/util/mach/exception_types_test.cc b/third_party/crashpad/crashpad/util/mach/exception_types_test.cc new file mode 100644 index 0000000..0520c2e --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/exception_types_test.cc
@@ -0,0 +1,140 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/exception_types.h" + +#include <kern/exc_resource.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/mac/mac_util.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ExceptionTypes, ExcCrashRecoverOriginalException) { + struct TestData { + mach_exception_code_t code_0; + exception_type_t exception; + mach_exception_code_t original_code_0; + int signal; + }; + const TestData kTestData[] = { + {0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV}, + {0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV}, + {0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS}, + {0x4200001, EXC_BAD_INSTRUCTION, 1, SIGILL}, + {0x8300001, EXC_ARITHMETIC, 1, SIGFPE}, + {0x5600002, EXC_BREAKPOINT, 2, SIGTRAP}, + {0x3000000, 0, 0, SIGQUIT}, + {0x6000000, 0, 0, SIGABRT}, + {0xc000000, 0, 0, SIGSYS}, + {0, 0, 0, 0}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& test_data = kTestData[index]; + SCOPED_TRACE(base::StringPrintf( + "index %zu, code_0 0x%llx", index, test_data.code_0)); + + mach_exception_code_t original_code_0; + int signal; + exception_type_t exception = ExcCrashRecoverOriginalException( + test_data.code_0, &original_code_0, &signal); + + EXPECT_EQ(test_data.exception, exception); + EXPECT_EQ(test_data.original_code_0, original_code_0); + EXPECT_EQ(test_data.signal, signal); + } + + // Now make sure that ExcCrashRecoverOriginalException() properly ignores + // optional arguments. + static_assert(arraysize(kTestData) >= 1, "must have something to test"); + const TestData& test_data = kTestData[0]; + EXPECT_EQ( + test_data.exception, + ExcCrashRecoverOriginalException(test_data.code_0, nullptr, nullptr)); + + mach_exception_code_t original_code_0; + EXPECT_EQ(test_data.exception, + ExcCrashRecoverOriginalException( + test_data.code_0, &original_code_0, nullptr)); + EXPECT_EQ(test_data.original_code_0, original_code_0); + + int signal; + EXPECT_EQ( + test_data.exception, + ExcCrashRecoverOriginalException(test_data.code_0, nullptr, &signal)); + EXPECT_EQ(test_data.signal, signal); +} + +// These macros come from the #ifdef KERNEL section of <kern/exc_resource.h>: +// 10.10 xnu-2782.1.97/osfmk/kern/exc_resource.h. +#define EXC_RESOURCE_ENCODE_TYPE(code, type) \ + ((code) |= ((static_cast<uint64_t>(type) & 0x7ull) << 61)) +#define EXC_RESOURCE_ENCODE_FLAVOR(code, flavor) \ + ((code) |= ((static_cast<uint64_t>(flavor) & 0x7ull) << 58)) + +TEST(ExceptionTypes, IsExceptionNonfatalResource) { + const pid_t pid = getpid(); + + mach_exception_code_t code = 0; + EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU); + EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR); + EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); + + if (MacOSXMinorVersion() >= 10) { + // FLAVOR_CPU_MONITOR_FATAL was introduced in Mac OS X 10.10. + code = 0; + EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU); + EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR_FATAL); + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); + } + + // This assumes that WAKEMON_MAKE_FATAL is not set for this process. The + // default is for WAKEMON_MAKE_FATAL to not be set, there’s no public API to + // enable it, and nothing in this process should have enabled it. + code = 0; + EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_WAKEUPS); + EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_WAKEUPS_MONITOR); + EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); + + code = 0; + EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_MEMORY); + EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_HIGH_WATERMARK); + EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid)); + + // Non-EXC_RESOURCE exceptions should never be considered nonfatal resource + // exceptions, because they aren’t resource exceptions at all. + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0xb100001, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0x0b00000, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0x6000000, pid)); + EXPECT_FALSE( + IsExceptionNonfatalResource(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_BAD_INSTRUCTION, 1, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_ARITHMETIC, 1, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(EXC_BREAKPOINT, 2, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(0, 0, pid)); + EXPECT_FALSE(IsExceptionNonfatalResource(kMachExceptionSimulated, 0, pid)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mach_extensions.cc b/third_party/crashpad/crashpad/util/mach/mach_extensions.cc new file mode 100644 index 0000000..64552f1c --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_extensions.cc
@@ -0,0 +1,167 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/mach_extensions.h" + +#include <AvailabilityMacros.h> +#include <pthread.h> +#include <servers/bootstrap.h> + +#include "base/mac/mach_logging.h" +#include "util/mac/mac_util.h" + +namespace { + +// This forms the internal implementation for BootstrapCheckIn() and +// BootstrapLookUp(), which follow the same logic aside from the routine called +// and the right type returned. + +struct BootstrapCheckInTraits { + using Type = base::mac::ScopedMachReceiveRight; + static kern_return_t Call(mach_port_t bootstrap_port, + const char* service_name, + mach_port_t* service_port) { + return bootstrap_check_in(bootstrap_port, service_name, service_port); + } + static const char kName[]; +}; +const char BootstrapCheckInTraits::kName[] = "bootstrap_check_in"; + +struct BootstrapLookUpTraits { + using Type = base::mac::ScopedMachSendRight; + static kern_return_t Call(mach_port_t bootstrap_port, + const char* service_name, + mach_port_t* service_port) { + return bootstrap_look_up(bootstrap_port, service_name, service_port); + } + static const char kName[]; +}; +const char BootstrapLookUpTraits::kName[] = "bootstrap_look_up"; + +template <typename Traits> +typename Traits::Type BootstrapCheckInOrLookUp( + const std::string& service_name) { + // bootstrap_check_in() and bootstrap_look_up() silently truncate service + // names longer than BOOTSTRAP_MAX_NAME_LEN. This check ensures that the name + // will not be truncated. + if (service_name.size() >= BOOTSTRAP_MAX_NAME_LEN) { + LOG(ERROR) << Traits::kName << " " << service_name << ": name too long"; + return typename Traits::Type(MACH_PORT_NULL); + } + + mach_port_t service_port; + kern_return_t kr = Traits::Call(bootstrap_port, + service_name.c_str(), + &service_port); + if (kr != BOOTSTRAP_SUCCESS) { + BOOTSTRAP_LOG(ERROR, kr) << Traits::kName << " " << service_name; + service_port = MACH_PORT_NULL; + } + + return typename Traits::Type(service_port); +} + +} // namespace + +namespace crashpad { + +thread_t MachThreadSelf() { + // The pthreads library keeps its own copy of the thread port. Using it does + // not increment its reference count. + return pthread_mach_thread_np(pthread_self()); +} + +mach_port_t NewMachPort(mach_port_right_t right) { + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr = mach_port_allocate(mach_task_self(), right, &port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_allocate"; + return port; +} + +exception_mask_t ExcMaskAll() { + // This is necessary because of the way that the kernel validates + // exception_mask_t arguments to + // {host,task,thread}_{get,set,swap}_exception_ports(). It is strict, + // rejecting attempts to operate on any bits that it does not recognize. See + // 10.9.4 xnu-2422.110.17/osfmk/mach/ipc_host.c and + // xnu-2422.110.17/osfmk/mach/ipc_tt.c. + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 + const int mac_os_x_minor_version = MacOSXMinorVersion(); +#endif + + // See 10.6.8 xnu-1504.15.3/osfmk/mach/exception_types.h. 10.7 uses the same + // definition as 10.6. See 10.7.5 xnu-1699.32.7/osfmk/mach/exception_types.h + const exception_mask_t kExcMaskAll_10_6 = + EXC_MASK_BAD_ACCESS | + EXC_MASK_BAD_INSTRUCTION | + EXC_MASK_ARITHMETIC | + EXC_MASK_EMULATION | + EXC_MASK_SOFTWARE | + EXC_MASK_BREAKPOINT | + EXC_MASK_SYSCALL | + EXC_MASK_MACH_SYSCALL | + EXC_MASK_RPC_ALERT | + EXC_MASK_MACHINE; +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 + if (mac_os_x_minor_version < 8) { + return kExcMaskAll_10_6; + } +#endif + + // 10.8 added EXC_MASK_RESOURCE. See 10.8.5 + // xnu-2050.48.11/osfmk/mach/exception_types.h. + const exception_mask_t kExcMaskAll_10_8 = + kExcMaskAll_10_6 | EXC_MASK_RESOURCE; +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 + if (mac_os_x_minor_version < 9) { + return kExcMaskAll_10_8; + } +#endif + + // 10.9 added EXC_MASK_GUARD. See 10.9.4 + // xnu-2422.110.17/osfmk/mach/exception_types.h. + const exception_mask_t kExcMaskAll_10_9 = kExcMaskAll_10_8 | EXC_MASK_GUARD; + return kExcMaskAll_10_9; +} + +exception_mask_t ExcMaskValid() { + const exception_mask_t kExcMaskValid_10_6 = ExcMaskAll() | EXC_MASK_CRASH; +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 + if (MacOSXMinorVersion() < 11) { + return kExcMaskValid_10_6; + } +#endif + + // 10.11 added EXC_MASK_CORPSE_NOTIFY. See 10.11 <mach/exception_types.h>. + const exception_mask_t kExcMaskValid_10_11 = + kExcMaskValid_10_6 | EXC_MASK_CORPSE_NOTIFY; + return kExcMaskValid_10_11; +} + +base::mac::ScopedMachReceiveRight BootstrapCheckIn( + const std::string& service_name) { + return BootstrapCheckInOrLookUp<BootstrapCheckInTraits>(service_name); +} + +base::mac::ScopedMachSendRight BootstrapLookUp( + const std::string& service_name) { + return BootstrapCheckInOrLookUp<BootstrapLookUpTraits>(service_name); +} + +base::mac::ScopedMachSendRight SystemCrashReporterHandler() { + return BootstrapLookUp("com.apple.ReportCrash"); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mach_extensions.h b/third_party/crashpad/crashpad/util/mach/mach_extensions.h new file mode 100644 index 0000000..5eeca9ae --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_extensions.h
@@ -0,0 +1,161 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_ +#define CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_ + +#include <mach/mach.h> + +#include <string> + +#include "base/mac/scoped_mach_port.h" + +namespace crashpad { + +//! \brief `MACH_PORT_NULL` with the correct type for a Mach port, +//! `mach_port_t`. +//! +//! For situations where implicit conversions between signed and unsigned types +//! are not performed, use kMachPortNull instead of an explicit `implicit_cast` +//! of `MACH_PORT_NULL` to `mach_port_t`. This is useful for logging and testing +//! assertions. +const mach_port_t kMachPortNull = MACH_PORT_NULL; + +//! \brief `MACH_EXCEPTION_CODES` with the correct type for a Mach exception +//! behavior, `exception_behavior_t`. +//! +//! Signedness problems can occur when ORing `MACH_EXCEPTION_CODES` as a signed +//! integer, because a signed integer overflow results. This constant can be +//! used instead of `MACH_EXCEPTION_CODES` in such cases. +const exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES; + +// Because exception_mask_t is an int and has one bit for each defined +// exception_type_t, it’s reasonable to assume that there cannot be any +// officially-defined exception_type_t values higher than 31. +// kMachExceptionSimulated uses a value well outside this range because it does +// not require a corresponding mask value. Simulated exceptions are delivered to +// the exception handler registered for EXC_CRASH exceptions using +// EXC_MASK_CRASH. + +//! \brief An exception type to use for simulated exceptions. +const exception_type_t kMachExceptionSimulated = 'CPsx'; + +//! \brief A const version of `thread_state_t`. +//! +//! This is useful as the \a old_state parameter to exception handler functions. +//! Normally, these parameters are of type `thread_state_t`, but this allows +//! modification of the state, which is conceptually `const`. +using ConstThreadState = const natural_t*; + +//! \brief Like `mach_thread_self()`, but without the obligation to release the +//! send right. +//! +//! `mach_thread_self()` returns a send right to the current thread port, +//! incrementing its reference count. This burdens the caller with maintaining +//! this send right, and calling `mach_port_deallocate()` when it is no longer +//! needed. This is burdensome, and is at odds with the normal operation of +//! `mach_task_self()`, which does not increment the task port’s reference count +//! whose result must not be deallocated. +//! +//! Callers can use this function in preference to `mach_thread_self()`. This +//! function returns an extant reference to the current thread’s port without +//! incrementing its reference count. +//! +//! \return The value of `mach_thread_self()` without incrementing its reference +//! count. The returned port must not be deallocated by +//! `mach_port_deallocate()`. The returned value is valid as long as the +//! thread continues to exist as a `pthread_t`. +thread_t MachThreadSelf(); + +//! \brief Creates a new Mach port in the current task. +//! +//! This function wraps the `mach_port_allocate()` providing a simpler +//! interface. +//! +//! \param[in] right The type of right to create. +//! +//! \return The new Mach port. On failure, `MACH_PORT_NULL` with a message +//! logged. +mach_port_t NewMachPort(mach_port_right_t right); + +//! \brief The value for `EXC_MASK_ALL` appropriate for the operating system at +//! run time. +//! +//! The SDK’s definition of `EXC_MASK_ALL` has changed over time, with later +//! versions containing more bits set than earlier versions. However, older +//! kernels will reject exception masks that contain bits set that they don’t +//! recognize. Calling this function will return a value for `EXC_MASK_ALL` +//! appropriate for the system at run time. +//! +//! \note `EXC_MASK_ALL` does not include the value of `EXC_MASK_CRASH` or +//! `EXC_MASK_CORPSE_NOTIFY`. Consumers that want `EXC_MASK_ALL` along with +//! `EXC_MASK_CRASH` may use ExcMaskAll() `| EXC_MASK_CRASH`. Consumers may +//! use ExcMaskValid() for `EXC_MASK_ALL` along with `EXC_MASK_CRASH`, +//! `EXC_MASK_CORPSE_NOTIFY`, and any values that come into existence in the +//! future. +exception_mask_t ExcMaskAll(); + +//! \brief An exception mask containing every possible exception understood by +//! the operating system at run time. +//! +//! `EXC_MASK_ALL`, and thus ExcMaskAll(), never includes the value of +//! `EXC_MASK_CRASH` or `EXC_MASK_CORPSE_NOTIFY`. For situations where an +//! exception mask corresponding to every possible exception understood by the +//! running kernel is desired, use this function instead. +//! +//! Should new exception types be introduced in the future, this function will +//! be updated to include their bits in the returned mask value when run time +//! support is present. +exception_mask_t ExcMaskValid(); + +//! \brief Makes a `boostrap_check_in()` call to the process’ bootstrap server. +//! +//! This function is provided to make it easier to call `bootstrap_check_in()` +//! while avoiding accidental leaks of the returned receive right. +//! +//! \param[in] service_name The service name to check in. +//! +//! \return On success, a receive right to the service port. On failure, +//! `MACH_PORT_NULL` with a message logged. +base::mac::ScopedMachReceiveRight BootstrapCheckIn( + const std::string& service_name); + +//! \brief Makes a `boostrap_look_up()` call to the process’ bootstrap server. +//! +//! This function is provided to make it easier to call `bootstrap_look_up()` +//! while avoiding accidental leaks of the returned send right. +//! +//! \param[in] service_name The service name to look up. +//! +//! \return On success, a send right to the service port. On failure, +//! `MACH_PORT_NULL` with a message logged. +base::mac::ScopedMachSendRight BootstrapLookUp(const std::string& service_name); + +//! \brief Obtains the system’s default Mach exception handler for crash-type +//! exceptions. +//! +//! This is obtained by looking up `"com.apple.ReportCrash"` with the bootstrap +//! server. The service name comes from the first launch agent loaded by +//! `launchd` with a `MachServices` entry having `ExceptionServer` set. This +//! launch agent is normally loaded from +//! `/System/Library/LaunchAgents/com.apple.ReportCrash.plist`. +//! +//! \return On success, a send right to an `exception_handler_t` corresponding +//! to the system’s default crash reporter. On failure, `MACH_PORT_NULL`, +//! with a message logged. +base::mac::ScopedMachSendRight SystemCrashReporterHandler(); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_
diff --git a/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc b/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc new file mode 100644 index 0000000..358f235 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc
@@ -0,0 +1,179 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/mach_extensions.h" + +#include "base/mac/scoped_mach_port.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "util/mac/mac_util.h" +#include "util/misc/random_string.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MachExtensions, MachThreadSelf) { + base::mac::ScopedMachSendRight thread_self(mach_thread_self()); + EXPECT_EQ(thread_self, MachThreadSelf()); +} + +TEST(MachExtensions, NewMachPort_Receive) { + base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_NE(kMachPortNull, port); + + mach_port_type_t type; + kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type"); + + EXPECT_EQ(MACH_PORT_TYPE_RECEIVE, type); +} + +TEST(MachExtensions, NewMachPort_PortSet) { + base::mac::ScopedMachPortSet port(NewMachPort(MACH_PORT_RIGHT_PORT_SET)); + ASSERT_NE(kMachPortNull, port); + + mach_port_type_t type; + kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type"); + + EXPECT_EQ(MACH_PORT_TYPE_PORT_SET, type); +} + +TEST(MachExtensions, NewMachPort_DeadName) { + base::mac::ScopedMachSendRight port(NewMachPort(MACH_PORT_RIGHT_DEAD_NAME)); + ASSERT_NE(kMachPortNull, port); + + mach_port_type_t type; + kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type"); + + EXPECT_EQ(MACH_PORT_TYPE_DEAD_NAME, type); +} + +const exception_mask_t kExcMaskBasic = + EXC_MASK_BAD_ACCESS | + EXC_MASK_BAD_INSTRUCTION | + EXC_MASK_ARITHMETIC | + EXC_MASK_EMULATION | + EXC_MASK_SOFTWARE | + EXC_MASK_BREAKPOINT | + EXC_MASK_SYSCALL | + EXC_MASK_MACH_SYSCALL | + EXC_MASK_RPC_ALERT; + +TEST(MachExtensions, ExcMaskAll) { + const exception_mask_t exc_mask_all = ExcMaskAll(); + EXPECT_EQ(kExcMaskBasic, exc_mask_all & kExcMaskBasic); + + EXPECT_FALSE(exc_mask_all & EXC_MASK_CRASH); + EXPECT_FALSE(exc_mask_all & EXC_MASK_CORPSE_NOTIFY); + + const int mac_os_x_minor_version = MacOSXMinorVersion(); + if (mac_os_x_minor_version >= 8) { + EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE); + } else { + EXPECT_FALSE(exc_mask_all & EXC_MASK_RESOURCE); + } + + if (mac_os_x_minor_version >= 9) { + EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD); + } else { + EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD); + } + + // Bit 0 should not be set. + EXPECT_FALSE(ExcMaskAll() & 1); + + // Every bit set in ExcMaskAll() must also be set in ExcMaskValid(). + EXPECT_EQ(ExcMaskAll(), ExcMaskAll() & ExcMaskValid()); +} + +TEST(MachExtensions, ExcMaskValid) { + const exception_mask_t exc_mask_valid = ExcMaskValid(); + EXPECT_EQ(kExcMaskBasic, exc_mask_valid & kExcMaskBasic); + + EXPECT_TRUE(exc_mask_valid & EXC_MASK_CRASH); + + const int mac_os_x_minor_version = MacOSXMinorVersion(); + if (mac_os_x_minor_version >= 8) { + EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE); + } else { + EXPECT_FALSE(exc_mask_valid & EXC_MASK_RESOURCE); + } + + if (mac_os_x_minor_version >= 9) { + EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD); + } else { + EXPECT_FALSE(exc_mask_valid & EXC_MASK_GUARD); + } + + if (mac_os_x_minor_version >= 11) { + EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY); + } else { + EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY); + } + + // Bit 0 should not be set. + EXPECT_FALSE(ExcMaskValid() & 1); + + // There must be bits set in ExcMaskValid() that are not set in ExcMaskAll(). + EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll()); +} + +TEST(MachExtensions, BootstrapCheckInAndLookUp) { + // This should always exist. + base::mac::ScopedMachSendRight + report_crash(BootstrapLookUp("com.apple.ReportCrash")); + EXPECT_NE(report_crash, kMachPortNull); + + std::string service_name = "org.chromium.crashpad.test.bootstrap_check_in."; + service_name.append(RandomString()); + + { + // The new service hasn’t checked in yet, so this should fail. + base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name)); + EXPECT_EQ(kMachPortNull, send); + + // Check it in. + base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name)); + EXPECT_NE(receive, kMachPortNull); + + // Now it should be possible to look up the new service. + send = BootstrapLookUp(service_name); + EXPECT_NE(send, kMachPortNull); + + // It shouldn’t be possible to check the service in while it’s active. + base::mac::ScopedMachReceiveRight receive_2(BootstrapCheckIn(service_name)); + EXPECT_EQ(kMachPortNull, receive_2); + } + + // The new service should be gone now. + base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name)); + EXPECT_EQ(kMachPortNull, send); + + // It should be possible to check it in again. + base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name)); + EXPECT_NE(receive, kMachPortNull); +} + +TEST(MachExtensions, SystemCrashReporterHandler) { + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + EXPECT_TRUE(system_crash_reporter_handler.is_valid()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message.cc b/third_party/crashpad/crashpad/util/mach/mach_message.cc new file mode 100644 index 0000000..30c3a8cf --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_message.cc
@@ -0,0 +1,286 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/mach_message.h" + +#include <AvailabilityMacros.h> +#include <bsm/libbsm.h> + +#include <limits> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "util/misc/clock.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +namespace { + +const int kNanosecondsPerMillisecond = 1E6; + +// TimerRunning() determines whether |deadline| has passed. If |deadline| is +// kMachMessageDeadlineWaitIndefinitely, |*timeout_options| is set to +// MACH_MSG_OPTION_NONE, |*remaining_ms| is set to MACH_MSG_TIMEOUT_NONE, and +// this function returns true. When used with mach_msg(), this will cause +// indefinite waiting. In any other case, |*timeout_options| is set to +// MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT, so mach_msg() will enforce a timeout +// specified by |*remaining_ms|. If |deadline| is in the future, |*remaining_ms| +// is set to the number of milliseconds remaining, which will always be a +// positive value, and this function returns true. If |deadline| is +// kMachMessageDeadlineNonblocking (indicating that no timer is in effect), +// |*remaining_ms| is set to zero and this function returns true. Otherwise, +// this function sets |*remaining_ms| to zero and returns false. +bool TimerRunning(uint64_t deadline, + mach_msg_timeout_t* remaining_ms, + mach_msg_option_t* timeout_options) { + if (deadline == kMachMessageDeadlineWaitIndefinitely) { + *remaining_ms = MACH_MSG_TIMEOUT_NONE; + *timeout_options = MACH_MSG_OPTION_NONE; + return true; + } + + *timeout_options = MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT; + + if (deadline == kMachMessageDeadlineNonblocking) { + *remaining_ms = 0; + return true; + } + + uint64_t now = ClockMonotonicNanoseconds(); + + if (now >= deadline) { + *remaining_ms = 0; + } else { + uint64_t remaining = deadline - now; + + // Round to the nearest millisecond, taking care not to overflow. + const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; + if (remaining <= + std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) { + *remaining_ms = (remaining + kHalfMillisecondInNanoseconds) / + kNanosecondsPerMillisecond; + } else { + *remaining_ms = remaining / kNanosecondsPerMillisecond; + } + } + + return *remaining_ms != 0; +} + +// This is an internal implementation detail of MachMessageWithDeadline(). It +// determines whether |deadline| has expired, and what timeout value and +// timeout-related options to pass to mach_msg() based on the value of +// |deadline|. mach_msg() will only be called if TimerRunning() returns true or +// if run_even_if_expired is true. +mach_msg_return_t MachMessageWithDeadlineInternal(mach_msg_header_t* message, + mach_msg_option_t options, + mach_msg_size_t receive_size, + mach_port_name_t receive_port, + MachMessageDeadline deadline, + mach_port_name_t notify_port, + bool run_even_if_expired) { + mach_msg_timeout_t remaining_ms; + mach_msg_option_t timeout_options; + if (!TimerRunning(deadline, &remaining_ms, &timeout_options) && + !run_even_if_expired) { + // Simulate the timed-out return values from mach_msg(). + if (options & MACH_SEND_MSG) { + return MACH_SEND_TIMED_OUT; + } + if (options & MACH_RCV_MSG) { + return MACH_RCV_TIMED_OUT; + } + return MACH_MSG_SUCCESS; + } + + // Turn off the passed-in timeout bits and replace them with the ones from + // TimerRunning(). Get the send_size value from message->msgh_size if sending + // a message. + return mach_msg( + message, + (options & ~(MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT)) | timeout_options, + options & MACH_SEND_MSG ? message->msgh_size : 0, + receive_size, + receive_port, + remaining_ms, + notify_port); +} + +} // namespace + +MachMessageDeadline MachMessageDeadlineFromTimeout( + mach_msg_timeout_t timeout_ms) { + switch (timeout_ms) { + case kMachMessageTimeoutNonblocking: + return kMachMessageDeadlineNonblocking; + case kMachMessageTimeoutWaitIndefinitely: + return kMachMessageDeadlineWaitIndefinitely; + default: + return ClockMonotonicNanoseconds() + + implicit_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond; + } +} + +mach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message, + mach_msg_option_t options, + mach_msg_size_t receive_size, + mach_port_name_t receive_port, + MachMessageDeadline deadline, + mach_port_name_t notify_port, + bool run_even_if_expired) { + // mach_msg() actaully does return MACH_MSG_SUCCESS when not asked to send or + // receive anything. See 10.9.5 xnu-1504.15.3/osfmk/ipc/mach_msg.c + // mach_msg_overwrite_trap(). + mach_msg_return_t mr = MACH_MSG_SUCCESS; + + // Break up the send and receive into separate operations, so that the timeout + // can be recomputed from the deadline for each. Otherwise, the computed + // timeout will apply individually to the send and then to the receive, and + // the desired deadline could be exceeded. + // + // During sends, always set MACH_SEND_INTERRUPT, and during receives, always + // set MACH_RCV_INTERRUPT. If the caller didn’t specify these options, the + // calls will be retried with a recomputed deadline. If these bits weren’t + // set, the libsyscall wrapper (10.9.5 + // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg() would restart + // interrupted calls with the original timeout value computed from the + // deadline, which would no longer correspond to the actual deadline. If the + // caller did specify these bits, don’t restart anything, because the caller + // wants to be notified of any interrupted calls. + + if (options & MACH_SEND_MSG) { + do { + mr = MachMessageWithDeadlineInternal( + message, + (options & ~MACH_RCV_MSG) | MACH_SEND_INTERRUPT, + 0, + MACH_PORT_NULL, + deadline, + notify_port, + run_even_if_expired); + } while (mr == MACH_SEND_INTERRUPTED && !(options & MACH_SEND_INTERRUPT)); + + if (mr != MACH_MSG_SUCCESS) { + return mr; + } + } + + if (options & MACH_RCV_MSG) { + do { + mr = MachMessageWithDeadlineInternal( + message, + (options & ~MACH_SEND_MSG) | MACH_RCV_INTERRUPT, + receive_size, + receive_port, + deadline, + notify_port, + run_even_if_expired); + } while (mr == MACH_RCV_INTERRUPTED && !(options & MACH_RCV_INTERRUPT)); + } + + return mr; +} + +void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header, + mach_msg_header_t* out_header) { + out_header->msgh_bits = + MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0); + out_header->msgh_remote_port = in_header->msgh_remote_port; + out_header->msgh_size = sizeof(mig_reply_error_t); + out_header->msgh_local_port = MACH_PORT_NULL; + out_header->msgh_id = in_header->msgh_id + 100; + reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record; + + // MIG-generated dispatch routines don’t do this, but they should. + out_header->msgh_reserved = 0; +} + +void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) { + reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error; +} + +const mach_msg_trailer_t* MachMessageTrailerFromHeader( + const mach_msg_header_t* header) { + vm_address_t header_address = reinterpret_cast<vm_address_t>(header); + vm_address_t trailer_address = header_address + round_msg(header->msgh_size); + return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address); +} + +pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer) { + if (trailer->msgh_trailer_type != MACH_MSG_TRAILER_FORMAT_0) { + LOG(ERROR) << "unexpected msgh_trailer_type " << trailer->msgh_trailer_type; + return -1; + } + if (trailer->msgh_trailer_size < + REQUESTED_TRAILER_SIZE(kMachMessageReceiveAuditTrailer)) { + LOG(ERROR) << "small msgh_trailer_size " << trailer->msgh_trailer_size; + return -1; + } + + const mach_msg_audit_trailer_t* audit_trailer = + reinterpret_cast<const mach_msg_audit_trailer_t*>(trailer); + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 + pid_t audit_pid; + audit_token_to_au32(audit_trailer->msgh_audit, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + &audit_pid, + nullptr, + nullptr); +#else + pid_t audit_pid = audit_token_to_pid(audit_trailer->msgh_audit); +#endif + + return audit_pid; +} + +bool MachMessageDestroyReceivedPort(mach_port_t port, + mach_msg_type_name_t port_right_type) { + // This implements a subset of 10.10.5 + // xnu-2782.40.9/libsyscall/mach/mach_msg.c mach_msg_destroy_port() that deals + // only with port rights that can be received in Mach messages. + switch (port_right_type) { + case MACH_MSG_TYPE_PORT_RECEIVE: { + kern_return_t kr = mach_port_mod_refs( + mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_port_mod_refs"; + return false; + } + return true; + } + + case MACH_MSG_TYPE_PORT_SEND: + case MACH_MSG_TYPE_PORT_SEND_ONCE: { + kern_return_t kr = mach_port_deallocate(mach_task_self(), port); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_port_deallocate"; + return false; + } + return true; + } + + default: { + LOG(ERROR) << "unexpected port right type " << port_right_type; + return false; + } + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message.h b/third_party/crashpad/crashpad/util/mach/mach_message.h new file mode 100644 index 0000000..2fd8151 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_message.h
@@ -0,0 +1,194 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_ +#define CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_ + +#include <mach/mach.h> +#include <stdint.h> +#include <sys/types.h> + +namespace crashpad { + +//! \brief A Mach message option specifying that an audit trailer should be +//! delivered during a receive operation. +//! +//! This constant is provided because the macros normally used to request this +//! behavior are cumbersome. +const mach_msg_option_t kMachMessageReceiveAuditTrailer = + MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | + MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); + +//! \brief Special constants used as `mach_msg_timeout_t` values. +enum : mach_msg_timeout_t { + //! \brief When passed to MachMessageDeadlineFromTimeout(), that function will + //! return #kMachMessageDeadlineNonblocking. + kMachMessageTimeoutNonblocking = 0, + + //! \brief When passed to MachMessageDeadlineFromTimeout(), that function will + //! return #kMachMessageDeadlineWaitIndefinitely. + kMachMessageTimeoutWaitIndefinitely = 0xffffffff, +}; + +//! \brief The time before which a MachMessageWithDeadline() call should +//! complete. +//! +//! A value of this type may be one of the special constants +//! #kMachMessageDeadlineNonblocking or #kMachMessageDeadlineWaitIndefinitely. +//! Any other values should be produced by calling +//! MachMessageDeadlineFromTimeout(). +//! +//! Internally, these are currently specified on the same time base as +//! ClockMonotonicNanoseconds(), although this is an implementation detail. +using MachMessageDeadline = uint64_t; + +//! \brief Special constants used as \ref MachMessageDeadline values. +enum : MachMessageDeadline { + //! \brief MachMessageWithDeadline() should not block at all in its operation. + kMachMessageDeadlineNonblocking = 0, + + //! \brief MachMessageWithDeadline() should wait indefinitely for the + //! requested operation to complete. + kMachMessageDeadlineWaitIndefinitely = 0xffffffffffffffff, +}; + +//! \brief Computes the deadline for a specified timeout value. +//! +//! While deadlines exist on an absolute time scale, timeouts are relative. This +//! function calculates the deadline as \a timeout_ms milliseconds after it +//! executes. +//! +//! If \a timeout_ms is #kMachMessageDeadlineNonblocking, this function will +//! return #kMachMessageDeadlineNonblocking. If \a timeout_ms is +//! #kMachMessageTimeoutWaitIndefinitely, this function will return +//! #kMachMessageDeadlineWaitIndefinitely. +MachMessageDeadline MachMessageDeadlineFromTimeout( + mach_msg_timeout_t timeout_ms); + +//! \brief Runs `mach_msg()` with a deadline, as opposed to a timeout. +//! +//! This function is similar to `mach_msg()`, with the following differences: +//! - The `timeout` parameter has been replaced by \a deadline. The deadline +//! applies uniformly to a call that is requested to both send and receive +//! a message. +//! - The `MACH_SEND_TIMEOUT` and `MACH_RCV_TIMEOUT` bits in \a options are +//! not used. Timeouts are specified by the \a deadline argument. +//! - The `send_size` parameter has been removed. Its value is implied by +//! \a message when \a options contains `MACH_SEND_MSG`. +//! - The \a run_even_if_expired parameter has been added. +//! +//! Like the `mach_msg()` wrapper in `libsyscall`, this function will retry +//! operations when experiencing `MACH_SEND_INTERRUPTED` and +//! `MACH_RCV_INTERRUPTED`, unless \a options contains `MACH_SEND_INTERRUPT` or +//! `MACH_RCV_INTERRUPT`. Unlike `mach_msg()`, which restarts the call with the +//! full timeout when this occurs, this function continues enforcing the +//! user-specified \a deadline. +//! +//! Except as noted, the parameters and return value are identical to those of +//! `mach_msg()`. +//! +//! \param[in] deadline The time by which this call should complete. If the +//! deadline is exceeded, this call will return `MACH_SEND_TIMED_OUT` or +//! `MACH_RCV_TIMED_OUT`. +//! \param[in] run_even_if_expired If `true`, a deadline that is expired when +//! this function is called will be treated as though a deadline of +//! #kMachMessageDeadlineNonblocking had been specified. When `false`, an +//! expired deadline will result in a `MACH_SEND_TIMED_OUT` or +//! `MACH_RCV_TIMED_OUT` return value, even if the deadline is already +//! expired when the function is called. +mach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message, + mach_msg_option_t options, + mach_msg_size_t receive_size, + mach_port_name_t receive_port, + MachMessageDeadline deadline, + mach_port_name_t notify_port, + bool run_even_if_expired); + +//! \brief Initializes a reply message for a MIG server routine based on its +//! corresponding request. +//! +//! If a request is handled by a server routine, it may be necessary to revise +//! some of the fields set by this function, such as `msgh_size` and any fields +//! defined in a routine’s reply structure type. +//! +//! \param[in] in_header The request message to base the reply on. +//! \param[out] out_header The reply message to initialize. \a out_header will +//! be treated as a `mig_reply_error_t*` and all of its fields will be set +//! except for `RetCode`, which must be set by SetMIGReplyError(). This +//! argument is accepted as a `mach_msg_header_t*` instead of a +//! `mig_reply_error_t*` because that is the type that callers are expected +//! to possess in the C API. +void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header, + mach_msg_header_t* out_header); + +//! \brief Sets the error code in a reply message for a MIG server routine. +//! +//! \param[inout] out_header The reply message to operate on. \a out_header will +//! be treated as a `mig_reply_error_t*` and its `RetCode` field will be +//! set. This argument is accepted as a `mach_msg_header_t*` instead of a +//! `mig_reply_error_t*` because that is the type that callers are expected +//! to possess in the C API. +//! \param[in] error The error code to store in \a out_header. +//! +//! \sa PrepareMIGReplyFromRequest() +void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error); + +//! \brief Returns a Mach message trailer for a message that has been received. +//! +//! This function must only be called on Mach messages that have been received +//! via the Mach messaging interface, such as `mach_msg()`. Messages constructed +//! for sending do not contain trailers. +//! +//! \param[in] header A pointer to a received Mach message. +//! +//! \return A pointer to the trailer following the received Mach message’s body. +//! The contents of the trailer depend on the options provided to +//! `mach_msg()` or a similar function when the message was received. +const mach_msg_trailer_t* MachMessageTrailerFromHeader( + const mach_msg_header_t* header); + +//! \brief Returns the process ID of a Mach message’s sender from its audit +//! trailer. +//! +//! For the audit trailer to be present, the message must have been received +//! with #kMachMessageReceiveAuditTrailer or its macro equivalent specified in +//! the receive options. +//! +//! If the kernel is the message’s sender, a process ID of `0` will be returned. +//! +//! \param[in] trailer The trailer received with a Mach message. +//! +//! \return The process ID of the message’s sender, or `-1` on failure with a +//! message logged. It is considered a failure for \a trailer to not contain +//! audit information. +pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer); + +//! \brief Destroys or deallocates a Mach port received in a Mach message. +//! +//! This function disposes of port rights received in a Mach message. Receive +//! rights will be destroyed with `mach_port_mod_refs()`. Send and send-once +//! rights will be deallocated with `mach_port_deallocate()`. +//! +//! \param[in] port The port to destroy or deallocate. +//! \param[in] port_right_type The right type held for \a port: +//! `MACH_MSG_TYPE_PORT_RECEIVE`, `MACH_MSG_TYPE_PORT_SEND`, or +//! `MACH_MSG_TYPE_PORT_SEND_ONCE`. +//! +//! \return `true` on success, or `false` on failure with a message logged. +bool MachMessageDestroyReceivedPort(mach_port_t port, + mach_msg_type_name_t port_right_type); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message_server.cc b/third_party/crashpad/crashpad/util/mach/mach_message_server.cc new file mode 100644 index 0000000..0af1901 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_message_server.cc
@@ -0,0 +1,280 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/mach_message_server.h" + +#include <string.h> + +#include <limits> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_vm.h" +#include "util/mach/mach_message.h" + +namespace crashpad { + +namespace { + +//! \brief Manages a dynamically-allocated buffer to be used for Mach messaging. +class MachMessageBuffer { + public: + MachMessageBuffer() : vm_() {} + + ~MachMessageBuffer() {} + + //! \return A pointer to the buffer. + mach_msg_header_t* Header() const { + return reinterpret_cast<mach_msg_header_t*>(vm_.address()); + } + + //! \brief Ensures that this object has a buffer of exactly \a size bytes + //! available. + //! + //! If the existing buffer is a different size, it will be reallocated without + //! copying any of the old buffer’s contents to the new buffer. The contents + //! of the buffer are unspecified after this call, even if no reallocation is + //! performed. + kern_return_t Reallocate(vm_size_t size) { + // This test uses == instead of > so that a large reallocation to receive a + // large message doesn’t cause permanent memory bloat for the duration of + // a MachMessageServer::Run() loop. + if (size != vm_.size()) { + // reset() first, so that two allocations don’t exist simultaneously. + vm_.reset(); + + if (size) { + vm_address_t address; + kern_return_t kr = + vm_allocate(mach_task_self(), + &address, + size, + VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); + if (kr != KERN_SUCCESS) { + return kr; + } + + vm_.reset(address, size); + } + } + +#if !defined(NDEBUG) + // Regardless of whether the allocation was changed, scribble over the + // memory to make sure that nothing relies on zero-initialization or stale + // contents. + memset(Header(), 0x66, size); +#endif + + return KERN_SUCCESS; + } + + private: + base::mac::ScopedMachVM vm_; + + DISALLOW_COPY_AND_ASSIGN(MachMessageBuffer); +}; + +// Wraps MachMessageWithDeadline(), using a MachMessageBuffer argument which +// will be resized to |receive_size| (after being page-rounded). MACH_RCV_MSG +// is always combined into |options|. +mach_msg_return_t MachMessageAllocateReceive(MachMessageBuffer* request, + mach_msg_option_t options, + mach_msg_size_t receive_size, + mach_port_name_t receive_port, + MachMessageDeadline deadline, + mach_port_name_t notify_port, + bool run_even_if_expired) { + mach_msg_size_t request_alloc = round_page(receive_size); + kern_return_t kr = request->Reallocate(request_alloc); + if (kr != KERN_SUCCESS) { + return kr; + } + + return MachMessageWithDeadline(request->Header(), + options | MACH_RCV_MSG, + receive_size, + receive_port, + deadline, + notify_port, + run_even_if_expired); +} + +} // namespace + +// This method implements a server similar to 10.9.4 +// xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(). The server +// callback function and |max_size| parameter have been replaced with a C++ +// interface. The |persistent| parameter has been added, allowing this method to +// serve as a stand-in for mach_msg_server(). The |timeout_ms| parameter has +// been added, allowing this function to not block indefinitely. +// +// static +mach_msg_return_t MachMessageServer::Run(Interface* interface, + mach_port_t receive_port, + mach_msg_options_t options, + Persistent persistent, + ReceiveLarge receive_large, + mach_msg_timeout_t timeout_ms) { + options &= ~(MACH_RCV_MSG | MACH_SEND_MSG); + + const MachMessageDeadline deadline = + MachMessageDeadlineFromTimeout(timeout_ms); + + if (receive_large == kReceiveLargeResize) { + options |= MACH_RCV_LARGE; + } else { + options &= ~MACH_RCV_LARGE; + } + + const mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options); + const mach_msg_size_t expected_receive_size = + round_msg(interface->MachMessageServerRequestSize()) + trailer_alloc; + const mach_msg_size_t request_size = (receive_large == kReceiveLargeResize) + ? round_page(expected_receive_size) + : expected_receive_size; + DCHECK_GE(request_size, sizeof(mach_msg_empty_rcv_t)); + + // mach_msg_server() and mach_msg_server_once() would consider whether + // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this + // computation if it does, but that option is ineffective on OS X. + const mach_msg_size_t reply_size = interface->MachMessageServerReplySize(); + DCHECK_GE(reply_size, sizeof(mach_msg_empty_send_t)); + const mach_msg_size_t reply_alloc = round_page(reply_size); + + MachMessageBuffer request; + MachMessageBuffer reply; + bool received_any_request = false; + bool retry; + + kern_return_t kr; + + do { + retry = false; + + kr = MachMessageAllocateReceive(&request, + options, + request_size, + receive_port, + deadline, + MACH_PORT_NULL, + !received_any_request); + if (kr == MACH_RCV_TOO_LARGE) { + switch (receive_large) { + case kReceiveLargeError: + break; + + case kReceiveLargeIgnore: + // Try again, even in one-shot mode. The caller is expecting this + // method to take action on the first message in the queue, and has + // indicated that they want large messages to be ignored. The + // alternatives, which might involve returning MACH_MSG_SUCCESS, + // MACH_RCV_TIMED_OUT, or MACH_RCV_TOO_LARGE to a caller that + // specified one-shot behavior, all seem less correct than retrying. + MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; + retry = true; + continue; + + case kReceiveLargeResize: { + mach_msg_size_t this_request_size = round_page( + round_msg(request.Header()->msgh_size) + trailer_alloc); + DCHECK_GT(this_request_size, request_size); + + kr = MachMessageAllocateReceive(&request, + options & ~MACH_RCV_LARGE, + this_request_size, + receive_port, + deadline, + MACH_PORT_NULL, + !received_any_request); + + break; + } + } + } + + if (kr != MACH_MSG_SUCCESS) { + return kr; + } + + received_any_request = true; + + kr = reply.Reallocate(reply_alloc); + if (kr != KERN_SUCCESS) { + return kr; + } + + mach_msg_header_t* request_header = request.Header(); + mach_msg_header_t* reply_header = reply.Header(); + bool destroy_complex_request = false; + interface->MachMessageServerFunction( + request_header, reply_header, &destroy_complex_request); + + if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { + // This only works if the reply message is not complex, because otherwise, + // the location of the RetCode field is not known. It should be possible + // to locate the RetCode field by looking beyond the descriptors in a + // complex reply message, but this is not currently done. This behavior + // has not proven itself necessary in practice, and it’s not done by + // mach_msg_server() or mach_msg_server_once() either. + mig_reply_error_t* reply_mig = + reinterpret_cast<mig_reply_error_t*>(reply_header); + if (reply_mig->RetCode == MIG_NO_REPLY) { + reply_header->msgh_remote_port = MACH_PORT_NULL; + } else if (reply_mig->RetCode != KERN_SUCCESS && + request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { + destroy_complex_request = true; + } + } + + if (destroy_complex_request && + request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { + request_header->msgh_remote_port = MACH_PORT_NULL; + mach_msg_destroy(request_header); + } + + if (reply_header->msgh_remote_port != MACH_PORT_NULL) { + // Avoid blocking indefinitely. This duplicates the logic in 10.9.5 + // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg_server_once(), + // although the special provision for sending to a send-once right is not + // made, because kernel keeps sends to a send-once right on the fast path + // without considering the user-specified timeout. See 10.9.5 + // xnu-2422.115.4/osfmk/ipc/ipc_mqueue.c ipc_mqueue_send(). + const MachMessageDeadline send_deadline = + deadline == kMachMessageDeadlineWaitIndefinitely + ? kMachMessageDeadlineNonblocking + : deadline; + + kr = MachMessageWithDeadline(reply_header, + options | MACH_SEND_MSG, + 0, + MACH_PORT_NULL, + send_deadline, + MACH_PORT_NULL, + true); + + if (kr != MACH_MSG_SUCCESS) { + if (kr == MACH_SEND_INVALID_DEST || + kr == MACH_SEND_TIMED_OUT || + kr == MACH_SEND_INTERRUPTED) { + mach_msg_destroy(reply_header); + } + return kr; + } + } + } while (persistent == kPersistent || retry); + + return kr; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message_server.h b/third_party/crashpad/crashpad/util/mach/mach_message_server.h new file mode 100644 index 0000000..484ee8bb1 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_message_server.h
@@ -0,0 +1,179 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_ +#define CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_ + +#include <mach/mach.h> + +#include <set> + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Runs a Mach message server to handle a Mach RPC request for MIG +//! servers. +//! +//! The principal entry point to this interface is the static Run() method. +class MachMessageServer { + public: + //! \brief A Mach RPC callback interface, called by Run(). + class Interface { + public: + //! \brief Handles a Mach RPC request. + //! + //! This method is a stand-in for a MIG-generated Mach RPC server “demux” + //! function such as `exc_server()` and `mach_exc_server()`. Implementations + //! may call such a function directly. This method is expected to behave + //! exactly as these functions behave. + //! + //! \param[in] in The request message, received as a Mach message. Note that + //! this interface uses a `const` parameter for this purpose, whereas + //! MIG-generated “demux” functions do not. + //! \param[out] out The reply message. The caller allocates storage, and the + //! callee is expected to populate the reply message appropriately. + //! After returning, the caller will send this reply as a Mach message + //! via the message’s reply port. + //! \param[out] destroy_complex_request `true` if a complex request message + //! is to be destroyed even when handled successfully, `false` + //! otherwise. The traditional behavior is `false`. In this case, the + //! caller only destroys the request message in \a in when the reply + //! message in \a out is not complex and when it indicates a return code + //! other than `KERN_SUCCESS` or `MIG_NO_REPLY`. The assumption is that + //! the rights or out-of-line data carried in a complex message may be + //! retained by the server in this situation, and that it is the + //! responsibility of the server to release these resources as needed. + //! However, in many cases, these resources are not needed beyond the + //! duration of a request-reply transaction, and in such cases, it is + //! less error-prone to always have the caller, + //! MachMessageServer::Run(), destroy complex request messages. To + //! choose this behavior, this parameter should be set to `true`. + //! + //! \return `true` on success and `false` on failure, although the caller + //! ignores the return value. However, the return code to be included in + //! the reply message should be set as `mig_reply_error_t::RetCode`. The + //! non-`void` return value is used for increased compatibility with + //! MIG-generated functions. + virtual bool MachMessageServerFunction(const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) = 0; + + //! \return The set of request message Mach message IDs that + //! MachMessageServerFunction() is able to handle. + virtual std::set<mach_msg_id_t> MachMessageServerRequestIDs() = 0; + + //! \return The expected or maximum size, in bytes, of a request message to + //! be received as the \a in parameter of MachMessageServerFunction(). + virtual mach_msg_size_t MachMessageServerRequestSize() = 0; + + //! \return The maximum size, in bytes, of a reply message to be sent via + //! the \a out parameter of MachMessageServerFunction(). This value does + //! not need to include the size of any trailer to be sent with the + //! message. + virtual mach_msg_size_t MachMessageServerReplySize() = 0; + + protected: + ~Interface() {} + }; + + //! \brief Informs Run() whether to handle a single request-reply transaction + //! or to run in a loop. + enum Persistent { + //! \brief Handle a single request-reply transaction and then return. + kOneShot = false, + + //! \brief Run in a loop, potentially handling multiple request-reply + //! transactions. + kPersistent, + }; + + //! \brief Determines how to handle the reception of messages larger than the + //! size of the buffer allocated to store them. + enum ReceiveLarge { + //! \brief Return `MACH_RCV_TOO_LARGE` upon receipt of a large message. + //! + //! This mimics the default behavior of `mach_msg_server()` when `options` + //! does not contain `MACH_RCV_LARGE`. + kReceiveLargeError = 0, + + //! \brief Ignore large messages, and attempt to receive the next queued + //! message upon encountering one. + //! + //! When a large message is encountered, a warning will be logged. + //! + //! `mach_msg()` will be called to receive the next message after a large + //! one even when accompanied by a #Persistent value of #kOneShot. + kReceiveLargeIgnore, + + //! \brief Allocate an appropriately-sized buffer upon encountering a large + //! message. The buffer will be used to receive the message. This + //! + //! This mimics the behavior of `mach_msg_server()` when `options` contains + //! `MACH_RCV_LARGE`. + kReceiveLargeResize, + }; + + //! \brief Runs a Mach message server to handle a Mach RPC request for MIG + //! servers. + //! + //! This function listens for a request message and passes it to a callback + //! interface. A reponse is collected from that interface, and is sent back as + //! a reply. + //! + //! This function is similar to `mach_msg_server()` and + //! `mach_msg_server_once()`. + //! + //! \param[in] interface The MachMessageServerInterface that is responsible + //! for handling the message. Interface::MachMessageServerRequestSize() is + //! used as the receive size for the request message, and + //! Interface::MachMessageServerReplySize() is used as the + //! maximum size of the reply message. If \a options contains + //! `MACH_RCV_LARGE`, this function will retry a receive operation that + //! returns `MACH_RCV_TOO_LARGE` with an appropriately-sized buffer. + //! MachMessageServerInterface::MachMessageServerFunction() is called to + //! handle the request and populate the reply. + //! \param[in] receive_port The port on which to receive the request message. + //! \param[in] options Options suitable for mach_msg. For the defaults, use + //! `MACH_MSG_OPTION_NONE`. `MACH_RCV_LARGE` when specified here is + //! ignored. Set \a receive_large to #kReceiveLargeResize instead. + //! \param[in] persistent Chooses between one-shot and persistent operation. + //! \param[in] receive_large Determines the behavior upon encountering a + //! message larger than the receive buffer’s size. + //! \param[in] timeout_ms The maximum duration that this entire function will + //! run, in milliseconds. This may be #kMachMessageTimeoutNonblocking or + //! #kMachMessageTimeoutWaitIndefinitely. When \a persistent is + //! #kPersistent, the timeout applies to the overall duration of this + //! function, not to any individual `mach_msg()` call. + //! + //! \return On success, `MACH_MSG_SUCCESS` (when \a persistent is #kOneShot) + //! or `MACH_RCV_TIMED_OUT` (when \a persistent is #kOneShot and \a + //! timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function + //! has no successful return value when \a persistent is #kPersistent and + //! \a timeout_ms is #kMachMessageTimeoutWaitIndefinitely. On failure, + //! returns a value identifying the nature of the error. + static mach_msg_return_t Run(Interface* interface, + mach_port_t receive_port, + mach_msg_options_t options, + Persistent persistent, + ReceiveLarge receive_large, + mach_msg_timeout_t timeout_ms); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MachMessageServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc b/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc new file mode 100644 index 0000000..a59554b3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc
@@ -0,0 +1,854 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/mach_message_server.h" + +#include <mach/mach.h> +#include <string.h> + +#include <set> + +#include "base/basictypes.h" +#include "base/mac/scoped_mach_port.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/file/file_io.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMachMessageServer : public MachMessageServer::Interface, + public MachMultiprocess { + public: + struct Options { + // The type of reply port that the client should put in its request message. + enum ReplyPortType { + // The normal reply port is the client’s local port, to which it holds + // a receive right. This allows the server to respond directly to the + // client. The client will expect a reply. + kReplyPortNormal, + + // Use MACH_PORT_NULL as the reply port, which the server should detect + // avoid attempting to send a message to, and return success. The client + // will not expect a reply. + kReplyPortNull, + + // Make the server see the reply port as a dead name by setting the reply + // port to a receive right and then destroying that right before the + // server processes the request. The server should return + // MACH_SEND_INVALID_DEST, and the client will not expect a reply. + kReplyPortDead, + }; + + Options() + : expect_server_interface_method_called(true), + parent_wait_for_child_pipe(false), + server_options(MACH_MSG_OPTION_NONE), + server_persistent(MachMessageServer::kOneShot), + server_receive_large(MachMessageServer::kReceiveLargeError), + server_timeout_ms(kMachMessageTimeoutWaitIndefinitely), + server_mig_retcode(KERN_SUCCESS), + server_destroy_complex(true), + expect_server_destroyed_complex(true), + expect_server_result(KERN_SUCCESS), + expect_server_transaction_count(1), + child_wait_for_parent_pipe_early(false), + client_send_request_count(1), + client_send_complex(false), + client_send_large(false), + client_reply_port_type(kReplyPortNormal), + client_expect_reply(true), + child_send_all_requests_before_receiving_any_replies(false), + child_wait_for_parent_pipe_late(false) { + } + + // true if MachMessageServerFunction() is expected to be called. + bool expect_server_interface_method_called; + + // true if the parent should wait for the child to write a byte to the pipe + // as a signal that the child is ready for the parent to begin its side of + // the test. This is used for nonblocking tests, which require that there + // be something in the server’s queue before attempting a nonblocking + // receive if the receive is to be successful. + bool parent_wait_for_child_pipe; + + // Options to pass to MachMessageServer::Run() as the |options| parameter. + mach_msg_options_t server_options; + + // Whether the server should run in one-shot or persistent mode. + MachMessageServer::Persistent server_persistent; + + // The strategy for handling large messages. + MachMessageServer::ReceiveLarge server_receive_large; + + // The server’s timeout in milliseconds, or kMachMessageTimeoutNonblocking + // or kMachMessageTimeoutWaitIndefinitely. + mach_msg_timeout_t server_timeout_ms; + + // The return code that the server returns to the client via the + // mig_reply_error_t::RetCode field. A client would normally see this as + // a Mach RPC return value. + kern_return_t server_mig_retcode; + + // The value that the server function should set its destroy_complex_request + // parameter to. This is true if resources sent in complex request messages + // should be destroyed, and false if they should not be destroyed, assuming + // that the server function indicates success. + bool server_destroy_complex; + + // Whether to expect the server to destroy a complex message. Even if + // server_destroy_complex is false, a complex message will be destroyed if + // the MIG return code was unsuccessful. + bool expect_server_destroyed_complex; + + // The expected return value from MachMessageServer::Run(). + kern_return_t expect_server_result; + + // The number of transactions that the server is expected to handle. + size_t expect_server_transaction_count; + + // true if the child should wait for the parent to signal that it’s ready + // for the child to begin sending requests via the pipe. This is done if the + // parent needs to perform operations on its receive port before the child + // should be permitted to send anything to it. Currently, this is used to + // allow the parent to ensure that the receive port’s queue length is high + // enough before the child begins attempting to fill it. + bool child_wait_for_parent_pipe_early; + + // The number of requests that the client should send to the server. + size_t client_send_request_count; + + // true if the client should send a complex message, one that carries a port + // descriptor in its body. Normally false. + bool client_send_complex; + + // true if the client should send a larger message than the server has + // allocated space to receive. The server’s response is directed by + // server_receive_large. + bool client_send_large; + + // The type of reply port that the client should provide in its request’s + // mach_msg_header_t::msgh_local_port, which will appear to the server as + // mach_msg_header_t::msgh_remote_port. + ReplyPortType client_reply_port_type; + + // true if the client should wait for a reply from the server. For + // non-normal reply ports or requests which the server responds to with no + // reply (MIG_NO_REPLY), the server will either not send a reply or not + // succeed in sending a reply, and the child process should not wait for + // one. + bool client_expect_reply; + + // true if the client should send all requests before attempting to receive + // any replies from the server. This is used for the persistent nonblocking + // test, which requires the client to fill the server’s queue before the + // server can attempt processing it. + bool child_send_all_requests_before_receiving_any_replies; + + // true if the child should wait to receive a byte from the parent before + // exiting. This can be used to keep a receive right in the child alive + // until the parent has a chance to verify that it’s holding a send right. + // Otherwise, the right might appear in the parent as a dead name if the + // child exited before the parent had a chance to examine it. This would be + // a race. + bool child_wait_for_parent_pipe_late; + }; + + explicit TestMachMessageServer(const Options& options) + : MachMessageServer::Interface(), + MachMultiprocess(), + options_(options), + child_complex_message_port_(), + parent_complex_message_port_(MACH_PORT_NULL) { + } + + // Runs the test. + void Test() { + EXPECT_EQ(requests_, replies_); + uint32_t start = requests_; + + Run(); + + EXPECT_EQ(requests_, replies_); + EXPECT_EQ(options_.expect_server_transaction_count, requests_ - start); + } + + // MachMessageServerInterface: + + virtual bool MachMessageServerFunction( + const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) override { + *destroy_complex_request = options_.server_destroy_complex; + + EXPECT_TRUE(options_.expect_server_interface_method_called); + if (!options_.expect_server_interface_method_called) { + return false; + } + + struct ReceiveRequestMessage : public RequestMessage { + mach_msg_trailer_t trailer; + }; + + struct ReceiveLargeRequestMessage : public LargeRequestMessage { + mach_msg_trailer_t trailer; + }; + + const ReceiveRequestMessage* request = + reinterpret_cast<const ReceiveRequestMessage*>(in); + const mach_msg_bits_t expect_msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) | + (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0); + EXPECT_EQ(expect_msgh_bits, request->header.msgh_bits); + EXPECT_EQ(options_.client_send_large ? sizeof(LargeRequestMessage) + : sizeof(RequestMessage), + request->header.msgh_size); + if (options_.client_reply_port_type == Options::kReplyPortNormal) { + EXPECT_EQ(RemotePort(), request->header.msgh_remote_port); + } + EXPECT_EQ(LocalPort(), request->header.msgh_local_port); + EXPECT_EQ(kRequestMessageID, request->header.msgh_id); + if (options_.client_send_complex) { + EXPECT_EQ(1u, request->body.msgh_descriptor_count); + EXPECT_NE(kMachPortNull, request->port_descriptor.name); + parent_complex_message_port_ = request->port_descriptor.name; + EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND), + request->port_descriptor.disposition); + EXPECT_EQ( + implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR), + request->port_descriptor.type); + } else { + EXPECT_EQ(0u, request->body.msgh_descriptor_count); + EXPECT_EQ(kMachPortNull, request->port_descriptor.name); + EXPECT_EQ(0u, request->port_descriptor.disposition); + EXPECT_EQ(0u, request->port_descriptor.type); + } + EXPECT_EQ(0, memcmp(&request->ndr, &NDR_record, sizeof(NDR_record))); + EXPECT_EQ(requests_, request->number); + + // Look for the trailer in the right spot, depending on whether the request + // message was a RequestMessage or a LargeRequestMessage. + const mach_msg_trailer_t* trailer; + if (options_.client_send_large) { + const ReceiveLargeRequestMessage* large_request = + reinterpret_cast<const ReceiveLargeRequestMessage*>(request); + for (size_t index = 0; index < sizeof(large_request->data); ++index) { + EXPECT_EQ('!', large_request->data[index]); + } + trailer = &large_request->trailer; + } else { + trailer = &request->trailer; + } + + EXPECT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0), + trailer->msgh_trailer_type); + EXPECT_EQ(MACH_MSG_TRAILER_MINIMUM_SIZE, trailer->msgh_trailer_size); + + ++requests_; + + ReplyMessage* reply = reinterpret_cast<ReplyMessage*>(out); + reply->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + reply->Head.msgh_size = sizeof(*reply); + reply->Head.msgh_remote_port = request->header.msgh_remote_port; + reply->Head.msgh_local_port = MACH_PORT_NULL; + reply->Head.msgh_id = kReplyMessageID; + reply->NDR = NDR_record; + reply->RetCode = options_.server_mig_retcode; + reply->number = replies_++; + + return true; + } + + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override { + const mach_msg_id_t request_ids[] = {kRequestMessageID}; + return std::set<mach_msg_id_t>(&request_ids[0], + &request_ids[arraysize(request_ids)]); + } + + mach_msg_size_t MachMessageServerRequestSize() override { + return sizeof(RequestMessage); + } + + mach_msg_size_t MachMessageServerReplySize() override { + return sizeof(ReplyMessage); + } + + private: + struct RequestMessage : public mach_msg_base_t { + // If body.msgh_descriptor_count is 0, port_descriptor will still be + // present, but it will be zeroed out. It wouldn’t normally be present in a + // message froma MIG-generated interface, but it’s harmless and simpler to + // leave it here and just treat it as more data. + mach_msg_port_descriptor_t port_descriptor; + + NDR_record_t ndr; + uint32_t number; + }; + + // LargeRequestMessage is larger enough than a regular RequestMessage to + // ensure that whatever buffer was allocated to receive a RequestMessage is + // not large enough to receive a LargeRequestMessage. + struct LargeRequestMessage : public RequestMessage { + uint8_t data[4 * PAGE_SIZE]; + }; + + struct ReplyMessage : public mig_reply_error_t { + uint32_t number; + }; + + // MachMultiprocess: + + void MachMultiprocessParent() override { + mach_port_t local_port = LocalPort(); + + kern_return_t kr; + if (options_.child_send_all_requests_before_receiving_any_replies) { + // On Mac OS X 10.10, the queue limit of a new Mach port seems to be 2 + // by default, which is below the value of MACH_PORT_QLIMIT_DEFAULT. Set + // the port’s queue limit explicitly here. + mach_port_limits limits = {}; + limits.mpl_qlimit = MACH_PORT_QLIMIT_DEFAULT; + kr = mach_port_set_attributes(mach_task_self(), + local_port, + MACH_PORT_LIMITS_INFO, + reinterpret_cast<mach_port_info_t>(&limits), + MACH_PORT_LIMITS_INFO_COUNT); + ASSERT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_set_attributes"); + } + + if (options_.child_wait_for_parent_pipe_early) { + // Tell the child to begin sending messages. + char c = '\0'; + CheckedWriteFile(WritePipeHandle(), &c, 1); + } + + if (options_.parent_wait_for_child_pipe) { + // Wait until the child is done sending what it’s going to send. + char c; + CheckedReadFile(ReadPipeHandle(), &c, 1); + EXPECT_EQ('\0', c); + } + + ASSERT_EQ(options_.expect_server_result, + (kr = MachMessageServer::Run(this, + local_port, + options_.server_options, + options_.server_persistent, + options_.server_receive_large, + options_.server_timeout_ms))) + << MachErrorMessage(kr, "MachMessageServer"); + + if (options_.client_send_complex) { + EXPECT_NE(kMachPortNull, parent_complex_message_port_); + mach_port_type_t type; + + if (!options_.expect_server_destroyed_complex) { + // MachMessageServer should not have destroyed the resources sent in the + // complex request message. + kr = mach_port_type( + mach_task_self(), parent_complex_message_port_, &type); + EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_type"); + EXPECT_EQ(MACH_PORT_TYPE_SEND, type); + + // Destroy the resources here. + kr = mach_port_deallocate(mach_task_self(), + parent_complex_message_port_); + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_deallocate"); + } + + // The kernel won’t have reused the same name for another Mach port in + // this task so soon. It’s possible that something else in this task could + // have reused the name, but it’s unlikely for that to have happened in + // this test environment. + kr = + mach_port_type(mach_task_self(), parent_complex_message_port_, &type); + EXPECT_EQ(KERN_INVALID_NAME, kr) + << MachErrorMessage(kr, "mach_port_type"); + } + + if (options_.child_wait_for_parent_pipe_late) { + // Let the child know it’s safe to exit. + char c = '\0'; + CheckedWriteFile(WritePipeHandle(), &c, 1); + } + } + + void MachMultiprocessChild() override { + if (options_.child_wait_for_parent_pipe_early) { + // Wait until the parent is done setting things up on its end. + char c; + CheckedReadFile(ReadPipeHandle(), &c, 1); + EXPECT_EQ('\0', c); + } + + for (size_t index = 0; + index < options_.client_send_request_count; + ++index) { + if (options_.child_send_all_requests_before_receiving_any_replies) { + // For this test, all of the messages need to go into the queue before + // the parent is allowed to start processing them. Don’t attempt to + // process replies before all of the requests are sent, because the + // server won’t have sent any replies until all of the requests are in + // its queue. + ASSERT_NO_FATAL_FAILURE(ChildSendRequest()); + } else { + ASSERT_NO_FATAL_FAILURE(ChildSendRequestAndWaitForReply()); + } + } + + if (options_.parent_wait_for_child_pipe && + options_.child_send_all_requests_before_receiving_any_replies) { + // Now that all of the requests have been sent, let the parent know that + // it’s safe to begin processing them, and then wait for the replies. + ASSERT_NO_FATAL_FAILURE(ChildNotifyParentViaPipe()); + + for (size_t index = 0; + index < options_.client_send_request_count; + ++index) { + ASSERT_NO_FATAL_FAILURE(ChildWaitForReply()); + } + } + + if (options_.child_wait_for_parent_pipe_late) { + char c; + CheckedReadFile(ReadPipeHandle(), &c, 1); + ASSERT_EQ('\0', c); + } + } + + // In the child process, sends a request message to the server. + void ChildSendRequest() { + // local_receive_port_owner will the receive right that is created in this + // scope and intended to be destroyed when leaving this scope, after it has + // been carried in a Mach message. + base::mac::ScopedMachReceiveRight local_receive_port_owner; + + // A LargeRequestMessage is always allocated, but the message that will be + // sent will be a normal RequestMessage due to the msgh_size field + // indicating the size of the smaller base structure unless + // options_.client_send_large is true. + LargeRequestMessage request = {}; + + request.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) | + (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0); + request.header.msgh_size = options_.client_send_large ? + sizeof(LargeRequestMessage) : sizeof(RequestMessage); + request.header.msgh_remote_port = RemotePort(); + kern_return_t kr; + switch (options_.client_reply_port_type) { + case Options::kReplyPortNormal: + request.header.msgh_local_port = LocalPort(); + break; + case Options::kReplyPortNull: + request.header.msgh_local_port = MACH_PORT_NULL; + break; + case Options::kReplyPortDead: { + // Use a newly-allocated receive right that will be destroyed when this + // method returns. A send right will be made from this receive right and + // carried in the request message to the server. By the time the server + // looks at the right, it will have become a dead name. + local_receive_port_owner.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(local_receive_port_owner.is_valid()); + request.header.msgh_local_port = local_receive_port_owner.get(); + break; + } + } + request.header.msgh_id = kRequestMessageID; + if (options_.client_send_complex) { + // Allocate a new receive right in this process and make a send right that + // will appear in the parent process. This is used to test that the server + // properly handles ownership of resources received in complex messages. + request.body.msgh_descriptor_count = 1; + child_complex_message_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(child_complex_message_port_.is_valid()); + request.port_descriptor.name = child_complex_message_port_.get(); + request.port_descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND; + request.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR; + } else { + request.body.msgh_descriptor_count = 0; + request.port_descriptor.name = MACH_PORT_NULL; + request.port_descriptor.disposition = 0; + request.port_descriptor.type = 0; + } + request.ndr = NDR_record; + request.number = requests_++; + + if (options_.client_send_large) { + memset(request.data, '!', sizeof(request.data)); + } + + kr = mach_msg(&request.header, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + request.header.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); + } + + // In the child process, waits for a reply message from the server. + void ChildWaitForReply() { + if (!options_.client_expect_reply) { + // The client shouldn’t expect a reply when it didn’t send a good reply + // port with its request, or when testing the server behaving in a way + // that doesn’t send replies. + return; + } + + struct ReceiveReplyMessage : public ReplyMessage { + mach_msg_trailer_t trailer; + }; + + ReceiveReplyMessage reply = {}; + kern_return_t kr = mach_msg(&reply.Head, + MACH_RCV_MSG, + 0, + sizeof(reply), + LocalPort(), + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg"); + + ASSERT_EQ(implicit_cast<mach_msg_bits_t>( + MACH_MSGH_BITS(0, MACH_MSG_TYPE_MOVE_SEND)), reply.Head.msgh_bits); + ASSERT_EQ(sizeof(ReplyMessage), reply.Head.msgh_size); + ASSERT_EQ(kMachPortNull, reply.Head.msgh_remote_port); + ASSERT_EQ(LocalPort(), reply.Head.msgh_local_port); + ASSERT_EQ(kReplyMessageID, reply.Head.msgh_id); + ASSERT_EQ(0, memcmp(&reply.NDR, &NDR_record, sizeof(NDR_record))); + ASSERT_EQ(options_.server_mig_retcode, reply.RetCode); + ASSERT_EQ(replies_, reply.number); + ASSERT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0), + reply.trailer.msgh_trailer_type); + ASSERT_EQ(MACH_MSG_TRAILER_MINIMUM_SIZE, reply.trailer.msgh_trailer_size); + + ++replies_; + } + + // For test types where the child needs to notify the server in the parent + // that the child is ready, this method will send a byte via the POSIX pipe. + // The parent will be waiting in a read() on this pipe, and will proceed to + // running MachMessageServer() once it’s received. + void ChildNotifyParentViaPipe() { + char c = '\0'; + CheckedWriteFile(WritePipeHandle(), &c, 1); + } + + // In the child process, sends a request message to the server and then + // receives a reply message. + void ChildSendRequestAndWaitForReply() { + ASSERT_NO_FATAL_FAILURE(ChildSendRequest()); + + if (options_.parent_wait_for_child_pipe && + !options_.child_send_all_requests_before_receiving_any_replies) { + // The parent is waiting to read a byte to indicate that the message has + // been placed in the queue. + ASSERT_NO_FATAL_FAILURE(ChildNotifyParentViaPipe()); + } + + ASSERT_NO_FATAL_FAILURE(ChildWaitForReply()); + } + + const Options& options_; + + // A receive right allocated in the child process. A send right will be + // created from this right and sent to the parent parent process in the + // request message. + base::mac::ScopedMachReceiveRight child_complex_message_port_; + + // The send right received in the parent process. This right is stored in a + // member variable to test that resources carried in complex messages are + // properly destroyed in the server when expected. + mach_port_t parent_complex_message_port_; + + static uint32_t requests_; + static uint32_t replies_; + + static const mach_msg_id_t kRequestMessageID = 16237; + static const mach_msg_id_t kReplyMessageID = kRequestMessageID + 100; + + DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer); +}; + +uint32_t TestMachMessageServer::requests_; +uint32_t TestMachMessageServer::replies_; +const mach_msg_id_t TestMachMessageServer::kRequestMessageID; +const mach_msg_id_t TestMachMessageServer::kReplyMessageID; + +TEST(MachMessageServer, Basic) { + // The client sends one message to the server, which will wait indefinitely in + // blocking mode for it. + TestMachMessageServer::Options options; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, NonblockingNoMessage) { + // The server waits in nonblocking mode and the client sends nothing, so the + // server should return immediately without processing any message. + TestMachMessageServer::Options options; + options.expect_server_interface_method_called = false; + options.server_timeout_ms = kMachMessageTimeoutNonblocking; + options.expect_server_result = MACH_RCV_TIMED_OUT; + options.expect_server_transaction_count = 0; + options.client_send_request_count = 0; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, TimeoutNoMessage) { + // The server waits in blocking mode for one message, but with a timeout. The + // client sends no message, so the server returns after the timeout. + TestMachMessageServer::Options options; + options.expect_server_interface_method_called = false; + options.server_timeout_ms = 10; + options.expect_server_result = MACH_RCV_TIMED_OUT; + options.expect_server_transaction_count = 0; + options.client_send_request_count = 0; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, Nonblocking) { + // The client sends one message to the server and then signals the server that + // it’s safe to start waiting for it in nonblocking mode. The message is in + // the server’s queue, so it’s able to receive it when it begins listening in + // nonblocking mode. + TestMachMessageServer::Options options; + options.parent_wait_for_child_pipe = true; + options.server_timeout_ms = kMachMessageTimeoutNonblocking; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, Timeout) { + // The client sends one message to the server, which will wait in blocking + // mode for it up to a specific timeout. + TestMachMessageServer::Options options; + options.server_timeout_ms = 10; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, PersistentTenMessages) { + // The server waits for as many messages as it can receive in blocking mode + // with a timeout. The client sends several messages, and the server processes + // them all. + TestMachMessageServer::Options options; + options.server_persistent = MachMessageServer::kPersistent; + options.server_timeout_ms = 10; + options.expect_server_result = MACH_RCV_TIMED_OUT; + options.expect_server_transaction_count = 10; + options.client_send_request_count = 10; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, PersistentNonblockingFourMessages) { + // The client sends several messages to the server and then signals the server + // that it’s safe to start waiting for them in nonblocking mode. The server + // then listens for them in nonblocking persistent mode, and receives all of + // them because they’ve been queued up. The client doesn’t wait for the + // replies until after it’s put all of its requests into the server’s queue. + // + // This test is sensitive to the length of the IPC queue limit. Mach ports + // normally have a queue length limit of MACH_PORT_QLIMIT_DEFAULT (which is + // MACH_PORT_QLIMIT_BASIC, or 5). The number of messages sent for this test + // must be below this, because the server does not begin dequeueing request + // messages until the client has finished sending them. + // + // The queue limit on new ports has been seen to be below + // MACH_PORT_QLIMIT_DEFAULT, so it will explicitly be set by + // mach_port_set_attributes() for this test. This needs to happen before the + // child is allowed to begin sending messages, so + // child_wait_for_parent_pipe_early is used to make the child wait until the + // parent is ready. + const size_t kTransactionCount = 4; + static_assert(kTransactionCount <= MACH_PORT_QLIMIT_DEFAULT, + "must not exceed queue limit"); + + TestMachMessageServer::Options options; + options.parent_wait_for_child_pipe = true; + options.server_persistent = MachMessageServer::kPersistent; + options.server_timeout_ms = kMachMessageTimeoutNonblocking; + options.expect_server_result = MACH_RCV_TIMED_OUT; + options.expect_server_transaction_count = kTransactionCount; + options.child_wait_for_parent_pipe_early = true; + options.client_send_request_count = kTransactionCount; + options.child_send_all_requests_before_receiving_any_replies = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReturnCodeInvalidArgument) { + // This tests that the mig_reply_error_t::RetCode field is properly returned + // to the client. + TestMachMessageServer::Options options; + options.server_mig_retcode = KERN_INVALID_ARGUMENT; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReturnCodeNoReply) { + // This tests that when mig_reply_error_t::RetCode is set to MIG_NO_REPLY, no + // response is sent to the client. + TestMachMessageServer::Options options; + options.server_mig_retcode = MIG_NO_REPLY; + options.client_expect_reply = false; + options.child_wait_for_parent_pipe_late = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReplyPortNull) { + // The client sets its reply port to MACH_PORT_NULL. The server should see + // this and avoid sending a message to the null port. No reply message is + // sent and the server returns success. + TestMachMessageServer::Options options; + options.client_reply_port_type = + TestMachMessageServer::Options::kReplyPortNull; + options.client_expect_reply = false; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReplyPortDead) { + // The client allocates a new port and uses it as the reply port in its + // request message, and then deallocates its receive right to that port. It + // then signals the server to process the request message. The server’s view + // of the port is that it is a dead name. The server function will return + // MACH_SEND_INVALID_DEST because it’s not possible to send a message to a + // dead name. + TestMachMessageServer::Options options; + options.parent_wait_for_child_pipe = true; + options.expect_server_result = MACH_SEND_INVALID_DEST; + options.client_reply_port_type = + TestMachMessageServer::Options::kReplyPortDead; + options.client_expect_reply = false; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, Complex) { + // The client allocates a new receive right and sends a complex request + // message to the server with a send right made out of this receive right. The + // server receives this message and is instructed to destroy the send right + // when it is done handling the request-reply transaction. The former send + // right is verified to be invalid after the server runs. This test ensures + // that resources transferred to a server process temporarily aren’t leaked. + TestMachMessageServer::Options options; + options.client_send_complex = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ComplexNotDestroyed) { + // As in MachMessageServer.Complex, but the server is instructed not to + // destroy the send right. After the server runs, the send right is verified + // to continue to exist in the server task. The client process is then + // signalled by pipe that it’s safe to exit so that the send right in the + // server task doesn’t prematurely become a dead name. This test ensures that + // rights that are expected to be retained in the server task are properly + // retained. + TestMachMessageServer::Options options; + options.server_destroy_complex = false; + options.expect_server_destroyed_complex = false; + options.client_send_complex = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ComplexDestroyedInvalidArgument) { + // As in MachMessageServer.ComplexNotDestroyed, but the server does not return + // a successful code via MIG. The server is expected to destroy resources in + // this case, because server_destroy_complex = false is only honored when a + // MIG request is handled successfully or with no reply. + TestMachMessageServer::Options options; + options.server_mig_retcode = KERN_INVALID_TASK; + options.server_destroy_complex = false; + options.client_send_complex = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ComplexNotDestroyedNoReply) { + // As in MachMessageServer.ComplexNotDestroyed, but the server does not send + // a reply message and is expected to retain the send right in the server + // task. + TestMachMessageServer::Options options; + options.server_mig_retcode = MIG_NO_REPLY; + options.server_destroy_complex = false; + options.expect_server_destroyed_complex = false; + options.client_send_complex = true; + options.client_expect_reply = false; + options.child_wait_for_parent_pipe_late = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReceiveLargeError) { + // The client sends a request to the server that is larger than the server is + // expecting. server_receive_large is kReceiveLargeError, so the request is + // destroyed and the server returns a MACH_RCV_TOO_LARGE error. The client + // does not receive a reply. + TestMachMessageServer::Options options; + options.expect_server_result = MACH_RCV_TOO_LARGE; + options.expect_server_transaction_count = 0; + options.client_send_large = true; + options.client_expect_reply = false; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReceiveLargeRetry) { + // The client sends a request to the server that is larger than the server is + // initially expecting. server_receive_large is kReceiveLargeResize, so a new + // buffer is allocated to receive the message. The server receives the large + // request message, processes it, and returns a reply to the client. + TestMachMessageServer::Options options; + options.server_receive_large = MachMessageServer::kReceiveLargeResize; + options.client_send_large = true; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +TEST(MachMessageServer, ReceiveLargeIgnore) { + // The client sends a request to the server that is larger than the server is + // expecting. server_receive_large is kReceiveLargeIgnore, so the request is + // destroyed but the server does not consider this an error. The server is + // running in blocking mode with a timeout, and continues to wait for a + // message until it times out. The client does not receive a reply. + TestMachMessageServer::Options options; + options.server_receive_large = MachMessageServer::kReceiveLargeIgnore; + options.server_timeout_ms = 10; + options.expect_server_result = MACH_RCV_TIMED_OUT; + options.expect_server_transaction_count = 0; + options.client_send_large = true; + options.client_expect_reply = false; + TestMachMessageServer test_mach_message_server(options); + test_mach_message_server.Test(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message_test.cc b/third_party/crashpad/crashpad/util/mach/mach_message_test.cc new file mode 100644 index 0000000..d77f0dd3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mach_message_test.cc
@@ -0,0 +1,203 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/mach_message.h" + +#include <unistd.h> + +#include "base/basictypes.h" +#include "base/mac/scoped_mach_port.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MachMessage, MachMessageDeadlineFromTimeout) { + MachMessageDeadline deadline_0 = + MachMessageDeadlineFromTimeout(kMachMessageTimeoutNonblocking); + EXPECT_EQ(kMachMessageDeadlineNonblocking, deadline_0); + + deadline_0 = + MachMessageDeadlineFromTimeout(kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(kMachMessageDeadlineWaitIndefinitely, deadline_0); + + deadline_0 = MachMessageDeadlineFromTimeout(1); + MachMessageDeadline deadline_1 = MachMessageDeadlineFromTimeout(100); + + EXPECT_NE(kMachMessageDeadlineNonblocking, deadline_0); + EXPECT_NE(kMachMessageDeadlineWaitIndefinitely, deadline_0); + EXPECT_NE(kMachMessageDeadlineNonblocking, deadline_1); + EXPECT_NE(kMachMessageDeadlineWaitIndefinitely, deadline_1); + EXPECT_GE(deadline_1, deadline_0); +} + +TEST(MachMessage, PrepareMIGReplyFromRequest_SetMIGReplyError) { + mach_msg_header_t request; + request.msgh_bits = + MACH_MSGH_BITS_COMPLEX | + MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND); + request.msgh_size = 64; + request.msgh_remote_port = 0x01234567; + request.msgh_local_port = 0x89abcdef; + request.msgh_reserved = 0xa5a5a5a5; + request.msgh_id = 1011; + + mig_reply_error_t reply; + + // PrepareMIGReplyFromRequest() doesn’t touch this field. + reply.RetCode = MIG_TYPE_ERROR; + + PrepareMIGReplyFromRequest(&request, &reply.Head); + + EXPECT_EQ(implicit_cast<mach_msg_bits_t>( + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)), + reply.Head.msgh_bits); + EXPECT_EQ(sizeof(reply), reply.Head.msgh_size); + EXPECT_EQ(request.msgh_remote_port, reply.Head.msgh_remote_port); + EXPECT_EQ(kMachPortNull, reply.Head.msgh_local_port); + EXPECT_EQ(0u, reply.Head.msgh_reserved); + EXPECT_EQ(1111, reply.Head.msgh_id); + EXPECT_EQ(NDR_record.mig_vers, reply.NDR.mig_vers); + EXPECT_EQ(NDR_record.if_vers, reply.NDR.if_vers); + EXPECT_EQ(NDR_record.reserved1, reply.NDR.reserved1); + EXPECT_EQ(NDR_record.mig_encoding, reply.NDR.mig_encoding); + EXPECT_EQ(NDR_record.int_rep, reply.NDR.int_rep); + EXPECT_EQ(NDR_record.char_rep, reply.NDR.char_rep); + EXPECT_EQ(NDR_record.float_rep, reply.NDR.float_rep); + EXPECT_EQ(NDR_record.reserved2, reply.NDR.reserved2); + EXPECT_EQ(MIG_TYPE_ERROR, reply.RetCode); + + SetMIGReplyError(&reply.Head, MIG_BAD_ID); + + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); +} + +TEST(MachMessage, MachMessageTrailerFromHeader) { + mach_msg_empty_t empty; + empty.send.header.msgh_size = sizeof(mach_msg_empty_send_t); + EXPECT_EQ(&empty.rcv.trailer, + MachMessageTrailerFromHeader(&empty.rcv.header)); + + struct TestSendMessage : public mach_msg_header_t { + uint8_t data[126]; + }; + struct TestReceiveMessage : public TestSendMessage { + mach_msg_trailer_t trailer; + }; + union TestMessage { + TestSendMessage send; + TestReceiveMessage receive; + }; + + TestMessage test; + test.send.msgh_size = sizeof(TestSendMessage); + EXPECT_EQ(&test.receive.trailer, MachMessageTrailerFromHeader(&test.receive)); +} + +TEST(MachMessage, AuditPIDFromMachMessageTrailer) { + base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_NE(kMachPortNull, port); + + mach_msg_empty_send_t send = {}; + send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); + send.header.msgh_size = sizeof(send); + send.header.msgh_remote_port = port.get(); + mach_msg_return_t mr = + MachMessageWithDeadline(&send.header, + MACH_SEND_MSG, + 0, + MACH_PORT_NULL, + kMachMessageDeadlineNonblocking, + MACH_PORT_NULL, + false); + ASSERT_EQ(MACH_MSG_SUCCESS, mr) + << MachErrorMessage(mr, "MachMessageWithDeadline send"); + + struct EmptyReceiveMessageWithAuditTrailer : public mach_msg_empty_send_t { + union { + mach_msg_trailer_t trailer; + mach_msg_audit_trailer_t audit_trailer; + }; + }; + + EmptyReceiveMessageWithAuditTrailer receive; + mr = MachMessageWithDeadline(&receive.header, + MACH_RCV_MSG | kMachMessageReceiveAuditTrailer, + sizeof(receive), + port.get(), + kMachMessageDeadlineNonblocking, + MACH_PORT_NULL, + false); + ASSERT_EQ(MACH_MSG_SUCCESS, mr) + << MachErrorMessage(mr, "MachMessageWithDeadline receive"); + + EXPECT_EQ(getpid(), AuditPIDFromMachMessageTrailer(&receive.trailer)); +} + +TEST(MachMessage, MachMessageDestroyReceivedPort) { + mach_port_t port = NewMachPort(MACH_PORT_RIGHT_RECEIVE); + ASSERT_NE(kMachPortNull, port); + EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_RECEIVE)); + + base::mac::ScopedMachReceiveRight receive( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + mach_msg_type_name_t right_type; + kern_return_t kr = mach_port_extract_right(mach_task_self(), + receive.get(), + MACH_MSG_TYPE_MAKE_SEND, + &port, + &right_type); + ASSERT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_extract_right"); + ASSERT_EQ(receive, port); + ASSERT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND), + right_type); + EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND)); + + kr = mach_port_extract_right(mach_task_self(), + receive.get(), + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &port, + &right_type); + ASSERT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_extract_right"); + ASSERT_NE(kMachPortNull, port); + EXPECT_NE(receive, port); + ASSERT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE), + right_type); + EXPECT_TRUE( + MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND_ONCE)); + + kr = mach_port_extract_right(mach_task_self(), + receive.get(), + MACH_MSG_TYPE_MAKE_SEND, + &port, + &right_type); + ASSERT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_extract_right"); + ASSERT_EQ(receive, port); + ASSERT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND), + right_type); + EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_RECEIVE)); + ignore_result(receive.release()); + EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/mig.py b/third_party/crashpad/crashpad/util/mach/mig.py new file mode 100755 index 0000000..2d24885 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/mig.py
@@ -0,0 +1,123 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +import re +import subprocess +import sys + +def FixUserImplementation(implementation): + """Rewrites a MIG-generated user implementation (.c) file. + + Rewrites the file at |implementation| by adding “__attribute__((unused))” to + the definition of any structure typedefed as “__Reply” by searching for the + pattern unique to those structure definitions. These structures are in fact + unused in the user implementation file, and this will trigger a + -Wunused-local-typedefs warning in gcc unless removed or marked with the + “unused” attribute. + """ + + file = open(implementation, 'r+') + contents = file.read() + + pattern = re.compile('^(\t} __Reply);$', re.MULTILINE) + contents = pattern.sub(r'\1 __attribute__((unused));', contents) + + file.seek(0) + file.truncate() + file.write(contents) + file.close() + +def FixServerImplementation(implementation): + """Rewrites a MIG-generated server implementation (.c) file. + + Rewrites the file at |implementation| by replacing “mig_internal” with + “mig_external” on functions that begin with “__MIG_check__”. This makes these + functions available to other callers outside this file from a linkage + perspective. It then returns, as a list of lines, declarations that can be + added to a header file, so that other files that include that header file will + have access to these declarations from a compilation perspective. + """ + + file = open(implementation, 'r+') + contents = file.read() + + # Find interesting declarations. + declaration_pattern = \ + re.compile('^mig_internal (kern_return_t __MIG_check__.*)$', + re.MULTILINE) + declarations = declaration_pattern.findall(contents) + + # Remove “__attribute__((__unused__))” from the declarations, and call them + # “mig_external” or “extern” depending on whether “mig_external” is defined. + attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ') + declarations = ['#ifdef mig_external\nmig_external\n#else\nextern\n#endif\n' + + attribute_pattern.sub('', x) + + ';\n' for x in declarations] + + # Rewrite the declarations in this file as “mig_external”. + contents = declaration_pattern.sub(r'mig_external \1', contents); + + file.seek(0) + file.truncate() + file.write(contents) + file.close() + return declarations + +def FixHeader(header, declarations=[]): + """Rewrites a MIG-generated header (.h) file. + + Rewrites the file at |header| by placing it inside an “extern "C"” block, so + that it declares things properly when included by a C++ compilation unit. + |declarations| can be a list of additional declarations to place inside the + “extern "C"” block after the original contents of |header|. + """ + + file = open(header, 'r+') + contents = file.read() + declarations_text = ''.join(declarations) + contents = '''\ +#ifdef __cplusplus +extern "C" { +#endif + +%s +%s +#ifdef __cplusplus +} +#endif +''' % (contents, declarations_text) + file.seek(0) + file.truncate() + file.write(contents) + file.close() + +def main(args): + assert len(args) == 5 + (defs_file, user_c, server_c, user_h, server_h) = args + subprocess.check_call(['mig', + '-user', user_c, + '-server', server_c, + '-header', user_h, + '-sheader', server_h, + defs_file]) + FixUserImplementation(user_c) + server_declarations = FixServerImplementation(server_c) + FixHeader(user_h) + FixHeader(server_h, server_declarations) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/util/mach/notify_server.cc b/third_party/crashpad/crashpad/util/mach/notify_server.cc new file mode 100644 index 0000000..48a4a9e9 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/notify_server.cc
@@ -0,0 +1,281 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/notify_server.h" + +#include "base/logging.h" +#include "util/mach/notifyServer.h" +#include "util/mach/mach_message.h" + +extern "C" { + +// These five functions are not used, and are in fact obsoleted by the other +// functionality implemented in this file. The standard MIG-generated +// notify_server() (in notifyServer.c) server dispatch routine usable with the +// standard mach_msg_server() function calls out to this function. +// notify_server() is unused and is replaced by the more flexible NotifyServer, +// but the linker still needs to see these five function definitions. + +kern_return_t do_mach_notify_port_deleted(notify_port_t notify, + mach_port_name_t name) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t do_mach_notify_port_destroyed(notify_port_t notify, + mach_port_t rights) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t do_mach_notify_no_senders(notify_port_t notify, + mach_port_mscount_t mscount) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t do_mach_notify_send_once(notify_port_t notify) { + NOTREACHED(); + return KERN_FAILURE; +} + +kern_return_t do_mach_notify_dead_name(notify_port_t notify, + mach_port_name_t name) { + NOTREACHED(); + return KERN_FAILURE; +} + +} // extern "C" + +namespace { + +// The MIG-generated __MIG_check__Request__*() functions are not declared as +// accepting const data, but they could have been because they in fact do not +// modify the data. These wrapper functions are provided to bridge the const gap +// between the code in this file, which is const-correct and treats request +// message data as const, and the generated functions. + +kern_return_t MIGCheckRequestMachNotifyPortDeleted( + const __Request__mach_notify_port_deleted_t* in_request) { + using Request = __Request__mach_notify_port_deleted_t; + return __MIG_check__Request__mach_notify_port_deleted_t( + const_cast<Request*>(in_request)); +} + +kern_return_t MIGCheckRequestMachNotifyPortDestroyed( + const __Request__mach_notify_port_destroyed_t* in_request) { + using Request = __Request__mach_notify_port_destroyed_t; + return __MIG_check__Request__mach_notify_port_destroyed_t( + const_cast<Request*>(in_request)); +} + +kern_return_t MIGCheckRequestMachNotifyNoSenders( + const __Request__mach_notify_no_senders_t* in_request) { + using Request = __Request__mach_notify_no_senders_t; + return __MIG_check__Request__mach_notify_no_senders_t( + const_cast<Request*>(in_request)); +} + +kern_return_t MIGCheckRequestMachNotifySendOnce( + const __Request__mach_notify_send_once_t* in_request) { + using Request = __Request__mach_notify_send_once_t; + return __MIG_check__Request__mach_notify_send_once_t( + const_cast<Request*>(in_request)); +} + +kern_return_t MIGCheckRequestMachNotifyDeadName( + const __Request__mach_notify_dead_name_t* in_request) { + using Request = __Request__mach_notify_dead_name_t; + return __MIG_check__Request__mach_notify_dead_name_t( + const_cast<Request*>(in_request)); +} + +} // namespace + +namespace crashpad { + +kern_return_t NotifyServer::DefaultInterface::DoMachNotifyPortDeleted( + notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer) { + return MIG_BAD_ID; +} + +kern_return_t NotifyServer::DefaultInterface::DoMachNotifyPortDestroyed( + notify_port_t notify, + mach_port_t rights, + const mach_msg_trailer_t* trailer, + bool* destroy_request) { + *destroy_request = true; + return MIG_BAD_ID; +} + +kern_return_t NotifyServer::DefaultInterface::DoMachNotifyNoSenders( + notify_port_t notify, + mach_port_mscount_t mscount, + const mach_msg_trailer_t* trailer) { + return MIG_BAD_ID; +} + +kern_return_t NotifyServer::DefaultInterface::DoMachNotifySendOnce( + notify_port_t notify, + const mach_msg_trailer_t* trailer) { + return MIG_BAD_ID; +} + +kern_return_t NotifyServer::DefaultInterface::DoMachNotifyDeadName( + notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer) { + return MIG_BAD_ID; +} + +NotifyServer::NotifyServer(NotifyServer::Interface* interface) + : MachMessageServer::Interface(), + interface_(interface) { +} + +bool NotifyServer::MachMessageServerFunction( + const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) { + PrepareMIGReplyFromRequest(in_header, out_header); + + const mach_msg_trailer_t* in_trailer = + MachMessageTrailerFromHeader(in_header); + + switch (in_header->msgh_id) { + case MACH_NOTIFY_PORT_DELETED: { + // mach_notify_port_deleted(), do_mach_notify_port_deleted(). + using Request = __Request__mach_notify_port_deleted_t; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = MIGCheckRequestMachNotifyPortDeleted(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = __Reply__mach_notify_port_deleted_t; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->DoMachNotifyPortDeleted(in_header->msgh_local_port, + in_request->name, + in_trailer); + return true; + } + + case MACH_NOTIFY_PORT_DESTROYED: { + // mach_notify_port_destroyed(), do_mach_notify_port_destroyed(). + using Request = __Request__mach_notify_port_destroyed_t; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = MIGCheckRequestMachNotifyPortDestroyed(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = __Reply__mach_notify_port_destroyed_t; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->DoMachNotifyPortDestroyed(in_header->msgh_local_port, + in_request->rights.name, + in_trailer, + destroy_complex_request); + return true; + } + + case MACH_NOTIFY_NO_SENDERS: { + // mach_notify_no_senders(), do_mach_notify_no_senders(). + using Request = __Request__mach_notify_no_senders_t; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = MIGCheckRequestMachNotifyNoSenders(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = __Reply__mach_notify_no_senders_t; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->DoMachNotifyNoSenders(in_header->msgh_local_port, + in_request->mscount, + in_trailer); + return true; + } + + case MACH_NOTIFY_SEND_ONCE: { + // mach_notify_send_once(), do_mach_notify_send_once(). + using Request = __Request__mach_notify_send_once_t; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = MIGCheckRequestMachNotifySendOnce(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = __Reply__mach_notify_send_once_t; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->DoMachNotifySendOnce(in_header->msgh_local_port, + in_trailer); + return true; + } + + case MACH_NOTIFY_DEAD_NAME: { + // mach_notify_dead_name(), do_mach_notify_dead_name(). + using Request = __Request__mach_notify_dead_name_t; + const Request* in_request = reinterpret_cast<const Request*>(in_header); + kern_return_t kr = MIGCheckRequestMachNotifyDeadName(in_request); + if (kr != MACH_MSG_SUCCESS) { + SetMIGReplyError(out_header, kr); + return true; + } + + using Reply = __Reply__mach_notify_dead_name_t; + Reply* out_reply = reinterpret_cast<Reply*>(out_header); + out_reply->RetCode = + interface_->DoMachNotifyDeadName(in_header->msgh_local_port, + in_request->name, + in_trailer); + return true; + } + + default: { + SetMIGReplyError(out_header, MIG_BAD_ID); + return false; + } + } +} + +std::set<mach_msg_id_t> NotifyServer::MachMessageServerRequestIDs() { + const mach_msg_id_t request_ids[] = { + MACH_NOTIFY_PORT_DELETED, + MACH_NOTIFY_PORT_DESTROYED, + MACH_NOTIFY_NO_SENDERS, + MACH_NOTIFY_SEND_ONCE, + MACH_NOTIFY_DEAD_NAME, + }; + return std::set<mach_msg_id_t>(&request_ids[0], + &request_ids[arraysize(request_ids)]); +} + +mach_msg_size_t NotifyServer::MachMessageServerRequestSize() { + return sizeof(__RequestUnion__do_notify_subsystem); +} + +mach_msg_size_t NotifyServer::MachMessageServerReplySize() { + return sizeof(__ReplyUnion__do_notify_subsystem); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/notify_server.h b/third_party/crashpad/crashpad/util/mach/notify_server.h new file mode 100644 index 0000000..ce33b21 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/notify_server.h
@@ -0,0 +1,242 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_ +#define CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_ + +#include <mach/mach.h> + +#include <set> + +#include "base/basictypes.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { + +//! \brief A server interface for the `notify` Mach subsystem. +//! +//! The <a +//! href="https://lists.apple.com/archives/darwin-development/2001/Sep/msg00451.html">mach +//! port notifications</a> thread on the <a +//! href="https://lists.apple.com/archives/darwin-development/">darwin-development</a> +//! mailing list (now known as <a +//! href="https://lists.apple.com/mailman/listinfo/darwin-dev">darwin-dev</a>) +//! is good background for the various notification types. +class NotifyServer : public MachMessageServer::Interface { + public: + //! \brief An interface that the different request messages that are a part of + //! the `notify` Mach subsystem can be dispatched to. + //! + //! Default implementations of all methods are available in the + //! DefaultInterface class. + class Interface { + public: + //! \brief Handles port-deleted notifications sent by + //! `mach_notify_port_deleted()`. + //! + //! A port-deleted notification is generated when a port with a dead-name + //! notification request is destroyed and the port name becomes available + //! for reuse. + //! + //! This behaves equivalently to a `do_mach_notify_port_deleted()` function + //! used with `notify_server()`. + //! + //! \param[in] notify The Mach port that the notification was sent to. + //! \param[in] name The name that formerly referenced the deleted port. When + //! this method is called, \a name no longer corresponds to the port + //! that has been deleted, and may be reused for another purpose. + //! \param[in] trailer The trailer received with the notification message. + virtual kern_return_t DoMachNotifyPortDeleted( + notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer) = 0; + + //! \brief Handles port-destroyed notifications sent by + //! `mach_notify_port_destroyed()`. + //! + //! A port-destroyed notification is generated when a receive right with a + //! port-destroyed notification request is destroyed. Rather than destroying + //! the receive right, it is transferred via this notification’s \a rights + //! parameter. + //! + //! This behaves equivalently to a `do_mach_notify_port_destroyed()` + //! function used with `notify_server()`. + //! + //! \param[in] notify The Mach port that the notification was sent to. + //! \param[in] rights A receive right for the port that would have been + //! destroyed. The callee takes ownership of this port, however, if the + //! callee does not wish to take ownership, it may set \a + //! destroy_request to `true`. + //! \param[in] trailer The trailer received with the notification message. + //! \param[out] destroy_request `true` if the request message is to be + //! destroyed even when this method returns success. See + //! MachMessageServer::Interface. + virtual kern_return_t DoMachNotifyPortDestroyed( + notify_port_t notify, + mach_port_t rights, + const mach_msg_trailer_t* trailer, + bool* destroy_request) = 0; + + //! \brief Handles no-senders notifications sent by + //! `mach_notify_no_senders()`. + //! + //! A no-senders notification is generated when a receive right with a + //! no-senders notification request loses its last corresponding send right. + //! + //! This behaves equivalently to a `do_mach_notify_no_senders()` function + //! used with `notify_server()`. + //! + //! \param[in] notify The Mach port that the notification was sent to. + //! \param[in] mscount The value of the sender-less port’s make-send count + //! at the time the notification was generated. + //! \param[in] trailer The trailer received with the notification message. + virtual kern_return_t DoMachNotifyNoSenders( + notify_port_t notify, + mach_port_mscount_t mscount, + const mach_msg_trailer_t* trailer) = 0; + + //! \brief Handles send-once notifications sent by + //! `mach_notify_send_once()`. + //! + //! A send-once notification is generated when a send-once right is + //! destroyed without being used. + //! + //! This behaves equivalently to a `do_mach_notify_send_once()` function + //! used with `notify_server()`. + //! + //! \param[in] notify The Mach port that the notification was sent to. + //! \param[in] trailer The trailer received with the notification message. + //! + //! \note Unlike the other notifications in the `notify` subsystem, + //! send-once notifications are not generated as a result of a + //! notification request, but are generated any time a send-once right + //! is destroyed rather than being used. The notification is sent via + //! the send-once right to its receiver. These notifications are more + //! useful for clients, not servers. Send-once notifications are + //! normally handled by MIG-generated client routines, which make + //! send-once rights for their reply ports and interpret send-once + //! notifications as a signal that there will be no reply. Although not + //! expected to be primarily useful for servers, this method is provided + //! because send-once notifications are defined as a part of the + //! `notify` subsystem. + virtual kern_return_t DoMachNotifySendOnce( + notify_port_t notify, + const mach_msg_trailer_t* trailer) = 0; + + //! \brief Handles dead-name notifications sent by + //! `mach_notify_dead_name()`. + //! + //! A dead-name notification is generated when a port with a dead-name + //! notification request is destroyed and the right becomes a dead name. + //! + //! This behaves equivalently to a `do_mach_notify_dead_name()` function + //! used with `notify_server()`. + //! + //! \param[in] notify The Mach port that the notification was sent to. + //! \param[in] name The dead name. Although this is transferred as a + //! `mach_port_name_t` and not a `mach_port_t`, the callee assumes an + //! additional reference to this port when this method is called. See + //! the note below. + //! \param[in] trailer The trailer received with the notification message. + //! + //! \note When a dead-name notification is generated, the user reference + //! count of the dead name is incremented. A send right with one + //! reference that becomes a dead name will have one dead-name + //! reference, and the dead-name notification will add another dead-name + //! reference, for a total of 2. DoMachNotifyDeadName() implementations + //! must take care to deallocate this extra reference. There is no \a + //! destroy_request parameter to simplify this operation because + //! dead-name notifications carry a port name only (\a name is of type + //! `mach_port_name_t`) without transferring port rights, and are thus + //! not complex Mach messages. + virtual kern_return_t DoMachNotifyDeadName( + notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer) = 0; + + protected: + ~Interface() {} + }; + + //! \brief A concrete implementation of Interface that provides a default + //! behavior for all `notify` routines. + //! + //! The Mach `notify` subsystem contains a collection of unrelated routines, + //! and a single server would rarely need to implement all of them. To make it + //! easier to use NotifyServer, a server can inherit from DefaultInterface + //! instead of Interface. Unless overridden, each routine in DefaultInterface + //! returns `MIG_BAD_ID` to indicate to the caller that the `notify` message + //! was unexpected and not processed. + class DefaultInterface : public Interface { + public: + // Interface: + + kern_return_t DoMachNotifyPortDeleted( + notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer) override; + + kern_return_t DoMachNotifyPortDestroyed( + notify_port_t notify, + mach_port_t rights, + const mach_msg_trailer_t* trailer, + bool* destroy_request) override; + + kern_return_t DoMachNotifyNoSenders( + notify_port_t notify, + mach_port_mscount_t mscount, + const mach_msg_trailer_t* trailer) override; + + kern_return_t DoMachNotifySendOnce( + notify_port_t notify, + const mach_msg_trailer_t* trailer) override; + + kern_return_t DoMachNotifyDeadName( + notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer) override; + + protected: + DefaultInterface() : Interface() {} + ~DefaultInterface() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DefaultInterface); + }; + + //! \brief Constructs an object of this class. + //! + //! \param[in] interface The interface to dispatch requests to. Weak. + explicit NotifyServer(Interface* interface); + + // MachMessageServer::Interface: + + bool MachMessageServerFunction(const mach_msg_header_t* in_header, + mach_msg_header_t* out_header, + bool* destroy_complex_request) override; + + std::set<mach_msg_id_t> MachMessageServerRequestIDs() override; + + mach_msg_size_t MachMessageServerRequestSize() override; + mach_msg_size_t MachMessageServerReplySize() override; + + private: + Interface* interface_; // weak + + DISALLOW_COPY_AND_ASSIGN(NotifyServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_
diff --git a/third_party/crashpad/crashpad/util/mach/notify_server_test.cc b/third_party/crashpad/crashpad/util/mach/notify_server_test.cc new file mode 100644 index 0000000..445e35b --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/notify_server_test.cc
@@ -0,0 +1,559 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/notify_server.h" + +#include "base/compiler_specific.h" +#include "base/mac/scoped_mach_port.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +using testing::AllOf; +using testing::Eq; +using testing::Invoke; +using testing::Pointee; +using testing::ResultOf; +using testing::Return; +using testing::SetArgPointee; +using testing::StrictMock; +using testing::WithArg; + +//! \brief Adds a send right to an existing receive right. +//! +//! \param[in] receive_right The receive right to add a send right to. +//! +//! \return The send right, which will have the same name as the receive right. +//! On failure, `MACH_PORT_NULL` with a gtest failure added. +mach_port_t SendRightFromReceiveRight(mach_port_t receive_right) { + kern_return_t kr = mach_port_insert_right( + mach_task_self(), receive_right, receive_right, MACH_MSG_TYPE_MAKE_SEND); + if (kr != KERN_SUCCESS) { + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_insert_right"); + return MACH_PORT_NULL; + } + + return receive_right; +} + +//! \brief Extracts a send-once right from a receive right. +//! +//! \param[in] receive_right The receive right to make a send-once right from. +//! +//! \return The send-once right. On failure, `MACH_PORT_NULL` with a gtest +//! failure added. +mach_port_t SendOnceRightFromReceiveRight(mach_port_t receive_right) { + mach_port_t send_once_right; + mach_msg_type_name_t acquired_type; + kern_return_t kr = mach_port_extract_right(mach_task_self(), + receive_right, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &send_once_right, + &acquired_type); + if (kr != KERN_SUCCESS) { + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_extract_right"); + return MACH_PORT_NULL; + } + + EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE), + acquired_type); + + return send_once_right; +} + +//! \brief Deallocates a Mach port by calling `mach_port_deallocate()`. +//! +//! This function exists to adapt `mach_port_deallocate()` to a function that +//! accepts a single argument and has no return value. It can be used with the +//! testing::Invoke() gmock action. +//! +//! On failure, a gtest failure will be added. +void MachPortDeallocate(mach_port_t port) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), port); + EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_deallocate"); +} + +//! \brief Determines whether a specific right is held for a Mach port. +//! +//! \param[in] port The port to check for a right. +//! \param[in] right The right to check for. +//! +//! \return `true` if \a port has \a right, `false` otherwise. On faliure, +//! `false` with a gtest failure added. +bool IsRight(mach_port_t port, mach_port_type_t right) { + mach_port_type_t type; + kern_return_t kr = mach_port_type(mach_task_self(), port, &type); + if (kr != KERN_SUCCESS) { + EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_type"); + return false; + } + + return type & right; +} + +//! \brief Determines whether a receive right is held for a Mach port. +//! +//! This is a special single-argument form of IsRight() for ease of use in a +//! gmock matcher. +//! +//! \param[in] port The port to check for a receive right. +//! +//! \return `true` if a receive right is held, `false` otherwise. On faliure, +//! `false` with a gtest failure added. +bool IsReceiveRight(mach_port_t port) { + return IsRight(port, MACH_PORT_TYPE_RECEIVE); +} + +//! \brief Returns the user reference count for port rights. +//! +//! \param[in] port The port whose user reference count should be returned. +//! \param[in] right The port right to return the user reference count for. +//! +//! \return The user reference count for the specified port and right. On +//! failure, `-1` with a gtest failure added. +mach_port_urefs_t RightRefCount(mach_port_t port, mach_port_right_t right) { + mach_port_urefs_t refs; + kern_return_t kr = mach_port_get_refs(mach_task_self(), port, right, &refs); + if (kr != KERN_SUCCESS) { + EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_refs"); + return -1; + } + + return refs; +} + +//! \brief Returns the user reference count for a port’s dead-name rights. +//! +//! This is a special single-argument form of RightRefCount() for ease of use in +//! a gmock matcher. +//! +//! \param[in] port The port whose dead-name user reference count should be +//! returned. +//! +//! \return The user reference count for the port’s dead-name rights. On +//! failure, `-1` with a gtest failure added. +mach_port_urefs_t DeadNameRightRefCount(mach_port_t port) { + return RightRefCount(port, MACH_PORT_RIGHT_DEAD_NAME); +} + +class NotifyServerTestBase : public testing::Test, + public NotifyServer::Interface { + public: + // NotifyServer::Interface: + + MOCK_METHOD3(DoMachNotifyPortDeleted, + kern_return_t(notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer)); + + MOCK_METHOD4(DoMachNotifyPortDestroyed, + kern_return_t(notify_port_t notify, + mach_port_t rights, + const mach_msg_trailer_t* trailer, + bool* destroy_request)); + + MOCK_METHOD3(DoMachNotifyNoSenders, + kern_return_t(notify_port_t notify, + mach_port_mscount_t mscount, + const mach_msg_trailer_t* trailer)); + + MOCK_METHOD2(DoMachNotifySendOnce, + kern_return_t(notify_port_t notify, + const mach_msg_trailer_t* trailer)); + + MOCK_METHOD3(DoMachNotifyDeadName, + kern_return_t(notify_port_t notify, + mach_port_name_t name, + const mach_msg_trailer_t* trailer)); + + protected: + NotifyServerTestBase() : testing::Test(), NotifyServer::Interface() {} + + ~NotifyServerTestBase() override {} + + //! \brief Requests a Mach port notification. + //! + //! \a name, \a variant, and \a sync are passed as-is to + //! `mach_port_request_notification()`. The notification will be sent to a + //! send-once right made from ServerPort(). Any previous send right for the + //! notification will be deallocated. + //! + //! \return `true` on success, `false` on failure with a gtest failure added. + bool RequestMachPortNotification(mach_port_t name, + mach_msg_id_t variant, + mach_port_mscount_t sync) { + mach_port_t previous; + kern_return_t kr = + mach_port_request_notification(mach_task_self(), + name, + variant, + sync, + ServerPort(), + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &previous); + if (kr != KERN_SUCCESS) { + EXPECT_EQ(KERN_SUCCESS, kr) + << MachErrorMessage(kr, "mach_port_request_notification"); + return false; + } + + base::mac::ScopedMachSendRight previous_owner(previous); + EXPECT_EQ(kMachPortNull, previous); + + return true; + } + + //! \brief Runs a NotifyServer Mach message server. + //! + //! The server will listen on ServerPort() in persistent nonblocking mode, and + //! dispatch received messages to the appropriate NotifyServer::Interface + //! method. gmock expectations check that the proper method, if any, is called + //! exactly once, and that no undesired methods are called. + //! + //! MachMessageServer::Run() is expected to return `MACH_RCV_TIMED_OUT`, + //! because it runs in persistent nonblocking mode. If it returns anything + //! else, a gtest assertion is added. + void RunServer() { + NotifyServer notify_server(this); + mach_msg_return_t mr = + MachMessageServer::Run(¬ify_server, + ServerPort(), + kMachMessageReceiveAuditTrailer, + MachMessageServer::kPersistent, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutNonblocking); + ASSERT_EQ(MACH_RCV_TIMED_OUT, mr) + << MachErrorMessage(mr, "MachMessageServer::Run"); + } + + //! \brief Returns the receive right to be used for the server. + //! + //! This receive right is created lazily on a per-test basis. It is destroyed + //! by TearDown() at the conclusion of each test. + //! + //! \return The server port receive right, creating it if one has not yet been + //! established for the current test. On failure, returns `MACH_PORT_NULL` + //! with a gtest failure added. + mach_port_t ServerPort() { + if (!server_port_.is_valid()) { + server_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + EXPECT_TRUE(server_port_.is_valid()); + } + + return server_port_.get(); + } + + // testing::Test: + void TearDown() override { + server_port_.reset(); + } + + private: + base::mac::ScopedMachReceiveRight server_port_; + + DISALLOW_COPY_AND_ASSIGN(NotifyServerTestBase); +}; + +using NotifyServerTest = StrictMock<NotifyServerTestBase>; + +TEST_F(NotifyServerTest, Basic) { + NotifyServer server(this); + + std::set<mach_msg_id_t> expect_request_ids; + expect_request_ids.insert(MACH_NOTIFY_PORT_DELETED); + expect_request_ids.insert(MACH_NOTIFY_PORT_DESTROYED); + expect_request_ids.insert(MACH_NOTIFY_NO_SENDERS); + expect_request_ids.insert(MACH_NOTIFY_SEND_ONCE); + expect_request_ids.insert(MACH_NOTIFY_DEAD_NAME); + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + + // The port-destroyed notification is the largest request message in the + // subsystem. <mach/notify.h> defines the same structure, but with a basic + // trailer, so use offsetof to get the size of the basic structure without any + // trailer. + EXPECT_EQ(offsetof(mach_port_destroyed_notification_t, trailer), + server.MachMessageServerRequestSize()); + + mig_reply_error_t reply; + EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize()); +} + +// When no notifications are requested, nothing should happen. +TEST_F(NotifyServerTest, NoNotification) { + RunServer(); +} + +// When a send-once right with a dead-name notification request is deallocated, +// a port-deleted notification should be generated. +TEST_F(NotifyServerTest, MachNotifyPortDeleted) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + base::mac::ScopedMachSendRight send_once_right( + SendOnceRightFromReceiveRight(receive_right.get())); + ASSERT_TRUE(send_once_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0)); + + EXPECT_CALL( + *this, + DoMachNotifyPortDeleted(ServerPort(), + send_once_right.get(), + ResultOf(AuditPIDFromMachMessageTrailer, 0))) + .WillOnce(Return(MIG_NO_REPLY)) + .RetiresOnSaturation(); + + send_once_right.reset(); + + RunServer(); +} + +// When a receive right with a port-destroyed notification request is destroyed, +// a port-destroyed notification should be generated. +TEST_F(NotifyServerTest, MachNotifyPortDestroyed) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0)); + + EXPECT_CALL( + *this, + DoMachNotifyPortDestroyed(ServerPort(), + ResultOf(IsReceiveRight, true), + ResultOf(AuditPIDFromMachMessageTrailer, 0), + Pointee(Eq(false)))) + .WillOnce(DoAll(SetArgPointee<3>(true), Return(MIG_NO_REPLY))) + .RetiresOnSaturation(); + + receive_right.reset(); + + RunServer(); +} + +// When a receive right with a port-destroyed notification request is not +// destroyed, no port-destroyed notification should be generated. +TEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0)); + + RunServer(); +} + +// When a no-senders notification request is registered for a receive right with +// no senders, a no-senders notification should be generated. +TEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + receive_right.get(), MACH_NOTIFY_NO_SENDERS, 0)); + + EXPECT_CALL(*this, + DoMachNotifyNoSenders( + ServerPort(), 0, ResultOf(AuditPIDFromMachMessageTrailer, 0))) + .WillOnce(Return(MIG_NO_REPLY)) + .RetiresOnSaturation(); + + RunServer(); +} + +// When the last send right corresponding to a receive right with a no-senders +// notification request is deallocated, a no-senders notification should be +// generated. +TEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + base::mac::ScopedMachSendRight send_right( + SendRightFromReceiveRight(receive_right.get())); + ASSERT_TRUE(send_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1)); + + EXPECT_CALL(*this, + DoMachNotifyNoSenders( + ServerPort(), 1, ResultOf(AuditPIDFromMachMessageTrailer, 0))) + .WillOnce(Return(MIG_NO_REPLY)) + .RetiresOnSaturation(); + + send_right.reset(); + + RunServer(); +} + +// When the a receive right with a no-senders notification request never loses +// all senders, no no-senders notification should be generated. +TEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + base::mac::ScopedMachSendRight send_right_0( + SendRightFromReceiveRight(receive_right.get())); + ASSERT_TRUE(send_right_0.is_valid()); + + base::mac::ScopedMachSendRight send_right_1( + SendRightFromReceiveRight(receive_right.get())); + ASSERT_TRUE(send_right_1.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1)); + + send_right_1.reset(); + + RunServer(); + + EXPECT_EQ(1u, RightRefCount(receive_right.get(), MACH_PORT_RIGHT_RECEIVE)); + EXPECT_EQ(1u, RightRefCount(receive_right.get(), MACH_PORT_RIGHT_SEND)); +} + +// When a send-once right is deallocated without being used, a send-once +// notification notification should be sent via the send-once right. +TEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) { + base::mac::ScopedMachSendRight send_once_right( + SendOnceRightFromReceiveRight(ServerPort())); + ASSERT_TRUE(send_once_right.is_valid()); + + EXPECT_CALL(*this, + DoMachNotifySendOnce(ServerPort(), + ResultOf(AuditPIDFromMachMessageTrailer, 0))) + .WillOnce(Return(MIG_NO_REPLY)) + .RetiresOnSaturation(); + + send_once_right.reset(); + + RunServer(); +} + +// When a send-once right is sent to a receiver that never dequeues the message, +// the send-once right is destroyed, and a send-once notification should appear +// on the reply port. +TEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + mach_msg_empty_send_t message = {}; + message.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + message.header.msgh_size = sizeof(message); + message.header.msgh_remote_port = receive_right.get(); + message.header.msgh_local_port = ServerPort(); + mach_msg_return_t mr = mach_msg(&message.header, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.header.msgh_size, + 0, + MACH_PORT_NULL, + 0, + MACH_PORT_NULL); + ASSERT_EQ(MACH_MSG_SUCCESS, mr) << MachErrorMessage(mr, "mach_msg"); + + EXPECT_CALL(*this, + DoMachNotifySendOnce(ServerPort(), + ResultOf(AuditPIDFromMachMessageTrailer, 0))) + .WillOnce(Return(MIG_NO_REPLY)) + .RetiresOnSaturation(); + + receive_right.reset(); + + RunServer(); +} + +// When the receive right corresponding to a send-once right with a dead-name +// notification request is destroyed, a dead-name notification should be +// generated. +TEST_F(NotifyServerTest, MachNotifyDeadName) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + base::mac::ScopedMachSendRight send_once_right( + SendOnceRightFromReceiveRight(receive_right.get())); + ASSERT_TRUE(send_once_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0)); + + // send_once_right becomes a dead name with the send-once right’s original + // user reference count of 1, but the dead-name notification increments the + // dead-name reference count, so it becomes 2. Take care to deallocate that + // reference. The original reference is managed by send_once_right_owner. + EXPECT_CALL(*this, + DoMachNotifyDeadName(ServerPort(), + AllOf(send_once_right.get(), + ResultOf(DeadNameRightRefCount, 2)), + ResultOf(AuditPIDFromMachMessageTrailer, 0))) + .WillOnce( + DoAll(WithArg<1>(Invoke(MachPortDeallocate)), Return(MIG_NO_REPLY))) + .RetiresOnSaturation(); + + receive_right.reset(); + + RunServer(); + + EXPECT_TRUE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME)); + + EXPECT_EQ(0u, + RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE)); + EXPECT_EQ(1u, DeadNameRightRefCount(send_once_right.get())); +} + +// When the receive right corresponding to a send-once right with a dead-name +// notification request is not destroyed, no dead-name notification should be +// generated. +TEST_F(NotifyServerTest, MachNotifyDeadName_NoNotification) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_TRUE(receive_right.is_valid()); + + base::mac::ScopedMachSendRight send_once_right( + SendOnceRightFromReceiveRight(receive_right.get())); + ASSERT_TRUE(send_once_right.is_valid()); + + ASSERT_TRUE(RequestMachPortNotification( + send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0)); + + RunServer(); + + EXPECT_FALSE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME)); + + EXPECT_EQ(1u, + RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE)); + EXPECT_EQ(0u, DeadNameRightRefCount(send_once_right.get())); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc b/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc new file mode 100644 index 0000000..abca77e --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc
@@ -0,0 +1,39 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/scoped_task_suspend.h" + +#include "base/logging.h" +#include "base/mac/mach_logging.h" + +namespace crashpad { + +ScopedTaskSuspend::ScopedTaskSuspend(task_t task) : task_(task) { + DCHECK_NE(task_, mach_task_self()); + + kern_return_t kr = task_suspend(task_); + if (kr != KERN_SUCCESS) { + task_ = TASK_NULL; + MACH_LOG(ERROR, kr) << "task_suspend"; + } +} + +ScopedTaskSuspend::~ScopedTaskSuspend() { + if (task_ != TASK_NULL) { + kern_return_t kr = task_resume(task_); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "task_resume"; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h b/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h new file mode 100644 index 0000000..4b90aba --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h
@@ -0,0 +1,45 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_ +#define CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_ + +#include <mach/mach.h> + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Manages the suspension of another task. +//! +//! While an object of this class exists, the other task will be suspended. Once +//! the object is destroyed, the other task will become eligible for resumption. +//! Note that suspensions are counted, and the task will not actually resume +//! unless its suspend count drops to 0. +//! +//! Callers should not attempt to suspend the current task (`mach_task_self()`). +class ScopedTaskSuspend { + public: + explicit ScopedTaskSuspend(task_t task); + ~ScopedTaskSuspend(); + + private: + task_t task_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_
diff --git a/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc b/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc new file mode 100644 index 0000000..b53b83d --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc
@@ -0,0 +1,85 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/scoped_task_suspend.h" + +#include <mach/mach.h> + +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" + +namespace crashpad { +namespace test { +namespace { + +int SuspendCount(task_t task) { + // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO. + // TASK_BASIC_INFO_64 is equivalent and works on earlier systems. + task_basic_info_64 task_basic_info; + mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT; + kern_return_t kr = task_info(task, + TASK_BASIC_INFO_64, + reinterpret_cast<task_info_t>(&task_basic_info), + &task_basic_info_count); + if (kr != KERN_SUCCESS) { + ADD_FAILURE() << MachErrorMessage(kr, "task_info"); + return -1; + } + + return task_basic_info.suspend_count; +} + +class ScopedTaskSuspendTest final : public MachMultiprocess { + public: + ScopedTaskSuspendTest() : MachMultiprocess() {} + ~ScopedTaskSuspendTest() {} + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + task_t child_task = ChildTask(); + + EXPECT_EQ(0, SuspendCount(child_task)); + + { + ScopedTaskSuspend suspend(child_task); + EXPECT_EQ(1, SuspendCount(child_task)); + + { + ScopedTaskSuspend suspend_again(child_task); + EXPECT_EQ(2, SuspendCount(child_task)); + } + + EXPECT_EQ(1, SuspendCount(child_task)); + } + + EXPECT_EQ(0, SuspendCount(child_task)); + } + + void MachMultiprocessChild() override { + } + + DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspendTest); +}; + +TEST(ScopedTaskSuspend, ScopedTaskSuspend) { + ScopedTaskSuspendTest scoped_task_suspend_test; + scoped_task_suspend_test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc new file mode 100644 index 0000000..fc86b1f --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
@@ -0,0 +1,547 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/symbolic_constants_mach.h" + +#include <string.h> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/implicit_cast.h" +#include "util/stdlib/string_number_conversion.h" + +namespace { + +const char* kExceptionNames[] = { + nullptr, + + // sed -Ene 's/^#define[[:space:]]EXC_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' + // /usr/include/mach/exception_types.h + "BAD_ACCESS", + "BAD_INSTRUCTION", + "ARITHMETIC", + "EMULATION", + "SOFTWARE", + "BREAKPOINT", + "SYSCALL", + "MACH_SYSCALL", + "RPC_ALERT", + "CRASH", + "RESOURCE", + "GUARD", + "CORPSE_NOTIFY", +}; +static_assert(arraysize(kExceptionNames) == EXC_TYPES_COUNT, + "kExceptionNames length"); + +const char kExcPrefix[] = "EXC_"; +const char kExcMaskPrefix[] = "EXC_MASK_"; + +const char* kBehaviorNames[] = { + nullptr, + + // sed -Ene 's/^# define[[:space:]]EXCEPTION_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' + // /usr/include/mach/exception_types.h + "DEFAULT", + "STATE", + "STATE_IDENTITY", +}; + +const char kBehaviorPrefix[] = "EXCEPTION_"; +const char kMachExceptionCodesFull[] = "MACH_EXCEPTION_CODES"; +const char kMachExceptionCodesShort[] = "MACH"; + +const char* kFlavorNames[] = { + "THREAD_STATE_FLAVOR_LIST", + +#if defined(__i386__) || defined(__x86_64__) + // sed -Ene 's/^#define ((x86|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p' + // /usr/include/mach/i386/thread_status.h + // and then fix up by adding x86_SAVED_STATE32 and x86_SAVED_STATE64. + "x86_THREAD_STATE32", + "x86_FLOAT_STATE32", + "x86_EXCEPTION_STATE32", + "x86_THREAD_STATE64", + "x86_FLOAT_STATE64", + "x86_EXCEPTION_STATE64", + "x86_THREAD_STATE", + "x86_FLOAT_STATE", + "x86_EXCEPTION_STATE", + "x86_DEBUG_STATE32", + "x86_DEBUG_STATE64", + "x86_DEBUG_STATE", + "THREAD_STATE_NONE", + "x86_SAVED_STATE32", + "x86_SAVED_STATE64", + "x86_AVX_STATE32", + "x86_AVX_STATE64", + "x86_AVX_STATE", +#elif defined(__ppc__) || defined(__ppc64__) + // sed -Ene 's/^#define ((PPC|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p' + // usr/include/mach/ppc/thread_status.h + // (Mac OS X 10.6 SDK) + "PPC_THREAD_STATE", + "PPC_FLOAT_STATE", + "PPC_EXCEPTION_STATE", + "PPC_VECTOR_STATE", + "PPC_THREAD_STATE64", + "PPC_EXCEPTION_STATE64", + "THREAD_STATE_NONE", +#elif defined(__arm__) || defined(__arm64__) + // sed -Ene 's/^#define ((ARM|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p' + // usr/include/mach/arm/thread_status.h + // (iOS 7 SDK) + // and then fix up by making the list sparse as appropriate. + "ARM_THREAD_STATE", + "ARM_VFP_STATE", + "ARM_EXCEPTION_STATE", + "ARM_DEBUG_STATE", + "THREAD_STATE_NONE", + "ARM_THREAD_STATE64", + "ARM_EXCEPTION_STATE64", + nullptr, + "ARM_THREAD_STATE32", + nullptr, + nullptr, + nullptr, + nullptr, + "ARM_DEBUG_STATE32", + "ARM_DEBUG_STATE64", + "ARM_NEON_STATE", + "ARM_NEON_STATE64", +#endif +}; + +// Certain generic flavors have high constants not contiguous with the flavors +// above. List them separately alongside their constants. +const struct { + thread_state_flavor_t flavor; + const char* name; +} kGenericFlavorNames[] = { + {THREAD_STATE_FLAVOR_LIST_NEW, "THREAD_STATE_FLAVOR_LIST_NEW"}, + {THREAD_STATE_FLAVOR_LIST_10_9, "THREAD_STATE_FLAVOR_LIST_10_9"}, +}; + +// Returns the short name for a flavor name, given its full flavor name. +std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) { + // For generic flavors like THREAD_STATE_NONE and THREAD_STATE_FLAVOR_LIST_*. + const char kThreadState[] = "THREAD_STATE_"; + size_t prefix_len = strlen(kThreadState); + const char* flavor_data = flavor.data(); + size_t flavor_len = flavor.size(); + if (flavor_len >= prefix_len && + strncmp(flavor_data, kThreadState, prefix_len) == 0) { + return std::string(flavor_data + prefix_len, flavor_len - prefix_len); + } + + // For architecture-specific flavors. +#if defined(__i386__) || defined(__x86_64__) + const char kArchPrefix[] = "x86_"; +#elif defined(__ppc__) || defined(__ppc64__) + const char kArchPrefix[] = "PPC_"; +#elif defined(__arm__) || defined(__arm64__) + const char kArchPrefix[] = "ARM_" +#endif + prefix_len = strlen(kArchPrefix); + if (flavor_len >= prefix_len && + strncmp(flavor_data, kArchPrefix, prefix_len) == 0) { + // Shorten the suffix by removing _STATE. If the suffix contains a + // significant designation like 32 or 64, keep it, so that a full name like + // x86_THREAD_STATE64 becomes a short name like THREAD64. + const struct { + const char* orig; + const char* repl; + } kStateSuffixes[] = { + {"_STATE", ""}, + {"_STATE32", "32"}, + {"_STATE64", "64"}, + }; + for (size_t suffix_index = 0; + suffix_index < arraysize(kStateSuffixes); + ++suffix_index) { + const char* suffix = kStateSuffixes[suffix_index].orig; + size_t suffix_len = strlen(suffix); + if (flavor_len >= suffix_len && + strncmp(flavor_data + flavor_len - suffix_len, suffix, suffix_len) == + 0) { + std::string s(flavor_data + prefix_len, + flavor_len - prefix_len - suffix_len); + return s.append(kStateSuffixes[suffix_index].repl); + } + } + } + + return std::string(flavor_data, flavor_len); +} + +} // namespace + +namespace crashpad { + +std::string ExceptionToString(exception_type_t exception, + SymbolicConstantToStringOptions options) { + const char* exception_name = + implicit_cast<size_t>(exception) < arraysize(kExceptionNames) + ? kExceptionNames[exception] + : nullptr; + if (!exception_name) { + if (options & kUnknownIsNumeric) { + return base::StringPrintf("%d", exception); + } + return std::string(); + } + + if (options & kUseShortName) { + return std::string(exception_name); + } + return base::StringPrintf("%s%s", kExcPrefix, exception_name); +} + +bool StringToException(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + exception_type_t* exception) { + if ((options & kAllowFullName) || (options & kAllowShortName)) { + bool can_match_full = + (options & kAllowFullName) && + string.substr(0, strlen(kExcPrefix)).compare(kExcPrefix) == 0; + base::StringPiece short_string = + can_match_full ? string.substr(strlen(kExcPrefix)) : string; + for (exception_type_t index = 0; + index < implicit_cast<exception_type_t>(arraysize(kExceptionNames)); + ++index) { + const char* exception_name = kExceptionNames[index]; + if (!exception_name) { + continue; + } + if (can_match_full && short_string.compare(exception_name) == 0) { + *exception = index; + return true; + } + if ((options & kAllowShortName) && string.compare(exception_name) == 0) { + *exception = index; + return true; + } + } + } + + if (options & kAllowNumber) { + return StringToNumber(string, reinterpret_cast<unsigned int*>(exception)); + } + + return false; +} + +std::string ExceptionMaskToString(exception_mask_t exception_mask, + SymbolicConstantToStringOptions options) { + exception_mask_t local_exception_mask = exception_mask; + std::string mask_string; + bool has_forbidden_or = false; + for (size_t exception = 0; + exception < arraysize(kExceptionNames); + ++exception) { + const char* exception_name = kExceptionNames[exception]; + exception_mask_t exception_mask_value = 1 << exception; + if (exception_name && (local_exception_mask & exception_mask_value)) { + if (!mask_string.empty()) { + if (!(options & kUseOr)) { + has_forbidden_or = true; + break; + } + mask_string.append("|"); + } + if (!(options & kUseShortName)) { + mask_string.append(kExcMaskPrefix); + } + mask_string.append(exception_name); + local_exception_mask &= ~exception_mask_value; + } + } + + if (has_forbidden_or) { + local_exception_mask = exception_mask; + mask_string.clear(); + } + + // Deal with any remainder. + if (local_exception_mask) { + if (!(options & kUnknownIsNumeric)) { + return std::string(); + } + if (!mask_string.empty()) { + mask_string.append("|"); + } + mask_string.append(base::StringPrintf("%#x", local_exception_mask)); + } + + return mask_string; +} + +bool StringToExceptionMask(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + exception_mask_t* exception_mask) { + if (options & kAllowOr) { + options &= ~kAllowOr; + exception_mask_t build_mask = 0; + size_t pos = -1; + do { + ++pos; + const char* substring_begin = string.begin() + pos; + pos = string.find('|', pos); + const char* substring_end = (pos == base::StringPiece::npos) + ? string.end() + : (string.begin() + pos); + base::StringPiece substring = string.substr( + substring_begin - string.begin(), substring_end - substring_begin); + + exception_mask_t temp_mask; + if (!StringToExceptionMask(substring, options, &temp_mask)) { + return false; + } + build_mask |= temp_mask; + } while (pos != base::StringPiece::npos); + + *exception_mask = build_mask; + return true; + } + + if ((options & kAllowFullName) || (options & kAllowShortName)) { + bool can_match_full = + (options & kAllowFullName) && + string.substr(0, strlen(kExcMaskPrefix)).compare(kExcMaskPrefix) == 0; + base::StringPiece short_string = + can_match_full ? string.substr(strlen(kExcMaskPrefix)) : string; + for (exception_type_t index = 0; + index < implicit_cast<exception_type_t>(arraysize(kExceptionNames)); + ++index) { + const char* exception_name = kExceptionNames[index]; + if (!exception_name) { + continue; + } + if (can_match_full && short_string.compare(exception_name) == 0) { + *exception_mask = 1 << index; + return true; + } + if ((options & kAllowShortName) && string.compare(exception_name) == 0) { + *exception_mask = 1 << index; + return true; + } + } + + // EXC_MASK_ALL is a special case: it is not in kExceptionNames as it exists + // only as a mask value. + const char kExcMaskAll[] = "ALL"; + if ((can_match_full && short_string.compare(kExcMaskAll) == 0) || + ((options & kAllowShortName) && string.compare(kExcMaskAll) == 0)) { + *exception_mask = ExcMaskAll(); + return true; + } + } + + if (options & kAllowNumber) { + return StringToNumber(string, + reinterpret_cast<unsigned int*>(exception_mask)); + } + + return false; +} + +std::string ExceptionBehaviorToString(exception_behavior_t behavior, + SymbolicConstantToStringOptions options) { + const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior); + + const char* behavior_name = + implicit_cast<size_t>(basic_behavior) < arraysize(kBehaviorNames) + ? kBehaviorNames[basic_behavior] + : nullptr; + if (!behavior_name) { + if (options & kUnknownIsNumeric) { + return base::StringPrintf("%#x", behavior); + } + return std::string(); + } + + std::string behavior_string; + if (options & kUseShortName) { + behavior_string.assign(behavior_name); + } else { + behavior_string.assign(base::StringPrintf( + "%s%s", kBehaviorPrefix, behavior_name)); + } + + if (ExceptionBehaviorHasMachExceptionCodes(behavior)) { + behavior_string.append("|"); + behavior_string.append((options & kUseShortName) ? kMachExceptionCodesShort + : kMachExceptionCodesFull); + } + + return behavior_string; +} + +bool StringToExceptionBehavior(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + exception_behavior_t* behavior) { + base::StringPiece sp = string; + exception_behavior_t build_behavior = 0; + size_t pos = sp.find('|', 0); + if (pos != base::StringPiece::npos) { + base::StringPiece left = sp.substr(0, pos); + base::StringPiece right = sp.substr(pos + 1, sp.length() - pos - 1); + if (options & kAllowFullName) { + if (left.compare(kMachExceptionCodesFull) == 0) { + build_behavior |= MACH_EXCEPTION_CODES; + sp = right; + } else if (right.compare(kMachExceptionCodesFull) == 0) { + build_behavior |= MACH_EXCEPTION_CODES; + sp = left; + } + } + if (!(build_behavior & MACH_EXCEPTION_CODES) && + (options & kAllowShortName)) { + if (left.compare(kMachExceptionCodesShort) == 0) { + build_behavior |= MACH_EXCEPTION_CODES; + sp = right; + } else if (right.compare(kMachExceptionCodesShort) == 0) { + build_behavior |= MACH_EXCEPTION_CODES; + sp = left; + } + } + if (!(build_behavior & MACH_EXCEPTION_CODES)) { + return false; + } + } + + if ((options & kAllowFullName) || (options & kAllowShortName)) { + bool can_match_full = + (options & kAllowFullName) && + sp.substr(0, strlen(kBehaviorPrefix)).compare(kBehaviorPrefix) == 0; + base::StringPiece short_string = + can_match_full ? sp.substr(strlen(kBehaviorPrefix)) : sp; + for (exception_behavior_t index = 0; + index < implicit_cast<exception_behavior_t>(arraysize(kBehaviorNames)); + ++index) { + const char* behavior_name = kBehaviorNames[index]; + if (!behavior_name) { + continue; + } + if (can_match_full && short_string.compare(behavior_name) == 0) { + build_behavior |= index; + *behavior = build_behavior; + return true; + } + if ((options & kAllowShortName) && sp.compare(behavior_name) == 0) { + build_behavior |= index; + *behavior = build_behavior; + return true; + } + } + } + + if (options & kAllowNumber) { + exception_behavior_t temp_behavior; + if (!StringToNumber(sp, reinterpret_cast<unsigned int*>(&temp_behavior))) { + return false; + } + build_behavior |= temp_behavior; + *behavior = build_behavior; + return true; + } + + return false; +} + +std::string ThreadStateFlavorToString(thread_state_flavor_t flavor, + SymbolicConstantToStringOptions options) { + const char* flavor_name = + implicit_cast<size_t>(flavor) < arraysize(kFlavorNames) + ? kFlavorNames[flavor] + : nullptr; + + if (!flavor_name) { + for (size_t generic_flavor_index = 0; + generic_flavor_index < arraysize(kGenericFlavorNames); + ++generic_flavor_index) { + if (flavor == kGenericFlavorNames[generic_flavor_index].flavor) { + flavor_name = kGenericFlavorNames[generic_flavor_index].name; + break; + } + } + } + + if (!flavor_name) { + if (options & kUnknownIsNumeric) { + return base::StringPrintf("%d", flavor); + } + return std::string(); + } + + if (options & kUseShortName) { + return ThreadStateFlavorFullToShort(flavor_name); + } + return std::string(flavor_name); +} + +bool StringToThreadStateFlavor(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + thread_state_flavor_t* flavor) { + if ((options & kAllowFullName) || (options & kAllowShortName)) { + for (thread_state_flavor_t index = 0; + index < implicit_cast<thread_state_flavor_t>(arraysize(kFlavorNames)); + ++index) { + const char* flavor_name = kFlavorNames[index]; + if (!flavor_name) { + continue; + } + if ((options & kAllowFullName) && string.compare(flavor_name) == 0) { + *flavor = index; + return true; + } + if (options & kAllowShortName) { + std::string short_name = ThreadStateFlavorFullToShort(flavor_name); + if (string.compare(short_name) == 0) { + *flavor = index; + return true; + } + } + } + + for (size_t generic_flavor_index = 0; + generic_flavor_index < arraysize(kGenericFlavorNames); + ++generic_flavor_index) { + const char* flavor_name = kGenericFlavorNames[generic_flavor_index].name; + thread_state_flavor_t flavor_number = + kGenericFlavorNames[generic_flavor_index].flavor; + if ((options & kAllowFullName) && string.compare(flavor_name) == 0) { + *flavor = flavor_number; + return true; + } + if (options & kAllowShortName) { + std::string short_name = ThreadStateFlavorFullToShort(flavor_name); + if (string.compare(short_name) == 0) { + *flavor = flavor_number; + return true; + } + } + } + } + + if (options & kAllowNumber) { + return StringToNumber(string, reinterpret_cast<unsigned int*>(flavor)); + } + + return false; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h new file mode 100644 index 0000000..be522fa --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h
@@ -0,0 +1,120 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_ +#define CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_ + +#include <mach/mach.h> + +#include <string> + +#include "base/strings/string_piece.h" +#include "util/misc/symbolic_constants_common.h" + +namespace crashpad { + +//! \brief Converts a Mach exception value to a textual representation. +//! +//! \param[in] exception The Mach exception value to convert. +//! \param[in] options Options affecting the conversion. ::kUseOr is ignored. +//! For ::kUnknownIsNumeric, the format is `"%d"`. +//! +//! \return The converted string. +std::string ExceptionToString(exception_type_t exception, + SymbolicConstantToStringOptions options); + +//! \brief Converts a string to its corresponding Mach exception value. +//! +//! \param[in] string The string to convert. +//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored. +//! \param[out] exception The converted Mach exception value. +//! +//! \return `true` on success, `false` if \a string could not be converted as +//! requested. +bool StringToException(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + exception_type_t* exception); + +//! \brief Converts a Mach exception mask value to a textual representation. +//! +//! \param[in] exception_mask The Mach exception mask value to convert. +//! \param[in] options Options affecting the conversion. ::kUseOr is honored. +//! For ::kUnknownIsNumeric, the format is `"%#x"`. +//! +//! \return The converted string. +std::string ExceptionMaskToString(exception_mask_t exception_mask, + SymbolicConstantToStringOptions options); + +//! \brief Converts a string to its corresponding Mach exception mask value. +//! +//! \param[in] string The string to convert. +//! \param[in] options Options affecting the conversion. ::kAllowOr is honored. +//! \param[out] exception_mask The converted Mach exception mask value. +//! +//! \return `true` on success, `false` if \a string could not be converted as +//! requested. +bool StringToExceptionMask(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + exception_mask_t* exception_mask); + +//! \brief Converts a Mach exception behavior value to a textual representation. +//! +//! \param[in] behavior The Mach exception behavior value to convert. +//! \param[in] options Options affecting the conversion. ::kUseOr is ignored. +//! `MACH_EXCEPTION_CODES` can always be ORed in, but no other values can be +//! ORed with each other. For ::kUnknownIsNumeric, the format is `"%#x"`. +//! +//! \return The converted string. +std::string ExceptionBehaviorToString(exception_behavior_t behavior, + SymbolicConstantToStringOptions options); + +//! \brief Converts a string to its corresponding Mach exception behavior value. +//! +//! \param[in] string The string to convert. +//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored. +//! `MACH_EXCEPTION_CODES` can always be ORed in, but no other values can be +//! ORed with each other. +//! \param[out] behavior The converted Mach exception behavior value. +//! +//! \return `true` on success, `false` if \a string could not be converted as +//! requested. +bool StringToExceptionBehavior(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + exception_behavior_t* behavior); + +//! \brief Converts a thread state flavor value to a textual representation. +//! +//! \param[in] flavor The thread state flavor value to convert. +//! \param[in] options Options affecting the conversion. ::kUseOr is ignored. +//! For ::kUnknownIsNumeric, the format is `"%d"`. +//! +//! \return The converted string. +std::string ThreadStateFlavorToString(thread_state_flavor_t flavor, + SymbolicConstantToStringOptions options); + +//! \brief Converts a string to its corresponding thread state flavor value. +//! +//! \param[in] string The string to convert. +//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored. +//! \param[out] flavor The converted thread state flavor value. +//! +//! \return `true` on success, `false` if \a string could not be converted as +//! requested. +bool StringToThreadStateFlavor(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + thread_state_flavor_t* flavor); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_
diff --git a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc new file mode 100644 index 0000000..5f3d7d9 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc
@@ -0,0 +1,1067 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/symbolic_constants_mach.h" + +#include <mach/mach.h> +#include <string.h> + +#include "base/basictypes.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/implicit_cast.h" + +#define NUL_TEST_DATA(string) { string, arraysize(string) - 1 } + +namespace crashpad { +namespace test { +namespace { + +// Options to use for normal tests, those that don’t require kAllowOr. +const StringToSymbolicConstantOptions kNormalOptions[] = { + 0, + kAllowFullName, + kAllowShortName, + kAllowFullName | kAllowShortName, + kAllowNumber, + kAllowFullName | kAllowNumber, + kAllowShortName | kAllowNumber, + kAllowFullName | kAllowShortName | kAllowNumber, +}; + +// If |expect| is nullptr, the conversion is expected to fail. If |expect| is +// empty, the conversion is expected to succeed, but the precise returned string +// value is unknown. Otherwise, the conversion is expected to succeed, and +// |expect| contains the precise expected string value to be returned. If +// |expect| contains the substring "0x1", the conversion is expected only to +// succeed when kUnknownIsNumeric is set. +// +// Only set kUseFullName or kUseShortName when calling this. Other options are +// exercised directly by this function. +template <typename Traits> +void TestSomethingToStringOnce(typename Traits::ValueType value, + const char* expect, + SymbolicConstantToStringOptions options) { + std::string actual = + Traits::SomethingToString(value, options | kUnknownIsEmpty | kUseOr); + std::string actual_numeric = + Traits::SomethingToString(value, options | kUnknownIsNumeric | kUseOr); + if (expect) { + if (expect[0] == '\0') { + EXPECT_FALSE(actual.empty()) << Traits::kValueName << " " << value; + } else if (strstr(expect, "0x1")) { + EXPECT_TRUE(actual.empty()) << Traits::kValueName << " " << value + << ", actual " << actual; + actual.assign(expect); + } else { + EXPECT_EQ(expect, actual) << Traits::kValueName << " " << value; + } + EXPECT_EQ(actual, actual_numeric) << Traits::kValueName << " " << value; + } else { + EXPECT_TRUE(actual.empty()) << Traits::kValueName << " " << value + << ", actual " << actual; + EXPECT_FALSE(actual_numeric.empty()) << Traits::kValueName << " " << value + << ", actual_numeric " + << actual_numeric; + } +} + +template <typename Traits> +void TestSomethingToString(typename Traits::ValueType value, + const char* expect_full, + const char* expect_short) { + { + SCOPED_TRACE("full_name"); + TestSomethingToStringOnce<Traits>(value, expect_full, kUseFullName); + } + + { + SCOPED_TRACE("short_name"); + TestSomethingToStringOnce<Traits>(value, expect_short, kUseShortName); + } +} + +template <typename Traits> +void TestStringToSomething(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + bool expect_result, + typename Traits::ValueType expect_value) { + typename Traits::ValueType actual_value; + bool actual_result = + Traits::StringToSomething(string, options, &actual_value); + if (expect_result) { + EXPECT_TRUE(actual_result) << "string " << string << ", options " << options + << ", " << Traits::kValueName << " " + << expect_value; + if (actual_result) { + EXPECT_EQ(expect_value, actual_value) << "string " << string + << ", options " << options; + } + } else { + EXPECT_FALSE(actual_result) << "string " << string << ", options " + << options << ", " << Traits::kValueName << " " + << actual_value; + } +} + +const struct { + exception_type_t exception; + const char* full_name; + const char* short_name; +} kExceptionTestData[] = { + {EXC_BAD_ACCESS, "EXC_BAD_ACCESS", "BAD_ACCESS"}, + {EXC_BAD_INSTRUCTION, "EXC_BAD_INSTRUCTION", "BAD_INSTRUCTION"}, + {EXC_ARITHMETIC, "EXC_ARITHMETIC", "ARITHMETIC"}, + {EXC_EMULATION, "EXC_EMULATION", "EMULATION"}, + {EXC_SOFTWARE, "EXC_SOFTWARE", "SOFTWARE"}, + {EXC_MACH_SYSCALL, "EXC_MACH_SYSCALL", "MACH_SYSCALL"}, + {EXC_RPC_ALERT, "EXC_RPC_ALERT", "RPC_ALERT"}, + {EXC_CRASH, "EXC_CRASH", "CRASH"}, + {EXC_RESOURCE, "EXC_RESOURCE", "RESOURCE"}, + {EXC_GUARD, "EXC_GUARD", "GUARD"}, +}; + +struct ConvertExceptionTraits { + using ValueType = exception_type_t; + static std::string SomethingToString( + ValueType value, + SymbolicConstantToStringOptions options) { + return ExceptionToString(value, options); + } + static bool StringToSomething(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + ValueType* value) { + return StringToException(string, options, value); + } + static const char kValueName[]; +}; +const char ConvertExceptionTraits::kValueName[] = "exception"; + +void TestExceptionToString(exception_type_t value, + const char* expect_full, + const char* expect_short) { + return TestSomethingToString<ConvertExceptionTraits>( + value, expect_full, expect_short); +} + +TEST(SymbolicConstantsMach, ExceptionToString) { + for (size_t index = 0; index < arraysize(kExceptionTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestExceptionToString(kExceptionTestData[index].exception, + kExceptionTestData[index].full_name, + kExceptionTestData[index].short_name); + } + + for (exception_type_t exception = 0; + exception < EXC_TYPES_COUNT + 8; + ++exception) { + SCOPED_TRACE(base::StringPrintf("exception %d", exception)); + if (exception > 0 && exception < EXC_TYPES_COUNT) { + TestExceptionToString(exception, "", ""); + } else { + TestExceptionToString(exception, nullptr, nullptr); + } + } +} + +void TestStringToException(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + bool expect_result, + exception_type_t expect_value) { + return TestStringToSomething<ConvertExceptionTraits>( + string, options, expect_result, expect_value); +} + +TEST(SymbolicConstantsMach, StringToException) { + for (size_t option_index = 0; + option_index < arraysize(kNormalOptions); + ++option_index) { + SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index)); + StringToSymbolicConstantOptions options = kNormalOptions[option_index]; + for (size_t index = 0; index < arraysize(kExceptionTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + exception_type_t exception = kExceptionTestData[index].exception; + { + SCOPED_TRACE("full_name"); + TestStringToException(kExceptionTestData[index].full_name, + options, + options & kAllowFullName, + exception); + } + { + SCOPED_TRACE("short_name"); + TestStringToException(kExceptionTestData[index].short_name, + options, + options & kAllowShortName, + exception); + } + { + SCOPED_TRACE("number"); + std::string number_string = base::StringPrintf("%d", exception); + TestStringToException( + number_string, options, options & kAllowNumber, exception); + } + } + + const char* const kNegativeTestData[] = { + "EXC_CRASH ", + " EXC_BAD_INSTRUCTION", + "CRASH ", + " BAD_INSTRUCTION", + "EXC_EXC_BAD_ACCESS", + "EXC_SOFTWARES", + "SOFTWARES", + "EXC_JUNK", + "random", + "", + }; + + for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToException(kNegativeTestData[index], options, false, 0); + } + + const struct { + const char* string; + size_t length; + } kNULTestData[] = { + NUL_TEST_DATA("\0EXC_ARITHMETIC"), + NUL_TEST_DATA("EXC_\0ARITHMETIC"), + NUL_TEST_DATA("EXC_ARITH\0METIC"), + NUL_TEST_DATA("EXC_ARITHMETIC\0"), + NUL_TEST_DATA("\0ARITHMETIC"), + NUL_TEST_DATA("ARITH\0METIC"), + NUL_TEST_DATA("ARITHMETIC\0"), + NUL_TEST_DATA("\0003"), + NUL_TEST_DATA("3\0"), + NUL_TEST_DATA("1\0002"), + }; + + for (size_t index = 0; index < arraysize(kNULTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + base::StringPiece string(kNULTestData[index].string, + kNULTestData[index].length); + TestStringToException(string, options, false, 0); + } + } + + // Ensure that a NUL is not required at the end of the string. + { + SCOPED_TRACE("trailing_NUL_full"); + TestStringToException(base::StringPiece("EXC_BREAKPOINTED", 14), + kAllowFullName, + true, + EXC_BREAKPOINT); + } + { + SCOPED_TRACE("trailing_NUL_short"); + TestStringToException(base::StringPiece("BREAKPOINTED", 10), + kAllowShortName, + true, + EXC_BREAKPOINT); + } +} + +const struct { + exception_mask_t exception_mask; + const char* full_name; + const char* short_name; +} kExceptionMaskTestData[] = { + {EXC_MASK_BAD_ACCESS, "EXC_MASK_BAD_ACCESS", "BAD_ACCESS"}, + {EXC_MASK_BAD_INSTRUCTION, "EXC_MASK_BAD_INSTRUCTION", "BAD_INSTRUCTION"}, + {EXC_MASK_ARITHMETIC, "EXC_MASK_ARITHMETIC", "ARITHMETIC"}, + {EXC_MASK_EMULATION, "EXC_MASK_EMULATION", "EMULATION"}, + {EXC_MASK_SOFTWARE, "EXC_MASK_SOFTWARE", "SOFTWARE"}, + {EXC_MASK_MACH_SYSCALL, "EXC_MASK_MACH_SYSCALL", "MACH_SYSCALL"}, + {EXC_MASK_RPC_ALERT, "EXC_MASK_RPC_ALERT", "RPC_ALERT"}, + {EXC_MASK_CRASH, "EXC_MASK_CRASH", "CRASH"}, + {EXC_MASK_RESOURCE, "EXC_MASK_RESOURCE", "RESOURCE"}, + {EXC_MASK_GUARD, "EXC_MASK_GUARD", "GUARD"}, + {0x1, "0x1", "0x1"}, + {EXC_MASK_CRASH | 0x1, "EXC_MASK_CRASH|0x1", "CRASH|0x1"}, + {EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | + EXC_MASK_EMULATION | + EXC_MASK_SOFTWARE | + EXC_MASK_BREAKPOINT | + EXC_MASK_SYSCALL | + EXC_MASK_MACH_SYSCALL | + EXC_MASK_RPC_ALERT, + "EXC_MASK_BAD_ACCESS|EXC_MASK_BAD_INSTRUCTION|EXC_MASK_ARITHMETIC|" + "EXC_MASK_EMULATION|EXC_MASK_SOFTWARE|EXC_MASK_BREAKPOINT|" + "EXC_MASK_SYSCALL|EXC_MASK_MACH_SYSCALL|EXC_MASK_RPC_ALERT", + "BAD_ACCESS|BAD_INSTRUCTION|ARITHMETIC|EMULATION|SOFTWARE|BREAKPOINT|" + "SYSCALL|MACH_SYSCALL|RPC_ALERT"}, + {EXC_MASK_RESOURCE | EXC_MASK_GUARD, + "EXC_MASK_RESOURCE|EXC_MASK_GUARD", + "RESOURCE|GUARD"}, +}; + +struct ConvertExceptionMaskTraits { + using ValueType = exception_mask_t; + static std::string SomethingToString( + ValueType value, + SymbolicConstantToStringOptions options) { + return ExceptionMaskToString(value, options); + } + static bool StringToSomething(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + ValueType* value) { + return StringToExceptionMask(string, options, value); + } + static const char kValueName[]; +}; +const char ConvertExceptionMaskTraits::kValueName[] = "exception_mask"; + +void TestExceptionMaskToString(exception_mask_t value, + const char* expect_full, + const char* expect_short) { + return TestSomethingToString<ConvertExceptionMaskTraits>( + value, expect_full, expect_short); +} + +TEST(SymbolicConstantsMach, ExceptionMaskToString) { + for (size_t index = 0; index < arraysize(kExceptionMaskTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestExceptionMaskToString(kExceptionMaskTestData[index].exception_mask, + kExceptionMaskTestData[index].full_name, + kExceptionMaskTestData[index].short_name); + } + + // Test kUseOr handling. + EXPECT_TRUE(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD, + kUseFullName).empty()); + EXPECT_TRUE(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD, + kUseShortName).empty()); + EXPECT_EQ("0x1400", + ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD, + kUseFullName | kUnknownIsNumeric)); + EXPECT_EQ("0x1400", + ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD, + kUseShortName | kUnknownIsNumeric)); + EXPECT_EQ("EXC_MASK_CRASH|EXC_MASK_GUARD", + ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD, + kUseFullName | kUseOr)); + EXPECT_EQ("CRASH|GUARD", + ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD, + kUseShortName | kUseOr)); +} + +void TestStringToExceptionMask(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + bool expect_result, + exception_mask_t expect_value) { + return TestStringToSomething<ConvertExceptionMaskTraits>( + string, options, expect_result, expect_value); +} + +TEST(SymbolicConstantsMach, StringToExceptionMask) { + // Don’t use kNormalOptions, because kAllowOr needs to be tested. + const StringToSymbolicConstantOptions kOptions[] = { + 0, + kAllowFullName, + kAllowShortName, + kAllowFullName | kAllowShortName, + kAllowNumber, + kAllowFullName | kAllowNumber, + kAllowShortName | kAllowNumber, + kAllowFullName | kAllowShortName | kAllowNumber, + kAllowOr, + kAllowFullName | kAllowOr, + kAllowShortName | kAllowOr, + kAllowFullName | kAllowShortName | kAllowOr, + kAllowNumber | kAllowOr, + kAllowFullName | kAllowNumber | kAllowOr, + kAllowShortName | kAllowNumber | kAllowOr, + kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr, + }; + + for (size_t option_index = 0; + option_index < arraysize(kOptions); + ++option_index) { + SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index)); + StringToSymbolicConstantOptions options = kOptions[option_index]; + for (size_t index = 0; index < arraysize(kExceptionMaskTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + exception_mask_t exception_mask = + kExceptionMaskTestData[index].exception_mask; + { + SCOPED_TRACE("full_name"); + base::StringPiece full_name(kExceptionMaskTestData[index].full_name); + bool has_number = full_name.find("0x", 0) != base::StringPiece::npos; + bool has_or = full_name.find('|', 0) != base::StringPiece::npos; + bool allowed_characteristics = + (has_number ? (options & kAllowNumber) : true) && + (has_or ? (options & kAllowOr) : true); + bool is_number = full_name.compare("0x1") == 0; + bool expect_valid = + ((options & kAllowFullName) && allowed_characteristics) || + ((options & kAllowNumber) && is_number); + TestStringToExceptionMask( + full_name, options, expect_valid, exception_mask); + } + { + SCOPED_TRACE("short_name"); + base::StringPiece short_name(kExceptionMaskTestData[index].short_name); + bool has_number = short_name.find("0x", 0) != base::StringPiece::npos; + bool has_or = short_name.find('|', 0) != base::StringPiece::npos; + bool allowed_characteristics = + (has_number ? (options & kAllowNumber) : true) && + (has_or ? (options & kAllowOr) : true); + bool is_number = short_name.compare("0x1") == 0; + bool expect_valid = + ((options & kAllowShortName) && allowed_characteristics) || + ((options & kAllowNumber) && is_number); + TestStringToExceptionMask( + short_name, options, expect_valid, exception_mask); + } + } + + const char* const kNegativeTestData[] = { + "EXC_MASK_CRASH ", + " EXC_MASK_BAD_INSTRUCTION", + "EXC_MASK_EXC_MASK_BAD_ACCESS", + "EXC_MASK_SOFTWARES", + "EXC_MASK_JUNK", + "EXC_GUARD", + "EXC_ARITHMETIC|EXC_FAKE", + "ARITHMETIC|FAKE", + "FAKE|ARITHMETIC", + "EXC_FAKE|EXC_ARITHMETIC", + "random", + "", + }; + + for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToExceptionMask(kNegativeTestData[index], options, false, 0); + } + + const struct { + const char* string; + size_t length; + } kNULTestData[] = { + NUL_TEST_DATA("\0EXC_MASK_ARITHMETIC"), + NUL_TEST_DATA("EXC_\0MASK_ARITHMETIC"), + NUL_TEST_DATA("EXC_MASK_\0ARITHMETIC"), + NUL_TEST_DATA("EXC_MASK_ARITH\0METIC"), + NUL_TEST_DATA("EXC_MASK_ARITHMETIC\0"), + NUL_TEST_DATA("\0ARITHMETIC"), + NUL_TEST_DATA("ARITH\0METIC"), + NUL_TEST_DATA("ARITHMETIC\0"), + NUL_TEST_DATA("\0003"), + NUL_TEST_DATA("3\0"), + NUL_TEST_DATA("1\0002"), + NUL_TEST_DATA("EXC_MASK_ARITHMETIC\0|EXC_MASK_EMULATION"), + NUL_TEST_DATA("EXC_MASK_ARITHMETIC|\0EXC_MASK_EMULATION"), + NUL_TEST_DATA("ARITHMETIC\0|EMULATION"), + NUL_TEST_DATA("ARITHMETIC|\0EMULATION"), + }; + + for (size_t index = 0; index < arraysize(kNULTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + base::StringPiece string(kNULTestData[index].string, + kNULTestData[index].length); + TestStringToExceptionMask(string, options, false, 0); + } + } + + const struct { + const char* string; + StringToSymbolicConstantOptions options; + exception_mask_t mask; + } kNonCanonicalTestData[] = { + {"EXC_MASK_ALL", kAllowFullName, ExcMaskAll()}, + {"ALL", kAllowShortName, ExcMaskAll()}, + {"EXC_MASK_ALL|EXC_MASK_CRASH", + kAllowFullName | kAllowOr, + ExcMaskAll() | EXC_MASK_CRASH}, + {"ALL|CRASH", + kAllowShortName | kAllowOr, + ExcMaskAll() | EXC_MASK_CRASH}, + {"EXC_MASK_BAD_INSTRUCTION|EXC_MASK_BAD_ACCESS", + kAllowFullName | kAllowOr, + EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION}, + {"EMULATION|ARITHMETIC", + kAllowShortName | kAllowOr, + EXC_MASK_ARITHMETIC | EXC_MASK_EMULATION}, + {"EXC_MASK_SOFTWARE|BREAKPOINT", + kAllowFullName | kAllowShortName | kAllowOr, + EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT}, + {"SYSCALL|0x100", + kAllowShortName | kAllowNumber | kAllowOr, + EXC_MASK_SYSCALL | 0x100}, + }; + + for (size_t index = 0; index < arraysize(kNonCanonicalTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToExceptionMask(kNonCanonicalTestData[index].string, + kNonCanonicalTestData[index].options, + true, + kNonCanonicalTestData[index].mask); + } + + // Ensure that a NUL is not required at the end of the string. + { + SCOPED_TRACE("trailing_NUL_full"); + TestStringToExceptionMask(base::StringPiece("EXC_MASK_BREAKPOINTED", 19), + kAllowFullName, + true, + EXC_MASK_BREAKPOINT); + } + { + SCOPED_TRACE("trailing_NUL_short"); + TestStringToExceptionMask(base::StringPiece("BREAKPOINTED", 10), + kAllowShortName, + true, + EXC_MASK_BREAKPOINT); + } +} + +const struct { + exception_behavior_t behavior; + const char* full_name; + const char* short_name; +} kExceptionBehaviorTestData[] = { + {EXCEPTION_DEFAULT, "EXCEPTION_DEFAULT", "DEFAULT"}, + {EXCEPTION_STATE, "EXCEPTION_STATE", "STATE"}, + {EXCEPTION_STATE_IDENTITY, "EXCEPTION_STATE_IDENTITY", "STATE_IDENTITY"}, + {implicit_cast<exception_behavior_t>(EXCEPTION_DEFAULT | + MACH_EXCEPTION_CODES), + "EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES", + "DEFAULT|MACH"}, + {implicit_cast<exception_behavior_t>(EXCEPTION_STATE | + MACH_EXCEPTION_CODES), + "EXCEPTION_STATE|MACH_EXCEPTION_CODES", + "STATE|MACH"}, + {implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY | + MACH_EXCEPTION_CODES), + "EXCEPTION_STATE_IDENTITY|MACH_EXCEPTION_CODES", + "STATE_IDENTITY|MACH"}, +}; + +struct ConvertExceptionBehaviorTraits { + using ValueType = exception_behavior_t; + static std::string SomethingToString( + ValueType value, + SymbolicConstantToStringOptions options) { + return ExceptionBehaviorToString(value, options); + } + static bool StringToSomething(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + ValueType* value) { + return StringToExceptionBehavior(string, options, value); + } + static const char kValueName[]; +}; +const char ConvertExceptionBehaviorTraits::kValueName[] = "behavior"; + +void TestExceptionBehaviorToString(exception_behavior_t value, + const char* expect_full, + const char* expect_short) { + return TestSomethingToString<ConvertExceptionBehaviorTraits>( + value, expect_full, expect_short); +} + +TEST(SymbolicConstantsMach, ExceptionBehaviorToString) { + for (size_t index = 0; + index < arraysize(kExceptionBehaviorTestData); + ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestExceptionBehaviorToString(kExceptionBehaviorTestData[index].behavior, + kExceptionBehaviorTestData[index].full_name, + kExceptionBehaviorTestData[index].short_name); + } + + for (exception_behavior_t behavior = 0; behavior < 8; ++behavior) { + SCOPED_TRACE(base::StringPrintf("behavior %d", behavior)); + exception_behavior_t behavior_mach = behavior | MACH_EXCEPTION_CODES; + if (behavior > 0 && behavior <= EXCEPTION_STATE_IDENTITY) { + TestExceptionBehaviorToString(behavior, "", ""); + TestExceptionBehaviorToString(behavior_mach, "", ""); + } else { + TestExceptionBehaviorToString(behavior, nullptr, nullptr); + TestExceptionBehaviorToString(behavior_mach, nullptr, nullptr); + } + } +} + +void TestStringToExceptionBehavior(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + bool expect_result, + exception_behavior_t expect_value) { + return TestStringToSomething<ConvertExceptionBehaviorTraits>( + string, options, expect_result, expect_value); +} + +TEST(SymbolicConstantsMach, StringToExceptionBehavior) { + for (size_t option_index = 0; + option_index < arraysize(kNormalOptions); + ++option_index) { + SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index)); + StringToSymbolicConstantOptions options = kNormalOptions[option_index]; + for (size_t index = 0; + index < arraysize(kExceptionBehaviorTestData); + ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + exception_behavior_t behavior = + kExceptionBehaviorTestData[index].behavior; + { + SCOPED_TRACE("full_name"); + TestStringToExceptionBehavior( + kExceptionBehaviorTestData[index].full_name, + options, + options & kAllowFullName, + behavior); + } + { + SCOPED_TRACE("short_name"); + TestStringToExceptionBehavior( + kExceptionBehaviorTestData[index].short_name, + options, + options & kAllowShortName, + behavior); + } + { + SCOPED_TRACE("number"); + std::string number_string = base::StringPrintf("0x%x", behavior); + TestStringToExceptionBehavior( + number_string, options, options & kAllowNumber, behavior); + } + } + + const char* const kNegativeTestData[] = { + "EXCEPTION_DEFAULT ", + " EXCEPTION_STATE", + "EXCEPTION_EXCEPTION_STATE_IDENTITY", + "EXCEPTION_DEFAULTS", + "EXCEPTION_JUNK", + "random", + "MACH_EXCEPTION_CODES", + "MACH", + "MACH_EXCEPTION_CODES|MACH_EXCEPTION_CODES", + "MACH_EXCEPTION_CODES|EXCEPTION_NONEXISTENT", + "MACH|junk", + "EXCEPTION_DEFAULT|EXCEPTION_STATE", + "1|2", + "", + }; + + for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToExceptionBehavior( + kNegativeTestData[index], options, false, 0); + } + + const struct { + const char* string; + size_t length; + } kNULTestData[] = { + NUL_TEST_DATA("\0EXCEPTION_STATE_IDENTITY"), + NUL_TEST_DATA("EXCEPTION_\0STATE_IDENTITY"), + NUL_TEST_DATA("EXCEPTION_STATE\0_IDENTITY"), + NUL_TEST_DATA("EXCEPTION_STATE_IDENTITY\0"), + NUL_TEST_DATA("\0STATE_IDENTITY"), + NUL_TEST_DATA("STATE\0_IDENTITY"), + NUL_TEST_DATA("STATE_IDENTITY\0"), + NUL_TEST_DATA("\0003"), + NUL_TEST_DATA("3\0"), + NUL_TEST_DATA("0x8000000\0001"), + NUL_TEST_DATA("EXCEPTION_STATE_IDENTITY\0|MACH_EXCEPTION_CODES"), + NUL_TEST_DATA("EXCEPTION_STATE_IDENTITY|\0MACH_EXCEPTION_CODES"), + NUL_TEST_DATA("STATE_IDENTITY\0|MACH"), + NUL_TEST_DATA("STATE_IDENTITY|\0MACH"), + }; + + for (size_t index = 0; index < arraysize(kNULTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + base::StringPiece string(kNULTestData[index].string, + kNULTestData[index].length); + TestStringToExceptionBehavior(string, options, false, 0); + } + } + + const struct { + const char* string; + StringToSymbolicConstantOptions options; + exception_behavior_t behavior; + } kNonCanonicalTestData[] = { + {"MACH_EXCEPTION_CODES|EXCEPTION_STATE_IDENTITY", + kAllowFullName, + implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY | + MACH_EXCEPTION_CODES)}, + {"MACH|STATE_IDENTITY", + kAllowShortName, + implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY | + MACH_EXCEPTION_CODES)}, + {"MACH_EXCEPTION_CODES|STATE", + kAllowFullName | kAllowShortName, + implicit_cast<exception_behavior_t>(EXCEPTION_STATE | + MACH_EXCEPTION_CODES)}, + {"MACH|EXCEPTION_STATE", + kAllowFullName | kAllowShortName, + implicit_cast<exception_behavior_t>(EXCEPTION_STATE | + MACH_EXCEPTION_CODES)}, + {"3|MACH_EXCEPTION_CODES", + kAllowFullName | kAllowNumber, + implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 3)}, + {"MACH|0x2", + kAllowShortName | kAllowNumber, + implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 0x2)}, + }; + + for (size_t index = 0; index < arraysize(kNonCanonicalTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToExceptionBehavior(kNonCanonicalTestData[index].string, + kNonCanonicalTestData[index].options, + true, + kNonCanonicalTestData[index].behavior); + } + + // Ensure that a NUL is not required at the end of the string. + { + SCOPED_TRACE("trailing_NUL_full"); + TestStringToExceptionBehavior(base::StringPiece("EXCEPTION_DEFAULTS", 17), + kAllowFullName, + true, + EXCEPTION_DEFAULT); + } + { + SCOPED_TRACE("trailing_NUL_short"); + TestStringToExceptionBehavior(base::StringPiece("DEFAULTS", 7), + kAllowShortName, + true, + EXCEPTION_DEFAULT); + } + { + SCOPED_TRACE("trailing_NUL_full_mach"); + base::StringPiece string("EXCEPTION_DEFAULT|MACH_EXCEPTION_CODESS", 38); + TestStringToExceptionBehavior(string, + kAllowFullName | kAllowOr, + true, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES); + } + { + SCOPED_TRACE("trailing_NUL_short_mach"); + TestStringToExceptionBehavior(base::StringPiece("DEFAULT|MACH_", 12), + kAllowShortName | kAllowOr, + true, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES); + } +} + +const struct { + thread_state_flavor_t flavor; + const char* full_name; + const char* short_name; +} kThreadStateFlavorTestData[] = { + {THREAD_STATE_NONE, "THREAD_STATE_NONE", "NONE"}, + {THREAD_STATE_FLAVOR_LIST, "THREAD_STATE_FLAVOR_LIST", "FLAVOR_LIST"}, + {THREAD_STATE_FLAVOR_LIST_NEW, + "THREAD_STATE_FLAVOR_LIST_NEW", + "FLAVOR_LIST_NEW"}, + {THREAD_STATE_FLAVOR_LIST_10_9, + "THREAD_STATE_FLAVOR_LIST_10_9", + "FLAVOR_LIST_10_9"}, +#if defined(__i386__) || defined(__x86_64__) + {x86_THREAD_STATE32, "x86_THREAD_STATE32", "THREAD32"}, + {x86_FLOAT_STATE32, "x86_FLOAT_STATE32", "FLOAT32"}, + {x86_EXCEPTION_STATE32, "x86_EXCEPTION_STATE32", "EXCEPTION32"}, + {x86_THREAD_STATE64, "x86_THREAD_STATE64", "THREAD64"}, + {x86_FLOAT_STATE64, "x86_FLOAT_STATE64", "FLOAT64"}, + {x86_EXCEPTION_STATE64, "x86_EXCEPTION_STATE64", "EXCEPTION64"}, + {x86_THREAD_STATE, "x86_THREAD_STATE", "THREAD"}, + {x86_FLOAT_STATE, "x86_FLOAT_STATE", "FLOAT"}, + {x86_EXCEPTION_STATE, "x86_EXCEPTION_STATE", "EXCEPTION"}, + {x86_DEBUG_STATE32, "x86_DEBUG_STATE32", "DEBUG32"}, + {x86_DEBUG_STATE64, "x86_DEBUG_STATE64", "DEBUG64"}, + {x86_DEBUG_STATE, "x86_DEBUG_STATE", "DEBUG"}, + {14, "x86_SAVED_STATE32", "SAVED32"}, + {15, "x86_SAVED_STATE64", "SAVED64"}, + {x86_AVX_STATE32, "x86_AVX_STATE32", "AVX32"}, + {x86_AVX_STATE64, "x86_AVX_STATE64", "AVX64"}, + {x86_AVX_STATE, "x86_AVX_STATE", "AVX"}, +#elif defined(__ppc__) || defined(__ppc64__) + {PPC_THREAD_STATE, "PPC_THREAD_STATE", "THREAD"}, + {PPC_FLOAT_STATE, "PPC_FLOAT_STATE", "FLOAT"}, + {PPC_EXCEPTION_STATE, "PPC_EXCEPTION_STATE", "EXCEPTION"}, + {PPC_VECTOR_STATE, "PPC_VECTOR_STATE", "VECTOR"}, + {PPC_THREAD_STATE64, "PPC_THREAD_STATE64", "THREAD64"}, + {PPC_EXCEPTION_STATE64, "PPC_EXCEPTION_STATE64", "EXCEPTION64"}, +#elif defined(__arm__) || defined(__arm64__) + {ARM_THREAD_STATE, "ARM_THREAD_STATE", "THREAD"}, + {ARM_VFP_STATE, "ARM_VFP_STATE", "VFP"}, + {ARM_EXCEPTION_STATE, "ARM_EXCEPTION_STATE", "EXCEPTION"}, + {ARM_DEBUG_STATE, "ARM_DEBUG_STATE", "DEBUG"}, + {ARM_THREAD_STATE64, "ARM_THREAD_STATE64", "THREAD64"}, + {ARM_EXCEPTION_STATE64, "ARM_EXCEPTION_STATE64", "EXCEPTION64"}, + {ARM_THREAD_STATE32, "ARM_THREAD_STATE32", "THREAD32"}, + {ARM_DEBUG_STATE32, "ARM_DEBUG_STATE32", "DEBUG32"}, + {ARM_DEBUG_STATE64, "ARM_DEBUG_STATE64", "DEBUG64"}, + {ARM_NEON_STATE, "ARM_NEON_STATE", "NEON"}, + {ARM_NEON_STATE64, "ARM_NEON_STATE64", "NEON64"}, +#endif +}; + +struct ConvertThreadStateFlavorTraits { + using ValueType = thread_state_flavor_t; + static std::string SomethingToString( + ValueType value, + SymbolicConstantToStringOptions options) { + return ThreadStateFlavorToString(value, options); + } + static bool StringToSomething(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + ValueType* value) { + return StringToThreadStateFlavor(string, options, value); + } + static const char kValueName[]; +}; +const char ConvertThreadStateFlavorTraits::kValueName[] = "flavor"; + +void TestThreadStateFlavorToString(exception_type_t value, + const char* expect_full, + const char* expect_short) { + return TestSomethingToString<ConvertThreadStateFlavorTraits>( + value, expect_full, expect_short); +} + +TEST(SymbolicConstantsMach, ThreadStateFlavorToString) { + for (size_t index = 0; + index < arraysize(kThreadStateFlavorTestData); + ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestThreadStateFlavorToString(kThreadStateFlavorTestData[index].flavor, + kThreadStateFlavorTestData[index].full_name, + kThreadStateFlavorTestData[index].short_name); + } + + for (thread_state_flavor_t flavor = 0; flavor < 136; ++flavor) { + SCOPED_TRACE(base::StringPrintf("flavor %d", flavor)); + + // Flavor numbers appear to be assigned somewhat haphazardly, especially on + // certain architectures. The conditional should match flavors that + // ThreadStateFlavorToString() knows how to convert. + if ( +#if defined(__i386__) || defined(__x86_64__) + flavor <= x86_AVX_STATE +#elif defined(__ppc__) || defined(__ppc64__) + flavor <= THREAD_STATE_NONE +#elif defined(__arm__) || defined(__arm64__) + (flavor <= ARM_EXCEPTION_STATE64 || flavor == ARM_THREAD_STATE32 || + (flavor >= ARM_DEBUG_STATE32 && flavor <= ARM_NEON_STATE64)) +#endif + || + flavor == THREAD_STATE_FLAVOR_LIST_NEW || + flavor == THREAD_STATE_FLAVOR_LIST_10_9) { + TestThreadStateFlavorToString(flavor, "", ""); + } else { + TestThreadStateFlavorToString(flavor, nullptr, nullptr); + } + } +} + +void TestStringToThreadStateFlavor(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + bool expect_result, + thread_state_flavor_t expect_value) { + return TestStringToSomething<ConvertThreadStateFlavorTraits>( + string, options, expect_result, expect_value); +} + +TEST(SymbolicConstantsMach, StringToThreadStateFlavor) { + for (size_t option_index = 0; + option_index < arraysize(kNormalOptions); + ++option_index) { + SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index)); + StringToSymbolicConstantOptions options = kNormalOptions[option_index]; + for (size_t index = 0; + index < arraysize(kThreadStateFlavorTestData); + ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + thread_state_flavor_t flavor = kThreadStateFlavorTestData[index].flavor; + { + SCOPED_TRACE("full_name"); + TestStringToThreadStateFlavor( + kThreadStateFlavorTestData[index].full_name, + options, + options & kAllowFullName, + flavor); + } + { + SCOPED_TRACE("short_name"); + TestStringToThreadStateFlavor( + kThreadStateFlavorTestData[index].short_name, + options, + options & kAllowShortName, + flavor); + } + { + SCOPED_TRACE("number"); + std::string number_string = base::StringPrintf("%d", flavor); + TestStringToThreadStateFlavor( + number_string, options, options & kAllowNumber, flavor); + } + } + + const char* const kNegativeTestData[] = { + "THREAD_STATE_NONE ", + " THREAD_STATE_NONE", + "NONE ", + " NONE", + "THREAD_STATE_THREAD_STATE_NONE", + "THREAD_STATE_NONE_AT_ALL", + "NONE_AT_ALL", + "THREAD_STATE_JUNK", + "JUNK", + "random", + " THREAD64", + "THREAD64 ", + "THREAD642", + "", +#if defined(__i386__) || defined(__x86_64__) + " x86_THREAD_STATE64", + "x86_THREAD_STATE64 ", + "x86_THREAD_STATE642", + "x86_JUNK", + "x86_JUNK_STATE32", + "PPC_THREAD_STATE", + "ARM_THREAD_STATE", +#elif defined(__ppc__) || defined(__ppc64__) + " PPC_THREAD_STATE64", + "PPC_THREAD_STATE64 ", + "PPC_THREAD_STATE642", + "PPC_JUNK", + "PPC_JUNK_STATE32", + "x86_THREAD_STATE", + "ARM_THREAD_STATE", +#elif defined(__arm__) || defined(__arm64__) + " ARM_THREAD_STATE64", + "ARM_THREAD_STATE64 ", + "ARM_THREAD_STATE642", + "ARM_JUNK", + "ARM_JUNK_STATE32", + "x86_THREAD_STATE", + "PPC_THREAD_STATE", +#endif + }; + + for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToThreadStateFlavor( + kNegativeTestData[index], options, false, 0); + } + + const struct { + const char* string; + size_t length; + } kNULTestData[] = { + NUL_TEST_DATA("\0THREAD_STATE_NONE"), + NUL_TEST_DATA("THREAD_\0STATE_NONE"), + NUL_TEST_DATA("THREAD_STATE_\0NONE"), + NUL_TEST_DATA("THREAD_STATE_NO\0NE"), + NUL_TEST_DATA("THREAD_STATE_NONE\0"), + NUL_TEST_DATA("\0NONE"), + NUL_TEST_DATA("NO\0NE"), + NUL_TEST_DATA("NONE\0"), + NUL_TEST_DATA("\0THREAD_STATE_FLAVOR_LIST_NEW"), + NUL_TEST_DATA("THREAD_STATE_\0FLAVOR_LIST_NEW"), + NUL_TEST_DATA("THREAD_STATE_FLAVOR_LIST\0_NEW"), + NUL_TEST_DATA("THREAD_STATE_FLAVOR_LIST_NEW\0"), + NUL_TEST_DATA("\0FLAVOR_LIST_NEW"), + NUL_TEST_DATA("FLAVOR_LIST\0_NEW"), + NUL_TEST_DATA("FLAVOR_LIST_NEW\0"), + NUL_TEST_DATA("\0THREAD"), + NUL_TEST_DATA("THR\0EAD"), + NUL_TEST_DATA("THREAD\0"), + NUL_TEST_DATA("\0THREAD64"), + NUL_TEST_DATA("THR\0EAD64"), + NUL_TEST_DATA("THREAD\064"), + NUL_TEST_DATA("THREAD64\0"), + NUL_TEST_DATA("\0002"), + NUL_TEST_DATA("2\0"), + NUL_TEST_DATA("1\0002"), +#if defined(__i386__) || defined(__x86_64__) + NUL_TEST_DATA("\0x86_THREAD_STATE64"), + NUL_TEST_DATA("x86\0_THREAD_STATE64"), + NUL_TEST_DATA("x86_\0THREAD_STATE64"), + NUL_TEST_DATA("x86_THR\0EAD_STATE64"), + NUL_TEST_DATA("x86_THREAD\0_STATE64"), + NUL_TEST_DATA("x86_THREAD_\0STATE64"), + NUL_TEST_DATA("x86_THREAD_STA\0TE64"), + NUL_TEST_DATA("x86_THREAD_STATE\00064"), + NUL_TEST_DATA("x86_THREAD_STATE64\0"), +#elif defined(__ppc__) || defined(__ppc64__) + NUL_TEST_DATA("\0PPC_THREAD_STATE64"), + NUL_TEST_DATA("PPC\0_THREAD_STATE64"), + NUL_TEST_DATA("PPC_\0THREAD_STATE64"), + NUL_TEST_DATA("PPC_THR\0EAD_STATE64"), + NUL_TEST_DATA("PPC_THREAD\0_STATE64"), + NUL_TEST_DATA("PPC_THREAD_\0STATE64"), + NUL_TEST_DATA("PPC_THREAD_STA\0TE64"), + NUL_TEST_DATA("PPC_THREAD_STATE\00064"), +#elif defined(__arm__) || defined(__arm64__) + NUL_TEST_DATA("\0ARM_THREAD_STATE64"), + NUL_TEST_DATA("ARM\0_THREAD_STATE64"), + NUL_TEST_DATA("ARM_\0THREAD_STATE64"), + NUL_TEST_DATA("ARM_THR\0EAD_STATE64"), + NUL_TEST_DATA("ARM_THREAD\0_STATE64"), + NUL_TEST_DATA("ARM_THREAD_\0STATE64"), + NUL_TEST_DATA("ARM_THREAD_STA\0TE64"), + NUL_TEST_DATA("ARM_THREAD_STATE\00064"), +#endif + }; + + for (size_t index = 0; index < arraysize(kNULTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + base::StringPiece string(kNULTestData[index].string, + kNULTestData[index].length); + TestStringToThreadStateFlavor(string, options, false, 0); + } + } + + // Ensure that a NUL is not required at the end of the string. + { + SCOPED_TRACE("trailing_NUL_full"); + TestStringToThreadStateFlavor(base::StringPiece("THREAD_STATE_NONER", 17), + kAllowFullName, + true, + THREAD_STATE_NONE); + } + { + SCOPED_TRACE("trailing_NUL_short"); + TestStringToThreadStateFlavor(base::StringPiece("NONER", 4), + kAllowShortName, + true, + THREAD_STATE_NONE); + } + { + SCOPED_TRACE("trailing_NUL_full_new"); + base::StringPiece string("THREAD_STATE_FLAVOR_LIST_NEWS", 28); + TestStringToThreadStateFlavor( + string, kAllowFullName, true, THREAD_STATE_FLAVOR_LIST_NEW); + } + { + SCOPED_TRACE("trailing_NUL_short_new"); + TestStringToThreadStateFlavor(base::StringPiece("FLAVOR_LIST_NEWS", 15), + kAllowShortName, + true, + THREAD_STATE_FLAVOR_LIST_NEW); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/task_for_pid.cc b/third_party/crashpad/crashpad/util/mach/task_for_pid.cc new file mode 100644 index 0000000..1f238b1 --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/task_for_pid.cc
@@ -0,0 +1,167 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/task_for_pid.h" + +#include <sys/sysctl.h> +#include <unistd.h> + +#include <algorithm> +#include <iterator> +#include <set> + +#include "base/basictypes.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "util/posix/process_info.h" + +namespace crashpad { + +namespace { + +//! \brief Determines whether the groups that \a process_reader belongs to are +//! a subset of the groups that the current process belongs to. +//! +//! This function is similar to 10.9.5 +//! `xnu-2422.115.4/bsd/kern/kern_credential.c` `kauth_cred_gid_subset()`. +bool TaskForPIDGroupCheck(const ProcessInfo& process_info) { + std::set<gid_t> groups = process_info.AllGroups(); + + ProcessInfo process_info_self; + if (!process_info_self.Initialize(getpid())) { + return false; + } + + std::set<gid_t> groups_self = process_info_self.AllGroups(); + + // difference will only contain elements of groups not present in groups_self. + // It will not contain elements of groups_self not present in groups. (That + // would be std::set_symmetric_difference.) + std::set<gid_t> difference; + std::set_difference(groups.begin(), + groups.end(), + groups_self.begin(), + groups_self.end(), + std::inserter(difference, difference.begin())); + if (!difference.empty()) { + LOG(ERROR) << "permission denied (gid)"; + return false; + } + + return true; +} + +//! \brief Determines whether the current process should have permission to +//! access the specified task port. +//! +//! This function is similar to 10.9.5 +//! `xnu-2422.115.4/bsd/vm/vm_unix.c` `task_for_pid_posix_check()`. +//! +//! This function accepts a `task_t` argument instead of a `pid_t` argument, +//! implying that the task send right must be retrieved before it can be +//! checked. This is done because a `pid_t` argument may refer to a different +//! task in between the time that access is checked and its corresponding +//! `task_t` is obtained by `task_for_pid()`. When `task_for_pid()` is called +//! first, any operations requiring the process ID will call `pid_for_task()` +//! and be guaranteed to use the process ID corresponding to the correct task, +//! or to fail if that task is no longer running. If the task dies and the PID +//! is recycled, it is still possible to look up the wrong PID, but falsely +//! granting task access based on the new process’ characteristics is harmless +//! because the task will be a dead name at that point. +bool TaskForPIDCheck(task_t task) { + // If the effective user ID is not 0, then this code is not running as root at + // all, and the kernel’s own checks are sufficient to determine access. The + // point of this function is to simulate the kernel’s own checks when the + // effective user ID is 0 but the real user ID is anything else. + if (geteuid() != 0) { + return true; + } + + // If the real user ID is 0, then this code is not running setuid root, it’s + // genuinely running as root, and it should be allowed maximum access. + uid_t uid = getuid(); + if (uid == 0) { + return true; + } + + // task_for_pid_posix_check() would permit access to the running process’ own + // task here, and would then check the kern.tfp.policy sysctl. If set to + // KERN_TFP_POLICY_DENY, it would deny access. + // + // This behavior is not duplicated here because the point of this function is + // to permit task_for_pid() access for setuid root programs. It is assumed + // that a setuid root program ought to be able to overcome any policy set in + // kern.tfp.policy. + // + // Access to the running process’ own task is not granted outright and is + // instead subjected to the same user/group ID checks as any other process. + // This has the effect of denying access to the running process’ own task when + // it is setuid root. This is intentional, because it prevents the same sort + // of cross-privilege disclosure discussed below at the DidChangePriveleges() + // check. The running process can still access its own task port via + // mach_task_self(), but a non-root user cannot coerce a setuid root tool to + // operate on itself by specifying its own process ID to this TaskForPID() + // interface. + + ProcessInfo process_info; + if (!process_info.InitializeFromTask(task)) { + return false; + } + + // The target process’ real user ID, effective user ID, and saved set-user ID + // must match this process’ own real user ID. task_for_pid_posix_check() + // checks against the current process’ effective user ID, but for the purposes + // of this function, when running setuid root, the real user ID is the correct + // choice. + if (process_info.RealUserID() != uid || + process_info.EffectiveUserID() != uid || + process_info.SavedUserID() != uid) { + LOG(ERROR) << "permission denied (uid)"; + return false; + } + + // The target process must not have changed privileges. The rationale for this + // check is explained in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c + // issetugid(): processes that have changed privileges may have loaded data + // using different credentials than they are currently operating with, and + // allowing other processes access to this data based solely on a check of the + // current credentials could violate confidentiality. + if (process_info.DidChangePrivileges()) { + LOG(ERROR) << "permission denied (P_SUGID)"; + return false; + } + + return TaskForPIDGroupCheck(process_info); +} + +} // namespace + +task_t TaskForPID(pid_t pid) { + task_t task; + kern_return_t kr = task_for_pid(mach_task_self(), pid, &task); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "task_for_pid"; + return TASK_NULL; + } + + base::mac::ScopedMachSendRight task_owner(task); + + if (!TaskForPIDCheck(task)) { + return TASK_NULL; + } + + return task_owner.release(); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/task_for_pid.h b/third_party/crashpad/crashpad/util/mach/task_for_pid.h new file mode 100644 index 0000000..2b81fbe --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/task_for_pid.h
@@ -0,0 +1,59 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_ +#define CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_ + +#include <mach/mach.h> +#include <sys/types.h> + +namespace crashpad { + +//! \brief Wraps `task_for_pid()`. +//! +//! This function exists to support `task_for_pid()` access checks in a setuid +//! environment. Normally, `task_for_pid()` can only return an arbitrary task’s +//! port when running as root or when taskgated(8) approves. When not running as +//! root, a series of access checks are perfomed to ensure that the running +//! process has permission to obtain the other process’ task port. +//! +//! It is possible to make an executable setuid root to give it broader +//! `task_for_pid()` access by bypassing taskgated(8) checks, but this also has +//! the effect of bypassing the access checks, allowing any process’ task port +//! to be obtained. In most situations, these access checks are desirable to +//! prevent security and privacy breaches. +//! +//! When running as setuid root, this function wraps `task_for_pid()`, +//! reimplementing those access checks. A process whose effective user ID is 0 +//! and whose real user ID is nonzero is understood to be running setuid root. +//! In this case, the requested task’s real, effective, and saved set-user IDs +//! must all equal the running process’ real user ID, the requested task must +//! not have changed privileges, and the requested task’s set of all group IDs +//! (including its real, effective, and saved set-group IDs and supplementary +//! group list) must be a subset of the running process’ set of all group IDs. +//! These access checks mimic those that the kernel performs. +//! +//! When not running as setuid root, `task_for_pid()` is called directly, +//! without imposing any additional checks beyond what the kernel does. +//! +//! \param[in] pid The process ID of the task whose task port is desired. +//! +//! \return A send right to the task port if it could be obtained, or +//! `TASK_NULL` otherwise, with an error message logged. If a send right is +//! returned, the caller takes ownership of it. +task_t TaskForPID(pid_t pid); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_
diff --git a/third_party/crashpad/crashpad/util/mach/task_memory.cc b/third_party/crashpad/crashpad/util/mach/task_memory.cc new file mode 100644 index 0000000..b87d02e --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/task_memory.cc
@@ -0,0 +1,158 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/task_memory.h" + +#include <mach/mach_vm.h> +#include <string.h> + +#include <algorithm> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/strings/stringprintf.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { + +TaskMemory::MappedMemory::~MappedMemory() { +} + +bool TaskMemory::MappedMemory::ReadCString( + size_t offset, std::string* string) const { + if (offset >= user_size_) { + LOG(WARNING) << "offset out of range"; + return false; + } + + const char* string_base = reinterpret_cast<const char*>(data_) + offset; + size_t max_length = user_size_ - offset; + size_t string_length = strnlen(string_base, max_length); + if (string_length == max_length) { + LOG(WARNING) << "unterminated string"; + return false; + } + + string->assign(string_base, string_length); + return true; +} + +TaskMemory::MappedMemory::MappedMemory(vm_address_t vm_address, + size_t vm_size, + size_t user_offset, + size_t user_size) + : vm_(vm_address, vm_size), + data_(reinterpret_cast<const void*>(vm_address + user_offset)), + user_size_(user_size) { + vm_address_t vm_end = vm_address + vm_size; + vm_address_t user_address = reinterpret_cast<vm_address_t>(data_); + vm_address_t user_end = user_address + user_size; + DCHECK_GE(user_address, vm_address); + DCHECK_LE(user_address, vm_end); + DCHECK_GE(user_end, vm_address); + DCHECK_LE(user_end, vm_end); +} + +TaskMemory::TaskMemory(task_t task) : task_(task) { +} + +bool TaskMemory::Read(mach_vm_address_t address, size_t size, void* buffer) { + scoped_ptr<MappedMemory> memory = ReadMapped(address, size); + if (!memory) { + return false; + } + + memcpy(buffer, memory->data(), size); + return true; +} + +scoped_ptr<TaskMemory::MappedMemory> TaskMemory::ReadMapped( + mach_vm_address_t address, size_t size) { + if (size == 0) { + return scoped_ptr<MappedMemory>(new MappedMemory(0, 0, 0, 0)); + } + + mach_vm_address_t region_address = mach_vm_trunc_page(address); + mach_vm_size_t region_size = + mach_vm_round_page(address - region_address + size); + + vm_offset_t region; + mach_msg_type_number_t region_count; + kern_return_t kr = + mach_vm_read(task_, region_address, region_size, ®ion, ®ion_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << base::StringPrintf( + "mach_vm_read(0x%llx, 0x%llx)", region_address, region_size); + return scoped_ptr<MappedMemory>(); + } + + DCHECK_EQ(region_count, region_size); + return scoped_ptr<MappedMemory>( + new MappedMemory(region, region_size, address - region_address, size)); +} + +bool TaskMemory::ReadCString(mach_vm_address_t address, std::string* string) { + return ReadCStringInternal(address, false, 0, string); +} + +bool TaskMemory::ReadCStringSizeLimited(mach_vm_address_t address, + mach_vm_size_t size, + std::string* string) { + return ReadCStringInternal(address, true, size, string); +} + +bool TaskMemory::ReadCStringInternal(mach_vm_address_t address, + bool has_size, + mach_vm_size_t size, + std::string* string) { + if (has_size) { + if (size == 0) { + string->clear(); + return true; + } + } else { + size = PAGE_SIZE; + } + + std::string local_string; + mach_vm_address_t read_address = address; + do { + mach_vm_size_t read_length = + std::min(size, PAGE_SIZE - (read_address % PAGE_SIZE)); + scoped_ptr<MappedMemory> read_region = + ReadMapped(read_address, read_length); + if (!read_region) { + return false; + } + + const char* read_region_data = + reinterpret_cast<const char*>(read_region->data()); + size_t read_region_data_length = strnlen(read_region_data, read_length); + local_string.append(read_region_data, read_region_data_length); + if (read_region_data_length < read_length) { + string->swap(local_string); + return true; + } + + if (has_size) { + size -= read_length; + } + read_address = mach_vm_trunc_page(read_address + read_length); + } while ((!has_size || size > 0) && read_address > address); + + LOG(WARNING) << base::StringPrintf("unterminated string at 0x%llx", address); + return false; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/mach/task_memory.h b/third_party/crashpad/crashpad/util/mach/task_memory.h new file mode 100644 index 0000000..2077d5ea --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/task_memory.h
@@ -0,0 +1,177 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MACH_TASK_MEMORY_H_ +#define CRASHPAD_UTIL_MACH_TASK_MEMORY_H_ + +#include <mach/mach.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/mac/scoped_mach_vm.h" +#include "base/memory/scoped_ptr.h" + +namespace crashpad { + +//! \brief Accesses the memory of another Mach task. +class TaskMemory { + public: + //! \brief A memory region mapped from another Mach task. + //! + //! The mapping is maintained until this object is destroyed. + class MappedMemory { + public: + ~MappedMemory(); + + //! \brief Returns a pointer to the data requested by the user. + //! + //! This is the value of the \a vm_address + \a user_offset parameters + //! passed to the constructor, casted to `const void*`. + const void* data() const { return data_; } + + //! \brief Reads a `NUL`-terminated C string from the mapped region. + //! + //! This method will read contiguous memory until a `NUL` terminator is + //! found. + //! + //! \param[in] offset The offset into data() of the string to be read. + //! \param[out] string The string, whose contents begin at data() and + //! continue up to a `NUL` terminator. + //! + //! \return `true` on success, with \a string set appropriately. If \a + //! offset is greater than or equal to the \a user_size constructor + //! parameter, or if no `NUL` terminator was found in data() after \a + //! offset, returns `false` with an appropriate warning logged. + bool ReadCString(size_t offset, std::string* string) const; + + private: + //! \brief Creates an object that owns a memory region mapped from another + //! Mach task. + //! + //! \param[in] vm_address The address in this process’ address space where + //! the mapping begins. This must be page-aligned. + //! \param[in] vm_size The total size of the mapping that begins at \a + //! vm_address. This must be page-aligned. + //! \param[in] user_offset The offset into the mapped region where the data + //! requested by the user begins. This accounts for the fact that a + //! mapping must be page-aligned but the user data may not be. This + //! parameter must be equal to or less than \a vm_size. + //! \param[in] user_size The size of the data requested by the user. This + //! parameter can be used to compute the end address of user data, which + //! must be within the mapped region. + MappedMemory(vm_address_t vm_address, + size_t vm_size, + size_t user_offset, + size_t user_size); + + base::mac::ScopedMachVM vm_; + const void* data_; + size_t user_size_; + + // The outer class needs to be able to call this class’ private constructor. + friend class TaskMemory; + + DISALLOW_COPY_AND_ASSIGN(MappedMemory); + }; + + //! \param[in] task A send right to the target task’s task port. This object + //! does not take ownership of the send right. + explicit TaskMemory(task_t task); + + ~TaskMemory() {} + + //! \brief Copies memory from the target task into a caller-provided buffer in + //! the current task. + //! + //! \param[in] address The address, in the target task’s address space, of the + //! memory region to copy. + //! \param[in] size The size, in bytes, of the memory region to copy. \a + //! buffer must be at least this size. + //! \param[out] buffer The buffer into which the contents of the other task’s + //! memory will be copied. + //! + //! \return `true` on success, with \a buffer filled appropriately. `false` on + //! failure, with a warning logged. Failures can occur, for example, when + //! encountering unmapped or unreadable pages. + //! + //! \sa ReadMapped() + bool Read(mach_vm_address_t address, size_t size, void* buffer); + + //! \brief Maps memory from the target task into the current task. + //! + //! This interface is an alternative to Read() that does not require the + //! caller to provide a buffer to fill. This avoids copying memory, which can + //! offer a performance improvement. + //! + //! \param[in] address The address, in the target task’s address space, of the + //! memory region to map. + //! \param[in] size The size, in bytes, of the memory region to map. + //! + //! \return On success, a MappedMemory object that provides access to the data + //! requested. On faliure, `nullptr`, with a warning logged. Failures can + //! occur, for example, when encountering unmapped or unreadable pages. + scoped_ptr<MappedMemory> ReadMapped(mach_vm_address_t address, size_t size); + + //! \brief Reads a `NUL`-terminated C string from the target task into a + //! string in the current task. + //! + //! The length of the string need not be known ahead of time. This method will + //! read contiguous memory until a `NUL` terminator is found. + //! + //! \param[in] address The address, in the target task’s address space, of the + //! string to copy. + //! \param[out] string The string read from the other task. + //! + //! \return `true` on success, with \a string set appropriately. `false` on + //! failure, with a warning logged. Failures can occur, for example, when + //! encountering unmapped or unreadable pages. + //! + //! \sa MappedMemory::ReadCString() + bool ReadCString(mach_vm_address_t address, std::string* string); + + //! \brief Reads a `NUL`-terminated C string from the target task into a + //! string in the current task. + //! + //! \param[in] address The address, in the target task’s address space, of the + //! string to copy. + //! \param[in] size The maximum number of bytes to read. The string is + //! required to be `NUL`-terminated within this many bytes. + //! \param[out] string The string read from the other task. + //! + //! \return `true` on success, with \a string set appropriately. `false` on + //! failure, with a warning logged. Failures can occur, for example, when + //! a `NUL` terminator is not found within \a size bytes, or when + //! encountering unmapped or unreadable pages. + //! + //! \sa MappedMemory::ReadCString() + bool ReadCStringSizeLimited(mach_vm_address_t address, + mach_vm_size_t size, + std::string* string); + + private: + // The common internal implementation shared by the ReadCString*() methods. + bool ReadCStringInternal(mach_vm_address_t address, + bool has_size, + mach_vm_size_t size, + std::string* string); + + task_t task_; // weak + + DISALLOW_COPY_AND_ASSIGN(TaskMemory); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
diff --git a/third_party/crashpad/crashpad/util/mach/task_memory_test.cc b/third_party/crashpad/crashpad/util/mach/task_memory_test.cc new file mode 100644 index 0000000..2247ed2f --- /dev/null +++ b/third_party/crashpad/crashpad/util/mach/task_memory_test.cc
@@ -0,0 +1,555 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/mach/task_memory.h" + +#include <mach/mach.h> +#include <string.h> + +#include <algorithm> +#include <string> + +#include "base/mac/scoped_mach_port.h" +#include "base/mac/scoped_mach_vm.h" +#include "base/memory/scoped_ptr.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(TaskMemory, ReadSelf) { + vm_address_t address = 0; + const vm_size_t kSize = 4 * PAGE_SIZE; + kern_return_t kr = + vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); + base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); + + char* region = reinterpret_cast<char*>(address); + for (size_t index = 0; index < kSize; ++index) { + region[index] = (index % 256) ^ ((index >> 8) % 256); + } + + TaskMemory memory(mach_task_self()); + + // This tests using both the Read() and ReadMapped() interfaces. + std::string result(kSize, '\0'); + scoped_ptr<TaskMemory::MappedMemory> mapped; + + // Ensure that the entire region can be read. + ASSERT_TRUE(memory.Read(address, kSize, &result[0])); + EXPECT_EQ(0, memcmp(region, &result[0], kSize)); + ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize))); + EXPECT_EQ(0, memcmp(region, mapped->data(), kSize)); + + // Ensure that a read of length 0 succeeds and doesn’t touch the result. + result.assign(kSize, '\0'); + std::string zeroes = result; + ASSERT_TRUE(memory.Read(address, 0, &result[0])); + EXPECT_EQ(zeroes, result); + ASSERT_TRUE((mapped = memory.ReadMapped(address, 0))); + + // Ensure that a read starting at an unaligned address works. + ASSERT_TRUE(memory.Read(address + 1, kSize - 1, &result[0])); + EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 1)); + ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1))); + EXPECT_EQ(0, memcmp(region + 1, mapped->data(), kSize - 1)); + + // Ensure that a read ending at an unaligned address works. + ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0])); + EXPECT_EQ(0, memcmp(region, &result[0], kSize - 1)); + ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1))); + EXPECT_EQ(0, memcmp(region, mapped->data(), kSize - 1)); + + // Ensure that a read starting and ending at unaligned addresses works. + ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0])); + EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 2)); + ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2))); + EXPECT_EQ(0, memcmp(region + 1, mapped->data(), kSize - 2)); + + // Ensure that a read of exactly one page works. + ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0])); + EXPECT_EQ(0, memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE)); + ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE))); + EXPECT_EQ(0, memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE)); + + // Ensure that a read of a single byte works. + ASSERT_TRUE(memory.Read(address + 2, 1, &result[0])); + EXPECT_EQ(region[2], result[0]); + ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1))); + EXPECT_EQ(region[2], reinterpret_cast<const char*>(mapped->data())[0]); + + // Ensure that a read of length zero works and doesn’t touch the data. + result[0] = 'M'; + ASSERT_TRUE(memory.Read(address + 3, 0, &result[0])); + EXPECT_EQ('M', result[0]); + ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0))); +} + +TEST(TaskMemory, ReadSelfUnmapped) { + vm_address_t address = 0; + const vm_size_t kSize = 2 * PAGE_SIZE; + kern_return_t kr = + vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); + base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); + + char* region = reinterpret_cast<char*>(address); + for (size_t index = 0; index < kSize; ++index) { + // Don’t include any NUL bytes, because ReadCString stops when it encounters + // a NUL. + region[index] = (index % 255) + 1; + } + + kr = vm_protect( + mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect"); + + TaskMemory memory(mach_task_self()); + std::string result(kSize, '\0'); + + EXPECT_FALSE(memory.Read(address, kSize, &result[0])); + EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); + EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); + EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0])); + EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0])); + EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0])); + + // Do the same thing with the ReadMapped() interface. + scoped_ptr<TaskMemory::MappedMemory> mapped; + EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize))); + EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1))); + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1))); + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2))); + EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE))); + EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1))); + + // Repeat the test with an unmapped page instead of an unreadable one. This + // portion of the test may be flaky in the presence of other threads, if + // another thread maps something in the region that is deallocated here. + kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); + vm_owner.reset(address, PAGE_SIZE); + + EXPECT_FALSE(memory.Read(address, kSize, &result[0])); + EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); + EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); + EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0])); + EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0])); + EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0])); + + // Do the same thing with the ReadMapped() interface. + EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize))); + EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1))); + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1))); + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2))); + EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE))); + EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1))); +} + +// This function consolidates the cast from a char* to mach_vm_address_t in one +// location when reading from the current task. +bool ReadCStringSelf(TaskMemory* memory, + const char* pointer, + std::string* result) { + return memory->ReadCString(reinterpret_cast<mach_vm_address_t>(pointer), + result); +} + +TEST(TaskMemory, ReadCStringSelf) { + TaskMemory memory(mach_task_self()); + std::string result; + + const char kConstCharEmpty[] = ""; + ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharEmpty, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kConstCharEmpty, result); + + const char kConstCharShort[] = "A short const char[]"; + ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharShort, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kConstCharShort, result); + + static const char kStaticConstCharEmpty[] = ""; + ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharEmpty, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kStaticConstCharEmpty, result); + + static const char kStaticConstCharShort[] = "A short static const char[]"; + ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharShort, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kStaticConstCharShort, result); + + std::string string_short("A short std::string in a function"); + ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(string_short, result); + + std::string string_long; + const size_t kStringLongSize = 4 * PAGE_SIZE; + for (size_t index = 0; index < kStringLongSize; ++index) { + // Don’t include any NUL bytes, because ReadCString stops when it encounters + // a NUL. + string_long.append(1, (index % 255) + 1); + } + ASSERT_EQ(kStringLongSize, string_long.size()); + ASSERT_TRUE(ReadCStringSelf(&memory, &string_long[0], &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kStringLongSize, result.size()); + EXPECT_EQ(string_long, result); +} + +TEST(TaskMemory, ReadCStringSelfUnmapped) { + vm_address_t address = 0; + const vm_size_t kSize = 2 * PAGE_SIZE; + kern_return_t kr = + vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); + base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); + + char* region = reinterpret_cast<char*>(address); + for (size_t index = 0; index < kSize; ++index) { + // Don’t include any NUL bytes, because ReadCString stops when it encounters + // a NUL. + region[index] = (index % 255) + 1; + } + + kr = vm_protect( + mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect"); + + TaskMemory memory(mach_task_self()); + std::string result; + EXPECT_FALSE(memory.ReadCString(address, &result)); + + // Make sure that if the string is NUL-terminated within the mapped memory + // region, it can be read properly. + char terminator_or_not = '\0'; + std::swap(region[PAGE_SIZE - 1], terminator_or_not); + ASSERT_TRUE(memory.ReadCString(address, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(PAGE_SIZE - 1u, result.size()); + EXPECT_EQ(region, result); + + // Repeat the test with an unmapped page instead of an unreadable one. This + // portion of the test may be flaky in the presence of other threads, if + // another thread maps something in the region that is deallocated here. + std::swap(region[PAGE_SIZE - 1], terminator_or_not); + kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); + ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); + vm_owner.reset(address, PAGE_SIZE); + + EXPECT_FALSE(memory.ReadCString(address, &result)); + + // Clear the result before testing that the string can be read. This makes + // sure that the result is actually filled in, because it already contains the + // expected value from the tests above. + result.clear(); + std::swap(region[PAGE_SIZE - 1], terminator_or_not); + ASSERT_TRUE(memory.ReadCString(address, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(PAGE_SIZE - 1u, result.size()); + EXPECT_EQ(region, result); +} + +// This function consolidates the cast from a char* to mach_vm_address_t in one +// location when reading from the current task. +bool ReadCStringSizeLimitedSelf(TaskMemory* memory, + const char* pointer, + size_t size, + std::string* result) { + return memory->ReadCStringSizeLimited( + reinterpret_cast<mach_vm_address_t>(pointer), size, result); +} + +TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) { + TaskMemory memory(mach_task_self()); + std::string result; + + const char kConstCharEmpty[] = ""; + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kConstCharEmpty, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kConstCharEmpty, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kConstCharEmpty, result); +} + +TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) { + TaskMemory memory(mach_task_self()); + std::string result; + + const char kConstCharShort[] = "A short const char[]"; + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, kConstCharShort, arraysize(kConstCharShort), &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kConstCharShort, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kConstCharShort, result); + + ASSERT_FALSE(ReadCStringSizeLimitedSelf( + &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); +} + +TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) { + TaskMemory memory(mach_task_self()); + std::string result; + + static const char kStaticConstCharEmpty[] = ""; + ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, + kStaticConstCharEmpty, + arraysize(kStaticConstCharEmpty), + &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kStaticConstCharEmpty, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, + kStaticConstCharEmpty, + arraysize(kStaticConstCharEmpty) + 1, + &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kStaticConstCharEmpty, result); + + result.clear(); + ASSERT_TRUE( + ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(kStaticConstCharEmpty, result); +} + +TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) { + TaskMemory memory(mach_task_self()); + std::string result; + + static const char kStaticConstCharShort[] = "A short static const char[]"; + ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, + kStaticConstCharShort, + arraysize(kStaticConstCharShort), + &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kStaticConstCharShort, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, + kStaticConstCharShort, + arraysize(kStaticConstCharShort) + 1, + &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kStaticConstCharShort, result); + + ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory, + kStaticConstCharShort, + arraysize(kStaticConstCharShort) - 1, + &result)); +} + +TEST(TaskMemory, ReadCStringSizeLimited_StringShort) { + TaskMemory memory(mach_task_self()); + std::string result; + + std::string string_short("A short std::string in a function"); + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, &string_short[0], string_short.size() + 1, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(string_short, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, &string_short[0], string_short.size() + 2, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(string_short, result); + + ASSERT_FALSE(ReadCStringSizeLimitedSelf( + &memory, &string_short[0], string_short.size(), &result)); +} + +TEST(TaskMemory, ReadCStringSizeLimited_StringLong) { + TaskMemory memory(mach_task_self()); + std::string result; + + std::string string_long; + const size_t kStringLongSize = 4 * PAGE_SIZE; + for (size_t index = 0; index < kStringLongSize; ++index) { + // Don’t include any NUL bytes, because ReadCString stops when it encounters + // a NUL. + string_long.append(1, (index % 255) + 1); + } + ASSERT_EQ(kStringLongSize, string_long.size()); + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, &string_long[0], string_long.size() + 1, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kStringLongSize, result.size()); + EXPECT_EQ(string_long, result); + + result.clear(); + ASSERT_TRUE(ReadCStringSizeLimitedSelf( + &memory, &string_long[0], string_long.size() + 2, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(kStringLongSize, result.size()); + EXPECT_EQ(string_long, result); + + ASSERT_FALSE(ReadCStringSizeLimitedSelf( + &memory, &string_long[0], string_long.size(), &result)); +} + +bool IsAddressMapped(vm_address_t address) { + vm_address_t region_address = address; + vm_size_t region_size; + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + vm_region_basic_info_64 info; + mach_port_t object; + kern_return_t kr = vm_region_64(mach_task_self(), + ®ion_address, + ®ion_size, + VM_REGION_BASIC_INFO_64, + reinterpret_cast<vm_region_info_t>(&info), + &count, + &object); + if (kr == KERN_SUCCESS) { + // |object| will be MACH_PORT_NULL (10.9.4 xnu-2422.110.17/osfmk/vm/vm_map.c + // vm_map_region()), but the interface acts as if it might carry a send + // right, so treat it as documented. + base::mac::ScopedMachSendRight object_owner(object); + + return address >= region_address && address <= region_address + region_size; + } + + if (kr == KERN_INVALID_ADDRESS) { + return false; + } + + ADD_FAILURE() << MachErrorMessage(kr, "vm_region_64");; + return false; +} + +TEST(TaskMemory, MappedMemoryDeallocates) { + // This tests that once a TaskMemory::MappedMemory object is destroyed, it + // releases the mapped memory that it owned. Technically, this test is not + // valid because after the mapping is released, something else (on another + // thread) might wind up mapped in the same address. In the test environment, + // hopefully there are either no other threads or they’re all quiescent, so + // nothing else should wind up mapped in the address. + + TaskMemory memory(mach_task_self()); + scoped_ptr<TaskMemory::MappedMemory> mapped; + + static const char kTestBuffer[] = "hello!"; + mach_vm_address_t test_address = + reinterpret_cast<mach_vm_address_t>(&kTestBuffer); + ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer)))); + EXPECT_EQ(0, memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer))); + + vm_address_t mapped_address = reinterpret_cast<vm_address_t>(mapped->data()); + EXPECT_TRUE(IsAddressMapped(mapped_address)); + + mapped.reset(); + EXPECT_FALSE(IsAddressMapped(mapped_address)); + + // This is the same but with a big buffer that’s definitely larger than a + // single page. This makes sure that the whole mapped region winds up being + // deallocated. + const size_t kBigSize = 4 * PAGE_SIZE; + scoped_ptr<char[]> big_buffer(new char[kBigSize]); + test_address = reinterpret_cast<mach_vm_address_t>(&big_buffer[0]); + ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize))); + + mapped_address = reinterpret_cast<vm_address_t>(mapped->data()); + vm_address_t mapped_last_address = mapped_address + kBigSize - 1; + EXPECT_TRUE(IsAddressMapped(mapped_address)); + EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE)); + EXPECT_TRUE(IsAddressMapped(mapped_last_address)); + + mapped.reset(); + EXPECT_FALSE(IsAddressMapped(mapped_address)); + EXPECT_FALSE(IsAddressMapped(mapped_address + PAGE_SIZE)); + EXPECT_FALSE(IsAddressMapped(mapped_last_address)); +} + +TEST(TaskMemory, MappedMemoryReadCString) { + // This tests the behavior of TaskMemory::MappedMemory::ReadCString(). + TaskMemory memory(mach_task_self()); + scoped_ptr<TaskMemory::MappedMemory> mapped; + + static const char kTestBuffer[] = "0\0" "2\0" "45\0" "789"; + const mach_vm_address_t kTestAddress = + reinterpret_cast<mach_vm_address_t>(&kTestBuffer); + ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10))); + + std::string string; + ASSERT_TRUE(mapped->ReadCString(0, &string)); + EXPECT_EQ("0", string); + ASSERT_TRUE(mapped->ReadCString(1, &string)); + EXPECT_EQ("", string); + ASSERT_TRUE(mapped->ReadCString(2, &string)); + EXPECT_EQ("2", string); + ASSERT_TRUE(mapped->ReadCString(3, &string)); + EXPECT_EQ("", string); + ASSERT_TRUE(mapped->ReadCString(4, &string)); + EXPECT_EQ("45", string); + ASSERT_TRUE(mapped->ReadCString(5, &string)); + EXPECT_EQ("5", string); + ASSERT_TRUE(mapped->ReadCString(6, &string)); + EXPECT_EQ("", string); + + // kTestBuffer’s NUL terminator was not read, so these will see an + // unterminated string and fail. + EXPECT_FALSE(mapped->ReadCString(7, &string)); + EXPECT_FALSE(mapped->ReadCString(8, &string)); + EXPECT_FALSE(mapped->ReadCString(9, &string)); + + // This is out of the range of what was read, so it will fail. + EXPECT_FALSE(mapped->ReadCString(10, &string)); + EXPECT_FALSE(mapped->ReadCString(11, &string)); + + // Read it again, this time with a length long enough to include the NUL + // terminator. + ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 11))); + + ASSERT_TRUE(mapped->ReadCString(6, &string)); + EXPECT_EQ("", string); + + // These should now succeed. + ASSERT_TRUE(mapped->ReadCString(7, &string)); + EXPECT_EQ("789", string); + ASSERT_TRUE(mapped->ReadCString(8, &string)); + EXPECT_EQ("89", string); + ASSERT_TRUE(mapped->ReadCString(9, &string)); + EXPECT_EQ("9", string); + EXPECT_TRUE(mapped->ReadCString(10, &string)); + EXPECT_EQ("", string); + + // These are still out of range. + EXPECT_FALSE(mapped->ReadCString(11, &string)); + EXPECT_FALSE(mapped->ReadCString(12, &string)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/clock.h b/third_party/crashpad/crashpad/util/misc/clock.h new file mode 100644 index 0000000..a07e12a6 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/clock.h
@@ -0,0 +1,52 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_CLOCK_H_ +#define CRASHPAD_UTIL_MISC_CLOCK_H_ + +#include <stdint.h> + +#include "build/build_config.h" + +namespace crashpad { + +//! \brief Returns the value of the system’s monotonic clock. +//! +//! The monotonic clock is a tick counter whose epoch is unspecified. It is a +//! monotonically-increasing clock that cannot be set, and never jumps backwards +//! on a running system. The monotonic clock may stop while the system is +//! sleeping, and it may be reset when the system starts up. This clock is +//! suitable for computing durations of events. Subject to the underlying +//! clock’s resolution, successive calls to this function will result in a +//! series of increasing values. +//! +//! \return The value of the system’s monotonic clock, in nanoseconds. +uint64_t ClockMonotonicNanoseconds(); + +#if !defined(OS_WIN) // Not implemented on Windows yet. + +//! \brief Sleeps for the specified duration. +//! +//! \param[in] nanoseconds The number of nanoseconds to sleep. The actual sleep +//! may be slightly longer due to latencies and timer resolution. +//! +//! This function is resilient against the underlying `nanosleep()` system call +//! being interrupted by a signal. +void SleepNanoseconds(uint64_t nanoseconds); + +#endif + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_CLOCK_H_
diff --git a/third_party/crashpad/crashpad/util/misc/clock_mac.cc b/third_party/crashpad/crashpad/util/misc/clock_mac.cc new file mode 100644 index 0000000..bb52299e --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/clock_mac.cc
@@ -0,0 +1,45 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/clock.h" + +#include <mach/mach_time.h> + +#include "base/mac/mach_logging.h" + +namespace { + +mach_timebase_info_data_t* TimebaseInternal() { + mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t; + kern_return_t kr = mach_timebase_info(timebase_info); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info"; + return timebase_info; +} + +mach_timebase_info_data_t* Timebase() { + static mach_timebase_info_data_t* timebase_info = TimebaseInternal(); + return timebase_info; +} + +} // namespace + +namespace crashpad { + +uint64_t ClockMonotonicNanoseconds() { + uint64_t absolute_time = mach_absolute_time(); + mach_timebase_info_data_t* timebase_info = Timebase(); + return absolute_time * timebase_info->numer / timebase_info->denom; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/clock_posix.cc b/third_party/crashpad/crashpad/util/misc/clock_posix.cc new file mode 100644 index 0000000..2417109 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/clock_posix.cc
@@ -0,0 +1,51 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/clock.h" + +#include <time.h> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" + +namespace { + +const uint64_t kNanosecondsPerSecond = 1E9; + +} // namespace + +namespace crashpad { + +#if !defined(OS_MACOSX) + +uint64_t ClockMonotonicNanoseconds() { + timespec now; + int rv = clock_gettime(CLOCK_MONOTONIC, &now); + DPCHECK(rv == 0) << "clock_gettime"; + + return now.tv_sec * kNanosecondsPerSecond + now.tv_nsec; +} + +#endif + +void SleepNanoseconds(uint64_t nanoseconds) { + timespec sleep_time; + sleep_time.tv_sec = nanoseconds / kNanosecondsPerSecond; + sleep_time.tv_nsec = nanoseconds % kNanosecondsPerSecond; + int rv = HANDLE_EINTR(nanosleep(&sleep_time, &sleep_time)); + DPCHECK(rv == 0) << "nanosleep"; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/clock_test.cc b/third_party/crashpad/crashpad/util/misc/clock_test.cc new file mode 100644 index 0000000..82ca85fa --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/clock_test.cc
@@ -0,0 +1,98 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/clock.h" + +#include <stdint.h> + +#include <algorithm> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Clock, ClockMonotonicNanoseconds) { + uint64_t start = ClockMonotonicNanoseconds(); + EXPECT_GT(start, 0u); + + uint64_t now = start; + for (size_t iteration = 0; iteration < 10; ++iteration) { + uint64_t last = now; + now = ClockMonotonicNanoseconds(); + + // Use EXPECT_GE instead of EXPECT_GT, because there are no guarantees about + // the clock’s resolution. + EXPECT_GE(now, last); + } + +#if !defined(OS_WIN) // No SleepNanoseconds implemented on Windows. + // SleepNanoseconds() should sleep for at least the value of the clock’s + // resolution, so the clock’s value should definitely increase after a sleep. + // EXPECT_GT can be used instead of EXPECT_GE after the sleep. + SleepNanoseconds(1); + now = ClockMonotonicNanoseconds(); + EXPECT_GT(now, start); +#endif // OS_WIN +} + +#if !defined(OS_WIN) // No SleepNanoseconds implemented on Windows. + +void TestSleepNanoseconds(uint64_t nanoseconds) { + uint64_t start = ClockMonotonicNanoseconds(); + + SleepNanoseconds(nanoseconds); + + uint64_t end = ClockMonotonicNanoseconds(); + uint64_t diff = end - start; + + // |nanoseconds| is the lower bound for the actual amount of time spent + // sleeping. + EXPECT_GE(diff, nanoseconds); + + // It’s difficult to set an upper bound for the time spent sleeping, and + // attempting to do so results in a flaky test. +} + +TEST(Clock, SleepNanoseconds) { + const uint64_t kTestData[] = { + 0, + 1, + static_cast<uint64_t>(1E3), // 1 microsecond + static_cast<uint64_t>(1E4), // 10 microseconds + static_cast<uint64_t>(1E5), // 100 microseconds + static_cast<uint64_t>(1E6), // 1 millisecond + static_cast<uint64_t>(1E7), // 10 milliseconds + static_cast<uint64_t>(2E7), // 20 milliseconds + static_cast<uint64_t>(5E7), // 50 milliseconds + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const uint64_t nanoseconds = kTestData[index]; + SCOPED_TRACE( + base::StringPrintf("index %zu, nanoseconds %llu", index, nanoseconds)); + + TestSleepNanoseconds(nanoseconds); + } +} + +#endif // OS_WIN + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/clock_win.cc b/third_party/crashpad/crashpad/util/misc/clock_win.cc new file mode 100644 index 0000000..7231205 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/clock_win.cc
@@ -0,0 +1,48 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/clock.h" + +#include <sys/types.h> +#include <windows.h> + +namespace { + +LARGE_INTEGER QpcFrequencyInternal() { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + return frequency; +} + +int64_t QpcFrequency() { + static LARGE_INTEGER frequency = QpcFrequencyInternal(); + return frequency.QuadPart; +} + +} // namespace + +namespace crashpad { + +uint64_t ClockMonotonicNanoseconds() { + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + int64_t frequency = QpcFrequency(); + int64_t whole_seconds = time.QuadPart / frequency; + int64_t leftover_ticks = time.QuadPart % frequency; + const int64_t kNanosecondsPerSecond = static_cast<const int64_t>(1E9); + return (whole_seconds * kNanosecondsPerSecond) + + ((leftover_ticks * kNanosecondsPerSecond) / frequency); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/implicit_cast.h b/third_party/crashpad/crashpad/util/misc/implicit_cast.h new file mode 100644 index 0000000..6f8a1a3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/implicit_cast.h
@@ -0,0 +1,44 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_IMPLICIT_CAST_H_ +#define CRASHPAD_UTIL_MISC_IMPLICIT_CAST_H_ + +namespace crashpad { + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertible to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast<ToType>(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template<typename To, typename From> +inline To implicit_cast(From const &f) { + return f; +} + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_IMPLICIT_CAST_H_
diff --git a/third_party/crashpad/crashpad/util/misc/initialization_state.h b/third_party/crashpad/crashpad/util/misc/initialization_state.h new file mode 100644 index 0000000..b0496e5 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/initialization_state.h
@@ -0,0 +1,100 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_ +#define CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_ + +#include <stdint.h> + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Tracks whether data are initialized. +//! +//! Objects of this type track whether the data they’re guarding are +//! initialized. The three possible states are uninitialized (the initial +//! state), initializing, and valid. As the guarded data are initialized, an +//! InitializationState object will normally transition through these three +//! states. A fourth state corresponds to the destruction of objects of this +//! type, making it less likely that a use-after-free of an InitializationState +//! object will appear in the valid state. +//! +//! If the only purpose for tracking the initialization state of guarded data is +//! to DCHECK when the object is in an unexpected state, use +//! InitializationStateDcheck instead. +class InitializationState { + public: + //! \brief The object’s state. + enum State : uint8_t { + //! \brief The object has not yet been initialized. + kStateUninitialized = 0, + + //! \brief The object is being initialized. + //! + //! This state protects against attempted reinitializaton of + //! partially-initialized objects whose initial initialization attempt + //! failed. This state is to be used while objects are initializing, but are + //! not yet fully initialized. + kStateInvalid, + + //! \brief The object has been initialized. + kStateValid, + + //! \brief The object has been destroyed. + kStateDestroyed, + }; + + InitializationState() : state_(kStateUninitialized) {} + ~InitializationState() { state_ = kStateDestroyed; } + + //! \brief Returns `true` if the object’s state is #kStateUninitialized and it + //! is safe to begin initializing it. + bool is_uninitialized() const { return state_ == kStateUninitialized; } + + //! \brief Sets the object’s state to #kStateInvalid, marking initialization + //! as being in process. + void set_invalid() { state_ = kStateInvalid; } + + //! \brief Sets the object’s state to #kStateValid, marking it initialized. + void set_valid() { state_ = kStateValid; } + + //! \brief Returns `true` if the the object’s state is #kStateValid and it has + //! been fully initialized and may be used. + bool is_valid() const { return state_ == kStateValid; } + + protected: + //! \brief Returns the object’s state. + //! + //! Consumers of this class should use an is_state_*() method instead. + State state() const { return state_; } + + //! \brief Sets the object’s state. + //! + //! Consumers of this class should use a set_state_*() method instead. + void set_state(State state) { state_ = state; } + + private: + // state_ is volatile to ensure that it’ll be set by the destructor when it + // runs. Otherwise, optimizations might prevent it from ever being set to + // kStateDestroyed, limiting this class’ ability to catch use-after-free + // errors. + volatile State state_; + + DISALLOW_COPY_AND_ASSIGN(InitializationState); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_
diff --git a/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc b/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc new file mode 100644 index 0000000..82bb5b6 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc
@@ -0,0 +1,41 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +#if DCHECK_IS_ON() + +InitializationStateDcheck::State InitializationStateDcheck::SetInitializing() { + State old_state = state(); + if (old_state == kStateUninitialized) { + set_invalid(); + } + + return old_state; +} + +InitializationStateDcheck::State InitializationStateDcheck::SetValid() { + State old_state = state(); + if (old_state == kStateInvalid) { + set_valid(); + } + + return old_state; +} + +#endif + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h b/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h new file mode 100644 index 0000000..24b3d077d --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h
@@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_ +#define CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_ + +//! \file + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "util/misc/initialization_state.h" + +namespace crashpad { + +#if DCHECK_IS_ON() || DOXYGEN + +//! \brief Tracks whether data are initialized, triggering a DCHECK assertion +//! on an invalid data access. +//! +//! Put an InitializationStateDcheck member into a class to help DCHECK that +//! it’s in the right states at the right times. This is useful for classes with +//! Initialize() methods. The chief advantage of InitializationStateDcheck over +//! having a member variable to track state is that when the only use of the +//! variable is to DCHECK, it wastes space (in memory and executable code) in +//! non-DCHECK builds unless the code is also peppered with ugly #ifdefs. +//! +//! This implementation concentrates the ugly #ifdefs in one location. +//! +//! Usage: +//! +//! \code +//! class Class { +//! public: +//! Class() : initialized_() {} +//! +//! void Initialize() { +//! INITIALIZATION_STATE_SET_INITIALIZING(initialized_); +//! // Perform initialization. +//! INITIALIZATION_STATE_SET_VALID(initialized_); +//! } +//! +//! void DoSomething() { +//! INITIALIZATION_STATE_DCHECK_VALID(initialized_); +//! // Do something. +//! } +//! +//! private: +//! InitializationStateDcheck initialized_; +//! }; +//! \endcode +class InitializationStateDcheck : public InitializationState { + public: + InitializationStateDcheck() : InitializationState() {} + + //! \brief Returns the object’s state. + //! + //! Consumers of this class should not call this method. Use the + //! INITIALIZATION_STATE_SET_INITIALIZING(), INITIALIZATION_STATE_SET_VALID(), + //! and INITIALIZATION_STATE_DCHECK_VALID() macros instead. + // + // The superclass’ state() accessor is protected, but it needs to be exposed + // to consumers of this class for the macros below to work properly. The + // macros prefer access to the unerlying state value over a simple boolean + // because with access to the state value, DCHECK_EQ can be used, which, when + // tripped, prints both the expected and observed values. This can aid + // troubleshooting. + State state() const { return InitializationState::state(); } + + //! \brief Marks an uninitialized object as initializing. + //! + //! If the object is in the #kStateUninitialized state, changes its state to + //! #kStateInvalid (initializing) and returns the previous + //! (#kStateUninitialized) state. Otherwise, returns the object’s current + //! state. + //! + //! Consumers of this class should not call this method. Use the + //! INITIALIZATION_STATE_SET_INITIALIZING() macro instead. + State SetInitializing(); + + //! \brief Marks an initializing object as valid. + //! + //! If the object is in the #kStateInvalid (initializing) state, changes its + //! state to #kStateValid and returns the previous (#kStateInvalid) state. + //! Otherwise, returns the object’s current state. + //! + //! Consumers of this class should not call this method. Use the + //! INITIALIZATION_STATE_SET_VALID() macro instead. + State SetValid(); + + private: + DISALLOW_COPY_AND_ASSIGN(InitializationStateDcheck); +}; + +// Using macros enables the non-DCHECK no-op implementation below to be more +// compact and less intrusive. These are macros instead of methods that call +// DCHECK to enable the DCHECK failure message to point to the correct file and +// line number, and to allow additional messages to be streamed on failure with +// the << operator. + +//! \brief Checks that a crashpad::InitializationStateDcheck object is in the +//! crashpad::InitializationState::kStateUninitialized state, and changes +//! its state to initializing +//! (crashpad::InitializationState::kStateInvalid). +//! +//! If the object is not in the correct state, a DCHECK assertion is triggered +//! and the object’s state remains unchanged. +//! +//! \param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck +//! object. +//! +//! \sa crashpad::InitializationStateDcheck +#define INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck) \ + DCHECK_EQ((initialization_state_dcheck).SetInitializing(), \ + (initialization_state_dcheck).kStateUninitialized) + +//! \brief Checks that a crashpad::InitializationStateDcheck object is in the +//! initializing (crashpad::InitializationState::kStateInvalid) state, and +//! changes its state to crashpad::InitializationState::kStateValid. +//! +//! If the object is not in the correct state, a DCHECK assertion is triggered +//! and the object’s state remains unchanged. +//! +//! \param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck +//! object. +//! +//! \sa crashpad::InitializationStateDcheck +#define INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck) \ + DCHECK_EQ((initialization_state_dcheck).SetValid(), \ + (initialization_state_dcheck).kStateInvalid) + +//! \brief Checks that a crashpad::InitializationStateDcheck object is in the +//! crashpad::InitializationState::kStateValid state. +//! +//! If the object is not in the correct state, a DCHECK assertion is triggered. +//! +//! \param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck +//! object. +//! +//! \sa crashpad::InitializationStateDcheck +#define INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck) \ + DCHECK_EQ((initialization_state_dcheck).state(), \ + (initialization_state_dcheck).kStateValid) + +#else + +#if defined(COMPILER_MSVC) +// bool[0] (below) is not accepted by MSVC. +struct InitializationStateDcheck { +}; +#else +// Since this is to be used as a DCHECK (for debugging), it should be +// non-intrusive in non-DCHECK (non-debug, release) builds. An empty struct +// would still have a nonzero size (rationale: +// http://www.stroustrup.com/bs_faq2.html#sizeof-empty). Zero-length arrays are +// technically invalid according to the standard, but clang and g++ accept them +// without complaint even with warnings turned up. They take up no space at all, +// and they can be “initialized” with the same () syntax used to initialize +// objects of the DCHECK_IS_ON() InitializationStateDcheck class above. +using InitializationStateDcheck = bool[0]; +#endif // COMPILER_MSVC + +// Avoid triggering warnings by repurposing these macros when DCHECKs are +// disabled. +#define INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck) \ + ALLOW_UNUSED_LOCAL(initialization_state_dcheck) +#define INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck) \ + ALLOW_UNUSED_LOCAL(initialization_state_dcheck) +#define INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck) \ + ALLOW_UNUSED_LOCAL(initialization_state_dcheck) + +#endif + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_
diff --git a/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc b/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc new file mode 100644 index 0000000..b293ab7 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc
@@ -0,0 +1,135 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/initialization_state_dcheck.h" + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(InitializationStateDcheck, InitializationStateDcheck) { + InitializationStateDcheck initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck); + INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck); +} + +#if DCHECK_IS_ON() + +// InitializationStateDcheck only DCHECKs, so the death tests can only run +// when DCHECKs are enabled. + +TEST(InitializationStateDcheckDeathTest, Uninitialized_NotInvalid) { + // This tests that an attempt to set an uninitialized object as valid without + // transitioning through the initializing (invalid) state fails. + InitializationStateDcheck initialization_state_dcheck; + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck), + "kStateInvalid"); +} + +TEST(InitializationStateDcheckDeathTest, Uninitialized_NotValid) { + // This tests that an attempt to use an uninitialized object as though it + // were valid fails. + InitializationStateDcheck initialization_state_dcheck; + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck), + "kStateValid"); +} + +TEST(InitializationStateDcheckDeathTest, Invalid_NotUninitialized) { + // This tests that an attempt to begin initializing an object on which + // initialization was already attempted fails. + InitializationStateDcheck initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck), + "kStateUninitialized"); +} + +TEST(InitializationStateDcheckDeathTest, Invalid_NotValid) { + // This tests that an attempt to use an initializing object as though it + // were valid fails. + InitializationStateDcheck initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck), + "kStateValid"); +} + +TEST(InitializationStateDcheckDeathTest, Valid_NotUninitialized) { + // This tests that an attempt to begin initializing an object that has already + // been initialized fails. + InitializationStateDcheck initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck); + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck), + "kStateUninitialized"); +} + +TEST(InitializationStateDcheckDeathTest, Valid_NotInvalid) { + // This tests that an attempt to set a valid object as valid a second time + // fails. + InitializationStateDcheck initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck); + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck), + "kStateInvalid"); +} + +TEST(InitializationStateDcheckDeathTest, Destroyed_NotUninitialized) { + // This tests that an attempt to reinitialize a destroyed object fails. See + // the InitializationState.InitializationState test for an explanation of this + // use-after-free test. + InitializationStateDcheck* initialization_state_dcheck_pointer; + { + InitializationStateDcheck initialization_state_dcheck; + initialization_state_dcheck_pointer = &initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck); + INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck); + } + ASSERT_DEATH_CHECK(INITIALIZATION_STATE_SET_INITIALIZING( + *initialization_state_dcheck_pointer), + "kStateUninitialized"); +} + +TEST(InitializationStateDcheckDeathTest, Destroyed_NotValid) { + // This tests that an attempt to use a destroyed object fails. See the + // InitializationState.InitializationState test for an explanation of this + // use-after-free test. + InitializationStateDcheck* initialization_state_dcheck_pointer; + { + InitializationStateDcheck initialization_state_dcheck; + initialization_state_dcheck_pointer = &initialization_state_dcheck; + INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck); + INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck); + INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck); + } + ASSERT_DEATH_CHECK( + INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck_pointer), + "kStateValid"); +} + +#endif + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc b/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc new file mode 100644 index 0000000..d427a14 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc
@@ -0,0 +1,58 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/initialization_state.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(InitializationState, InitializationState) { + InitializationState* initialization_state_pointer; + { + InitializationState initialization_state; + initialization_state_pointer = &initialization_state; + + EXPECT_TRUE(initialization_state.is_uninitialized()); + EXPECT_FALSE(initialization_state.is_valid()); + + initialization_state.set_invalid(); + + EXPECT_FALSE(initialization_state.is_uninitialized()); + EXPECT_FALSE(initialization_state.is_valid()); + + initialization_state.set_valid(); + + EXPECT_FALSE(initialization_state.is_uninitialized()); + EXPECT_TRUE(initialization_state.is_valid()); + } + + // initialization_state_pointer points to something that no longer exists. + // This portion of the test is intended to check that after an + // InitializationState object goes out of scope, it will not be considered + // valid on a use-after-free, assuming that nothing else was written to its + // former home in memory. + // + // This portion of the test is technically not valid C++, but it exists to + // test that the behavior is as desired when other code uses the language + // improperly. + EXPECT_FALSE(initialization_state_pointer->is_uninitialized()); + EXPECT_FALSE(initialization_state_pointer->is_valid()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/pdb_structures.cc b/third_party/crashpad/crashpad/util/misc/pdb_structures.cc new file mode 100644 index 0000000..c62f11c --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/pdb_structures.cc
@@ -0,0 +1,22 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/pdb_structures.h" + +namespace crashpad { + +const uint32_t CodeViewRecordPDB20::kSignature; +const uint32_t CodeViewRecordPDB70::kSignature; + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/pdb_structures.h b/third_party/crashpad/crashpad/util/misc/pdb_structures.h new file mode 100644 index 0000000..04d76eec --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/pdb_structures.h
@@ -0,0 +1,131 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_ +#define CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_ + +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief A CodeView record linking to a `.pdb` 2.0 file. +//! +//! This format provides an indirect link to debugging data by referencing an +//! external `.pdb` file by its name, timestamp, and age. This structure may be +//! pointed to by MINIDUMP_MODULE::CvRecord. It has been superseded by +//! CodeViewRecordPDB70. +//! +//! For more information about this structure and format, see <a +//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching +//! Debug Information</a>, PDB Files, and <a +//! href="http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf#page=63">Undocumented +//! Windows 2000 Secrets</a>, Windows 2000 Debugging Support/Microsoft Symbol +//! File Internals/CodeView Subsections. +//! +//! \sa IMAGE_DEBUG_MISC +struct CodeViewRecordPDB20 { + //! \brief The magic number identifying this structure version, stored in + //! #signature. + //! + //! In a hex dump, this will appear as “NB10” when produced by a little-endian + //! machine. + static const uint32_t kSignature = '01BN'; + + //! \brief The magic number identifying this structure version, the value of + //! #kSignature. + uint32_t signature; + + //! \brief The offset to CodeView data. + //! + //! In this structure, this field always has the value `0` because no CodeView + //! data is present, there is only a link to CodeView data stored in an + //! external file. + uint32_t offset; + + //! \brief The time that the `.pdb` file was created, in `time_t` format, the + //! number of seconds since the POSIX epoch. + uint32_t timestamp; + + //! \brief The revision of the `.pdb` file. + //! + //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb` + //! file is created, it has age `1`, and subsequent updates increase this + //! value. + uint32_t age; + + //! \brief The path or file name of the `.pdb` file associated with the + //! module. + //! + //! This is a NUL-terminated string. On Windows, it will be encoded in the + //! code page of the system that linked the module. On other operating + //! systems, UTF-8 may be used. + uint8_t pdb_name[1]; +}; + +//! \brief A CodeView record linking to a `.pdb` 7.0 file. +//! +//! This format provides an indirect link to debugging data by referencing an +//! external `.pdb` file by its name, %UUID, and age. This structure may be +//! pointed to by MINIDUMP_MODULE::CvRecord. +//! +//! For more information about this structure and format, see <a +//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching +//! Debug Information</a>, PDB Files. +//! +//! \sa CodeViewRecordPDB20 +//! \sa IMAGE_DEBUG_MISC +struct CodeViewRecordPDB70 { + // UUID has a constructor, which makes it non-POD, which makes this structure + // non-POD. In order for the default constructor to zero-initialize other + // members, an explicit constructor must be provided. + CodeViewRecordPDB70() + : signature(), + uuid(), + age(), + pdb_name() { + } + + //! \brief The magic number identifying this structure version, stored in + //! #signature. + //! + //! In a hex dump, this will appear as “RSDS” when produced by a little-endian + //! machine. + static const uint32_t kSignature = 'SDSR'; + + //! \brief The magic number identifying this structure version, the value of + //! #kSignature. + uint32_t signature; + + //! \brief The `.pdb` file’s unique identifier. + UUID uuid; + + //! \brief The revision of the `.pdb` file. + //! + //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb` + //! file is created, it has age `1`, and subsequent updates increase this + //! value. + uint32_t age; + + //! \brief The path or file name of the `.pdb` file associated with the + //! module. + //! + //! This is a NUL-terminated string. On Windows, it will be encoded in the + //! code page of the system that linked the module. On other operating + //! systems, UTF-8 may be used. + uint8_t pdb_name[1]; +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_
diff --git a/third_party/crashpad/crashpad/util/misc/random_string.cc b/third_party/crashpad/crashpad/util/misc/random_string.cc new file mode 100644 index 0000000..a522799 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/random_string.cc
@@ -0,0 +1,29 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/random_string.h" + +#include "base/rand_util.h" + +namespace crashpad { + +std::string RandomString() { + std::string random_string; + for (int index = 0; index < 16; ++index) { + random_string.append(1, static_cast<char>(base::RandInt('A', 'Z'))); + } + return random_string; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/random_string.h b/third_party/crashpad/crashpad/util/misc/random_string.h new file mode 100644 index 0000000..93b3a7fa --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/random_string.h
@@ -0,0 +1,31 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_RANDOM_STRING_H_ +#define CRASHPAD_UTIL_MISC_RANDOM_STRING_H_ + +#include <string> + +namespace crashpad { + +//! \brief Returns a random string. +//! +//! The string consists of 16 uppercase characters chosen at random. The +//! returned string has over 75 bits of randomness (26<sup>16</sup> > +//! 2<sup>75</sup>). +std::string RandomString(); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_RANDOM_STRING_H_
diff --git a/third_party/crashpad/crashpad/util/misc/random_string_test.cc b/third_party/crashpad/crashpad/util/misc/random_string_test.cc new file mode 100644 index 0000000..3ef85df --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/random_string_test.cc
@@ -0,0 +1,71 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/random_string.h" + +#include <set> + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(RandomString, RandomString) { + // Explicitly list the allowed characters, rather than relying on a range. + // This prevents the test from having any dependency on the character set, so + // that the implementation is free to assume all uppercase letters are + // contiguous as in ASCII. + const std::string allowed_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + size_t character_counts[26] = {}; + ASSERT_EQ(arraysize(character_counts), allowed_characters.size()); + + std::set<std::string> strings; + + for (size_t i = 0; i < 256; ++i) { + const std::string random_string = RandomString(); + EXPECT_EQ(16u, random_string.size()); + + // Make sure that the string is unique. It is possible, but extremely + // unlikely, for there to be collisions. + auto result = strings.insert(random_string); + EXPECT_TRUE(result.second) << random_string; + + for (char c : random_string) { + size_t character_index = allowed_characters.find(c); + + // Make sure that no unexpected characters appear. + EXPECT_NE(character_index, std::string::npos) << c; + + if (character_index != std::string::npos) { + ++character_counts[character_index]; + } + } + } + + // Make sure every character appears at least once. It is possible, but + // extremely unlikely, for a character to not appear at all. + for (size_t character_index = 0; + character_index < arraysize(character_counts); + ++character_index) { + EXPECT_GT(character_counts[character_index], 0u) + << allowed_characters[character_index]; + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc b/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc new file mode 100644 index 0000000..f8de1da --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc
@@ -0,0 +1,27 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/scoped_forbid_return.h" + +#include "base/logging.h" + +namespace crashpad { + +ScopedForbidReturn::~ScopedForbidReturn() { + if (armed_) { + LOG(FATAL) << "attempt to exit scope forbidden"; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h b/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h new file mode 100644 index 0000000..8705be7a --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h
@@ -0,0 +1,54 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_ +#define CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_ + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Asserts that a scope must not be exited while unsafe. +//! +//! An object of this class has two states: armed and disarmed. A disarmed +//! object is a harmless no-op. An armed object will abort execution upon +//! destruction. Newly-constructed objects are armed by default. +//! +//! These objects may be used to assert that a scope not be exited while it is +//! unsafe to do so. If it ever becomes safe to leave such a scope, an object +//! can be disarmed. +class ScopedForbidReturn { + public: + ScopedForbidReturn() : armed_(true) {} + ~ScopedForbidReturn(); + + //! \brief Arms the object so that it will abort execution when destroyed. + //! + //! The most recent call to Arm() or Disarm() sets the state of the object. + void Arm() { armed_ = true; } + + //! \brief Arms the object so that it will abort execution when destroyed. + //! + //! The most recent call to Arm() or Disarm() sets the state of the object. + void Disarm() { armed_ = false; } + + private: + bool armed_; + + DISALLOW_COPY_AND_ASSIGN(ScopedForbidReturn); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_
diff --git a/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc b/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc new file mode 100644 index 0000000..78787d49 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc
@@ -0,0 +1,68 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/scoped_forbid_return.h" + +#include "base/compiler_specific.h" +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" + +namespace crashpad { +namespace test { +namespace { + +enum ForbidReturnType { + kForbidReturnDefault = 0, + kForbidReturnArmed, + kForbidReturnDisarmed, +}; + +void ScopedForbidReturnHelper(ForbidReturnType type) { + ScopedForbidReturn forbid_return; + + switch (type) { + case kForbidReturnDefault: + break; + case kForbidReturnArmed: + forbid_return.Arm(); + break; + case kForbidReturnDisarmed: + forbid_return.Disarm(); + break; + } +} + +const char kForbiddenMessage[] = "attempt to exit scope forbidden"; + +TEST(ScopedForbidReturnDeathTest, Default) { + // kForbiddenMessage may appear to be unused if ASSERT_DEATH_CHECK() throws it + // away. + ALLOW_UNUSED_LOCAL(kForbiddenMessage); + + ASSERT_DEATH_CHECK(ScopedForbidReturnHelper(kForbidReturnDefault), + kForbiddenMessage); +} + +TEST(ScopedForbidReturnDeathTest, Armed) { + ASSERT_DEATH_CHECK(ScopedForbidReturnHelper(kForbidReturnArmed), + kForbiddenMessage); +} + +TEST(ScopedForbidReturn, Disarmed) { + ScopedForbidReturnHelper(kForbidReturnDisarmed); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h b/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h new file mode 100644 index 0000000..ea2495a --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h
@@ -0,0 +1,132 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_ +#define CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_ + +//! \file +//! +//! \anchor symbolic_constant_terminology +//! Symbolic constant terminology +//! ============================= +//! <dl> +//! <dt>Family</dt> +//! <dd>A group of related symbolic constants. Typically, within a single +//! family, one function will be used to transform a numeric value to a +//! string equivalent, and another will perform the inverse operation. +//! Families include POSIX signals and Mach exception masks.</dd> +//! <dt>Full name</dt> +//! <dd>The normal symbolic name used for a constant. For example, in the +//! family of POSIX signals, the strings `"SIGHUP"` and `"SIGSEGV"` are +//! full names.</dd> +//! <dt>Short name</dt> +//! <dd>An abbreviated form of symbolic name used for a constant. Short names +//! vary between families, but are commonly constructed by removing a +//! common prefix from full names. For example, in the family of POSIX +//! signals, the prefix is `SIG`, and short names include `"HUP"` and +//! `"SEGV"`.</dd> +//! <dt>Numeric string</dt> +//! <dd>A string that does not contain a full or short name, but contains a +//! numeric value that can be interpreted as a symbolic constant. For +//! example, in the family of POSIX signals, `SIGKILL` generally has value +//! `9`, so the numeric string `"9"` would be interpreted equivalently to +//! `"SIGKILL"`.</dd> +//! </dl> + +namespace crashpad { + +//! \brief Options for various `*ToString` functions in `symbolic_constants_*` +//! files. +//! +//! \sa \ref symbolic_constant_terminology "Symbolic constant terminology" +enum SymbolicConstantToStringOptionBits { + //! \brief Return the full name for a given constant. + //! + //! \attention API consumers should provide this value when desired, but + //! should provide only one of kUseFullName and ::kUseShortName. Because + //! kUseFullName is valueless, implementers should check for the absence + //! of ::kUseShortName instead. + kUseFullName = 0 << 0, + + //! \brief Return the short name for a given constant. + kUseShortName = 1 << 0, + + //! \brief If no symbolic name is known for a given constant, return an empty + //! string. + //! + //! \attention API consumers should provide this value when desired, but + //! should provide only one of kUnknownIsEmpty and ::kUnknownIsNumeric. + //! Because kUnknownIsEmpty is valueless, implementers should check for + //! the absence of ::kUnknownIsNumeric instead. + kUnknownIsEmpty = 0 << 1, + + //! \brief If no symbolic name is known for a given constant, return a numeric + //! string. + //! + //! The numeric format used will vary by family, but will be appropriate to + //! the family. Families whose values are typically constructed as bitfields + //! will generally use a hexadecimal format, and other families will generally + //! use a signed or unsigned decimal format. + kUnknownIsNumeric = 1 << 1, + + //! \brief Use `|` to combine values in a bitfield. + //! + //! For families whose values may be constructed as bitfields, allow + //! conversion to strings containing multiple individual components treated as + //! being combined by a bitwise “or” operation. An example family of constants + //! that behaves this way is the suite of Mach exception masks. For constants + //! that are not constructed as bitfields, or constants that are only + //! partially constructed as bitfields, this option has no effect. + kUseOr = 1 << 2, +}; + +//! \brief A bitfield containing values of #SymbolicConstantToStringOptionBits. +using SymbolicConstantToStringOptions = unsigned int; + +//! \brief Options for various `StringTo*` functions in `symbolic_constants_*` +//! files. +//! +//! Not every `StringTo*` function will implement each of these options. See +//! function-specific documentation for details. +//! +//! \sa \ref symbolic_constant_terminology "Symbolic constant terminology" +enum StringToSymbolicConstantOptionBits { + //! \brief Allow conversion from a string containing a symbolic constant by + //! its full name. + kAllowFullName = 1 << 0, + + //! \brief Allow conversion from a string containing a symbolic constant by + //! its short name. + kAllowShortName = 1 << 1, + + //! \brief Allow conversion from a numeric string. + kAllowNumber = 1 << 2, + + //! \brief Allow `|` to combine values in a bitfield. + //! + //! For families whose values may be constructed as bitfields, allow + //! conversion of strings containing multiple individual components treated as + //! being combined by a bitwise “or” operation. An example family of constants + //! that behaves this way is the suite of Mach exception masks. For constants + //! that are not constructed as bitfields, or constants that are only + //! partially constructed as bitfields, this option has no effect. + kAllowOr = 1 << 3, +}; + +//! \brief A bitfield containing values of #StringToSymbolicConstantOptionBits. +using StringToSymbolicConstantOptions = unsigned int; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_
diff --git a/third_party/crashpad/crashpad/util/misc/tri_state.h b/third_party/crashpad/crashpad/util/misc/tri_state.h new file mode 100644 index 0000000..be55721 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/tri_state.h
@@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_TRI_STATE_H_ +#define CRASHPAD_UTIL_MISC_TRI_STATE_H_ + +#include <stdint.h> + +namespace crashpad { + +//! \brief A tri-state value that can be unset, on, or off. +enum class TriState : uint8_t { + //! \brief The value has not explicitly been set. + //! + //! To allow a zero-initialized value to have this behavior, this must have + //! the value `0`. + kUnset = 0, + + //! \brief The value has explicitly been set to on, or a behavior has + //! explicitly been enabled. + kEnabled, + + //! \brief The value has explicitly been set to off, or a behavior has + //! explicitly been disabled. + kDisabled, +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_TRI_STATE_H_
diff --git a/third_party/crashpad/crashpad/util/misc/uuid.cc b/third_party/crashpad/crashpad/util/misc/uuid.cc new file mode 100644 index 0000000..ca7b22b1 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/uuid.cc
@@ -0,0 +1,150 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#if !defined(__STDC_FORMAT_MACROS) +#define __STDC_FORMAT_MACROS +#endif + +#include "util/misc/uuid.h" + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/sys_byteorder.h" +#include "util/stdlib/cxx.h" + +#if defined(OS_MACOSX) +#include <uuid/uuid.h> +#endif // OS_MACOSX + +#if CXX_LIBRARY_VERSION >= 2011 +#include <type_traits> +#endif + +namespace crashpad { + +static_assert(sizeof(UUID) == 16, "UUID must be 16 bytes"); + +#if CXX_LIBRARY_VERSION >= 2011 +static_assert(std::is_standard_layout<UUID>::value, + "UUID must be standard layout"); +#endif + +UUID::UUID() : data_1(0), data_2(0), data_3(0), data_4(), data_5() { +} + +UUID::UUID(InitializeWithNewTag) { + CHECK(InitializeWithNew()); +} + +UUID::UUID(const uint8_t* bytes) { + InitializeFromBytes(bytes); +} + +bool UUID::operator==(const UUID& that) const { + return memcmp(this, &that, sizeof(UUID)) == 0; +} + +void UUID::InitializeFromBytes(const uint8_t* bytes) { + memcpy(this, bytes, sizeof(*this)); + data_1 = base::NetToHost32(data_1); + data_2 = base::NetToHost16(data_2); + data_3 = base::NetToHost16(data_3); +} + +bool UUID::InitializeFromString(const base::StringPiece& string) { + if (string.length() != 36) + return false; + + UUID temp; + const char kScanFormat[] = + "%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16 + "-%02" SCNx8 "%02" SCNx8 + "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8; + int rv = sscanf(string.data(), + kScanFormat, + &temp.data_1, + &temp.data_2, + &temp.data_3, + &temp.data_4[0], + &temp.data_4[1], + &temp.data_5[0], + &temp.data_5[1], + &temp.data_5[2], + &temp.data_5[3], + &temp.data_5[4], + &temp.data_5[5]); + if (rv != 11) + return false; + + *this = temp; + return true; +} + +bool UUID::InitializeWithNew() { +#if defined(OS_MACOSX) + uuid_t uuid; + uuid_generate(uuid); + InitializeFromBytes(uuid); + return true; +#elif defined(OS_WIN) + ::UUID system_uuid; + if (UuidCreate(&system_uuid) != RPC_S_OK) { + LOG(ERROR) << "UuidCreate"; + return false; + } + InitializeFromSystemUUID(&system_uuid); + return true; +#else +#error Port. +#endif // OS_MACOSX +} + +#if defined(OS_WIN) +void UUID::InitializeFromSystemUUID(const ::UUID* system_uuid) { + static_assert(sizeof(::UUID) == sizeof(UUID), + "unexpected system uuid size"); + static_assert(offsetof(::UUID, Data1) == offsetof(UUID, data_1), + "unexpected system uuid layout"); + memcpy(this, system_uuid, sizeof(::UUID)); +} +#endif // OS_WIN + +std::string UUID::ToString() const { + return base::StringPrintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + data_1, + data_2, + data_3, + data_4[0], + data_4[1], + data_5[0], + data_5[1], + data_5[2], + data_5[3], + data_5[4], + data_5[5]); +} + +#if defined(OS_WIN) +base::string16 UUID::ToString16() const { + return base::UTF8ToUTF16(ToString()); +} +#endif // OS_WIN + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/misc/uuid.h b/third_party/crashpad/crashpad/util/misc/uuid.h new file mode 100644 index 0000000..f25aa65 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/uuid.h
@@ -0,0 +1,114 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_MISC_UUID_H_ +#define CRASHPAD_UTIL_MISC_UUID_H_ + +#include <stdint.h> + +#include <string> + +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <rpc.h> +#endif + +namespace crashpad { + +//! \brief A universally unique identifier (%UUID). +//! +//! An alternate term for %UUID is “globally unique identifier” (GUID), used +//! primarily by Microsoft. +//! +//! A %UUID is a unique 128-bit number specified by RFC 4122. +//! +//! This is a standard-layout structure. +struct UUID { + //! \brief Initializes the %UUID to zero. + UUID(); + + //! \brief Tag to pass to constructor to indicate it should initialize with + //! generated data. + struct InitializeWithNewTag {}; + + //! \brief Initializes the %UUID using a standard system facility to generate + //! the value. + //! + //! CHECKs on failure with a message logged. + explicit UUID(InitializeWithNewTag); + + //! \copydoc InitializeFromBytes() + explicit UUID(const uint8_t* bytes); + + bool operator==(const UUID& that) const; + bool operator!=(const UUID& that) const { return !operator==(that); } + + //! \brief Initializes the %UUID from a sequence of bytes. + //! + //! \a bytes is taken as a %UUID laid out in big-endian format in memory. On + //! little-endian machines, appropriate byte-swapping will be performed to + //! initialize an object’s data members. + //! + //! \param[in] bytes A buffer of exactly 16 bytes that will be assigned to the + //! %UUID. + void InitializeFromBytes(const uint8_t* bytes); + + //! \brief Initializes the %UUID from a RFC 4122 §3 formatted string. + //! + //! \param[in] string A string of the form + //! `"00112233-4455-6677-8899-aabbccddeeff"`. + //! + //! \return `true` if the string was formatted correctly and the object has + //! been initialized with the data. `false` if the string could not be + //! parsed, with the object state untouched. + bool InitializeFromString(const base::StringPiece& string); + + //! \brief Initializes the %UUID using a standard system facility to generate + //! the value. + //! + //! \return `true` if the %UUID was initialized correctly, `false` otherwise + //! with a message logged. + bool InitializeWithNew(); + +#if defined(OS_WIN) || DOXYGEN + //! \brief Initializes the %UUID from a system `UUID` or `GUID` structure. + //! + //! \param[in] system_uuid A system `UUID` or `GUID` structure. + void InitializeFromSystemUUID(const ::UUID* system_uuid); +#endif // OS_WIN + + //! \brief Formats the %UUID per RFC 4122 §3. + //! + //! \return A string of the form `"00112233-4455-6677-8899-aabbccddeeff"`. + std::string ToString() const; + +#if defined(OS_WIN) || DOXYGEN + //! \brief The same as ToString, but returned as a string16. + base::string16 ToString16() const; +#endif // OS_WIN + + // These fields are laid out according to RFC 4122 §4.1.2. + uint32_t data_1; + uint16_t data_2; + uint16_t data_3; + uint8_t data_4[2]; + uint8_t data_5[6]; +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_UUID_H_
diff --git a/third_party/crashpad/crashpad/util/misc/uuid_test.cc b/third_party/crashpad/crashpad/util/misc/uuid_test.cc new file mode 100644 index 0000000..0dc6b425 --- /dev/null +++ b/third_party/crashpad/crashpad/util/misc/uuid_test.cc
@@ -0,0 +1,240 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/misc/uuid.h" + +#include <stdint.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/format_macros.h" +#include "base/scoped_generic.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(UUID, UUID) { + UUID uuid_zero; + EXPECT_EQ(0u, uuid_zero.data_1); + EXPECT_EQ(0u, uuid_zero.data_2); + EXPECT_EQ(0u, uuid_zero.data_3); + EXPECT_EQ(0u, uuid_zero.data_4[0]); + EXPECT_EQ(0u, uuid_zero.data_4[1]); + EXPECT_EQ(0u, uuid_zero.data_5[0]); + EXPECT_EQ(0u, uuid_zero.data_5[1]); + EXPECT_EQ(0u, uuid_zero.data_5[2]); + EXPECT_EQ(0u, uuid_zero.data_5[3]); + EXPECT_EQ(0u, uuid_zero.data_5[4]); + EXPECT_EQ(0u, uuid_zero.data_5[5]); + EXPECT_EQ("00000000-0000-0000-0000-000000000000", uuid_zero.ToString()); + + const uint8_t kBytes[16] = {0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f}; + UUID uuid(kBytes); + EXPECT_EQ(0x00010203u, uuid.data_1); + EXPECT_EQ(0x0405u, uuid.data_2); + EXPECT_EQ(0x0607u, uuid.data_3); + EXPECT_EQ(0x08u, uuid.data_4[0]); + EXPECT_EQ(0x09u, uuid.data_4[1]); + EXPECT_EQ(0x0au, uuid.data_5[0]); + EXPECT_EQ(0x0bu, uuid.data_5[1]); + EXPECT_EQ(0x0cu, uuid.data_5[2]); + EXPECT_EQ(0x0du, uuid.data_5[3]); + EXPECT_EQ(0x0eu, uuid.data_5[4]); + EXPECT_EQ(0x0fu, uuid.data_5[5]); + EXPECT_EQ("00010203-0405-0607-0809-0a0b0c0d0e0f", uuid.ToString()); + + // Test both operator== and operator!=. + EXPECT_FALSE(uuid == uuid_zero); + EXPECT_NE(uuid, uuid_zero); + + UUID uuid_2(kBytes); + EXPECT_EQ(uuid, uuid_2); + EXPECT_FALSE(uuid != uuid_2); + + // Make sure that operator== and operator!= check the entire UUID. + ++uuid.data_1; + EXPECT_NE(uuid, uuid_2); + --uuid.data_1; + ++uuid.data_2; + EXPECT_NE(uuid, uuid_2); + --uuid.data_2; + ++uuid.data_3; + EXPECT_NE(uuid, uuid_2); + --uuid.data_3; + for (size_t index = 0; index < arraysize(uuid.data_4); ++index) { + ++uuid.data_4[index]; + EXPECT_NE(uuid, uuid_2); + --uuid.data_4[index]; + } + for (size_t index = 0; index < arraysize(uuid.data_5); ++index) { + ++uuid.data_5[index]; + EXPECT_NE(uuid, uuid_2); + --uuid.data_5[index]; + } + + // Make sure that the UUIDs are equal again, otherwise the test above may not + // have been valid. + EXPECT_EQ(uuid, uuid_2); + + const uint8_t kMoreBytes[16] = {0xff, + 0xee, + 0xdd, + 0xcc, + 0xbb, + 0xaa, + 0x99, + 0x88, + 0x77, + 0x66, + 0x55, + 0x44, + 0x33, + 0x22, + 0x11, + 0x00}; + uuid.InitializeFromBytes(kMoreBytes); + EXPECT_EQ(0xffeeddccu, uuid.data_1); + EXPECT_EQ(0xbbaau, uuid.data_2); + EXPECT_EQ(0x9988u, uuid.data_3); + EXPECT_EQ(0x77u, uuid.data_4[0]); + EXPECT_EQ(0x66u, uuid.data_4[1]); + EXPECT_EQ(0x55u, uuid.data_5[0]); + EXPECT_EQ(0x44u, uuid.data_5[1]); + EXPECT_EQ(0x33u, uuid.data_5[2]); + EXPECT_EQ(0x22u, uuid.data_5[3]); + EXPECT_EQ(0x11u, uuid.data_5[4]); + EXPECT_EQ(0x00u, uuid.data_5[5]); + EXPECT_EQ("ffeeddcc-bbaa-9988-7766-554433221100", uuid.ToString()); + + EXPECT_NE(uuid, uuid_2); + EXPECT_NE(uuid, uuid_zero); + + // Test that UUID is standard layout. + memset(&uuid, 0x45, 16); + EXPECT_EQ(0x45454545u, uuid.data_1); + EXPECT_EQ(0x4545u, uuid.data_2); + EXPECT_EQ(0x4545u, uuid.data_3); + EXPECT_EQ(0x45u, uuid.data_4[0]); + EXPECT_EQ(0x45u, uuid.data_4[1]); + EXPECT_EQ(0x45u, uuid.data_5[0]); + EXPECT_EQ(0x45u, uuid.data_5[1]); + EXPECT_EQ(0x45u, uuid.data_5[2]); + EXPECT_EQ(0x45u, uuid.data_5[3]); + EXPECT_EQ(0x45u, uuid.data_5[4]); + EXPECT_EQ(0x45u, uuid.data_5[5]); + EXPECT_EQ("45454545-4545-4545-4545-454545454545", uuid.ToString()); + + UUID initialized_generated(UUID::InitializeWithNewTag{}); + EXPECT_NE(initialized_generated, uuid_zero); +} + +TEST(UUID, FromString) { + const struct TestCase { + const char* uuid_string; + bool success; + } kCases[] = { + // Valid: + {"c6849cb5-fe14-4a79-8978-9ae6034c521d", true}, + {"00000000-0000-0000-0000-000000000000", true}, + {"ffffffff-ffff-ffff-ffff-ffffffffffff", true}, + // Outside HEX range: + {"7318z10b-c453-4cef-9dc8-015655cb4bbc", false}, + {"7318a10b-c453-4cef-9dz8-015655cb4bbc", false}, + // Incomplete: + {"15655cb4-", false}, + {"7318f10b-c453-4cef-9dc8-015655cb4bb", false}, + {"318f10b-c453-4cef-9dc8-015655cb4bb2", false}, + {"7318f10b-c453-4ef-9dc8-015655cb4bb2", false}, + {"", false}, + {"abcd", false}, + // Trailing data: + {"6d247a34-53d5-40ec-a90d-d8dea9e94cc01", false} + }; + + const std::string empty_uuid = UUID().ToString(); + + for (size_t index = 0; index < arraysize(kCases); ++index) { + const TestCase& test_case = kCases[index]; + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ": %s", index, test_case.uuid_string)); + + UUID uuid; + EXPECT_EQ(test_case.success, + uuid.InitializeFromString(test_case.uuid_string)); + if (test_case.success) { + EXPECT_EQ(test_case.uuid_string, uuid.ToString()); + } else { + EXPECT_EQ(empty_uuid, uuid.ToString()); + } + } + + // Test for case insensitivty. + UUID uuid; + uuid.InitializeFromString("F32E5BDC-2681-4C73-A4E6-911FFD89B846"); + EXPECT_EQ("f32e5bdc-2681-4c73-a4e6-911ffd89b846", uuid.ToString()); + + // Mixed case. + uuid.InitializeFromString("5762C15D-50b5-4171-a2e9-7429C9EC6CAB"); + EXPECT_EQ("5762c15d-50b5-4171-a2e9-7429c9ec6cab", uuid.ToString()); +} + +#if defined(OS_WIN) + +TEST(UUID, FromSystem) { + ::GUID system_uuid; + ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid)); + + UUID uuid; + uuid.InitializeFromSystemUUID(&system_uuid); + + RPC_WSTR system_string; + ASSERT_EQ(RPC_S_OK, UuidToString(&system_uuid, &system_string)); + + struct ScopedRpcStringFreeTraits { + static RPC_WSTR* InvalidValue() { return nullptr; } + static void Free(RPC_WSTR* rpc_string) { + EXPECT_EQ(RPC_S_OK, RpcStringFree(rpc_string)); + } + }; + using ScopedRpcString = + base::ScopedGeneric<RPC_WSTR*, ScopedRpcStringFreeTraits>; + ScopedRpcString scoped_system_string(&system_string); + + EXPECT_EQ(reinterpret_cast<wchar_t*>(system_string), uuid.ToString16()); +} + +#endif // OS_WIN + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_body.cc b/third_party/crashpad/crashpad/util/net/http_body.cc new file mode 100644 index 0000000..6bc6ec9 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_body.cc
@@ -0,0 +1,119 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_body.h" + +#include <string.h> + +#include <algorithm> +#include <limits> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +StringHTTPBodyStream::StringHTTPBodyStream(const std::string& string) + : HTTPBodyStream(), string_(string), bytes_read_() { +} + +StringHTTPBodyStream::~StringHTTPBodyStream() { +} + +FileOperationResult StringHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, + size_t max_len) { + size_t num_bytes_remaining = string_.length() - bytes_read_; + if (num_bytes_remaining == 0) { + return num_bytes_remaining; + } + + size_t num_bytes_returned = std::min( + std::min(num_bytes_remaining, max_len), + implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max())); + memcpy(buffer, &string_[bytes_read_], num_bytes_returned); + bytes_read_ += num_bytes_returned; + return num_bytes_returned; +} + +FileHTTPBodyStream::FileHTTPBodyStream(const base::FilePath& path) + : HTTPBodyStream(), path_(path), file_(), file_state_(kUnopenedFile) { +} + +FileHTTPBodyStream::~FileHTTPBodyStream() { +} + +FileOperationResult FileHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, + size_t max_len) { + switch (file_state_) { + case kUnopenedFile: + file_.reset(LoggingOpenFileForRead(path_)); + if (!file_.is_valid()) { + file_state_ = kFileOpenError; + return -1; + } + file_state_ = kReading; + break; + case kFileOpenError: + return -1; + case kClosedAtEOF: + return 0; + case kReading: + break; + } + + FileOperationResult rv = ReadFile(file_.get(), buffer, max_len); + if (rv == 0) { + file_.reset(); + file_state_ = kClosedAtEOF; + } else if (rv < 0) { + PLOG(ERROR) << "read"; + } + return rv; +} + +CompositeHTTPBodyStream::CompositeHTTPBodyStream( + const CompositeHTTPBodyStream::PartsList& parts) + : HTTPBodyStream(), parts_(parts), current_part_(parts_.begin()) { +} + +CompositeHTTPBodyStream::~CompositeHTTPBodyStream() { + STLDeleteContainerPointers(parts_.begin(), parts_.end()); +} + +FileOperationResult CompositeHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, + size_t buffer_len) { + FileOperationResult max_len = std::min( + buffer_len, + implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max())); + FileOperationResult bytes_copied = 0; + while (bytes_copied < max_len && current_part_ != parts_.end()) { + FileOperationResult this_read = + (*current_part_) + ->GetBytesBuffer(buffer + bytes_copied, max_len - bytes_copied); + + if (this_read == 0) { + // If the current part has returned 0 indicating EOF, advance the current + // part and try again. + ++current_part_; + } else if (this_read < 0) { + return this_read; + } + bytes_copied += this_read; + } + + return bytes_copied; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_body.h b/third_party/crashpad/crashpad/util/net/http_body.h new file mode 100644 index 0000000..1646591 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_body.h
@@ -0,0 +1,130 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_H_ +#define CRASHPAD_UTIL_NET_HTTP_BODY_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "util/file/file_io.h" + +namespace crashpad { + +//! \brief An interface to a stream that can be used for an HTTP request body. +class HTTPBodyStream { + public: + virtual ~HTTPBodyStream() {} + + //! \brief Copies up to \a max_len bytes into the user-supplied buffer. + //! + //! \param[out] buffer A user-supplied buffer into which this method will copy + //! bytes from the stream. + //! \param[in] max_len The length (or size) of \a buffer. At most this many + //! bytes will be copied. + //! + //! \return On success, a positive number indicating the number of bytes + //! actually copied to \a buffer. On failure, a negative number. When + //! the stream has no more data, returns `0`. + virtual FileOperationResult GetBytesBuffer(uint8_t* buffer, + size_t max_len) = 0; + + protected: + HTTPBodyStream() {} +}; + +//! \brief An implementation of HTTPBodyStream that turns a fixed string into +//! a stream. +class StringHTTPBodyStream : public HTTPBodyStream { + public: + //! \brief Creates a stream with the specified string. + //! + //! \param[in] string The string to turn into a stream. + explicit StringHTTPBodyStream(const std::string& string); + + ~StringHTTPBodyStream() override; + + // HTTPBodyStream: + FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override; + + private: + std::string string_; + size_t bytes_read_; + + DISALLOW_COPY_AND_ASSIGN(StringHTTPBodyStream); +}; + +//! \brief An implementation of HTTPBodyStream that reads from the specified +//! file and provides its contents for an HTTP body. +class FileHTTPBodyStream : public HTTPBodyStream { + public: + //! \brief Creates a stream for reading the file at the specified \a path. + //! + //! \param[in] path The file from which this HTTPBodyStream will read. + explicit FileHTTPBodyStream(const base::FilePath& path); + + ~FileHTTPBodyStream() override; + + // HTTPBodyStream: + FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override; + + private: + enum FileState { + kUnopenedFile, + kFileOpenError, + kClosedAtEOF, + kReading, + }; + + base::FilePath path_; + ScopedFileHandle file_; + FileState file_state_; + + DISALLOW_COPY_AND_ASSIGN(FileHTTPBodyStream); +}; + +//! \brief An implementation of HTTPBodyStream that combines an array of +//! several other HTTPBodyStream objects into a single, unified stream. +class CompositeHTTPBodyStream : public HTTPBodyStream { + public: + using PartsList = std::vector<HTTPBodyStream*>; + + //! \brief Creates a stream from an array of other stream parts. + //! + //! \param[in] parts A vector of HTTPBodyStream objects, of which this object + //! takes ownership, that will be represented as a single unified stream. + //! Callers should not mutate the stream objects after passing them to + //! an instance of this class. + explicit CompositeHTTPBodyStream(const PartsList& parts); + + ~CompositeHTTPBodyStream() override; + + // HTTPBodyStream: + FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override; + + private: + PartsList parts_; + PartsList::iterator current_part_; + + DISALLOW_COPY_AND_ASSIGN(CompositeHTTPBodyStream); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NET_HTTP_BODY_H_
diff --git a/third_party/crashpad/crashpad/util/net/http_body_test.cc b/third_party/crashpad/crashpad/util/net/http_body_test.cc new file mode 100644 index 0000000..ea4d869 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_body_test.cc
@@ -0,0 +1,218 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_body.h" + +#include "gtest/gtest.h" +#include "test/paths.h" +#include "util/misc/implicit_cast.h" +#include "util/net/http_body_test_util.h" + +namespace crashpad { +namespace test { +namespace { + +void ExpectBufferSet(const uint8_t* actual, + uint8_t expected_byte, + size_t num_expected_bytes) { + for (size_t i = 0; i < num_expected_bytes; ++i) { + EXPECT_EQ(expected_byte, actual[i]) << i; + } +} + +TEST(StringHTTPBodyStream, EmptyString) { + uint8_t buf[32]; + memset(buf, '!', sizeof(buf)); + + std::string empty_string; + StringHTTPBodyStream stream(empty_string); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + ExpectBufferSet(buf, '!', sizeof(buf)); +} + +TEST(StringHTTPBodyStream, SmallString) { + uint8_t buf[32]; + memset(buf, '!', sizeof(buf)); + + std::string string("Hello, world"); + StringHTTPBodyStream stream(string); + EXPECT_EQ(implicit_cast<FileOperationResult>(string.length()), + stream.GetBytesBuffer(buf, sizeof(buf))); + + std::string actual(reinterpret_cast<const char*>(buf), string.length()); + EXPECT_EQ(string, actual); + ExpectBufferSet(buf + string.length(), '!', sizeof(buf) - string.length()); + + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); +} + +TEST(StringHTTPBodyStream, MultipleReads) { + uint8_t buf[2]; + memset(buf, '!', sizeof(buf)); + + { + std::string string("test"); + SCOPED_TRACE("aligned buffer boundary"); + + StringHTTPBodyStream stream(string); + EXPECT_EQ(2, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ('t', buf[0]); + EXPECT_EQ('e', buf[1]); + EXPECT_EQ(2, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ('s', buf[0]); + EXPECT_EQ('t', buf[1]); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ('s', buf[0]); + EXPECT_EQ('t', buf[1]); + } + + { + std::string string("abc"); + SCOPED_TRACE("unaligned buffer boundary"); + + StringHTTPBodyStream stream(string); + EXPECT_EQ(2, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ('a', buf[0]); + EXPECT_EQ('b', buf[1]); + EXPECT_EQ(1, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ('c', buf[0]); + EXPECT_EQ('b', buf[1]); // Unmodified from last read. + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ('c', buf[0]); + EXPECT_EQ('b', buf[1]); + } +} + +TEST(FileHTTPBodyStream, ReadASCIIFile) { + base::FilePath path = Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); + FileHTTPBodyStream stream(path); + std::string contents = ReadStreamToString(&stream, 32); + EXPECT_EQ("This is a test.\n", contents); + + // Make sure that the file is not read again after it has been read to + // completion. + uint8_t buf[8]; + memset(buf, '!', sizeof(buf)); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + ExpectBufferSet(buf, '!', sizeof(buf)); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + ExpectBufferSet(buf, '!', sizeof(buf)); +} + +TEST(FileHTTPBodyStream, ReadBinaryFile) { + // HEX contents of file: |FEEDFACE A11A15|. + base::FilePath path = Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("util/net/testdata/binary_http_body.dat")); + // This buffer size was chosen so that reading the file takes multiple reads. + uint8_t buf[4]; + + FileHTTPBodyStream stream(path); + + memset(buf, '!', sizeof(buf)); + EXPECT_EQ(4, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ(0xfe, buf[0]); + EXPECT_EQ(0xed, buf[1]); + EXPECT_EQ(0xfa, buf[2]); + EXPECT_EQ(0xce, buf[3]); + + memset(buf, '!', sizeof(buf)); + EXPECT_EQ(3, stream.GetBytesBuffer(buf, sizeof(buf))); + EXPECT_EQ(0xa1, buf[0]); + EXPECT_EQ(0x1a, buf[1]); + EXPECT_EQ(0x15, buf[2]); + EXPECT_EQ('!', buf[3]); + + memset(buf, '!', sizeof(buf)); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + ExpectBufferSet(buf, '!', sizeof(buf)); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + ExpectBufferSet(buf, '!', sizeof(buf)); +} + +TEST(FileHTTPBodyStream, NonExistentFile) { + base::FilePath path = base::FilePath( + FILE_PATH_LITERAL("/var/empty/crashpad/util/net/http_body/null")); + FileHTTPBodyStream stream(path); + + uint8_t buf = 0xff; + EXPECT_LT(stream.GetBytesBuffer(&buf, 1), 0); + EXPECT_EQ(0xff, buf); + EXPECT_LT(stream.GetBytesBuffer(&buf, 1), 0); + EXPECT_EQ(0xff, buf); +} + +TEST(CompositeHTTPBodyStream, TwoEmptyStrings) { + std::vector<HTTPBodyStream*> parts; + parts.push_back(new StringHTTPBodyStream(std::string())); + parts.push_back(new StringHTTPBodyStream(std::string())); + + CompositeHTTPBodyStream stream(parts); + + uint8_t buf[5]; + memset(buf, '!', sizeof(buf)); + EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf))); + ExpectBufferSet(buf, '!', sizeof(buf)); +} + +class CompositeHTTPBodyStreamBufferSize + : public testing::TestWithParam<size_t> { +}; + +TEST_P(CompositeHTTPBodyStreamBufferSize, ThreeStringParts) { + std::string string1("crashpad"); + std::string string2("test"); + std::string string3("foobar"); + const size_t all_strings_length = + string1.length() + string2.length() + string3.length(); + std::string buf(all_strings_length + 3, '!'); + + std::vector<HTTPBodyStream*> parts; + parts.push_back(new StringHTTPBodyStream(string1)); + parts.push_back(new StringHTTPBodyStream(string2)); + parts.push_back(new StringHTTPBodyStream(string3)); + + CompositeHTTPBodyStream stream(parts); + + std::string actual_string = ReadStreamToString(&stream, GetParam()); + EXPECT_EQ(string1 + string2 + string3, actual_string); + + ExpectBufferSet(reinterpret_cast<uint8_t*>(&buf[all_strings_length]), '!', 3); +} + +TEST_P(CompositeHTTPBodyStreamBufferSize, StringsAndFile) { + std::string string1("Hello! "); + std::string string2(" Goodbye :)"); + + std::vector<HTTPBodyStream*> parts; + parts.push_back(new StringHTTPBodyStream(string1)); + base::FilePath path = Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); + parts.push_back(new FileHTTPBodyStream(path)); + parts.push_back(new StringHTTPBodyStream(string2)); + + CompositeHTTPBodyStream stream(parts); + + std::string expected_string = string1 + "This is a test.\n" + string2; + std::string actual_string = ReadStreamToString(&stream, GetParam()); + EXPECT_EQ(expected_string, actual_string); +} + +INSTANTIATE_TEST_CASE_P(VariableBufferSize, + CompositeHTTPBodyStreamBufferSize, + testing::Values(1, 2, 9, 16, 31, 128, 1024)); + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_body_test_util.cc b/third_party/crashpad/crashpad/util/net/http_body_test_util.cc new file mode 100644 index 0000000..c3dbd19 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_body_test_util.cc
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_body_test_util.h" + +#include <stdint.h> + +#include "base/memory/scoped_ptr.h" +#include "gtest/gtest.h" +#include "util/file/file_io.h" +#include "util/net/http_body.h" + +namespace crashpad { +namespace test { + +std::string ReadStreamToString(HTTPBodyStream* stream) { + return ReadStreamToString(stream, 32); +} + +std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size) { + scoped_ptr<uint8_t[]> buf(new uint8_t[buffer_size]); + std::string result; + + FileOperationResult bytes_read; + while ((bytes_read = stream->GetBytesBuffer(buf.get(), buffer_size)) != 0) { + if (bytes_read < 0) { + ADD_FAILURE() << "Failed to read from stream: " << bytes_read; + return std::string(); + } + + result.append(reinterpret_cast<char*>(buf.get()), bytes_read); + } + + return result; +} + +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_body_test_util.h b/third_party/crashpad/crashpad/util/net/http_body_test_util.h new file mode 100644 index 0000000..288fcca --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_body_test_util.h
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_ +#define CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_ + +#include <sys/types.h> + +#include <string> + +namespace crashpad { + +class HTTPBodyStream; + +namespace test { + +//! \brief Reads a HTTPBodyStream to a string. If an error occurs, adds a +//! test failure and returns an empty string. +//! +//! \param[in] stream The stream from which to read. +//! +//! \return The contents of the stream, or an empty string on failure. +std::string ReadStreamToString(HTTPBodyStream* stream); + +//! \brief Reads a HTTPBodyStream to a string. If an error occurs, adds a +//! test failure and returns an empty string. +//! +//! \param[in] stream The stream from which to read. +//! \param[in] buffer_size The size of the buffer to use when reading from the +//! stream. +//! +//! \return The contents of the stream, or an empty string on failure. +std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
diff --git a/third_party/crashpad/crashpad/util/net/http_headers.cc b/third_party/crashpad/crashpad/util/net/http_headers.cc new file mode 100644 index 0000000..09d61b3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_headers.cc
@@ -0,0 +1,23 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_headers.h" + +namespace crashpad { + +const char kContentType[] = "Content-Type"; + +const char kContentLength[] = "Content-Length"; + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_headers.h b/third_party/crashpad/crashpad/util/net/http_headers.h new file mode 100644 index 0000000..3633cb2d --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_headers.h
@@ -0,0 +1,34 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NET_HTTP_HEADERS_H_ +#define CRASHPAD_UTIL_NET_HTTP_HEADERS_H_ + +#include <map> +#include <string> + +namespace crashpad { + +//! \brief A map of HTTP header fields to their values. +using HTTPHeaders = std::map<std::string, std::string>; + +//! \brief The header name `"Content-Type"`. +extern const char kContentType[]; + +//! \brief The header name `"Content-Length"`. +extern const char kContentLength[]; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NET_HTTP_HEADERS_H_
diff --git a/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc b/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc new file mode 100644 index 0000000..83186af67 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc
@@ -0,0 +1,199 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_multipart_builder.h" + +#include <utility> +#include <vector> + +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" +#include "util/net/http_body.h" + +namespace crashpad { + +namespace { + +const char kCRLF[] = "\r\n"; + +const char kBoundaryCRLF[] = "\r\n\r\n"; + +// Generates a random string suitable for use as a multipart boundary. +std::string GenerateBoundaryString() { + // RFC 2046 §5.1.1 says that the boundary string may be 1 to 70 characters + // long, choosing from the set of alphanumeric characters along with + // characters from the set “'()+_,-./:=? ”, and not ending in a space. + // However, some servers have been observed as dealing poorly with certain + // nonalphanumeric characters. See + // blink/Source/platform/network/FormDataBuilder.cpp + // blink::FormDataBuilder::generateUniqueBoundaryString(). + // + // This implementation produces a 56-character string with over 190 bits of + // randomness (62^32 > 2^190). + std::string boundary_string = "---MultipartBoundary-"; + for (int index = 0; index < 32; ++index) { + const char kCharacters[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + int random_value = + base::RandInt(0, static_cast<int>(strlen(kCharacters)) - 1); + boundary_string += kCharacters[random_value]; + } + boundary_string += "---"; + return boundary_string; +} + +// Escapes the specified name to be suitable for the name field of a +// form-data part. +std::string EncodeMIMEField(const std::string& name) { + // RFC 2388 §3 says to encode non-ASCII field names according to RFC 2047, but + // no browsers implement that behavior. Instead, they send field names in the + // page hosting the form’s encoding. However, some form of escaping is needed. + // This URL-escapes the quote character and newline characters, per Blink. See + // blink/Source/platform/network/FormDataBuilder.cpp + // blink::appendQuotedString(). + // + // TODO(mark): This encoding is not necessarily correct, and the same code in + // Blink is marked with a FIXME. Blink does not escape the '%' character, + // that’s a local addition, but it seems appropriate to be able to decode the + // string properly. + std::string encoded; + for (char character : name) { + switch (character) { + case '\r': + case '\n': + case '"': + case '%': + encoded += base::StringPrintf("%%%02x", character); + break; + default: + encoded += character; + break; + } + } + + return encoded; +} + +// Returns a string, formatted with a multipart boundary and a field name, +// after which the contents of the part at |name| can be appended. +std::string GetFormDataBoundary(const std::string& boundary, + const std::string& name) { + return base::StringPrintf( + "--%s%sContent-Disposition: form-data; name=\"%s\"", + boundary.c_str(), + kCRLF, + EncodeMIMEField(name).c_str()); +} + +void AssertSafeMIMEType(const std::string& string) { + for (size_t i = 0; i < string.length(); ++i) { + char c = string[i]; + CHECK((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '/' || + c == '.' || + c == '_' || + c == '+' || + c == '-'); + } +} + +} // namespace + +HTTPMultipartBuilder::HTTPMultipartBuilder() + : boundary_(GenerateBoundaryString()), form_data_(), file_attachments_() { +} + +HTTPMultipartBuilder::~HTTPMultipartBuilder() { +} + +void HTTPMultipartBuilder::SetFormData(const std::string& key, + const std::string& value) { + EraseKey(key); + form_data_[key] = value; +} + +void HTTPMultipartBuilder::SetFileAttachment( + const std::string& key, + const std::string& upload_file_name, + const base::FilePath& path, + const std::string& content_type) { + EraseKey(upload_file_name); + + FileAttachment attachment; + attachment.filename = EncodeMIMEField(upload_file_name); + attachment.path = path; + + if (content_type.empty()) { + attachment.content_type = "application/octet-stream"; + } else { + AssertSafeMIMEType(content_type); + attachment.content_type = content_type; + } + + file_attachments_[key] = attachment; +} + +scoped_ptr<HTTPBodyStream> HTTPMultipartBuilder::GetBodyStream() { + // The objects inserted into this vector will be owned by the returned + // CompositeHTTPBodyStream. Take care to not early-return without deleting + // this memory. + std::vector<HTTPBodyStream*> streams; + + for (const auto& pair : form_data_) { + std::string field = GetFormDataBoundary(boundary_, pair.first); + field += kBoundaryCRLF; + field += pair.second; + field += kCRLF; + streams.push_back(new StringHTTPBodyStream(field)); + } + + for (const auto& pair : file_attachments_) { + const FileAttachment& attachment = pair.second; + std::string header = GetFormDataBoundary(boundary_, pair.first); + header += base::StringPrintf("; filename=\"%s\"%s", + attachment.filename.c_str(), kCRLF); + header += base::StringPrintf("Content-Type: %s%s", + attachment.content_type.c_str(), kBoundaryCRLF); + + streams.push_back(new StringHTTPBodyStream(header)); + streams.push_back(new FileHTTPBodyStream(attachment.path)); + streams.push_back(new StringHTTPBodyStream(kCRLF)); + } + + streams.push_back( + new StringHTTPBodyStream("--" + boundary_ + "--" + kCRLF)); + + return scoped_ptr<HTTPBodyStream>(new CompositeHTTPBodyStream(streams)); +} + +HTTPHeaders::value_type HTTPMultipartBuilder::GetContentType() const { + std::string content_type = + base::StringPrintf("multipart/form-data; boundary=%s", boundary_.c_str()); + return std::make_pair(kContentType, content_type); +} + +void HTTPMultipartBuilder::EraseKey(const std::string& key) { + auto data_it = form_data_.find(key); + if (data_it != form_data_.end()) + form_data_.erase(data_it); + + auto file_it = file_attachments_.find(key); + if (file_it != file_attachments_.end()) + file_attachments_.erase(file_it); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_multipart_builder.h b/third_party/crashpad/crashpad/util/net/http_multipart_builder.h new file mode 100644 index 0000000..c9e03ce --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_multipart_builder.h
@@ -0,0 +1,90 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_ +#define CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "util/net/http_headers.h" + +namespace crashpad { + +class HTTPBodyStream; + +//! \brief This class is used to build a MIME multipart message, conforming to +//! RFC 2046, for use as a HTTP request body. +class HTTPMultipartBuilder { + public: + HTTPMultipartBuilder(); + ~HTTPMultipartBuilder(); + + //! \brief Sets a `Content-Disposition: form-data` key-value pair. + //! + //! \param[in] key The key of the form data, specified as the `name` in the + //! multipart message. Any data previously set on this class with this + //! key will be overwritten. + //! \param[in] value The value to set at the \a key. + void SetFormData(const std::string& key, const std::string& value); + + //! \brief Specifies the file at \a path to have its contents uploaded as + //! multipart data, available at `name` of \a upload_file_name. + //! + //! \param[in] key The key of the form data, specified as the `name` in the + //! multipart message. Any data previously set on this class with this + //! key will be overwritten. + //! \param[in] upload_file_name The `filename` to specify for this multipart + //! data attachment. + //! \param[in] path The path of the file whose contents will be uploaded. + //! \param[in] content_type The `Content-Type` to specify for the attachment. + //! If this is empty, `"application/octet-stream"` will be used. + void SetFileAttachment(const std::string& key, + const std::string& upload_file_name, + const base::FilePath& path, + const std::string& content_type); + + //! \brief Generates the HTTPBodyStream for the data currently supplied to + //! the builder. + //! + //! \return A caller-owned HTTPBodyStream object. + scoped_ptr<HTTPBodyStream> GetBodyStream(); + + //! \brief Gets the header pair for `"Content-Type"`. + HTTPHeaders::value_type GetContentType() const; + + private: + struct FileAttachment { + std::string filename; + std::string content_type; + base::FilePath path; + }; + + // Removes elements from both data maps at the specified |key|, to ensure + // uniqueness across the entire HTTP body. + void EraseKey(const std::string& key); + + std::string boundary_; + std::map<std::string, std::string> form_data_; + std::map<std::string, FileAttachment> file_attachments_; + + DISALLOW_COPY_AND_ASSIGN(HTTPMultipartBuilder); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
diff --git a/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc b/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc new file mode 100644 index 0000000..d024c52 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc
@@ -0,0 +1,293 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_multipart_builder.h" + +#include <vector> + +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" +#include "test/paths.h" +#include "util/net/http_body.h" +#include "util/net/http_body_test_util.h" + +namespace crashpad { +namespace test { +namespace { + +std::vector<std::string> SplitCRLF(const std::string& string) { + std::vector<std::string> lines; + size_t last_line = 0; + for (size_t i = 0; i < string.length(); ++i) { + if (string[i] == '\r' && i+1 < string.length() && string[i+1] == '\n') { + lines.push_back(string.substr(last_line, i - last_line)); + last_line = i + 2; + ++i; + } + } + // Append any remainder. + if (last_line < string.length()) { + lines.push_back(string.substr(last_line)); + } + return lines; +} + +// In the tests below, the form data pairs don’t appear in the order they were +// added. The current implementation uses a std::map which sorts keys, so the +// entires appear in alphabetical order. However, this is an implementation +// detail, and it’s OK if the writer stops sorting in this order. Testing for +// a specific order is just the easiest way to write this test while the writer +// will output things in a known order. + +TEST(HTTPMultipartBuilder, ThreeStringFields) { + HTTPMultipartBuilder builder; + + const char kKey1[] = "key1"; + const char kValue1[] = "test"; + builder.SetFormData(kKey1, kValue1); + + const char kKey2[] = "key2"; + const char kValue2[] = "This is another test."; + builder.SetFormData(kKey2, kValue2); + + const char kKey3[] = "key-three"; + const char kValue3[] = "More tests"; + builder.SetFormData(kKey3, kValue3); + + scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream()); + ASSERT_TRUE(body.get()); + std::string contents = ReadStreamToString(body.get()); + auto lines = SplitCRLF(contents); + auto lines_it = lines.begin(); + + // The first line is the boundary. All subsequent boundaries must match this. + const std::string& boundary = *lines_it++; + EXPECT_GE(boundary.length(), 1u); + EXPECT_LE(boundary.length(), 70u); + + EXPECT_EQ("Content-Disposition: form-data; name=\"key-three\"", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kValue3, *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; name=\"key1\"", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kValue1, *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; name=\"key2\"", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kValue2, *lines_it++); + + EXPECT_EQ(boundary + "--", *lines_it++); + + EXPECT_EQ(lines.end(), lines_it); +} + +TEST(HTTPMultipartBuilder, ThreeFileAttachments) { + HTTPMultipartBuilder builder; + base::FilePath ascii_http_body_path = Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); + builder.SetFileAttachment("first", + "minidump.dmp", + ascii_http_body_path, + ""); + builder.SetFileAttachment("second", + "minidump.dmp", + ascii_http_body_path, + "text/plain"); + builder.SetFileAttachment("\"third 50% silly\"", + "test%foo.txt", + ascii_http_body_path, + "text/plain"); + + const char kFileContents[] = "This is a test.\n"; + + scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream()); + ASSERT_TRUE(body.get()); + std::string contents = ReadStreamToString(body.get()); + auto lines = SplitCRLF(contents); + ASSERT_EQ(16u, lines.size()); + auto lines_it = lines.begin(); + + const std::string& boundary = *lines_it++; + EXPECT_GE(boundary.length(), 1u); + EXPECT_LE(boundary.length(), 70u); + + EXPECT_EQ("Content-Disposition: form-data; " + "name=\"%22third 50%25 silly%22\"; filename=\"test%25foo.txt\"", + *lines_it++); + EXPECT_EQ("Content-Type: text/plain", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kFileContents, *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; " + "name=\"first\"; filename=\"minidump.dmp\"", + *lines_it++); + EXPECT_EQ("Content-Type: application/octet-stream", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kFileContents, *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; " + "name=\"second\"; filename=\"minidump.dmp\"", + *lines_it++); + EXPECT_EQ("Content-Type: text/plain", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kFileContents, *lines_it++); + + EXPECT_EQ(boundary + "--", *lines_it++); + + EXPECT_EQ(lines.end(), lines_it); +} + +TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) { + HTTPMultipartBuilder builder; + const char kKey[] = "a 100% \"silly\"\r\ntest"; + builder.SetFormData(kKey, "some dummy value"); + builder.SetFormData(kKey, "overwrite"); + scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream()); + ASSERT_TRUE(body.get()); + std::string contents = ReadStreamToString(body.get()); + auto lines = SplitCRLF(contents); + auto lines_it = lines.begin(); + + const std::string& boundary = *lines_it++; + EXPECT_GE(boundary.length(), 1u); + EXPECT_LE(boundary.length(), 70u); + + EXPECT_EQ( + "Content-Disposition: form-data; name=\"a 100%25 %22silly%22%0d%0atest\"", + *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ("overwrite", *lines_it++); + EXPECT_EQ(boundary + "--", *lines_it++); + EXPECT_EQ(lines.end(), lines_it); +} + +TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { + HTTPMultipartBuilder builder; + const char kValue[] = "1 2 3 test"; + builder.SetFormData("a key", kValue); + base::FilePath testdata_path = + Paths::TestDataRoot().Append(FILE_PATH_LITERAL("util/net/testdata")); + builder.SetFileAttachment("minidump", + "minidump.dmp", + testdata_path.Append(FILE_PATH_LITERAL( + "binary_http_body.dat")), + ""); + builder.SetFileAttachment("minidump2", + "minidump.dmp", + testdata_path.Append(FILE_PATH_LITERAL( + "binary_http_body.dat")), + ""); + builder.SetFileAttachment("minidump", + "minidump.dmp", + testdata_path.Append(FILE_PATH_LITERAL( + "ascii_http_body.txt")), + "text/plain"); + scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream()); + ASSERT_TRUE(body.get()); + std::string contents = ReadStreamToString(body.get()); + auto lines = SplitCRLF(contents); + ASSERT_EQ(15u, lines.size()); + auto lines_it = lines.begin(); + + const std::string& boundary = *lines_it++; + EXPECT_GE(boundary.length(), 1u); + EXPECT_LE(boundary.length(), 70u); + + EXPECT_EQ("Content-Disposition: form-data; name=\"a key\"", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kValue, *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; " + "name=\"minidump\"; filename=\"minidump.dmp\"", + *lines_it++); + EXPECT_EQ("Content-Type: text/plain", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ("This is a test.\n", *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; " + "name=\"minidump2\"; filename=\"minidump.dmp\"", + *lines_it++); + EXPECT_EQ("Content-Type: application/octet-stream", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ("\xFE\xED\xFA\xCE\xA1\x1A\x15", *lines_it++); + + EXPECT_EQ(boundary + "--", *lines_it++); + + EXPECT_EQ(lines.end(), lines_it); +} + +TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) { + HTTPMultipartBuilder builder; + const char kValue1[] = "11111"; + builder.SetFormData("one", kValue1); + base::FilePath ascii_http_body_path = Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); + builder.SetFileAttachment("minidump", + "minidump.dmp", + ascii_http_body_path, + ""); + const char kValue2[] = "this is not a file"; + builder.SetFormData("minidump", kValue2); + + scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream()); + ASSERT_TRUE(body.get()); + std::string contents = ReadStreamToString(body.get()); + auto lines = SplitCRLF(contents); + auto lines_it = lines.begin(); + + const std::string& boundary = *lines_it++; + EXPECT_GE(boundary.length(), 1u); + EXPECT_LE(boundary.length(), 70u); + + EXPECT_EQ("Content-Disposition: form-data; name=\"minidump\"", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kValue2, *lines_it++); + + EXPECT_EQ(boundary, *lines_it++); + EXPECT_EQ("Content-Disposition: form-data; name=\"one\"", *lines_it++); + EXPECT_EQ("", *lines_it++); + EXPECT_EQ(kValue1, *lines_it++); + + EXPECT_EQ(boundary + "--", *lines_it++); + + EXPECT_EQ(lines.end(), lines_it); +} + +TEST(HTTPMultipartBuilderDeathTest, AssertUnsafeMIMEType) { + HTTPMultipartBuilder builder; + // Invalid and potentially dangerous: + ASSERT_DEATH_CHECK( + builder.SetFileAttachment("", "", base::FilePath(), "\r\n"), ""); + ASSERT_DEATH_CHECK( + builder.SetFileAttachment("", "", base::FilePath(), "\""), ""); + ASSERT_DEATH_CHECK( + builder.SetFileAttachment("", "", base::FilePath(), "\x12"), ""); + ASSERT_DEATH_CHECK( + builder.SetFileAttachment("", "", base::FilePath(), "<>"), ""); + // Invalid but safe: + builder.SetFileAttachment("", "", base::FilePath(), "0/totally/-invalid.pdf"); + // Valid and safe: + builder.SetFileAttachment("", "", base::FilePath(), "application/xml+xhtml"); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_transport.cc b/third_party/crashpad/crashpad/util/net/http_transport.cc new file mode 100644 index 0000000..07b0b34 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_transport.cc
@@ -0,0 +1,54 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_transport.h" + +#include "util/stdlib/move.h" +#include "util/net/http_body.h" + +namespace crashpad { + +HTTPTransport::HTTPTransport() + : url_(), + method_("POST"), + headers_(), + body_stream_(), + timeout_(15.0) { +} + +HTTPTransport::~HTTPTransport() { +} + +void HTTPTransport::SetURL(const std::string& url) { + url_ = url; +} + +void HTTPTransport::SetMethod(const std::string& method) { + method_ = method; +} + +void HTTPTransport::SetHeader(const std::string& header, + const std::string& value) { + headers_[header] = value; +} + +void HTTPTransport::SetBodyStream(scoped_ptr<HTTPBodyStream> stream) { + body_stream_ = crashpad::move(stream); +} + +void HTTPTransport::SetTimeout(double timeout) { + timeout_ = timeout; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_transport.h b/third_party/crashpad/crashpad/util/net/http_transport.h new file mode 100644 index 0000000..333986a --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_transport.h
@@ -0,0 +1,106 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_ +#define CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "util/net/http_headers.h" + +namespace crashpad { + +class HTTPBodyStream; + +//! \brief HTTPTransport executes a HTTP request using the specified URL, HTTP +//! method, headers, and body. This class can only issue a synchronous +//! HTTP request. +//! +//! This class cannot be instantiated directly. A concrete subclass must be +//! instantiated instead, which provides an implementation to execute the +//! request that is appropriate for the host operating system. +class HTTPTransport { + public: + virtual ~HTTPTransport(); + + //! \brief Instantiates a concrete HTTPTransport class for the current + //! operating system. + //! + //! \return A new caller-owned HTTPTransport object. + static scoped_ptr<HTTPTransport> Create(); + + //! \brief Sets URL to which the request will be made. + //! + //! \param[in] url The request URL. + void SetURL(const std::string& url); + + //! \brief Sets the HTTP method to execute. E.g., GET, POST, etc. The default + //! method is `"POST"`. + //! + //! \param[in] http_method The HTTP method. + void SetMethod(const std::string& http_method); + + //! \brief Sets a HTTP header-value pair. + //! + //! \param[in] header The HTTP header name. Any previous value set at this + //! name will be overwritten. + //! \param[in] value The value to set for the header. + void SetHeader(const std::string& header, const std::string& value); + + //! \brief Sets the stream object from which to generate the HTTP body. + //! + //! \param[in] stream A HTTPBodyStream, of which this class will take + //! ownership. + void SetBodyStream(scoped_ptr<HTTPBodyStream> stream); + + //! \brief Sets the timeout for the HTTP request. The default is 15 seconds. + //! + //! \param[in] timeout The request timeout, in seconds. + void SetTimeout(double timeout); + + //! \brief Performs the HTTP request with the configured parameters and waits + //! for the execution to complete. + //! + //! \param[out] response On success, this will be set to the HTTP response + //! body. This parameter is optional and may be set to `nullptr` if the + //! response body is not required. + //! + //! \return Whether or not the request was successful, defined as returning + //! a HTTP status 200 (OK) code. + virtual bool ExecuteSynchronously(std::string* response_body) = 0; + + protected: + HTTPTransport(); + + const std::string& url() const { return url_; } + const std::string& method() const { return method_; } + const HTTPHeaders& headers() const { return headers_; } + HTTPBodyStream* body_stream() const { return body_stream_.get(); } + double timeout() const { return timeout_; } + + private: + std::string url_; + std::string method_; + HTTPHeaders headers_; + scoped_ptr<HTTPBodyStream> body_stream_; + double timeout_; + + DISALLOW_COPY_AND_ASSIGN(HTTPTransport); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_mac.mm b/third_party/crashpad/crashpad/util/net/http_transport_mac.mm new file mode 100644 index 0000000..aad505d --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_transport_mac.mm
@@ -0,0 +1,234 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_transport.h" + +#include <CoreFoundation/CoreFoundation.h> +#import <Foundation/Foundation.h> + +#include "base/mac/foundation_util.h" +#import "base/mac/scoped_nsobject.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "third_party/apple_cf/CFStreamAbstract.h" +#include "util/file/file_io.h" +#include "util/misc/implicit_cast.h" +#include "util/net/http_body.h" + +namespace crashpad { + +namespace { + +// An implementation of CFReadStream. This implements the V0 callback +// scheme. +class HTTPBodyStreamCFReadStream { + public: + explicit HTTPBodyStreamCFReadStream(HTTPBodyStream* body_stream) + : body_stream_(body_stream) { + } + + // Creates a new NSInputStream, which the caller owns. + NSInputStream* CreateInputStream() { + CFStreamClientContext context = { + .version = 0, + .info = this, + .retain = nullptr, + .release = nullptr, + .copyDescription = nullptr + }; + const CFReadStreamCallBacksV0 callbacks = { + .version = 0, + .open = &Open, + .openCompleted = &OpenCompleted, + .read = &Read, + .getBuffer = &GetBuffer, + .canRead = &CanRead, + .close = &Close, + .copyProperty = &CopyProperty, + .schedule = &Schedule, + .unschedule = &Unschedule + }; + CFReadStreamRef read_stream = CFReadStreamCreate(nullptr, + reinterpret_cast<const CFReadStreamCallBacks*>(&callbacks), &context); + return base::mac::CFToNSCast(read_stream); + } + + private: + static HTTPBodyStream* GetStream(void* info) { + return static_cast<HTTPBodyStreamCFReadStream*>(info)->body_stream_; + } + + static Boolean Open(CFReadStreamRef stream, + CFStreamError* error, + Boolean* open_complete, + void* info) { + *open_complete = TRUE; + return TRUE; + } + + static Boolean OpenCompleted(CFReadStreamRef stream, + CFStreamError* error, + void* info) { + return TRUE; + } + + static CFIndex Read(CFReadStreamRef stream, + UInt8* buffer, + CFIndex buffer_length, + CFStreamError* error, + Boolean* at_eof, + void* info) { + if (buffer_length == 0) { + *at_eof = FALSE; + return 0; + } + + FileOperationResult bytes_read = + GetStream(info)->GetBytesBuffer(buffer, buffer_length); + if (bytes_read < 0) { + error->error = -1; + error->domain = kCFStreamErrorDomainCustom; + } else { + *at_eof = bytes_read == 0; + } + + return bytes_read; + } + + static const UInt8* GetBuffer(CFReadStreamRef stream, + CFIndex max_bytes_to_read, + CFIndex* num_bytes_read, + CFStreamError* error, + Boolean* at_eof, + void* info) { + return nullptr; + } + + static Boolean CanRead(CFReadStreamRef stream, void* info) { + return TRUE; + } + + static void Close(CFReadStreamRef stream, void* info) {} + + static CFTypeRef CopyProperty(CFReadStreamRef stream, + CFStringRef property_name, + void* info) { + return nullptr; + } + + static void Schedule(CFReadStreamRef stream, + CFRunLoopRef run_loop, + CFStringRef run_loop_mode, + void* info) {} + + static void Unschedule(CFReadStreamRef stream, + CFRunLoopRef run_loop, + CFStringRef run_loop_mode, + void* info) {} + + HTTPBodyStream* body_stream_; // weak + + DISALLOW_COPY_AND_ASSIGN(HTTPBodyStreamCFReadStream); +}; + +class HTTPTransportMac final : public HTTPTransport { + public: + HTTPTransportMac(); + ~HTTPTransportMac() override; + + bool ExecuteSynchronously(std::string* response_body) override; + + private: + DISALLOW_COPY_AND_ASSIGN(HTTPTransportMac); +}; + +HTTPTransportMac::HTTPTransportMac() : HTTPTransport() { +} + +HTTPTransportMac::~HTTPTransportMac() { +} + +bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) { + DCHECK(body_stream()); + + @autoreleasepool { + NSString* url_ns_string = base::SysUTF8ToNSString(url()); + NSURL* url = [NSURL URLWithString:url_ns_string]; + NSMutableURLRequest* request = + [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:timeout()]; + [request setHTTPMethod:base::SysUTF8ToNSString(method())]; + + for (const auto& pair : headers()) { + [request setValue:base::SysUTF8ToNSString(pair.second) + forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)]; + } + + HTTPBodyStreamCFReadStream body_stream_cf(body_stream()); + base::scoped_nsobject<NSInputStream> input_stream( + body_stream_cf.CreateInputStream()); + [request setHTTPBodyStream:input_stream.get()]; + + NSURLResponse* response = nil; + NSError* error = nil; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // Deprecated in OS X 10.11. The suggested replacement, NSURLSession, is + // only available on 10.9 and later, and this needs to run on earlier + // releases. + NSData* body = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; +#pragma clang diagnostic pop + + if (error) { + LOG(ERROR) << [[error localizedDescription] UTF8String] << " (" + << [[error domain] UTF8String] << " " << [error code] << ")"; + return false; + } + if (!response) { + LOG(ERROR) << "no response"; + return false; + } + NSHTTPURLResponse* http_response = + base::mac::ObjCCast<NSHTTPURLResponse>(response); + if (!http_response) { + LOG(ERROR) << "no http_response"; + return false; + } + NSInteger http_status = [http_response statusCode]; + if (http_status != 200) { + LOG(ERROR) << base::StringPrintf("HTTP status %ld", + implicit_cast<long>(http_status)); + return false; + } + + if (response_body) { + response_body->assign(static_cast<const char*>([body bytes]), + [body length]); + } + + return true; + } +} + +} // namespace + +// static +scoped_ptr<HTTPTransport> HTTPTransport::Create() { + return scoped_ptr<HTTPTransport>(new HTTPTransportMac()); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_test.cc b/third_party/crashpad/crashpad/util/net/http_transport_test.cc new file mode 100644 index 0000000..b59b17fd --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_transport_test.cc
@@ -0,0 +1,314 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_transport.h" + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <vector> + +#include "base/files/file_path.h" +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/paths.h" +#include "util/file/file_io.h" +#include "util/stdlib/move.h" +#include "util/misc/random_string.h" +#include "util/net/http_body.h" +#include "util/net/http_headers.h" +#include "util/net/http_multipart_builder.h" + +namespace crashpad { +namespace test { +namespace { + +class HTTPTransportTestFixture : public MultiprocessExec { + public: + using RequestValidator = + void(*)(HTTPTransportTestFixture*, const std::string&); + + HTTPTransportTestFixture(const HTTPHeaders& headers, + scoped_ptr<HTTPBodyStream> body_stream, + uint16_t http_response_code, + RequestValidator request_validator) + : MultiprocessExec(), + headers_(headers), + body_stream_(crashpad::move(body_stream)), + response_code_(http_response_code), + request_validator_(request_validator) { + base::FilePath server_path = Paths::TestDataRoot().Append( + FILE_PATH_LITERAL("util/net/http_transport_test_server.py")); +#if defined(OS_POSIX) + SetChildCommand(server_path.value(), nullptr); +#elif defined(OS_WIN) + // Explicitly invoke a shell and python so that python can be found in the + // path, and run the test script. + std::vector<std::string> args; + args.push_back("/c"); + args.push_back("python"); + args.push_back(base::UTF16ToUTF8(server_path.value())); + SetChildCommand(getenv("COMSPEC"), &args); +#endif // OS_POSIX + } + + const HTTPHeaders& headers() { return headers_; } + + private: + void MultiprocessParent() override { + // Use Logging*File() instead of Checked*File() so that the test can fail + // gracefully with a gtest assertion if the child does not execute properly. + + // The child will write the HTTP server port number as a packed unsigned + // short to stdout. + uint16_t port; + ASSERT_TRUE(LoggingReadFile(ReadPipeHandle(), &port, sizeof(port))); + + // Then the parent will tell the web server what response code to send + // for the HTTP request. + ASSERT_TRUE(LoggingWriteFile( + WritePipeHandle(), &response_code_, sizeof(response_code_))); + + // The parent will also tell the web server what response body to send back. + // The web server will only send the response body if the response code is + // 200. + const std::string random_string = RandomString(); + + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), + random_string.c_str(), + random_string.size())); + + // Now execute the HTTP request. + scoped_ptr<HTTPTransport> transport(HTTPTransport::Create()); + transport->SetMethod("POST"); + transport->SetURL(base::StringPrintf("http://127.0.0.1:%d/upload", port)); + for (const auto& pair : headers_) { + transport->SetHeader(pair.first, pair.second); + } + transport->SetBodyStream(crashpad::move(body_stream_)); + + std::string response_body; + bool success = transport->ExecuteSynchronously(&response_body); + if (response_code_ == 200) { + EXPECT_TRUE(success); + std::string expect_response_body = random_string + "\r\n"; + EXPECT_EQ(expect_response_body, response_body); + } else { + EXPECT_FALSE(success); + EXPECT_TRUE(response_body.empty()); + } + + // Read until the child's stdout closes. + std::string request; + char buf[32]; + FileOperationResult bytes_read; + while ((bytes_read = ReadFile(ReadPipeHandle(), buf, sizeof(buf))) != 0) { + ASSERT_GE(bytes_read, 0); + request.append(buf, bytes_read); + } + + if (request_validator_) + request_validator_(this, request); + } + + HTTPHeaders headers_; + scoped_ptr<HTTPBodyStream> body_stream_; + uint16_t response_code_; + RequestValidator request_validator_; +}; + +const char kMultipartFormData[] = "multipart/form-data"; + +void GetHeaderField(const std::string& request, + const std::string& header, + std::string* value) { + size_t index = request.find(header); + ASSERT_NE(std::string::npos, index); + // Since the header is never the first line of the request, it should always + // be preceded by a CRLF. + EXPECT_EQ('\n', request[index - 1]); + EXPECT_EQ('\r', request[index - 2]); + + index += header.length(); + EXPECT_EQ(':', request[index++]); + // Per RFC 7230 §3.2, there can be one or more spaces or horizontal tabs. + // For testing purposes, just assume one space. + EXPECT_EQ(' ', request[index++]); + + size_t header_end = request.find('\r', index); + ASSERT_NE(std::string::npos, header_end); + + *value = request.substr(index, header_end - index); +} + +void GetMultipartBoundary(const std::string& request, + std::string* multipart_boundary) { + std::string content_type; + GetHeaderField(request, kContentType, &content_type); + + ASSERT_GE(content_type.length(), strlen(kMultipartFormData)); + size_t index = strlen(kMultipartFormData); + EXPECT_EQ(kMultipartFormData, content_type.substr(0, index)); + + EXPECT_EQ(';', content_type[index++]); + + size_t boundary_begin = content_type.find('=', index); + ASSERT_NE(std::string::npos, boundary_begin); + EXPECT_EQ('=', content_type[boundary_begin++]); + if (multipart_boundary) { + *multipart_boundary = content_type.substr(boundary_begin); + } +} + +const char kBoundaryEq[] = "boundary="; + +void ValidFormData(HTTPTransportTestFixture* fixture, + const std::string& request) { + std::string actual_boundary; + GetMultipartBoundary(request, &actual_boundary); + + const auto& content_type = fixture->headers().find(kContentType); + ASSERT_NE(fixture->headers().end(), content_type); + + size_t boundary = content_type->second.find(kBoundaryEq); + ASSERT_NE(std::string::npos, boundary); + std::string expected_boundary = + content_type->second.substr(boundary + strlen(kBoundaryEq)); + EXPECT_EQ(expected_boundary, actual_boundary); + + size_t body_start = request.find("\r\n\r\n"); + ASSERT_NE(std::string::npos, body_start); + body_start += 4; + + std::string expected = "--" + expected_boundary + "\r\n"; + expected += "Content-Disposition: form-data; name=\"key1\"\r\n\r\n"; + expected += "test\r\n"; + ASSERT_LT(body_start + expected.length(), request.length()); + EXPECT_EQ(expected, request.substr(body_start, expected.length())); + + body_start += expected.length(); + + expected = "--" + expected_boundary + "\r\n"; + expected += "Content-Disposition: form-data; name=\"key2\"\r\n\r\n"; + expected += "--abcdefg123\r\n"; + expected += "--" + expected_boundary + "--\r\n"; + ASSERT_EQ(body_start + expected.length(), request.length()); + EXPECT_EQ(expected, request.substr(body_start)); +} + +TEST(HTTPTransport, ValidFormData) { + HTTPMultipartBuilder builder; + builder.SetFormData("key1", "test"); + builder.SetFormData("key2", "--abcdefg123"); + + HTTPHeaders headers; + EXPECT_TRUE(headers.insert(builder.GetContentType()).second); + + HTTPTransportTestFixture test(headers, builder.GetBodyStream(), 200, + &ValidFormData); + test.Run(); +} + +const char kTextPlain[] = "text/plain"; + +void ErrorResponse(HTTPTransportTestFixture* fixture, + const std::string& request) { + std::string content_type; + GetHeaderField(request, kContentType, &content_type); + EXPECT_EQ(kTextPlain, content_type); +} + +TEST(HTTPTransport, ErrorResponse) { + HTTPMultipartBuilder builder; + HTTPHeaders headers; + headers[kContentType] = kTextPlain; + HTTPTransportTestFixture test(headers, builder.GetBodyStream(), + 404, &ErrorResponse); + test.Run(); +} + +const char kTextBody[] = "hello world"; + +void UnchunkedPlainText(HTTPTransportTestFixture* fixture, + const std::string& request) { + std::string header_value; + GetHeaderField(request, kContentType, &header_value); + EXPECT_EQ(kTextPlain, header_value); + + GetHeaderField(request, kContentLength, &header_value); + const auto& content_length = fixture->headers().find(kContentLength); + ASSERT_NE(fixture->headers().end(), content_length); + EXPECT_EQ(content_length->second, header_value); + + size_t body_start = request.rfind("\r\n"); + ASSERT_NE(std::string::npos, body_start); + + EXPECT_EQ(kTextBody, request.substr(body_start + 2)); +} + +TEST(HTTPTransport, UnchunkedPlainText) { + scoped_ptr<HTTPBodyStream> body_stream(new StringHTTPBodyStream(kTextBody)); + + HTTPHeaders headers; + headers[kContentType] = kTextPlain; + headers[kContentLength] = base::StringPrintf("%" PRIuS, strlen(kTextBody)); + + HTTPTransportTestFixture test(headers, crashpad::move(body_stream), 200, + &UnchunkedPlainText); + test.Run(); +} + +void RunUpload33k(bool has_content_length) { + // On OS X, NSMutableURLRequest winds up calling into a CFReadStream’s Read() + // callback with a 32kB buffer. Make sure that it’s able to get everything + // when enough is available to fill this buffer, requiring more than one + // Read(). + + std::string request_string(33 * 1024, 'a'); + scoped_ptr<HTTPBodyStream> body_stream( + new StringHTTPBodyStream(request_string)); + + HTTPHeaders headers; + headers[kContentType] = "application/octet-stream"; + if (has_content_length) { + headers[kContentLength] = + base::StringPrintf("%" PRIuS, request_string.size()); + } + HTTPTransportTestFixture test(headers, crashpad::move(body_stream), 200, + [](HTTPTransportTestFixture* fixture, const std::string& request) { + size_t body_start = request.rfind("\r\n"); + EXPECT_EQ(33 * 1024u + 2, request.size() - body_start); + }); + test.Run(); +} + +TEST(HTTPTransport, Upload33k) { + RunUpload33k(true); +} + +TEST(HTTPTransport, Upload33k_LengthUnknown) { + // The same as Upload33k, but without declaring Content-Length ahead of time. + RunUpload33k(false); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_test_server.py b/third_party/crashpad/crashpad/util/net/http_transport_test_server.py new file mode 100755 index 0000000..3f085a5 --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_transport_test_server.py
@@ -0,0 +1,159 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +"""A one-shot testing webserver. + +When invoked, this server will write a short integer to stdout, indiciating on +which port the server is listening. It will then read one integer from stdin, +indiciating the response code to be sent in response to a request. It also reads +16 characters from stdin, which, after having "\r\n" appended, will form the +response body in a successful response (one with code 200). The server will +process one HTTP request, deliver the prearranged response to the client, and +write the entire request to stdout. It will then terminate. + +This server is written in Python since it provides a simple HTTP stack, and +because parsing Chunked encoding is safer and easier in a memory-safe language. +This could easily have been written in C++ instead. +""" + +import BaseHTTPServer +import struct +import sys + +class BufferedReadFile(object): + """A File-like object that stores all read contents into a buffer.""" + + def __init__(self, real_file): + self.file = real_file + self.buffer = "" + + def read(self, size=-1): + buf = self.file.read(size) + self.buffer += buf + return buf + + def readline(self, size=-1): + buf = self.file.readline(size) + self.buffer += buf + return buf + + def flush(self): + self.file.flush() + + def close(self): + self.file.close() + + +class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + # Everything to be written to stdout is collected into this string. It can’t + # be written to stdout until after the HTTP transaction is complete, because + # stdout is a pipe being read by a test program that’s also the HTTP client. + # The test program expects to complete the entire HTTP transaction before it + # even starts reading this script’s stdout. If the stdout pipe buffer fills up + # during an HTTP transaction, deadlock would result. + raw_request = '' + + response_code = 500 + response_body = '' + + def handle_one_request(self): + # Wrap the rfile in the buffering file object so that the raw header block + # can be written to stdout after it is parsed. + self.rfile = BufferedReadFile(self.rfile) + BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self) + + def do_POST(self): + RequestHandler.raw_request = self.rfile.buffer + self.rfile.buffer = '' + + if self.headers.get('Transfer-Encoding', '') == 'Chunked': + body = self.handle_chunked_encoding() + else: + length = int(self.headers.get('Content-Length', -1)) + body = self.rfile.read(length) + + RequestHandler.raw_request += body + + self.send_response(self.response_code) + self.end_headers() + if self.response_code == 200: + self.wfile.write(self.response_body) + self.wfile.write('\r\n') + + def handle_chunked_encoding(self): + """This parses a "Transfer-Encoding: Chunked" body in accordance with + RFC 7230 §4.1. This returns the result as a string. + """ + body = '' + chunk_size = self.read_chunk_size() + while chunk_size > 0: + # Read the body. + data = self.rfile.read(chunk_size) + chunk_size -= len(data) + body += data + + # Finished reading this chunk. + if chunk_size == 0: + # Read through any trailer fields. + trailer_line = self.rfile.readline() + while trailer_line.strip() != '': + trailer_line = self.rfile.readline() + + # Read the chunk size. + chunk_size = self.read_chunk_size() + return body + + def read_chunk_size(self): + # Read the whole line, including the \r\n. + chunk_size_and_ext_line = self.rfile.readline() + # Look for a chunk extension. + chunk_size_end = chunk_size_and_ext_line.find(';') + if chunk_size_end == -1: + # No chunk extensions; just encounter the end of line. + chunk_size_end = chunk_size_and_ext_line.find('\r') + if chunk_size_end == -1: + self.send_response(400) # Bad request. + return -1 + return int(chunk_size_and_ext_line[:chunk_size_end], base=16) + + +def Main(): + if sys.platform == 'win32': + import os, msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + # Start the server. + server = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), RequestHandler) + + # Write the port as an unsigned short to the parent process. + sys.stdout.write(struct.pack('=H', server.server_address[1])) + sys.stdout.flush() + + # Read the desired test response code as an unsigned short and the desired + # response body as a 16-byte string from the parent process. + RequestHandler.response_code, RequestHandler.response_body = \ + struct.unpack('=H16s', sys.stdin.read(struct.calcsize('=H16s'))) + + # Handle the request. + server.handle_request() + + # Share the entire request with the test program, which will validate it. + sys.stdout.write(RequestHandler.raw_request) + sys.stdout.flush() + +if __name__ == '__main__': + Main()
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_win.cc b/third_party/crashpad/crashpad/util/net/http_transport_win.cc new file mode 100644 index 0000000..9c3d00f --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/http_transport_win.cc
@@ -0,0 +1,250 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/net/http_transport.h" + +#include <windows.h> +#include <winhttp.h> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/scoped_generic.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "package.h" +#include "util/file/file_io.h" +#include "util/net/http_body.h" + +namespace crashpad { + +namespace { + +// PLOG doesn't work for messages from WinHTTP, so we need to use +// FORMAT_MESSAGE_FROM_HMODULE + the dll name manually here. +void LogErrorWinHttpMessage(const char* extra) { + DWORD error_code = GetLastError(); + char msgbuf[256]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE; + DWORD len = FormatMessageA(flags, + GetModuleHandle(L"winhttp.dll"), + error_code, + 0, + msgbuf, + arraysize(msgbuf), + NULL); + if (len) { + LOG(ERROR) << extra << ": " << msgbuf + << base::StringPrintf(" (0x%X)", error_code); + } else { + LOG(ERROR) << base::StringPrintf( + "Error (0x%X) while retrieving error. (0x%X)", + GetLastError(), + error_code); + } +} + +struct ScopedHINTERNETTraits { + static HINTERNET InvalidValue() { + return nullptr; + } + static void Free(HINTERNET handle) { + if (handle) { + if (!WinHttpCloseHandle(handle)) { + LogErrorWinHttpMessage("WinHttpCloseHandle"); + } + } + } +}; + +using ScopedHINTERNET = base::ScopedGeneric<HINTERNET, ScopedHINTERNETTraits>; + +class HTTPTransportWin final : public HTTPTransport { + public: + HTTPTransportWin(); + ~HTTPTransportWin() override; + + bool ExecuteSynchronously(std::string* response_body) override; + + private: + DISALLOW_COPY_AND_ASSIGN(HTTPTransportWin); +}; + +HTTPTransportWin::HTTPTransportWin() : HTTPTransport() { +} + +HTTPTransportWin::~HTTPTransportWin() { +} + +bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { + ScopedHINTERNET session( + WinHttpOpen(base::UTF8ToUTF16(PACKAGE_NAME "/" PACKAGE_VERSION).c_str(), + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0)); + if (!session.get()) { + LogErrorWinHttpMessage("WinHttpOpen"); + return false; + } + + int timeout_in_ms = static_cast<int>(timeout() * 1000); + if (!WinHttpSetTimeouts(session.get(), + timeout_in_ms, + timeout_in_ms, + timeout_in_ms, + timeout_in_ms)) { + LogErrorWinHttpMessage("WinHttpSetTimeouts"); + return false; + } + + URL_COMPONENTS url_components = {0}; + url_components.dwStructSize = sizeof(URL_COMPONENTS); + url_components.dwHostNameLength = 1; + url_components.dwUrlPathLength = 1; + url_components.dwExtraInfoLength = 1; + std::wstring url_wide(base::UTF8ToUTF16(url())); + // dwFlags = ICU_REJECT_USERPWD fails on XP. See "Community Additions" at: + // https://msdn.microsoft.com/en-us/library/aa384092.aspx + if (!WinHttpCrackUrl( + url_wide.c_str(), 0, 0, &url_components)) { + LogErrorWinHttpMessage("WinHttpCrackUrl"); + return false; + } + DCHECK(url_components.nScheme == INTERNET_SCHEME_HTTP || + url_components.nScheme == INTERNET_SCHEME_HTTPS); + std::wstring host_name(url_components.lpszHostName, + url_components.dwHostNameLength); + std::wstring url_path(url_components.lpszUrlPath, + url_components.dwUrlPathLength); + std::wstring extra_info(url_components.lpszExtraInfo, + url_components.dwExtraInfoLength); + + ScopedHINTERNET connect(WinHttpConnect( + session.get(), host_name.c_str(), url_components.nPort, 0)); + if (!connect.get()) { + LogErrorWinHttpMessage("WinHttpConnect"); + return false; + } + + ScopedHINTERNET request(WinHttpOpenRequest( + connect.get(), + base::UTF8ToUTF16(method()).c_str(), + url_path.c_str(), + nullptr, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + url_components.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE + : 0)); + if (!request.get()) { + LogErrorWinHttpMessage("WinHttpOpenRequest"); + return false; + } + + // Add headers to the request. + for (const auto& pair : headers()) { + std::wstring header_string = + base::UTF8ToUTF16(pair.first) + L": " + base::UTF8ToUTF16(pair.second); + if (!WinHttpAddRequestHeaders( + request.get(), + header_string.c_str(), + base::checked_cast<DWORD>(header_string.size()), + WINHTTP_ADDREQ_FLAG_ADD)) { + LogErrorWinHttpMessage("WinHttpAddRequestHeaders"); + return false; + } + } + + // We need the Content-Length up front, so buffer in memory. We should modify + // the interface to not require this, and then use WinHttpWriteData after + // WinHttpSendRequest. + std::vector<uint8_t> post_data; + + // Write the body of a POST if any. + const size_t kBufferSize = 4096; + for (;;) { + uint8_t buffer[kBufferSize]; + FileOperationResult bytes_to_write = + body_stream()->GetBytesBuffer(buffer, sizeof(buffer)); + if (bytes_to_write == 0) + break; + post_data.insert(post_data.end(), buffer, buffer + bytes_to_write); + } + + if (!WinHttpSendRequest(request.get(), + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + &post_data[0], + base::checked_cast<DWORD>(post_data.size()), + base::checked_cast<DWORD>(post_data.size()), + 0)) { + LogErrorWinHttpMessage("WinHttpSendRequest"); + return false; + } + + if (!WinHttpReceiveResponse(request.get(), nullptr)) { + LogErrorWinHttpMessage("WinHttpReceiveResponse"); + return false; + } + + DWORD status_code = 0; + DWORD sizeof_status_code = sizeof(status_code); + + if (!WinHttpQueryHeaders( + request.get(), + WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, + &status_code, + &sizeof_status_code, + WINHTTP_NO_HEADER_INDEX)) { + LogErrorWinHttpMessage("WinHttpQueryHeaders"); + return false; + } + + if (status_code != 200) { + LOG(ERROR) << base::StringPrintf("HTTP status %d", status_code); + return false; + } + + if (response_body) { + response_body->clear(); + + // There isn’t any reason to call WinHttpQueryDataAvailable(), because it + // returns the number of bytes available to be read without blocking at the + // time of the call, not the number of bytes until end-of-file. This method, + // which executes synchronously, is only concerned with reading until EOF. + DWORD bytes_read = 0; + do { + char read_buffer[kBufferSize]; + if (!WinHttpReadData( + request.get(), read_buffer, sizeof(read_buffer), &bytes_read)) { + LogErrorWinHttpMessage("WinHttpReadData"); + return false; + } + + response_body->append(read_buffer, bytes_read); + } while (bytes_read > 0); + } + + return true; +} + +} // namespace + +// static +scoped_ptr<HTTPTransport> HTTPTransport::Create() { + return scoped_ptr<HTTPTransportWin>(new HTTPTransportWin); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/net/testdata/ascii_http_body.txt b/third_party/crashpad/crashpad/util/net/testdata/ascii_http_body.txt new file mode 100644 index 0000000..484ba93e --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/testdata/ascii_http_body.txt
@@ -0,0 +1 @@ +This is a test.
diff --git a/third_party/crashpad/crashpad/util/net/testdata/binary_http_body.dat b/third_party/crashpad/crashpad/util/net/testdata/binary_http_body.dat new file mode 100644 index 0000000..d88b81a --- /dev/null +++ b/third_party/crashpad/crashpad/util/net/testdata/binary_http_body.dat
@@ -0,0 +1 @@ +þíúΡ \ No newline at end of file
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc b/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc new file mode 100644 index 0000000..dcbde23 --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc
@@ -0,0 +1,120 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/numeric/checked_address_range.h" + +#if defined(OS_MACOSX) +#include <mach/mach.h> +#elif defined(OS_WIN) +#include "util/win/address_types.h" +#endif // OS_MACOSX + +namespace crashpad { +namespace internal { + +template <class ValueType, class SizeType> +CheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric() + : range_32_(0, 0), +#if defined(COMPILER_MSVC) + range_64_(0, 0), +#endif // COMPILER_MSVC + is_64_bit_(false), + range_ok_(true) { +} + +template <class ValueType, class SizeType> +CheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric( + bool is_64_bit, + ValueType base, + SizeType size) +#if defined(COMPILER_MSVC) + : range_32_(0, 0), + range_64_(0, 0) +#endif // COMPILER_MSVC +{ + SetRange(is_64_bit, base, size); +} + +template <class ValueType, class SizeType> +ValueType CheckedAddressRangeGeneric<ValueType, SizeType>::Base() const { + return is_64_bit_ ? range_64_.base() : range_32_.base(); +} + +template <class ValueType, class SizeType> +SizeType CheckedAddressRangeGeneric<ValueType, SizeType>::Size() const { + return is_64_bit_ ? range_64_.size() : range_32_.size(); +} + +template <class ValueType, class SizeType> +ValueType CheckedAddressRangeGeneric<ValueType, SizeType>::End() const { + return is_64_bit_ ? range_64_.end() : range_32_.end(); +} + +template <class ValueType, class SizeType> +bool CheckedAddressRangeGeneric<ValueType, SizeType>::IsValid() const { + return range_ok_ && (is_64_bit_ ? range_64_.IsValid() : range_32_.IsValid()); +} + +template <class ValueType, class SizeType> +void CheckedAddressRangeGeneric<ValueType, SizeType>::SetRange(bool is_64_bit, + ValueType base, + SizeType size) { + is_64_bit_ = is_64_bit; + if (is_64_bit_) { + range_64_.SetRange(base, size); + range_ok_ = true; + } else { + range_32_.SetRange(static_cast<uint32_t>(base), + static_cast<uint32_t>(size)); + range_ok_ = base::IsValueInRangeForNumericType<uint32_t>(base) && + base::IsValueInRangeForNumericType<uint32_t>(size); + } +} + +template <class ValueType, class SizeType> +bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsValue( + ValueType value) const { + DCHECK(range_ok_); + + if (is_64_bit_) { + return range_64_.ContainsValue(value); + } + + if (!base::IsValueInRangeForNumericType<uint32_t>(value)) { + return false; + } + + return range_32_.ContainsValue(static_cast<uint32_t>(value)); +} + +template <class ValueType, class SizeType> +bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsRange( + const CheckedAddressRangeGeneric& that) const { + DCHECK_EQ(is_64_bit_, that.is_64_bit_); + DCHECK(range_ok_); + DCHECK(that.range_ok_); + + return is_64_bit_ ? range_64_.ContainsRange(that.range_64_) + : range_32_.ContainsRange(that.range_32_); +} + +// Explicit instantiations for the cases we use. +#if defined(OS_MACOSX) +template class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>; +#elif defined(OS_WIN) +template class CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>; +#endif // OS_MACOSX + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_address_range.h b/third_party/crashpad/crashpad/util/numeric/checked_address_range.h new file mode 100644 index 0000000..e9514bb --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/checked_address_range.h
@@ -0,0 +1,144 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_ +#define CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_ + +#include <stdint.h> + +#include "build/build_config.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { +namespace internal { + +//! \brief Ensures that a range, composed of a base and a size, does not +//! overflow the pointer type of the process it describes a range in. +//! +//! This class checks bases of type `ValueType` and sizes of type `SizeType` +//! against a process whose pointer type is either 32 or 64 bits wide. +//! +//! Aside from varying the overall range on the basis of a process’ pointer type +//! width, this class functions very similarly to CheckedRange. +//! +//! \sa CheckedMachAddressRange +template <class ValueType, class SizeType> +class CheckedAddressRangeGeneric { + public: + //! \brief Initializes a default range. + //! + //! The default range has base 0, size 0, and appears to be from a 32-bit + //! process. + CheckedAddressRangeGeneric(); + + //! \brief Initializes a range. + //! + //! See SetRange(). + CheckedAddressRangeGeneric(bool is_64_bit, ValueType base, SizeType size); + + //! \brief Sets a range’s fields. + //! + //! \param[in] is_64_bit `true` if \a base and \a size refer to addresses in a + //! 64-bit process; `false` if they refer to addresses in a 32-bit + //! process. + //! \param[in] base The range’s base address. + //! \param[in] size The range’s size. + void SetRange(bool is_64_bit, ValueType base, SizeType size); + + //! \brief The range’s base address. + ValueType Base() const; + + //! \brief The range’s size. + SizeType Size() const; + + //! \brief The range’s end address (its base address plus its size). + ValueType End() const; + + //! \brief Returns the validity of the address range. + //! + //! \return `true` if the address range is valid, `false` otherwise. + //! + //! An address range is valid if its size can be converted to the address + //! range’s data type without data loss, and if its end (base plus size) can + //! be computed without overflowing its data type. + bool IsValid() const; + + //! \brief Returns whether this range refers to a 64-bit process. + bool Is64Bit() const { return is_64_bit_; } + + //! \brief Returns whether the address range contains another address. + //! + //! \param[in] value The (possibly) contained address. + //! + //! \return `true` if the address range contains \a value, `false` otherwise. + //! + //! An address range contains a value if the value is greater than or equal to + //! its base address, and less than its end address (base address plus size). + //! + //! This method must only be called if IsValid() would return `true`. + bool ContainsValue(const ValueType value) const; + + //! \brief Returns whether the address range contains another address range. + //! + //! \param[in] that The (possibly) contained address range. + //! + //! \return `true` if `this` address range, the containing address range, + //! contains \a that, the contained address range. `false` otherwise. + //! + //! An address range contains another address range when the contained address + //! range’s base is greater than or equal to the containing address range’s + //! base, and the contained address range’s end is less than or equal to the + //! containing address range’s end. + //! + //! This method should only be called on two CheckedAddressRangeGeneric + //! objects representing address ranges in the same process. + //! + //! This method must only be called if IsValid() would return `true` for both + //! CheckedAddressRangeGeneric objects involved. + bool ContainsRange(const CheckedAddressRangeGeneric& that) const; + + private: +#if defined(COMPILER_MSVC) + // MSVC cannot handle a union containing CheckedRange (with constructor, etc.) + // currently. + CheckedRange<uint32_t> range_32_; + CheckedRange<uint64_t> range_64_; +#else + // The field of the union that is expressed is determined by is_64_bit_. + union { + CheckedRange<uint32_t> range_32_; + CheckedRange<uint64_t> range_64_; + }; +#endif + + // Determines which field of the union is expressed. + bool is_64_bit_; + + // Whether the base and size were valid for their data type when set. This is + // always true when is_64_bit_ is true because the underlying data types are + // 64 bits wide and there is no possibility for range and size to overflow. + // When is_64_bit_ is false, range_ok_ will be false if SetRange() was passed + // a base or size that overflowed the underlying 32-bit data type. This field + // is necessary because the interface exposes the address and size types + // uniformly, but these types are too wide for the underlying pointer and size + // types in 32-bit processes. + bool range_ok_; + + DISALLOW_COPY_AND_ASSIGN(CheckedAddressRangeGeneric); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc b/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc new file mode 100644 index 0000000..d902387c --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
@@ -0,0 +1,260 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/numeric/checked_address_range.h" + +#include <limits> + +#include "base/basictypes.h" +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +using CheckedAddressRange = + internal::CheckedAddressRangeGeneric<uint64_t, uint64_t>; + +enum Validity { + kInvalid = false, + kValid, + kValid64Invalid32, +}; + +bool ExpectationForValidity32(Validity validity) { + return validity == kValid; +} + +bool ExpectationForValidity64(Validity validity) { + return validity == kValid || validity == kValid64Invalid32; +} + +TEST(CheckedAddressRange, IsValid) { + const struct TestData { + uint64_t base; + uint64_t size; + Validity validity; + } kTestData[] = { + {0, 0, kValid}, + {0, 1, kValid}, + {0, 2, kValid}, + {0, 0x7fffffff, kValid}, + {0, 0x80000000, kValid}, + {0, 0xfffffffe, kValid}, + {0, 0xffffffff, kValid}, + {0, 0xffffffffffffffff, kValid64Invalid32}, + {1, 0, kValid}, + {1, 1, kValid}, + {1, 2, kValid}, + {1, 0x7fffffff, kValid}, + {1, 0x80000000, kValid}, + {1, 0xfffffffe, kValid}, + {1, 0xffffffff, kValid64Invalid32}, + {1, 0xfffffffffffffffe, kValid64Invalid32}, + {1, 0xffffffffffffffff, kInvalid}, + {0x7fffffff, 0, kValid}, + {0x7fffffff, 1, kValid}, + {0x7fffffff, 2, kValid}, + {0x7fffffff, 0x7fffffff, kValid}, + {0x7fffffff, 0x80000000, kValid}, + {0x7fffffff, 0xfffffffe, kValid64Invalid32}, + {0x7fffffff, 0xffffffff, kValid64Invalid32}, + {0x80000000, 0, kValid}, + {0x80000000, 1, kValid}, + {0x80000000, 2, kValid}, + {0x80000000, 0x7fffffff, kValid}, + {0x80000000, 0x80000000, kValid64Invalid32}, + {0x80000000, 0xfffffffe, kValid64Invalid32}, + {0x80000000, 0xffffffff, kValid64Invalid32}, + {0xfffffffe, 0, kValid}, + {0xfffffffe, 1, kValid}, + {0xfffffffe, 2, kValid64Invalid32}, + {0xfffffffe, 0x7fffffff, kValid64Invalid32}, + {0xfffffffe, 0x80000000, kValid64Invalid32}, + {0xfffffffe, 0xfffffffe, kValid64Invalid32}, + {0xfffffffe, 0xffffffff, kValid64Invalid32}, + {0xffffffff, 0, kValid}, + {0xffffffff, 1, kValid64Invalid32}, + {0xffffffff, 2, kValid64Invalid32}, + {0xffffffff, 0x7fffffff, kValid64Invalid32}, + {0xffffffff, 0x80000000, kValid64Invalid32}, + {0xffffffff, 0xfffffffe, kValid64Invalid32}, + {0xffffffff, 0xffffffff, kValid64Invalid32}, + {0x7fffffffffffffff, 0, kValid64Invalid32}, + {0x7fffffffffffffff, 1, kValid64Invalid32}, + {0x7fffffffffffffff, 2, kValid64Invalid32}, + {0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32}, + {0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32}, + {0x7fffffffffffffff, 0x8000000000000001, kInvalid}, + {0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid}, + {0x7fffffffffffffff, 0xffffffffffffffff, kInvalid}, + {0x8000000000000000, 0, kValid64Invalid32}, + {0x8000000000000000, 1, kValid64Invalid32}, + {0x8000000000000000, 2, kValid64Invalid32}, + {0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32}, + {0x8000000000000000, 0x8000000000000000, kInvalid}, + {0x8000000000000000, 0x8000000000000001, kInvalid}, + {0x8000000000000000, 0xfffffffffffffffe, kInvalid}, + {0x8000000000000000, 0xffffffffffffffff, kInvalid}, + {0xfffffffffffffffe, 0, kValid64Invalid32}, + {0xfffffffffffffffe, 1, kValid64Invalid32}, + {0xfffffffffffffffe, 2, kInvalid}, + {0xffffffffffffffff, 0, kValid64Invalid32}, + {0xffffffffffffffff, 1, kInvalid}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf("index %" PRIuS + ", base 0x%llx, size 0x%llx", + index, + testcase.base, + testcase.size)); + + CheckedAddressRange range_32(false, testcase.base, testcase.size); + EXPECT_EQ(ExpectationForValidity32(testcase.validity), range_32.IsValid()); + + CheckedAddressRange range_64(true, testcase.base, testcase.size); + EXPECT_EQ(ExpectationForValidity64(testcase.validity), range_64.IsValid()); + } +} + +TEST(CheckedAddressRange, ContainsValue) { + const struct TestData { + uint64_t value; + bool expectation; + } kTestData[] = { + {0, false}, + {1, false}, + {0x1fff, false}, + {0x2000, true}, + {0x2001, true}, + {0x2ffe, true}, + {0x2fff, true}, + {0x3000, false}, + {0x3001, false}, + {0x7fffffff, false}, + {0x80000000, false}, + {0x80000001, false}, + {0x80001fff, false}, + {0x80002000, false}, + {0x80002001, false}, + {0x80002ffe, false}, + {0x80002fff, false}, + {0x80003000, false}, + {0x80003001, false}, + {0xffffcfff, false}, + {0xffffdfff, false}, + {0xffffefff, false}, + {0xffffffff, false}, + {0x100000000, false}, + {0xffffffffffffffff, false}, + }; + + CheckedAddressRange parent_range_32(false, 0x2000, 0x1000); + ASSERT_TRUE(parent_range_32.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", value 0x%llx", index, testcase.value)); + + EXPECT_EQ(testcase.expectation, + parent_range_32.ContainsValue(testcase.value)); + } + + CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000); + ASSERT_TRUE(parent_range_64.IsValid()); + EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff)); + EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000)); + EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001)); + EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff)); + EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000)); +} + +TEST(CheckedAddressRange, ContainsRange) { + const struct TestData { + uint64_t base; + uint64_t size; + bool expectation; + } kTestData[] = { + {0, 0, false}, + {0, 1, false}, + {0x2000, 0x1000, true}, + {0, 0x2000, false}, + {0x3000, 0x1000, false}, + {0x1800, 0x1000, false}, + {0x2800, 0x1000, false}, + {0x2000, 0x800, true}, + {0x2800, 0x800, true}, + {0x2400, 0x800, true}, + {0x2800, 0, true}, + {0x2000, 0xffffdfff, false}, + {0x2800, 0xffffd7ff, false}, + {0x3000, 0xffffcfff, false}, + {0xfffffffe, 1, false}, + {0xffffffff, 0, false}, + {0x1fff, 0, false}, + {0x2000, 0, true}, + {0x2001, 0, true}, + {0x2fff, 0, true}, + {0x3000, 0, true}, + {0x3001, 0, false}, + {0x1fff, 1, false}, + {0x2000, 1, true}, + {0x2001, 1, true}, + {0x2fff, 1, true}, + {0x3000, 1, false}, + {0x3001, 1, false}, + }; + + CheckedAddressRange parent_range_32(false, 0x2000, 0x1000); + ASSERT_TRUE(parent_range_32.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf("index %" PRIuS + ", base 0x%llx, size 0x%llx", + index, + testcase.base, + testcase.size)); + + CheckedAddressRange child_range_32(false, testcase.base, testcase.size); + ASSERT_TRUE(child_range_32.IsValid()); + EXPECT_EQ(testcase.expectation, + parent_range_32.ContainsRange(child_range_32)); + } + + CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000); + ASSERT_TRUE(parent_range_64.IsValid()); + + CheckedAddressRange child_range_64(true, 0xffffffff, 2); + EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64)); + + child_range_64.SetRange(true, 0x100000000, 2); + EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64)); + + child_range_64.SetRange(true, 0x100000ffe, 2); + EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64)); + + child_range_64.SetRange(true, 0x100000fff, 2); + EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_range.h b/third_party/crashpad/crashpad/util/numeric/checked_range.h new file mode 100644 index 0000000..972a038 --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/checked_range.h
@@ -0,0 +1,136 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_ +#define CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_ + +#include <limits> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/numerics/safe_math.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +//! \brief Ensures that a range, composed of a base and size, does not overflow +//! its data type. +template <typename ValueType, typename SizeType = ValueType> +class CheckedRange { + public: + CheckedRange(ValueType base, SizeType size) { + static_assert(!std::numeric_limits<SizeType>::is_signed, + "SizeType must be unsigned"); + SetRange(base, size); + } + + //! \brief Sets the range’s base and size to \a base and \a size, + //! respectively. + void SetRange(ValueType base, SizeType size) { + base_ = base; + size_ = size; + } + + //! \brief The range’s base. + ValueType base() const { return base_; } + + //! \brief The range’s size. + SizeType size() const { return size_; } + + //! \brief The range’s end (its base plus its size). + ValueType end() const { return base_ + size_; } + + //! \brief Returns the validity of the range. + //! + //! \return `true` if the range is valid, `false` otherwise. + //! + //! A range is valid if its size can be converted to the range’s data type + //! without data loss, and if its end (base plus size) can be computed without + //! overflowing its data type. + bool IsValid() const { + if (!base::IsValueInRangeForNumericType<ValueType, SizeType>(size_)) { + return false; + } + base::CheckedNumeric<ValueType> checked_end(base_); + checked_end += implicit_cast<ValueType>(size_); + return checked_end.IsValid(); + } + + //! \brief Returns whether the range contains another value. + //! + //! \param[in] value The (possibly) contained value. + //! + //! \return `true` if the range contains \a value, `false` otherwise. + //! + //! A range contains a value if the value is greater than or equal to its + //! base, and less than its end (base plus size). + //! + //! This method must only be called if IsValid() would return `true`. + bool ContainsValue(ValueType value) const { + DCHECK(IsValid()); + + return value >= base() && value < end(); + } + + //! \brief Returns whether the range contains another range. + //! + //! \param[in] that The (possibly) contained range. + //! + //! \return `true` if `this` range, the containing range, contains \a that, + //! the contained range. `false` otherwise. + //! + //! A range contains another range when the contained range’s base is greater + //! than or equal to the containing range’s base, and the contained range’s + //! end is less than or equal to the containing range’s end. + //! + //! This method must only be called if IsValid() would return `true` for both + //! CheckedRange objects involved. + bool ContainsRange(const CheckedRange<ValueType, SizeType>& that) const { + DCHECK(IsValid()); + DCHECK(that.IsValid()); + + return that.base() >= base() && that.end() <= end(); + } + + //! \brief Returns whether the range overlaps another range. + //! + //! \param[in] that The (possibly) overlapping range. + //! + //! \return `true` if `this` range, the first range, overlaps \a that, + //! the provided range. `false` otherwise. + //! + //! Ranges are considered to be closed-open [base, end) for this test. Zero + //! length ranges are never considered to overlap another range. + //! + //! This method must only be called if IsValid() would return `true` for both + //! CheckedRange objects involved. + bool OverlapsRange(const CheckedRange<ValueType, SizeType>& that) const { + DCHECK(IsValid()); + DCHECK(that.IsValid()); + + if (size() == 0 || that.size() == 0) + return false; + + return base() < that.end() && that.base() < end(); + } + + private: + ValueType base_; + SizeType size_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc b/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc new file mode 100644 index 0000000..ea2e6ed --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc
@@ -0,0 +1,304 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/numeric/checked_range.h" + +#include <stdint.h> + +#include <limits> + +#include "base/basictypes.h" +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(CheckedRange, IsValid) { + const struct UnsignedTestData { + uint32_t base; + uint32_t size; + bool valid; + } kUnsignedTestData[] = { + {0, 0, true}, + {0, 1, true}, + {0, 2, true}, + {0, 0x7fffffff, true}, + {0, 0x80000000, true}, + {0, 0xfffffffe, true}, + {0, 0xffffffff, true}, + {1, 0, true}, + {1, 1, true}, + {1, 2, true}, + {1, 0x7fffffff, true}, + {1, 0x80000000, true}, + {1, 0xfffffffe, true}, + {1, 0xffffffff, false}, + {0x7fffffff, 0, true}, + {0x7fffffff, 1, true}, + {0x7fffffff, 2, true}, + {0x7fffffff, 0x7fffffff, true}, + {0x7fffffff, 0x80000000, true}, + {0x7fffffff, 0xfffffffe, false}, + {0x7fffffff, 0xffffffff, false}, + {0x80000000, 0, true}, + {0x80000000, 1, true}, + {0x80000000, 2, true}, + {0x80000000, 0x7fffffff, true}, + {0x80000000, 0x80000000, false}, + {0x80000000, 0xfffffffe, false}, + {0x80000000, 0xffffffff, false}, + {0xfffffffe, 0, true}, + {0xfffffffe, 1, true}, + {0xfffffffe, 2, false}, + {0xfffffffe, 0x7fffffff, false}, + {0xfffffffe, 0x80000000, false}, + {0xfffffffe, 0xfffffffe, false}, + {0xfffffffe, 0xffffffff, false}, + {0xffffffff, 0, true}, + {0xffffffff, 1, false}, + {0xffffffff, 2, false}, + {0xffffffff, 0x7fffffff, false}, + {0xffffffff, 0x80000000, false}, + {0xffffffff, 0xfffffffe, false}, + {0xffffffff, 0xffffffff, false}, + }; + + for (size_t index = 0; index < arraysize(kUnsignedTestData); ++index) { + const UnsignedTestData& testcase = kUnsignedTestData[index]; + SCOPED_TRACE(base::StringPrintf("unsigned index %" PRIuS + ", base 0x%x, size 0x%x", + index, + testcase.base, + testcase.size)); + + CheckedRange<uint32_t> range(testcase.base, testcase.size); + EXPECT_EQ(testcase.valid, range.IsValid()); + } + + const int32_t kMinInt32 = std::numeric_limits<int32_t>::min(); + const struct SignedTestData { + int32_t base; + uint32_t size; + bool valid; + } kSignedTestData[] = { + {0, 0, true}, + {0, 1, true}, + {0, 2, true}, + {0, 0x7fffffff, true}, + {0, 0x80000000, false}, + {0, 0xfffffffe, false}, + {0, 0xffffffff, false}, + {1, 0, true}, + {1, 1, true}, + {1, 2, true}, + {1, 0x7fffffff, false}, + {1, 0x80000000, false}, + {1, 0xfffffffe, false}, + {1, 0xffffffff, false}, + {0x7fffffff, 0, true}, + {0x7fffffff, 1, false}, + {0x7fffffff, 2, false}, + {0x7fffffff, 0x7fffffff, false}, + {0x7fffffff, 0x80000000, false}, + {0x7fffffff, 0xfffffffe, false}, + {0x7fffffff, 0xffffffff, false}, + {kMinInt32, 0, true}, + {kMinInt32, 1, true}, + {kMinInt32, 2, true}, + {kMinInt32, 0x7fffffff, true}, + {kMinInt32, 0x80000000, false}, + {kMinInt32, 0xfffffffe, false}, + {kMinInt32, 0xffffffff, false}, + {-2, 0, true}, + {-2, 1, true}, + {-2, 2, true}, + {-2, 0x7fffffff, true}, + {-2, 0x80000000, false}, + {-2, 0xfffffffe, false}, + {-2, 0xffffffff, false}, + {-1, 0, true}, + {-1, 1, true}, + {-1, 2, true}, + {-1, 0x7fffffff, true}, + {-1, 0x80000000, false}, + {-1, 0xfffffffe, false}, + {-1, 0xffffffff, false}, + }; + + for (size_t index = 0; index < arraysize(kSignedTestData); ++index) { + const SignedTestData& testcase = kSignedTestData[index]; + SCOPED_TRACE(base::StringPrintf("signed index %" PRIuS + ", base 0x%x, size 0x%x", + index, + testcase.base, + testcase.size)); + + CheckedRange<int32_t, uint32_t> range(testcase.base, testcase.size); + EXPECT_EQ(testcase.valid, range.IsValid()); + } +} + +TEST(CheckedRange, ContainsValue) { + const struct TestData { + uint32_t value; + bool contains; + } kTestData[] = { + {0, false}, + {1, false}, + {0x1fff, false}, + {0x2000, true}, + {0x2001, true}, + {0x2ffe, true}, + {0x2fff, true}, + {0x3000, false}, + {0x3001, false}, + {0x7fffffff, false}, + {0x80000000, false}, + {0x80000001, false}, + {0x80001fff, false}, + {0x80002000, false}, + {0x80002001, false}, + {0x80002ffe, false}, + {0x80002fff, false}, + {0x80003000, false}, + {0x80003001, false}, + {0xffffcfff, false}, + {0xffffdfff, false}, + {0xffffefff, false}, + {0xffffffff, false}, + }; + + CheckedRange<uint32_t> parent_range(0x2000, 0x1000); + ASSERT_TRUE(parent_range.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", value 0x%x", index, testcase.value)); + + EXPECT_EQ(testcase.contains, parent_range.ContainsValue(testcase.value)); + } +} + +TEST(CheckedRange, ContainsRange) { + const struct TestData { + uint32_t base; + uint32_t size; + bool contains; + } kTestData[] = { + {0, 0, false}, + {0, 1, false}, + {0x2000, 0x1000, true}, + {0, 0x2000, false}, + {0x3000, 0x1000, false}, + {0x1800, 0x1000, false}, + {0x2800, 0x1000, false}, + {0x2000, 0x800, true}, + {0x2800, 0x800, true}, + {0x2400, 0x800, true}, + {0x2800, 0, true}, + {0x2000, 0xffffdfff, false}, + {0x2800, 0xffffd7ff, false}, + {0x3000, 0xffffcfff, false}, + {0xfffffffe, 1, false}, + {0xffffffff, 0, false}, + {0x1fff, 0, false}, + {0x2000, 0, true}, + {0x2001, 0, true}, + {0x2fff, 0, true}, + {0x3000, 0, true}, + {0x3001, 0, false}, + {0x1fff, 1, false}, + {0x2000, 1, true}, + {0x2001, 1, true}, + {0x2fff, 1, true}, + {0x3000, 1, false}, + {0x3001, 1, false}, + }; + + CheckedRange<uint32_t> parent_range(0x2000, 0x1000); + ASSERT_TRUE(parent_range.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x", + index, + testcase.base, + testcase.size)); + + CheckedRange<uint32_t> child_range(testcase.base, testcase.size); + ASSERT_TRUE(child_range.IsValid()); + EXPECT_EQ(testcase.contains, parent_range.ContainsRange(child_range)); + } +} + +TEST(CheckedRange, OverlapsRange) { + const struct TestData { + uint32_t base; + uint32_t size; + bool overlaps; + } kTestData[] = { + {0, 0, false}, + {0, 1, false}, + {0x2000, 0x1000, true}, + {0, 0x2000, false}, + {0x3000, 0x1000, false}, + {0x1800, 0x1000, true}, + {0x1800, 0x2000, true}, + {0x2800, 0x1000, true}, + {0x2000, 0x800, true}, + {0x2800, 0x800, true}, + {0x2400, 0x800, true}, + {0x2800, 0, false}, + {0x2000, 0xffffdfff, true}, + {0x2800, 0xffffd7ff, true}, + {0x3000, 0xffffcfff, false}, + {0xfffffffe, 1, false}, + {0xffffffff, 0, false}, + {0x1fff, 0, false}, + {0x2000, 0, false}, + {0x2001, 0, false}, + {0x2fff, 0, false}, + {0x3000, 0, false}, + {0x3001, 0, false}, + {0x1fff, 1, false}, + {0x2000, 1, true}, + {0x2001, 1, true}, + {0x2fff, 1, true}, + {0x3000, 1, false}, + {0x3001, 1, false}, + }; + + CheckedRange<uint32_t> first_range(0x2000, 0x1000); + ASSERT_TRUE(first_range.IsValid()); + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + const TestData& testcase = kTestData[index]; + SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x", + index, + testcase.base, + testcase.size)); + + CheckedRange<uint32_t> second_range(testcase.base, testcase.size); + ASSERT_TRUE(second_range.IsValid()); + EXPECT_EQ(testcase.overlaps, first_range.OverlapsRange(second_range)); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/numeric/in_range_cast.h b/third_party/crashpad/crashpad/util/numeric/in_range_cast.h new file mode 100644 index 0000000..3dba10af --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/in_range_cast.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_ +#define CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_ + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" + +namespace crashpad { + +//! \brief Casts to a different type if it can be done without data loss, +//! logging a warning message and returing a default value otherwise. +//! +//! \param[in] source The value to convert and return. +//! \param[in] default_value The default value to return, in the event that \a +//! source cannot be represented in the destination type. +//! +//! \return \a source if it can be represented in the destination type, +//! otherwise \a default_value. +template <typename Destination, typename Source> +Destination InRangeCast(Source source, Destination default_value) { + if (base::IsValueInRangeForNumericType<Destination>(source)) { + return static_cast<Destination>(source); + } + + LOG(WARNING) << "value " << source << " out of range"; + return static_cast<Destination>(default_value); +} + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_
diff --git a/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc b/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc new file mode 100644 index 0000000..d8882bc4 --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc
@@ -0,0 +1,124 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/numeric/in_range_cast.h" + +#include <stdint.h> + +#include <limits> + +#include "gtest/gtest.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +const int32_t kInt32Min = std::numeric_limits<int32_t>::min(); +const int64_t kInt64Min = std::numeric_limits<int64_t>::min(); + +TEST(InRangeCast, Uint32) { + EXPECT_EQ(0u, InRangeCast<uint32_t>(0, 1)); + EXPECT_EQ(1u, InRangeCast<uint32_t>(1, 1)); + EXPECT_EQ(2u, InRangeCast<uint32_t>(2, 1)); + EXPECT_EQ(0u, InRangeCast<uint32_t>(-1, 0)); + EXPECT_EQ(1u, InRangeCast<uint32_t>(-1, 1)); + EXPECT_EQ(2u, InRangeCast<uint32_t>(-1, 2)); + EXPECT_EQ(0xffffffffu, InRangeCast<uint32_t>(0xffffffffu, 1)); + EXPECT_EQ(0xffffffffu, InRangeCast<uint32_t>(UINT64_C(0xffffffff), 1)); + EXPECT_EQ(1u, InRangeCast<uint32_t>(UINT64_C(0x100000000), 1)); + EXPECT_EQ(1u, InRangeCast<uint32_t>(UINT64_C(0x100000001), 1)); + EXPECT_EQ(1u, InRangeCast<uint32_t>(kInt32Min, 1)); + EXPECT_EQ(1u, InRangeCast<uint32_t>(kInt64Min, 1)); + EXPECT_EQ(0xffffffffu, InRangeCast<uint32_t>(-1, 0xffffffffu)); +} + +TEST(InRangeCast, Int32) { + EXPECT_EQ(0, InRangeCast<int32_t>(0, 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(1, 1)); + EXPECT_EQ(2, InRangeCast<int32_t>(2, 1)); + EXPECT_EQ(-1, InRangeCast<int32_t>(-1, 1)); + EXPECT_EQ(0x7fffffff, InRangeCast<int32_t>(0x7fffffff, 1)); + EXPECT_EQ(0x7fffffff, InRangeCast<int32_t>(0x7fffffffu, 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(0x80000000u, 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(0xffffffffu, 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(INT64_C(0x80000000), 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(INT64_C(0xffffffff), 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(INT64_C(0x100000000), 1)); + EXPECT_EQ(kInt32Min, InRangeCast<int32_t>(kInt32Min, 1)); + EXPECT_EQ(kInt32Min, + InRangeCast<int32_t>(implicit_cast<int64_t>(kInt32Min), 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(implicit_cast<int64_t>(kInt32Min) - 1, 1)); + EXPECT_EQ(1, InRangeCast<int32_t>(kInt64Min, 1)); + EXPECT_EQ(0, InRangeCast<int32_t>(0xffffffffu, 0)); + EXPECT_EQ(-1, InRangeCast<int32_t>(0xffffffffu, -1)); + EXPECT_EQ(kInt32Min, InRangeCast<int32_t>(0xffffffffu, kInt32Min)); + EXPECT_EQ(0x7fffffff, InRangeCast<int32_t>(0xffffffffu, 0x7fffffff)); +} + +TEST(InRangeCast, Uint64) { + EXPECT_EQ(0u, InRangeCast<uint64_t>(0, 1)); + EXPECT_EQ(1u, InRangeCast<uint64_t>(1, 1)); + EXPECT_EQ(2u, InRangeCast<uint64_t>(2, 1)); + EXPECT_EQ(0u, InRangeCast<uint64_t>(-1, 0)); + EXPECT_EQ(1u, InRangeCast<uint64_t>(-1, 1)); + EXPECT_EQ(2u, InRangeCast<uint64_t>(-1, 2)); + EXPECT_EQ(0xffffffffu, InRangeCast<uint64_t>(0xffffffffu, 1)); + EXPECT_EQ(0xffffffffu, InRangeCast<uint64_t>(UINT64_C(0xffffffff), 1)); + EXPECT_EQ(UINT64_C(0x100000000), + InRangeCast<uint64_t>(UINT64_C(0x100000000), 1)); + EXPECT_EQ(UINT64_C(0x100000001), + InRangeCast<uint64_t>(UINT64_C(0x100000001), 1)); + EXPECT_EQ(1u, InRangeCast<uint64_t>(kInt32Min, 1)); + EXPECT_EQ(1u, InRangeCast<uint64_t>(INT64_C(-1), 1)); + EXPECT_EQ(1u, InRangeCast<uint64_t>(kInt64Min, 1)); + EXPECT_EQ(UINT64_C(0xffffffffffffffff), + InRangeCast<uint64_t>(-1, UINT64_C(0xffffffffffffffff))); +} + +TEST(InRangeCast, Int64) { + EXPECT_EQ(0, InRangeCast<int64_t>(0, 1)); + EXPECT_EQ(1, InRangeCast<int64_t>(1, 1)); + EXPECT_EQ(2, InRangeCast<int64_t>(2, 1)); + EXPECT_EQ(-1, InRangeCast<int64_t>(-1, 1)); + EXPECT_EQ(0x7fffffff, InRangeCast<int64_t>(0x7fffffff, 1)); + EXPECT_EQ(0x7fffffff, InRangeCast<int64_t>(0x7fffffffu, 1)); + EXPECT_EQ(INT64_C(0x80000000), InRangeCast<int64_t>(0x80000000u, 1)); + EXPECT_EQ(INT64_C(0xffffffff), InRangeCast<int64_t>(0xffffffffu, 1)); + EXPECT_EQ(INT64_C(0x80000000), InRangeCast<int64_t>(INT64_C(0x80000000), 1)); + EXPECT_EQ(INT64_C(0xffffffff), InRangeCast<int64_t>(INT64_C(0xffffffff), 1)); + EXPECT_EQ(INT64_C(0x100000000), + InRangeCast<int64_t>(INT64_C(0x100000000), 1)); + EXPECT_EQ(INT64_C(0x7fffffffffffffff), + InRangeCast<int64_t>(INT64_C(0x7fffffffffffffff), 1)); + EXPECT_EQ(INT64_C(0x7fffffffffffffff), + InRangeCast<int64_t>(UINT64_C(0x7fffffffffffffff), 1)); + EXPECT_EQ(1, InRangeCast<int64_t>(UINT64_C(0x8000000000000000), 1)); + EXPECT_EQ(1, InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), 1)); + EXPECT_EQ(kInt32Min, InRangeCast<int64_t>(kInt32Min, 1)); + EXPECT_EQ(kInt32Min, + InRangeCast<int64_t>(implicit_cast<int64_t>(kInt32Min), 1)); + EXPECT_EQ(kInt64Min, InRangeCast<int64_t>(kInt64Min, 1)); + EXPECT_EQ(0, InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), 0)); + EXPECT_EQ(-1, InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), -1)); + EXPECT_EQ(kInt64Min, + InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), kInt64Min)); + EXPECT_EQ(INT64_C(0x7fffffffffffffff), + InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), + INT64_C(0x7fffffffffffffff))); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/numeric/int128.h b/third_party/crashpad/crashpad/util/numeric/int128.h new file mode 100644 index 0000000..c57bed6d --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/int128.h
@@ -0,0 +1,52 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NUMERIC_INT128_H_ +#define CRASHPAD_UTIL_NUMERIC_INT128_H_ + +#include <stdint.h> + +#include "build/build_config.h" + +namespace crashpad { + +//! \brief Stores a 128-bit quantity. +//! +//! This structure is organized so that 128-bit quantities are laid out in +//! memory according to the system’s natural byte order. If a system offers a +//! native 128-bit type, it should be possible to bit_cast<> between that type +//! and this one. +//! +//! This structure is designed to have the same layout, although not the same +//! field names, as the Windows SDK’s `M128A` type from `<winnt.h>`. It is +//! provided here instead of in `compat` because it is useful outside of the +//! scope of data structures defined by the Windows SDK. +struct uint128_struct { +#if defined(ARCH_CPU_LITTLE_ENDIAN) || DOXYGEN + //! \brief The low 64 bits of the 128-bit quantity. + uint64_t lo; + + //! \brief The high 64 bits of the 128-bit quantity. + uint64_t hi; +#else + uint64_t hi; + uint64_t lo; +#endif +}; + +static_assert(sizeof(uint128_struct) == 16, "uint128 must be 16 bytes"); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NUMERIC_INT128_H_
diff --git a/third_party/crashpad/crashpad/util/numeric/int128_test.cc b/third_party/crashpad/crashpad/util/numeric/int128_test.cc new file mode 100644 index 0000000..e2f02a4 --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/int128_test.cc
@@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/numeric/int128.h" + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Int128, UInt128) { +#if defined(ARCH_CPU_LITTLE_ENDIAN) + const uint8_t kBytes[] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; +#else + const uint8_t kBytes[] = + {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; +#endif + + uint128_struct uint128; + static_assert(sizeof(uint128) == sizeof(kBytes), "sizes must be equal"); + + uint128 = bit_cast<uint128_struct>(kBytes); + + EXPECT_EQ(0x0706050403020100u, uint128.lo); + EXPECT_EQ(0x0f0e0d0c0b0a0908u, uint128.hi); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/numeric/safe_assignment.h b/third_party/crashpad/crashpad/util/numeric/safe_assignment.h new file mode 100644 index 0000000..358d9837 --- /dev/null +++ b/third_party/crashpad/crashpad/util/numeric/safe_assignment.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_ +#define CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_ + +#include "base/numerics/safe_conversions.h" + +namespace crashpad { + +//! \brief Performs an assignment if it can be done safely, and signals if it +//! cannot be done safely. +//! +//! \param[out] destination A pointer to the variable to be assigned to. +//! \param[in] source The value to assign. +//! +//! \return `true` if \a source is in the range supported by the type of \a +//! *destination, with the assignment to \a *destination having been +//! performed. `false` if the assignment cannot be completed safely because +//! \a source is outside of this range. +template <typename Destination, typename Source> +bool AssignIfInRange(Destination* destination, Source source) { + if (!base::IsValueInRangeForNumericType<Destination>(source)) { + return false; + } + + *destination = static_cast<Destination>(source); + return true; +} + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_
diff --git a/third_party/crashpad/crashpad/util/posix/close_multiple.cc b/third_party/crashpad/crashpad/util/posix/close_multiple.cc new file mode 100644 index 0000000..d94d575c --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
@@ -0,0 +1,158 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/posix/close_multiple.h" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <algorithm> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "util/misc/implicit_cast.h" +#include "util/numeric/safe_assignment.h" + +// Everything in this file is expected to execute between fork() and exec(), +// so everything called here must be acceptable in this context. However, +// logging code that is not expected to execute under normal circumstances is +// currently permitted. + +namespace crashpad { +namespace { + +// This function attempts to close |fd| or mark it as close-on-exec. On systems +// where close-on-exec is attempted, a failure to mark it close-on-exec will be +// followed by an attempt to close it. |ebadf_ok| should be set to |true| if +// the caller is attempting to close the file descriptor “blind,” that is, +// without knowledge that it is or is not a valid file descriptor. +void CloseNowOrOnExec(int fd, bool ebadf_ok) { + int rv; + +#if defined(OS_MACOSX) + // Try to set close-on-exec, to avoid attempting to close a guarded FD with + // a close guard set. + rv = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (rv != -1 || (ebadf_ok && errno == EBADF)) { + return; + } + PLOG(WARNING) << "fcntl"; +#endif + + rv = IGNORE_EINTR(close(fd)); + if (rv != 0 && !(ebadf_ok && errno == EBADF)) { + PLOG(WARNING) << "close"; + } +} + +struct ScopedDIRCloser { + void operator()(DIR* dir) const { + if (dir) { + if (closedir(dir) < 0) { + PLOG(ERROR) << "closedir"; + } + } + } +}; + +using ScopedDIR = scoped_ptr<DIR, ScopedDIRCloser>; + +// This function implements CloseMultipleNowOrOnExec() using an operating +// system-specific FD directory to determine which file descriptors are open. +// This is an advantage over looping over all possible file descriptors, because +// no attempt needs to be made to close file descriptors that are not open. +bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) { +#if defined(OS_MACOSX) + const char kFDDir[] = "/dev/fd"; +#elif defined(OS_LINUX) + const char kFDDir[] = "/proc/self/fd"; +#endif + + DIR* dir = opendir(kFDDir); + if (!dir) { + PLOG(WARNING) << "opendir"; + return false; + } + + ScopedDIR dir_owner(dir); + + int dir_fd = dirfd(dir); + if (dir_fd == -1) { + PLOG(WARNING) << "dirfd"; + return false; + } + + dirent entry; + dirent* result; + int rv; + while ((rv = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) { + const char* entry_name = &(*result->d_name); + if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) { + continue; + } + + char* end; + long entry_fd_long = strtol(entry_name, &end, 10); + if (entry_name[0] == '\0' || *end) { + LOG(ERROR) << "unexpected entry " << entry_name; + return false; + } + + int entry_fd; + if (!AssignIfInRange(&entry_fd, entry_fd_long)) { + LOG(ERROR) << "out-of-range fd " << entry_name; + return false; + } + + if (entry_fd >= fd && entry_fd != preserve_fd && entry_fd != dir_fd) { + CloseNowOrOnExec(entry_fd, false); + } + } + + return true; +} + +} // namespace + +void CloseMultipleNowOrOnExec(int fd, int preserve_fd) { + if (CloseMultipleNowOrOnExecUsingFDDir(fd, preserve_fd)) { + return; + } + + // Fallback: close every file descriptor starting at |fd| and ending at the + // system’s file descriptor limit. Check a few values and use the highest as + // the limit, because these may be based on the file descriptor limit set by + // setrlimit(), and higher-numbered file descriptors may have been opened + // prior to the limit being lowered. For Mac OS X, see 10.9.2 + // Libc-997.90.3/gen/FreeBSD/sysconf.c sysconf() and 10.9.4 + // xnu-2422.110.17/bsd/kern/kern_descrip.c getdtablesize(), which both return + // the current RLIMIT_NOFILE value, not the maximum possible file descriptor. + int max_fd = std::max(implicit_cast<int>(sysconf(_SC_OPEN_MAX)), OPEN_MAX); + max_fd = std::max(max_fd, getdtablesize()); + + for (int entry_fd = fd; entry_fd < max_fd; ++entry_fd) { + if (entry_fd != preserve_fd) { + CloseNowOrOnExec(entry_fd, true); + } + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/close_multiple.h b/third_party/crashpad/crashpad/util/posix/close_multiple.h new file mode 100644 index 0000000..2a66f28a --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/close_multiple.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_ +#define CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_ + +namespace crashpad { + +//! \brief Close multiple file descriptors or mark them close-on-exec. +//! +//! This is similar to the BSD/Solaris-style `closefrom()` routine, which closes +//! all open file descriptors equal to or higher than its \a fd argument. This +//! function must not be called while other threads are active. It is intended +//! to be used in a child process created by `fork()`, prior to calling an +//! `exec()`-family function. This guarantees that a (possibly untrustworthy) +//! child process does not inherit file descriptors that it has no need for. +//! +//! Unlike the BSD function, this function may not close file descriptors +//! immediately, but may instead mark them as close-on-exec. The actual behavior +//! chosen is specific to the operating system. On Mac OS X, file descriptors +//! are marked close-on-exec instead of being closed outright in order to avoid +//! raising `EXC_GUARD` exceptions for guarded file descriptors that are +//! protected against `close()`. +//! +//! \param[in] fd The lowest file descriptor to close or set as close-on-exec. +//! \param[in] preserve_fd A file descriptor to preserve and not close (or set +//! as close-on-exec), even if it is open and its value is greater than \a +//! fd. To not preserve any file descriptor, pass `-1` for this parameter. +void CloseMultipleNowOrOnExec(int fd, int preserve_fd); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_
diff --git a/third_party/crashpad/crashpad/util/posix/close_stdio.cc b/third_party/crashpad/crashpad/util/posix/close_stdio.cc new file mode 100644 index 0000000..8ff59d0 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/close_stdio.cc
@@ -0,0 +1,52 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/posix/close_stdio.h" + +#include <fcntl.h> +#include <paths.h> +#include <unistd.h> + +#include "base/basictypes.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" + +namespace crashpad { + +namespace { + +void CloseStdioStream(int desired_fd, int oflag) { + base::ScopedFD fd(HANDLE_EINTR(open(_PATH_DEVNULL, oflag))); + if (fd == desired_fd) { + // Weird, but play along. + ignore_result(fd.release()); + } else { + PCHECK(fd.get() >= 0) << "open"; + PCHECK(HANDLE_EINTR(dup2(fd.get(), desired_fd)) != -1) << "dup2"; + fd.reset(); + } +} + +} // namespace + +void CloseStdinAndStdout() { + // Open /dev/null for stdin and stdout separately, so that it can be opened + // with the correct mode each time. This ensures that attempts to write to + // stdin or read from stdout fail with EBADF. + CloseStdioStream(STDIN_FILENO, O_RDONLY); + CloseStdioStream(STDOUT_FILENO, O_WRONLY); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/close_stdio.h b/third_party/crashpad/crashpad/util/posix/close_stdio.h new file mode 100644 index 0000000..9515923 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/close_stdio.h
@@ -0,0 +1,42 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_ +#define CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_ + +namespace crashpad { + +//! \brief Closes `stdin` and `stdout` by opening `/dev/null` over them. +//! +//! It is normally inadvisable to `close()` the three standard input/output +//! streams, because they occupy special file descriptors. Closing them outright +//! could result in their file descriptors being reused. This causes problems +//! for library code (including the standard library) that expects these file +//! descriptors to have special meaning. +//! +//! This function discards the standard input and standard output streams by +//! opening `/dev/null` and assigning it to their file descriptors, closing +//! whatever had been at those file descriptors previously. +//! +//! `stderr`, the standard error stream, is not closed. It is often useful to +//! retain the ability to send diagnostic messages to the standard error stream. +//! +//! \note This function can only maintain its guarantees in a single-threaded +//! process, or in situations where the caller has control of all threads in +//! the process. +void CloseStdinAndStdout(); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_
diff --git a/third_party/crashpad/crashpad/util/posix/drop_privileges.cc b/third_party/crashpad/crashpad/util/posix/drop_privileges.cc new file mode 100644 index 0000000..3fb0ab4 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/drop_privileges.cc
@@ -0,0 +1,91 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <unistd.h> + +#include "base/logging.h" +#include "build/build_config.h" + +namespace crashpad { + +void DropPrivileges() { + gid_t gid = getgid(); + uid_t uid = getuid(); + +#if defined(OS_MACOSX) + // Based on the POSIX.1-2008 2013 edition documentation for setreuid() and + // setregid(), setreuid() and setregid() alone should be sufficient to drop + // privileges. The standard specifies that the saved ID should be set to the + // effective ID whenever the real ID is not -1, or whenever the effective ID + // is set not equal to the real ID. This code never specifies -1, so the + // setreuid() and setregid() alone should work according to the standard. + // + // In practice, on Mac OS X, setuid() and setgid() (or seteuid() and + // setegid()) must be called first. Otherwise, setreuid() and setregid() do + // not alter the saved IDs, leaving open the possibility for future privilege + // escalation. + // + // The problem exists in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c + // setreuid(). Based on its comments, it purports to set the svuid to the new + // euid when the old svuid doesn’t match one of the new ruid and euid. This + // isn’t how POSIX.1-2008 says it should behave, but it should work for this + // function’s purposes. In reality, setreuid() doesn’t even do this: it sets + // the svuid to the old euid, which does not drop privileges when the old euid + // is different from the desired euid. The workaround of calling setuid() or + // seteuid() before setreuid() works because it sets the euid so that by the + // time setreuid() runs, the old euid is actually the value that ought to be + // set as the svuid. setregid() is similar. This bug is filed as radar + // 18987552. + // + // setuid() and setgid() alone will only set the saved IDs when running as + // root. When running a setuid non-root or setgid program, they do not alter + // the saved ID, and do not effect a permanent privilege drop. + gid_t egid = getegid(); + PCHECK(setgid(gid) == 0) << "setgid"; + PCHECK(setregid(gid, gid) == 0) << "setregid"; + + uid_t euid = geteuid(); + PCHECK(setuid(uid) == 0) << "setuid"; + PCHECK(setreuid(uid, uid) == 0) << "setreuid"; + + if (uid != 0) { + // Because the setXid()+setreXid() interface to change IDs is fragile, + // ensure that privileges cannot be regained. This can only be done if the + // real user ID (and now the effective user ID as well) is not root, because + // root always has permission to change identity. + if (euid != uid) { + CHECK_EQ(seteuid(euid), -1); + } + if (egid != gid) { + CHECK_EQ(setegid(egid), -1); + } + } +#elif defined(OS_LINUX) + PCHECK(setresgid(gid, gid, gid) == 0) << "setresgid"; + PCHECK(setresuid(uid, uid, uid) == 0) << "setresuid"; + + // Don’t check to see if privileges can be regained on Linux, because on + // Linux, it’s not as simple as ensuring that this can’t be done if non-root. + // Instead, the ability to change user and group IDs are controlled by the + // CAP_SETUID and CAP_SETGID capabilities, which may be granted to non-root + // processes. Since the setresXid() interface is well-defined, it shouldn’t be + // necessary to perform any additional checking anyway. + // + // TODO(mark): Drop CAP_SETUID and CAP_SETGID if present and non-root? +#else +#error Port this function to your system. +#endif +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/drop_privileges.h b/third_party/crashpad/crashpad/util/posix/drop_privileges.h new file mode 100644 index 0000000..91220e9 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/drop_privileges.h
@@ -0,0 +1,40 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_ +#define CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_ + +namespace crashpad { + +//! \brief Permanently drops privileges conferred by being a setuid or setgid +//! executable. +//! +//! The effective user ID and saved set-user ID are set to the real user ID, +//! negating any effects of being a setuid executable. The effective group ID +//! and saved set-group ID are set to the real group ID, negating any effects of +//! being a setgid executable. Because the saved set-user ID and saved set-group +//! ID are reset, there is no way to restore the prior privileges, and the drop +//! is permanent. +//! +//! This function drops privileges correctly when running setuid root and in +//! other circumstances, including when running setuid non-root. If the program +//! is not a setuid or setgid executable, this function has no effect. +//! +//! No changes are made to the supplementary group list, which is normally not +//! altered for setuid or setgid executables. +void DropPrivileges(); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_
diff --git a/third_party/crashpad/crashpad/util/posix/process_info.h b/third_party/crashpad/crashpad/util/posix/process_info.h new file mode 100644 index 0000000..4761cf3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/process_info.h
@@ -0,0 +1,152 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_ +#define CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_ + +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "util/misc/initialization_state_dcheck.h" + +#if defined(OS_MACOSX) +#include <mach/mach.h> +#endif + +namespace crashpad { + +class ProcessInfo { + public: + ProcessInfo(); + ~ProcessInfo(); + + //! \brief Initializes this object with information about the process whose ID + //! is \a pid. + //! + //! This method must be called successfully prior to calling any other method + //! in this class. This method may only be called once. + //! + //! It is unspecified whether the information that an object of this class + //! returns is loaded at the time Initialize() is called or subsequently, and + //! whether this information is cached in the object or not. + //! + //! \param[in] pid The process ID to obtain information for. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Initialize(pid_t pid); + +#if defined(OS_MACOSX) || DOXYGEN + //! \brief Initializes this object with information about a process based on + //! its Mach task. + //! + //! This method serves as a stand-in for Initialize() and may be called in its + //! place with the same restrictions and considerations. + //! + //! \param[in] task The Mach task to obtain information for. + //! + //! \return `true` on success, `false` on failure with an message logged. + bool InitializeFromTask(task_t task); +#endif + + //! \return The target task’s process ID. + pid_t ProcessID() const; + + //! \return The target task’s parent process ID. + pid_t ParentProcessID() const; + + //! \return The target process’ real user ID as would be returned to it by + //! `getuid()`. + uid_t RealUserID() const; + + //! \return The target process’ effective user ID as would be returned to it + //! by `geteuid()`. + uid_t EffectiveUserID() const; + + //! \return The target process’ saved set-user ID. + uid_t SavedUserID() const; + + //! \return the target process’ real group ID as would be returned to it by + //! `getgid()`. + gid_t RealGroupID() const; + + //! \return the target process’ effective group ID as would be returned to it + //! by `getegid()`. + gid_t EffectiveGroupID() const; + + //! \return The target process’ saved set-group ID. + gid_t SavedGroupID() const; + + //! \return the target process’ supplementary group list as would be returned + //! to it by `getgroups()`. + std::set<gid_t> SupplementaryGroups() const; + + //! \return All groups that the target process claims membership in, including + //! RealGroupID(), EffectiveGroupID(), SavedGroupID(), and + //! SupplementaryGroups(). + std::set<gid_t> AllGroups() const; + + //! \brief Determines whether the target process has changed privileges. + //! + //! A process is considered to have changed privileges if it has changed its + //! real, effective, or saved set-user or group IDs with the `setuid()`, + //! `seteuid()`, `setreuid()`, `setgid()`, `setegid()`, or `setregid()` system + //! calls since its most recent `execve()`, or if its privileges changed at + //! `execve()` as a result of executing a setuid or setgid executable. + bool DidChangePrivileges() const; + + //! \return `true` if the target task is a 64-bit process. + bool Is64Bit() const; + + //! \brief Determines the target process’ start time. + //! + //! \param[out] start_time The time that the process started. + void StartTime(timeval* start_time) const; + + //! \brief Obtains the arguments used to launch a process. + //! + //! Whether it is possible to obtain this information for a process with + //! different privileges than the running program is system-dependent. + //! + //! \param[out] argv The process’ arguments as passed to its `main()` function + //! as the \a argv parameter, possibly modified by the process. + //! + //! \return `true` on success, with \a argv populated appropriately. + //! Otherwise, `false` with a message logged. + //! + //! \note This function may spuriously return `false` when used to examine a + //! process that it is calling `exec()`. If examining such a process, call + //! this function in a retry loop with a small (100ns) delay to avoid an + //! erroneous assumption that \a pid is not running. + bool Arguments(std::vector<std::string>* argv) const; + + private: +#if defined(OS_MACOSX) + kinfo_proc kern_proc_info_; +#endif + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessInfo); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_mac.cc b/third_party/crashpad/crashpad/util/posix/process_info_mac.cc new file mode 100644 index 0000000..17d205a --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/process_info_mac.cc
@@ -0,0 +1,233 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/posix/process_info.h" + +#include <string.h> + +#include "base/logging.h" +#include "base/mac/mach_logging.h" + +namespace crashpad { + +ProcessInfo::ProcessInfo() : kern_proc_info_(), initialized_() { +} + +ProcessInfo::~ProcessInfo() { +} + +bool ProcessInfo::Initialize(pid_t pid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + size_t len = sizeof(kern_proc_info_); + if (sysctl(mib, arraysize(mib), &kern_proc_info_, &len, nullptr, 0) != 0) { + PLOG(ERROR) << "sysctl for pid " << pid; + return false; + } + + // This sysctl does not return an error if the pid was not found. 10.9.5 + // xnu-2422.115.4/bsd/kern/kern_sysctl.c sysctl_prochandle() calls + // xnu-2422.115.4/bsd/kern/kern_proc.c proc_iterate(), which provides no + // indication of whether anything was done. To catch this, check that the PID + // has changed from the 0 value it was given when initialized by the + // constructor. + if (kern_proc_info_.kp_proc.p_pid == 0) { + LOG(WARNING) << "pid " << pid << " not found"; + return false; + } + + DCHECK_EQ(kern_proc_info_.kp_proc.p_pid, pid); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessInfo::InitializeFromTask(task_t task) { + pid_t pid; + kern_return_t kr = pid_for_task(task, &pid); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "pid_for_task"; + return false; + } + + return Initialize(pid); +} + +pid_t ProcessInfo::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_proc.p_pid; +} + +pid_t ProcessInfo::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_ppid; +} + +uid_t ProcessInfo::RealUserID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_pcred.p_ruid; +} + +uid_t ProcessInfo::EffectiveUserID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_ucred.cr_uid; +} + +uid_t ProcessInfo::SavedUserID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_pcred.p_svuid; +} + +gid_t ProcessInfo::RealGroupID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_pcred.p_rgid; +} + +gid_t ProcessInfo::EffectiveGroupID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_ucred.cr_gid; +} + +gid_t ProcessInfo::SavedGroupID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_eproc.e_pcred.p_svgid; +} + +std::set<gid_t> ProcessInfo::SupplementaryGroups() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const short ngroups = kern_proc_info_.kp_eproc.e_ucred.cr_ngroups; + DCHECK_GE(ngroups, 0); + DCHECK_LE(static_cast<size_t>(ngroups), + arraysize(kern_proc_info_.kp_eproc.e_ucred.cr_groups)); + + const gid_t* groups = kern_proc_info_.kp_eproc.e_ucred.cr_groups; + return std::set<gid_t>(&groups[0], &groups[ngroups]); +} + +std::set<gid_t> ProcessInfo::AllGroups() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::set<gid_t> all_groups = SupplementaryGroups(); + all_groups.insert(RealGroupID()); + all_groups.insert(EffectiveGroupID()); + all_groups.insert(SavedGroupID()); + return all_groups; +} + +bool ProcessInfo::DidChangePrivileges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_proc.p_flag & P_SUGID; +} + +bool ProcessInfo::Is64Bit() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kern_proc_info_.kp_proc.p_flag & P_LP64; +} + +void ProcessInfo::StartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *start_time = kern_proc_info_.kp_proc.p_starttime; +} + +bool ProcessInfo::Arguments(std::vector<std::string>* argv) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // The format of KERN_PROCARGS2 is explained in 10.9.2 adv_cmds-153/ps/print.c + // getproclline(). It is an int (argc) followed by the executable’s string + // area. The string area consists of NUL-terminated strings, beginning with + // the executable path, and then starting on an aligned boundary, all of the + // elements of argv, envp, and applev. + + // It is possible for a process to exec() in between the two sysctl() calls + // below. If that happens, and the string area of the new program is larger + // than that of the old one, args_size_estimate will be too small. To detect + // this situation, the second sysctl() attempts to fetch args_size_estimate + + // 1 bytes, expecting to only receive args_size_estimate. If it gets the extra + // byte, it indicates that the string area has grown, and the sysctl() pair + // will be retried a limited number of times. + + size_t args_size_estimate; + size_t args_size; + std::string args; + int tries = 3; + const pid_t pid = ProcessID(); + do { + int mib[] = {CTL_KERN, KERN_PROCARGS2, pid}; + int rv = + sysctl(mib, arraysize(mib), nullptr, &args_size_estimate, nullptr, 0); + if (rv != 0) { + PLOG(ERROR) << "sysctl (size) for pid " << pid; + return false; + } + + args_size = args_size_estimate + 1; + args.resize(args_size); + rv = sysctl(mib, arraysize(mib), &args[0], &args_size, nullptr, 0); + if (rv != 0) { + PLOG(ERROR) << "sysctl (data) for pid " << pid; + return false; + } + } while (args_size == args_size_estimate + 1 && tries--); + + if (args_size == args_size_estimate + 1) { + LOG(ERROR) << "unexpected args_size"; + return false; + } + + // KERN_PROCARGS2 needs to at least contain argc. + if (args_size < sizeof(int)) { + LOG(ERROR) << "tiny args_size"; + return false; + } + args.resize(args_size); + + // Get argc. + int argc; + memcpy(&argc, &args[0], sizeof(argc)); + + // Find the end of the executable path. + size_t start_pos = sizeof(argc); + size_t nul_pos = args.find('\0', start_pos); + if (nul_pos == std::string::npos) { + LOG(ERROR) << "unterminated executable path"; + return false; + } + + // Find the beginning of the string area. + start_pos = args.find_first_not_of('\0', nul_pos); + if (start_pos == std::string::npos) { + LOG(ERROR) << "no string area"; + return false; + } + + std::vector<std::string> local_argv; + while (argc-- && nul_pos != std::string::npos) { + nul_pos = args.find('\0', start_pos); + local_argv.push_back(args.substr(start_pos, nul_pos - start_pos)); + start_pos = nul_pos + 1; + } + + if (argc >= 0) { + // Not every argument was recovered. + LOG(ERROR) << "did not recover all arguments"; + return false; + } + + argv->swap(local_argv); + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_test.cc b/third_party/crashpad/crashpad/util/posix/process_info_test.cc new file mode 100644 index 0000000..dd0844b --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/process_info_test.cc
@@ -0,0 +1,148 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/posix/process_info.h" + +#include <time.h> +#include <unistd.h> + +#include <set> +#include <string> +#include <vector> + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/misc/implicit_cast.h" + +#if defined(OS_MACOSX) +#include <crt_externs.h> +#endif + +namespace crashpad { +namespace test { +namespace { + +void TestSelfProcess(const ProcessInfo& process_info) { + EXPECT_EQ(getpid(), process_info.ProcessID()); + EXPECT_EQ(getppid(), process_info.ParentProcessID()); + + // There’s no system call to obtain the saved set-user ID or saved set-group + // ID in an easy way. Normally, they are the same as the effective user ID and + // effective group ID, so just check against those. + EXPECT_EQ(getuid(), process_info.RealUserID()); + const uid_t euid = geteuid(); + EXPECT_EQ(euid, process_info.EffectiveUserID()); + EXPECT_EQ(euid, process_info.SavedUserID()); + const gid_t gid = getgid(); + EXPECT_EQ(gid, process_info.RealGroupID()); + const gid_t egid = getegid(); + EXPECT_EQ(egid, process_info.EffectiveGroupID()); + EXPECT_EQ(egid, process_info.SavedGroupID()); + + // Test SupplementaryGroups(). + int group_count = getgroups(0, nullptr); + ASSERT_GE(group_count, 0) << ErrnoMessage("getgroups"); + + std::vector<gid_t> group_vector(group_count); + if (group_count > 0) { + group_count = getgroups(group_vector.size(), &group_vector[0]); + ASSERT_GE(group_count, 0) << ErrnoMessage("getgroups"); + ASSERT_EQ(group_vector.size(), implicit_cast<size_t>(group_count)); + } + + std::set<gid_t> group_set(group_vector.begin(), group_vector.end()); + EXPECT_EQ(group_set, process_info.SupplementaryGroups()); + + // Test AllGroups(), which is SupplementaryGroups() plus the real, effective, + // and saved set-group IDs. The effective and saved set-group IDs are expected + // to be identical (see above). + group_set.insert(gid); + group_set.insert(egid); + + EXPECT_EQ(group_set, process_info.AllGroups()); + + // The test executable isn’t expected to change privileges. + EXPECT_FALSE(process_info.DidChangePrivileges()); + +#if defined(ARCH_CPU_64_BITS) + EXPECT_TRUE(process_info.Is64Bit()); +#else + EXPECT_FALSE(process_info.Is64Bit()); +#endif + + // Test StartTime(). This program must have started at some time in the past. + timeval start_time; + process_info.StartTime(&start_time); + time_t now; + time(&now); + EXPECT_LE(start_time.tv_sec, now); + + std::vector<std::string> argv; + ASSERT_TRUE(process_info.Arguments(&argv)); + + // gtest argv processing scrambles argv, but it leaves argc and argv[0] + // intact, so test those. + +#if defined(OS_MACOSX) + int expect_argc = *_NSGetArgc(); + char** expect_argv = *_NSGetArgv(); +#else +#error Obtain expect_argc and expect_argv correctly on your system. +#endif + + int argc = implicit_cast<int>(argv.size()); + EXPECT_EQ(expect_argc, argc); + + ASSERT_GE(expect_argc, 1); + ASSERT_GE(argc, 1); + + EXPECT_EQ(std::string(expect_argv[0]), argv[0]); +} + + +TEST(ProcessInfo, Self) { + ProcessInfo process_info; + ASSERT_TRUE(process_info.Initialize(getpid())); + TestSelfProcess(process_info); +} + +#if defined(OS_MACOSX) +TEST(ProcessInfo, SelfTask) { + ProcessInfo process_info; + ASSERT_TRUE(process_info.InitializeFromTask(mach_task_self())); + TestSelfProcess(process_info); +} +#endif + +TEST(ProcessInfo, Pid1) { + // PID 1 is expected to be init or the system’s equivalent. This tests reading + // information about another process. + ProcessInfo process_info; + ASSERT_TRUE(process_info.Initialize(1)); + + EXPECT_EQ(implicit_cast<pid_t>(1), process_info.ProcessID()); + EXPECT_EQ(implicit_cast<pid_t>(0), process_info.ParentProcessID()); + EXPECT_EQ(implicit_cast<uid_t>(0), process_info.RealUserID()); + EXPECT_EQ(implicit_cast<uid_t>(0), process_info.EffectiveUserID()); + EXPECT_EQ(implicit_cast<uid_t>(0), process_info.SavedUserID()); + EXPECT_EQ(implicit_cast<gid_t>(0), process_info.RealGroupID()); + EXPECT_EQ(implicit_cast<gid_t>(0), process_info.EffectiveGroupID()); + EXPECT_EQ(implicit_cast<gid_t>(0), process_info.SavedGroupID()); + EXPECT_FALSE(process_info.AllGroups().empty()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc new file mode 100644 index 0000000..326be9f3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
@@ -0,0 +1,167 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/posix/symbolic_constants_posix.h" + +#include <sys/signal.h> + +#include "base/basictypes.h" +#include "base/strings/stringprintf.h" +#include "util/misc/implicit_cast.h" +#include "util/stdlib/string_number_conversion.h" + +namespace { + +const char* kSignalNames[] = { + nullptr, + +#if defined(OS_MACOSX) + // sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' + // /usr/include/sys/signal.h + // and fix up by removing the entry for SIGPOLL. + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "URG", + "STOP", + "TSTP", + "CONT", + "CHLD", + "TTIN", + "TTOU", + "IO", + "XCPU", + "XFSZ", + "VTALRM", + "PROF", + "WINCH", + "INFO", + "USR1", + "USR2", +#elif defined(OS_LINUX) + // sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' + // /usr/include/asm-generic/signal.h + // and fix up by removing SIGIOT, SIGLOST, SIGUNUSED, and SIGRTMIN. + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "BUS", + "FPE", + "KILL", + "USR1", + "SEGV", + "USR2", + "PIPE", + "ALRM", + "TERM", + "STKFLT", + "CHLD", + "CONT", + "STOP", + "TSTP", + "TTIN", + "TTOU", + "URG", + "XCPU", + "XFSZ", + "VTALRM", + "PROF", + "WINCH", + "IO", + "PWR", + "SYS", +#endif +}; +#if defined(OS_LINUX) +// NSIG is 64 to account for real-time signals. +static_assert(arraysize(kSignalNames) == 32, "kSignalNames length"); +#else +static_assert(arraysize(kSignalNames) == NSIG, "kSignalNames length"); +#endif + +const char kSigPrefix[] = "SIG"; + +} // namespace + +namespace crashpad { + +std::string SignalToString(int signal, + SymbolicConstantToStringOptions options) { + const char* signal_name = + implicit_cast<size_t>(signal) < arraysize(kSignalNames) + ? kSignalNames[signal] + : nullptr; + if (!signal_name) { + if (options & kUnknownIsNumeric) { + return base::StringPrintf("%d", signal); + } + return std::string(); + } + + if (options & kUseShortName) { + return std::string(signal_name); + } + return base::StringPrintf("%s%s", kSigPrefix, signal_name); +} + +bool StringToSignal(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + int* signal) { + if ((options & kAllowFullName) || (options & kAllowShortName)) { + bool can_match_full = + (options & kAllowFullName) && + string.substr(0, strlen(kSigPrefix)).compare(kSigPrefix) == 0; + base::StringPiece short_string = + can_match_full ? string.substr(strlen(kSigPrefix)) : string; + for (int index = 0; + index < implicit_cast<int>(arraysize(kSignalNames)); + ++index) { + const char* signal_name = kSignalNames[index]; + if (!signal_name) { + continue; + } + if (can_match_full && short_string.compare(signal_name) == 0) { + *signal = index; + return true; + } + if ((options & kAllowShortName) && string.compare(signal_name) == 0) { + *signal = index; + return true; + } + } + } + + if (options & kAllowNumber) { + return StringToNumber(string, signal); + } + + return false; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h new file mode 100644 index 0000000..4fb4a358 --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h
@@ -0,0 +1,48 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_ +#define CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_ + +#include <string> + +#include "base/strings/string_piece.h" +#include "util/misc/symbolic_constants_common.h" + +namespace crashpad { + +//! \brief Converts a POSIX signal value to a textual representation. +//! +//! \param[in] signal The signal value to convert. +//! \param[in] options Options affecting the conversion. ::kUseOr is ignored. +//! For ::kUnknownIsNumeric, the format is `"%d"`. +//! +//! \return The converted string. +std::string SignalToString(int signal, SymbolicConstantToStringOptions options); + +//! \brief Converts a string to its corresponding POSIX signal value. +//! +//! \param[in] string The string to convert. +//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored. +//! \param[out] signal The converted POSIX signal value. +//! +//! \return `true` on success, `false` if \a string could not be converted as +//! requested. +bool StringToSignal(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + int* signal); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc new file mode 100644 index 0000000..db3193d --- /dev/null +++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
@@ -0,0 +1,256 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/posix/symbolic_constants_posix.h" + +#include <sys/signal.h> + +#include "base/basictypes.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +#define NUL_TEST_DATA(string) { string, arraysize(string) - 1 } + +namespace crashpad { +namespace test { +namespace { + +const struct { + int signal; + const char* full_name; + const char* short_name; +} kSignalTestData[] = { + {SIGABRT, "SIGABRT", "ABRT"}, + {SIGALRM, "SIGALRM", "ALRM"}, + {SIGBUS, "SIGBUS", "BUS"}, + {SIGCHLD, "SIGCHLD", "CHLD"}, + {SIGCONT, "SIGCONT", "CONT"}, + {SIGFPE, "SIGFPE", "FPE"}, + {SIGHUP, "SIGHUP", "HUP"}, + {SIGILL, "SIGILL", "ILL"}, + {SIGINT, "SIGINT", "INT"}, + {SIGIO, "SIGIO", "IO"}, + {SIGKILL, "SIGKILL", "KILL"}, + {SIGPIPE, "SIGPIPE", "PIPE"}, + {SIGPROF, "SIGPROF", "PROF"}, + {SIGQUIT, "SIGQUIT", "QUIT"}, + {SIGSEGV, "SIGSEGV", "SEGV"}, + {SIGSTOP, "SIGSTOP", "STOP"}, + {SIGSYS, "SIGSYS", "SYS"}, + {SIGTERM, "SIGTERM", "TERM"}, + {SIGTRAP, "SIGTRAP", "TRAP"}, + {SIGTSTP, "SIGTSTP", "TSTP"}, + {SIGTTIN, "SIGTTIN", "TTIN"}, + {SIGTTOU, "SIGTTOU", "TTOU"}, + {SIGURG, "SIGURG", "URG"}, + {SIGUSR1, "SIGUSR1", "USR1"}, + {SIGUSR2, "SIGUSR2", "USR2"}, + {SIGVTALRM, "SIGVTALRM", "VTALRM"}, + {SIGWINCH, "SIGWINCH", "WINCH"}, + {SIGXCPU, "SIGXCPU", "XCPU"}, +#if defined(OS_MACOSX) + {SIGEMT, "SIGEMT", "EMT"}, + {SIGINFO, "SIGINFO", "INFO"}, +#elif defined(OS_LINUX) + {SIGPWR, "SIGPWR", "PWR"}, + {SIGSTKFLT, "SIGSTKFLT", "STKFLT"}, +#endif +}; + +// If |expect| is nullptr, the conversion is expected to fail. If |expect| is +// empty, the conversion is expected to succeed, but the precise returned string +// value is unknown. Otherwise, the conversion is expected to succeed, and +// |expect| contains the precise expected string value to be returned. +// +// Only set kUseFullName or kUseShortName when calling this. Other options are +// exercised directly by this function. +void TestSignalToStringOnce(int value, + const char* expect, + SymbolicConstantToStringOptions options) { + std::string actual = SignalToString(value, options | kUnknownIsEmpty); + std::string actual_numeric = + SignalToString(value, options | kUnknownIsNumeric); + if (expect) { + if (expect[0] == '\0') { + EXPECT_FALSE(actual.empty()) << "signal " << value; + } else { + EXPECT_EQ(expect, actual) << "signal " << value; + } + EXPECT_EQ(actual, actual_numeric) << "signal " << value; + } else { + EXPECT_TRUE(actual.empty()) << "signal " << value << ", actual " << actual; + EXPECT_FALSE(actual_numeric.empty()) + << "signal " << value << ", actual_numeric " << actual_numeric; + } +} + +void TestSignalToString(int value, + const char* expect_full, + const char* expect_short) { + { + SCOPED_TRACE("full_name"); + TestSignalToStringOnce(value, expect_full, kUseFullName); + } + + { + SCOPED_TRACE("short_name"); + TestSignalToStringOnce(value, expect_short, kUseShortName); + } +} + +TEST(SymbolicConstantsPOSIX, SignalToString) { + for (size_t index = 0; index < arraysize(kSignalTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestSignalToString(kSignalTestData[index].signal, + kSignalTestData[index].full_name, + kSignalTestData[index].short_name); + } + +#if defined(OS_LINUX) + // NSIG is 64 to account for real-time signals. + const int kSignalCount = 32; +#else + const int kSignalCount = NSIG; +#endif + + for (int signal = 0; signal < kSignalCount + 8; ++signal) { + SCOPED_TRACE(base::StringPrintf("signal %d", signal)); + if (signal > 0 && signal < kSignalCount) { + TestSignalToString(signal, "", ""); + } else { + TestSignalToString(signal, nullptr, nullptr); + } + } +} + +void TestStringToSignal(const base::StringPiece& string, + StringToSymbolicConstantOptions options, + bool expect_result, + int expect_value) { + int actual_value; + bool actual_result = StringToSignal(string, options, &actual_value); + if (expect_result) { + EXPECT_TRUE(actual_result) << "string " << string << ", options " << options + << ", signal " << expect_value; + if (actual_result) { + EXPECT_EQ(expect_value, actual_value) << "string " << string + << ", options " << options; + } + } else { + EXPECT_FALSE(actual_result) << "string " << string << ", options " + << options << ", signal " << actual_value; + } +} + +TEST(SymbolicConstantsPOSIX, StringToSignal) { + const StringToSymbolicConstantOptions kOptions[] = { + 0, + kAllowFullName, + kAllowShortName, + kAllowFullName | kAllowShortName, + kAllowNumber, + kAllowFullName | kAllowNumber, + kAllowShortName | kAllowNumber, + kAllowFullName | kAllowShortName | kAllowNumber, + }; + + for (size_t option_index = 0; + option_index < arraysize(kOptions); + ++option_index) { + SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index)); + StringToSymbolicConstantOptions options = kOptions[option_index]; + for (size_t index = 0; index < arraysize(kSignalTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + int signal = kSignalTestData[index].signal; + { + SCOPED_TRACE("full_name"); + TestStringToSignal(kSignalTestData[index].full_name, + options, + options & kAllowFullName, + signal); + } + { + SCOPED_TRACE("short_name"); + TestStringToSignal(kSignalTestData[index].short_name, + options, + options & kAllowShortName, + signal); + } + { + SCOPED_TRACE("number"); + std::string number_string = base::StringPrintf("%d", signal); + TestStringToSignal( + number_string, options, options & kAllowNumber, signal); + } + } + + const char* const kNegativeTestData[] = { + "SIGHUP ", + " SIGINT", + "QUIT ", + " ILL", + "SIGSIGTRAP", + "SIGABRTRON", + "FPES", + "SIGGARBAGE", + "random", + "", + }; + + for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + TestStringToSignal(kNegativeTestData[index], options, false, 0); + } + + const struct { + const char* string; + size_t length; + } kNULTestData[] = { + NUL_TEST_DATA("\0SIGBUS"), + NUL_TEST_DATA("SIG\0BUS"), + NUL_TEST_DATA("SIGB\0US"), + NUL_TEST_DATA("SIGBUS\0"), + NUL_TEST_DATA("\0BUS"), + NUL_TEST_DATA("BUS\0"), + NUL_TEST_DATA("B\0US"), + NUL_TEST_DATA("\0002"), + NUL_TEST_DATA("2\0"), + NUL_TEST_DATA("1\0002"), + }; + + for (size_t index = 0; index < arraysize(kNULTestData); ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + base::StringPiece string(kNULTestData[index].string, + kNULTestData[index].length); + TestStringToSignal(string, options, false, 0); + } + } + + // Ensure that a NUL is not required at the end of the string. + { + SCOPED_TRACE("trailing_NUL_full"); + TestStringToSignal( + base::StringPiece("SIGBUST", 6), kAllowFullName, true, SIGBUS); + } + { + SCOPED_TRACE("trailing_NUL_short"); + TestStringToSignal( + base::StringPiece("BUST", 3), kAllowShortName, true, SIGBUS); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/cxx.h b/third_party/crashpad/crashpad/util/stdlib/cxx.h new file mode 100644 index 0000000..0e1cc43 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/cxx.h
@@ -0,0 +1,70 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_CXX_H_ +#define CRASHPAD_UTIL_STDLIB_CXX_H_ + +#include "build/build_config.h" + +#if defined(COMPILER_MSVC) + +#define CXX_LIBRARY_VERSION 2011 +#define CXX_LIBRARY_HAS_CONSTEXPR 0 + +#else // !COMPILER_MSVC + +// <ciso646> doesn’t do very much, and under libc++, it will cause the +// _LIBCPP_VERSION macro to be defined properly. Under libstdc++, it doesn’t +// cause __GLIBCXX__ to be defined, but if _LIBCPP_VERSION isn’t defined after +// #including <ciso646>, assume libstdc++ and #include libstdc++’s internal +// header that defines __GLIBCXX__. + +#include <ciso646> + +#if !defined(_LIBCPP_VERSION) +#if defined(__has_include) +#if __has_include(<bits/c++config.h>) +#include <bits/c++config.h> +#endif +#else +#include <bits/c++config.h> +#endif +#endif + +// libstdc++ does not identify its version directly, but identifies the GCC +// package it is a part of via the __GLIBCXX__ macro, which is based on the date +// of the GCC release. libstdc++ had sufficient C++11 support as of GCC 4.6.0, +// __GLIBCXX__ 20110325, but maintenance releases in the 4.4 an 4.5 series +// followed this date, so check for those versions by their date stamps. +// http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning +// +// libc++, identified by _LIBCPP_VERSION, always supports C++11. +#if __cplusplus >= 201103l && \ + ((defined(__GLIBCXX__) && \ + __GLIBCXX__ >= 20110325ul && /* GCC >= 4.6.0 */ \ + __GLIBCXX__ != 20110416ul && /* GCC 4.4.6 */ \ + __GLIBCXX__ != 20120313ul && /* GCC 4.4.7 */ \ + __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \ + __GLIBCXX__ != 20120702ul) || /* GCC 4.5.4 */ \ + (defined(_LIBCPP_VERSION))) +#define CXX_LIBRARY_VERSION 2011 +#define CXX_LIBRARY_HAS_CONSTEXPR 1 +#else +#define CXX_LIBRARY_VERSION 2003 +#define CXX_LIBRARY_HAS_CONSTEXPR 0 +#endif + +#endif // COMPILER_MSVC + +#endif // CRASHPAD_UTIL_STDLIB_CXX_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/map_insert.h b/third_party/crashpad/crashpad/util/stdlib/map_insert.h new file mode 100644 index 0000000..956271b3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/map_insert.h
@@ -0,0 +1,56 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_ +#define CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_ + +#include <map> +#include <utility> + +namespace crashpad { + +//! \brief Inserts a mapping from \a key to \a value into \a map, or replaces +//! an existing mapping so that \a key maps to \value. +//! +//! This behaves similarly to `std::map<>::insert_or_assign()` proposed for +//! C++17, except that the \a old_value parameter is added. +//! +//! \param[inout] map The map to operate on. +//! \param[in] key The key that should be mapped to \a value. +//! \param[in] value The value that \a key should map to. +//! \param[out] old_value If \a key was previously present in \a map, this will +//! be set to its previous value. This parameter is optional and may be +//! `nullptr` if this information is not required. +//! +//! \return `false` if \a key was previously present in \a map. If \a old_value +//! is not `nullptr`, it will be set to the previous value. `true` if \a +//! key was not present in the map and was inserted. +template <typename T> +bool MapInsertOrReplace(T* map, + const typename T::key_type& key, + const typename T::mapped_type& value, + typename T::mapped_type* old_value) { + const auto result = map->insert(std::make_pair(key, value)); + if (!result.second) { + if (old_value) { + *old_value = result.first->second; + } + result.first->second = value; + } + return result.second; +} + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc b/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc new file mode 100644 index 0000000..d3197b9 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc
@@ -0,0 +1,54 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/map_insert.h" + +#include <string> + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MapInsert, MapInsertOrReplace) { + std::map<std::string, int> map; + int old_value; + EXPECT_TRUE(MapInsertOrReplace(&map, "key", 1, &old_value)); + std::map<std::string, int> expect_map; + expect_map["key"] = 1; + EXPECT_EQ(expect_map, map); + + EXPECT_FALSE(MapInsertOrReplace(&map, "key", 2, &old_value)); + EXPECT_EQ(1, old_value); + expect_map["key"] = 2; + EXPECT_EQ(expect_map, map); + + EXPECT_TRUE(MapInsertOrReplace(&map, "another", 3, &old_value)); + expect_map["another"] = 3; + EXPECT_EQ(expect_map, map); + + // Make sure nullptr is accepted as old_value. + EXPECT_TRUE(MapInsertOrReplace(&map, "yet another", 5, nullptr)); + expect_map["yet another"] = 5; + EXPECT_EQ(expect_map, map); + + EXPECT_FALSE(MapInsertOrReplace(&map, "yet another", 6, nullptr)); + expect_map["yet another"] = 6; + EXPECT_EQ(expect_map, map); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/move.h b/third_party/crashpad/crashpad/util/stdlib/move.h new file mode 100644 index 0000000..b6f15bc --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/move.h
@@ -0,0 +1,54 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_MOVE_H_ +#define CRASHPAD_UTIL_STDLIB_MOVE_H_ + +#include "util/stdlib/cxx.h" + +namespace crashpad { + +#if CXX_LIBRARY_VERSION >= 2011 +//! \brief A typedef for std::remove_reference until C++11 library support is +// always available. +template <typename T> +using remove_reference = std::remove_reference<T>; +#else +//! \brief A replacement for std::remove_reference until C++11 library support +// is always available. +template <class T> +struct remove_reference { using type = T; }; +template <class T> +struct remove_reference<T&> { using type = T; }; +#endif // CXX_LIBRARY_VERSION + +#if CXX_LIBRARY_VERSION >= 2011 +//! \brief A wrapper around std::move() until C++11 library support is +// always available. +template <typename T> +typename std::remove_reference<T>::type&& move(T&& t) { + return std::move(t); +} +#else +//! \brief A replacement for std::move() until C++11 library support is +// always available. +template <typename T> +typename remove_reference<T>::type&& move(T&& t) { + return static_cast<typename remove_reference<T>::type&&>(t); +} +#endif // CXX_LIBRARY_VERSION + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STDLIB_MOVE_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/objc.h b/third_party/crashpad/crashpad/util/stdlib/objc.h new file mode 100644 index 0000000..0b44bba --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/objc.h
@@ -0,0 +1,39 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_OBJC_H_ +#define CRASHPAD_UTIL_STDLIB_OBJC_H_ + +#include <AvailabilityMacros.h> +#include <objc/objc.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 + +// In order for the @NO and @YES literals to work, NO and YES must be defined as +// __objc_no and __objc_yes. See +// http://llvm.org/releases/3.1/tools/clang/docs/ObjectiveCLiterals.html . +// +// NO and YES are defined properly for this purpose in the 10.8 SDK, but not in +// earlier SDKs. Because this code is never expected to be compiled with a +// compiler that does not understand the modern forms of these boolean +// constants, but it may be built with an older SDK, replace the outdated SDK +// definitions unconditionally. +#undef NO +#undef YES +#define NO __objc_no +#define YES __objc_yes + +#endif + +#endif // CRASHPAD_UTIL_STDLIB_OBJC_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/pointer_container.h b/third_party/crashpad/crashpad/util/stdlib/pointer_container.h new file mode 100644 index 0000000..cb3e836 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/pointer_container.h
@@ -0,0 +1,57 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_POINTER_CONTAINER_H_ +#define CRASHPAD_UTIL_STDLIB_POINTER_CONTAINER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/stl_util.h" + +namespace crashpad { + +//! \brief Allows a standard container to “own” pointer elements stored in it. +//! +//! When the container is destroyed, `delete` will be called on its pointer +//! elements. +//! +//! \note No attempt is made to `delete` elements that are removed from the +//! container by other means, such as replacement or `clear()`. +template <typename ContainerType> +class PointerContainer : public ContainerType { + public: + PointerContainer() : ContainerType(), pointer_deleter_(this) {} + + ~PointerContainer() {} + + private: + STLElementDeleter<ContainerType> pointer_deleter_; + + DISALLOW_COPY_AND_ASSIGN(PointerContainer); +}; + +//! \brief Allows a `std::vector` to “own” pointer elements stored in it. +//! +//! When the vector is destroyed, `delete` will be called on its pointer +//! elements. +//! +//! \note No attempt is made to `delete` elements that are removed from the +//! vector by other means, such as replacement or `clear()`. +template <typename T> +using PointerVector = PointerContainer<std::vector<T*>>; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STDLIB_POINTER_CONTAINER_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc new file mode 100644 index 0000000..8ab949e --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
@@ -0,0 +1,156 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/string_number_conversion.h" + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <limits> + +#include "base/logging.h" +#include "util/stdlib/cxx.h" + +// CONSTEXPR_STATIC_ASSERT will be a normal static_assert if the C++ library is +// the C++11 library. If using an older C++ library, the +// std::numeric_limits<>::min() and max() functions will not be marked as +// constexpr, and thus won’t be usable with static_assert(). In that case, a +// run-time CHECK() will have to do. +#if CXX_LIBRARY_HAS_CONSTEXPR +#define CONSTEXPR_STATIC_ASSERT(condition, message) \ + static_assert(condition, message) +#else +#define CONSTEXPR_STATIC_ASSERT(condition, message) CHECK(condition) << message +#endif + +namespace { + +template <typename TIntType, typename TLongType> +struct StringToIntegerTraits { + using IntType = TIntType; + using LongType = TLongType; + static void TypeCheck() { + static_assert(std::numeric_limits<TIntType>::is_integer && + std::numeric_limits<TLongType>::is_integer, + "IntType and LongType must be integer"); + static_assert(std::numeric_limits<TIntType>::is_signed == + std::numeric_limits<TLongType>::is_signed, + "IntType and LongType signedness must agree"); + CONSTEXPR_STATIC_ASSERT(std::numeric_limits<TIntType>::min() >= + std::numeric_limits<TLongType>::min() && + std::numeric_limits<TIntType>::min() < + std::numeric_limits<TLongType>::max(), + "IntType min must be in LongType range"); + CONSTEXPR_STATIC_ASSERT(std::numeric_limits<TIntType>::max() > + std::numeric_limits<TLongType>::min() && + std::numeric_limits<TIntType>::max() <= + std::numeric_limits<TLongType>::max(), + "IntType max must be in LongType range"); + } +}; + +template <typename TIntType, typename TLongType> +struct StringToSignedIntegerTraits + : public StringToIntegerTraits<TIntType, TLongType> { + static void TypeCheck() { + static_assert(std::numeric_limits<TIntType>::is_signed, + "StringToSignedTraits IntType must be signed"); + return super::TypeCheck(); + } + static bool IsNegativeOverflow(TLongType value) { + return value < std::numeric_limits<TIntType>::min(); + } + + private: + using super = StringToIntegerTraits<TIntType, TLongType>; +}; + +template <typename TIntType, typename TLongType> +struct StringToUnsignedIntegerTraits + : public StringToIntegerTraits<TIntType, TLongType> { + static void TypeCheck() { + static_assert(!std::numeric_limits<TIntType>::is_signed, + "StringToUnsignedTraits IntType must be unsigned"); + return super::TypeCheck(); + } + static bool IsNegativeOverflow(TLongType value) { return false; } + + private: + using super = StringToIntegerTraits<TIntType, TLongType>; +}; + +struct StringToIntTraits : public StringToSignedIntegerTraits<int, long> { + static LongType Convert(const char* str, char** end, int base) { + return strtol(str, end, base); + } +}; + +struct StringToUnsignedIntTraits + : public StringToUnsignedIntegerTraits<unsigned int, unsigned long> { + static LongType Convert(const char* str, char** end, int base) { + if (str[0] == '-') { + return 0; + } + return strtoul(str, end, base); + } +}; + +template <typename Traits> +bool StringToIntegerInternal(const base::StringPiece& string, + typename Traits::IntType* number) { + using IntType = typename Traits::IntType; + using LongType = typename Traits::LongType; + + Traits::TypeCheck(); + + if (string.empty() || isspace(string[0])) { + return false; + } + + if (string[string.length()] != '\0') { + // The implementations use the C standard library’s conversion routines, + // which rely on the strings having a trailing NUL character. std::string + // will NUL-terminate. + std::string terminated_string(string.data(), string.length()); + return StringToIntegerInternal<Traits>(terminated_string, number); + } + + errno = 0; + char* end; + LongType result = Traits::Convert(string.data(), &end, 0); + if (Traits::IsNegativeOverflow(result) || + result > std::numeric_limits<IntType>::max() || + errno == ERANGE || + end != string.data() + string.length()) { + return false; + } + *number = result; + return true; +} + +} // namespace + +namespace crashpad { + +bool StringToNumber(const base::StringPiece& string, int* number) { + return StringToIntegerInternal<StringToIntTraits>(string, number); +} + +bool StringToNumber(const base::StringPiece& string, unsigned int* number) { + return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h new file mode 100644 index 0000000..dbc4611e --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
@@ -0,0 +1,63 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_ +#define CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_ + +#include "base/strings/string_piece.h" + +namespace crashpad { + +// Convert between strings and numbers. +// +// These functions will only set *number if a perfect conversion can be +// performed. A perfect conversion contains no leading or trailing characters +// (including whitespace) other than the number to convert, and does not +// overflow the targeted data type. If a perfect conversion is possible, *number +// is set and these functions return true. Otherwise, they return false. +// +// The interface in base/strings/string_number_conversions.h doesn’t allow +// arbitrary bases based on whether the string begins with prefixes such as "0x" +// as strtol does with base = 0. The functions here are implemented on the +// strtol family with base = 0, and thus do accept such input. + +//! \{ +//! \brief Convert a string to a number. +//! +//! A conversion will only be performed if it can be done perfectly: if \a +//! string contains no leading or trailing characters (including whitespace) +//! other than the number to convert, and does not overflow the targeted data +//! type. +//! +//! \param[in] string The string to convert to a number. As in `strtol()` with a +//! `base` parameter of `0`, the string is treated as decimal unless it +//! begins with a `"0x"` or `"0X"` prefix, in which case it is treated as +//! hexadecimal, or a `"0"` prefix, in which case it is treated as octal. +//! \param[out] number The converted number. This will only be set if a perfect +//! conversion can be performed. +//! +//! \return `true` if a perfect conversion could be performed, with \a number +//! set appropriately. `false` if a perfect conversion was not possible. +//! +//! \note The interface in `base/strings/string_number_conversions.h` doesn’t +//! allow arbitrary bases based on whether the string begins with a prefix +//! indicating its base. The functions here are provided for situations +//! where such prefix recognition is desirable. +bool StringToNumber(const base::StringPiece& string, int* number); +bool StringToNumber(const base::StringPiece& string, unsigned int* number); +//! \} + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc new file mode 100644 index 0000000..97d3152 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
@@ -0,0 +1,224 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/string_number_conversion.h" + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +#include <limits> + +namespace crashpad { +namespace test { +namespace { + +TEST(StringNumberConversion, StringToInt) { + const struct { + const char* string; + bool valid; + int value; + } kTestData[] = { + {"", false, 0}, + {"0", true, 0}, + {"1", true, 1}, + {"2147483647", true, std::numeric_limits<int>::max()}, + {"2147483648", false, 0}, + {"4294967295", false, 0}, + {"4294967296", false, 0}, + {"-1", true, -1}, + {"-2147483648", true, std::numeric_limits<int>::min()}, + {"-2147483649", false, 0}, + {"00", true, 0}, + {"01", true, 1}, + {"-01", true, -1}, + {"+2", true, 2}, + {"0x10", true, 16}, + {"-0x10", true, -16}, + {"+0x20", true, 32}, + {"0xf", true, 15}, + {"0xg", false, 0}, + {"0x7fffffff", true, std::numeric_limits<int>::max()}, + {"0x7FfFfFfF", true, std::numeric_limits<int>::max()}, + {"0x80000000", false, 0}, + {"0xFFFFFFFF", false, 0}, + {"-0x7fffffff", true, -2147483647}, + {"-0x80000000", true, std::numeric_limits<int>::min()}, + {"-0x80000001", false, 0}, + {"-0xffffffff", false, 0}, + {"0x100000000", false, 0}, + {"0xabcdef", true, 11259375}, + {"010", true, 8}, + {"-010", true, -8}, + {"+020", true, 16}, + {"07", true, 7}, + {"08", false, 0}, + {" 0", false, 0}, + {"0 ", false, 0}, + {" 0 ", false, 0}, + {" 1", false, 0}, + {"1 ", false, 0}, + {" 1 ", false, 0}, + {"a2", false, 0}, + {"2a", false, 0}, + {"2a2", false, 0}, + {".0", false, 0}, + {".1", false, 0}, + {"-.2", false, 0}, + {"+.3", false, 0}, + {"1.23", false, 0}, + {"-273.15", false, 0}, + {"+98.6", false, 0}, + {"1e1", false, 0}, + {"1E1", false, 0}, + {"0x123p4", false, 0}, + {"infinity", false, 0}, + {"NaN", false, 0}, + {"-9223372036854775810", false, 0}, + {"-9223372036854775809", false, 0}, + {"9223372036854775808", false, 0}, + {"9223372036854775809", false, 0}, + {"18446744073709551615", false, 0}, + {"18446744073709551616", false, 0}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + int value; + bool valid = StringToNumber(kTestData[index].string, &value); + if (kTestData[index].valid) { + EXPECT_TRUE(valid) << "index " << index << ", string " + << kTestData[index].string; + if (valid) { + EXPECT_EQ(kTestData[index].value, value) + << "index " << index << ", string " << kTestData[index].string; + } + } else { + EXPECT_FALSE(valid) << "index " << index << ", string " + << kTestData[index].string << ", value " << value; + } + } + + // Ensure that embedded NUL characters are treated as bad input. The string + // is split to avoid MSVC warning: + // "decimal digit terminates octal escape sequence". + const char input[] = "6\000" "6"; + base::StringPiece input_string(input, arraysize(input) - 1); + int output; + EXPECT_FALSE(StringToNumber(input_string, &output)); + + // Ensure that a NUL is not required at the end of the string. + EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output)); + EXPECT_EQ(6, output); +} + +TEST(StringNumberConversion, StringToUnsignedInt) { + const struct { + const char* string; + bool valid; + unsigned int value; + } kTestData[] = { + {"", false, 0}, + {"0", true, 0}, + {"1", true, 1}, + {"2147483647", true, 2147483647}, + {"2147483648", true, 2147483648}, + {"4294967295", true, std::numeric_limits<unsigned int>::max()}, + {"4294967296", false, 0}, + {"-1", false, 0}, + {"-2147483648", false, 0}, + {"-2147483649", false, 0}, + {"00", true, 0}, + {"01", true, 1}, + {"-01", false, 0}, + {"+2", true, 2}, + {"0x10", true, 16}, + {"-0x10", false, 0}, + {"+0x20", true, 32}, + {"0xf", true, 15}, + {"0xg", false, 0}, + {"0x7fffffff", true, 0x7fffffff}, + {"0x7FfFfFfF", true, 0x7fffffff}, + {"0x80000000", true, 0x80000000}, + {"0xFFFFFFFF", true, 0xffffffff}, + {"-0x7fffffff", false, 0}, + {"-0x80000000", false, 0}, + {"-0x80000001", false, 0}, + {"-0xffffffff", false, 0}, + {"0x100000000", false, 0}, + {"0xabcdef", true, 11259375}, + {"010", true, 8}, + {"-010", false, 0}, + {"+020", true, 16}, + {"07", true, 7}, + {"08", false, 0}, + {" 0", false, 0}, + {"0 ", false, 0}, + {" 0 ", false, 0}, + {" 1", false, 0}, + {"1 ", false, 0}, + {" 1 ", false, 0}, + {"a2", false, 0}, + {"2a", false, 0}, + {"2a2", false, 0}, + {".0", false, 0}, + {".1", false, 0}, + {"-.2", false, 0}, + {"+.3", false, 0}, + {"1.23", false, 0}, + {"-273.15", false, 0}, + {"+98.6", false, 0}, + {"1e1", false, 0}, + {"1E1", false, 0}, + {"0x123p4", false, 0}, + {"infinity", false, 0}, + {"NaN", false, 0}, + {"-9223372036854775810", false, 0}, + {"-9223372036854775809", false, 0}, + {"9223372036854775808", false, 0}, + {"9223372036854775809", false, 0}, + {"18446744073709551615", false, 0}, + {"18446744073709551616", false, 0}, + }; + + for (size_t index = 0; index < arraysize(kTestData); ++index) { + unsigned int value; + bool valid = StringToNumber(kTestData[index].string, &value); + if (kTestData[index].valid) { + EXPECT_TRUE(valid) << "index " << index << ", string " + << kTestData[index].string; + if (valid) { + EXPECT_EQ(kTestData[index].value, value) + << "index " << index << ", string " << kTestData[index].string; + } + } else { + EXPECT_FALSE(valid) << "index " << index << ", string " + << kTestData[index].string << ", value " << value; + } + } + + // Ensure that embedded NUL characters are treated as bad input. The string + // is split to avoid MSVC warning: + // "decimal digit terminates octal escape sequence". + const char input[] = "6\000" "6"; + base::StringPiece input_string(input, arraysize(input) - 1); + unsigned int output; + EXPECT_FALSE(StringToNumber(input_string, &output)); + + // Ensure that a NUL is not required at the end of the string. + EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output)); + EXPECT_EQ(6u, output); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc b/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc new file mode 100644 index 0000000..be0dfb2 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc
@@ -0,0 +1,53 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/strlcpy.h" + +#include "base/logging.h" +#include "build/build_config.h" + +#if defined(OS_WIN) && defined(WCHAR_T_IS_UTF16) +#include <strsafe.h> +#endif + +namespace crashpad { + +#if defined(OS_WIN) && defined(WCHAR_T_IS_UTF16) + +size_t c16lcpy(base::char16* destination, + const base::char16* source, + size_t length) { + HRESULT result = StringCchCopyW(destination, length, source); + CHECK(result == S_OK || result == STRSAFE_E_INSUFFICIENT_BUFFER); + return wcslen(source); +} + +#elif defined(WCHAR_T_IS_UTF32) + +size_t c16lcpy(base::char16* destination, + const base::char16* source, + size_t length) { + size_t source_length = base::c16len(source); + if (source_length < length) { + base::c16memcpy(destination, source, source_length + 1); + } else if (length != 0) { + base::c16memcpy(destination, source, length - 1); + destination[length - 1] = '\0'; + } + return source_length; +} + +#endif // WCHAR_T_IS_UTF32 + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/strlcpy.h b/third_party/crashpad/crashpad/util/stdlib/strlcpy.h new file mode 100644 index 0000000..3fcc270 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/strlcpy.h
@@ -0,0 +1,54 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_STRLCPY_H_ +#define CRASHPAD_UTIL_STDLIB_STRLCPY_H_ + +#include <stdint.h> + +#include "base/strings/string16.h" + +namespace crashpad { + +//! \brief Copy a `NUL`-terminated char16-based string to a fixed-size buffer. +//! +//! This function behaves identically to `strlcpy()`, but it operates on char16 +//! data instead of `char` data. It copies the `NUL`-terminated string in the +//! buffer beginning at \a source to the buffer of size \a length at \a +//! destination, ensuring that the destination buffer is `NUL`-terminated. No +//! data will be written outside of the \a destination buffer, but if \a length +//! is smaller than the length of the string at \a source, the string will be +//! truncated. +//! +//! \param[out] destination A pointer to a buffer of at least size \a length +//! char16 units (not bytes). The string will be copied to this buffer, +//! possibly with truncation, and `NUL`-terminated. Nothing will be written +//! following the `NUL` terminator. +//! \param[in] source A pointer to a `NUL`-terminated string of char16 data. The +//! `NUL` terminator must be a `NUL` value in a char16 unit, not just a +//! single `NUL` byte. +//! \param[in] length The length of the \a destination buffer in char16 units, +//! not bytes. A maximum of \a `length - 1` char16 units from \a source will +//! be copied to \a destination. +//! +//! \return The length of the \a source string in char16 units, not including +//! its `NUL` terminator. When truncation occurs, the return value will be +//! equal to or greater than than the \a length parameter. +size_t c16lcpy(base::char16* destination, + const base::char16* source, + size_t length); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STDLIB_STRLCPY_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc b/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc new file mode 100644 index 0000000..b9af201 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc
@@ -0,0 +1,95 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/strlcpy.h" + +#include <string.h> + +#include <algorithm> + +#include "base/basictypes.h" +#include "base/format_macros.h" +#include "base/strings/string16.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(strlcpy, c16lcpy) { + // Use a destination buffer that’s larger than the length passed to c16lcpy. + // The unused portion is a guard area that must not be written to. + struct TestBuffer { + base::char16 lead_guard[64]; + base::char16 data[128]; + base::char16 trail_guard[64]; + }; + TestBuffer expected_untouched; + memset(&expected_untouched, 0xa5, sizeof(expected_untouched)); + + // Test with M, é, Ā, ő, and Ḙ. This is a mix of characters that have zero and + // nonzero low and high bytes. + const base::char16 test_characters[] = {0x4d, 0xe9, 0x100, 0x151, 0x1e18}; + + for (size_t index = 0; index < arraysize(test_characters); ++index) { + base::char16 test_character = test_characters[index]; + SCOPED_TRACE(base::StringPrintf( + "character index %" PRIuS ", character 0x%x", index, test_character)); + for (size_t length = 0; length < 256; ++length) { + SCOPED_TRACE( + base::StringPrintf("index %" PRIuS, length)); + base::string16 test_string(length, test_character); + + TestBuffer destination; + memset(&destination, 0xa5, sizeof(destination)); + + EXPECT_EQ(length, + c16lcpy(destination.data, + test_string.c_str(), + arraysize(destination.data))); + + // Make sure that the destination buffer is NUL-terminated, and that as + // much of the test string was copied as could fit. + size_t expected_destination_length = + std::min(length, arraysize(destination.data) - 1); + + EXPECT_EQ('\0', destination.data[expected_destination_length]); + EXPECT_EQ(expected_destination_length, base::c16len(destination.data)); + EXPECT_TRUE(base::c16memcmp(test_string.c_str(), + destination.data, + expected_destination_length) == 0); + + // Make sure that the portion of the destination buffer that was not used + // was not touched. This includes the guard areas and the unused portion + // of the buffer passed to c16lcpy. + EXPECT_TRUE(base::c16memcmp(expected_untouched.lead_guard, + destination.lead_guard, + arraysize(destination.lead_guard)) == 0); + size_t expected_untouched_length = + arraysize(destination.data) - expected_destination_length - 1; + EXPECT_TRUE( + base::c16memcmp(expected_untouched.data, + &destination.data[expected_destination_length + 1], + expected_untouched_length) == 0); + EXPECT_TRUE(base::c16memcmp(expected_untouched.trail_guard, + destination.trail_guard, + arraysize(destination.trail_guard)) == 0); + } + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/stdlib/strnlen.cc b/third_party/crashpad/crashpad/util/stdlib/strnlen.cc new file mode 100644 index 0000000..3016bf63 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/strnlen.cc
@@ -0,0 +1,47 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/strnlen.h" + +#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 +// Redeclare a method only available on OSX 10.7+ to suppress a +// -Wpartial-availability warning. +extern "C" { +size_t strnlen(const char* string, size_t max_length); +} // extern "C" +#endif + +namespace crashpad { + +size_t strnlen(const char* string, size_t max_length) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (::strnlen) { + return ::strnlen(string, max_length); + } +#endif + + for (size_t index = 0; index < max_length; ++index) { + if (string[index] == '\0') { + return index; + } + } + + return max_length; +} + +} // namespace crashpad + +#endif
diff --git a/third_party/crashpad/crashpad/util/stdlib/strnlen.h b/third_party/crashpad/crashpad/util/stdlib/strnlen.h new file mode 100644 index 0000000..f1bbe63 --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/strnlen.h
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STDLIB_STRNLEN_H_ +#define CRASHPAD_UTIL_STDLIB_STRNLEN_H_ + +#include <string.h> + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include <AvailabilityMacros.h> +#endif + +namespace crashpad { + +//! \brief Returns the length of a string, not to exceed a maximum. +//! +//! \param[in] string The string whose length is to be calculated. +//! \param[in] max_length The maximum length to return. +//! +//! \return The length of \a string, determined as the index of the first `NUL` +//! byte found, not exceeding \a max_length. +//! +//! \note This function is provided because it was introduced in POSIX.1-2008, +//! and not all systems’ standard libraries provide an implementation. +size_t strnlen(const char* string, size_t max_length); + +#if !defined(OS_MACOSX) || \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 +inline size_t strnlen(const char* string, size_t max_length) { + return ::strnlen(string, max_length); +} +#endif + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STDLIB_STRNLEN_H_
diff --git a/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc b/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc new file mode 100644 index 0000000..35ca7f1a --- /dev/null +++ b/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc
@@ -0,0 +1,45 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/stdlib/strnlen.h" + +#include <string.h> + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(strnlen, strnlen) { + const char kTestBuffer[] = "abc\0d"; + ASSERT_EQ(3u, strlen(kTestBuffer)); + EXPECT_EQ(0u, crashpad::strnlen(kTestBuffer, 0)); + EXPECT_EQ(1u, crashpad::strnlen(kTestBuffer, 1)); + EXPECT_EQ(2u, crashpad::strnlen(kTestBuffer, 2)); + EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 3)); + EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 4)); + EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 5)); + EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 6)); + + const char kEmptyBuffer[] = "\0"; + ASSERT_EQ(0u, strlen(kEmptyBuffer)); + EXPECT_EQ(0u, crashpad::strnlen(kEmptyBuffer, 0)); + EXPECT_EQ(0u, crashpad::strnlen(kEmptyBuffer, 1)); + EXPECT_EQ(0u, crashpad::strnlen(kEmptyBuffer, 2)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/string/split_string.cc b/third_party/crashpad/crashpad/util/string/split_string.cc new file mode 100644 index 0000000..2123b4d --- /dev/null +++ b/third_party/crashpad/crashpad/util/string/split_string.cc
@@ -0,0 +1,33 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/string/split_string.h" + +namespace crashpad { + +bool SplitString(const std::string& string, + char delimiter, + std::string* left, + std::string* right) { + size_t delimiter_pos = string.find(delimiter); + if (delimiter_pos == 0 || delimiter_pos == std::string::npos) { + return false; + } + + left->assign(string, 0, delimiter_pos); + right->assign(string, delimiter_pos + 1, std::string::npos); + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/string/split_string.h b/third_party/crashpad/crashpad/util/string/split_string.h new file mode 100644 index 0000000..bb72904 --- /dev/null +++ b/third_party/crashpad/crashpad/util/string/split_string.h
@@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_STRING_SPLIT_STRING_H_ +#define CRASHPAD_UTIL_STRING_SPLIT_STRING_H_ + +#include <string> + +namespace crashpad { + +//! \brief Splits a string into two parts at the first delimiter found. +//! +//! \param[in] string The string to split. +//! \param[in] delimiter The delimiter to split at. +//! \param[out] left The portion of \a string up to, but not including, the +//! first \a delimiter character. +//! \param[out] right The portion of \a string after the first \a delimiter +//! character. +//! +//! \return `true` if \a string was split successfully. `false` if \a string +//! did not contain a \delimiter character or began with a \delimiter +//! character. +bool SplitString(const std::string& string, + char delimiter, + std::string* left, + std::string* right); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
diff --git a/third_party/crashpad/crashpad/util/string/split_string_test.cc b/third_party/crashpad/crashpad/util/string/split_string_test.cc new file mode 100644 index 0000000..7d205319 --- /dev/null +++ b/third_party/crashpad/crashpad/util/string/split_string_test.cc
@@ -0,0 +1,63 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/string/split_string.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(SplitString, SplitString) { + std::string left; + std::string right; + + EXPECT_FALSE(SplitString("", '=', &left, &right)); + EXPECT_FALSE(SplitString("no equals", '=', &left, &right)); + EXPECT_FALSE(SplitString("=", '=', &left, &right)); + EXPECT_FALSE(SplitString("=beginequals", '=', &left, &right)); + + ASSERT_TRUE(SplitString("a=b", '=', &left, &right)); + EXPECT_EQ("a", left); + EXPECT_EQ("b", right); + + ASSERT_TRUE(SplitString("EndsEquals=", '=', &left, &right)); + EXPECT_EQ("EndsEquals", left); + EXPECT_TRUE(right.empty()); + + ASSERT_TRUE(SplitString("key=VALUE", '=', &left, &right)); + EXPECT_EQ("key", left); + EXPECT_EQ("VALUE", right); + + EXPECT_FALSE(SplitString("a=b", '|', &left, &right)); + + ASSERT_TRUE(SplitString("ls | less", '|', &left, &right)); + EXPECT_EQ("ls ", left); + EXPECT_EQ(" less", right); + + ASSERT_TRUE(SplitString("when in", ' ', &left, &right)); + EXPECT_EQ("when", left); + EXPECT_EQ("in", right); + + ASSERT_TRUE(SplitString("zoo", 'o', &left, &right)); + EXPECT_EQ("z", left); + EXPECT_EQ("o", right); + + ASSERT_FALSE(SplitString("ooze", 'o', &left, &right)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/synchronization/semaphore.h b/third_party/crashpad/crashpad/util/synchronization/semaphore.h new file mode 100644 index 0000000..ac77cb7c --- /dev/null +++ b/third_party/crashpad/crashpad/util/synchronization/semaphore.h
@@ -0,0 +1,80 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_ +#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_ + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include <dispatch/dispatch.h> +#elif defined(OS_WIN) +#include <windows.h> +#else +#include <semaphore.h> +#endif + +namespace crashpad { + +//! \brief An anonymous in-process counting sempahore. +class Semaphore { + public: + //! \brief Initializes the semaphore. + //! + //! \param[in] value The initial value of the semaphore. + //! + //! If the semaphore cannot be created, execution is terminated. + explicit Semaphore(int value); + + ~Semaphore(); + + //! \brief Performs the wait (or “procure”) operation on the semaphore. + //! + //! Atomically decrements the value of the semaphore by 1. If the new value is + //! negative, this function blocks and will not return until the semaphore’s + //! value is incremented to 0 by Signal(). + //! + //! \sa TimedWait() + void Wait(); + + //! \brief Performs a timed wait (or “procure”) operation on the semaphore. + //! + //! \param[in] seconds The maximum number of seconds to wait for the operation + //! to complete. + //! + //! \return `false` if the wait timed out, `true` otherwise. + //! + //! This method is simlar to Wait(), except that the amount of time that it + //! blocks is limited. + bool TimedWait(double seconds); + + //! \brief Performs the signal (or “post”) operation on the semaphore. + //! + //! Atomically increments the value of the semaphore by 1. If the new value is + //! 0, a caller blocked in Wait() will be awakened. + void Signal(); + + private: +#if defined(OS_MACOSX) + dispatch_semaphore_t semaphore_; +#elif defined(OS_WIN) + HANDLE semaphore_; +#else + sem_t semaphore_; +#endif +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
diff --git a/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc b/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc new file mode 100644 index 0000000..e8a79ab --- /dev/null +++ b/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc
@@ -0,0 +1,45 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/synchronization/semaphore.h" + +#include "base/logging.h" + +namespace crashpad { + +Semaphore::Semaphore(int value) + : semaphore_(dispatch_semaphore_create(value)) { + CHECK(semaphore_) << "dispatch_semaphore_create"; +} + +Semaphore::~Semaphore() { + dispatch_release(semaphore_); +} + +void Semaphore::Wait() { + CHECK_EQ(dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER), 0); +} + +bool Semaphore::TimedWait(double seconds) { + DCHECK_GE(seconds, 0.0); + const dispatch_time_t timeout = + dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC); + return dispatch_semaphore_wait(semaphore_, timeout) == 0; +} + +void Semaphore::Signal() { + dispatch_semaphore_signal(semaphore_); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc b/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc new file mode 100644 index 0000000..973f0a5d --- /dev/null +++ b/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc
@@ -0,0 +1,57 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/synchronization/semaphore.h" + +#include <errno.h> + +#include <cmath> + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" + +namespace crashpad { + +#if !defined(OS_MACOSX) + +Semaphore::Semaphore(int value) { + PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init"; +} + +Semaphore::~Semaphore() { + PCHECK(sem_destroy(&semaphore_) == 0) << "sem_destroy"; +} + +void Semaphore::Wait() { + PCHECK(HANDLE_EINTR(sem_wait(&semaphore_)) == 0) << "sem_wait"; +} + +bool Semaphore::TimedWait(double seconds) { + DCHECK_GE(seconds, 0.0); + timespec timeout; + timeout.tv_sec = seconds; + timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9; + + int rv = HANDLE_EINTR(sem_timedwait(&semaphore_, &timeout)); + PCHECK(rv == 0 || errno == ETIMEDOUT) << "sem_timedwait"; + return rv == 0; +} + +void Semaphore::Signal() { + PCHECK(sem_post(&semaphore_) == 0) << "sem_post"; +} + +#endif + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc b/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc new file mode 100644 index 0000000..f359cbd --- /dev/null +++ b/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
@@ -0,0 +1,131 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/synchronization/semaphore.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif // OS_POSIX + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Semaphore, Simple) { + Semaphore semaphore(1); + semaphore.Wait(); + semaphore.Signal(); +} + +TEST(Semaphore, TimedWait) { + Semaphore semaphore(0); + semaphore.Signal(); + EXPECT_TRUE(semaphore.TimedWait(0.01)); // 10ms +} + +TEST(Semaphore, TimedWaitTimeout) { + Semaphore semaphore(0); + EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms +} + +struct ThreadMainInfo { +#if defined(OS_POSIX) + pthread_t pthread; +#elif defined(OS_WIN) + HANDLE thread; +#endif + Semaphore* semaphore; + size_t iterations; +}; + +#if defined(OS_POSIX) +void* +#elif defined(OS_WIN) +DWORD WINAPI +#endif // OS_POSIX +ThreadMain(void* argument) { + ThreadMainInfo* info = reinterpret_cast<ThreadMainInfo*>(argument); + for (size_t iteration = 0; iteration < info->iterations; ++iteration) { + info->semaphore->Wait(); + } +#if defined(OS_POSIX) + return nullptr; +#elif defined(OS_WIN) + return 0; +#endif // OS_POSIX +} + +void StartThread(ThreadMainInfo* info) { +#if defined(OS_POSIX) + int rv = pthread_create(&info->pthread, nullptr, ThreadMain, info); + ASSERT_EQ(0, rv) << "pthread_create"; +#elif defined(OS_WIN) + info->thread = CreateThread(nullptr, 0, ThreadMain, info, 0, nullptr); + ASSERT_NE(nullptr, info->thread) << "CreateThread"; +#endif // OS_POSIX +} + +void JoinThread(ThreadMainInfo* info) { +#if defined(OS_POSIX) + int rv = pthread_join(info->pthread, nullptr); + EXPECT_EQ(0, rv) << "pthread_join"; +#elif defined(OS_WIN) + DWORD result = WaitForSingleObject(info->thread, INFINITE); + EXPECT_EQ(WAIT_OBJECT_0, result) << "WaitForSingleObject"; +#endif // OS_POSIX +} + +TEST(Semaphore, Threaded) { + Semaphore semaphore(0); + ThreadMainInfo info; + info.semaphore = &semaphore; + info.iterations = 1; + + ASSERT_NO_FATAL_FAILURE(StartThread(&info)); + + semaphore.Signal(); + + JoinThread(&info); +} + +TEST(Semaphore, TenThreaded) { + // This test has a smaller initial value (5) than threads contending for these + // resources (10), and the threads each try to obtain the resource a different + // number of times. + Semaphore semaphore(5); + const size_t kThreads = 10; + ThreadMainInfo info[kThreads]; + size_t iterations = 0; + for (size_t index = 0; index < kThreads; ++index) { + info[index].semaphore = &semaphore; + info[index].iterations = index; + iterations += info[index].iterations; + + ASSERT_NO_FATAL_FAILURE(StartThread(&info[index])); + } + + for (size_t iteration = 0; iteration < iterations; ++iteration) { + semaphore.Signal(); + } + + for (size_t index = 0; index < kThreads; ++index) { + JoinThread(&info[index]); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc b/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc new file mode 100644 index 0000000..962c7ba --- /dev/null +++ b/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc
@@ -0,0 +1,50 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/synchronization/semaphore.h" + +#include <limits> + +#include "base/logging.h" + +namespace crashpad { + +Semaphore::Semaphore(int value) + : semaphore_(CreateSemaphore(nullptr, + value, + std::numeric_limits<LONG>::max(), + nullptr)) { + PCHECK(semaphore_) << "CreateSemaphore"; +} + +Semaphore::~Semaphore() { + PCHECK(CloseHandle(semaphore_)); +} + +void Semaphore::Wait() { + PCHECK(WaitForSingleObject(semaphore_, INFINITE) == WAIT_OBJECT_0); +} + +bool Semaphore::TimedWait(double seconds) { + DCHECK_GE(seconds, 0.0); + DWORD rv = WaitForSingleObject(semaphore_, static_cast<DWORD>(seconds * 1E3)); + PCHECK(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT) << "WaitForSingleObject"; + return rv == WAIT_OBJECT_0; +} + +void Semaphore::Signal() { + PCHECK(ReleaseSemaphore(semaphore_, 1, nullptr)); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/thread/thread.cc b/third_party/crashpad/crashpad/util/thread/thread.cc new file mode 100644 index 0000000..04782bb --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread.cc
@@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/thread/thread.h" + +#include "base/logging.h" + +namespace crashpad { + +Thread::Thread() : platform_thread_(0) { +} + +Thread::~Thread() { + DCHECK(!platform_thread_); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/thread/thread.h b/third_party/crashpad/crashpad/util/thread/thread.h new file mode 100644 index 0000000..b1801f8 --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread.h
@@ -0,0 +1,67 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_THREAD_THREAD_H_ +#define CRASHPAD_UTIL_THREAD_THREAD_H_ + +#include "base/basictypes.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#elif defined(OS_WIN) +#include <windows.h> +#endif // OS_POSIX + +namespace crashpad { + +//! \brief Basic thread abstraction. Users should derive from this +//! class and implement ThreadMain(). +class Thread { + public: + Thread(); + virtual ~Thread(); + + //! \brief Create a platform thread, and run ThreadMain() on that thread. Must + //! be paired with a call to Join(). + void Start(); + + //! \brief Block until ThreadMain() exits. This may be called from any thread. + //! Must paired with a call to Start(). + void Join(); + + private: + //! \brief The thread entry point to be implemented by the subclass. + virtual void ThreadMain() = 0; + + static +#if defined(OS_POSIX) + void* +#elif defined(OS_WIN) + DWORD WINAPI +#endif // OS_POSIX + ThreadEntryThunk(void* argument); + +#if defined(OS_POSIX) + pthread_t platform_thread_; +#elif defined(OS_WIN) + HANDLE platform_thread_; +#endif + + DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_THREAD_THREAD_H_
diff --git a/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc b/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc new file mode 100644 index 0000000..bada15a --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
@@ -0,0 +1,99 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/thread/thread_log_messages.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/threading/thread_local_storage.h" + +namespace crashpad { + +namespace { + +// While an object of this class exists, it will be set as the log message +// handler. A thread may register its thread-specific log message list to +// receive messages produced just on that thread. +// +// Only one object of this class may exist in the program at a time. There must +// not be any log message handler in effect when it is created, and nothing else +// can be set as a log message handler while an object of this class exists. +// +// Practically, the only object of this class that might exist is managed by the +// g_master lazy instance, which will create it upon first use. +class ThreadLogMessagesMaster { + public: + ThreadLogMessagesMaster() { + DCHECK(!tls_.initialized()); + tls_.Initialize(nullptr); + DCHECK(tls_.initialized()); + + DCHECK(!logging::GetLogMessageHandler()); + logging::SetLogMessageHandler(LogMessageHandler); + } + + ~ThreadLogMessagesMaster() { + DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler); + logging::SetLogMessageHandler(nullptr); + + tls_.Free(); + } + + void SetThreadMessageList(std::vector<std::string>* message_list) { + DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler); + DCHECK_NE(tls_.Get() != nullptr, message_list != nullptr); + tls_.Set(message_list); + } + + private: + static bool LogMessageHandler(logging::LogSeverity severity, + const char* file_path, + int line, + size_t message_start, + const std::string& string) { + std::vector<std::string>* log_messages = + reinterpret_cast<std::vector<std::string>*>(tls_.Get()); + if (log_messages) { + log_messages->push_back(string); + } + + // Don’t consume the message. Allow it to be logged as if nothing was set as + // the log message handler. + return false; + } + + static base::ThreadLocalStorage::StaticSlot tls_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLogMessagesMaster); +}; + +// static +base::ThreadLocalStorage::StaticSlot ThreadLogMessagesMaster::tls_ + = TLS_INITIALIZER; + +base::LazyInstance<ThreadLogMessagesMaster>::Leaky g_master = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +ThreadLogMessages::ThreadLogMessages() + : log_messages_() { + g_master.Get().SetThreadMessageList(&log_messages_); +} + +ThreadLogMessages::~ThreadLogMessages() { + g_master.Get().SetThreadMessageList(nullptr); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/thread/thread_log_messages.h b/third_party/crashpad/crashpad/util/thread/thread_log_messages.h new file mode 100644 index 0000000..3f850df94 --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread_log_messages.h
@@ -0,0 +1,48 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_ +#define CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Captures log messages produced on the current thread during an +//! object’s lifetime. +//! +//! At most one object of this class type may exist on a single thread at a +//! time. When using this class, no other part of the program may call +//! `logging::SetLogMessageHandler()` at any time. +class ThreadLogMessages { + public: + ThreadLogMessages(); + ~ThreadLogMessages(); + + //! \return The log messages collected on the thread that this object was + //! created on since the time it was created. + const std::vector<std::string>& log_messages() const { return log_messages_; } + + private: + std::vector<std::string> log_messages_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLogMessages); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_
diff --git a/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc b/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc new file mode 100644 index 0000000..e2590826 --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc
@@ -0,0 +1,190 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/thread/thread_log_messages.h" + +#include <string.h> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ThreadLogMessages, Empty) { + ThreadLogMessages thread_log_messages; + + const std::vector<std::string>& log_messages = + thread_log_messages.log_messages(); + + EXPECT_TRUE(log_messages.empty()); +} + +// For a message formatted like "[preamble] message\n", returns just "message". +// If the message is not formatted as expected, a gtest expectation failure will +// be recorded and this function will return an empty string. +std::string MessageString(const std::string& log_message) { + if (log_message.size() < 1) { + EXPECT_GE(log_message.size(), 1u); + return std::string(); + } + + const char kStartChar = '['; + if (log_message[0] != kStartChar) { + EXPECT_EQ(kStartChar, log_message[0]); + return std::string(); + } + + const char kFindString[] = "] "; + size_t pos = log_message.find(kFindString); + if (pos == std::string::npos) { + EXPECT_NE(std::string::npos, pos); + return std::string(); + } + + std::string message_string = log_message.substr(pos + strlen(kFindString)); + if (message_string.size() < 1) { + EXPECT_GE(message_string.size(), 1u); + return std::string(); + } + + const char kEndChar = '\n'; + if (message_string[message_string.size() - 1] != kEndChar) { + EXPECT_NE(message_string[message_string.size() - 1], kEndChar); + return std::string(); + } + + message_string.resize(message_string.size() - 1); + return message_string; +} + +TEST(ThreadLogMessages, Basic) { + // Logging must be enabled at least at this level for this test to work. + ASSERT_TRUE(LOG_IS_ON(INFO)); + + { + const char* const kMessages[] = { + "An info message", + "A warning message", + "An error message", + }; + + ThreadLogMessages thread_log_messages; + + LOG(INFO) << kMessages[0]; + LOG(WARNING) << kMessages[1]; + LOG(ERROR) << kMessages[2]; + + const std::vector<std::string>& log_messages = + thread_log_messages.log_messages(); + + EXPECT_EQ(arraysize(kMessages), log_messages.size()); + for (size_t index = 0; index < arraysize(kMessages); ++index) { + EXPECT_EQ(kMessages[index], MessageString(log_messages[index])) + << "index " << index; + } + } + + { + const char kMessage[] = "Sample error message"; + + ThreadLogMessages thread_log_messages; + + LOG(ERROR) << kMessage; + + const std::vector<std::string>& log_messages = + thread_log_messages.log_messages(); + + EXPECT_EQ(1u, log_messages.size()); + EXPECT_EQ(kMessage, MessageString(log_messages[0])); + } + + { + ThreadLogMessages thread_log_messages; + + LOG(INFO) << "I can't believe I " << "streamed" << " the whole thing."; + + const std::vector<std::string>& log_messages = + thread_log_messages.log_messages(); + + EXPECT_EQ(1u, log_messages.size()); + EXPECT_EQ("I can't believe I streamed the whole thing.", + MessageString(log_messages[0])); + } +} + +class LoggingTestThread : public Thread { + public: + LoggingTestThread() : thread_number_(0), start_(0), count_(0) {} + ~LoggingTestThread() override {} + + void Initialize(size_t thread_number, int start, int count) { + thread_number_ = thread_number; + start_ = start; + count_ = count; + } + + private: + void ThreadMain() override { + ThreadLogMessages thread_log_messages; + + std::vector<std::string> expected_messages; + for (int index = start_; index < start_ + count_; ++index) { + std::string message = base::StringPrintf("message %d", index); + expected_messages.push_back(message); + LOG(WARNING) << message; + } + + const std::vector<std::string>& log_messages = + thread_log_messages.log_messages(); + + ASSERT_EQ(static_cast<size_t>(count_), log_messages.size()); + for (size_t index = 0; index < log_messages.size(); ++index) { + EXPECT_EQ(expected_messages[index], MessageString(log_messages[index])) + << "thread_number_ " << thread_number_ << ", index " << index; + } + } + + size_t thread_number_; + int start_; + int count_; + + DISALLOW_COPY_AND_ASSIGN(LoggingTestThread); +}; + +TEST(ThreadLogMessages, Multithreaded) { + // Logging must be enabled at least at this level for this test to work. + ASSERT_TRUE(LOG_IS_ON(WARNING)); + + LoggingTestThread threads[20]; + int start = 0; + for (size_t index = 0; index < arraysize(threads); ++index) { + threads[index].Initialize( + index, static_cast<int>(start), static_cast<int>(index)); + start += static_cast<int>(index); + + ASSERT_NO_FATAL_FAILURE(threads[index].Start()); + } + + for (LoggingTestThread& thread : threads) { + thread.Join(); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/thread/thread_posix.cc b/third_party/crashpad/crashpad/util/thread/thread_posix.cc new file mode 100644 index 0000000..7142c78 --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread_posix.cc
@@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/thread/thread.h" + +#include "base/logging.h" + +namespace crashpad { + +void Thread::Start() { + DCHECK(!platform_thread_); + int rv = pthread_create(&platform_thread_, nullptr, ThreadEntryThunk, this); + PCHECK(0 == rv); +} + +void Thread::Join() { + DCHECK(platform_thread_); + int rv = pthread_join(platform_thread_, nullptr); + PCHECK(0 == rv); + platform_thread_ = 0; +} + +// static +void* Thread::ThreadEntryThunk(void* argument) { + Thread* self = reinterpret_cast<Thread*>(argument); + self->ThreadMain(); + return nullptr; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/thread/thread_test.cc b/third_party/crashpad/crashpad/util/thread/thread_test.cc new file mode 100644 index 0000000..7f7cf615 --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread_test.cc
@@ -0,0 +1,98 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/thread/thread.h" + +#include "base/basictypes.h" +#include "gtest/gtest.h" +#include "util/synchronization/semaphore.h" + +namespace crashpad { +namespace test { +namespace { + +class NoopThread : public Thread { + public: + NoopThread() {} + ~NoopThread() override {} + + private: + void ThreadMain() override {} + + DISALLOW_COPY_AND_ASSIGN(NoopThread); +}; + +class WaitThread : public Thread { + public: + explicit WaitThread(Semaphore* semaphore) : semaphore_(semaphore) {} + ~WaitThread() override {} + + private: + void ThreadMain() override { semaphore_->Wait(); } + + Semaphore* semaphore_; + + DISALLOW_COPY_AND_ASSIGN(WaitThread); +}; + +class JoinAndSignalThread : public Thread { + public: + JoinAndSignalThread(Thread* thread, Semaphore* semaphore) + : thread_(thread), semaphore_(semaphore) {} + ~JoinAndSignalThread() override {} + + private: + void ThreadMain() override { + thread_->Join(); + semaphore_->Signal(); + } + + Thread* thread_; + Semaphore* semaphore_; + + DISALLOW_COPY_AND_ASSIGN(JoinAndSignalThread); +}; + +TEST(ThreadTest, NoStart) { + NoopThread thread; +} + +TEST(ThreadTest, Start) { + NoopThread thread; + thread.Start(); + thread.Join(); +} + +TEST(ThreadTest, JoinBlocks) { + Semaphore unblock_wait_thread_semaphore(0); + Semaphore join_completed_semaphore(0); + WaitThread wait_thread(&unblock_wait_thread_semaphore); + wait_thread.Start(); + JoinAndSignalThread join_and_signal_thread(&wait_thread, + &join_completed_semaphore); + join_and_signal_thread.Start(); + // join_completed_semaphore will be signaled when wait_thread.Join() returns + // (in JoinAndSignalThread::ThreadMain). Since wait_thread is blocking on + // unblock_wait_thread_semaphore, we don't expect the Join to return yet. We + // wait up to 100ms to give a broken implementation of Thread::Join a chance + // to return. + ASSERT_FALSE(join_completed_semaphore.TimedWait(.1)); + unblock_wait_thread_semaphore.Signal(); + join_completed_semaphore.Wait(); + join_and_signal_thread.Join(); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/thread/thread_win.cc b/third_party/crashpad/crashpad/util/thread/thread_win.cc new file mode 100644 index 0000000..c4ac1eb --- /dev/null +++ b/third_party/crashpad/crashpad/util/thread/thread_win.cc
@@ -0,0 +1,42 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/thread/thread.h" + +#include "base/logging.h" + +namespace crashpad { + +void Thread::Start() { + DCHECK(!platform_thread_); + platform_thread_ = + CreateThread(nullptr, 0, ThreadEntryThunk, this, 0, nullptr); + PCHECK(platform_thread_); +} + +void Thread::Join() { + DCHECK(platform_thread_); + DWORD result = WaitForSingleObject(platform_thread_, INFINITE); + PCHECK(WAIT_OBJECT_0 == result); + platform_thread_ = 0; +} + +// static +DWORD WINAPI Thread::ThreadEntryThunk(void* argument) { + Thread* self = reinterpret_cast<Thread*>(argument); + self->ThreadMain(); + return 0; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/util.gyp b/third_party/crashpad/crashpad/util/util.gyp new file mode 100644 index 0000000..eb5f898 --- /dev/null +++ b/third_party/crashpad/crashpad/util/util.gyp
@@ -0,0 +1,275 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_util', + 'type': 'static_library', + 'dependencies': [ + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + '<(INTERMEDIATE_DIR)', + ], + 'sources': [ + 'file/file_io.cc', + 'file/file_io.h', + 'file/file_io_posix.cc', + 'file/file_io_win.cc', + 'file/file_reader.cc', + 'file/file_reader.h', + 'file/file_seeker.cc', + 'file/file_seeker.h', + 'file/file_writer.cc', + 'file/file_writer.h', + 'file/string_file.cc', + 'file/string_file.h', + 'mac/checked_mach_address_range.h', + 'mac/launchd.h', + 'mac/launchd.mm', + 'mac/mac_util.cc', + 'mac/mac_util.h', + 'mac/service_management.cc', + 'mac/service_management.h', + 'mac/xattr.cc', + 'mac/xattr.h', + 'mach/child_port.defs', + 'mach/child_port_handshake.cc', + 'mach/child_port_handshake.h', + 'mach/child_port_server.cc', + 'mach/child_port_server.h', + 'mach/child_port_types.h', + 'mach/composite_mach_message_server.cc', + 'mach/composite_mach_message_server.h', + 'mach/exc_client_variants.cc', + 'mach/exc_client_variants.h', + 'mach/exc_server_variants.cc', + 'mach/exc_server_variants.h', + 'mach/exception_behaviors.cc', + 'mach/exception_behaviors.h', + 'mach/exception_ports.cc', + 'mach/exception_ports.h', + 'mach/exception_types.cc', + 'mach/exception_types.h', + 'mach/mach_extensions.cc', + 'mach/mach_extensions.h', + 'mach/mach_message.cc', + 'mach/mach_message.h', + 'mach/mach_message_server.cc', + 'mach/mach_message_server.h', + 'mach/notify_server.cc', + 'mach/notify_server.h', + 'mach/scoped_task_suspend.cc', + 'mach/scoped_task_suspend.h', + 'mach/symbolic_constants_mach.cc', + 'mach/symbolic_constants_mach.h', + 'mach/task_for_pid.cc', + 'mach/task_for_pid.h', + 'mach/task_memory.cc', + 'mach/task_memory.h', + 'misc/clock.h', + 'misc/clock_mac.cc', + 'misc/clock_posix.cc', + 'misc/clock_win.cc', + 'misc/implicit_cast.h', + 'misc/initialization_state.h', + 'misc/initialization_state_dcheck.cc', + 'misc/initialization_state_dcheck.h', + 'misc/pdb_structures.cc', + 'misc/pdb_structures.h', + 'misc/random_string.cc', + 'misc/random_string.h', + 'misc/scoped_forbid_return.cc', + 'misc/scoped_forbid_return.h', + 'misc/symbolic_constants_common.h', + 'misc/tri_state.h', + 'misc/uuid.cc', + 'misc/uuid.h', + 'net/http_body.cc', + 'net/http_body.h', + 'net/http_headers.cc', + 'net/http_headers.h', + 'net/http_multipart_builder.cc', + 'net/http_multipart_builder.h', + 'net/http_transport.cc', + 'net/http_transport.h', + 'net/http_transport_mac.mm', + 'net/http_transport_win.cc', + 'numeric/checked_address_range.cc', + 'numeric/checked_address_range.h', + 'numeric/checked_range.h', + 'numeric/in_range_cast.h', + 'numeric/int128.h', + 'numeric/safe_assignment.h', + 'posix/close_multiple.cc', + 'posix/close_multiple.h', + 'posix/close_stdio.cc', + 'posix/close_stdio.h', + 'posix/drop_privileges.cc', + 'posix/drop_privileges.h', + 'posix/process_info.h', + 'posix/process_info_mac.cc', + 'posix/symbolic_constants_posix.cc', + 'posix/symbolic_constants_posix.h', + 'stdlib/cxx.h', + 'stdlib/map_insert.h', + 'stdlib/move.h', + 'stdlib/objc.h', + 'stdlib/pointer_container.h', + 'stdlib/string_number_conversion.cc', + 'stdlib/string_number_conversion.h', + 'stdlib/strlcpy.cc', + 'stdlib/strlcpy.h', + 'stdlib/strnlen.cc', + 'stdlib/strnlen.h', + 'string/split_string.cc', + 'string/split_string.h', + 'synchronization/semaphore_mac.cc', + 'synchronization/semaphore_posix.cc', + 'synchronization/semaphore_win.cc', + 'synchronization/semaphore.h', + 'thread/thread.cc', + 'thread/thread.h', + 'thread/thread_log_messages.cc', + 'thread/thread_log_messages.h', + 'thread/thread_posix.cc', + 'thread/thread_win.cc', + 'win/address_types.h', + 'win/capture_context.asm', + 'win/capture_context.h', + 'win/checked_win_address_range.h', + 'win/command_line.cc', + 'win/command_line.h', + 'win/critical_section_with_debug_info.cc', + 'win/critical_section_with_debug_info.h', + 'win/exception_handler_server.cc', + 'win/exception_handler_server.h', + 'win/get_function.cc', + 'win/get_function.h', + 'win/handle.cc', + 'win/handle.h', + 'win/module_version.cc', + 'win/module_version.h', + 'win/nt_internals.cc', + 'win/nt_internals.h', + 'win/ntstatus_logging.cc', + 'win/ntstatus_logging.h', + 'win/process_info.cc', + 'win/process_info.h', + 'win/process_structs.h', + 'win/registration_protocol_win.cc', + 'win/registration_protocol_win.h', + 'win/scoped_handle.cc', + 'win/scoped_handle.h', + 'win/scoped_local_alloc.cc', + 'win/scoped_local_alloc.h', + 'win/scoped_process_suspend.cc', + 'win/scoped_process_suspend.h', + 'win/time.cc', + 'win/time.h', + 'win/xp_compat.h', + ], + 'conditions': [ + ['OS=="mac"', { + 'conditions': [ + ['GENERATOR=="ninja"', { + # ninja’s rules can’t deal with sources that have paths relative + # to environment variables like SDKROOT. Copy the .defs files out + # of SDKROOT and into a place they can be referenced without any + # environment variables. + 'copies': [ + { + 'destination': '<(INTERMEDIATE_DIR)/util/mach', + 'files': [ + '$(SDKROOT)/usr/include/mach/exc.defs', + '$(SDKROOT)/usr/include/mach/mach_exc.defs', + '$(SDKROOT)/usr/include/mach/notify.defs', + ], + }, + ], + 'sources': [ + '<(INTERMEDIATE_DIR)/util/mach/exc.defs', + '<(INTERMEDIATE_DIR)/util/mach/mach_exc.defs', + '<(INTERMEDIATE_DIR)/util/mach/notify.defs', + ], + }, { # else: GENERATOR!="ninja" + # The Xcode generator does copies after rules, so the above trick + # won’t work, but its rules tolerate sources with SDKROOT-relative + # paths. + 'sources': [ + '$(SDKROOT)/usr/include/mach/exc.defs', + '$(SDKROOT)/usr/include/mach/mach_exc.defs', + '$(SDKROOT)/usr/include/mach/notify.defs', + ], + }], + ], + 'rules': [ + { + 'rule_name': 'mig', + 'extension': 'defs', + 'inputs': [ + 'mach/mig.py', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)User.c', + '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)Server.c', + '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT).h', + '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)Server.h', + ], + 'action': [ + 'python', '<@(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)' + ], + 'process_outputs_as_sources': 1, + }, + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework', + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + '$(SDKROOT)/System/Library/Frameworks/IOKit.framework', + '$(SDKROOT)/usr/lib/libbsm.dylib', + ], + }, + }], + ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-ladvapi32.lib', + '-lrpcrt4.lib', + '-lwinhttp.lib', + ], + }, + 'msvs_disabled_warnings': [ + 4201, # nonstandard extension used : nameless struct/union. + ], + 'conditions': [ + ['target_arch=="ia32"', { + 'msvs_settings': { + 'MASM': { + 'UseSafeExceptionHandlers': 'true', + }, + }, + }], + ], + }], + ], + }, + ], +}
diff --git a/third_party/crashpad/crashpad/util/util_test.gyp b/third_party/crashpad/crashpad/util/util_test.gyp new file mode 100644 index 0000000..2d94ea71 --- /dev/null +++ b/third_party/crashpad/crashpad/util/util_test.gyp
@@ -0,0 +1,140 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + 'includes': [ + '../build/crashpad.gypi', + ], + 'targets': [ + { + 'target_name': 'crashpad_util_test', + 'type': 'executable', + 'dependencies': [ + 'util.gyp:crashpad_util', + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../test/test.gyp:crashpad_test', + '../third_party/gtest/gmock.gyp:gmock', + '../third_party/gtest/gmock.gyp:gmock_main', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'file/file_io_test.cc', + 'file/string_file_test.cc', + 'mac/launchd_test.mm', + 'mac/mac_util_test.mm', + 'mac/service_management_test.mm', + 'mac/xattr_test.cc', + 'mach/child_port_handshake_test.cc', + 'mach/child_port_server_test.cc', + 'mach/composite_mach_message_server_test.cc', + 'mach/exc_client_variants_test.cc', + 'mach/exc_server_variants_test.cc', + 'mach/exception_behaviors_test.cc', + 'mach/exception_ports_test.cc', + 'mach/exception_types_test.cc', + 'mach/mach_extensions_test.cc', + 'mach/mach_message_server_test.cc', + 'mach/mach_message_test.cc', + 'mach/notify_server_test.cc', + 'mach/scoped_task_suspend_test.cc', + 'mach/symbolic_constants_mach_test.cc', + 'mach/task_memory_test.cc', + 'misc/clock_test.cc', + 'misc/initialization_state_dcheck_test.cc', + 'misc/initialization_state_test.cc', + 'misc/scoped_forbid_return_test.cc', + 'misc/random_string_test.cc', + 'misc/uuid_test.cc', + 'net/http_body_test.cc', + 'net/http_body_test_util.cc', + 'net/http_body_test_util.h', + 'net/http_multipart_builder_test.cc', + 'net/http_transport_test.cc', + 'numeric/checked_address_range_test.cc', + 'numeric/checked_range_test.cc', + 'numeric/in_range_cast_test.cc', + 'numeric/int128_test.cc', + 'posix/process_info_test.cc', + 'posix/symbolic_constants_posix_test.cc', + 'stdlib/map_insert_test.cc', + 'stdlib/string_number_conversion_test.cc', + 'stdlib/strlcpy_test.cc', + 'stdlib/strnlen_test.cc', + 'string/split_string_test.cc', + 'synchronization/semaphore_test.cc', + 'thread/thread_log_messages_test.cc', + 'thread/thread_test.cc', + 'win/capture_context_test.cc', + 'win/command_line_test.cc', + 'win/critical_section_with_debug_info_test.cc', + 'win/exception_handler_server_test.cc', + 'win/get_function_test.cc', + 'win/handle_test.cc', + 'win/process_info_test.cc', + 'win/scoped_process_suspend_test.cc', + 'win/time_test.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + ], + }, + }], + ['OS=="win"', { + 'dependencies': [ + 'crashpad_util_test_process_info_test_child', + ], + 'link_settings': { + 'libraries': [ + '-limagehlp.lib', + '-lrpcrt4.lib', + ], + }, + }], + ], + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'crashpad_util_test_process_info_test_child', + 'type': 'executable', + 'sources': [ + 'win/process_info_test_child.cc', + ], + # Set an unusually high load address to make sure that the main + # executable still appears as the first element in + # ProcessInfo::Modules(). + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/BASE:0x78000000', + ], + 'RandomizedBaseAddress': '1', # /DYNAMICBASE:NO. + 'FixedBaseAddress': '2', # /FIXED. + }, + }, + }, + ] + }], + ], +}
diff --git a/third_party/crashpad/crashpad/util/win/address_types.h b/third_party/crashpad/crashpad/util/win/address_types.h new file mode 100644 index 0000000..f26a57e --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/address_types.h
@@ -0,0 +1,32 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_ +#define CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_ + +#include <stdint.h> + +namespace crashpad { + +//! \brief Type used to represent an address in a process, potentially across +//! bitness. +using WinVMAddress = uint64_t; + +//! \brief Type used to represent the size of a memory range (with a +//! WinVMAddress), potentially across bitness. +using WinVMSize = uint64_t; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
diff --git a/third_party/crashpad/crashpad/util/win/capture_context.asm b/third_party/crashpad/crashpad/util/win/capture_context.asm new file mode 100644 index 0000000..56efec7f --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/capture_context.asm
@@ -0,0 +1,530 @@ +; Copyright 2015 The Crashpad Authors. All rights reserved. +; +; 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. + +; Detect ml64 assembling for x86_64 by checking for rax. +ifdef rax +_M_X64 equ 1 +else +_M_IX86 equ 1 +endif + +ifdef _M_IX86 +.586 +.XMM +.model flat +endif + +offsetof macro structure, field + exitm <structure.&field> +endm + +; The CONTEXT structure definitions that follow are based on those in <winnt.h>. +; Field names are prefixed (as in c_Rax) to avoid colliding with the predefined +; register names (such as Rax). + +ifdef _M_IX86 + +CONTEXT_i386 equ 10000h +CONTEXT_CONTROL equ CONTEXT_i386 or 1h +CONTEXT_INTEGER equ CONTEXT_i386 or 2h +CONTEXT_SEGMENTS equ CONTEXT_i386 or 4h +CONTEXT_FLOATING_POINT equ CONTEXT_i386 or 8h +CONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 or 10h +CONTEXT_EXTENDED_REGISTERS equ CONTEXT_i386 or 20h +CONTEXT_XSTATE equ CONTEXT_i386 or 40h + +MAXIMUM_SUPPORTED_EXTENSION equ 512 + +CONTEXT struct + c_ContextFlags dword ? + + c_Dr0 dword ? + c_Dr1 dword ? + c_Dr2 dword ? + c_Dr3 dword ? + c_Dr6 dword ? + c_Dr7 dword ? + + struct c_FloatSave + f_ControlWord dword ? + f_StatusWord dword ? + f_TagWord dword ? + f_ErrorOffset dword ? + f_ErrorSelector dword ? + f_DataOffset dword ? + f_DataSelector dword ? + f_RegisterArea byte 80 dup(?) + + union + f_Spare0 dword ? ; As in FLOATING_SAVE_AREA. + f_Cr0NpxState dword ? ; As in WOW64_FLOATING_SAVE_AREA. + ends + ends + + c_SegGs dword ? + c_SegFs dword ? + c_SegEs dword ? + c_SegDs dword ? + + c_Edi dword ? + c_Esi dword ? + c_Ebx dword ? + c_Edx dword ? + c_Ecx dword ? + c_Eax dword ? + + c_Ebp dword ? + + c_Eip dword ? + c_SegCs dword ? + + c_EFlags dword ? + + c_Esp dword ? + c_SegSs dword ? + + c_ExtendedRegisters byte MAXIMUM_SUPPORTED_EXTENSION dup(?) +CONTEXT ends + +elseifdef _M_X64 + +M128A struct 16 + m_Low qword ? + m_High qword ? +M128A ends + +CONTEXT_AMD64 equ 100000h +CONTEXT_CONTROL equ CONTEXT_AMD64 or 1h +CONTEXT_INTEGER equ CONTEXT_AMD64 or 2h +CONTEXT_SEGMENTS equ CONTEXT_AMD64 or 4h +CONTEXT_FLOATING_POINT equ CONTEXT_AMD64 or 8h +CONTEXT_DEBUG_REGISTERS equ CONTEXT_AMD64 or 10h +CONTEXT_XSTATE equ CONTEXT_AMD64 or 40h + +CONTEXT struct 16 + c_P1Home qword ? + c_P2Home qword ? + c_P3Home qword ? + c_P4Home qword ? + c_P5Home qword ? + c_P6Home qword ? + + c_ContextFlags dword ? + c_MxCsr dword ? + + c_SegCs word ? + c_SegDs word ? + c_SegEs word ? + c_SegFs word ? + c_SegGs word ? + c_SegSs word ? + + c_EFlags dword ? + + c_Dr0 qword ? + c_Dr1 qword ? + c_Dr2 qword ? + c_Dr3 qword ? + c_Dr6 qword ? + c_Dr7 qword ? + + c_Rax qword ? + c_Rcx qword ? + c_Rdx qword ? + c_Rbx qword ? + c_Rsp qword ? + c_Rbp qword ? + c_Rsi qword ? + c_Rdi qword ? + c_R8 qword ? + c_R9 qword ? + c_R10 qword ? + c_R11 qword ? + c_R12 qword ? + c_R13 qword ? + c_R14 qword ? + c_R15 qword ? + + c_Rip qword ? + + union + struct c_FltSave + f_ControlWord word ? + f_StatusWord word ? + f_TagWord byte ? + f_Reserved1 byte ? + f_ErrorOpcode word ? + f_ErrorOffset dword ? + f_ErrorSelector word ? + f_Reserved2 word ? + f_DataOffset dword ? + f_DataSelector word ? + f_Reserved3 word ? + f_MxCsr dword ? + f_MxCsr_Mask dword ? + f_FloatRegisters M128A 8 dup(<?>) + f_XmmRegisters M128A 16 dup(<?>) + f_Reserved4 byte 96 dup(?) + ends + struct + fx_Header M128A 2 dup(<?>) + fx_Legacy M128A 8 dup(<?>) + fx_Xmm0 M128A <?> + fx_Xmm1 M128A <?> + fx_Xmm2 M128A <?> + fx_Xmm3 M128A <?> + fx_Xmm4 M128A <?> + fx_Xmm5 M128A <?> + fx_Xmm6 M128A <?> + fx_Xmm7 M128A <?> + fx_Xmm8 M128A <?> + fx_Xmm9 M128A <?> + fx_Xmm10 M128A <?> + fx_Xmm11 M128A <?> + fx_Xmm12 M128A <?> + fx_Xmm13 M128A <?> + fx_Xmm14 M128A <?> + fx_Xmm15 M128A <?> + ends + ends + + c_VectorRegister M128A 26 dup(<?>) + c_VectorControl qword ? + + c_DebugControl qword ? + c_LastBranchToRip qword ? + c_LastBranchFromRip qword ? + c_LastExceptionToRip qword ? + c_LastExceptionFromRip qword ? +CONTEXT ends + +endif + +; namespace crashpad { +; void CaptureContext(CONTEXT* context) +; } // namespace crashpad +ifdef _M_IX86 +CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPAU_CONTEXT@@@Z +elseifdef _M_X64 +CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z +endif + +_TEXT segment +public CAPTURECONTEXT_SYMBOL + +ifdef _M_IX86 + +CAPTURECONTEXT_SYMBOL proc + + push ebp + mov ebp, esp + + ; pushfd first, because some instructions affect eflags. eflags will be in + ; [ebp-4]. + pushfd + + ; Save the original value of ebx, and use ebx to hold the CONTEXT* argument. + ; The original value of ebx will be in [ebp-8]. + push ebx + mov ebx, [ebp+8] + + ; General-purpose registers whose values haven’t changed can be captured + ; directly. + mov [ebx.CONTEXT].c_Edi, edi + mov [ebx.CONTEXT].c_Esi, esi + mov [ebx.CONTEXT].c_Edx, edx + mov [ebx.CONTEXT].c_Ecx, ecx + mov [ebx.CONTEXT].c_Eax, eax + + ; Now that the original value of edx has been saved, it can be repurposed to + ; hold other registers’ values. + + ; The original ebx was saved on the stack above. + mov edx, dword ptr [ebp-8] + mov [ebx.CONTEXT].c_Ebx, edx + + ; The original ebp was saved on the stack in this function’s prologue. + mov edx, dword ptr [ebp] + mov [ebx.CONTEXT].c_Ebp, edx + + ; eip can’t be accessed directly, but the return address saved on the stack + ; by the call instruction that reached this function can be used. + mov edx, dword ptr [ebp+4] + mov [ebx.CONTEXT].c_Eip, edx + + ; The original eflags was saved on the stack above. + mov edx, dword ptr [ebp-4] + mov [ebx.CONTEXT].c_EFlags, edx + + ; esp was saved in ebp in this function’s prologue, but the caller’s esp is 8 + ; more than this value: 4 for the original ebp saved on the stack in this + ; function’s prologue, and 4 for the return address saved on the stack by the + ; call instruction that reached this function. + lea edx, [ebp+8] + mov [ebx.CONTEXT].c_Esp, edx + + ; The segment registers are 16 bits wide, but CONTEXT declares them as + ; unsigned 32-bit values, so zero the top half. + xor edx, edx + mov dx, gs + mov [ebx.CONTEXT].c_SegGs, edx + mov dx, fs + mov [ebx.CONTEXT].c_SegFs, edx + mov dx, es + mov [ebx.CONTEXT].c_SegEs, edx + mov dx, ds + mov [ebx.CONTEXT].c_SegDs, edx + mov dx, cs + mov [ebx.CONTEXT].c_SegCs, edx + mov dx, ss + mov [ebx.CONTEXT].c_SegSs, edx + + ; Prepare for the string move that will populate the ExtendedRegisters area, + ; or the string store that will zero it. + cld + + ; Use cpuid 1 to check whether fxsave is supported. If it is, perform it + ; before fnsave because fxsave is a less-destructive operation. + mov esi, ebx + mov eax, 1 + cpuid + mov ebx, esi + + test edx, 01000000 ; FXSR + jnz $FXSave + + ; fxsave is not supported. Set ContextFlags to not include + ; CONTEXT_EXTENDED_REGISTERS, and zero the ExtendedRegisters area. + mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \ + CONTEXT_CONTROL or \ + CONTEXT_INTEGER or \ + CONTEXT_SEGMENTS or \ + CONTEXT_FLOATING_POINT + lea edi, [ebx.CONTEXT].c_ExtendedRegisters + xor eax, eax + mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128 + rep stosd + jmp $FXSaveDone + +$FXSave: + ; fxsave is supported. Set ContextFlags to include CONTEXT_EXTENDED_REGISTERS. + mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \ + CONTEXT_CONTROL or \ + CONTEXT_INTEGER or \ + CONTEXT_SEGMENTS or \ + CONTEXT_FLOATING_POINT or \ + CONTEXT_EXTENDED_REGISTERS + + ; fxsave requires a 16 byte-aligned destination memory area. Nothing + ; guarantees the alignment of a CONTEXT structure, so create a temporary + ; aligned fxsave destination on the stack. + and esp, 0fffffff0h + sub esp, MAXIMUM_SUPPORTED_EXTENSION + + ; Zero out the temporary fxsave area before performing the fxsave. Some of the + ; fxsave area may not be written by fxsave, and some is definitely not written + ; by fxsave. + mov edi, esp + xor eax, eax + mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128 + rep stosd + + fxsave [esp] + + ; Copy the temporary fxsave area into the CONTEXT structure. + lea edi, [ebx.CONTEXT].c_ExtendedRegisters + mov esi, esp + mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128 + rep movsd + + ; Free the stack space used for the temporary fxsave area. + lea esp, [ebp-8] + + ; TODO(mark): AVX/xsave support. https://crashpad.chromium.org/bug/58 + +$FXSaveDone: + ; fnsave reinitializes the FPU with an implicit finit operation, so use frstor + ; to restore the original state. + fnsave [ebx.CONTEXT].c_FloatSave + frstor [ebx.CONTEXT].c_FloatSave + + ; cr0 is inaccessible from user code, and this field would not be used anyway. + mov [ebx.CONTEXT].c_FloatSave.f_Cr0NpxState, 0 + + ; The debug registers can’t be read from user code, so zero them out in the + ; CONTEXT structure. context->ContextFlags doesn’t indicate that they are + ; present. + mov [ebx.CONTEXT].c_Dr0, 0 + mov [ebx.CONTEXT].c_Dr1, 0 + mov [ebx.CONTEXT].c_Dr2, 0 + mov [ebx.CONTEXT].c_Dr3, 0 + mov [ebx.CONTEXT].c_Dr6, 0 + mov [ebx.CONTEXT].c_Dr7, 0 + + ; Clean up by restoring clobbered registers, even those considered volatile + ; by the ABI, so that the captured context represents the state at this + ; function’s exit. + mov edi, [ebx.CONTEXT].c_Edi + mov esi, [ebx.CONTEXT].c_Esi + mov edx, [ebx.CONTEXT].c_Edx + mov ecx, [ebx.CONTEXT].c_Ecx + mov eax, [ebx.CONTEXT].c_Eax + pop ebx + popfd + + pop ebp + + ret + +CAPTURECONTEXT_SYMBOL endp + +elseifdef _M_X64 + +CAPTURECONTEXT_SYMBOL proc frame + + push rbp + .pushreg rbp + mov rbp, rsp + .setframe rbp, 0 + + ; Note that 16-byte stack alignment is not maintained because this function + ; does not call out to any other. + + ; pushfq first, because some instructions affect rflags. rflags will be in + ; [rbp-8]. + pushfq + .allocstack 8 + .endprolog + + mov [rcx.CONTEXT].c_ContextFlags, CONTEXT_AMD64 or \ + CONTEXT_CONTROL or \ + CONTEXT_INTEGER or \ + CONTEXT_SEGMENTS or \ + CONTEXT_FLOATING_POINT + + ; General-purpose registers whose values haven’t changed can be captured + ; directly. + mov [rcx.CONTEXT].c_Rax, rax + mov [rcx.CONTEXT].c_Rdx, rdx + mov [rcx.CONTEXT].c_Rbx, rbx + mov [rcx.CONTEXT].c_Rsi, rsi + mov [rcx.CONTEXT].c_Rdi, rdi + mov [rcx.CONTEXT].c_R8, r8 + mov [rcx.CONTEXT].c_R9, r9 + mov [rcx.CONTEXT].c_R10, r10 + mov [rcx.CONTEXT].c_R11, r11 + mov [rcx.CONTEXT].c_R12, r12 + mov [rcx.CONTEXT].c_R13, r13 + mov [rcx.CONTEXT].c_R14, r14 + mov [rcx.CONTEXT].c_R15, r15 + + ; Because of the calling convention, there’s no way to recover the value of + ; the caller’s rcx as it existed prior to calling this function. This + ; function captures a snapshot of the register state at its return, which + ; involves rcx containing a pointer to its first argument. + mov [rcx.CONTEXT].c_Rcx, rcx + + ; Now that the original value of rax has been saved, it can be repurposed to + ; hold other registers’ values. + + ; Save mxcsr. This is duplicated in context->FltSave.MxCsr, saved by fxsave + ; below. + stmxcsr [rcx.CONTEXT].c_MxCsr + + ; Segment registers. + mov [rcx.CONTEXT].c_SegCs, cs + mov [rcx.CONTEXT].c_SegDs, ds + mov [rcx.CONTEXT].c_SegEs, es + mov [rcx.CONTEXT].c_SegFs, fs + mov [rcx.CONTEXT].c_SegGs, gs + mov [rcx.CONTEXT].c_SegSs, ss + + ; The original rflags was saved on the stack above. Note that the CONTEXT + ; structure only stores eflags, the low 32 bits. The high 32 bits in rflags + ; are reserved. + mov rax, qword ptr [rbp-8] + mov [rcx.CONTEXT].c_EFlags, eax + + ; rsp was saved in rbp in this function’s prologue, but the caller’s rsp is + ; 16 more than this value: 8 for the original rbp saved on the stack in this + ; function’s prologue, and 8 for the return address saved on the stack by the + ; call instruction that reached this function. + lea rax, [rbp+16] + mov [rcx.CONTEXT].c_Rsp, rax + + ; The original rbp was saved on the stack in this function’s prologue. + mov rax, qword ptr [rbp] + mov [rcx.CONTEXT].c_Rbp, rax + + ; rip can’t be accessed directly, but the return address saved on the stack by + ; the call instruction that reached this function can be used. + mov rax, qword ptr [rbp+8] + mov [rcx.CONTEXT].c_Rip, rax + + ; Zero out the fxsave area before performing the fxsave. Some of the fxsave + ; area may not be written by fxsave, and some is definitely not written by + ; fxsave. This also zeroes out the rest of the CONTEXT structure to its end, + ; including the unused VectorRegister and VectorControl fields, and the debug + ; control register fields. + mov rbx, rcx + cld + lea rdi, [rcx.CONTEXT].c_FltSave + xor rax, rax + mov rcx, (sizeof(CONTEXT) - offsetof(CONTEXT, c_FltSave)) / \ + sizeof(qword) ; 122 + rep stosq + mov rcx, rbx + + ; Save the floating point (including SSE) state. The CONTEXT structure is + ; declared as 16-byte-aligned, which is correct for this operation. + fxsave [rcx.CONTEXT].c_FltSave + + ; TODO(mark): AVX/xsave support. https://crashpad.chromium.org/bug/58 + + ; The register parameter home address fields aren’t used, so zero them out. + mov [rcx.CONTEXT].c_P1Home, 0 + mov [rcx.CONTEXT].c_P2Home, 0 + mov [rcx.CONTEXT].c_P3Home, 0 + mov [rcx.CONTEXT].c_P4Home, 0 + mov [rcx.CONTEXT].c_P5Home, 0 + mov [rcx.CONTEXT].c_P6Home, 0 + + ; The debug registers can’t be read from user code, so zero them out in the + ; CONTEXT structure. context->ContextFlags doesn’t indicate that they are + ; present. + mov [rcx.CONTEXT].c_Dr0, 0 + mov [rcx.CONTEXT].c_Dr1, 0 + mov [rcx.CONTEXT].c_Dr2, 0 + mov [rcx.CONTEXT].c_Dr3, 0 + mov [rcx.CONTEXT].c_Dr6, 0 + mov [rcx.CONTEXT].c_Dr7, 0 + + ; Clean up by restoring clobbered registers, even those considered volatile by + ; the ABI, so that the captured context represents the state at this + ; function’s exit. + mov rax, [rcx.CONTEXT].c_Rax + mov rbx, [rcx.CONTEXT].c_Rbx + mov rdi, [rcx.CONTEXT].c_Rdi + popfq + + pop rbp + + ret + +CAPTURECONTEXT_SYMBOL endp + +endif + +_TEXT ends +end
diff --git a/third_party/crashpad/crashpad/util/win/capture_context.h b/third_party/crashpad/crashpad/util/win/capture_context.h new file mode 100644 index 0000000..2f501f8 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/capture_context.h
@@ -0,0 +1,47 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CAPTURE_CONTEXT_WIN_H_ +#define CRASHPAD_CLIENT_CAPTURE_CONTEXT_WIN_H_ + +#include <windows.h> + +namespace crashpad { + +//! \brief Saves the CPU context. +//! +//! The CPU context will be captured as accurately and completely as possible, +//! containing an atomic snapshot at the point of this function’s return. This +//! function does not modify any registers. +//! +//! This function captures all integer registers as well as the floating-point +//! and vector (SSE) state. It does not capture debug registers, which are +//! inaccessible by user code. +//! +//! This function is a replacement for `RtlCaptureContext()`, which contains +//! bugs and limitations. On 32-bit x86, `RtlCaptureContext()` requires that +//! `ebp` be used as a frame pointer, and returns `ebp`, `esp`, and `eip` out of +//! sync with the other registers. Both the 32-bit x86 and 64-bit x86_64 +//! versions of `RtlCaptureContext()` capture only the state of the integer +//! registers, ignoring floating-point and vector state. +//! +//! \param[out] context The structure to store the context in. +//! +//! \note On x86_64, the value for `rcx` will be populated with the address of +//! this function’s argument, as mandated by the ABI. +void CaptureContext(CONTEXT* context); + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CAPTURE_CONTEXT_WIN_H_
diff --git a/third_party/crashpad/crashpad/util/win/capture_context_test.cc b/third_party/crashpad/crashpad/util/win/capture_context_test.cc new file mode 100644 index 0000000..270ecb3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/capture_context_test.cc
@@ -0,0 +1,182 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/capture_context.h" + +#include <stdint.h> + +#include <algorithm> + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +// If the context structure has fields that tell whether it’s valid, such as +// magic numbers or size fields, sanity-checks those fields for validity with +// fatal gtest assertions. For other fields, where it’s possible to reason about +// their validity based solely on their contents, sanity-checks via nonfatal +// gtest assertions. +void SanityCheckContext(const CONTEXT& context) { +#if defined(ARCH_CPU_X86) + const uint32_t must_have = CONTEXT_i386 | + CONTEXT_CONTROL | + CONTEXT_INTEGER | + CONTEXT_SEGMENTS | + CONTEXT_FLOATING_POINT; + ASSERT_EQ(must_have, context.ContextFlags & must_have); + const uint32_t may_have = CONTEXT_EXTENDED_REGISTERS; + ASSERT_EQ(0, context.ContextFlags & ~(must_have | may_have)); +#elif defined(ARCH_CPU_X86_64) + ASSERT_EQ(CONTEXT_AMD64 | + CONTEXT_CONTROL | + CONTEXT_INTEGER | + CONTEXT_SEGMENTS | + CONTEXT_FLOATING_POINT, + context.ContextFlags); +#endif + +#if defined(ARCH_CPU_X86_FAMILY) + // Many bit positions in the flags register are reserved and will always read + // a known value. Most reserved bits are always 0, but bit 1 is always 1. + // Check that the reserved bits are all set to their expected values. Note + // that the set of reserved bits may be relaxed over time with newer CPUs, and + // that this test may need to be changed to reflect these developments. The + // current set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel + // Software Developer’s Manual, Volume 1: Basic Architecture (253665-055), + // 3.4.3 “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume + // 2: System Programming (24593-3.25), 3.1.6 “RFLAGS Register”. + EXPECT_EQ(2u, context.EFlags & 0xffc0802a); + + // CaptureContext() doesn’t capture debug registers, so make sure they read 0. + EXPECT_EQ(0, context.Dr0); + EXPECT_EQ(0, context.Dr1); + EXPECT_EQ(0, context.Dr2); + EXPECT_EQ(0, context.Dr3); + EXPECT_EQ(0, context.Dr6); + EXPECT_EQ(0, context.Dr7); +#endif + +#if defined(ARCH_CPU_X86) + // fxsave doesn’t write these bytes. + for (size_t i = 464; i < arraysize(context.ExtendedRegisters); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(0, context.ExtendedRegisters[i]); + } +#elif defined(ARCH_CPU_X86_64) + // mxcsr shows up twice in the context structure. Make sure the values are + // identical. + EXPECT_EQ(context.MxCsr, context.FltSave.MxCsr); + + // fxsave doesn’t write these bytes. + for (size_t i = 0; i < arraysize(context.FltSave.Reserved4); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(0, context.FltSave.Reserved4[i]); + } + + // CaptureContext() doesn’t use these fields. + EXPECT_EQ(0, context.P1Home); + EXPECT_EQ(0, context.P2Home); + EXPECT_EQ(0, context.P3Home); + EXPECT_EQ(0, context.P4Home); + EXPECT_EQ(0, context.P5Home); + EXPECT_EQ(0, context.P6Home); + for (size_t i = 0; i < arraysize(context.VectorRegister); ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(0, context.VectorRegister[i].Low); + EXPECT_EQ(0, context.VectorRegister[i].High); + } + EXPECT_EQ(0, context.VectorControl); + EXPECT_EQ(0, context.DebugControl); + EXPECT_EQ(0, context.LastBranchToRip); + EXPECT_EQ(0, context.LastBranchFromRip); + EXPECT_EQ(0, context.LastExceptionToRip); + EXPECT_EQ(0, context.LastExceptionFromRip); +#endif +} + +// A CPU-independent function to return the program counter. +uintptr_t ProgramCounterFromContext(const CONTEXT& context) { +#if defined(ARCH_CPU_X86) + return context.Eip; +#elif defined(ARCH_CPU_X86_64) + return context.Rip; +#endif +} + +// A CPU-independent function to return the stack pointer. +uintptr_t StackPointerFromContext(const CONTEXT& context) { +#if defined(ARCH_CPU_X86) + return context.Esp; +#elif defined(ARCH_CPU_X86_64) + return context.Rsp; +#endif +} + +void TestCaptureContext() { + CONTEXT context_1; + CaptureContext(&context_1); + + { + SCOPED_TRACE("context_1"); + ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_1)); + } + + // The program counter reference value is this function’s address. The + // captured program counter should be slightly greater than or equal to the + // reference program counter. + uintptr_t pc = ProgramCounterFromContext(context_1); + + // Declare sp and context_2 here because all local variables need to be + // declared before computing the stack pointer reference value, so that the + // reference value can be the lowest value possible. + uintptr_t sp; + CONTEXT context_2; + + // The stack pointer reference value is the lowest address of a local variable + // in this function. The captured program counter will be slightly less than + // or equal to the reference stack pointer. + const uintptr_t kReferenceSP = + std::min(std::min(reinterpret_cast<uintptr_t>(&context_1), + reinterpret_cast<uintptr_t>(&context_2)), + std::min(reinterpret_cast<uintptr_t>(&pc), + reinterpret_cast<uintptr_t>(&sp))); + sp = StackPointerFromContext(context_1); + EXPECT_LT(kReferenceSP - sp, 512u); + + // Capture the context again, expecting that the stack pointer stays the same + // and the program counter increases. Strictly speaking, there’s no guarantee + // that these conditions will hold, although they do for known compilers even + // under typical optimization. + CaptureContext(&context_2); + + { + SCOPED_TRACE("context_2"); + ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_2)); + } + + EXPECT_EQ(sp, StackPointerFromContext(context_2)); + EXPECT_GT(ProgramCounterFromContext(context_2), pc); +} + +TEST(CaptureContextWin, CaptureContext) { + ASSERT_NO_FATAL_FAILURE(TestCaptureContext()); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/checked_win_address_range.h b/third_party/crashpad/crashpad/util/win/checked_win_address_range.h new file mode 100644 index 0000000..e86c7b7 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/checked_win_address_range.h
@@ -0,0 +1,36 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_ +#define CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_ + +#include "util/numeric/checked_address_range.h" +#include "util/win/address_types.h" + +namespace crashpad { + +//! \brief Ensures that a range, composed of a base and a size, does not +//! overflow the pointer type of the process it describes a range in. +//! +//! This class checks bases of type WinVMAddress and sizes of type WinVMSize +//! against a process whose pointer type is either 32 or 64 bits wide. +//! +//! Aside from varying the overall range on the basis of a process' pointer type +//! width, this class functions very similarly to CheckedRange. +using CheckedWinAddressRange = + internal::CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
diff --git a/third_party/crashpad/crashpad/util/win/command_line.cc b/third_party/crashpad/crashpad/util/win/command_line.cc new file mode 100644 index 0000000..5829c12 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/command_line.cc
@@ -0,0 +1,58 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/command_line.h" + +namespace crashpad { + +// Ref: +// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx +void AppendCommandLineArgument(const std::wstring& argument, + std::wstring* command_line) { + if (!command_line->empty()) { + command_line->push_back(L' '); + } + + // Don’t bother quoting if unnecessary. + if (!argument.empty() && + argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) { + command_line->append(argument); + } else { + command_line->push_back(L'"'); + for (std::wstring::const_iterator i = argument.begin();; ++i) { + size_t backslash_count = 0; + while (i != argument.end() && *i == L'\\') { + ++i; + ++backslash_count; + } + if (i == argument.end()) { + // Escape all backslashes, but let the terminating double quotation mark + // we add below be interpreted as a metacharacter. + command_line->append(backslash_count * 2, L'\\'); + break; + } else if (*i == L'"') { + // Escape all backslashes and the following double quotation mark. + command_line->append(backslash_count * 2 + 1, L'\\'); + command_line->push_back(*i); + } else { + // Backslashes aren’t special here. + command_line->append(backslash_count, L'\\'); + command_line->push_back(*i); + } + } + command_line->push_back(L'"'); + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/command_line.h b/third_party/crashpad/crashpad/util/win/command_line.h new file mode 100644 index 0000000..eb3f712 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/command_line.h
@@ -0,0 +1,38 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_COMMAND_LINE_H_ +#define CRASHPAD_UTIL_WIN_COMMAND_LINE_H_ + +#include <string> + +namespace crashpad { + +//! \brief Utility function for building escaped command lines. +//! +//! This builds a command line so that individual arguments can be reliably +//! decoded by `CommandLineToArgvW()`. +//! +//! \a argument is appended to \a command_line. If necessary, it will be placed +//! in quotation marks and escaped properly. If \a command_line is initially +//! non-empty, a space will precede \a argument. +//! +//! \param[in] argument The argument to append to \a command_line. +//! \param[inout] command_line The command line being constructed. +void AppendCommandLineArgument(const std::wstring& argument, + std::wstring* command_line); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
diff --git a/third_party/crashpad/crashpad/util/win/command_line_test.cc b/third_party/crashpad/crashpad/util/win/command_line_test.cc new file mode 100644 index 0000000..d317927 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/command_line_test.cc
@@ -0,0 +1,167 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/command_line.h" + +#include <windows.h> +#include <shellapi.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/scoped_generic.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/win/scoped_local_alloc.h" + +namespace crashpad { +namespace test { +namespace { + +// Calls AppendCommandLineArgument() for every argument in argv, then calls +// CommandLineToArgvW() to decode the string into a vector again, and compares +// the input and output. +void AppendCommandLineArgumentTest(size_t argc, const wchar_t* const argv[]) { + std::wstring command_line; + for (size_t index = 0; index < argc; ++index) { + AppendCommandLineArgument(argv[index], &command_line); + } + + int test_argc; + wchar_t** test_argv = CommandLineToArgvW(command_line.c_str(), &test_argc); + + ASSERT_TRUE(test_argv) << ErrorMessage("CommandLineToArgvW"); + ScopedLocalAlloc test_argv_owner(test_argv); + ASSERT_EQ(argc, test_argc); + + for (size_t index = 0; index < argc; ++index) { + EXPECT_STREQ(argv[index], test_argv[index]) << "index " << index; + } + EXPECT_FALSE(test_argv[argc]); +} + +TEST(CommandLine, AppendCommandLineArgument) { + // Most of these test cases come from + // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx, + // which was also a reference for the implementation of + // AppendCommandLineArgument(). + + { + SCOPED_TRACE("simple"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"argument 1", + L"argument 2", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } + + { + SCOPED_TRACE("path with spaces"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"argument1", + L"argument 2", + L"\\some\\path with\\spaces", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } + + { + SCOPED_TRACE("argument with embedded quotation marks"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"argument1", + L"she said, \"you had me at hello\"", + L"\\some\\path with\\spaces", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } + + { + SCOPED_TRACE("argument with unbalanced quotation marks"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"argument1", + L"argument\"2", + L"argument3", + L"argument4", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } + + { + SCOPED_TRACE("argument ending with backslash"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"\\some\\directory with\\spaces\\", + L"argument2", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } + + { + SCOPED_TRACE("empty argument"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"", + L"argument2", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } + + { + SCOPED_TRACE("funny nonprintable characters"); + + const wchar_t* const kArguments[] = { + L"child.exe", + L"argument 1", + L"argument\t2", + L"argument\n3", + L"argument\v4", + L"argument\"5", + L" ", + L"\t", + L"\n", + L"\v", + L"\"", + L" x", + L"\tx", + L"\nx", + L"\vx", + L"\"x", + L"x ", + L"x\t", + L"x\n", + L"x\v", + L"x\"", + L" ", + L"\t\t", + L"\n\n", + L"\v\v", + L"\"\"", + L" \t\n\v\"", + }; + AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info.cc b/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info.cc new file mode 100644 index 0000000..1613a51 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info.cc
@@ -0,0 +1,72 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/critical_section_with_debug_info.h" + +#include "base/logging.h" +#include "util/win/get_function.h" + +namespace crashpad { + +namespace { + +bool CrashpadInitializeCriticalSectionEx( + CRITICAL_SECTION* critical_section, + DWORD spin_count, + DWORD flags) { + static const auto initialize_critical_section_ex = + GET_FUNCTION_REQUIRED(L"kernel32.dll", ::InitializeCriticalSectionEx); + BOOL ret = + initialize_critical_section_ex(critical_section, spin_count, flags); + if (!ret) { + PLOG(ERROR) << "InitializeCriticalSectionEx"; + return false; + } + return true; +} + +} // namespace + +bool InitializeCriticalSectionWithDebugInfoIfPossible( + CRITICAL_SECTION* critical_section) { + // On XP and Vista, a plain initialization causes the CRITICAL_SECTION to be + // allocated with .DebugInfo. On 8 and above, we can pass an additional flag + // to InitializeCriticalSectionEx() to force the .DebugInfo on. Before Win 8, + // that flag causes InitializeCriticalSectionEx() to fail. So, for XP, Vista, + // and 7 we use InitializeCriticalSection(), and for 8 and above, + // InitializeCriticalSectionEx() with the additional flag. + // + // TODO(scottmg): Try to find a solution for Win 7. It's unclear how to force + // it on for Win 7, however the Loader Lock does have .DebugInfo so there may + // be a way to do it. The comments in winnt.h imply that perhaps it's passed + // to InitializeCriticalSectionAndSpinCount() as the top bits of the spin + // count, but that doesn't appear to work. For now, we initialize a valid + // CRITICAL_SECTION, but without .DebugInfo. + + const DWORD version = GetVersion(); + const DWORD major_version = LOBYTE(LOWORD(version)); + const DWORD minor_version = HIBYTE(LOWORD(version)); + const bool win7_or_lower = + major_version < 6 || (major_version == 6 && minor_version <= 1); + + if (win7_or_lower) { + InitializeCriticalSection(critical_section); + return true; + } + + return CrashpadInitializeCriticalSectionEx( + critical_section, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info.h b/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info.h new file mode 100644 index 0000000..1271685 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info.h
@@ -0,0 +1,34 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_ +#define CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_ + +#include <windows.h> + +namespace crashpad { + +//! \brief Equivalent to `InitializeCritialSection()`, but attempts to allocate +//! with a valid `.DebugInfo` field on versions of Windows where it's +//! possible to do so. +//! +//! \return `true` on success, or `false` on failure with a message logged. +//! Success means that the critical section was successfully initialized, +//! but it does not necessarily have a valid `.DebugInfo` field. +bool InitializeCriticalSectionWithDebugInfoIfPossible( + CRITICAL_SECTION* critical_section); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_
diff --git a/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info_test.cc b/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info_test.cc new file mode 100644 index 0000000..dcc59655 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/critical_section_with_debug_info_test.cc
@@ -0,0 +1,34 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/critical_section_with_debug_info.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(CriticalSectionWithDebugInfo, CriticalSectionWithDebugInfo) { + CRITICAL_SECTION critical_section; + ASSERT_TRUE( + InitializeCriticalSectionWithDebugInfoIfPossible(&critical_section)); + EnterCriticalSection(&critical_section); + LeaveCriticalSection(&critical_section); + DeleteCriticalSection(&critical_section); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc new file mode 100644 index 0000000..47815ce --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
@@ -0,0 +1,602 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/exception_handler_server.h" + +#include <sddl.h> +#include <string.h> + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/win/process_snapshot_win.h" +#include "util/file/file_writer.h" +#include "util/stdlib/move.h" +#include "util/misc/random_string.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" +#include "util/win/get_function.h" +#include "util/win/handle.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_local_alloc.h" +#include "util/win/xp_compat.h" + +namespace crashpad { + +namespace { + +// We create two pipe instances, so that there's one listening while the +// PipeServiceProc is processing a registration. +const size_t kPipeInstances = 2; + +// Wraps CreateNamedPipe() to create a single named pipe instance. +// +// If first_instance is true, the named pipe instance will be created with +// FILE_FLAG_FIRST_PIPE_INSTANCE. This ensures that the the pipe name is not +// already in use when created. The first instance will be created with an +// untrusted integrity SACL so instances of this pipe can be connected to by +// processes of any integrity level. +HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name, + bool first_instance) { + SECURITY_ATTRIBUTES security_attributes; + SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr; + ScopedLocalAlloc scoped_sec_desc; + + if (first_instance) { + // Pre-Vista does not have integrity levels. + const DWORD version = GetVersion(); + const DWORD major_version = LOBYTE(LOWORD(version)); + const bool is_vista_or_later = major_version >= 6; + if (is_vista_or_later) { + // Mandatory Label, no ACE flags, no ObjectType, integrity level + // untrusted. + const wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)"; + + PSECURITY_DESCRIPTOR sec_desc; + PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor( + kSddl, SDDL_REVISION_1, &sec_desc, nullptr)) + << "ConvertStringSecurityDescriptorToSecurityDescriptor"; + + // Take ownership of the allocated SECURITY_DESCRIPTOR. + scoped_sec_desc.reset(sec_desc); + + memset(&security_attributes, 0, sizeof(security_attributes)); + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.lpSecurityDescriptor = sec_desc; + security_attributes.bInheritHandle = FALSE; + security_attributes_pointer = &security_attributes; + } + } + + return CreateNamedPipe( + pipe_name.c_str(), + PIPE_ACCESS_DUPLEX | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0), + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + kPipeInstances, + 512, + 512, + 0, + security_attributes_pointer); +} + +decltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() { + static const auto get_named_pipe_client_process_id = + GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId); + return get_named_pipe_client_process_id; +} + +HANDLE DuplicateEvent(HANDLE process, HANDLE event) { + HANDLE handle; + if (DuplicateHandle(GetCurrentProcess(), + event, + process, + &handle, + SYNCHRONIZE | EVENT_MODIFY_STATE, + false, + 0)) { + return handle; + } + return nullptr; +} + +} // namespace + +namespace internal { + +//! \brief Context information for the named pipe handler threads. +class PipeServiceContext { + public: + PipeServiceContext(HANDLE port, + HANDLE pipe, + ExceptionHandlerServer::Delegate* delegate, + base::Lock* clients_lock, + std::set<internal::ClientData*>* clients, + uint64_t shutdown_token) + : port_(port), + pipe_(pipe), + delegate_(delegate), + clients_lock_(clients_lock), + clients_(clients), + shutdown_token_(shutdown_token) {} + + HANDLE port() const { return port_; } + HANDLE pipe() const { return pipe_.get(); } + ExceptionHandlerServer::Delegate* delegate() const { return delegate_; } + base::Lock* clients_lock() const { return clients_lock_; } + std::set<internal::ClientData*>* clients() const { return clients_; } + uint64_t shutdown_token() const { return shutdown_token_; } + + private: + HANDLE port_; // weak + ScopedKernelHANDLE pipe_; + ExceptionHandlerServer::Delegate* delegate_; // weak + base::Lock* clients_lock_; // weak + std::set<internal::ClientData*>* clients_; // weak + uint64_t shutdown_token_; + + DISALLOW_COPY_AND_ASSIGN(PipeServiceContext); +}; + +//! \brief The context data for registered threadpool waits. +//! +//! This object must be created and destroyed on the main thread. Access must be +//! guarded by use of the lock() with the exception of the threadpool wait +//! variables which are accessed only by the main thread. +class ClientData { + public: + ClientData(HANDLE port, + ExceptionHandlerServer::Delegate* delegate, + ScopedKernelHANDLE process, + WinVMAddress crash_exception_information_address, + WinVMAddress non_crash_exception_information_address, + WinVMAddress debug_critical_section_address, + WAITORTIMERCALLBACK crash_dump_request_callback, + WAITORTIMERCALLBACK non_crash_dump_request_callback, + WAITORTIMERCALLBACK process_end_callback) + : crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE), + non_crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE), + process_end_thread_pool_wait_(INVALID_HANDLE_VALUE), + lock_(), + port_(port), + delegate_(delegate), + crash_dump_requested_event_( + CreateEvent(nullptr, false /* auto reset */, false, nullptr)), + non_crash_dump_requested_event_( + CreateEvent(nullptr, false /* auto reset */, false, nullptr)), + non_crash_dump_completed_event_( + CreateEvent(nullptr, false /* auto reset */, false, nullptr)), + process_(crashpad::move(process)), + crash_exception_information_address_( + crash_exception_information_address), + non_crash_exception_information_address_( + non_crash_exception_information_address), + debug_critical_section_address_(debug_critical_section_address) { + RegisterThreadPoolWaits(crash_dump_request_callback, + non_crash_dump_request_callback, + process_end_callback); + } + + ~ClientData() { + // It is important that this only access the threadpool waits (it's called + // from the main thread) until the waits are unregistered, to ensure that + // any outstanding callbacks are complete. + UnregisterThreadPoolWaits(); + } + + base::Lock* lock() { return &lock_; } + HANDLE port() const { return port_; } + ExceptionHandlerServer::Delegate* delegate() const { return delegate_; } + HANDLE crash_dump_requested_event() const { + return crash_dump_requested_event_.get(); + } + HANDLE non_crash_dump_requested_event() const { + return non_crash_dump_requested_event_.get(); + } + HANDLE non_crash_dump_completed_event() const { + return non_crash_dump_completed_event_.get(); + } + WinVMAddress crash_exception_information_address() const { + return crash_exception_information_address_; + } + WinVMAddress non_crash_exception_information_address() const { + return non_crash_exception_information_address_; + } + WinVMAddress debug_critical_section_address() const { + return debug_critical_section_address_; + } + HANDLE process() const { return process_.get(); } + + private: + void RegisterThreadPoolWaits( + WAITORTIMERCALLBACK crash_dump_request_callback, + WAITORTIMERCALLBACK non_crash_dump_request_callback, + WAITORTIMERCALLBACK process_end_callback) { + if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_, + crash_dump_requested_event_.get(), + crash_dump_request_callback, + this, + INFINITE, + WT_EXECUTEDEFAULT)) { + LOG(ERROR) << "RegisterWaitForSingleObject crash dump requested"; + } + + if (!RegisterWaitForSingleObject(&non_crash_dump_request_thread_pool_wait_, + non_crash_dump_requested_event_.get(), + non_crash_dump_request_callback, + this, + INFINITE, + WT_EXECUTEDEFAULT)) { + LOG(ERROR) << "RegisterWaitForSingleObject non-crash dump requested"; + } + + if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_, + process_.get(), + process_end_callback, + this, + INFINITE, + WT_EXECUTEONLYONCE)) { + LOG(ERROR) << "RegisterWaitForSingleObject process end"; + } + } + + // This blocks until outstanding calls complete so that we know it's safe to + // delete this object. Because of this, it must be executed on the main + // thread, not a threadpool thread. + void UnregisterThreadPoolWaits() { + UnregisterWaitEx(crash_dump_request_thread_pool_wait_, + INVALID_HANDLE_VALUE); + crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE; + UnregisterWaitEx(non_crash_dump_request_thread_pool_wait_, + INVALID_HANDLE_VALUE); + non_crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE; + UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE); + process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE; + } + + // These are only accessed on the main thread. + HANDLE crash_dump_request_thread_pool_wait_; + HANDLE non_crash_dump_request_thread_pool_wait_; + HANDLE process_end_thread_pool_wait_; + + base::Lock lock_; + // Access to these fields must be guarded by lock_. + HANDLE port_; // weak + ExceptionHandlerServer::Delegate* delegate_; // weak + ScopedKernelHANDLE crash_dump_requested_event_; + ScopedKernelHANDLE non_crash_dump_requested_event_; + ScopedKernelHANDLE non_crash_dump_completed_event_; + ScopedKernelHANDLE process_; + WinVMAddress crash_exception_information_address_; + WinVMAddress non_crash_exception_information_address_; + WinVMAddress debug_critical_section_address_; + + DISALLOW_COPY_AND_ASSIGN(ClientData); +}; + +} // namespace internal + +ExceptionHandlerServer::Delegate::~Delegate() { +} + +ExceptionHandlerServer::ExceptionHandlerServer(bool persistent) + : pipe_name_(), + port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)), + first_pipe_instance_(), + clients_lock_(), + clients_(), + persistent_(persistent) { +} + +ExceptionHandlerServer::~ExceptionHandlerServer() { +} + +void ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) { + DCHECK(pipe_name_.empty()); + DCHECK(!pipe_name.empty()); + + pipe_name_ = pipe_name; +} + +std::wstring ExceptionHandlerServer::CreatePipe() { + DCHECK(!first_pipe_instance_.is_valid()); + + int tries = 5; + std::string pipe_name_base = + base::StringPrintf("\\\\.\\pipe\\crashpad_%d_", GetCurrentProcessId()); + std::wstring pipe_name; + do { + pipe_name = base::UTF8ToUTF16(pipe_name_base + RandomString()); + + first_pipe_instance_.reset(CreateNamedPipeInstance(pipe_name, true)); + + // CreateNamedPipe() is documented as setting the error to + // ERROR_ACCESS_DENIED if FILE_FLAG_FIRST_PIPE_INSTANCE is specified and the + // pipe name is already in use. However it may set the error to other codes + // such as ERROR_PIPE_BUSY (if the pipe already exists and has reached its + // maximum instance count) or ERROR_INVALID_PARAMETER (if the pipe already + // exists and its attributes differ from those specified to + // CreateNamedPipe()). Some of these errors may be ambiguous: for example, + // ERROR_INVALID_PARAMETER may also occur if CreateNamedPipe() is called + // incorrectly even in the absence of an existing pipe by the same name. + // + // Rather than chasing down all of the possible errors that might indicate + // that a pipe name is already in use, retry up to a few times on any error. + } while (!first_pipe_instance_.is_valid() && --tries); + + PCHECK(first_pipe_instance_.is_valid()) << "CreateNamedPipe"; + + SetPipeName(pipe_name); + return pipe_name; +} + +void ExceptionHandlerServer::Run(Delegate* delegate) { + uint64_t shutdown_token = base::RandUint64(); + ScopedKernelHANDLE thread_handles[kPipeInstances]; + for (int i = 0; i < arraysize(thread_handles); ++i) { + HANDLE pipe; + if (first_pipe_instance_.is_valid()) { + pipe = first_pipe_instance_.release(); + } else { + pipe = CreateNamedPipeInstance(pipe_name_, i == 0); + PCHECK(pipe != INVALID_HANDLE_VALUE) << "CreateNamedPipe"; + } + + // Ownership of this object (and the pipe instance) is given to the new + // thread. We close the thread handles at the end of the scope. They clean + // up the context object and the pipe instance on termination. + internal::PipeServiceContext* context = + new internal::PipeServiceContext(port_.get(), + pipe, + delegate, + &clients_lock_, + &clients_, + shutdown_token); + thread_handles[i].reset( + CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr)); + PCHECK(thread_handles[i].is_valid()) << "CreateThread"; + } + + delegate->ExceptionHandlerServerStarted(); + + // This is the main loop of the server. Most work is done on the threadpool, + // other than process end handling which is posted back to this main thread, + // as we must unregister the threadpool waits here. + for (;;) { + OVERLAPPED* ov = nullptr; + ULONG_PTR key = 0; + DWORD bytes = 0; + GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE); + if (!key) { + // Shutting down. + break; + } + + // Otherwise, this is a request to unregister and destroy the given client. + // delete'ing the ClientData blocks in UnregisterWaitEx to ensure all + // outstanding threadpool waits are complete. This is important because the + // process handle can be signalled *before* the dump request is signalled. + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key); + base::AutoLock lock(clients_lock_); + clients_.erase(client); + delete client; + if (!persistent_ && clients_.empty()) + break; + } + + // Signal to the named pipe instances that they should terminate. + for (int i = 0; i < arraysize(thread_handles); ++i) { + ClientToServerMessage message; + memset(&message, 0, sizeof(message)); + message.type = ClientToServerMessage::kShutdown; + message.shutdown.token = shutdown_token; + ServerToClientMessage response; + SendToCrashHandlerServer(pipe_name_, + reinterpret_cast<ClientToServerMessage&>(message), + &response); + } + + for (auto& handle : thread_handles) + WaitForSingleObject(handle.get(), INFINITE); + + // Deleting ClientData does a blocking wait until the threadpool executions + // have terminated when unregistering them. + { + base::AutoLock lock(clients_lock_); + for (auto* client : clients_) + delete client; + clients_.clear(); + } +} + +void ExceptionHandlerServer::Stop() { + // Post a null key (third argument) to trigger shutdown. + PostQueuedCompletionStatus(port_.get(), 0, 0, nullptr); +} + +// This function must be called with service_context.pipe() already connected to +// a client pipe. It exchanges data with the client and adds a ClientData record +// to service_context->clients(). +// +// static +bool ExceptionHandlerServer::ServiceClientConnection( + const internal::PipeServiceContext& service_context) { + ClientToServerMessage message; + + if (!LoggingReadFile(service_context.pipe(), &message, sizeof(message))) + return false; + + switch (message.type) { + case ClientToServerMessage::kShutdown: { + if (message.shutdown.token != service_context.shutdown_token()) { + LOG(ERROR) << "forged shutdown request, got: " + << message.shutdown.token; + return false; + } + ServerToClientMessage shutdown_response = {}; + LoggingWriteFile(service_context.pipe(), + &shutdown_response, + sizeof(shutdown_response)); + return true; + } + + case ClientToServerMessage::kRegister: + // Handled below. + break; + + default: + LOG(ERROR) << "unhandled message type: " << message.type; + return false; + } + + if (message.registration.version != RegistrationRequest::kMessageVersion) { + LOG(ERROR) << "unexpected version. got: " << message.registration.version + << " expecting: " << RegistrationRequest::kMessageVersion; + return false; + } + + decltype(GetNamedPipeClientProcessId)* get_named_pipe_client_process_id = + GetNamedPipeClientProcessIdFunction(); + if (get_named_pipe_client_process_id) { + // GetNamedPipeClientProcessId is only available on Vista+. + DWORD real_pid = 0; + if (get_named_pipe_client_process_id(service_context.pipe(), &real_pid) && + message.registration.client_process_id != real_pid) { + LOG(ERROR) << "forged client pid, real pid: " << real_pid + << ", got: " << message.registration.client_process_id; + return false; + } + } + + // We attempt to open the process as us. This is the main case that should + // almost always succeed as the server will generally be more privileged. If + // we're running as a different user, it may be that we will fail to open + // the process, but the client will be able to, so we make a second attempt + // having impersonated the client. + HANDLE client_process = OpenProcess( + kXPProcessAllAccess, false, message.registration.client_process_id); + if (!client_process) { + if (!ImpersonateNamedPipeClient(service_context.pipe())) { + PLOG(ERROR) << "ImpersonateNamedPipeClient"; + return false; + } + client_process = OpenProcess( + kXPProcessAllAccess, false, message.registration.client_process_id); + PCHECK(RevertToSelf()); + if (!client_process) { + LOG(ERROR) << "failed to open " << message.registration.client_process_id; + return false; + } + } + + internal::ClientData* client; + { + base::AutoLock lock(*service_context.clients_lock()); + client = new internal::ClientData( + service_context.port(), + service_context.delegate(), + ScopedKernelHANDLE(client_process), + message.registration.crash_exception_information, + message.registration.non_crash_exception_information, + message.registration.critical_section_address, + &OnCrashDumpEvent, + &OnNonCrashDumpEvent, + &OnProcessEnd); + service_context.clients()->insert(client); + } + + // Duplicate the events back to the client so they can request a dump. + ServerToClientMessage response; + response.registration.request_crash_dump_event = + HandleToInt(DuplicateEvent( + client->process(), client->crash_dump_requested_event())); + response.registration.request_non_crash_dump_event = + HandleToInt(DuplicateEvent( + client->process(), client->non_crash_dump_requested_event())); + response.registration.non_crash_dump_completed_event = + HandleToInt(DuplicateEvent( + client->process(), client->non_crash_dump_completed_event())); + + if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response))) + return false; + + return false; +} + +// static +DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) { + internal::PipeServiceContext* service_context = + reinterpret_cast<internal::PipeServiceContext*>(ctx); + DCHECK(service_context); + + for (;;) { + bool ret = !!ConnectNamedPipe(service_context->pipe(), nullptr); + if (!ret && GetLastError() != ERROR_PIPE_CONNECTED) { + PLOG(ERROR) << "ConnectNamedPipe"; + } else if (ServiceClientConnection(*service_context)) { + break; + } + DisconnectNamedPipe(service_context->pipe()); + } + + delete service_context; + + return 0; +} + +// static +void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) { + // This function is executed on the thread pool. + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx); + base::AutoLock lock(*client->lock()); + + // Capture the exception. + unsigned int exit_code = client->delegate()->ExceptionHandlerServerException( + client->process(), + client->crash_exception_information_address(), + client->debug_critical_section_address()); + + TerminateProcess(client->process(), exit_code); +} + +// static +void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) { + // This function is executed on the thread pool. + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx); + base::AutoLock lock(*client->lock()); + + // Capture the exception. + client->delegate()->ExceptionHandlerServerException( + client->process(), + client->non_crash_exception_information_address(), + client->debug_critical_section_address()); + + bool result = !!SetEvent(client->non_crash_dump_completed_event()); + PLOG_IF(ERROR, !result) << "SetEvent"; +} + +// static +void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) { + // This function is executed on the thread pool. + internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx); + base::AutoLock lock(*client->lock()); + + // Post back to the main thread to have it delete this client record. + PostQueuedCompletionStatus(client->port(), 0, ULONG_PTR(client), nullptr); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server.h b/third_party/crashpad/crashpad/util/win/exception_handler_server.h new file mode 100644 index 0000000..46141414 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/exception_handler_server.h
@@ -0,0 +1,123 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_ +#define CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_ + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/synchronization/lock.h" +#include "util/file/file_io.h" +#include "util/win/address_types.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { + +namespace internal { +class PipeServiceContext; +class ClientData; +} // namespace internal + +//! \brief Runs the main exception-handling server in Crashpad's handler +//! process. +class ExceptionHandlerServer { + public: + class Delegate { + public: + virtual ~Delegate(); + + //! \brief Called when the server has created the named pipe connection + //! points and is ready to service requests. + virtual void ExceptionHandlerServerStarted() = 0; + + //! \brief Called when the client has signalled that it has encountered an + //! exception and so wants a crash dump to be taken. + //! + //! \param[in] process A handle to the client process. Ownership of the + //! lifetime of this handle is not passed to the delegate. + //! \param[in] exception_information_address The address in the client's + //! address space of an ExceptionInformation structure. + //! \param[in] debug_critical_section_address The address in the client's + //! address space of a `CRITICAL_SECTION` allocated with a valid + //! `.DebugInfo` field, or `0` if unavailable. + //! \return The exit code that should be used when terminating the client + //! process. + virtual unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) = 0; + }; + + //! \brief Constructs the exception handling server. + //! + //! \param[in] persistent `true` if Run() should not return until Stop() is + //! called. If `false`, Run() will return when all clients have exited, + //! although Run() will always wait for the first client to connect. + explicit ExceptionHandlerServer(bool persistent); + + ~ExceptionHandlerServer(); + + //! \brief Sets the pipe name to listen for client registrations on. + //! + //! Either this method or CreatePipe(), but not both, must be called before + //! Run(). + //! + //! \param[in] pipe_name The name of the pipe to listen on. Must be of the + //! form "\\.\pipe\<some_name>". + void SetPipeName(const std::wstring& pipe_name); + + //! \brief Creates a randomized pipe name to listen for client registrations + //! on and returns its name. + //! + //! Either this method or CreatePipe(), but not both, must be called before + //! Run(). + //! + //! \return The pipe name that will be listened on. + std::wstring CreatePipe(); + + //! \brief Runs the exception-handling server. + //! + //! \param[in] delegate The interface to which the exceptions are delegated + //! when they are caught in Run(). Ownership is not transferred. + void Run(Delegate* delegate); + + //! \brief Stops the exception-handling server. Returns immediately. The + //! object must not be destroyed until Run() returns. + void Stop(); + + private: + static bool ServiceClientConnection( + const internal::PipeServiceContext& service_context); + static DWORD __stdcall PipeServiceProc(void* ctx); + static void __stdcall OnCrashDumpEvent(void* ctx, BOOLEAN); + static void __stdcall OnNonCrashDumpEvent(void* ctx, BOOLEAN); + static void __stdcall OnProcessEnd(void* ctx, BOOLEAN); + + std::wstring pipe_name_; + ScopedKernelHANDLE port_; + ScopedFileHandle first_pipe_instance_; + + base::Lock clients_lock_; + std::set<internal::ClientData*> clients_; + + bool persistent_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc new file mode 100644 index 0000000..ee31e26 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc
@@ -0,0 +1,211 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/exception_handler_server.h" + +#include <windows.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/strings/utf_string_conversions.h" +#include "client/crashpad_client.h" +#include "gtest/gtest.h" +#include "test/win/win_child_process.h" +#include "util/thread/thread.h" +#include "util/win/address_types.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { +namespace { + +// Runs the ExceptionHandlerServer on a background thread. +class RunServerThread : public Thread { + public: + // Instantiates a thread which will invoke server->Run(delegate). + RunServerThread(ExceptionHandlerServer* server, + ExceptionHandlerServer::Delegate* delegate) + : server_(server), delegate_(delegate) {} + ~RunServerThread() override {} + + private: + // Thread: + void ThreadMain() override { server_->Run(delegate_); } + + ExceptionHandlerServer* server_; + ExceptionHandlerServer::Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(RunServerThread); +}; + +class TestDelegate : public ExceptionHandlerServer::Delegate { + public: + explicit TestDelegate(HANDLE server_ready) : server_ready_(server_ready) {} + ~TestDelegate() override {} + + void ExceptionHandlerServerStarted() override { + SetEvent(server_ready_); + } + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override { + return 0; + } + + void WaitForStart() { WaitForSingleObject(server_ready_, INFINITE); } + + private: + HANDLE server_ready_; // weak + bool started_; + + DISALLOW_COPY_AND_ASSIGN(TestDelegate); +}; + +class ExceptionHandlerServerTest : public testing::Test { + public: + ExceptionHandlerServerTest() + : server_(true), + pipe_name_(server_.CreatePipe()), + server_ready_(CreateEvent(nullptr, false, false, nullptr)), + delegate_(server_ready_.get()), + server_thread_(&server_, &delegate_) {} + + TestDelegate& delegate() { return delegate_; } + ExceptionHandlerServer& server() { return server_; } + Thread& server_thread() { return server_thread_; } + const std::wstring& pipe_name() const { return pipe_name_; } + + private: + ExceptionHandlerServer server_; + std::wstring pipe_name_; + ScopedKernelHANDLE server_ready_; + TestDelegate delegate_; + RunServerThread server_thread_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest); +}; + +// During destruction, ensures that the server is stopped and the background +// thread joined. +class ScopedStopServerAndJoinThread { + public: + ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread) + : server_(server), thread_(thread) {} + ~ScopedStopServerAndJoinThread() { + server_->Stop(); + thread_->Join(); + } + + private: + ExceptionHandlerServer* server_; + Thread* thread_; + DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); +}; + +TEST_F(ExceptionHandlerServerTest, Instantiate) { +} + +TEST_F(ExceptionHandlerServerTest, StartAndStop) { + server_thread().Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &server(), &server_thread()); + ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); +} + +TEST_F(ExceptionHandlerServerTest, StopWhileConnected) { + server_thread().Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &server(), &server_thread()); + ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); + CrashpadClient client; + client.SetHandlerIPCPipe(pipe_name()); + // Leaving this scope causes the server to be stopped, while the connection + // is still open. +} + +std::wstring ReadWString(FileHandle handle) { + size_t length = 0; + EXPECT_TRUE(LoggingReadFile(handle, &length, sizeof(length))); + std::wstring str(length, L'\0'); + if (length > 0) { + EXPECT_TRUE(LoggingReadFile(handle, &str[0], length * sizeof(str[0]))); + } + return str; +} + +void WriteWString(FileHandle handle, const std::wstring& str) { + size_t length = str.size(); + EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length))); + if (length > 0) { + EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length * sizeof(str[0]))); + } +} + +class TestClient final : public WinChildProcess { + public: + TestClient() : WinChildProcess() {} + + ~TestClient() {} + + private: + int Run() override { + std::wstring pipe_name = ReadWString(ReadPipeHandle()); + CrashpadClient client; + if (!client.SetHandlerIPCPipe(pipe_name)) { + ADD_FAILURE(); + return EXIT_FAILURE; + } + if (!client.UseHandler()) { + ADD_FAILURE(); + return EXIT_FAILURE; + } + WriteWString(WritePipeHandle(), L"OK"); + return EXIT_SUCCESS; + } + + DISALLOW_COPY_AND_ASSIGN(TestClient); +}; + +TEST_F(ExceptionHandlerServerTest, MultipleConnections) { + WinChildProcess::EntryPoint<TestClient>(); + + scoped_ptr<WinChildProcess::Handles> handles_1 = WinChildProcess::Launch(); + scoped_ptr<WinChildProcess::Handles> handles_2 = WinChildProcess::Launch(); + scoped_ptr<WinChildProcess::Handles> handles_3 = WinChildProcess::Launch(); + + // Must ensure the delegate outlasts the server. + { + server_thread().Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &server(), &server_thread()); + ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); + + // Tell all the children where to connect. + WriteWString(handles_1->write.get(), pipe_name()); + WriteWString(handles_2->write.get(), pipe_name()); + WriteWString(handles_3->write.get(), pipe_name()); + + ASSERT_EQ(L"OK", ReadWString(handles_3->read.get())); + ASSERT_EQ(L"OK", ReadWString(handles_2->read.get())); + ASSERT_EQ(L"OK", ReadWString(handles_1->read.get())); + } +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/get_function.cc b/third_party/crashpad/crashpad/util/win/get_function.cc new file mode 100644 index 0000000..d498d30b --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/get_function.cc
@@ -0,0 +1,44 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/get_function.h" + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" + +namespace crashpad { +namespace internal { + +FARPROC GetFunctionInternal( + const wchar_t* library, const char* function, bool required) { + HMODULE module = LoadLibrary(library); + DPCHECK(!required || module) << "LoadLibrary " << base::UTF16ToUTF8(library); + if (!module) { + return nullptr; + } + + // Strip off any leading :: that may have come from stringifying the + // function’s name. + if (function[0] == ':' && function[1] == ':' && + function[2] && function[2] != ':') { + function += 2; + } + + FARPROC proc = GetProcAddress(module, function); + DPCHECK(!required || proc) << "GetProcAddress " << function; + return proc; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/get_function.h b/third_party/crashpad/crashpad/util/win/get_function.h new file mode 100644 index 0000000..50e5f150 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/get_function.h
@@ -0,0 +1,121 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_GET_FUNCTION_H_ +#define CRASHPAD_UTIL_WIN_GET_FUNCTION_H_ + +#include <windows.h> + +//! \file + +namespace crashpad { +namespace internal { + +//! \brief Returns a function pointer to a named function in a library. +//! +//! Do not call this directly, use the GET_FUNCTION() or GET_FUNCTION_REQUIRED() +//! macros instead. +//! +//! This accesses \a library by calling `LoadLibrary()` and is subject to the +//! same restrictions as that function. Notably, it can’t be used from a +//! `DllMain()` entry point. +//! +//! \param[in] library The library to search in. +//! \param[in] function The function to search for. If a leading `::` is +//! present, it will be stripped. +//! \param[in] required If `true`, require the function to resolve by `DCHECK`. +//! +//! \return A pointer to the requested function on success. If \a required is +//! `true`, triggers a `DCHECK` assertion on failure, otherwise, `nullptr` +//! on failure. +FARPROC GetFunctionInternal( + const wchar_t* library, const char* function, bool required); + +//! \copydoc GetFunctionInternal +template <typename FunctionType> +FunctionType* GetFunction( + const wchar_t* library, const char* function, bool required) { + return reinterpret_cast<FunctionType*>( + internal::GetFunctionInternal(library, function, required)); +} + +} // namespace internal +} // namespace crashpad + +//! \brief Returns a function pointer to a named function in a library without +//! requiring that it be found. +//! +//! If the library or function cannot be found, this will return `nullptr`. This +//! macro is intended to be used to access functions that may not be available +//! at runtime. +//! +//! This macro returns a properly-typed function pointer. It is expected to be +//! used in this way: +//! \code +//! static const auto get_named_pipe_client_process_id = +//! GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId); +//! if (get_named_pipe_client_process_id) { +//! BOOL rv = get_named_pipe_client_process_id(pipe, &client_process_id); +//! } +//! \endcode +//! +//! This accesses \a library by calling `LoadLibrary()` and is subject to the +//! same restrictions as that function. Notably, it can’t be used from a +//! `DllMain()` entry point. +//! +//! \param[in] library The library to search in. +//! \param[in] function The function to search for. A leading `::` is +//! recommended when a wrapper function of the same name is present. +//! +//! \return A pointer to the requested function on success, or `nullptr` on +//! failure. +//! +//! \sa GET_FUNCTION_REQUIRED +#define GET_FUNCTION(library, function) \ + crashpad::internal::GetFunction<decltype(function)>( \ + library, #function, false) + +//! \brief Returns a function pointer to a named function in a library, +//! requiring that it be found. +//! +//! If the library or function cannot be found, this will trigger a `DCHECK` +//! assertion. This macro is intended to be used to access functions that are +//! always expected to be available at runtime but which are not present in any +//! import library. +//! +//! This macro returns a properly-typed function pointer. It is expected to be +//! used in this way: +//! \code +//! static const auto nt_query_object = +//! GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryObject); +//! NTSTATUS status = +//! nt_query_object(handle, type, &info, info_length, &return_length); +//! \endcode +//! +//! This accesses \a library by calling `LoadLibrary()` and is subject to the +//! same restrictions as that function. Notably, it can’t be used from a +//! `DllMain()` entry point. +//! +//! \param[in] library The library to search in. +//! \param[in] function The function to search for. A leading `::` is +//! recommended when a wrapper function of the same name is present. +//! +//! \return A pointer to the requested function. +//! +//! \sa GET_FUNCTION +#define GET_FUNCTION_REQUIRED(library, function) \ + crashpad::internal::GetFunction<decltype(function)>( \ + library, #function, true) + +#endif // CRASHPAD_UTIL_WIN_GET_FUNCTION_H_
diff --git a/third_party/crashpad/crashpad/util/win/get_function_test.cc b/third_party/crashpad/crashpad/util/win/get_function_test.cc new file mode 100644 index 0000000..a2217aef6 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/get_function_test.cc
@@ -0,0 +1,78 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/get_function.h" + +#include <windows.h> +#include <winternl.h> + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(GetFunction, GetFunction) { + // Check equivalence of GET_FUNCTION_REQUIRED() with functions that are + // available in the SDK normally. + EXPECT_EQ(&GetProcAddress, + GET_FUNCTION_REQUIRED(L"kernel32.dll", GetProcAddress)); + EXPECT_EQ(&LoadLibraryW, + GET_FUNCTION_REQUIRED(L"kernel32.dll", LoadLibraryW)); + + // Make sure that a function pointer retrieved by GET_FUNCTION_REQUIRED() can + // be called and that it works correctly. + const auto get_current_process_id = + GET_FUNCTION_REQUIRED(L"kernel32.dll", GetCurrentProcessId); + EXPECT_EQ(&GetCurrentProcessId, get_current_process_id); + ASSERT_TRUE(get_current_process_id); + EXPECT_EQ(GetCurrentProcessId(), get_current_process_id()); + + // GET_FUNCTION_REQUIRED() and GET_FUNCTION() should behave identically when + // the function is present. + EXPECT_EQ(get_current_process_id, + GET_FUNCTION(L"kernel32.dll", GetCurrentProcessId)); + + // Using a leading :: should also work. + EXPECT_EQ(get_current_process_id, + GET_FUNCTION(L"kernel32.dll", ::GetCurrentProcessId)); + EXPECT_EQ(get_current_process_id, + GET_FUNCTION_REQUIRED(L"kernel32.dll", ::GetCurrentProcessId)); + + // Try a function that’s declared in the SDK’s headers but that has no import + // library. + EXPECT_TRUE(GET_FUNCTION_REQUIRED(L"ntdll.dll", RtlNtStatusToDosError)); + + // GetNamedPipeClientProcessId() is only available on Vista and later. + const auto get_named_pipe_client_process_id = + GET_FUNCTION(L"kernel32.dll", GetNamedPipeClientProcessId); + const DWORD version = GetVersion(); + const DWORD major_version = LOBYTE(LOWORD(version)); + EXPECT_EQ(major_version >= 6, get_named_pipe_client_process_id != nullptr); + + // Test that GET_FUNCTION() can fail by trying a nonexistent library and a + // symbol that doesn’t exist in the specified library. + EXPECT_FALSE(GET_FUNCTION(L"not_a_real_library.dll", TerminateProcess)); + EXPECT_FALSE(GET_FUNCTION(L"ntdll.dll", TerminateProcess)); + EXPECT_FALSE(GET_FUNCTION(L"not_a_real_library.dll", ::TerminateProcess)); + EXPECT_FALSE(GET_FUNCTION(L"ntdll.dll", ::TerminateProcess)); + + // Here it is! + EXPECT_TRUE(GET_FUNCTION(L"kernel32.dll", TerminateProcess)); + EXPECT_TRUE(GET_FUNCTION(L"kernel32.dll", ::TerminateProcess)); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/handle.cc b/third_party/crashpad/crashpad/util/win/handle.cc new file mode 100644 index 0000000..c53f543 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/handle.cc
@@ -0,0 +1,36 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/handle.h" + +#include <stdint.h> + +#include "base/numerics/safe_conversions.h" + +namespace crashpad { + +// These functions use “int” for the 32-bit integer handle type because +// sign-extension needs to work correctly. INVALID_HANDLE_VALUE is defined as +// ((HANDLE)(LONG_PTR)-1), and this needs to round-trip through an integer and +// back to the same HANDLE value. + +int HandleToInt(HANDLE handle) { + return base::checked_cast<int>(reinterpret_cast<intptr_t>(handle)); +} + +HANDLE IntToHandle(int handle_int) { + return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle_int)); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/handle.h b/third_party/crashpad/crashpad/util/win/handle.h new file mode 100644 index 0000000..8a63069 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/handle.h
@@ -0,0 +1,65 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_ +#define CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_ + +#include <windows.h> + +namespace crashpad { + +//! \brief Converts a `HANDLE` to an `int`. +//! +//! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t +//! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even +//! in 64-bit processes on 64-bit operating systems. See <a +//! href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203">Interprocess +//! Communication Between 32-bit and 64-bit Applications</a>. +//! +//! This function safely converts a kernel `HANDLE` to an `int` similarly to a +//! cast operation. It checks that the operation can be performed safely, and +//! aborts execution if it cannot. +//! +//! \param[in] handle The kernel `HANDLE` to convert. +//! +//! \return An equivalent `int`, truncated (if necessary) from \a handle. If +//! truncation would have resulted in an `int` that could not be converted +//! back to \a handle, aborts execution. +//! +//! \sa IntToHandle() +int HandleToInt(HANDLE handle); + +//! \brief Converts an `int` to an `HANDLE`. +//! +//! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t +//! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even +//! in 64-bit processes on 64-bit operating systems. See <a +//! href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203">Interprocess +//! Communication Between 32-bit and 64-bit Applications</a>. +//! +//! This function safely convert an `int` to a kernel `HANDLE` similarly to a +//! cast operation. +//! +//! \param[in] handle_int The `int` to convert. This must have been produced by +//! HandleToInt(), possibly in a different process. +//! +//! \return An equivalent kernel `HANDLE`, sign-extended (if necessary) from \a +//! handle_int. +//! +//! \sa HandleToInt() +HANDLE IntToHandle(int handle_int); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_
diff --git a/third_party/crashpad/crashpad/util/win/handle_test.cc b/third_party/crashpad/crashpad/util/win/handle_test.cc new file mode 100644 index 0000000..60e5037 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/handle_test.cc
@@ -0,0 +1,53 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/handle.h" + +#include <stdint.h> + +#include <limits> + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Handle, HandleToInt) { + EXPECT_EQ(0, HandleToInt(nullptr)); + EXPECT_EQ(-1, HandleToInt(INVALID_HANDLE_VALUE)); + EXPECT_EQ(1, HandleToInt(reinterpret_cast<HANDLE>(1))); + EXPECT_EQ(std::numeric_limits<int>::max(), + HandleToInt(reinterpret_cast<HANDLE>( + static_cast<intptr_t>(std::numeric_limits<int>::max())))); + EXPECT_EQ(std::numeric_limits<int>::min(), + HandleToInt(reinterpret_cast<HANDLE>( + static_cast<intptr_t>(std::numeric_limits<int>::min())))); +} + +TEST(Handle, IntToHandle) { + EXPECT_EQ(nullptr, IntToHandle(0)); + EXPECT_EQ(INVALID_HANDLE_VALUE, IntToHandle(-1)); + EXPECT_EQ(reinterpret_cast<HANDLE>(1), IntToHandle(1)); + EXPECT_EQ(reinterpret_cast<HANDLE>( + static_cast<intptr_t>(std::numeric_limits<int>::max())), + IntToHandle(std::numeric_limits<int>::max())); + EXPECT_EQ(reinterpret_cast<HANDLE>( + static_cast<intptr_t>(std::numeric_limits<int>::min())), + IntToHandle(std::numeric_limits<int>::min())); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/module_version.cc b/third_party/crashpad/crashpad/util/win/module_version.cc new file mode 100644 index 0000000..f49df0c --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/module_version.cc
@@ -0,0 +1,56 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/module_version.h" + +#include <windows.h> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" + +namespace crashpad { + +bool GetModuleVersionAndType(const base::FilePath& path, + VS_FIXEDFILEINFO* vs_fixedfileinfo) { + DWORD size = GetFileVersionInfoSize(path.value().c_str(), nullptr); + if (!size) { + PLOG_IF(WARNING, GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND) + << "GetFileVersionInfoSize: " << base::UTF16ToUTF8(path.value()); + return false; + } + + scoped_ptr<uint8_t[]> data(new uint8_t[size]); + if (!GetFileVersionInfo(path.value().c_str(), 0, size, data.get())) { + PLOG(WARNING) << "GetFileVersionInfo: " + << base::UTF16ToUTF8(path.value()); + return false; + } + + VS_FIXEDFILEINFO* fixed_file_info; + UINT ffi_size; + if (!VerQueryValue(data.get(), + L"\\", + reinterpret_cast<void**>(&fixed_file_info), + &ffi_size)) { + PLOG(WARNING) << "VerQueryValue"; + return false; + } + + *vs_fixedfileinfo = *fixed_file_info; + vs_fixedfileinfo->dwFileFlags &= vs_fixedfileinfo->dwFileFlagsMask; + return true; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/module_version.h b/third_party/crashpad/crashpad/util/win/module_version.h new file mode 100644 index 0000000..be56eb9d --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/module_version.h
@@ -0,0 +1,39 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_MODULE_VERSION_H_ +#define CRASHPAD_UTIL_WIN_MODULE_VERSION_H_ + +#include <windows.h> + +#include "base/files/file_path.h" + +namespace crashpad { + +//! \brief Retrieve the type and version information from a given module (exe, +//! dll, etc.) +//! +//! \param[in] path The path to the module to be inspected. +//! \param[out] vs_fixedfileinfo The `VS_FIXEDFILEINFO` on success. +//! `dwFileFlags` will have been masked with `dwFileFlagsMask` already. +//! +//! \return `true` on success, or `false` on failure with a message logged. If +//! the module has no `VERSIONINFO` resource, `false` will be returned +//! without any messages logged. +bool GetModuleVersionAndType(const base::FilePath& path, + VS_FIXEDFILEINFO* vs_fixedfileinfo); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_MODULE_VERSION_H_
diff --git a/third_party/crashpad/crashpad/util/win/nt_internals.cc b/third_party/crashpad/crashpad/util/win/nt_internals.cc new file mode 100644 index 0000000..46192b353 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/nt_internals.cc
@@ -0,0 +1,102 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/nt_internals.h" + +#include "base/logging.h" +#include "util/win/get_function.h" + +// Declarations that the system headers should provide but don’t. + +struct CLIENT_ID; + +NTSTATUS NTAPI NtOpenThread(HANDLE* ThreadHandle, + ACCESS_MASK DesiredAccess, + OBJECT_ATTRIBUTES* ObjectAttributes, + CLIENT_ID* ClientId); + +namespace crashpad { + +NTSTATUS NtQuerySystemInformation( + SYSTEM_INFORMATION_CLASS system_information_class, + PVOID system_information, + ULONG system_information_length, + PULONG return_length) { + static const auto nt_query_system_information = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQuerySystemInformation); + return nt_query_system_information(system_information_class, + system_information, + system_information_length, + return_length); +} + +NTSTATUS NtQueryInformationThread(HANDLE thread_handle, + THREADINFOCLASS thread_information_class, + PVOID thread_information, + ULONG thread_information_length, + PULONG return_length) { + static const auto nt_query_information_thread = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryInformationThread); + return nt_query_information_thread(thread_handle, + thread_information_class, + thread_information, + thread_information_length, + return_length); +} + +template <class Traits> +NTSTATUS NtOpenThread(PHANDLE thread_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + const process_types::CLIENT_ID<Traits>* client_id) { + static const auto nt_open_thread = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtOpenThread); + return nt_open_thread( + thread_handle, + desired_access, + object_attributes, + const_cast<CLIENT_ID*>(reinterpret_cast<const CLIENT_ID*>(client_id))); +} + +NTSTATUS NtQueryObject(HANDLE handle, + OBJECT_INFORMATION_CLASS object_information_class, + void* object_information, + ULONG object_information_length, + ULONG* return_length) { + static const auto nt_query_object = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryObject); + return nt_query_object(handle, + object_information_class, + object_information, + object_information_length, + return_length); +} + +// Explicit instantiations with the only 2 valid template arguments to avoid +// putting the body of the function in the header. +template NTSTATUS NtOpenThread<process_types::internal::Traits32>( + PHANDLE thread_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + const process_types::CLIENT_ID<process_types::internal::Traits32>* + client_id); + +template NTSTATUS NtOpenThread<process_types::internal::Traits64>( + PHANDLE thread_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + const process_types::CLIENT_ID<process_types::internal::Traits64>* + client_id); + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/nt_internals.h b/third_party/crashpad/crashpad/util/win/nt_internals.h new file mode 100644 index 0000000..aab884f --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/nt_internals.h
@@ -0,0 +1,57 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <windows.h> +#include <winternl.h> + +#include "util/win/process_structs.h" + +namespace crashpad { + +// Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of +// ntstatus.h. +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) + +// winternal.h defines THREADINFOCLASS, but not all members. +enum { ThreadBasicInformation = 0 }; + +// winternal.h defines SYSTEM_INFORMATION_CLASS, but not all members. +enum { SystemExtendedHandleInformation = 64 }; + +NTSTATUS NtQuerySystemInformation( + SYSTEM_INFORMATION_CLASS system_information_class, + PVOID system_information, + ULONG system_information_length, + PULONG return_length); + +NTSTATUS NtQueryInformationThread(HANDLE thread_handle, + THREADINFOCLASS thread_information_class, + PVOID thread_information, + ULONG thread_information_length, + PULONG return_length); + +template <class Traits> +NTSTATUS NtOpenThread(PHANDLE thread_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + const process_types::CLIENT_ID<Traits>* client_id); + +NTSTATUS NtQueryObject(HANDLE handle, + OBJECT_INFORMATION_CLASS object_information_class, + void* object_information, + ULONG object_information_length, + ULONG* return_length); + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc b/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc new file mode 100644 index 0000000..4c243cc --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc
@@ -0,0 +1,69 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/ntstatus_logging.h" + +#include <string> + +#include "base/strings/stringprintf.h" + +namespace { + +std::string FormatNtstatus(DWORD ntstatus) { + char msgbuf[256]; + DWORD len = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE, + GetModuleHandle(L"ntdll.dll"), + ntstatus, + 0, + msgbuf, + arraysize(msgbuf), + nullptr); + if (len) { + return msgbuf; + } else { + return base::StringPrintf("<failed to retrieve error message (0x%x)>", + GetLastError()); + } +} + +} // namespace + +namespace logging { + +NtstatusLogMessage::NtstatusLogMessage( +#if defined(MINI_CHROMIUM_BASE_LOGGING_H_) + const char* function, +#endif + const char* file_path, + int line, + LogSeverity severity, + DWORD ntstatus) + : LogMessage( +#if defined(MINI_CHROMIUM_BASE_LOGGING_H_) + function, +#endif + file_path, + line, + severity), + ntstatus_(ntstatus) { +} + +NtstatusLogMessage::~NtstatusLogMessage() { + stream() << ": " << FormatNtstatus(ntstatus_) + << base::StringPrintf(" (0x%08x)", ntstatus_); +} + +} // namespace logging
diff --git a/third_party/crashpad/crashpad/util/win/ntstatus_logging.h b/third_party/crashpad/crashpad/util/win/ntstatus_logging.h new file mode 100644 index 0000000..0dc2460 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/ntstatus_logging.h
@@ -0,0 +1,98 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_ +#define CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace logging { + +class NtstatusLogMessage : public logging::LogMessage { + public: + NtstatusLogMessage( +#if defined(MINI_CHROMIUM_BASE_LOGGING_H_) + const char* function, +#endif + const char* file_path, + int line, + LogSeverity severity, + DWORD ntstatus); + ~NtstatusLogMessage(); + + private: + DWORD ntstatus_; + + DISALLOW_COPY_AND_ASSIGN(NtstatusLogMessage); +}; + +} // namespace logging + +#define NTSTATUS_LOG_STREAM(severity, ntstatus) \ + COMPACT_GOOGLE_LOG_EX_##severity(NtstatusLogMessage, ntstatus).stream() + +#if defined(MINI_CHROMIUM_BASE_LOGGING_H_) + +#define NTSTATUS_VLOG_STREAM(verbose_level, ntstatus) \ + logging::NtstatusLogMessage( \ + __PRETTY_FUNCTION__, __FILE__, __LINE__, -verbose_level, ntstatus) \ + .stream() + +#else + +#define NTSTATUS_VLOG_STREAM(verbose_level, ntstatus) \ + logging::NtstatusLogMessage(__FILE__, __LINE__, -verbose_level, ntstatus) \ + .stream() + +#endif // MINI_CHROMIUM_BASE_LOGGING_H_ + +#define NTSTATUS_LOG(severity, ntstatus) \ + LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), LOG_IS_ON(severity)) +#define NTSTATUS_LOG_IF(severity, condition, ntstatus) \ + LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), \ + LOG_IS_ON(severity) && (condition)) + +#define NTSTATUS_VLOG(verbose_level, ntstatus) \ + LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \ + VLOG_IS_ON(verbose_level)) +#define NTSTATUS_VLOG_IF(verbose_level, condition, ntstatus) \ + LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define NTSTATUS_CHECK(condition, ntstatus) \ + LAZY_STREAM(NTSTATUS_LOG_STREAM(FATAL, ntstatus), !(condition)) \ + << "Check failed: " #condition << ". " + +#define NTSTATUS_DLOG(severity, ntstatus) \ + LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), DLOG_IS_ON(severity)) +#define NTSTATUS_DLOG_IF(severity, condition, ntstatus) \ + LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), \ + DLOG_IS_ON(severity) && (condition)) + +#define NTSTATUS_DVLOG(verbose_level, ntstatus) \ + LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \ + DVLOG_IS_ON(verbose_level)) +#define NTSTATUS_DVLOG_IF(verbose_level, condition, ntstatus) \ + LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \ + DVLOG_IS_ON(verbose_level) && (condition)) + +#define NTSTATUS_DCHECK(condition, ntstatus) \ + LAZY_STREAM(NTSTATUS_LOG_STREAM(FATAL, ntstatus), \ + DCHECK_IS_ON && !(condition)) \ + << "Check failed: " #condition << ". " + +#endif // CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_
diff --git a/third_party/crashpad/crashpad/util/win/process_info.cc b/third_party/crashpad/crashpad/util/win/process_info.cc new file mode 100644 index 0000000..2ac3a98 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/process_info.cc
@@ -0,0 +1,692 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/process_info.h" + +#include <winternl.h> + +#include <algorithm> +#include <limits> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringprintf.h" +#include "base/template_util.h" +#include "build/build_config.h" +#include "util/numeric/safe_assignment.h" +#include "util/win/get_function.h" +#include "util/win/handle.h" +#include "util/win/nt_internals.h" +#include "util/win/ntstatus_logging.h" +#include "util/win/process_structs.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { + +namespace { + +NTSTATUS NtQueryInformationProcess(HANDLE process_handle, + PROCESSINFOCLASS process_information_class, + PVOID process_information, + ULONG process_information_length, + PULONG return_length) { + static const auto nt_query_information_process = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryInformationProcess); + return nt_query_information_process(process_handle, + process_information_class, + process_information, + process_information_length, + return_length); +} + +bool IsProcessWow64(HANDLE process_handle) { + static const auto is_wow64_process = + GET_FUNCTION(L"kernel32.dll", ::IsWow64Process); + if (!is_wow64_process) + return false; + BOOL is_wow64; + if (!is_wow64_process(process_handle, &is_wow64)) { + PLOG(ERROR) << "IsWow64Process"; + return false; + } + return !!is_wow64; +} + +template <class T> +bool ReadUnicodeString(HANDLE process, + const process_types::UNICODE_STRING<T>& us, + std::wstring* result) { + if (us.Length == 0) { + result->clear(); + return true; + } + DCHECK_EQ(us.Length % sizeof(wchar_t), 0u); + result->resize(us.Length / sizeof(wchar_t)); + SIZE_T bytes_read; + if (!ReadProcessMemory( + process, + reinterpret_cast<const void*>(static_cast<uintptr_t>(us.Buffer)), + &result->operator[](0), + us.Length, + &bytes_read)) { + PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; + return false; + } + if (bytes_read != us.Length) { + LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; + return false; + } + return true; +} + +template <class T> +bool ReadStruct(HANDLE process, WinVMAddress at, T* into) { + SIZE_T bytes_read; + if (!ReadProcessMemory(process, + reinterpret_cast<const void*>(at), + into, + sizeof(T), + &bytes_read)) { + // We don't have a name for the type we're reading, so include the signature + // to get the type of T. + PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; + return false; + } + if (bytes_read != sizeof(T)) { + LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; + return false; + } + return true; +} + +bool RegionIsAccessible(const MEMORY_BASIC_INFORMATION64& memory_info) { + return memory_info.State == MEM_COMMIT && + (memory_info.Protect & PAGE_NOACCESS) == 0 && + (memory_info.Protect & PAGE_GUARD) == 0; +} + +MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64( + const MEMORY_BASIC_INFORMATION& mbi) { + MEMORY_BASIC_INFORMATION64 mbi64 = {0}; + mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress); + mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase); + mbi64.AllocationProtect = mbi.AllocationProtect; + mbi64.RegionSize = mbi.RegionSize; + mbi64.State = mbi.State; + mbi64.Protect = mbi.Protect; + mbi64.Type = mbi.Type; + return mbi64; +} + +// NtQueryObject with a retry for size mismatch as well as a minimum size to +// retrieve (and expect). +scoped_ptr<uint8_t[]> QueryObject( + HANDLE handle, + OBJECT_INFORMATION_CLASS object_information_class, + ULONG minimum_size) { + ULONG size = minimum_size; + ULONG return_length; + scoped_ptr<uint8_t[]> buffer(new uint8_t[size]); + NTSTATUS status = crashpad::NtQueryObject( + handle, object_information_class, buffer.get(), size, &return_length); + if (status == STATUS_INFO_LENGTH_MISMATCH) { + DCHECK_GT(return_length, size); + size = return_length; + buffer.reset(new uint8_t[size]); + status = crashpad::NtQueryObject( + handle, object_information_class, buffer.get(), size, &return_length); + } + + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtQueryObject"; + return nullptr; + } + + DCHECK_GE(return_length, minimum_size); + return buffer; +} + +} // namespace + +template <class Traits> +bool GetProcessBasicInformation(HANDLE process, + bool is_wow64, + ProcessInfo* process_info, + WinVMAddress* peb_address, + WinVMSize* peb_size) { + ULONG bytes_returned; + process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; + NTSTATUS status = + crashpad::NtQueryInformationProcess(process, + ProcessBasicInformation, + &process_basic_information, + sizeof(process_basic_information), + &bytes_returned); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess"; + return false; + } + if (bytes_returned != sizeof(process_basic_information)) { + LOG(ERROR) << "NtQueryInformationProcess incorrect size"; + return false; + } + + // API functions (e.g. OpenProcess) take only a DWORD, so there's no sense in + // maintaining the top bits. + process_info->process_id_ = + static_cast<DWORD>(process_basic_information.UniqueProcessId); + process_info->inherited_from_process_id_ = static_cast<DWORD>( + process_basic_information.InheritedFromUniqueProcessId); + + // We now want to read the PEB to gather the rest of our information. The + // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32, + // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both). + // The address of this is found by a second call to NtQueryInformationProcess. + if (!is_wow64) { + *peb_address = process_basic_information.PebBaseAddress; + *peb_size = sizeof(process_types::PEB<Traits>); + } else { + ULONG_PTR wow64_peb_address; + status = crashpad::NtQueryInformationProcess(process, + ProcessWow64Information, + &wow64_peb_address, + sizeof(wow64_peb_address), + &bytes_returned); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status), "NtQueryInformationProcess"; + return false; + } + if (bytes_returned != sizeof(wow64_peb_address)) { + LOG(ERROR) << "NtQueryInformationProcess incorrect size"; + return false; + } + *peb_address = wow64_peb_address; + *peb_size = sizeof(process_types::PEB<process_types::internal::Traits32>); + } + + return true; +} + +template <class Traits> +bool ReadProcessData(HANDLE process, + WinVMAddress peb_address_vmaddr, + ProcessInfo* process_info) { + typename Traits::Pointer peb_address; + if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) { + LOG(ERROR) << base::StringPrintf("peb address 0x%x out of range", + peb_address_vmaddr); + return false; + } + + // Try to read the process environment block. + process_types::PEB<Traits> peb; + if (!ReadStruct(process, peb_address, &peb)) + return false; + + process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters; + if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) + return false; + + if (!ReadUnicodeString(process, + process_parameters.CommandLine, + &process_info->command_line_)) { + return false; + } + + process_types::PEB_LDR_DATA<Traits> peb_ldr_data; + if (!ReadStruct(process, peb.Ldr, &peb_ldr_data)) + return false; + + process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry; + + // Include the first module in the memory order list to get our the main + // executable's name, as it's not included in initialization order below. + if (!ReadStruct(process, + static_cast<WinVMAddress>( + peb_ldr_data.InMemoryOrderModuleList.Flink) - + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, + InMemoryOrderLinks), + &ldr_data_table_entry)) { + return false; + } + ProcessInfo::Module module; + if (!ReadUnicodeString( + process, ldr_data_table_entry.FullDllName, &module.name)) { + return false; + } + module.dll_base = ldr_data_table_entry.DllBase; + module.size = ldr_data_table_entry.SizeOfImage; + module.timestamp = ldr_data_table_entry.TimeDateStamp; + process_info->modules_.push_back(module); + + // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded + // modules. We use this method rather than EnumProcessModules to get the + // modules in initialization order rather than memory order. + typename Traits::Pointer last = + peb_ldr_data.InInitializationOrderModuleList.Blink; + for (typename Traits::Pointer cur = + peb_ldr_data.InInitializationOrderModuleList.Flink; + ; + cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { + // |cur| is the pointer to the LIST_ENTRY embedded in the + // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need + // to read from the target, and also offset back to the beginning of the + // structure. + if (!ReadStruct(process, + static_cast<WinVMAddress>(cur) - + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, + InInitializationOrderLinks), + &ldr_data_table_entry)) { + break; + } + // TODO(scottmg): Capture Checksum, etc. too? + if (!ReadUnicodeString( + process, ldr_data_table_entry.FullDllName, &module.name)) { + break; + } + module.dll_base = ldr_data_table_entry.DllBase; + module.size = ldr_data_table_entry.SizeOfImage; + module.timestamp = ldr_data_table_entry.TimeDateStamp; + process_info->modules_.push_back(module); + if (cur == last) + break; + } + + return true; +} + +bool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) { + DCHECK(process_info->memory_info_.empty()); + + const WinVMAddress min_address = 0; + // We can't use GetSystemInfo() to get the address space range for another + // process. VirtualQueryEx() will fail with ERROR_INVALID_PARAMETER if the + // address is above the highest memory address accessible to the process, so + // we just probe the entire potential range (2^32 for x86, or 2^64 for x64). + const WinVMAddress max_address = is_64_bit + ? std::numeric_limits<uint64_t>::max() + : std::numeric_limits<uint32_t>::max(); + MEMORY_BASIC_INFORMATION memory_basic_information; + for (WinVMAddress address = min_address; address <= max_address; + address += memory_basic_information.RegionSize) { + size_t result = VirtualQueryEx(process, + reinterpret_cast<void*>(address), + &memory_basic_information, + sizeof(memory_basic_information)); + if (result == 0) { + if (GetLastError() == ERROR_INVALID_PARAMETER) + break; + PLOG(ERROR) << "VirtualQueryEx"; + return false; + } + + process_info->memory_info_.push_back( + MemoryBasicInformationToMemoryBasicInformation64( + memory_basic_information)); + + if (memory_basic_information.RegionSize == 0) { + LOG(ERROR) << "RegionSize == 0"; + return false; + } + } + + return true; +} + +std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector( + HANDLE process) const { + ULONG buffer_size = 2 * 1024 * 1024; + scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); + + // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would + // return the correct size in the final argument, but it does not for + // SystemExtendedHandleInformation, so we loop and attempt larger sizes. + NTSTATUS status; + ULONG returned_length; + for (int tries = 0; tries < 5; ++tries) { + status = crashpad::NtQuerySystemInformation( + static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation), + buffer.get(), + buffer_size, + &returned_length); + if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH) + break; + + buffer_size *= 2; + buffer.reset(); + buffer.reset(new uint8_t[buffer_size]); + } + + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) + << "NtQuerySystemInformation SystemExtendedHandleInformation"; + return std::vector<Handle>(); + } + + const auto& system_handle_information_ex = + *reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>( + buffer.get()); + + DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) + + system_handle_information_ex.NumberOfHandles * + sizeof(system_handle_information_ex.Handles[0]), + returned_length); + + std::vector<Handle> handles; + + for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) { + const auto& handle = system_handle_information_ex.Handles[i]; + if (handle.UniqueProcessId != process_id_) + continue; + + Handle result_handle; + result_handle.handle = HandleToInt(handle.HandleValue); + result_handle.attributes = handle.HandleAttributes; + result_handle.granted_access = handle.GrantedAccess; + + // TODO(scottmg): Could special case for self. + HANDLE dup_handle; + if (DuplicateHandle(process, + handle.HandleValue, + GetCurrentProcess(), + &dup_handle, + 0, + false, + DUPLICATE_SAME_ACCESS)) { + // Some handles cannot be duplicated, for example, handles of type + // EtwRegistration. If we fail to duplicate, then we can't gather any more + // information, but include the information that we do have already. + ScopedKernelHANDLE scoped_dup_handle(dup_handle); + + scoped_ptr<uint8_t[]> object_basic_information_buffer = + QueryObject(dup_handle, + ObjectBasicInformation, + sizeof(PUBLIC_OBJECT_BASIC_INFORMATION)); + if (object_basic_information_buffer) { + PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information = + reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>( + object_basic_information_buffer.get()); + // The Attributes and GrantedAccess sometimes differ slightly between + // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and + // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in + // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the + // target process, rather than on the duplicated handle, so don't use + // them here. + + // Subtract one to account for our DuplicateHandle() and another for + // NtQueryObject() while the query was being executed. + DCHECK_GT(object_basic_information->PointerCount, 2u); + result_handle.pointer_count = + object_basic_information->PointerCount - 2; + + // Subtract one to account for our DuplicateHandle(). + DCHECK_GT(object_basic_information->HandleCount, 1u); + result_handle.handle_count = object_basic_information->HandleCount - 1; + } + + scoped_ptr<uint8_t[]> object_type_information_buffer = + QueryObject(dup_handle, + ObjectTypeInformation, + sizeof(PUBLIC_OBJECT_TYPE_INFORMATION)); + if (object_type_information_buffer) { + PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information = + reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>( + object_type_information_buffer.get()); + + DCHECK_EQ(object_type_information->TypeName.Length % + sizeof(result_handle.type_name[0]), + 0u); + result_handle.type_name = + std::wstring(object_type_information->TypeName.Buffer, + object_type_information->TypeName.Length / + sizeof(result_handle.type_name[0])); + } + } + + handles.push_back(result_handle); + } + return handles; +} + +ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { +} + +ProcessInfo::Module::~Module() { +} + +ProcessInfo::Handle::Handle() + : type_name(), + handle(0), + attributes(0), + granted_access(0), + pointer_count(0), + handle_count(0) { +} + +ProcessInfo::Handle::~Handle() { +} + +ProcessInfo::ProcessInfo() + : process_id_(), + inherited_from_process_id_(), + process_(), + command_line_(), + peb_address_(0), + peb_size_(0), + modules_(), + memory_info_(), + handles_(), + is_64_bit_(false), + is_wow64_(false), + initialized_() { +} + +ProcessInfo::~ProcessInfo() { +} + +bool ProcessInfo::Initialize(HANDLE process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_ = process; + + is_wow64_ = IsProcessWow64(process); + + if (is_wow64_) { + // If it's WoW64, then it's 32-on-64. + is_64_bit_ = false; + } else { + // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to + // distinguish between these two cases. + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + is_64_bit_ = + system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; + } + +#if defined(ARCH_CPU_32_BITS) + if (is_64_bit_) { + LOG(ERROR) << "Reading x64 process from x86 process not supported"; + return false; + } +#endif // ARCH_CPU_32_BITS + +#if defined(ARCH_CPU_64_BITS) + bool result = GetProcessBasicInformation<process_types::internal::Traits64>( + process, is_wow64_, this, &peb_address_, &peb_size_); +#else + bool result = GetProcessBasicInformation<process_types::internal::Traits32>( + process, false, this, &peb_address_, &peb_size_); +#endif // ARCH_CPU_64_BITS + + if (!result) { + LOG(ERROR) << "GetProcessBasicInformation failed"; + return false; + } + + result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>( + process, peb_address_, this) + : ReadProcessData<process_types::internal::Traits32>( + process, peb_address_, this); + if (!result) { + LOG(ERROR) << "ReadProcessData failed"; + return false; + } + + if (!ReadMemoryInfo(process, is_64_bit_, this)) { + LOG(ERROR) << "ReadMemoryInfo failed"; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessInfo::Is64Bit() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return is_64_bit_; +} + +bool ProcessInfo::IsWow64() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return is_wow64_; +} + +pid_t ProcessInfo::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_id_; +} + +pid_t ProcessInfo::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return inherited_from_process_id_; +} + +bool ProcessInfo::CommandLine(std::wstring* command_line) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *command_line = command_line_; + return true; +} + +void ProcessInfo::Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const { + *peb_address = peb_address_; + *peb_size = peb_size_; +} + +bool ProcessInfo::Modules(std::vector<Module>* modules) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *modules = modules_; + return true; +} + +const std::vector<MEMORY_BASIC_INFORMATION64>& ProcessInfo::MemoryInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return memory_info_; +} + +std::vector<CheckedRange<WinVMAddress, WinVMSize>> +ProcessInfo::GetReadableRanges( + const CheckedRange<WinVMAddress, WinVMSize>& range) const { + return GetReadableRangesOfMemoryMap(range, MemoryInfo()); +} + +bool ProcessInfo::LoggingRangeIsFullyReadable( + const CheckedRange<WinVMAddress, WinVMSize>& range) const { + const auto ranges = GetReadableRanges(range); + if (ranges.size() != 1) { + LOG(ERROR) << base::StringPrintf( + "range at 0x%llx, size 0x%llx fully unreadable", + range.base(), + range.size()); + return false; + } + if (ranges[0].base() != range.base() || ranges[0].size() != range.size()) { + LOG(ERROR) << base::StringPrintf( + "some of range at 0x%llx, size 0x%llx unreadable", + range.base(), + range.size()); + return false; + } + return true; +} + +const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (handles_.empty()) + handles_ = BuildHandleVector(process_); + return handles_; +} + +std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap( + const CheckedRange<WinVMAddress, WinVMSize>& range, + const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info) { + using Range = CheckedRange<WinVMAddress, WinVMSize>; + + // Find all the ranges that overlap the target range, maintaining their order. + std::vector<MEMORY_BASIC_INFORMATION64> overlapping; + for (const auto& mi : memory_info) { + static_assert(base::is_same<decltype(mi.BaseAddress), WinVMAddress>::value, + "expected range address to be WinVMAddress"); + static_assert(base::is_same<decltype(mi.RegionSize), WinVMSize>::value, + "expected range size to be WinVMSize"); + if (range.OverlapsRange(Range(mi.BaseAddress, mi.RegionSize))) + overlapping.push_back(mi); + } + if (overlapping.empty()) + return std::vector<Range>(); + + // For the first and last, trim to the boundary of the incoming range. + MEMORY_BASIC_INFORMATION64& front = overlapping.front(); + WinVMAddress original_front_base_address = front.BaseAddress; + front.BaseAddress = std::max(front.BaseAddress, range.base()); + front.RegionSize = + (original_front_base_address + front.RegionSize) - front.BaseAddress; + + MEMORY_BASIC_INFORMATION64& back = overlapping.back(); + WinVMAddress back_end = back.BaseAddress + back.RegionSize; + back.RegionSize = std::min(range.end(), back_end) - back.BaseAddress; + + // Discard all non-accessible. + overlapping.erase(std::remove_if(overlapping.begin(), + overlapping.end(), + [](const MEMORY_BASIC_INFORMATION64& mbi) { + return !RegionIsAccessible(mbi); + }), + overlapping.end()); + if (overlapping.empty()) + return std::vector<Range>(); + + // Convert to return type. + std::vector<Range> as_ranges; + for (const auto& mi : overlapping) { + as_ranges.push_back(Range(mi.BaseAddress, mi.RegionSize)); + DCHECK(as_ranges.back().IsValid()); + } + + // Coalesce remaining regions. + std::vector<Range> result; + result.push_back(as_ranges[0]); + for (size_t i = 1; i < as_ranges.size(); ++i) { + if (result.back().end() == as_ranges[i].base()) { + result.back().SetRange(result.back().base(), + result.back().size() + as_ranges[i].size()); + } else { + result.push_back(as_ranges[i]); + } + DCHECK(result.back().IsValid()); + } + + return result; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/process_info.h b/third_party/crashpad/crashpad/util/win/process_info.h new file mode 100644 index 0000000..fb1b8b3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/process_info.h
@@ -0,0 +1,202 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_PROCESS_INFO_H_ +#define CRASHPAD_UTIL_WIN_PROCESS_INFO_H_ + +#include <sys/types.h> +#include <windows.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/numeric/checked_range.h" +#include "util/win/address_types.h" + +namespace crashpad { + +//! \brief Gathers information about a process given its `HANDLE`. This consists +//! primarily of information stored in the Process Environment Block. +class ProcessInfo { + public: + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::wstring name; + + //! \brief The base address of the loaded DLL. + WinVMAddress dll_base; + + //! \brief The size of the module. + WinVMSize size; + + //! \brief The module's timestamp. + time_t timestamp; + }; + + struct Handle { + Handle(); + ~Handle(); + + //! \brief A string representation of the handle's type. + std::wstring type_name; + + //! \brief The handle's value. + int handle; + + //! \brief The attributes for the handle, e.g. `OBJ_INHERIT`, + //! `OBJ_CASE_INSENSITIVE`, etc. + uint32_t attributes; + + //! \brief The `ACCESS_MASK` for the handle in this process. + //! + //! See + //! http://blogs.msdn.com/b/openspecification/archive/2010/04/01/about-the-access-mask-structure.aspx + //! for more information. + uint32_t granted_access; + + //! \brief The number of kernel references to the object that this handle + //! refers to. + uint32_t pointer_count; + + //! \brief The number of open handles to the object that this handle refers + //! to. + uint32_t handle_count; + }; + + ProcessInfo(); + ~ProcessInfo(); + + //! \brief Initializes this object with information about the given + //! \a process. + //! + //! This method must be called successfully prior to calling any other + //! method in this class. This method may only be called once. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Initialize(HANDLE process); + + //! \return `true` if the target process is a 64-bit process. + bool Is64Bit() const; + + //! \return `true` if the target process is running on the Win32-on-Win64 + //! subsystem. + bool IsWow64() const; + + //! \return The target process's process ID. + pid_t ProcessID() const; + + //! \return The target process's parent process ID. + pid_t ParentProcessID() const; + + //! \return The command line from the target process's Process Environment + //! Block. + bool CommandLine(std::wstring* command_line) const; + + //! \brief Gets the address and size of the process's Process Environment + //! Block. + //! + //! \param[out] peb_address The address of the Process Environment Block. + //! \param[out] peb_size The size of the Process Environment Block. + void Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const; + + //! \brief Retrieves the modules loaded into the target process. + //! + //! The modules are enumerated in initialization order as detailed in the + //! Process Environment Block. The main executable will always be the + //! first element. + bool Modules(std::vector<Module>* modules) const; + + //! \brief Retrieves information about all pages mapped into the process. + const std::vector<MEMORY_BASIC_INFORMATION64>& MemoryInfo() const; + + //! \brief Given a range to be read from the target process, returns a vector + //! of ranges, representing the readable portions of the original range. + //! + //! \param[in] range The range being identified. + //! + //! \return A vector of ranges corresponding to the portion of \a range that + //! is readable based on the memory map. + std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges( + const CheckedRange<WinVMAddress, WinVMSize>& range) const; + + //! \brief Given a range in the target process, determines if the entire range + //! is readable. + //! + //! \param[in] range The range being inspected. + //! + //! \return `true` if the range is fully readable, otherwise `false` with a + //! message logged. + bool LoggingRangeIsFullyReadable( + const CheckedRange<WinVMAddress, WinVMSize>& range) const; + + //! \brief Retrieves information about open handles in the target process. + const std::vector<Handle>& Handles() const; + + private: + template <class Traits> + friend bool GetProcessBasicInformation(HANDLE process, + bool is_wow64, + ProcessInfo* process_info, + WinVMAddress* peb_address, + WinVMSize* peb_size); + template <class Traits> + friend bool ReadProcessData(HANDLE process, + WinVMAddress peb_address_vmaddr, + ProcessInfo* process_info); + + friend bool ReadMemoryInfo(HANDLE process, + bool is_64_bit, + ProcessInfo* process_info); + + std::vector<Handle> BuildHandleVector(HANDLE process) const; + + pid_t process_id_; + pid_t inherited_from_process_id_; + HANDLE process_; + std::wstring command_line_; + WinVMAddress peb_address_; + WinVMSize peb_size_; + std::vector<Module> modules_; + std::vector<MEMORY_BASIC_INFORMATION64> memory_info_; + + // Handles() is logically const, but updates this member on first retrieval. + // See https://crashpad.chromium.org/bug/9. + mutable std::vector<Handle> handles_; + + bool is_64_bit_; + bool is_wow64_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessInfo); +}; + +//! \brief Given a memory map of a process, and a range to be read from the +//! target process, returns a vector of ranges, representing the readable +//! portions of the original range. +//! +//! This is a free function for testing, but prefer +//! ProcessInfo::GetReadableRanges(). +std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap( + const CheckedRange<WinVMAddress, WinVMSize>& range, + const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
diff --git a/third_party/crashpad/crashpad/util/win/process_info_test.cc b/third_party/crashpad/crashpad/util/win/process_info_test.cc new file mode 100644 index 0000000..e5bde2f --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/process_info_test.cc
@@ -0,0 +1,648 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/process_info.h" + +#include <dbghelp.h> +#include <intrin.h> +#include <wchar.h> + +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" +#include "test/paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/misc/random_string.h" +#include "util/misc/uuid.h" +#include "util/win/command_line.h" +#include "util/win/get_function.h" +#include "util/win/handle.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { +namespace { + +const wchar_t kNtdllName[] = L"\\ntdll.dll"; + +bool IsProcessWow64(HANDLE process_handle) { + static const auto is_wow64_process = + GET_FUNCTION(L"kernel32.dll", ::IsWow64Process); + if (!is_wow64_process) + return false; + BOOL is_wow64; + if (!is_wow64_process(process_handle, &is_wow64)) { + PLOG(ERROR) << "IsWow64Process"; + return false; + } + return !!is_wow64; +} + +void VerifyAddressInInCodePage(const ProcessInfo& process_info, + WinVMAddress code_address) { + // Make sure the child code address is an code page address with the right + // information. + const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info = + process_info.MemoryInfo(); + bool found_region = false; + for (const auto& mi : memory_info) { + if (mi.BaseAddress <= code_address && + mi.BaseAddress + mi.RegionSize > code_address) { + EXPECT_EQ(MEM_COMMIT, mi.State); + EXPECT_EQ(PAGE_EXECUTE_READ, mi.Protect); + EXPECT_EQ(MEM_IMAGE, mi.Type); + EXPECT_FALSE(found_region); + found_region = true; + } + } + EXPECT_TRUE(found_region); +} + +TEST(ProcessInfo, Self) { + ProcessInfo process_info; + ASSERT_TRUE(process_info.Initialize(GetCurrentProcess())); + EXPECT_EQ(GetCurrentProcessId(), process_info.ProcessID()); + EXPECT_GT(process_info.ParentProcessID(), 0u); + +#if defined(ARCH_CPU_64_BITS) + EXPECT_TRUE(process_info.Is64Bit()); + EXPECT_FALSE(process_info.IsWow64()); +#else + EXPECT_FALSE(process_info.Is64Bit()); + if (IsProcessWow64(GetCurrentProcess())) + EXPECT_TRUE(process_info.IsWow64()); + else + EXPECT_FALSE(process_info.IsWow64()); +#endif + + std::wstring command_line; + EXPECT_TRUE(process_info.CommandLine(&command_line)); + EXPECT_EQ(std::wstring(GetCommandLine()), command_line); + + std::vector<ProcessInfo::Module> modules; + EXPECT_TRUE(process_info.Modules(&modules)); + ASSERT_GE(modules.size(), 2u); + const wchar_t kSelfName[] = L"\\crashpad_util_test.exe"; + ASSERT_GE(modules[0].name.size(), wcslen(kSelfName)); + EXPECT_EQ(kSelfName, + modules[0].name.substr(modules[0].name.size() - wcslen(kSelfName))); + ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName)); + EXPECT_EQ( + kNtdllName, + modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName))); + + EXPECT_EQ(reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)), + modules[0].dll_base); + EXPECT_EQ(reinterpret_cast<uintptr_t>(GetModuleHandle(L"ntdll.dll")), + modules[1].dll_base); + + EXPECT_GT(modules[0].size, 0); + EXPECT_GT(modules[1].size, 0); + + EXPECT_EQ(GetTimestampForLoadedLibrary(GetModuleHandle(nullptr)), + modules[0].timestamp); + // System modules are forced to particular stamps and the file header values + // don't match the on-disk times. Just make sure we got some data here. + EXPECT_GT(modules[1].timestamp, 0); + + // Find something we know is a code address and confirm expected memory + // information settings. + VerifyAddressInInCodePage(process_info, + reinterpret_cast<WinVMAddress>(_ReturnAddress())); +} + +void TestOtherProcess(const base::string16& directory_modification) { + ProcessInfo process_info; + + UUID done_uuid(UUID::InitializeWithNewTag{}); + + ScopedKernelHANDLE done( + CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str())); + ASSERT_TRUE(done.get()); + + base::FilePath test_executable = Paths::Executable(); + + std::wstring child_test_executable = + test_executable.DirName() + .Append(directory_modification) + .Append(test_executable.BaseName().RemoveFinalExtension().value() + + L"_process_info_test_child.exe") + .value(); + + std::wstring args; + AppendCommandLineArgument(done_uuid.ToString16(), &args); + + ChildLauncher child(child_test_executable, args); + child.Start(); + + // The child sends us a code address we can look up in the memory map. + WinVMAddress code_address; + CheckedReadFile( + child.stdout_read_handle(), &code_address, sizeof(code_address)); + + ASSERT_TRUE(process_info.Initialize(child.process_handle())); + + // Tell the test it's OK to shut down now that we've read our data. + EXPECT_TRUE(SetEvent(done.get())); + + std::vector<ProcessInfo::Module> modules; + EXPECT_TRUE(process_info.Modules(&modules)); + ASSERT_GE(modules.size(), 3u); + std::wstring child_name = L"\\crashpad_util_test_process_info_test_child.exe"; + ASSERT_GE(modules[0].name.size(), child_name.size()); + EXPECT_EQ(child_name, + modules[0].name.substr(modules[0].name.size() - child_name.size())); + ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName)); + EXPECT_EQ( + kNtdllName, + modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName))); + // lz32.dll is an uncommonly-used-but-always-available module that the test + // binary manually loads. + const wchar_t kLz32dllName[] = L"\\lz32.dll"; + ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName)); + EXPECT_EQ(kLz32dllName, + modules.back().name.substr(modules.back().name.size() - + wcslen(kLz32dllName))); + + VerifyAddressInInCodePage(process_info, code_address); +} + +TEST(ProcessInfo, OtherProcess) { + TestOtherProcess(FILE_PATH_LITERAL(".")); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ProcessInfo, OtherProcessWOW64) { +#ifndef NDEBUG + TestOtherProcess(FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestOtherProcess(FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} +#endif // ARCH_CPU_64_BITS + +TEST(ProcessInfo, AccessibleRangesNone) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_FREE; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4), + memory_info); + + EXPECT_TRUE(result.empty()); +} + +TEST(ProcessInfo, AccessibleRangesOneInside) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(2, result[0].base()); + EXPECT_EQ(4, result[0].size()); +} + +TEST(ProcessInfo, AccessibleRangesOneTruncatedSize) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 20; + mbi.State = MEM_FREE; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(5, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, AccessibleRangesOneMovedStart) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_FREE; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 20; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(10, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, ReserveIsInaccessible) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_RESERVE; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 20; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(10, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, PageGuardIsInaccessible) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + mbi.Protect = PAGE_GUARD; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 20; + mbi.State = MEM_COMMIT; + mbi.Protect = 0; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(10, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, PageNoAccessIsInaccessible) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + mbi.Protect = PAGE_NOACCESS; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 20; + mbi.State = MEM_COMMIT; + mbi.Protect = 0; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(10, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, AccessibleRangesCoalesced) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_FREE; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 2; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + mbi.BaseAddress = 12; + mbi.RegionSize = 5; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(11, 4), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(11, result[0].base()); + EXPECT_EQ(4, result[0].size()); +} + +TEST(ProcessInfo, AccessibleRangesMiddleUnavailable) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 0; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + mbi.BaseAddress = 10; + mbi.RegionSize = 5; + mbi.State = MEM_FREE; + memory_info.push_back(mbi); + + mbi.BaseAddress = 15; + mbi.RegionSize = 100; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 45), + memory_info); + + ASSERT_EQ(2u, result.size()); + EXPECT_EQ(5, result[0].base()); + EXPECT_EQ(5, result[0].size()); + EXPECT_EQ(15, result[1].base()); + EXPECT_EQ(35, result[1].size()); +} + +TEST(ProcessInfo, RequestedBeforeMap) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 10; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10), + memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(10, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, RequestedAfterMap) { + std::vector<MEMORY_BASIC_INFORMATION64> memory_info; + MEMORY_BASIC_INFORMATION64 mbi = {0}; + + mbi.BaseAddress = 10; + mbi.RegionSize = 10; + mbi.State = MEM_COMMIT; + memory_info.push_back(mbi); + + std::vector<CheckedRange<WinVMAddress, WinVMSize>> result = + GetReadableRangesOfMemoryMap( + CheckedRange<WinVMAddress, WinVMSize>(15, 100), memory_info); + + ASSERT_EQ(1u, result.size()); + EXPECT_EQ(15, result[0].base()); + EXPECT_EQ(5, result[0].size()); +} + +TEST(ProcessInfo, ReadableRanges) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + + const size_t kBlockSize = system_info.dwPageSize; + + // Allocate 6 pages, and then commit the second, fourth, and fifth, and mark + // two as committed, but PAGE_NOACCESS, so we have a setup like this: + // 0 1 2 3 4 5 + // +-----------------------------------------------+ + // | ????? | | xxxxx | | | ????? | + // +-----------------------------------------------+ + void* reserve_region = + VirtualAlloc(nullptr, kBlockSize * 6, MEM_RESERVE, PAGE_READWRITE); + ASSERT_TRUE(reserve_region); + uintptr_t reserved_as_int = reinterpret_cast<uintptr_t>(reserve_region); + void* readable1 = + VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + kBlockSize), + kBlockSize, + MEM_COMMIT, + PAGE_READWRITE); + ASSERT_TRUE(readable1); + void* readable2 = + VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 3)), + kBlockSize * 2, + MEM_COMMIT, + PAGE_READWRITE); + ASSERT_TRUE(readable2); + + void* no_access = + VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 2)), + kBlockSize, + MEM_COMMIT, + PAGE_NOACCESS); + ASSERT_TRUE(no_access); + + HANDLE current_process = GetCurrentProcess(); + ProcessInfo info; + info.Initialize(current_process); + auto ranges = info.GetReadableRanges( + CheckedRange<WinVMAddress, WinVMSize>(reserved_as_int, kBlockSize * 6)); + + ASSERT_EQ(2u, ranges.size()); + EXPECT_EQ(reserved_as_int + kBlockSize, ranges[0].base()); + EXPECT_EQ(kBlockSize, ranges[0].size()); + EXPECT_EQ(reserved_as_int + (kBlockSize * 3), ranges[1].base()); + EXPECT_EQ(kBlockSize * 2, ranges[1].size()); + + // Also make sure what we think we can read corresponds with what we can + // actually read. + scoped_ptr<unsigned char[]> into(new unsigned char[kBlockSize * 6]); + SIZE_T bytes_read; + + EXPECT_TRUE(ReadProcessMemory( + current_process, readable1, into.get(), kBlockSize, &bytes_read)); + EXPECT_EQ(kBlockSize, bytes_read); + + EXPECT_TRUE(ReadProcessMemory( + current_process, readable2, into.get(), kBlockSize * 2, &bytes_read)); + EXPECT_EQ(kBlockSize * 2, bytes_read); + + EXPECT_FALSE(ReadProcessMemory( + current_process, no_access, into.get(), kBlockSize, &bytes_read)); + EXPECT_FALSE(ReadProcessMemory( + current_process, reserve_region, into.get(), kBlockSize, &bytes_read)); + EXPECT_FALSE(ReadProcessMemory(current_process, + reserve_region, + into.get(), + kBlockSize * 6, + &bytes_read)); +} + +struct ScopedRegistryKeyCloseTraits { + static HKEY InvalidValue() { + return nullptr; + } + static void Free(HKEY key) { + RegCloseKey(key); + } +}; + +using ScopedRegistryKey = + base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>; + +TEST(ProcessInfo, Handles) { + ScopedTempDir temp_dir; + + ScopedFileHandle file(LoggingOpenFileForWrite( + temp_dir.path().Append(FILE_PATH_LITERAL("test_file")), + FileWriteMode::kTruncateOrCreate, + FilePermissions::kWorldReadable)); + ASSERT_TRUE(file.is_valid()); + + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(security_attributes); + security_attributes.bInheritHandle = true; + ScopedFileHandle inherited_file(CreateFile( + temp_dir.path().Append(FILE_PATH_LITERAL("inheritable")).value().c_str(), + GENERIC_WRITE, + 0, + &security_attributes, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr)); + ASSERT_TRUE(inherited_file.is_valid()); + + HKEY key; + ASSERT_EQ(ERROR_SUCCESS, + RegOpenKeyEx( + HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft", 0, KEY_READ, &key)); + ScopedRegistryKey scoped_key(key); + ASSERT_TRUE(scoped_key.is_valid()); + + std::wstring mapping_name = + base::UTF8ToUTF16(base::StringPrintf("Local\\test_mapping_%d_%s", + GetCurrentProcessId(), + RandomString().c_str())); + ScopedKernelHANDLE mapping(CreateFileMapping(INVALID_HANDLE_VALUE, + nullptr, + PAGE_READWRITE, + 0, + 1024, + mapping_name.c_str())); + ASSERT_TRUE(mapping.is_valid()) << ErrorMessage("CreateFileMapping"); + + ProcessInfo info; + info.Initialize(GetCurrentProcess()); + bool found_file_handle = false; + bool found_inherited_file_handle = false; + bool found_key_handle = false; + bool found_mapping_handle = false; + for (auto handle : info.Handles()) { + if (handle.handle == HandleToInt(file.get())) { + EXPECT_FALSE(found_file_handle); + found_file_handle = true; + EXPECT_EQ(L"File", handle.type_name); + EXPECT_EQ(1, handle.handle_count); + EXPECT_NE(0u, handle.pointer_count); + EXPECT_EQ(STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE, + handle.granted_access & STANDARD_RIGHTS_ALL); + EXPECT_EQ(0, handle.attributes); + } + if (handle.handle == HandleToInt(inherited_file.get())) { + EXPECT_FALSE(found_inherited_file_handle); + found_inherited_file_handle = true; + EXPECT_EQ(L"File", handle.type_name); + EXPECT_EQ(1, handle.handle_count); + EXPECT_NE(0u, handle.pointer_count); + EXPECT_EQ(STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE, + handle.granted_access & STANDARD_RIGHTS_ALL); + + // OBJ_INHERIT from ntdef.h, but including that conflicts with other + // headers. + const int kObjInherit = 0x2; + EXPECT_EQ(kObjInherit, handle.attributes); + } + if (handle.handle == HandleToInt(scoped_key.get())) { + EXPECT_FALSE(found_key_handle); + found_key_handle = true; + EXPECT_EQ(L"Key", handle.type_name); + EXPECT_EQ(1, handle.handle_count); + EXPECT_NE(0u, handle.pointer_count); + EXPECT_EQ(STANDARD_RIGHTS_READ, + handle.granted_access & STANDARD_RIGHTS_ALL); + EXPECT_EQ(0, handle.attributes); + } + if (handle.handle == HandleToInt(mapping.get())) { + EXPECT_FALSE(found_mapping_handle); + found_mapping_handle = true; + EXPECT_EQ(L"Section", handle.type_name); + EXPECT_EQ(1, handle.handle_count); + EXPECT_NE(0u, handle.pointer_count); + EXPECT_EQ(DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER | + STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE, + handle.granted_access & STANDARD_RIGHTS_ALL); + EXPECT_EQ(0, handle.attributes); + } + } + EXPECT_TRUE(found_file_handle); + EXPECT_TRUE(found_inherited_file_handle); + EXPECT_TRUE(found_key_handle); + EXPECT_TRUE(found_mapping_handle); +} + +TEST(ProcessInfo, OutOfRangeCheck) { + const size_t kAllocationSize = 12345; + scoped_ptr<char[]> safe_memory(new char[kAllocationSize]); + + ProcessInfo info; + info.Initialize(GetCurrentProcess()); + + EXPECT_TRUE( + info.LoggingRangeIsFullyReadable(CheckedRange<WinVMAddress, WinVMSize>( + reinterpret_cast<WinVMAddress>(safe_memory.get()), kAllocationSize))); + EXPECT_FALSE(info.LoggingRangeIsFullyReadable( + CheckedRange<WinVMAddress, WinVMSize>(0, 1024))); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/process_info_test_child.cc b/third_party/crashpad/crashpad/util/win/process_info_test_child.cc new file mode 100644 index 0000000..c587f5b --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/process_info_test_child.cc
@@ -0,0 +1,54 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include <intrin.h> +#include <stdlib.h> +#include <stdint.h> +#include <wchar.h> +#include <windows.h> + +// A simple binary to be loaded and inspected by ProcessInfo. +int wmain(int argc, wchar_t** argv) { + if (argc != 2) + abort(); + + // Get a handle to the event we use to communicate with our parent. + HANDLE done_event = CreateEvent(nullptr, true, false, argv[1]); + if (!done_event) + abort(); + + // Load an unusual module (that we don't depend upon) so we can do an + // existence check. + if (!LoadLibrary(L"lz32.dll")) + abort(); + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + if (out == INVALID_HANDLE_VALUE) + abort(); + // We just want any valid address that's known to be code. + uint64_t code_address = reinterpret_cast<uint64_t>(_ReturnAddress()); + DWORD bytes_written; + if (!WriteFile( + out, &code_address, sizeof(code_address), &bytes_written, nullptr) || + bytes_written != sizeof(code_address)) { + abort(); + } + + if (WaitForSingleObject(done_event, INFINITE) != WAIT_OBJECT_0) + abort(); + + CloseHandle(done_event); + + return EXIT_SUCCESS; +}
diff --git a/third_party/crashpad/crashpad/util/win/process_structs.h b/third_party/crashpad/crashpad/util/win/process_structs.h new file mode 100644 index 0000000..e33c4fb --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/process_structs.h
@@ -0,0 +1,511 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_ +#define CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_ + +#include <windows.h> + +namespace crashpad { +namespace process_types { + +namespace internal { + +struct Traits32 { + using Pad = DWORD; + using UnsignedIntegral = DWORD; + using Pointer = DWORD; +}; + +struct Traits64 { + using Pad = DWORD64; + using UnsignedIntegral = DWORD64; + using Pointer = DWORD64; +}; + +} // namespace internal + +//! \{ + +//! \brief Selected structures from winternl.h, ntddk.h, and `dt ntdll!xxx`, +//! customized to have both x86 and x64 sizes available. +//! +//! The structure and field names follow the Windows names for clarity. We do, +//! however, use plain integral types rather than pointer types. This is both +//! easier to define, and avoids accidentally treating them as pointers into the +//! current address space. +//! +//! The templates below should be instantiated with either internal::Traits32 +//! for structures targeting x86, or internal::Traits64 for x64. + +// We set packing to 1 so that we can explicitly control the layout to make it +// match the OS defined structures. +#pragma pack(push, 1) + +template <class Traits> +struct PROCESS_BASIC_INFORMATION { + union { + DWORD ExitStatus; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer PebBaseAddress; + typename Traits::UnsignedIntegral AffinityMask; + union { + DWORD BasePriority; + typename Traits::Pad padding_for_x64_1; + }; + typename Traits::UnsignedIntegral UniqueProcessId; + typename Traits::UnsignedIntegral InheritedFromUniqueProcessId; +}; + +template <class Traits> +struct LIST_ENTRY { + typename Traits::Pointer Flink; + typename Traits::Pointer Blink; +}; + +template <class Traits> +struct UNICODE_STRING { + union { + struct { + USHORT Length; + USHORT MaximumLength; + }; + typename Traits::Pad padding_for_x64; + }; + typename Traits::Pointer Buffer; +}; + +template <class Traits> +struct PEB_LDR_DATA { + ULONG Length; + DWORD Initialized; + typename Traits::Pointer SsHandle; + LIST_ENTRY<Traits> InLoadOrderModuleList; + LIST_ENTRY<Traits> InMemoryOrderModuleList; + LIST_ENTRY<Traits> InInitializationOrderModuleList; +}; + +template <class Traits> +struct LDR_DATA_TABLE_ENTRY { + LIST_ENTRY<Traits> InLoadOrderLinks; + LIST_ENTRY<Traits> InMemoryOrderLinks; + LIST_ENTRY<Traits> InInitializationOrderLinks; + typename Traits::Pointer DllBase; + typename Traits::Pointer EntryPoint; + union { + ULONG SizeOfImage; + typename Traits::Pad padding_for_x64; + }; + UNICODE_STRING<Traits> FullDllName; + UNICODE_STRING<Traits> BaseDllName; + ULONG Flags; + USHORT ObsoleteLoadCount; + USHORT TlsIndex; + LIST_ENTRY<Traits> HashLinks; + ULONG TimeDateStamp; +}; + +template <class Traits> +struct CURDIR { + UNICODE_STRING<Traits> DosPath; + typename Traits::Pointer Handle; +}; + +template <class Traits> +struct STRING { + union { + struct { + DWORD Length; + DWORD MaximumLength; + }; + typename Traits::Pad padding_for_x64; + }; + typename Traits::Pointer Buffer; +}; + +template <class Traits> +struct RTL_DRIVE_LETTER_CURDIR { + WORD Flags; + WORD Length; + DWORD TimeStamp; + STRING<Traits> DosPath; +}; + +template <class Traits> +struct RTL_USER_PROCESS_PARAMETERS { + DWORD MaximumLength; + DWORD Length; + DWORD Flags; + DWORD DebugFlags; + typename Traits::Pointer ConsoleHandle; + union { + DWORD ConsoleFlags; + typename Traits::Pad padding_for_x64; + }; + typename Traits::Pointer StandardInput; + typename Traits::Pointer StandardOutput; + typename Traits::Pointer StandardError; + CURDIR<Traits> CurrentDirectory; + UNICODE_STRING<Traits> DllPath; + UNICODE_STRING<Traits> ImagePathName; + UNICODE_STRING<Traits> CommandLine; + typename Traits::Pointer Environment; + DWORD StartingX; + DWORD StartingY; + DWORD CountX; + DWORD CountY; + DWORD CountCharsX; + DWORD CountCharsY; + DWORD FillAttribute; + DWORD WindowFlags; + DWORD ShowWindowFlags; + UNICODE_STRING<Traits> WindowTitle; + UNICODE_STRING<Traits> DesktopInfo; + UNICODE_STRING<Traits> ShellInfo; + UNICODE_STRING<Traits> RuntimeData; + RTL_DRIVE_LETTER_CURDIR<Traits> CurrentDirectores[32]; // sic. +}; + +template <class T> +struct GdiHandleBufferCountForBitness; + +template <> +struct GdiHandleBufferCountForBitness<internal::Traits32> { + enum { value = 34 }; +}; +template <> +struct GdiHandleBufferCountForBitness<internal::Traits64> { + enum { value = 60 }; +}; + +template <class Traits> +struct PEB { + union { + struct { + BYTE InheritedAddressSpace; + BYTE ReadImageFileExecOptions; + BYTE BeingDebugged; + BYTE BitField; + }; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer Mutant; + typename Traits::Pointer ImageBaseAddress; + typename Traits::Pointer Ldr; + typename Traits::Pointer ProcessParameters; + typename Traits::Pointer SubSystemData; + typename Traits::Pointer ProcessHeap; + typename Traits::Pointer FastPebLock; + typename Traits::Pointer AtlThunkSListPtr; + typename Traits::Pointer IFEOKey; + union { + DWORD CrossProcessFlags; + typename Traits::Pad padding_for_x64_1; + }; + typename Traits::Pointer KernelCallbackTable; + DWORD SystemReserved; + DWORD AtlThunkSListPtr32; + typename Traits::Pointer ApiSetMap; + union { + DWORD TlsExpansionCounter; + typename Traits::Pad padding_for_x64_2; + }; + typename Traits::Pointer TlsBitmap; + DWORD TlsBitmapBits[2]; + typename Traits::Pointer ReadOnlySharedMemoryBase; + typename Traits::Pointer SparePvoid0; + typename Traits::Pointer ReadOnlyStaticServerData; + typename Traits::Pointer AnsiCodePageData; + typename Traits::Pointer OemCodePageData; + typename Traits::Pointer UnicodeCaseTableData; + DWORD NumberOfProcessors; + DWORD NtGlobalFlag; + DWORD alignment_for_x86; + LARGE_INTEGER CriticalSectionTimeout; + typename Traits::UnsignedIntegral HeapSegmentReserve; + typename Traits::UnsignedIntegral HeapSegmentCommit; + typename Traits::UnsignedIntegral HeapDeCommitTotalFreeThreshold; + typename Traits::UnsignedIntegral HeapDeCommitFreeBlockThreshold; + DWORD NumberOfHeaps; + DWORD MaximumNumberOfHeaps; + typename Traits::Pointer ProcessHeaps; + typename Traits::Pointer GdiSharedHandleTable; + typename Traits::Pointer ProcessStarterHelper; + DWORD GdiDCAttributeList; + typename Traits::Pointer LoaderLock; + DWORD OSMajorVersion; + DWORD OSMinorVersion; + WORD OSBuildNumber; + WORD OSCSDVersion; + DWORD OSPlatformId; + DWORD ImageSubsystem; + DWORD ImageSubsystemMajorVersion; + union { + DWORD ImageSubsystemMinorVersion; + typename Traits::Pad padding_for_x64_3; + }; + typename Traits::UnsignedIntegral ActiveProcessAffinityMask; + DWORD GdiHandleBuffer[GdiHandleBufferCountForBitness<Traits>::value]; + typename Traits::Pointer PostProcessInitRoutine; + typename Traits::Pointer TlsExpansionBitmap; + DWORD TlsExpansionBitmapBits[32]; + union { + DWORD SessionId; + typename Traits::Pad padding_for_x64_4; + }; + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + typename Traits::Pointer pShimData; + typename Traits::Pointer AppCompatInfo; + UNICODE_STRING<Traits> CSDVersion; + typename Traits::Pointer ActivationContextData; + typename Traits::Pointer ProcessAssemblyStorageMap; + typename Traits::Pointer SystemDefaultActivationContextData; + typename Traits::Pointer SystemAssemblyStorageMap; + typename Traits::UnsignedIntegral MinimumStackCommit; + typename Traits::Pointer FlsCallback; + LIST_ENTRY<Traits> FlsListHead; + typename Traits::Pointer FlsBitmap; + DWORD FlsBitmapBits[4]; + DWORD FlsHighIndex; +}; + +template <class Traits> +struct NT_TIB { + union { + // See https://msdn.microsoft.com/en-us/library/dn424783.aspx. + typename Traits::Pointer Wow64Teb; + struct { + typename Traits::Pointer ExceptionList; + typename Traits::Pointer StackBase; + typename Traits::Pointer StackLimit; + typename Traits::Pointer SubSystemTib; + union { + typename Traits::Pointer FiberData; + BYTE Version[4]; + }; + typename Traits::Pointer ArbitraryUserPointer; + typename Traits::Pointer Self; + }; + }; +}; + +// See https://msdn.microsoft.com/en-us/library/gg750647.aspx. +template <class Traits> +struct CLIENT_ID { + typename Traits::Pointer UniqueProcess; + typename Traits::Pointer UniqueThread; +}; + +// This is a partial definition of the TEB, as we do not currently use many +// fields of it. See http://www.nirsoft.net/kernel_struct/vista/TEB.html, and +// the (arch-specific) definition of _TEB in winternl.h. +template <class Traits> +struct TEB { + NT_TIB<Traits> NtTib; + typename Traits::Pointer ProcessEnvironmentBlock; + CLIENT_ID<Traits> ClientId; + + // Not identical to Reserved2 in winternl's _TEB because we define ClientId. + typename Traits::Pointer RemainderOfReserved2[397]; + + BYTE Reserved3[1952]; + typename Traits::Pointer TlsSlots[64]; + BYTE Reserved4[8]; + typename Traits::Pointer Reserved5[26]; + typename Traits::Pointer ReservedForOle; + typename Traits::Pointer Reserved6[4]; + typename Traits::Pointer TlsExpansionSlots; +}; + +// See https://msdn.microsoft.com/en-us/library/gg750724.aspx. +template <class Traits> +struct SYSTEM_THREAD_INFORMATION { + union { + struct { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + union { + ULONG WaitTime; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer StartAddress; + CLIENT_ID<Traits> ClientId; + LONG Priority; + LONG BasePriority; + ULONG ContextSwitches; + ULONG ThreadState; + union { + ULONG WaitReason; + typename Traits::Pad padding_for_x64_1; + }; + }; + LARGE_INTEGER alignment_for_x86[8]; + }; +}; + +// There's an extra field in the x64 VM_COUNTERS (or maybe it's VM_COUNTERS_EX, +// it's not clear), so we just make separate specializations for 32/64. +template <class Traits> +struct VM_COUNTERS {}; + +template <> +struct VM_COUNTERS<internal::Traits32> { + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; +}; + +template <> +struct VM_COUNTERS<internal::Traits64> { + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + union { + ULONG PageFaultCount; + internal::Traits64::Pad padding_for_x64; + }; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivateUsage; +}; + +// See http://undocumented.ntinternals.net/source/usermode/undocumented%20functions/system%20information/structures/system_process_information.html +template <class Traits> +struct SYSTEM_PROCESS_INFORMATION { + ULONG NextEntryOffset; + ULONG NumberOfThreads; + LARGE_INTEGER WorkingSetPrivateSize; + ULONG HardFaultCount; + ULONG NumberOfThreadsHighWatermark; + ULONGLONG CycleTime; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING<Traits> ImageName; + union { + LONG BasePriority; + typename Traits::Pad padding_for_x64_0; + }; + union { + DWORD UniqueProcessId; + typename Traits::Pad padding_for_x64_1; + }; + union { + DWORD InheritedFromUniqueProcessId; + typename Traits::Pad padding_for_x64_2; + }; + ULONG HandleCount; + ULONG SessionId; + typename Traits::Pointer UniqueProcessKey; + union { + VM_COUNTERS<Traits> VirtualMemoryCounters; + LARGE_INTEGER alignment_for_x86[6]; + }; + IO_COUNTERS IoCounters; + SYSTEM_THREAD_INFORMATION<Traits> Threads[1]; +}; + +// http://undocumented.ntinternals.net/source/usermode/structures/thread_basic_information.html +template <class Traits> +struct THREAD_BASIC_INFORMATION { + union { + LONG ExitStatus; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer TebBaseAddress; + CLIENT_ID<Traits> ClientId; + typename Traits::Pointer AffinityMask; + ULONG Priority; + LONG BasePriority; +}; + +template <class Traits> +struct EXCEPTION_POINTERS { + typename Traits::Pointer ExceptionRecord; + typename Traits::Pointer ContextRecord; +}; + +using EXCEPTION_POINTERS32 = EXCEPTION_POINTERS<internal::Traits32>; +using EXCEPTION_POINTERS64 = EXCEPTION_POINTERS<internal::Traits64>; + +// This is defined in winnt.h, but not for cross-bitness. +template <class Traits> +struct RTL_CRITICAL_SECTION { + typename Traits::Pointer DebugInfo; + LONG LockCount; + LONG RecursionCount; + typename Traits::Pointer OwningThread; + typename Traits::Pointer LockSemaphore; + typename Traits::UnsignedIntegral SpinCount; +}; + +template <class Traits> +struct RTL_CRITICAL_SECTION_DEBUG { + union { + struct { + WORD Type; + WORD CreatorBackTraceIndex; + }; + typename Traits::Pad alignment_for_x64; + }; + typename Traits::Pointer CriticalSection; + LIST_ENTRY<Traits> ProcessLocksList; + DWORD EntryCount; + DWORD ContentionCount; + DWORD Flags; + WORD CreatorBackTraceIndexHigh; + WORD SpareWORD; +}; + +struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { + void* Object; + ULONG_PTR UniqueProcessId; + HANDLE HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +}; + +struct SYSTEM_HANDLE_INFORMATION_EX { + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +}; + +#pragma pack(pop) + +//! \} + +} // namespace process_types +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
diff --git a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc new file mode 100644 index 0000000..eff27e28 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
@@ -0,0 +1,94 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/registration_protocol_win.h" + +#include <windows.h> + +#include "base/logging.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { + +bool SendToCrashHandlerServer(const base::string16& pipe_name, + const crashpad::ClientToServerMessage& message, + crashpad::ServerToClientMessage* response) { + // Retry CreateFile() in a loop. If the handler isn’t actively waiting in + // ConnectNamedPipe() on a pipe instance because it’s busy doing something + // else, CreateFile() will fail with ERROR_PIPE_BUSY. WaitNamedPipe() waits + // until a pipe instance is ready, but there’s no way to wait for this + // condition and atomically open the client side of the pipe in a single + // operation. CallNamedPipe() implements similar retry logic to this, also in + // user-mode code. + // + // This loop is only intended to retry on ERROR_PIPE_BUSY. Notably, if the + // handler is so lazy that it hasn’t even called CreateNamedPipe() yet, + // CreateFile() will fail with ERROR_FILE_NOT_FOUND, and this function is + // expected to fail without retrying anything. If the handler is started at + // around the same time as its client, something external to this code must be + // done to guarantee correct ordering. When the client starts the handler + // itself, CrashpadClient::StartHandler() provides this synchronization. + for (int tries = 0;;) { + ScopedFileHANDLE pipe( + CreateFile(pipe_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, + nullptr)); + if (!pipe.is_valid()) { + if (++tries == 5 || GetLastError() != ERROR_PIPE_BUSY) { + PLOG(ERROR) << "CreateFile"; + return false; + } + + if (!WaitNamedPipe(pipe_name.c_str(), 1000) && + GetLastError() != ERROR_SEM_TIMEOUT) { + PLOG(ERROR) << "WaitNamedPipe"; + return false; + } + + continue; + } + + DWORD mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(pipe.get(), &mode, nullptr, nullptr)) { + PLOG(ERROR) << "SetNamedPipeHandleState"; + return false; + } + DWORD bytes_read = 0; + BOOL result = TransactNamedPipe( + pipe.get(), + // This is [in], but is incorrectly declared non-const. + const_cast<crashpad::ClientToServerMessage*>(&message), + sizeof(message), + response, + sizeof(*response), + &bytes_read, + nullptr); + if (!result) { + PLOG(ERROR) << "TransactNamedPipe"; + return false; + } + if (bytes_read != sizeof(*response)) { + LOG(ERROR) << "TransactNamedPipe: expected " << sizeof(*response) + << ", observed " << bytes_read; + return false; + } + return true; + } +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/registration_protocol_win.h b/third_party/crashpad/crashpad/util/win/registration_protocol_win.h new file mode 100644 index 0000000..c57c6e20 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/registration_protocol_win.h
@@ -0,0 +1,133 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_ +#define CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_ + +#include <windows.h> +#include <stdint.h> + +#include "base/strings/string16.h" +#include "util/win/address_types.h" + +namespace crashpad { + +#pragma pack(push, 1) + +//! \brief Structure read out of the client process by the crash handler when an +//! exception occurs. +struct ExceptionInformation { + //! \brief The address of an EXCEPTION_POINTERS structure in the client + //! process that describes the exception. + WinVMAddress exception_pointers; + + //! \brief The thread on which the exception happened. + DWORD thread_id; +}; + +//! \brief A client registration request. +struct RegistrationRequest { + //! \brief The expected value of `version`. This should be changed whenever + //! the messages or ExceptionInformation are modified incompatibly. + enum { kMessageVersion = 1 }; + + //! \brief Version field to detect skew between client and server. Should be + //! set to kMessageVersion. + int version; + + //! \brief The PID of the client process. + DWORD client_process_id; + + //! \brief The address, in the client process's address space, of an + //! ExceptionInformation structure, used when handling a crash dump + //! request. + WinVMAddress crash_exception_information; + + //! \brief The address, in the client process's address space, of an + //! ExceptionInformation structure, used when handling a non-crashing dump + //! request. + WinVMAddress non_crash_exception_information; + + //! \brief The address, in the client process's address space, of a + //! `CRITICAL_SECTION` allocated with a valid .DebugInfo field. This can + //! be accomplished by using + //! InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This + //! value can be `0`, however then limited lock data will be available in + //! minidumps. + WinVMAddress critical_section_address; +}; + +//! \brief A message only sent to the server by itself to trigger shutdown. +struct ShutdownRequest { + //! \brief A randomly generated token used to validate the the shutdown + //! request was not sent from another process. + uint64_t token; +}; + +//! \brief The message passed from client to server by +//! SendToCrashHandlerServer(). +struct ClientToServerMessage { + //! \brief Indicates which field of the union is in use. + enum Type : uint32_t { + //! \brief For RegistrationRequest. + kRegister, + //! \brief For ShutdownRequest. + kShutdown, + } type; + + union { + RegistrationRequest registration; + ShutdownRequest shutdown; + }; +}; + +//! \brief A client registration response. +struct RegistrationResponse { + //! \brief An event `HANDLE`, valid in the client process, that should be + //! signaled to request a crash report. Clients should convert the value + //! to a `HANDLE` by calling IntToHandle(). + int request_crash_dump_event; + + //! \brief An event `HANDLE`, valid in the client process, that should be + //! signaled to request a non-crashing dump be taken. Clients should + //! convert the value to a `HANDLE` by calling IntToHandle(). + int request_non_crash_dump_event; + + //! \brief An event `HANDLE`, valid in the client process, that will be + //! signaled by the server when the non-crashing dump is complete. Clients + //! should convert the value to a `HANDLE` by calling IntToHandle(). + int non_crash_dump_completed_event; +}; + +//! \brief The response sent back to the client via SendToCrashHandlerServer(). +union ServerToClientMessage { + RegistrationResponse registration; +}; + +#pragma pack(pop) + +//! \brief Connect over the given \a pipe_name, passing \a message to the +//! server, storing the server's reply into \a response. +//! +//! Typically clients will not use this directly, instead using +//! CrashpadClient::SetHandler(). +//! +//! \sa CrashpadClient::SetHandler() +bool SendToCrashHandlerServer(const base::string16& pipe_name, + const ClientToServerMessage& message, + ServerToClientMessage* response); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_
diff --git a/third_party/crashpad/crashpad/util/win/scoped_handle.cc b/third_party/crashpad/crashpad/util/win/scoped_handle.cc new file mode 100644 index 0000000..5eb440e --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_handle.cc
@@ -0,0 +1,32 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/scoped_handle.h" + +#include "base/logging.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace internal { + +void ScopedFileHANDLECloseTraits::Free(HANDLE handle) { + CheckedCloseFile(handle); +} + +void ScopedKernelHANDLECloseTraits::Free(HANDLE handle) { + PCHECK(CloseHandle(handle)); +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/scoped_handle.h b/third_party/crashpad/crashpad/util/win/scoped_handle.h new file mode 100644 index 0000000..68be4f7 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_handle.h
@@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_ +#define CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_ + +#include <windows.h> + +#include "base/scoped_generic.h" + +namespace crashpad { + +namespace internal { + +struct ScopedFileHANDLECloseTraits { + static HANDLE InvalidValue() { + return INVALID_HANDLE_VALUE; + } + static void Free(HANDLE handle); +}; + +struct ScopedKernelHANDLECloseTraits { + static HANDLE InvalidValue() { + return nullptr; + } + static void Free(HANDLE handle); +}; + +} // namespace internal + +using ScopedFileHANDLE = + base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>; +using ScopedKernelHANDLE = + base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
diff --git a/third_party/crashpad/crashpad/util/win/scoped_local_alloc.cc b/third_party/crashpad/crashpad/util/win/scoped_local_alloc.cc new file mode 100644 index 0000000..75838bc --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_local_alloc.cc
@@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/scoped_local_alloc.h" + +#include "base/logging.h" + +namespace crashpad { +namespace internal { + +// static +void LocalAllocTraits::Free(HLOCAL memory) { + PLOG_IF(ERROR, LocalFree(memory) != nullptr) << "LocalFree"; +} + +} // namespace internal +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/scoped_local_alloc.h b/third_party/crashpad/crashpad/util/win/scoped_local_alloc.h new file mode 100644 index 0000000..b4607852 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_local_alloc.h
@@ -0,0 +1,38 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_ +#define CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_ + +#include <windows.h> + +#include "base/scoped_generic.h" + +namespace crashpad { + +namespace internal { + +struct LocalAllocTraits { + static HLOCAL InvalidValue() { return nullptr; } + static void Free(HLOCAL mem); +}; + +} // namespace internal + +using ScopedLocalAlloc = + base::ScopedGeneric<HLOCAL, internal::LocalAllocTraits>; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_
diff --git a/third_party/crashpad/crashpad/util/win/scoped_process_suspend.cc b/third_party/crashpad/crashpad/util/win/scoped_process_suspend.cc new file mode 100644 index 0000000..4450efb --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_process_suspend.cc
@@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/scoped_process_suspend.h" + +#include <winternl.h> + +#include "base/logging.h" + +namespace crashpad { + +ScopedProcessSuspend::ScopedProcessSuspend(HANDLE process) : process_(process) { + typedef NTSTATUS(__stdcall * NtSuspendProcessFunc)(HANDLE); + static NtSuspendProcessFunc func = reinterpret_cast<NtSuspendProcessFunc>( + GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtSuspendProcess")); + NTSTATUS status = func(process_); + if (status) + LOG(ERROR) << "NtSuspendProcess, ntstatus=" << status; +} + +ScopedProcessSuspend::~ScopedProcessSuspend() { + typedef NTSTATUS(__stdcall * NtResumeProcessFunc)(HANDLE); + static NtResumeProcessFunc func = reinterpret_cast<NtResumeProcessFunc>( + GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtResumeProcess")); + NTSTATUS status = func(process_); + if (status) + LOG(ERROR) << "NtResumeProcess, ntstatus=" << status; +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/scoped_process_suspend.h b/third_party/crashpad/crashpad/util/win/scoped_process_suspend.h new file mode 100644 index 0000000..dad2e29 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_process_suspend.h
@@ -0,0 +1,46 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_ +#define CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_ + +#include <windows.h> + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Manages the suspension of another process. +//! +//! While an object of this class exists, the other process will be suspended. +//! Once the object is destroyed, the other process will become eligible for +//! resumption. +//! +//! If this process crashes while this object exists, there is no guarantee that +//! the other process will be resumed. +class ScopedProcessSuspend { + public: + //! Does not take ownership of \a process. + explicit ScopedProcessSuspend(HANDLE process); + ~ScopedProcessSuspend(); + + private: + HANDLE process_; + + DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspend); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
diff --git a/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc b/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc new file mode 100644 index 0000000..5b7813df --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc
@@ -0,0 +1,118 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/scoped_process_suspend.h" + +#include <tlhelp32.h> + +#include <algorithm> +#include <vector> + +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/win/win_child_process.h" +#include "util/win/xp_compat.h" + +namespace crashpad { +namespace test { +namespace { + +// There is no per-process suspend count on Windows, only a per-thread suspend +// count. NtSuspendProcess just suspends all threads of a given process. So, +// verify that all thread's suspend counts match the desired suspend count. +bool SuspendCountMatches(HANDLE process, DWORD desired_suspend_count) { + DWORD process_id = GetProcessId(process); + + ScopedKernelHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); + if (!snapshot.is_valid()) { + ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot"); + return false; + } + + THREADENTRY32 te; + te.dwSize = sizeof(te); + + BOOL ret = Thread32First(snapshot.get(), &te); + if (!ret) { + ADD_FAILURE() << ErrorMessage("Thread32First"); + return false; + } + do { + if (te.dwSize >= offsetof(THREADENTRY32, th32OwnerProcessID) + + sizeof(te.th32OwnerProcessID) && + te.th32OwnerProcessID == process_id) { + ScopedKernelHANDLE thread( + OpenThread(kXPThreadAllAccess, false, te.th32ThreadID)); + EXPECT_TRUE(thread.is_valid()) << ErrorMessage("OpenThread"); + DWORD result = SuspendThread(thread.get()); + EXPECT_NE(result, static_cast<DWORD>(-1)) + << ErrorMessage("SuspendThread"); + if (result != static_cast<DWORD>(-1)) { + EXPECT_NE(ResumeThread(thread.get()), static_cast<DWORD>(-1)) + << ErrorMessage("ResumeThread"); + } + if (result != desired_suspend_count) + return false; + } + te.dwSize = sizeof(te); + } while (Thread32Next(snapshot.get(), &te)); + + return true; +} + +class ScopedProcessSuspendTest final : public WinChildProcess { + public: + ScopedProcessSuspendTest() : WinChildProcess() {} + ~ScopedProcessSuspendTest() {} + + private: + int Run() override { + char c; + // Wait for notification from parent. + EXPECT_TRUE(LoggingReadFile(ReadPipeHandle(), &c, sizeof(c))); + EXPECT_EQ(' ', c); + return EXIT_SUCCESS; + } + + DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspendTest); +}; + +TEST(ScopedProcessSuspend, ScopedProcessSuspend) { + WinChildProcess::EntryPoint<ScopedProcessSuspendTest>(); + scoped_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch(); + + EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0)); + + { + ScopedProcessSuspend suspend0(handles->process.get()); + EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1)); + + { + ScopedProcessSuspend suspend1(handles->process.get()); + EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 2)); + } + + EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1)); + } + + EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0)); + + // Tell the child it's OK to terminate. + char c = ' '; + EXPECT_TRUE(WriteFile(handles->write.get(), &c, sizeof(c))); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/time.cc b/third_party/crashpad/crashpad/util/win/time.cc new file mode 100644 index 0000000..1665b9c --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/time.cc
@@ -0,0 +1,65 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/time.h" + +#include <inttypes.h> + +#include "base/logging.h" + +namespace crashpad { + +namespace { + +const uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6); + +uint64_t FiletimeToMicroseconds(const FILETIME& filetime) { + uint64_t t = (static_cast<uint64_t>(filetime.dwHighDateTime) << 32) | + filetime.dwLowDateTime; + return t / 10; // 100 nanosecond intervals to microseconds. +} + +timeval MicrosecondsToTimeval(uint64_t microseconds) { + timeval tv; + tv.tv_sec = static_cast<long>(microseconds / kMicrosecondsPerSecond); + tv.tv_usec = static_cast<long>(microseconds % kMicrosecondsPerSecond); + return tv; +} + +} // namespace + +timeval FiletimeToTimevalEpoch(const FILETIME& filetime) { + uint64_t microseconds = FiletimeToMicroseconds(filetime); + + // Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds. + // 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per + // day. It's not entirely clear, but it appears that these are solar seconds, + // not SI seconds, so there are no leap seconds to be considered. + const uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL; + DCHECK_GE(microseconds, kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond); + microseconds -= kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond; + return MicrosecondsToTimeval(microseconds); +} + +timeval FiletimeToTimevalInterval(const FILETIME& filetime) { + return MicrosecondsToTimeval(FiletimeToMicroseconds(filetime)); +} + +void GetTimeOfDay(timeval* tv) { + FILETIME filetime; + GetSystemTimeAsFileTime(&filetime); + *tv = FiletimeToTimevalEpoch(filetime); +} + +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/time.h b/third_party/crashpad/crashpad/util/win/time.h new file mode 100644 index 0000000..7cc0094 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/time.h
@@ -0,0 +1,36 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_TIME_H_ +#define CRASHPAD_UTIL_WIN_TIME_H_ + +#include <sys/time.h> +#include <windows.h> + +namespace crashpad { + +//! \brief Convert Windows `FILETIME` to `timeval`, converting from Windows +//! epoch to POSIX epoch. +timeval FiletimeToTimevalEpoch(const FILETIME& filetime); + +//! \brief Convert Windows `FILETIME` to `timeval`, treating the values as +//! an interval of elapsed time. +timeval FiletimeToTimevalInterval(const FILETIME& filetime); + +//! \brief Similar to POSIX gettimeofday(), gets the current system time in UTC. +void GetTimeOfDay(timeval* tv); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_TIME_H_
diff --git a/third_party/crashpad/crashpad/util/win/time_test.cc b/third_party/crashpad/crashpad/util/win/time_test.cc new file mode 100644 index 0000000..ad0771e3 --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/time_test.cc
@@ -0,0 +1,34 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#include "util/win/time.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Time, Reasonable) { + timeval t; + GetTimeOfDay(&t); + // Assume that time's time_t return is seconds from 1970. + time_t approx_now = time(nullptr); + EXPECT_GE(approx_now, t.tv_sec); + EXPECT_LT(approx_now - 100, t.tv_sec); +} + +} // namespace +} // namespace test +} // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/win/xp_compat.h b/third_party/crashpad/crashpad/util/win/xp_compat.h new file mode 100644 index 0000000..7c62e41b --- /dev/null +++ b/third_party/crashpad/crashpad/util/win/xp_compat.h
@@ -0,0 +1,40 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_WIN_XP_COMPAT_H_ +#define CRASHPAD_UTIL_WIN_XP_COMPAT_H_ + +#include <windows.h> + +namespace crashpad { + +enum { + //! \brief This is the XP-suitable value of `PROCESS_ALL_ACCESS`. + //! + //! Requesting `PROCESS_ALL_ACCESS` with the value defined when building + //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP. + //! See https://msdn.microsoft.com/en-ca/library/windows/desktop/ms684880.aspx + kXPProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF, + + //! \brief This is the XP-suitable value of `THREAD_ALL_ACCESS`. + //! + //! Requesting `THREAD_ALL_ACCESS` with the value defined when building + //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP. + //! See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686769.aspx + kXPThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF, +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_XP_COMPAT_H_
diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp index 8d0b22d..b39e14a0 100644 --- a/tools/clang/plugins/ChromeClassTester.cpp +++ b/tools/clang/plugins/ChromeClassTester.cpp
@@ -189,12 +189,31 @@ bool ChromeClassTester::InImplementationFile(SourceLocation record_location) { std::string filename; - if (!GetFilename(record_location, &filename)) - return false; - if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") || - ends_with(filename, ".mm")) { - return true; + if (options_.follow_macro_expansion) { + // If |record_location| is a macro, check the whole chain of expansions. + const SourceManager& source_manager = instance_.getSourceManager(); + while (true) { + if (GetFilename(record_location, &filename)) { + if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") || + ends_with(filename, ".mm")) { + return true; + } + } + if (!record_location.isMacroID()) { + break; + } + record_location = + source_manager.getImmediateExpansionRange(record_location).first; + } + } else { + if (!GetFilename(record_location, &filename)) + return false; + + if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") || + ends_with(filename, ".mm")) { + return true; + } } return false;
diff --git a/tools/clang/plugins/FindBadConstructsAction.cpp b/tools/clang/plugins/FindBadConstructsAction.cpp index 9fe6605..443eecc 100644 --- a/tools/clang/plugins/FindBadConstructsAction.cpp +++ b/tools/clang/plugins/FindBadConstructsAction.cpp
@@ -59,6 +59,8 @@ options_.with_ast_visitor = true; } else if (args[i] == "check-templates") { options_.check_templates = true; + } else if (args[i] == "follow-macro-expansion") { + options_.follow_macro_expansion = true; } else { parsed = false; llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n";
diff --git a/tools/clang/plugins/Options.h b/tools/clang/plugins/Options.h index 1538298..186c1e3 100644 --- a/tools/clang/plugins/Options.h +++ b/tools/clang/plugins/Options.h
@@ -20,6 +20,7 @@ bool check_enum_last_value; bool with_ast_visitor; bool check_templates; + bool follow_macro_expansion = false; }; } // namespace chrome_checker
diff --git a/tools/clang/plugins/tests/inline_ctor.cpp b/tools/clang/plugins/tests/inline_ctor.cpp index 6a751fb..f209b289 100644 --- a/tools/clang/plugins/tests/inline_ctor.cpp +++ b/tools/clang/plugins/tests/inline_ctor.cpp
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define MACRO_FROM_CPP INLINE_CTORS_IN_A_MACRO(InlineCtorsInvolvingCppAreOK) + #include "inline_ctor.h" #include <string> @@ -18,8 +20,13 @@ std::vector<std::string> two_; }; +INLINE_CTORS_IN_A_MACRO(InlineCtorsBehindAMacroAreOKInCpp); + int main() { InlineInCPPOK one; InlineCtorsArentOKInHeader two; + InlineCtorsBehindAMacroArentOKInHeader three; + InlineCtorsBehindAMacroAreOKInCpp four; + InlineCtorsInvolvingCppAreOK five; return 0; }
diff --git a/tools/clang/plugins/tests/inline_ctor.h b/tools/clang/plugins/tests/inline_ctor.h index 1ab1589..2b9beb97 100644 --- a/tools/clang/plugins/tests/inline_ctor.h +++ b/tools/clang/plugins/tests/inline_ctor.h
@@ -18,6 +18,19 @@ std::vector<std::string> two_; }; +#define INLINE_CTORS_IN_A_MACRO(CLASS_NAME) \ + class CLASS_NAME { \ + public: \ + CLASS_NAME() {} \ + ~CLASS_NAME() {} \ + \ + private: \ + std::vector<int> one_; \ + std::vector<std::string> two_; \ + } +INLINE_CTORS_IN_A_MACRO(InlineCtorsBehindAMacroArentOKInHeader); +MACRO_FROM_CPP; + class DeletedMembersInHeaderAreOKThough { public: DeletedMembersInHeaderAreOKThough() = delete;
diff --git a/tools/clang/plugins/tests/inline_ctor.txt b/tools/clang/plugins/tests/inline_ctor.txt index bd2c53b..7ca0629 100644 --- a/tools/clang/plugins/tests/inline_ctor.txt +++ b/tools/clang/plugins/tests/inline_ctor.txt
@@ -1,12 +1,21 @@ -In file included from inline_ctor.cpp:5: +In file included from inline_ctor.cpp:7: ./inline_ctor.h:13:3: warning: [chromium-style] Complex constructor has an inlined body. InlineCtorsArentOKInHeader() {} ^ ./inline_ctor.h:14:3: warning: [chromium-style] Complex destructor has an inline body. ~InlineCtorsArentOKInHeader() {} ^ -./inline_ctor.h:82:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor. +./inline_ctor.h:31:25: warning: [chromium-style] Complex constructor has an inlined body. +INLINE_CTORS_IN_A_MACRO(InlineCtorsBehindAMacroArentOKInHeader); + ^ +./inline_ctor.h:31:1: warning: [chromium-style] Complex destructor has an inline body. +INLINE_CTORS_IN_A_MACRO(InlineCtorsBehindAMacroArentOKInHeader); +^ +./inline_ctor.h:25:5: note: expanded from macro 'INLINE_CTORS_IN_A_MACRO' + ~CLASS_NAME() {} \ + ^ +./inline_ctor.h:95:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor. struct FourNonTrivialMembers { ^ -./inline_ctor.h:82:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor. -4 warnings generated. +./inline_ctor.h:95:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor. +6 warnings generated.
diff --git a/tools/clang/plugins/tests/test.sh b/tools/clang/plugins/tests/test.sh index 61bee99..b096d1c 100755 --- a/tools/clang/plugins/tests/test.sh +++ b/tools/clang/plugins/tests/test.sh
@@ -41,6 +41,8 @@ flags="${flags} -Xclang -plugin-arg-find-bad-constructs \ -Xclang with-ast-visitor" + flags="${flags} -Xclang -plugin-arg-find-bad-constructs \ + -Xclang follow-macro-expansion" local output="$("${CLANG_PATH}" -fsyntax-only -Wno-c++11-extensions \ -Wno-inconsistent-missing-override \
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index 3f70c6c5..66e22e9c 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -25,7 +25,7 @@ # Do NOT CHANGE this if you don't know what you're doing -- see # https://code.google.com/p/chromium/wiki/UpdatingClang # Reverting problematic clang rolls is safe, though. -CLANG_REVISION = '254793' +CLANG_REVISION = '255169' use_head_revision = 'LLVM_FORCE_HEAD_REVISION' in os.environ if use_head_revision:
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc index 50638fb..07d42e1 100644 --- a/tools/gn/ninja_build_writer.cc +++ b/tools/gn/ninja_build_writer.cc
@@ -85,6 +85,27 @@ return result; } +bool HasOutputIdenticalToLabel(const Target* target, + const std::string& short_name) { + if (target->output_type() != Target::ACTION && + target->output_type() != Target::ACTION_FOREACH) + return false; + + // Rather than convert all outputs to be relative to the build directory + // and then compare to the short name, convert the short name to look like a + // file in the output directory since this is only one conversion. + SourceFile short_name_as_source_file( + target->settings()->build_settings()->build_dir().value() + short_name); + + std::vector<SourceFile> outputs_as_source; + target->action_values().GetOutputsAsSourceFiles(target, &outputs_as_source); + for (const SourceFile& output_as_source : outputs_as_source) { + if (output_as_source == short_name_as_source_file) + return true; + } + return false; +} + // Given an output that appears more than once, generates an error message // that describes the problem and which targets generate it. Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets, @@ -294,7 +315,24 @@ if (small_name_count[label.name()] == 1 || (target->output_type() == Target::EXECUTABLE && exe_count[label.name()] == 1)) { - WritePhonyRule(target, target_file, label.name(), &written_rules); + // It's reasonable to generate output files in the root build directory + // with the same name as the target. Don't generate phony rules for + // these cases. + // + // All of this does not do the general checking of all target's outputs + // which may theoretically collide. But it's not very reasonable for + // a script target named "foo" to generate a file named "bar" with no + // extension in the root build directory while another target is named + // "bar". If this does occur, the user is likely to be confused when + // building "bar" that is builds foo anyway, so you probably just + // shouldn't do that. + // + // We should fix this however, and build up all generated script outputs + // and check everything against that. There are other edge cases that the + // current phony rule generator doesn't check. We may need to make a big + // set of every possible generated file in the build for this purpose. + if (!HasOutputIdenticalToLabel(target, label.name())) + WritePhonyRule(target, target_file, label.name(), &written_rules); } if (!all_rules.empty())
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 5d7c1d9..6ffba85 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -82,17 +82,9 @@ 'swarming_gpu_tests_gyp_debug_trybot': ['swarming', 'gpu_tests', 'gyp', 'debug_trybot'], 'swarming_gpu_tests_gyp_release_trybot': ['swarming', 'gpu_tests', 'gyp', 'release_trybot'], 'swarming_gn_debug_bot': ['swarming', 'gn', 'debug_bot'], - 'swarming_gn_debug_bot_minimal_symbols_x64': ['swarming', 'gn', 'debug_bot_minimal_symbols', 'x64'], - 'swarming_gn_debug_bot_x64': ['swarming', 'gn', 'debug_bot', 'x64'], 'swarming_gn_debug_trybot': ['swarming', 'gn', 'debug_trybot'], - 'swarming_gn_oilpan_debug_bot_minimal_symbols_x64': ['swarming', 'gn', 'oilpan', 'debug_bot', 'minimal_symbols', 'x64'], - 'swarming_gn_oilpan_debug_bot_x64': ['swarming', 'gn', 'oilpan', 'debug_bot', 'minimal_symbols', 'x64'], - 'swarming_gn_oilpan_release_bot_x64': ['swarming', 'gn', 'oilpan', 'release_bot', 'x64'], - 'swarming_gn_oilpan_release_trybot_minimal_symbols_x64': ['swarming', 'gn', 'oilpan', 'release_trybot', 'minimal_symbols', 'x64'], 'swarming_gn_release_bot': ['swarming', 'gn', 'release_bot'], - 'swarming_gn_release_bot_x64': ['swarming', 'gn', 'release_bot', 'x64'], 'swarming_gn_release_trybot': ['swarming', 'gn', 'release_bot'], - 'swarming_gn_release_trybot_minimal_symbols_x64': ['swarming', 'gn', 'release_trybot', 'minimal_symbols', 'x64'], 'swarming_gyp_asan_lsan_release_bot_x64': ['swarming', 'gyp', 'asan', 'lsan', 'release_bot', 'x64'], 'swarming_gyp_debug_bot_minimal_symbols_x64': ['swarming', 'gyp', 'debug_bot_minimal_symbols', 'x64'], 'swarming_gyp_debug_bot_minimal_symbols_x86': ['swarming', 'gyp', 'debug_bot_minimal_symbols', 'x86'], @@ -500,17 +492,17 @@ 'WebKit Mac Builder (dbg)': 'swarming_gyp_debug_bot_x64', 'WebKit Mac10.7 (dbg)': 'none', 'WebKit Mac Oilpan (dbg)': 'swarming_gyp_oilpan_debug_bot_x64', - 'WebKit Linux': 'swarming_gn_release_bot_x64', - 'WebKit Linux Trusty': 'swarming_gn_release_bot_x64', + 'WebKit Linux': 'swarming_gyp_release_bot_x64', + 'WebKit Linux Trusty': 'swarming_gyp_release_bot_x64', 'WebKit Linux 32': 'swarming_gyp_release_bot_x86', - 'WebKit Linux Oilpan': 'swarming_gn_oilpan_release_bot_x64', + 'WebKit Linux Oilpan': 'swarming_gyp_oilpan_release_bot_x64', 'WebKit Linux ASAN': 'swarming_gyp_asan_lsan_release_bot_x64', 'WebKit Linux Oilpan ASAN': 'swarming_gyp_oilpan_asan_lsan_release_bot_x64', 'WebKit Linux MSAN': 'swarming_gyp_msan_release_bot_x64', - 'WebKit Linux Leak': 'swarming_gn_release_bot_x64', - 'WebKit Linux Oilpan Leak': 'swarming_gn_oilpan_release_bot_x64', - 'WebKit Linux (dbg)': 'swarming_gn_debug_bot_x64', - 'WebKit Linux Oilpan (dbg)': 'swarming_gn_oilpan_debug_bot_x64', + 'WebKit Linux Leak': 'swarming_gyp_release_bot_x64', + 'WebKit Linux Oilpan Leak': 'swarming_gyp_oilpan_release_bot_x64', + 'WebKit Linux (dbg)': 'swarming_gyp_debug_bot_x64', + 'WebKit Linux Oilpan (dbg)': 'swarming_gyp_oilpan_debug_bot_x64', 'Android Builder': 'gyp_release_bot_android', 'WebKit Android (Nexus4)': 'gyp_release_bot_android', }, @@ -549,10 +541,10 @@ 'precise64 trunk': 'gn_official', }, 'tryserver.blink': { - 'linux_blink_dbg': 'swarming_gn_debug_bot_minimal_symbols_x64', - 'linux_blink_compile_dbg': 'swarming_gn_debug_bot_minimal_symbols_x64', - 'linux_blink_compile_rel': 'swarming_gn_release_trybot_minimal_symbols_x64', - 'linux_blink_rel': 'swarming_gn_release_trybot_minimal_symbols_x64', + 'linux_blink_dbg': 'swarming_gyp_debug_bot_minimal_symbols_x64', + 'linux_blink_compile_dbg': 'swarming_gyp_debug_bot_minimal_symbols_x64', + 'linux_blink_compile_rel': 'swarming_gyp_release_trybot_minimal_symbols_x64', + 'linux_blink_rel': 'swarming_gyp_release_trybot_minimal_symbols_x64', 'mac_blink_dbg': 'swarming_gyp_debug_bot_minimal_symbols_x64', 'mac_blink_compile_dbg': 'swarming_gyp_debug_bot_minimal_symbols_x64', 'mac_blink_compile_rel': 'swarming_gyp_release_bot_minimal_symbols_x64', @@ -561,16 +553,16 @@ 'win_blink_compile_dbg': 'swarming_gyp_debug_bot_minimal_symbols_x86', 'win_blink_compile_rel': 'swarming_gyp_release_trybot_minimal_symbols_x86', 'win_blink_rel': 'swarming_gyp_release_trybot_minimal_symbols_x86', - 'linux_blink_oilpan_dbg': 'swarming_gn_oilpan_debug_bot_minimal_symbols_x64', - 'linux_blink_oilpan_rel': 'swarming_gn_oilpan_release_trybot_minimal_symbols_x64', - 'linux_blink_oilpan_compile_rel': 'swarming_gn_oilpan_release_trybot_minimal_symbols_x64', + 'linux_blink_oilpan_dbg': 'swarming_gyp_oilpan_debug_bot_minimal_symbols_x64', + 'linux_blink_oilpan_rel': 'swarming_gyp_oilpan_release_trybot_minimal_symbols_x64', + 'linux_blink_oilpan_compile_rel': 'swarming_gyp_oilpan_release_trybot_minimal_symbols_x64', 'mac_blink_oilpan_dbg': 'swarming_gyp_oilpan_debug_bot_minimal_symbols_x64', 'mac_blink_oilpan_rel': 'swarming_gyp_oilpan_release_trybot_minimal_symbols_x64', 'mac_blink_oilpan_compile_rel': 'swarming_gyp_oilpan_release_trybot_minimal_symbols_x64', 'win_blink_oilpan_dbg': 'swarming_gyp_oilpan_debug_bot_minimal_symbols_x86', 'win_blink_oilpan_rel': 'swarming_gyp_oilpan_release_trybot_minimal_symbols_x86', 'win_blink_oilpan_compile_rel': 'swarming_gyp_oilpan_release_trybot_minimal_symbols_x86', - 'linux_blink_rel_ng': 'swarming_gn_release_trybot_minimal_symbols_x64', + 'linux_blink_rel_ng': 'swarming_gyp_release_trybot_minimal_symbols_x64', 'blink_presubmit': 'none', }, 'tryserver.chromium.android': {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 618e243..a7638ad 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -23318,6 +23318,20 @@ </summary> </histogram> +<histogram name="Net.ErrAborted.ReceivedBytes" units="bytes"> + <owner>csharrison@chromium.org</owner> + <summary> + The TotalReceivedBytes() at the time the request finishes with ERR_ABORTED. + </summary> +</histogram> + +<histogram name="Net.ErrAborted.SentBytes" units="bytes"> + <owner>csharrison@chromium.org</owner> + <summary> + The TotalSentBytes() at the time the request finishes with ERR_ABORTED. + </summary> +</histogram> + <histogram name="Net.ErrorCodesForHTTPSGoogleMainFrame" enum="NetErrorCodes"> <obsolete> Deprecated as of 2012/5/16, replaced by @@ -46192,6 +46206,11 @@ <histogram name="Startup.BrowserMessageLoopStartHardFaultCount.Success" enum="BooleanSuccess"> <owner>chrisha@chromium.org</owner> + <obsolete> + Deprecated 12/2015. No longer tracked because values collected on stable + show that the function pretty much never fails (succeeds 99.9996% of the + time). + </obsolete> <summary> If OS support was detected (Windows 7 and greater) indicates whether it was possible to determine the number of hard faults that have occurred in the @@ -63953,6 +63972,16 @@ <int value="1072" label="SVG1DOMSVGTests"/> <int value="1073" label="V8SVGViewElement_ViewTarget_AttributeGetter"/> <int value="1074" label="DisableRemotePlaybackAttribute"/> + <int value="1075" label="V8SloppyMode"/> + <int value="1076" label="V8StrictMode"/> + <int value="1077" label="V8StrongMode"/> + <int value="1078" label="AudioNodeConnectToAudioNode"/> + <int value="1079" label="AudioNodeConnectToAudioParam"/> + <int value="1080" label="AudioNodeDisconnectFromAudioNode"/> + <int value="1081" label="AudioNodeDisconnectFromAudioParam"/> + <int value="1082" label="V8CSSFontFaceRule_Style_AttributeGetter"/> + <int value="1083" label="SelectionCollapseNull"/> + <int value="1084" label="SelectionSetBaseAndExtentNull"/> </enum> <enum name="FetchRequestMode" type="int">
diff --git a/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder.py b/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder.py index 9df0eb9..fafa2884 100644 --- a/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder.py +++ b/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder.py
@@ -310,12 +310,19 @@ @decorators.Cache -def _GetTrybotList(): +def _GetBuilderList(): f = urllib2.urlopen( 'http://build.chromium.org/p/tryserver.chromium.perf/json') builders = json.loads(f.read()).get('builders', {}).keys() + # Exclude unsupported bots like win xp and some dummy bots. + builders = [bot for bot in builders if bot not in EXCLUDED_BOTS] + return builders + + +def _GetTrybotList(): + builders = _GetBuilderList() builders = ['trybot-%s' % bot.replace('_perf_bisect', '').replace('_', '-') - for bot in builders if bot not in EXCLUDED_BOTS] + for bot in builders] builders.extend(INCLUDE_BOTS) return sorted(builders) @@ -330,11 +337,7 @@ bot_platform += '-x64' return {bot_platform: bot} - f = urllib2.urlopen( - 'http://build.chromium.org/p/tryserver.chromium.perf/json') - builders = json.loads(f.read()).get('builders', {}).keys() - # Exclude unsupported bots like win xp and some dummy bots. - builders = [bot for bot in builders if bot not in EXCLUDED_BOTS] + builders = _GetBuilderList() platform_and_bots = {} for os_name in ['linux', 'android', 'mac', 'win']:
diff --git a/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder_unittest.py b/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder_unittest.py index faf2060..68674c7 100644 --- a/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder_unittest.py +++ b/tools/telemetry/telemetry/internal/backends/remote/trybot_browser_finder_unittest.py
@@ -2,7 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import json import logging import StringIO import unittest @@ -20,15 +19,15 @@ self.stream_handler = logging.StreamHandler(self.log_output) logging.getLogger().addHandler(self.stream_handler) self._real_subprocess = trybot_browser_finder.subprocess - self._real_urllib2 = trybot_browser_finder.urllib2 self._stubs = system_stub.Override(trybot_browser_finder, ['sys', 'open', 'os']) + self._builder_list = "" def tearDown(self): logging.getLogger().removeHandler(self.stream_handler) self.log_output.close() trybot_browser_finder.subprocess = self._real_subprocess - trybot_browser_finder.urllib2 = self._real_urllib2 + self._builder_list = "" self._stubs.Restore() def _ExpectProcesses(self, args): @@ -42,11 +41,14 @@ 'Popen').WithArgs(arg[0]).WillReturn(mock_popen) trybot_browser_finder.subprocess = mock_subprocess + def _MockBuilderList(self): + ExcludedBots = trybot_browser_finder.EXCLUDED_BOTS + builders = [bot for bot in self._builder_list if bot not in ExcludedBots] + return builders + def _MockTryserverJson(self, bots_dict): - trybot_browser_finder.urllib2 = simple_mock.MockObject() - trybot_browser_finder.urllib2.ExpectCall('urlopen').WithArgs( - 'http://build.chromium.org/p/tryserver.chromium.perf/json').WillReturn( - StringIO.StringIO(json.dumps({'builders': bots_dict}))) + trybot_browser_finder._GetBuilderList = self._MockBuilderList + self._builder_list = bots_dict def test_find_all_browser_types_list(self): finder_options = browser_options.BrowserFinderOptions(browser_type='list')
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc index 5a1c7adf..cb09ad3 100644 --- a/ui/compositor/compositor.cc +++ b/ui/compositor/compositor.cc
@@ -139,8 +139,8 @@ settings.use_property_trees = false; settings.use_zero_copy = IsUIZeroCopyEnabled(); - if (command_line->HasSwitch(switches::kUIEnableRGBA4444Textures)) - settings.renderer_settings.preferred_tile_format = cc::RGBA_4444; + settings.renderer_settings.use_rgba_4444_textures = + command_line->HasSwitch(switches::kUIEnableRGBA4444Textures); // UI compositor always uses partial raster if not using zero-copy. Zero copy // doesn't currently support partial raster.
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn index 4f474f6b..6c6c181 100644 --- a/ui/display/BUILD.gn +++ b/ui/display/BUILD.gn
@@ -15,6 +15,8 @@ "chromeos/display_configurator.cc", "chromeos/display_configurator.h", "chromeos/display_layout_manager.h", + "chromeos/display_snapshot_virtual.cc", + "chromeos/display_snapshot_virtual.h", "chromeos/display_util.cc", "chromeos/display_util.h", "chromeos/ozone/display_configurator_ozone.cc", @@ -122,6 +124,7 @@ "//base", "//ui/display/types", "//ui/gfx", + "//ui/gfx:test_support", "//ui/gfx/geometry", ] }
diff --git a/ui/display/DEPS b/ui/display/DEPS index 51399a6..7d1062ce 100644 --- a/ui/display/DEPS +++ b/ui/display/DEPS
@@ -1,4 +1,5 @@ include_rules = [ "+third_party/cros_system_api", + "+ui/gfx/display.h", "+ui/gfx/geometry", ]
diff --git a/ui/display/chromeos/apply_content_protection_task.cc b/ui/display/chromeos/apply_content_protection_task.cc index e25d427..43214709 100644 --- a/ui/display/chromeos/apply_content_protection_task.cc +++ b/ui/display/chromeos/apply_content_protection_task.cc
@@ -28,6 +28,7 @@ case DISPLAY_CONNECTION_TYPE_INTERNAL: case DISPLAY_CONNECTION_TYPE_VGA: case DISPLAY_CONNECTION_TYPE_NETWORK: + case DISPLAY_CONNECTION_TYPE_VIRTUAL: // No protections for these types. Do nothing. break; case DISPLAY_CONNECTION_TYPE_NONE:
diff --git a/ui/display/chromeos/configure_displays_task.cc b/ui/display/chromeos/configure_displays_task.cc index 17f1a12e..0596c57 100644 --- a/ui/display/chromeos/configure_displays_task.cc +++ b/ui/display/chromeos/configure_displays_task.cc
@@ -6,6 +6,7 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "ui/display/chromeos/display_util.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/native_display_delegate.h" @@ -74,9 +75,15 @@ size_t index = pending_request_indexes_.front(); DisplayConfigureRequest* request = &requests_[index]; pending_request_indexes_.pop(); - delegate_->Configure(*request->display, request->mode, request->origin, - base::Bind(&ConfigureDisplaysTask::OnConfigured, - weak_ptr_factory_.GetWeakPtr(), index)); + // Non-native displays do not require configuration through the + // NativeDisplayDelegate. + if (!IsPhysicalDisplayType(request->display->type())) { + OnConfigured(index, true); + } else { + delegate_->Configure(*request->display, request->mode, request->origin, + base::Bind(&ConfigureDisplaysTask::OnConfigured, + weak_ptr_factory_.GetWeakPtr(), index)); + } } }
diff --git a/ui/display/chromeos/display_configurator.cc b/ui/display/chromeos/display_configurator.cc index 68c026e..dc2db593 100644 --- a/ui/display/chromeos/display_configurator.cc +++ b/ui/display/chromeos/display_configurator.cc
@@ -11,12 +11,15 @@ #include "base/time/time.h" #include "ui/display/chromeos/apply_content_protection_task.h" #include "ui/display/chromeos/display_layout_manager.h" +#include "ui/display/chromeos/display_snapshot_virtual.h" #include "ui/display/chromeos/display_util.h" #include "ui/display/chromeos/update_display_configuration_task.h" #include "ui/display/display_switches.h" #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/native_display_delegate.h" +#include "ui/display/util/display_util.h" +#include "ui/gfx/display.h" namespace ui { @@ -34,6 +37,9 @@ // such that we read an up to date state. const int kResumeDelayMs = 500; +// The EDID specification marks the top bit of the manufacturer id as reserved. +const int16_t kReservedManufacturerID = 1 << 15; + struct DisplayState { DisplaySnapshot* display = nullptr; // Not owned. @@ -661,6 +667,16 @@ ContentProtectionClientId client_id, int64_t display_id, const QueryProtectionCallback& callback) { + // Exclude virtual displays so that protected content will not be recaptured + // through the cast stream. + for (const DisplaySnapshot* display : cached_displays_) { + if (display->display_id() == display_id && + !IsPhysicalDisplayType(display->type())) { + callback.Run(QueryProtectionResponse()); + return; + } + } + if (!configure_display_ || display_externally_controlled_) { callback.Run(QueryProtectionResponse()); return; @@ -779,7 +795,8 @@ if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableDisplayColorCalibration)) { for (const DisplaySnapshot* display : cached_displays_) { - if (display->display_id() == display_id) { + if (display->display_id() == display_id && + IsPhysicalDisplayType(display->type())) { return native_display_delegate_->GetAvailableColorCalibrationProfiles( *display); } @@ -793,7 +810,8 @@ int64_t display_id, ui::ColorCalibrationProfile new_profile) { for (const DisplaySnapshot* display : cached_displays_) { - if (display->display_id() == display_id) { + if (display->display_id() == display_id && + IsPhysicalDisplayType(display->type())) { return native_display_delegate_->SetColorCalibrationProfile(*display, new_profile); } @@ -964,6 +982,8 @@ requested_display_state_, requested_power_state_, requested_power_flags_, 0, force_configure_, base::Bind(&DisplayConfigurator::OnConfigured, weak_ptr_factory_.GetWeakPtr()))); + configuration_task_->set_virtual_display_snapshots( + virtual_display_snapshots_.get()); // Reset the flags before running the task; otherwise it may end up scheduling // another configuration. @@ -1079,4 +1099,43 @@ } } +int64_t DisplayConfigurator::AddVirtualDisplay(gfx::Size display_size) { + if (last_virtual_display_id_ == 0xff) { + LOG(WARNING) << "Exceeded virtual display id limit"; + return gfx::Display::kInvalidDisplayID; + } + + DisplaySnapshotVirtual* virtual_snapshot = + new DisplaySnapshotVirtual(GenerateDisplayID(kReservedManufacturerID, 0x0, + ++last_virtual_display_id_), + display_size); + virtual_display_snapshots_.push_back(virtual_snapshot); + ConfigureDisplays(); + + return virtual_snapshot->display_id(); +} + +bool DisplayConfigurator::RemoveVirtualDisplay(int64_t display_id) { + bool display_found = false; + for (auto it = virtual_display_snapshots_.begin(); + it != virtual_display_snapshots_.end(); ++it) { + if ((*it)->display_id() == display_id) { + virtual_display_snapshots_.erase(it); + ConfigureDisplays(); + display_found = true; + break; + } + } + + if (!display_found) + return false; + + int64_t max_display_id = 0; + for (const auto& display : virtual_display_snapshots_) + max_display_id = std::max(max_display_id, display->display_id()); + last_virtual_display_id_ = max_display_id & 0xff; + + return true; +} + } // namespace ui
diff --git a/ui/display/chromeos/display_configurator.h b/ui/display/chromeos/display_configurator.h index 5a2b2d4..6fc1773 100644 --- a/ui/display/chromeos/display_configurator.h +++ b/ui/display/chromeos/display_configurator.h
@@ -14,14 +14,17 @@ #include "base/event_types.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/timer/timer.h" #include "third_party/cros_system_api/dbus/service_constants.h" +#include "ui/display/chromeos/display_snapshot_virtual.h" #include "ui/display/chromeos/query_content_protection_task.h" #include "ui/display/display_export.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/native_display_observer.h" +#include "ui/display/util/display_util.h" #include "ui/gfx/geometry/size.h" namespace gfx { @@ -269,6 +272,10 @@ bool SetGammaRamp(int64_t display_id, const std::vector<GammaRampRGBEntry>& lut); + // Enables/disables virtual display. + int64_t AddVirtualDisplay(gfx::Size display_size); + bool RemoveVirtualDisplay(int64_t display_id); + private: class DisplayLayoutManagerImpl; @@ -421,6 +428,12 @@ // Whether the displays are currently suspended. bool displays_suspended_; + // Virtual display control. + ScopedVector<DisplaySnapshot> virtual_display_snapshots_; + + // Last used virtual display id. + uint8_t last_virtual_display_id_ = 0; + scoped_ptr<DisplayLayoutManager> layout_manager_; scoped_ptr<UpdateDisplayConfigurationTask> configuration_task_;
diff --git a/ui/display/chromeos/display_configurator_unittest.cc b/ui/display/chromeos/display_configurator_unittest.cc index bd5f7e9..9ede75770 100644 --- a/ui/display/chromeos/display_configurator_unittest.cc +++ b/ui/display/chromeos/display_configurator_unittest.cc
@@ -10,6 +10,7 @@ #include "ui/display/chromeos/test/action_logger_util.h" #include "ui/display/chromeos/test/test_display_snapshot.h" #include "ui/display/chromeos/test/test_native_display_delegate.h" +#include "ui/display/util/display_util.h" namespace ui { namespace test { @@ -337,6 +338,160 @@ output, gfx::Size(1440, 900))); } +TEST_F(DisplayConfiguratorTest, EnableVirtualDisplay) { + InitWithSingleOutput(); + + observer_.Reset(); + const DisplayConfigurator::DisplayStateList& cached = + configurator_.cached_displays(); + ASSERT_EQ(static_cast<size_t>(1u), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + + // Add virtual display. + int64_t virtual_display_id = + configurator_.AddVirtualDisplay(big_mode_.size()); + EXPECT_EQ(GenerateDisplayID(0x8000, 0x0, 1), virtual_display_id); + EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); + EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, + configurator_.display_state()); + + // Virtual should not trigger addition of added crtc but does change FB + // height. + const int kVirtualHeight = small_mode_.size().height() + + DisplayConfigurator::kVerticalGap + + big_mode_.size().height(); + EXPECT_EQ( + JoinActions( + kGrab, GetFramebufferAction( + gfx::Size(big_mode_.size().width(), kVirtualHeight), + &outputs_[0], nullptr) + .c_str(), + GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), + kUngrab, nullptr), + log_->GetActionsAndClear()); + EXPECT_EQ(1, observer_.num_changes()); + ASSERT_EQ(static_cast<size_t>(2u), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + EXPECT_EQ(big_mode_.size(), cached[1]->current_mode()->size()); + EXPECT_EQ(virtual_display_id, cached[1]->display_id()); + + // Remove virtual display. + observer_.Reset(); + EXPECT_TRUE(configurator_.RemoveVirtualDisplay(virtual_display_id)); + EXPECT_EQ( + JoinActions( + kGrab, GetFramebufferAction(small_mode_.size(), &outputs_[0], nullptr) + .c_str(), + GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), + kUngrab, nullptr), + log_->GetActionsAndClear()); + EXPECT_EQ(1, observer_.num_changes()); + ASSERT_EQ(static_cast<size_t>(1u), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); +} + +TEST_F(DisplayConfiguratorTest, EnableTwoVirtualDisplays) { + InitWithSingleOutput(); + + observer_.Reset(); + const DisplayConfigurator::DisplayStateList& cached = + configurator_.cached_displays(); + ASSERT_EQ(static_cast<size_t>(1u), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + + // Add 1st virtual display. + int64_t virtual_display_id_1 = + configurator_.AddVirtualDisplay(big_mode_.size()); + EXPECT_EQ(GenerateDisplayID(0x8000, 0x0, 1), virtual_display_id_1); + EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); + EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, + configurator_.display_state()); + + // Virtual should not trigger addition of added crtc but does change FB + // height. + const int kSingleVirtualHeight = small_mode_.size().height() + + DisplayConfigurator::kVerticalGap + + big_mode_.size().height(); + EXPECT_EQ( + JoinActions( + kGrab, GetFramebufferAction( + gfx::Size(big_mode_.size().width(), kSingleVirtualHeight), + &outputs_[0], nullptr) + .c_str(), + GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), + kUngrab, nullptr), + log_->GetActionsAndClear()); + EXPECT_EQ(1, observer_.num_changes()); + ASSERT_EQ(static_cast<size_t>(2), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + EXPECT_EQ(big_mode_.size(), cached[1]->current_mode()->size()); + EXPECT_EQ(virtual_display_id_1, cached[1]->display_id()); + + // Add 2nd virtual display + int64_t virtual_display_id_2 = + configurator_.AddVirtualDisplay(big_mode_.size()); + EXPECT_EQ(GenerateDisplayID(0x8000, 0x0, 2), virtual_display_id_2); + EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); + EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, + configurator_.display_state()); + + const int kDualVirtualHeight = + small_mode_.size().height() + + (DisplayConfigurator::kVerticalGap + big_mode_.size().height()) * 2; + EXPECT_EQ( + JoinActions( + kGrab, GetFramebufferAction( + gfx::Size(big_mode_.size().width(), kDualVirtualHeight), + &outputs_[0], nullptr) + .c_str(), + GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), + kUngrab, nullptr), + log_->GetActionsAndClear()); + EXPECT_EQ(2, observer_.num_changes()); + ASSERT_EQ(static_cast<size_t>(3u), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + EXPECT_EQ(big_mode_.size(), cached[1]->current_mode()->size()); + EXPECT_EQ(big_mode_.size(), cached[2]->current_mode()->size()); + EXPECT_EQ(virtual_display_id_1, cached[1]->display_id()); + EXPECT_EQ(virtual_display_id_2, cached[2]->display_id()); + + // Remove 1st virtual display. + observer_.Reset(); + EXPECT_TRUE(configurator_.RemoveVirtualDisplay(virtual_display_id_1)); + EXPECT_EQ( + JoinActions( + kGrab, GetFramebufferAction( + gfx::Size(big_mode_.size().width(), kSingleVirtualHeight), + &outputs_[0], nullptr) + .c_str(), + GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), + kUngrab, nullptr), + log_->GetActionsAndClear()); + EXPECT_EQ(1, observer_.num_changes()); + ASSERT_EQ(static_cast<size_t>(2u), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + EXPECT_EQ(big_mode_.size(), cached[1]->current_mode()->size()); + EXPECT_EQ(virtual_display_id_2, cached[1]->display_id()); + EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, + configurator_.display_state()); + + // Remove 2nd virtual display. + observer_.Reset(); + EXPECT_TRUE(configurator_.RemoveVirtualDisplay(virtual_display_id_2)); + EXPECT_EQ( + JoinActions( + kGrab, GetFramebufferAction(small_mode_.size(), &outputs_[0], nullptr) + .c_str(), + GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), + kUngrab, nullptr), + log_->GetActionsAndClear()); + EXPECT_EQ(1, observer_.num_changes()); + ASSERT_EQ(static_cast<size_t>(1), cached.size()); + EXPECT_EQ(small_mode_.size(), cached[0]->current_mode()->size()); + EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); +} + TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { InitWithSingleOutput(); @@ -905,19 +1060,18 @@ TEST_F(DisplayConfiguratorTest, UpdateCachedOutputsEvenAfterFailure) { InitWithSingleOutput(); - const DisplayConfigurator::DisplayStateList* cached = - &configurator_.cached_displays(); - ASSERT_EQ(static_cast<size_t>(1), cached->size()); - EXPECT_EQ(outputs_[0].current_mode(), (*cached)[0]->current_mode()); + const DisplayConfigurator::DisplayStateList& cached = + configurator_.cached_displays(); + ASSERT_EQ(static_cast<size_t>(1), cached.size()); + EXPECT_EQ(outputs_[0].current_mode(), cached[0]->current_mode()); // After connecting a second output, check that it shows up in // |cached_displays_| even if an invalid state is requested. state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); UpdateOutputs(2, true); - cached = &configurator_.cached_displays(); - ASSERT_EQ(static_cast<size_t>(2), cached->size()); - EXPECT_EQ(outputs_[0].current_mode(), (*cached)[0]->current_mode()); - EXPECT_EQ(outputs_[1].current_mode(), (*cached)[1]->current_mode()); + ASSERT_EQ(static_cast<size_t>(2), cached.size()); + EXPECT_EQ(outputs_[0].current_mode(), cached[0]->current_mode()); + EXPECT_EQ(outputs_[1].current_mode(), cached[1]->current_mode()); } TEST_F(DisplayConfiguratorTest, PanelFitting) {
diff --git a/ui/display/chromeos/display_snapshot_virtual.cc b/ui/display/chromeos/display_snapshot_virtual.cc new file mode 100644 index 0000000..39c6f950b --- /dev/null +++ b/ui/display/chromeos/display_snapshot_virtual.cc
@@ -0,0 +1,50 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/display/chromeos/display_snapshot_virtual.h" + +#include <inttypes.h> + +#include "base/strings/stringprintf.h" +#include "ui/display/types/display_mode.h" + +namespace ui { + +namespace { + +// For a non hi-DPI display (96 dpi) use a pitch of 265µm. +static const float kVirtualDisplayPitchMM = 0.265; + +} // namespace + +DisplaySnapshotVirtual::DisplaySnapshotVirtual(int64_t display_id, + const gfx::Size& display_size) + : DisplaySnapshot(display_id, + gfx::Point(0, 0), + // Calculate physical size assuming 96dpi display. + gfx::Size(display_size.width() * kVirtualDisplayPitchMM, + display_size.height() * kVirtualDisplayPitchMM), + DISPLAY_CONNECTION_TYPE_VIRTUAL, + false, + false, + "Virtual display", + std::vector<const DisplayMode*>(), + nullptr, + nullptr) { + mode_.reset(new DisplayMode(display_size, false, 30)); + modes_.push_back(mode_.get()); + + native_mode_ = modes_.front(); +} + +DisplaySnapshotVirtual::~DisplaySnapshotVirtual() {} + +std::string DisplaySnapshotVirtual::ToString() const { + return base::StringPrintf( + "Virtual id=%" PRId64 " current_mode=%s physical_size=%s", display_id_, + current_mode_ ? current_mode_->ToString().c_str() : "nullptr", + physical_size_.ToString().c_str()); +} + +} // namespace ui
diff --git a/ui/display/chromeos/display_snapshot_virtual.h b/ui/display/chromeos/display_snapshot_virtual.h new file mode 100644 index 0000000..8ecd51e --- /dev/null +++ b/ui/display/chromeos/display_snapshot_virtual.h
@@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_DISPLAY_CHROMEOS_DISPLAY_SNAPSHOT_VIRTUAL_H_ +#define UI_DISPLAY_CHROMEOS_DISPLAY_SNAPSHOT_VIRTUAL_H_ + +#include "ui/display/types/display_snapshot.h" + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/display/types/display_mode.h" + +namespace ui { + +// This class represents a virtual display to be enabled on demand. The display +// is constructed for the desired pixel resolution. +class DisplaySnapshotVirtual : public DisplaySnapshot { + public: + // |display_id| is the numerical identifier for the virtual display, + // |display_size| is the pixel dimensions for the display. + DisplaySnapshotVirtual(int64_t display_id, const gfx::Size& display_size); + ~DisplaySnapshotVirtual() override; + + // DisplaySnapshot overrides. + std::string ToString() const override; + + private: + scoped_ptr<DisplayMode> mode_; + DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotVirtual); +}; + +} // namespace ui + +#endif // UI_DISPLAY_CHROMEOS_DISPLAY_SNAPSHOT_VIRTUAL_H_
diff --git a/ui/display/chromeos/display_util.cc b/ui/display/chromeos/display_util.cc index a62e47c..93bfee0 100644 --- a/ui/display/chromeos/display_util.cc +++ b/ui/display/chromeos/display_util.cc
@@ -55,6 +55,7 @@ for (size_t i = 0; i < displays.size(); ++i) { bool internal = displays[i]->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; bool on = + displays[i]->type() == DISPLAY_CONNECTION_TYPE_VIRTUAL || state == chromeos::DISPLAY_POWER_ALL_ON || (state == chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON && !internal) || @@ -67,4 +68,9 @@ return num_on_displays; } +bool IsPhysicalDisplayType(ui::DisplayConnectionType type) { + return !(type & + (DISPLAY_CONNECTION_TYPE_NETWORK | DISPLAY_CONNECTION_TYPE_VIRTUAL)); +} + } // namespace ui
diff --git a/ui/display/chromeos/display_util.h b/ui/display/chromeos/display_util.h index cfe6f6c..dc09698 100644 --- a/ui/display/chromeos/display_util.h +++ b/ui/display/chromeos/display_util.h
@@ -30,6 +30,11 @@ chromeos::DisplayPowerState state, std::vector<bool>* display_power); +// Returns whether the DisplayConnectionType |type| is a physically connected +// display. Currently DISPLAY_CONNECTION_TYPE_VIRTUAL and +// DISPLAY_CONNECTION_TYPE_NETWORK return false. All other types return true. +bool IsPhysicalDisplayType(ui::DisplayConnectionType type); + } // namespace ui #endif // UI_DISPLAY_CHROMEOS_DISPLAY_UTIL_H_
diff --git a/ui/display/chromeos/query_content_protection_task.cc b/ui/display/chromeos/query_content_protection_task.cc index d90d55f..c35e5c1 100644 --- a/ui/display/chromeos/query_content_protection_task.cc +++ b/ui/display/chromeos/query_content_protection_task.cc
@@ -47,6 +47,7 @@ case DISPLAY_CONNECTION_TYPE_INTERNAL: case DISPLAY_CONNECTION_TYPE_VGA: case DISPLAY_CONNECTION_TYPE_NETWORK: + case DISPLAY_CONNECTION_TYPE_VIRTUAL: // No protections for these types. Do nothing. break; case DISPLAY_CONNECTION_TYPE_NONE:
diff --git a/ui/display/chromeos/update_display_configuration_task.cc b/ui/display/chromeos/update_display_configuration_task.cc index 335c661..6b41271 100644 --- a/ui/display/chromeos/update_display_configuration_task.cc +++ b/ui/display/chromeos/update_display_configuration_task.cc
@@ -48,6 +48,11 @@ const std::vector<DisplaySnapshot*>& displays) { cached_displays_ = displays; + // Add virtual displays after retrieving the physical displays from the NDD. + cached_displays_.insert(cached_displays_.end(), + virtual_display_snapshots_.begin(), + virtual_display_snapshots_.end()); + if (cached_displays_.size() > 1 && background_color_argb_) delegate_->SetBackgroundColor(background_color_argb_);
diff --git a/ui/display/chromeos/update_display_configuration_task.h b/ui/display/chromeos/update_display_configuration_task.h index 7b8272e..c3c9ba2 100644 --- a/ui/display/chromeos/update_display_configuration_task.h +++ b/ui/display/chromeos/update_display_configuration_task.h
@@ -35,6 +35,12 @@ const ResponseCallback& callback); ~UpdateDisplayConfigurationTask(); + // The pointers to the DisplaySnapshots in this vector are owned by + // DisplayConfigurator. + void set_virtual_display_snapshots(std::vector<DisplaySnapshot*> snapshots) { + virtual_display_snapshots_ = snapshots; + } + void Run(); private: @@ -92,6 +98,9 @@ // List of updated displays. std::vector<DisplaySnapshot*> cached_displays_; + // Vector of unowned VirtualDisplaySnapshots to be added when doing the task. + std::vector<DisplaySnapshot*> virtual_display_snapshots_; + gfx::Size framebuffer_size_; scoped_ptr<ConfigureDisplaysTask> configure_task_;
diff --git a/ui/display/display.gyp b/ui/display/display.gyp index c329f752..fe32ea3b 100644 --- a/ui/display/display.gyp +++ b/ui/display/display.gyp
@@ -53,6 +53,8 @@ 'chromeos/display_configurator.cc', 'chromeos/display_configurator.h', 'chromeos/display_layout_manager.h', + 'chromeos/display_snapshot_virtual.cc', + 'chromeos/display_snapshot_virtual.h', 'chromeos/display_util.cc', 'chromeos/display_util.h', 'chromeos/ozone/display_configurator_ozone.cc', @@ -193,6 +195,7 @@ '../../base/base.gyp:run_all_unittests', '../../testing/gtest.gyp:gtest', '../../ui/gfx/gfx.gyp:gfx_geometry', + '../../ui/gfx/gfx.gyp:gfx_test_support', 'display_util', ], 'include_dirs': [
diff --git a/ui/display/types/display_constants.h b/ui/display/types/display_constants.h index aedb04b..b24bab7 100644 --- a/ui/display/types/display_constants.h +++ b/ui/display/types/display_constants.h
@@ -28,9 +28,10 @@ DISPLAY_CONNECTION_TYPE_DVI = 1 << 4, DISPLAY_CONNECTION_TYPE_DISPLAYPORT = 1 << 5, DISPLAY_CONNECTION_TYPE_NETWORK = 1 << 6, + DISPLAY_CONNECTION_TYPE_VIRTUAL = 1 << 7, // Update this when adding a new type. - DISPLAY_CONNECTION_TYPE_LAST = DISPLAY_CONNECTION_TYPE_NETWORK + DISPLAY_CONNECTION_TYPE_LAST = DISPLAY_CONNECTION_TYPE_VIRTUAL }; // Content protection methods applied on video output.
diff --git a/ui/display/util/display_util.cc b/ui/display/util/display_util.cc index 5d7bbb7..26d7865a 100644 --- a/ui/display/util/display_util.cc +++ b/ui/display/util/display_util.cc
@@ -68,4 +68,11 @@ return (dpi > threshold) ? 2.0f : 1.0f; } +int64_t GenerateDisplayID(uint16_t manufacturer_id, + uint32_t product_code_hash, + uint8_t output_index) { + return ((static_cast<int64_t>(manufacturer_id) << 40) | + (static_cast<int64_t>(product_code_hash) << 8) | output_index); +} + } // namespace ui
diff --git a/ui/display/util/display_util.h b/ui/display/util/display_util.h index 012269d..1eb4750c 100644 --- a/ui/display/util/display_util.h +++ b/ui/display/util/display_util.h
@@ -21,6 +21,17 @@ const gfx::Size& physical_size_in_mm, const gfx::Size& screen_size_in_pixels); +// Returns 64-bit persistent ID for the specified manufacturer's ID and +// product_code_hash, and the index of the output it is connected to. +// |output_index| is used to distinguish the displays of the same type. For +// example, swapping two identical display between two outputs will not be +// treated as swap. The 'serial number' field in EDID isn't used here because +// it is not guaranteed to have unique number and it may have the same fixed +// value (like 0). +DISPLAY_UTIL_EXPORT int64_t GenerateDisplayID(uint16_t manufacturer_id, + uint32_t product_code_hash, + uint8_t output_index); + } // namespace ui #endif // UI_DISPLAY_UTIL_DISPLAY_UTIL_H_
diff --git a/ui/display/util/edid_parser.cc b/ui/display/util/edid_parser.cc index 097cc1e..37031c86 100644 --- a/ui/display/util/edid_parser.cc +++ b/ui/display/util/edid_parser.cc
@@ -9,26 +9,13 @@ #include "base/hash.h" #include "base/strings/string_util.h" #include "base/sys_byteorder.h" +#include "ui/display/util/display_util.h" #include "ui/gfx/geometry/size.h" namespace ui { namespace { -// Returns 64-bit persistent ID for the specified manufacturer's ID and -// product_code_hash, and the index of the output it is connected to. -// |output_index| is used to distinguish the displays of the same type. For -// example, swapping two identical display between two outputs will not be -// treated as swap. The 'serial number' field in EDID isn't used here because -// it is not guaranteed to have unique number and it may have the same fixed -// value (like 0). -int64_t GetID(uint16_t manufacturer_id, - uint32_t product_code_hash, - uint8_t output_index) { - return ((static_cast<int64_t>(manufacturer_id) << 40) | - (static_cast<int64_t>(product_code_hash) << 8) | output_index); -} - // Returns a 32-bit identifier for this model of display, using // |manufacturer_id| and |product_code|. uint32_t GetProductID(uint16_t manufacturer_id, uint16_t product_code) { @@ -58,8 +45,8 @@ product_name.empty() ? 0 : base::Hash(product_name); // An ID based on display's index will be assigned later if this call // fails. - *display_id_out = GetID( - manufacturer_id, product_code_hash, output_index); + *display_id_out = + GenerateDisplayID(manufacturer_id, product_code_hash, output_index); // product_id is 64-bit signed so it can store -1 as kInvalidProductID and // not match a valid product id which will all be in the lowest 32-bits. if (product_id_out)
diff --git a/ui/file_manager/video_player/js/video_player.js b/ui/file_manager/video_player/js/video_player.js index 6bc21007..a97ed24e 100644 --- a/ui/file_manager/video_player/js/video_player.js +++ b/ui/file_manager/video_player/js/video_player.js
@@ -494,8 +494,8 @@ var oldLeft = window.screenX; var oldTop = window.screenY; - var oldWidth = window.outerWidth; - var oldHeight = window.outerHeight; + var oldWidth = window.innerWidth; + var oldHeight = window.innerHeight; if (!oldWidth && !oldHeight) { oldLeft = window.screen.availWidth / 2; @@ -505,8 +505,10 @@ var appWindow = chrome.app.window.current(); appWindow.innerBounds.width = Math.round(newWidth); appWindow.innerBounds.height = Math.round(newHeight); - appWindow.outerBounds.left = Math.round(oldLeft - (newWidth - oldWidth) / 2); - appWindow.outerBounds.top = Math.round(oldTop - (newHeight - oldHeight) / 2); + appWindow.outerBounds.left = Math.max( + 0, Math.round(oldLeft - (newWidth - oldWidth) / 2)); + appWindow.outerBounds.top = Math.max( + 0, Math.round(oldTop - (newHeight - oldHeight) / 2)); appWindow.show(); this.videoElement_.play();
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 21015b5..b411f18 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -72,7 +72,6 @@ if (is_chromeos) { sources -= [ - "controls/menu/menu_config_linux.cc", "linux_ui/status_icon_linux.cc", "linux_ui/status_icon_linux.h", ]
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm index 82c40dd1..c30e7c7 100644 --- a/ui/views/cocoa/bridged_content_view.mm +++ b/ui/views/cocoa/bridged_content_view.mm
@@ -312,7 +312,7 @@ return; if (drawMenuBackgroundForBlur_) { - const CGFloat radius = views::MenuConfig::instance().corner_radius; + const CGFloat radius = views::MenuConfig::instance(nullptr).corner_radius; [gfx::SkColorToSRGBNSColor(0x01000000) set]; [[NSBezierPath bezierPathWithRoundedRect:[self bounds] xRadius:radius
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc index 2074b0e..51b17e53 100644 --- a/ui/views/controls/combobox/combobox.cc +++ b/ui/views/controls/combobox/combobox.cc
@@ -233,7 +233,8 @@ private: bool UseCheckmarks() const { return owner_->style_ != STYLE_ACTION && - MenuConfig::instance().check_selected_combobox_item; + MenuConfig::instance(owner_->GetNativeTheme()) + .check_selected_combobox_item; } // Overridden from MenuModel:
diff --git a/ui/views/controls/menu/menu_config.cc b/ui/views/controls/menu/menu_config.cc index 52c39e4d..0d726c54 100644 --- a/ui/views/controls/menu/menu_config.cc +++ b/ui/views/controls/menu/menu_config.cc
@@ -4,11 +4,11 @@ #include "ui/views/controls/menu/menu_config.h" -#include "base/macros.h" +#include "build/build_config.h" namespace views { -MenuConfig::MenuConfig() +MenuConfig::MenuConfig(const ui::NativeTheme* theme) : arrow_color(SK_ColorBLACK), menu_vertical_border_size(3), menu_horizontal_border_size(0), @@ -42,15 +42,9 @@ check_selected_combobox_item(false), show_delay(400), corner_radius(0) { - Init(); + Init(theme); } MenuConfig::~MenuConfig() {} -// static -const MenuConfig& MenuConfig::instance() { - CR_DEFINE_STATIC_LOCAL(MenuConfig, instance, ()); - return instance; -} - } // namespace views
diff --git a/ui/views/controls/menu/menu_config.h b/ui/views/controls/menu/menu_config.h index 6bbf4e8..2a43aca 100644 --- a/ui/views/controls/menu/menu_config.h +++ b/ui/views/controls/menu/menu_config.h
@@ -9,15 +9,19 @@ #include "ui/gfx/font_list.h" #include "ui/views/views_export.h" +namespace ui { +class NativeTheme; +} + namespace views { // Layout type information for menu items. Use the instance() method to obtain // the MenuConfig for the current platform. struct VIEWS_EXPORT MenuConfig { - MenuConfig(); + explicit MenuConfig(const ui::NativeTheme* theme); ~MenuConfig(); - static const MenuConfig& instance(); + static const MenuConfig& instance(const ui::NativeTheme* theme); // Font list used by menus. gfx::FontList font_list; @@ -121,7 +125,10 @@ private: // Configures a MenuConfig as appropriate for the current platform. - void Init(); + void Init(const ui::NativeTheme* theme); + + // TODO: temporary until we standardize. + void InitAura(const ui::NativeTheme* theme); }; } // namespace views
diff --git a/ui/views/controls/menu/menu_config_aura.cc b/ui/views/controls/menu/menu_config_aura.cc new file mode 100644 index 0000000..a49d447 --- /dev/null +++ b/ui/views/controls/menu/menu_config_aura.cc
@@ -0,0 +1,64 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/controls/menu/menu_config.h" + +#include "ui/base/layout.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/native_theme/native_theme_aura.h" +#include "ui/resources/grit/ui_resources.h" +#include "ui/views/controls/menu/menu_image_util.h" +#include "ui/views/resources/grit/views_resources.h" + +namespace views { + +namespace { +#if defined(OS_CHROMEOS) +static const int kMenuCornerRadiusForAura = 2; +#else +static const int kMenuCornerRadiusForAura = 0; +#endif +} // namespace + +#if !defined(OS_WIN) +void MenuConfig::Init(const ui::NativeTheme* theme) { + if (theme == ui::NativeThemeAura::instance()) + InitAura(theme); +} +#endif + +void MenuConfig::InitAura(const ui::NativeTheme* theme) { + submenu_horizontal_inset = 1; + arrow_to_edge_padding = 20; + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + arrow_width = rb.GetImageNamed(IDR_MENU_HIERARCHY_ARROW).Width(); + gfx::ImageSkia check = GetMenuCheckImage(false); + check_height = check.height(); + item_min_height = 29; + separator_spacing_height = 7; + separator_lower_height = 8; + separator_upper_height = 8; + label_to_arrow_padding = 20; + label_to_minor_text_padding = 20; + always_use_icon_to_label_padding = true; + align_arrow_and_shortcut = true; + offset_context_menus = true; + corner_radius = kMenuCornerRadiusForAura; + + // In Ash, the border is provided by the shadow. + use_outer_border = false; +} + +#if !defined(OS_WIN) +// static +const MenuConfig& MenuConfig::instance(const ui::NativeTheme* theme) { + CR_DEFINE_STATIC_LOCAL(MenuConfig, instance, + (theme ? theme : ui::NativeThemeAura::instance())); + return instance; +} +#endif + +} // namespace views
diff --git a/ui/views/controls/menu/menu_config_chromeos.cc b/ui/views/controls/menu/menu_config_chromeos.cc deleted file mode 100644 index 428b1fdd..0000000 --- a/ui/views/controls/menu/menu_config_chromeos.cc +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/menu/menu_config.h" - -#include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/image/image.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/resources/grit/ui_resources.h" -#include "ui/views/controls/menu/menu_image_util.h" - -namespace views { - -void MenuConfig::Init() { - submenu_horizontal_inset = 1; - arrow_to_edge_padding = 20; - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - arrow_width = rb.GetImageNamed(IDR_MENU_HIERARCHY_ARROW).Width(); - gfx::ImageSkia check = GetMenuCheckImage(false); - check_height = check.height(); - item_min_height = 29; - separator_spacing_height = 7; - separator_lower_height = 8; - separator_upper_height = 8; - label_to_arrow_padding = 20; - label_to_minor_text_padding = 20; - always_use_icon_to_label_padding = true; - align_arrow_and_shortcut = true; - offset_context_menus = true; - corner_radius = 2; - - // In Ash, the border is provided by the shadow. - use_outer_border = false; -} - -} // namespace views
diff --git a/ui/views/controls/menu/menu_config_linux.cc b/ui/views/controls/menu/menu_config_linux.cc deleted file mode 100644 index c76ac686..0000000 --- a/ui/views/controls/menu/menu_config_linux.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/menu/menu_config.h" - -namespace views { - -void MenuConfig::Init() { - // Desktop Linux uses the config provided by data member initializers. - // Note this causes --open-ash on Linux to use the Desktop Linux style as - // well. -} - -} // namespace views
diff --git a/ui/views/controls/menu/menu_config_mac.mm b/ui/views/controls/menu/menu_config_mac.mm index 9c73a1f2..78d6a2cc 100644 --- a/ui/views/controls/menu/menu_config_mac.mm +++ b/ui/views/controls/menu/menu_config_mac.mm
@@ -6,9 +6,14 @@ #import <AppKit/AppKit.h> +#include "ui/gfx/image/image_skia.h" +#include "ui/native_theme/native_theme_mac.h" +#include "ui/views/controls/menu/menu_image_util.h" + namespace views { -void MenuConfig::Init() { +void MenuConfig::Init(const ui::NativeTheme* theme) { + DCHECK_EQ(theme, ui::NativeThemeMac::instance()); font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0])); menu_vertical_border_size = 4; item_top_margin = item_no_icon_top_margin = 1; @@ -28,4 +33,10 @@ use_outer_border = false; } +// static +const MenuConfig& MenuConfig::instance(const ui::NativeTheme* theme) { + CR_DEFINE_STATIC_LOCAL(MenuConfig, mac_instance, (theme)); + return mac_instance; +} + } // namespace views
diff --git a/ui/views/controls/menu/menu_config_win.cc b/ui/views/controls/menu/menu_config_win.cc index 0b695674..3f17f582 100644 --- a/ui/views/controls/menu/menu_config_win.cc +++ b/ui/views/controls/menu/menu_config_win.cc
@@ -13,6 +13,7 @@ #include "base/win/win_util.h" #include "ui/base/l10n/l10n_util_win.h" #include "ui/gfx/color_utils.h" +#include "ui/native_theme/native_theme_aura.h" #include "ui/native_theme/native_theme_win.h" using ui::NativeTheme; @@ -20,7 +21,12 @@ namespace views { -void MenuConfig::Init() { +void MenuConfig::Init(const NativeTheme* theme) { + if (theme == ui::NativeThemeAura::instance()) { + InitAura(theme); + return; + } + arrow_color = color_utils::GetSysSkColor(COLOR_MENUTEXT); NONCLIENTMETRICS_XP metrics; @@ -72,4 +78,19 @@ separator_lower_height = 7; } +// static +const MenuConfig& MenuConfig::instance(const ui::NativeTheme* theme) { + // NOTE: |theme| may be NULL if used before the menu is running. + if (!theme || theme == NativeThemeWin::instance()) { + static MenuConfig* win_instance = NULL; + if (!win_instance) + win_instance = new MenuConfig(NativeThemeWin::instance()); + return *win_instance; + } + static MenuConfig* views_instance = NULL; + if (!views_instance) + views_instance = new MenuConfig(theme); + return *views_instance; +} + } // namespace views
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc index 43f167e..bc718ef 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc
@@ -1692,8 +1692,9 @@ } void MenuController::StartShowTimer() { + MenuItemView* item = pending_state_.item ? pending_state_.item : state_.item; show_timer_.Start( - FROM_HERE, TimeDelta::FromMilliseconds(MenuConfig::instance().show_delay), + FROM_HERE, TimeDelta::FromMilliseconds(item->GetMenuConfig().show_delay), this, &MenuController::CommitPendingSelection); } @@ -1736,7 +1737,7 @@ int x, y; - const MenuConfig& menu_config = MenuConfig::instance(); + const MenuConfig& menu_config = item->GetMenuConfig(); if (!item->GetParentMenuItem()) { // First item, position relative to initial location.
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc index 06e14e3..da2f127 100644 --- a/ui/views/controls/menu/menu_host.cc +++ b/ui/views/controls/menu/menu_host.cc
@@ -41,7 +41,7 @@ Widget::InitParams params(Widget::InitParams::TYPE_MENU); const MenuController* menu_controller = submenu_->GetMenuItem()->GetMenuController(); - const MenuConfig& menu_config = MenuConfig::instance(); + const MenuConfig& menu_config = submenu_->GetMenuItem()->GetMenuConfig(); bool rounded_border = menu_controller && menu_config.corner_radius > 0; bool bubble_border = submenu_->GetScrollViewContainer() && submenu_->GetScrollViewContainer()->HasBubbleBorder();
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc index a1a4b15..ff2737e 100644 --- a/ui/views/controls/menu/menu_item_view.cc +++ b/ui/views/controls/menu/menu_item_view.cc
@@ -240,7 +240,7 @@ CreateSubmenu(); DCHECK_GE(submenu_->child_count(), index); if (type == SEPARATOR) { - submenu_->AddChildViewAt(new MenuSeparator(separator_style), index); + submenu_->AddChildViewAt(new MenuSeparator(this, separator_style), index); return NULL; } MenuItemView* item = new MenuItemView(this, item_id, type); @@ -418,7 +418,7 @@ int height = child_at(0)->GetHeightForWidth(width); if (!icon_view_ && GetRootMenuItem()->has_icons()) - height = std::max(height, MenuConfig::instance().check_height); + height = std::max(height, GetMenuConfig().check_height); height += GetBottomMargin() + GetTopMargin(); return height; @@ -545,7 +545,7 @@ x -= width - kChildXPadding; } // Position |icon_view|. - const MenuConfig& config = MenuConfig::instance(); + const MenuConfig& config = GetMenuConfig(); if (icon_view_) { icon_view_->SizeToPreferredSize(); gfx::Size size = icon_view_->GetPreferredSize(); @@ -567,6 +567,10 @@ invalidate_dimensions(); } +const MenuConfig& MenuItemView::GetMenuConfig() const { + return MenuConfig::instance(GetNativeTheme()); +} + MenuItemView::MenuItemView(MenuItemView* parent, int command, MenuItemView::Type type) @@ -605,7 +609,7 @@ // // This is invoked prior to Running a menu. void MenuItemView::UpdateMenuPartSizes() { - const MenuConfig& config = MenuConfig::instance(); + const MenuConfig& config = GetMenuConfig(); item_right_margin_ = config.label_to_arrow_padding + config.arrow_width + config.arrow_to_edge_padding; @@ -683,8 +687,7 @@ flags |= gfx::Canvas::TEXT_ALIGN_LEFT; if (GetRootMenuItem()->has_mnemonics_) { - if (MenuConfig::instance().show_mnemonics || - GetRootMenuItem()->show_mnemonics_) { + if (GetMenuConfig().show_mnemonics || GetRootMenuItem()->show_mnemonics_) { flags |= gfx::Canvas::SHOW_PREFIX; } else { flags |= gfx::Canvas::HIDE_PREFIX; @@ -700,7 +703,7 @@ if (font_list) return *font_list; } - return MenuConfig::instance().font_list; + return GetMenuConfig().font_list; } void MenuItemView::AddEmptyMenus() { @@ -740,7 +743,7 @@ } void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { - const MenuConfig& config = MenuConfig::instance(); + const MenuConfig& config = GetMenuConfig(); bool render_selection = (mode == PB_NORMAL && IsSelected() && parent_menu_item_->GetSubmenu()->GetShowSelection(this) && @@ -866,7 +869,7 @@ int available_height = height() - GetTopMargin() - GetBottomMargin(); int max_accel_width = parent_menu_item_->GetSubmenu()->max_minor_text_width(); - const MenuConfig& config = MenuConfig::instance(); + const MenuConfig& config = GetMenuConfig(); int accel_right_margin = config.align_arrow_and_shortcut ? config.arrow_to_edge_padding : item_right_margin_; gfx::Rect accel_bounds(width() - accel_right_margin - max_accel_width, @@ -905,8 +908,8 @@ const MenuItemView* root = GetRootMenuItem(); return root && root->has_icons_ - ? MenuConfig::instance().item_top_margin - : MenuConfig::instance().item_no_icon_top_margin; + ? GetMenuConfig().item_top_margin : + GetMenuConfig().item_no_icon_top_margin; } int MenuItemView::GetBottomMargin() const { @@ -915,8 +918,8 @@ const MenuItemView* root = GetRootMenuItem(); return root && root->has_icons_ - ? MenuConfig::instance().item_bottom_margin - : MenuConfig::instance().item_no_icon_bottom_margin; + ? GetMenuConfig().item_bottom_margin : + GetMenuConfig().item_no_icon_bottom_margin; } gfx::Size MenuItemView::GetChildPreferredSize() const { @@ -954,8 +957,8 @@ // Adjust item content height if menu has both items with and without icons. // This way all menu items will have the same height. if (!icon_view_ && GetRootMenuItem()->has_icons()) { - dimensions.height = - std::max(dimensions.height, MenuConfig::instance().check_height); + dimensions.height = std::max(dimensions.height, + GetMenuConfig().check_height); } dimensions.height += GetBottomMargin() + GetTopMargin(); @@ -997,13 +1000,13 @@ std::max(dimensions.height, (subtitle_.empty() ? 0 : font_list.GetHeight()) + font_list.GetHeight() + GetBottomMargin() + GetTopMargin()); - dimensions.height = - std::max(dimensions.height, MenuConfig::instance().item_min_height); + dimensions.height = std::max(dimensions.height, + GetMenuConfig().item_min_height); return dimensions; } int MenuItemView::GetLabelStartForThisItem() const { - const MenuConfig& config = MenuConfig::instance(); + const MenuConfig& config = GetMenuConfig(); int label_start = label_start_ + left_icon_margin_ + right_icon_margin_; if ((config.icons_in_label || type_ == CHECKBOX || type_ == RADIO) && icon_view_) @@ -1019,9 +1022,8 @@ } ui::Accelerator accelerator; - if (MenuConfig::instance().show_accelerators && GetDelegate() && - GetCommand() && - GetDelegate()->GetAccelerator(GetCommand(), &accelerator)) { + if (GetMenuConfig().show_accelerators && GetDelegate() && GetCommand() && + GetDelegate()->GetAccelerator(GetCommand(), &accelerator)) { return accelerator.GetShortcutText(); } @@ -1052,8 +1054,7 @@ continue; } else if (menu_item->HasSubmenu()) { temp_width = menu_item->GetMaxIconViewWidth(); - } else if (menu_item->icon_view() && - !MenuConfig::instance().icons_in_label) { + } else if (menu_item->icon_view() && !GetMenuConfig().icons_in_label) { temp_width = menu_item->icon_view()->GetPreferredSize().width(); } width = std::max(width, temp_width);
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h index b6a9010..8348320 100644 --- a/ui/views/controls/menu/menu_item_view.h +++ b/ui/views/controls/menu/menu_item_view.h
@@ -324,6 +324,9 @@ use_right_margin_ = use_right_margin; } + // Returns a reference to MenuConfig to be used with this menu. + const MenuConfig& GetMenuConfig() const; + protected: // Creates a MenuItemView. This is used by the various AddXXX methods. MenuItemView(MenuItemView* parent, int command, Type type);
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc index 80a63af..4412a33 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -41,8 +41,9 @@ } gfx::Size GetPreferredSize() const override { - return gfx::Size(MenuConfig::instance().scroll_arrow_height * 2 - 1, - pref_height_); + return gfx::Size( + host_->GetMenuItem()->GetMenuConfig().scroll_arrow_height * 2 - 1, + pref_height_); } bool CanDrop(const OSExchangeData& data) override { @@ -70,7 +71,7 @@ } void OnPaint(gfx::Canvas* canvas) override { - const MenuConfig& config = MenuConfig::instance(); + const MenuConfig& config = host_->GetMenuItem()->GetMenuConfig(); // The background. gfx::Rect item_bounds(0, 0, width(), height()); @@ -249,7 +250,7 @@ gfx::Rect bounds(0, 0, width(), height()); NativeTheme::ExtraParams extra; - const MenuConfig& menu_config = MenuConfig::instance(); + const MenuConfig& menu_config = content_view_->GetMenuItem()->GetMenuConfig(); extra.menu_background.corner_radius = menu_config.corner_radius; GetNativeTheme()->Paint(canvas->sk_canvas(), NativeTheme::kMenuPopupBackground, NativeTheme::kNormal, bounds, extra); @@ -279,7 +280,8 @@ DCHECK_EQ(arrow_, BubbleBorder::NONE); bubble_border_ = nullptr; - const MenuConfig& menu_config = MenuConfig::instance(); + const MenuConfig& menu_config = + content_view_->GetMenuItem()->GetMenuConfig(); int padding = menu_config.use_outer_border && menu_config.corner_radius > 0 ? kBorderPaddingDueToRoundedCorners
diff --git a/ui/views/controls/menu/menu_separator.h b/ui/views/controls/menu/menu_separator.h index 966e03f..a0fd2c81 100644 --- a/ui/views/controls/menu/menu_separator.h +++ b/ui/views/controls/menu/menu_separator.h
@@ -15,7 +15,9 @@ class MenuSeparator : public View { public: - explicit MenuSeparator(ui::MenuSeparatorType type) : type_(type) {} + MenuSeparator(MenuItemView* parent, ui::MenuSeparatorType type) + : type_(type), + parent_menu_item_(parent) {} // View overrides. void OnPaint(gfx::Canvas* canvas) override; @@ -30,6 +32,9 @@ // The type of the separator. const ui::MenuSeparatorType type_; + // Our parent. + MenuItemView* parent_menu_item_; + DISALLOW_COPY_AND_ASSIGN(MenuSeparator); };
diff --git a/ui/views/controls/menu/menu_separator_views.cc b/ui/views/controls/menu/menu_separator_views.cc index 6194136..c61cc59 100644 --- a/ui/views/controls/menu/menu_separator_views.cc +++ b/ui/views/controls/menu/menu_separator_views.cc
@@ -8,6 +8,7 @@ #include "ui/gfx/canvas.h" #include "ui/native_theme/native_theme.h" #include "ui/views/controls/menu/menu_config.h" +#include "ui/views/controls/menu/menu_item_view.h" namespace { @@ -24,7 +25,7 @@ #endif gfx::Size MenuSeparator::GetPreferredSize() const { - const MenuConfig& menu_config = MenuConfig::instance(); + const MenuConfig& menu_config = parent_menu_item_->GetMenuConfig(); int height = menu_config.separator_height; switch(type_) { case ui::SPACING_SEPARATOR: @@ -60,7 +61,7 @@ } gfx::Rect paint_rect(0, pos, width(), kSeparatorHeight); - if (MenuConfig::instance().use_outer_border) + if (parent_menu_item_->GetMenuConfig().use_outer_border) paint_rect.Inset(1, 0); return paint_rect; }
diff --git a/ui/views/controls/menu/submenu_view.cc b/ui/views/controls/menu/submenu_view.cc index 1e2de11..5fba696 100644 --- a/ui/views/controls/menu/submenu_view.cc +++ b/ui/views/controls/menu/submenu_view.cc
@@ -160,9 +160,10 @@ child->GetPreferredSize().width()); } } - if (max_minor_text_width_ > 0) - max_minor_text_width_ += MenuConfig::instance().label_to_minor_text_padding; - + if (max_minor_text_width_ > 0) { + max_minor_text_width_ += + GetMenuItem()->GetMenuConfig().label_to_minor_text_padding; + } // Finish calculating our optimum width. gfx::Insets insets = GetInsets(); int width = std::max(max_complex_width,
diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 30471e4a..4400ab24 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp
@@ -101,8 +101,6 @@ 'controls/menu/display_change_listener_mac.cc', 'controls/menu/menu_config.cc', 'controls/menu/menu_config.h', - 'controls/menu/menu_config_chromeos.cc', - 'controls/menu/menu_config_linux.cc', 'controls/menu/menu_config_mac.mm', 'controls/menu/menu_config_win.cc', 'controls/menu/menu_controller.cc', @@ -374,6 +372,7 @@ 'bubble/tray_bubble_view.cc', 'bubble/tray_bubble_view.h', 'controls/menu/display_change_listener_aura.cc', + 'controls/menu/menu_config_aura.cc', 'controls/menu/menu_event_dispatcher.cc', 'controls/menu/menu_event_dispatcher.h', 'controls/menu/menu_key_event_handler.cc', @@ -500,10 +499,10 @@ 'test/focus_manager_test.h', 'test/menu_runner_test_api.cc', 'test/menu_runner_test_api.h', - 'test/scoped_views_test_helper.cc', - 'test/scoped_views_test_helper.h', 'test/slider_test_api.cc', 'test/slider_test_api.h', + 'test/scoped_views_test_helper.cc', + 'test/scoped_views_test_helper.h', 'test/test_views.cc', 'test/test_views.h', 'test/test_views_delegate.h', @@ -694,9 +693,6 @@ 'sources/': [ ['exclude', 'linux_ui'], ], - 'sources!': [ - 'controls/menu/menu_config_linux.cc', - ], }], ['OS=="win"', { 'sources': [