diff --git a/AUTHORS b/AUTHORS index d0e12c4..8ed2917 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -915,6 +915,7 @@ Collabora Limited <*@collabora.com> Comodo CA Limited Endless Mobile, Inc. <*@endlessm.com> +Estimote, Inc. <*@estimote.com> Facebook, Inc. <*@fb.com> Facebook, Inc. <*@oculus.com> Google Inc. <*@google.com>
diff --git a/DEPS b/DEPS index 7f82f3b5..42cf6e7 100644 --- a/DEPS +++ b/DEPS
@@ -86,7 +86,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '5b1180df6835e403e5a9636fe9f7c987e1e48e2c', + 'angle_revision': 'f2afccca7b27bf2a2415cbf066771295106500c5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. @@ -98,7 +98,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': 'd01f2429bc561eed07293b46e08a07c54e471521', + 'pdfium_revision': 'd84b42c71a8122b7cd144474fd6d6aaa787f54e1', # 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. @@ -323,7 +323,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cfb9a236fb19cacf6cab23aa317f2628e3d0fdfb', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9fce213bdbb8512c571c6e14b0302dbb5ecd653e', # DevTools node modules. Used on Linux buildbots only. 'src/third_party/devtools-node-modules': { @@ -639,7 +639,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '1a8ed15c9c25f2b1ccd97149c94ea245ab982252', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '28a06b16cc4daa9f380ad45af8acfd11b6057283', # commit position 20628 + Var('webrtc_git') + '/src.git' + '@' + '67c20ae57100e5878e9734b1c8c53ebd5b5d7bbf', # commit position 20628 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc index e4887a86..28f9232 100644 --- a/android_webview/common/crash_reporter/crash_keys.cc +++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -29,8 +29,6 @@ size_t RegisterWebViewCrashKeys() { base::debug::CrashKey fixed_keys[] = { - {kClientId, kSmallSize}, - {kChannel, kSmallSize}, {kActiveURL, kLargeSize}, {kNumVariations, kSmallSize}, {kVariations, kHugeSize}, @@ -51,8 +49,6 @@ std::vector<base::debug::CrashKey> keys(fixed_keys, fixed_keys + arraysize(fixed_keys)); - GetCrashKeysForCommandLineSwitches(&keys); - return base::debug::InitCrashKeys(&keys.at(0), keys.size(), kChunkMaxLength); }
diff --git a/base/BUILD.gn b/base/BUILD.gn index f5cbb79..5ebbf04 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1386,6 +1386,8 @@ ] if (use_partition_alloc) { + defines += [ "USE_PARTITION_ALLOC" ] + # Add stuff that doesn't work in NaCl. sources += [ # PartitionAlloc uses SpinLock, which doesn't work in NaCl (see below).
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h index 51bf92c..e2a404f 100644 --- a/base/metrics/histogram_snapshot_manager.h +++ b/base/metrics/histogram_snapshot_manager.h
@@ -28,10 +28,10 @@ // corruption, this class also validates as much redundancy as it can before // calling for the marginal change (a.k.a., delta) in a histogram to be // recorded. -class BASE_EXPORT HistogramSnapshotManager { +class BASE_EXPORT HistogramSnapshotManager final { public: explicit HistogramSnapshotManager(HistogramFlattener* histogram_flattener); - virtual ~HistogramSnapshotManager(); + ~HistogramSnapshotManager(); // Snapshot all histograms, and ask |histogram_flattener_| to record the // delta. |flags_to_set| is used to set flags for each histogram. @@ -39,15 +39,13 @@ // Only histograms that have all the flags specified by the argument will be // chosen. If all histograms should be recorded, set it to // |Histogram::kNoFlags|. - template <class ForwardHistogramIterator> - void PrepareDeltas(ForwardHistogramIterator begin, - ForwardHistogramIterator end, + void PrepareDeltas(const std::vector<HistogramBase*>& histograms, HistogramBase::Flags flags_to_set, HistogramBase::Flags required_flags) { - for (ForwardHistogramIterator it = begin; it != end; ++it) { - (*it)->SetFlags(flags_to_set); - if (((*it)->flags() & required_flags) == required_flags) - PrepareDelta(*it); + for (HistogramBase* const histogram : histograms) { + histogram->SetFlags(flags_to_set); + if ((histogram->flags() & required_flags) == required_flags) + PrepareDelta(histogram); } }
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc index a21adc0..82d7a93 100644 --- a/base/metrics/statistics_recorder.cc +++ b/base/metrics/statistics_recorder.cc
@@ -28,13 +28,24 @@ bool HistogramNameLesser(const base::HistogramBase* a, const base::HistogramBase* b) { - return a->histogram_name() < b->histogram_name(); + return strcmp(a->histogram_name(), b->histogram_name()) < 0; } } // namespace namespace base { +size_t StatisticsRecorder::BucketRangesHash::operator()( + const BucketRanges* const a) const { + return a->checksum(); +} + +bool StatisticsRecorder::BucketRangesEqual::operator()( + const BucketRanges* const a, + const BucketRanges* const b) const { + return a->Equals(b); +} + StatisticsRecorder::~StatisticsRecorder() { DCHECK(histograms_); DCHECK(ranges_); @@ -81,92 +92,72 @@ // static HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( HistogramBase* histogram) { - HistogramBase* histogram_to_delete = nullptr; - HistogramBase* histogram_to_return = nullptr; - { - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) { - histogram_to_return = histogram; + // Declared before auto_lock to ensure correct destruction order. + std::unique_ptr<HistogramBase> histogram_deleter; + base::AutoLock auto_lock(lock_.Get()); - // As per crbug.com/79322 the histograms are intentionally leaked, so we - // need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used - // only once for an object, the duplicates should not be annotated. - // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) - // twice |if (!histograms_)|. - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - } else { - const char* name = histogram->histogram_name(); - StringPiece name_piece(name); - HistogramMap::iterator it = histograms_->find(name_piece); - if (histograms_->end() == it) { - // |name_piece| is guaranteed to never change or be deallocated so long - // as the histogram is alive (which is forever). - (*histograms_)[name_piece] = histogram; - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - // If there are callbacks for this histogram, we set the kCallbackExists - // flag. - auto callback_iterator = callbacks_->find(name); - if (callback_iterator != callbacks_->end()) { - if (!callback_iterator->second.is_null()) - histogram->SetFlags(HistogramBase::kCallbackExists); - else - histogram->ClearFlags(HistogramBase::kCallbackExists); - } - histogram_to_return = histogram; - } else if (histogram == it->second) { - // The histogram was registered before. - histogram_to_return = histogram; - } else { - // We already have one histogram with this name. - DCHECK_EQ(StringPiece(histogram->histogram_name()), - StringPiece(it->second->histogram_name())) - << "hash collision"; - histogram_to_return = it->second; - histogram_to_delete = histogram; - } - } + if (!histograms_) { + // As per crbug.com/79322 the histograms are intentionally leaked, so we + // need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used + // only once for an object, the duplicates should not be annotated. + // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) + // twice |if (!histograms_)|. + ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 + return histogram; } - delete histogram_to_delete; - return histogram_to_return; + + const char* const name = histogram->histogram_name(); + HistogramBase*& registered = (*histograms_)[name]; + + if (!registered) { + // |name| is guaranteed to never change or be deallocated so long + // as the histogram is alive (which is forever). + registered = histogram; + ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 + // If there are callbacks for this histogram, we set the kCallbackExists + // flag. + const auto callback_iterator = callbacks_->find(name); + if (callback_iterator != callbacks_->end()) { + if (!callback_iterator->second.is_null()) + histogram->SetFlags(HistogramBase::kCallbackExists); + else + histogram->ClearFlags(HistogramBase::kCallbackExists); + } + return histogram; + } + + if (histogram == registered) { + // The histogram was registered before. + return histogram; + } + + // We already have one histogram with this name. + histogram_deleter.reset(histogram); + return registered; } // static const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( const BucketRanges* ranges) { DCHECK(ranges->HasValidChecksum()); - std::unique_ptr<const BucketRanges> ranges_deleter; + // Declared before auto_lock to ensure correct destruction order. + std::unique_ptr<const BucketRanges> ranges_deleter; base::AutoLock auto_lock(lock_.Get()); + if (!ranges_) { ANNOTATE_LEAKING_OBJECT_PTR(ranges); return ranges; } - std::list<const BucketRanges*>* checksum_matching_list; - RangesMap::iterator ranges_it = ranges_->find(ranges->checksum()); - if (ranges_->end() == ranges_it) { - // Add a new matching list to map. - checksum_matching_list = new std::list<const BucketRanges*>(); - ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list); - (*ranges_)[ranges->checksum()] = checksum_matching_list; + const BucketRanges* const registered = *ranges_->insert(ranges).first; + if (registered == ranges) { + ANNOTATE_LEAKING_OBJECT_PTR(ranges); } else { - checksum_matching_list = ranges_it->second; + ranges_deleter.reset(ranges); } - for (const BucketRanges* existing_ranges : *checksum_matching_list) { - if (existing_ranges->Equals(ranges)) { - if (existing_ranges == ranges) { - return ranges; - } else { - ranges_deleter.reset(ranges); - return existing_ranges; - } - } - } - // We haven't found a BucketRanges which has the same ranges. Register the - // new BucketRanges. - checksum_matching_list->push_front(ranges); - return ranges; + return registered; } // static @@ -180,7 +171,7 @@ std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); for (const HistogramBase* histogram : snapshot) { histogram->WriteHTMLGraph(output); - output->append("<br><hr><br>"); + *output += "<br><hr><br>"; } } @@ -208,16 +199,14 @@ if (!IsActive()) return std::string(); - std::string output("{"); Histograms snapshot; GetSnapshot(std::string(), &snapshot); - output += "\"histograms\":["; - bool first_histogram = true; - for (const HistogramBase* histogram : snapshot) { - if (first_histogram) - first_histogram = false; - else - output += ","; + + std::string output = "{\"histograms\":["; + const char* sep = ""; + for (const HistogramBase* const histogram : snapshot) { + output += sep; + sep = ","; std::string json; histogram->WriteJSON(&json, verbosity_level); output += json; @@ -244,10 +233,8 @@ if (!ranges_) return; - for (const auto& entry : *ranges_) { - for (auto* range_entry : *entry.second) { - output->push_back(range_entry); - } + for (const BucketRanges* const p : *ranges_) { + output->push_back(p); } } @@ -262,10 +249,8 @@ if (!histograms_) return nullptr; - HistogramMap::iterator it = histograms_->find(name); - if (histograms_->end() == it) - return nullptr; - return it->second; + const HistogramMap::const_iterator it = histograms_->find(name); + return it != histograms_->end() ? it->second : nullptr; } // static @@ -291,9 +276,9 @@ if (include_persistent) ImportGlobalPersistentHistograms(); - auto known = GetKnownHistograms(include_persistent); - snapshot_manager->PrepareDeltas(known.begin(), known.end(), flags_to_set, - required_flags); + Histograms known = GetKnownHistograms(include_persistent); + std::sort(known.begin(), known.end(), &HistogramNameLesser); + snapshot_manager->PrepareDeltas(known, flags_to_set, required_flags); } // static @@ -335,11 +320,10 @@ if (!histograms_) return false; - if (ContainsKey(*callbacks_, name)) + if (!callbacks_->insert({name, cb}).second) return false; - callbacks_->insert(std::make_pair(name, cb)); - auto it = histograms_->find(name); + const HistogramMap::const_iterator it = histograms_->find(name); if (it != histograms_->end()) it->second->SetFlags(HistogramBase::kCallbackExists); @@ -355,7 +339,7 @@ callbacks_->erase(name); // We also clear the flag from the histogram (if it exists). - auto it = histograms_->find(name); + const HistogramMap::const_iterator it = histograms_->find(name); if (it != histograms_->end()) it->second->ClearFlags(HistogramBase::kCallbackExists); } @@ -367,17 +351,14 @@ if (!histograms_) return OnSampleCallback(); - auto callback_iterator = callbacks_->find(name); - return callback_iterator != callbacks_->end() ? callback_iterator->second - : OnSampleCallback(); + const auto it = callbacks_->find(name); + return it != callbacks_->end() ? it->second : OnSampleCallback(); } // static size_t StatisticsRecorder::GetHistogramCount() { base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return 0; - return histograms_->size(); + return histograms_ ? histograms_->size() : 0; } // static @@ -385,18 +366,17 @@ if (!histograms_) return; - HistogramMap::iterator found = histograms_->find(name); + const HistogramMap::iterator found = histograms_->find(name); if (found == histograms_->end()) return; - HistogramBase* base = found->second; + HistogramBase* const base = found->second; if (base->GetHistogramType() != SPARSE_HISTOGRAM) { // When forgetting a histogram, it's likely that other information is // also becoming invalid. Clear the persistent reference that may no // longer be valid. There's no danger in this as, at worst, duplicates // will be created in persistent memory. - Histogram* histogram = static_cast<Histogram*>(base); - histogram->bucket_ranges()->set_persistent_reference(0); + static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0); } histograms_->erase(found); @@ -437,20 +417,18 @@ } // static -std::vector<HistogramBase*> StatisticsRecorder::GetKnownHistograms( +StatisticsRecorder::Histograms StatisticsRecorder::GetKnownHistograms( bool include_persistent) { - std::vector<HistogramBase*> known; + Histograms known; base::AutoLock auto_lock(lock_.Get()); if (!histograms_ || histograms_->empty()) return known; known.reserve(histograms_->size()); for (const auto& h : *histograms_) { - if (!include_persistent && - (h.second->flags() & HistogramBase::kIsPersistent)) { - continue; - } - known.push_back(h.second); + if (include_persistent || + (h.second->flags() & HistogramBase::kIsPersistent) == 0) + known.push_back(h.second); } return known; @@ -461,18 +439,17 @@ if (!histograms_) return; - // Import histograms from known persistent storage. Histograms could have - // been added by other processes and they must be fetched and recognized - // locally. If the persistent memory segment is not shared between processes, - // this call does nothing. - GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); - if (allocator) + // Import histograms from known persistent storage. Histograms could have been + // added by other processes and they must be fetched and recognized locally. + // If the persistent memory segment is not shared between processes, this call + // does nothing. + if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get()) allocator->ImportHistogramsToStatisticsRecorder(); } // This singleton instance should be started during the single threaded portion -// of main(), and hence it is not thread safe. It initializes globals to -// provide support for all future calls. +// of main(), and hence it is not thread safe. It initializes globals to provide +// support for all future calls. StatisticsRecorder::StatisticsRecorder() { base::AutoLock auto_lock(lock_.Get()); @@ -500,24 +477,24 @@ // static void StatisticsRecorder::Reset() { + // Declared before auto_lock to ensure correct destruction order. std::unique_ptr<HistogramMap> histograms_deleter; std::unique_ptr<CallbackMap> callbacks_deleter; std::unique_ptr<RangesMap> ranges_deleter; std::unique_ptr<HistogramProviders> providers_deleter; std::unique_ptr<RecordHistogramChecker> record_checker_deleter; - { - base::AutoLock auto_lock(lock_.Get()); - histograms_deleter.reset(histograms_); - callbacks_deleter.reset(callbacks_); - ranges_deleter.reset(ranges_); - providers_deleter.reset(providers_); - record_checker_deleter.reset(record_checker_); - histograms_ = nullptr; - callbacks_ = nullptr; - ranges_ = nullptr; - providers_ = nullptr; - record_checker_ = nullptr; - } + base::AutoLock auto_lock(lock_.Get()); + histograms_deleter.reset(histograms_); + callbacks_deleter.reset(callbacks_); + ranges_deleter.reset(ranges_); + providers_deleter.reset(providers_); + record_checker_deleter.reset(record_checker_); + histograms_ = nullptr; + callbacks_ = nullptr; + ranges_ = nullptr; + providers_ = nullptr; + record_checker_ = nullptr; + // We are going to leak the histograms and the ranges. } @@ -528,7 +505,6 @@ VLOG(1) << output; } - // static StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr; // static @@ -536,7 +512,8 @@ // static StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr; // static -StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_; +StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_ = + nullptr; // static RecordHistogramChecker* StatisticsRecorder::record_checker_ = nullptr; // static
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h index 49ccaf5..25427fd 100644 --- a/base/metrics/statistics_recorder.h +++ b/base/metrics/statistics_recorder.h
@@ -12,10 +12,10 @@ #include <stdint.h> -#include <list> -#include <map> #include <memory> #include <string> +#include <unordered_map> +#include <unordered_set> #include <vector> #include "base/base_export.h" @@ -36,35 +36,6 @@ class BASE_EXPORT StatisticsRecorder { public: - // A class used as a key for the histogram map below. It always references - // a string owned outside of this class, likely in the value of the map. - class StringKey : public StringPiece { - public: - // Constructs the StringKey using various sources. The source must live - // at least as long as the created object. - StringKey(const std::string& str) : StringPiece(str) {} - StringKey(StringPiece str) : StringPiece(str) {} - - // Though StringPiece is better passed by value than by reference, in - // this case it's being passed many times and likely already been stored - // in memory (not just registers) so the benefit of pass-by-value is - // negated. - bool operator<(const StringKey& rhs) const { - // Since order is unimportant in the map and string comparisons can be - // slow, use the length as the primary sort value. - if (length() < rhs.length()) - return true; - if (length() > rhs.length()) - return false; - - // Fall back to an actual string comparison. The lengths are the same - // so a simple memory-compare is sufficient. This is slightly more - // efficient than calling operator<() for StringPiece which would - // again have to check lengths before calling wordmemcmp(). - return wordmemcmp(data(), rhs.data(), length()) < 0; - } - }; - // An interface class that allows the StatisticsRecorder to forcibly merge // histograms from providers when necessary. class HistogramProvider { @@ -73,9 +44,7 @@ virtual void MergeHistogramDeltas() = 0; }; - typedef std::map<StringKey, HistogramBase*> HistogramMap; typedef std::vector<HistogramBase*> Histograms; - typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders; ~StatisticsRecorder(); @@ -115,13 +84,17 @@ static std::string ToJSON(JSONVerbosityLevel verbosity_level); // Method for extracting histograms which were marked for use by UMA. + // + // This method is thread safe. static void GetHistograms(Histograms* output); // Method for extracting BucketRanges used by all histograms registered. static void GetBucketRanges(std::vector<const BucketRanges*>* output); - // Find a histogram by name. It matches the exact name. This method is thread - // safe. It returns NULL if a matching histogram is not found. + // Find a histogram by name. It matches the exact name. + // It returns NULL if a matching histogram is not found. + // + // This method is thread safe. static HistogramBase* FindHistogram(base::StringPiece name); // Imports histograms from providers. This must be called on the UI thread. @@ -141,6 +114,8 @@ // caller supplied vector (Histograms). Only histograms which have |query| as // a substring are copied (an empty string will process all registered // histograms). + // + // This method is thread safe. static void GetSnapshot(const std::string& query, Histograms* snapshot); typedef base::Callback<void(HistogramBase::Sample)> OnSampleCallback; @@ -203,14 +178,26 @@ static bool ShouldRecordHistogram(uint64_t histogram_hash); private: + typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders; + + typedef std::unordered_map<StringPiece, HistogramBase*, StringPieceHash> + HistogramMap; + // We keep a map of callbacks to histograms, so that as histograms are // created, we can set the callback properly. - typedef std::map<std::string, OnSampleCallback> CallbackMap; + typedef std::unordered_map<std::string, OnSampleCallback> CallbackMap; - // We keep all |bucket_ranges_| in a map, from checksum to a list of - // |bucket_ranges_|. Checksum is calculated from the |ranges_| in - // |bucket_ranges_|. - typedef std::map<uint32_t, std::list<const BucketRanges*>*> RangesMap; + struct BucketRangesHash { + size_t operator()(const BucketRanges* a) const; + }; + + struct BucketRangesEqual { + bool operator()(const BucketRanges* a, const BucketRanges* b) const; + }; + + typedef std:: + unordered_set<const BucketRanges*, BucketRangesHash, BucketRangesEqual> + RangesMap; friend struct LazyInstanceTraitsBase<StatisticsRecorder>; friend class StatisticsRecorderTest; @@ -218,8 +205,7 @@ // Fetch set of existing histograms. Ownership of the individual histograms // remains with the StatisticsRecorder. - static std::vector<HistogramBase*> GetKnownHistograms( - bool include_persistent); + static Histograms GetKnownHistograms(bool include_persistent); // Imports histograms from global persistent memory. The global lock must // not be held during this call.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java index 9858416..2ebfc0e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelMetrics.java
@@ -120,11 +120,21 @@ if (mWasContextualCardsDataShown) { ContextualSearchUma.logContextualCardsResultsSeen(mWasSearchContentViewSeen); } + if (mRankerLogger != null) { + mRankerLogger.logOutcome( + ContextualSearchRankerLogger.Feature.OUTCOME_WAS_CARDS_DATA_SHOWN, + mWasContextualCardsDataShown); + } if (mWasQuickActionShown) { ContextualSearchUma.logQuickActionResultsSeen(mWasSearchContentViewSeen, mQuickActionCategory); ContextualSearchUma.logQuickActionClicked(mWasQuickActionClicked, mQuickActionCategory); + if (mRankerLogger != null) { + mRankerLogger.logOutcome( + ContextualSearchRankerLogger.Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, + mWasQuickActionClicked); + } } if (mResultsSeenExperiments != null) { @@ -132,7 +142,10 @@ mWasSearchContentViewSeen, mWasActivatedByTap); mResultsSeenExperiments.logPanelViewedDurations( panelViewDurationMs, mPanelOpenedBeyondPeekDurationMs); - if (!isChained) mResultsSeenExperiments = null; + if (mRankerLogger != null) { + mResultsSeenExperiments.logRankerTapSuppressionOutcome(mRankerLogger); + } + mResultsSeenExperiments = null; } mPanelOpenedBeyondPeekDurationMs = 0; @@ -140,10 +153,24 @@ boolean wasAnySuppressionHeuristicSatisfied = mWasAnyHeuristicSatisfiedOnPanelShow; ContextualSearchUma.logAnyTapSuppressionHeuristicSatisfied( mWasSearchContentViewSeen, wasAnySuppressionHeuristicSatisfied); + // Update The Ranker logger. + if (mRankerLogger != null) { + // Tell Ranker about the primary outcome. + mRankerLogger.logOutcome( + ContextualSearchRankerLogger.Feature.OUTCOME_WAS_PANEL_OPENED, + mWasSearchContentViewSeen); + ContextualSearchUma.logRankerInference(mWasSearchContentViewSeen, + mRankerLogger.getPredictionForTapSuppression()); + } ContextualSearchUma.logSelectionLengthResultsSeen( mWasSearchContentViewSeen, mSelectionLength); } + // Reset writing to Ranker so whatever interactions occurred are recorded as a + // complete record. + if (mRankerLogger != null) mRankerLogger.writeLogAndReset(); + mRankerLogger = null; + // Notifications to Feature Engagement. ContextualSearchIPH.doSearchFinishedNotifications(profile, mWasSearchContentViewSeen, mWasActivatedByTap, mWasContextualCardsDataShown); @@ -324,34 +351,6 @@ } /** - * Writes all the outcome features to the Ranker Logger and resets the logger. - * @param rankerLogger The {@link ContextualSearchRankerLogger} currently being used to measure - * or suppress the UI by Ranker. - */ - public void writeRankerLoggerOutcomesAndReset() { - if (mRankerLogger != null && mWasActivatedByTap) { - // Tell Ranker about the primary outcome. - mRankerLogger.logOutcome(ContextualSearchRankerLogger.Feature.OUTCOME_WAS_PANEL_OPENED, - mWasSearchContentViewSeen); - ContextualSearchUma.logRankerInference( - mWasSearchContentViewSeen, mRankerLogger.getPredictionForTapSuppression()); - mRankerLogger.logOutcome( - ContextualSearchRankerLogger.Feature.OUTCOME_WAS_CARDS_DATA_SHOWN, - mWasContextualCardsDataShown); - if (mWasQuickActionShown) { - mRankerLogger.logOutcome( - ContextualSearchRankerLogger.Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, - mWasQuickActionClicked); - } - if (mResultsSeenExperiments != null) { - mResultsSeenExperiments.logRankerTapSuppressionOutcome(mRankerLogger); - } - mRankerLogger.writeLogAndReset(); - mRankerLogger = null; - } - } - - /** * Determine whether a new contextual search is starting. * @param toState The contextual search state that will be transitioned to. * @param reason The reason for the search state transition.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java index ab4281d..057685b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java
@@ -12,7 +12,6 @@ */ public class ContextualSearchHeuristics { protected Set<ContextualSearchHeuristic> mHeuristics; - private QuickAnswersHeuristic mQuickAnswersHeuristic; /** * Manages a set of heuristics. @@ -97,20 +96,4 @@ heuristic.logRankerTapSuppressionOutcome(logger); } } - - /** - * Sets the {@link QuickAnswersHeuristic} so that it can be accessed externally by - * {@link #getQuickAnswersHeuristic}. - * @param quickAnswersHeuristic The active {@link QuickAnswersHeuristic}. - */ - public void setQuickAnswersHeuristic(QuickAnswersHeuristic quickAnswersHeuristic) { - mQuickAnswersHeuristic = quickAnswersHeuristic; - } - - /** - * @return The active {@link QuickAnswersHeuristic}. - */ - public QuickAnswersHeuristic getQuickAnswersHeuristic() { - return mQuickAnswersHeuristic; - } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateController.java index 6941ef2d..f864760 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateController.java
@@ -77,11 +77,6 @@ * within the waiting period we'll advance. */ WAITING_FOR_POSSIBLE_TAP_ON_TAP_SELECTION, - /** The first state in the Tap-gesture processing pipeline where we know we're processing - * a Tap-gesture that won't be converted into a long-press (from tap on tap-selection). It - * may later be suppressed or ignored due to being on an invalid character. - */ - TAP_GESTURE_COMMIT, /** Gathers text surrounding the selection. */ GATHERING_SURROUNDINGS, /** Decides if the gesture should trigger the UX or be suppressed. */ @@ -226,13 +221,14 @@ * or known. Only needed when we enter the IDLE state. */ private void startWorkingOn(InternalState state, @Nullable StateChangeReason reason) { - // All transitional states should be listed here, but not start states. switch (state) { case IDLE: assert reason != null; mStateHandler.hideContextualSearchUi(reason); break; + case LONG_PRESS_RECOGNIZED: + break; case SHOWING_LONGPRESS_SEARCH: mStateHandler.showContextualSearchLongpressUi(); break; @@ -240,12 +236,11 @@ case WAITING_FOR_POSSIBLE_TAP_NEAR_PREVIOUS: mStateHandler.waitForPossibleTapNearPrevious(); break; + case TAP_RECOGNIZED: + break; case WAITING_FOR_POSSIBLE_TAP_ON_TAP_SELECTION: mStateHandler.waitForPossibleTapOnTapSelection(); break; - case TAP_GESTURE_COMMIT: - mStateHandler.tapGestureCommit(); - break; case GATHERING_SURROUNDINGS: mStateHandler.gatherSurroundingText(); break; @@ -309,13 +304,10 @@ if (mPreviousState != null && mPreviousState != InternalState.IDLE) { transitionTo(InternalState.WAITING_FOR_POSSIBLE_TAP_ON_TAP_SELECTION); } else { - transitionTo(InternalState.TAP_GESTURE_COMMIT); + transitionTo(InternalState.GATHERING_SURROUNDINGS); } break; case WAITING_FOR_POSSIBLE_TAP_ON_TAP_SELECTION: - transitionTo(InternalState.TAP_GESTURE_COMMIT); - break; - case TAP_GESTURE_COMMIT: transitionTo(InternalState.GATHERING_SURROUNDINGS); break; case GATHERING_SURROUNDINGS:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java index 3b721c3..9e1f23b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java
@@ -30,13 +30,6 @@ void showContextualSearchLongpressUi(); /** - * The first state in the Tap-gesture processing pipeline where we know we're processing - * a Tap-gesture that won't be converted into something else. Starts the processing pipeline. - * @see ContextualSearchInternalStateController.InternalState#TAP_GESTURE_COMMIT - */ - void tapGestureCommit(); - - /** * Gathers text surrounding the current selection, which may have been created by either a Tap * or a Long-press gesture. * @see ContextualSearchInternalStateController.InternalState#GATHERING_SURROUNDINGS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java index cf3d9ee..bde03300 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -179,6 +179,7 @@ private boolean mIsAccessibilityModeEnabled; /** Tap Experiments and other variable behavior. */ + private ContextualSearchHeuristics mHeuristics; private QuickAnswersHeuristic mQuickAnswersHeuristic; // Counter for how many times we've called SelectWordAroundCaret without an ACK returned. @@ -1389,9 +1390,16 @@ @Override public void handleMetricsForWouldSuppressTap(ContextualSearchHeuristics tapHeuristics) { - mQuickAnswersHeuristic = tapHeuristics.getQuickAnswersHeuristic(); + mHeuristics = tapHeuristics; + + // TODO(donnd): QuickAnswersHeuristic is getting added to TapSuppressionHeuristics and + // and getting considered in TapSuppressionHeuristics#shouldSuppressTap(). It should + // be a part of ContextualSearchHeuristics for logging purposes but not for suppression. + mQuickAnswersHeuristic = new QuickAnswersHeuristic(); + mHeuristics.add(mQuickAnswersHeuristic); + if (mSearchPanel != null) { - mSearchPanel.getPanelMetrics().setResultsSeenExperiments(tapHeuristics); + mSearchPanel.getPanelMetrics().setResultsSeenExperiments(mHeuristics); mSearchPanel.getPanelMetrics().setRankerLogger(mTapSuppressionRankerLogger); } } @@ -1486,15 +1494,15 @@ // Called when the IDLE state has been entered. if (mContext != null) mContext.destroy(); mContext = null; + // Make sure we write to ranker and reset at the end of every search, even if it + // was a suppressed tap or longpress. + // TODO(donnd): Find a better place to just make a single call to this (now two). + mTapSuppressionRankerLogger.writeLogAndReset(); if (mSearchPanel == null) return; - // Make sure we write to Ranker and reset at the end of every search, even if the - // panel was not showing because it was a suppressed tap. if (isSearchPanelShowing()) { - mSearchPanel.getPanelMetrics().writeRankerLoggerOutcomesAndReset(); mSearchPanel.closePanel(reason, false); } else { - mTapSuppressionRankerLogger.writeLogAndReset(); if (mSelectionController.getSelectionType() == SelectionType.TAP) { mSelectionController.clearSelection(); } @@ -1540,25 +1548,16 @@ } } - /** First step where we're committed to processing the current Tap gesture. */ - @Override - public void tapGestureCommit() { - mInternalStateController.notifyStartingWorkOn(InternalState.TAP_GESTURE_COMMIT); - // We may be processing a chained search (aka a retap -- a tap near a previous tap). - // If it's chained we need to log the outcomes and reset, because we won't be hiding - // the panel at the end of the previous search (we'll update it to the new Search). - if (isSearchPanelShowing()) { - mSearchPanel.getPanelMetrics().writeRankerLoggerOutcomesAndReset(); - } - // Set up the next batch of Ranker logging. - mTapSuppressionRankerLogger.setupLoggingForPage(getBaseWebContents()); - mInternalStateController.notifyFinishedWorkOn(InternalState.TAP_GESTURE_COMMIT); - } - /** Starts the process of deciding if we'll suppress the current Tap gesture or not. */ @Override public void decideSuppression() { mInternalStateController.notifyStartingWorkOn(InternalState.DECIDING_SUPPRESSION); + + // Ranker will handle the suppression, but our legacy implementation uses + // TapSuppressionHeuristics (run from the ContextualSearchSelectionController). + // Usage includes tap-far-from-previous suppression. + mTapSuppressionRankerLogger.setupLoggingForPage(getBaseWebContents()); + // TODO(donnd): Move handleShouldSuppressTap out of the Selection Controller. mSelectionController.handleShouldSuppressTap(mContext, mTapSuppressionRankerLogger); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java index 7bd5be5..b8a4ae94 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
@@ -81,7 +81,6 @@ // A for-testing copy of all the features to log setup so that it will survive a {@link #reset}. private Map<Feature, Object> mFeaturesLoggedForTesting; - private Map<Feature, Object> mOutcomesLoggedForTesting; /** * Constructs a Ranker Logger and associated native implementation to write Contextual Search @@ -181,7 +180,7 @@ for (Map.Entry<Feature, Object> entry : mFeaturesToLog.entrySet()) { logObject(entry.getKey(), entry.getValue()); } - mOutcomesLoggedForTesting = mFeaturesToLog; + mFeaturesLoggedForTesting = mFeaturesToLog; } nativeWriteLogAndReset(mNativePointer); } @@ -244,9 +243,9 @@ } /** - * Gets the current set of features that have been logged. Should only be used for testing - * purposes! - * @return The current set of features that have been logged, or {@code null}. + * Gets the current set of features to log or that have been logged. Should only be used for + * testing purposes! + * @return The current set of features to log or that have been logged, or {@code null}. */ @VisibleForTesting @Nullable @@ -254,17 +253,6 @@ return mFeaturesLoggedForTesting; } - /** - * Gets the current set of outcomes that have been logged. Should only be used for - * testing purposes! - * @return The current set of outcomes that have been logged, or {@code null}. - */ - @VisibleForTesting - @Nullable - Map<Feature, Object> getOutcomesLogged() { - return mOutcomesLoggedForTesting; - } - // ============================================================================================ // Native methods. // ============================================================================================
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java index 76f2b8b..abdb4970 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -61,6 +61,7 @@ private boolean mWasTapGestureDetected; // Reflects whether the last tap was valid and whether we still have a tap-based selection. private ContextualSearchTapState mLastTapState; + private boolean mIsWaitingForInvalidTapDetection; private boolean mShouldHandleSelectionModification; // Whether the selection was automatically expanded due to an adjustment (e.g. Resolve). private boolean mDidExpandSelection; @@ -498,6 +499,14 @@ // ============================================================================================ /** + * @return whether a tap gesture has been detected, for testing. + */ + @VisibleForTesting + boolean wasAnyTapGestureDetected() { + return mIsWaitingForInvalidTapDetection; + } + + /** * @return whether selection is empty, for testing. */ @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java index b9ee3c6..5bb768f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppressionHeuristics.java
@@ -40,10 +40,6 @@ mHeuristics.add(new BarOverlapTapSuppression(selectionController, y)); // Second Tap ML Override. mHeuristics.add(new SecondTapMlOverride(selectionController, previousTapState, x, y)); - // Quick Answer that appears in the Caption via the JS API. - QuickAnswersHeuristic quickAnswersHeuristic = new QuickAnswersHeuristic(); - setQuickAnswersHeuristic(quickAnswersHeuristic); - mHeuristics.add(quickAnswersHeuristic); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java index 2506cf6..87d064c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
@@ -7,6 +7,7 @@ import android.text.method.LinkMovementMethod; import android.view.Gravity; import android.view.View; +import android.view.View.OnClickListener; import android.widget.TextView; import org.chromium.base.ApiCompatibilityUtils; @@ -15,6 +16,7 @@ import org.chromium.chrome.browser.survey.SurveyController; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.util.AccessibilityUtil; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.text.NoUnderlineClickableSpan; import org.chromium.ui.text.SpanApplier; @@ -36,6 +38,9 @@ // The delegate to handle what happens when info bar events are triggered. private final SurveyInfoBarDelegate mDelegate; + // Boolean to track if the infobar was clicked to prevent double triggering of the survey. + private boolean mClicked; + /** * Create and show the {@link SurveyInfoBar}. * @param webContents The webcontents to create the {@link InfoBar} around. @@ -92,17 +97,11 @@ NoUnderlineClickableSpan clickableSpan = new NoUnderlineClickableSpan() { /** Prevent double clicking on the text span. */ - private boolean mClicked; @Override public void onClick(View widget) { if (mClicked) return; - mDelegate.onSurveyTriggered(); - - SurveyController.getInstance().showSurveyIfAvailable( - tab.getActivity(), mSiteId, mShowAsBottomSheet, mDisplayLogoResId); - closeInfoBar(); - mClicked = true; + showSurvey(tab); } }; @@ -114,6 +113,7 @@ prompt.setMovementMethod(LinkMovementMethod.getInstance()); prompt.setGravity(Gravity.CENTER_VERTICAL); ApiCompatibilityUtils.setTextAppearance(prompt, R.style.BlackTitle1); + addAccessibilityClickListener(prompt, tab); layout.addContent(prompt, 1f); } @@ -139,6 +139,34 @@ siteId, showAsBottomSheet, displayLogoResId, surveyInfoBarDelegate); } + /** + * Allows the survey infobar to be triggered when talkback is enabled. + * @param view The view to attach the listener. + * @param tab The tab to attach the infobar. + */ + private void addAccessibilityClickListener(TextView view, Tab tab) { + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mClicked || !AccessibilityUtil.isAccessibilityEnabled()) return; + showSurvey(tab); + } + }); + } + + /** + * Shows the survey and closes the infobar. + * @param tab The tab on which to show the survey. + */ + private void showSurvey(Tab tab) { + mClicked = true; + mDelegate.onSurveyTriggered(); + + SurveyController.getInstance().showSurveyIfAvailable( + tab.getActivity(), mSiteId, mShowAsBottomSheet, mDisplayLogoResId); + closeInfoBar(); + } + private static native void nativeCreate(WebContents webContents, String siteId, boolean showAsBottomSheet, int displayLogoResId, SurveyInfoBarDelegate surveyInfoBarDelegate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeHomeSurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeHomeSurveyController.java index 74ad827..a004b59 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeHomeSurveyController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeHomeSurveyController.java
@@ -31,7 +31,6 @@ import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; -import org.chromium.chrome.browser.util.AccessibilityUtil; import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.components.variations.VariationsAssociatedData; import org.chromium.ui.base.DeviceFormFactor; @@ -118,7 +117,6 @@ private boolean doesUserQualifyForSurvey() { if (DeviceFormFactor.isTablet()) return false; if (!isUMAEnabled() && !sForceUmaEnabledForTesting) return false; - if (AccessibilityUtil.isAccessibilityEnabled()) return false; if (hasInfoBarBeenDisplayed()) return false; if (!FeatureUtilities.isChromeHomeEnabled()) return true; return wasChromeHomeEnabledForMinimumOneWeek()
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java index 6f4851e..d775c6c9 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFakeServer.java
@@ -624,9 +624,6 @@ QuickActionCategory.NONE)); registerFakeTapSearch(new FakeTapSearch("german", false, 200, "Deutsche", "Deutsche", "alternate-term", "", false, 0, 0, "de", "", "", "", QuickActionCategory.NONE)); - registerFakeTapSearch(new FakeTapSearch("intelligence", false, 200, "Intelligence", - "Intelligence", "alternate-term", "", false, 0, 0, "", "", "", "", - QuickActionCategory.NONE)); // Register a resolving search of "States" that expands to "United States". registerFakeSlowResolveSearch(new FakeSlowResolveSearch("states", false, 200, "States",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java index 9f1ab811..fb26d2c9 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateControllerWrapper.java
@@ -15,11 +15,11 @@ */ class ContextualSearchInternalStateControllerWrapper extends ContextualSearchInternalStateController { - static final List<InternalState> EXPECTED_TAP_RESOLVE_SEQUENCE = CollectionUtil.newArrayList( - InternalState.SELECTION_CLEARED_RECOGNIZED, InternalState.TAP_RECOGNIZED, - InternalState.TAP_GESTURE_COMMIT, InternalState.GATHERING_SURROUNDINGS, - InternalState.DECIDING_SUPPRESSION, InternalState.START_SHOWING_TAP_UI, - InternalState.SHOW_FULL_TAP_UI, InternalState.RESOLVING); + static final List<InternalState> EXPECTED_TAP_RESOLVE_SEQUENCE = + CollectionUtil.newArrayList(InternalState.SELECTION_CLEARED_RECOGNIZED, + InternalState.TAP_RECOGNIZED, InternalState.GATHERING_SURROUNDINGS, + InternalState.DECIDING_SUPPRESSION, InternalState.START_SHOWING_TAP_UI, + InternalState.SHOW_FULL_TAP_UI, InternalState.RESOLVING); static final List<InternalState> EXPECTED_LONGPRESS_SEQUENCE = CollectionUtil.newArrayList(InternalState.LONG_PRESS_RECOGNIZED, InternalState.GATHERING_SURROUNDINGS, InternalState.SHOWING_LONGPRESS_SEARCH);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java index e9dd28f..e6d7443 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -863,6 +863,20 @@ } /** + * Waits for the manager to finish processing a gesture. + * Tells the manager that a gesture has started, and then waits for it to complete. + */ + private void waitForGestureProcessing() { + CriteriaHelper.pollInstrumentationThread( + new Criteria("Gesture processing did not complete.") { + @Override + public boolean isSatisfied() { + return !mSelectionController.wasAnyTapGestureDetected(); + } + }, TEST_TIMEOUT, DEFAULT_POLLING_INTERVAL); + } + + /** * Shorthand for a common sequence: * 1) Waits for gesture processing, * 2) Waits for the panel to close, @@ -870,6 +884,7 @@ * @throws InterruptedException */ private void waitForGestureToClosePanelAndAssertNoSelection() throws InterruptedException { + waitForGestureProcessing(); waitForPanelToClose(); assertPanelClosedOrUndefined(); Assert.assertTrue(TextUtils.isEmpty(getSelectedText())); @@ -984,8 +999,6 @@ */ private void tapBasePageToClosePanel() throws InterruptedException { // TODO(pedrosimonetti): This is not reliable. Find a better approach. - // This taps on the panel in an area that will be selected if the "intelligence" node has - // been tap-selected, and that will cause it to be long-press selected. // We use the far right side (x == 0.9f) to prevent simulating a tap on top of an // existing long-press selection (the pins are a tap target). This might not work on RTL. // We are using y == 0.35f because otherwise it will fail for long press cases. @@ -1138,31 +1151,30 @@ assertWaitForSelectActionBarVisible(true); } - /** Gets the Ranker Logger and asserts if we can't. **/ - private ContextualSearchRankerLoggerImpl getRankerLogger() { + /** @return The value of the given logged feature, or {@code null} if not logged. */ + private Object loggedToRanker(ContextualSearchRankerLogger.Feature feature) { ContextualSearchRankerLoggerImpl rankerLogger = (ContextualSearchRankerLoggerImpl) mManager.getRankerLogger(); Assert.assertNotNull(rankerLogger); - return rankerLogger; + return rankerLogger.getFeaturesLogged().get(feature); } - /** @return The value of the given logged feature, or {@code null} if not logged. */ - private Object loggedToRanker(ContextualSearchRankerLogger.Feature feature) { - return getRankerLogger().getFeaturesLogged().get(feature); + /** Asserts that the given feature has been logged to Ranker. **/ + private void assertLoggedToRanker(ContextualSearchRankerLogger.Feature feature) { + Assert.assertNotNull(loggedToRanker(feature)); } /** Asserts that all the expected features have been logged to Ranker. **/ private void assertLoggedAllExpectedFeaturesToRanker() { for (ContextualSearchRankerLogger.Feature feature : EXPECTED_RANKER_FEATURES) { - Assert.assertNotNull(loggedToRanker(feature)); + assertLoggedToRanker(feature); } } /** Asserts that all the expected outcomes have been logged to Ranker. **/ private void assertLoggedAllExpectedOutcomesToRanker() { for (ContextualSearchRankerLogger.Feature feature : EXPECTED_RANKER_OUTCOMES) { - Assert.assertNotNull("Expected this outcome to be logged: " + feature, - getRankerLogger().getOutcomesLogged().get(feature)); + assertLoggedToRanker(feature); } } @@ -1241,17 +1253,19 @@ @SmallTest @Feature({"ContextualSearch"}) public void testTap() throws InterruptedException, TimeoutException { - simulateTapSearch("intelligence"); + clickWordNode("intelligence"); + + Assert.assertEquals("Intelligence", mFakeServer.getSearchTermRequested()); + fakeResponse(false, 200, "Intelligence", "display-text", "alternate-term", false); + assertContainsParameters("Intelligence", "alternate-term"); + waitForPanelToPeek(); assertLoadedLowPriorityUrl(); assertLoggedAllExpectedFeaturesToRanker(); Assert.assertEquals( true, loggedToRanker(ContextualSearchRankerLogger.Feature.IS_LONG_WORD)); // The panel must be closed for outcomes to be logged. - // Close the panel by clicking far away in order to make sure the outcomes get logged by - // the hideContextualSearchUi call to writeRankerLoggerOutcomesAndReset. - clickWordNode("states-far"); - waitForPanelToClose(); + closePanel(); assertLoggedAllExpectedOutcomesToRanker(); } @@ -1444,6 +1458,7 @@ waitForPanelToPeek(); assertLoadedNoUrl(); // No load after long-press until opening panel. clickNode("question-mark"); + waitForGestureProcessing(); waitForPanelToCloseAndSelectionEmpty(); Assert.assertTrue(TextUtils.isEmpty(getSelectedText())); assertLoadedNoUrl(); @@ -1476,6 +1491,7 @@ public void testTapGestureOnSpecialCharacterDoesntSelect() throws InterruptedException, TimeoutException { clickNode("question-mark"); + waitForGestureProcessing(); Assert.assertNull(getSelectedText()); assertPanelClosedOrUndefined(); assertLoadedNoUrl(); @@ -1544,12 +1560,13 @@ waitForPanelToClose(); Assert.assertNull(getSelectedText()); clickNode("states-far"); + waitForGestureProcessing(); waitForPanelToPeek(); Assert.assertEquals("States", getSelectedText()); } /** - * Tests a "retap" -- that sequential Tap gestures nearby keep selecting. + * Tests that sequential Tap gestures nearby keep selecting. */ @Test @SmallTest @@ -1558,7 +1575,6 @@ clickWordNode("states"); Assert.assertEquals("States", getSelectedText()); waitForPanelToPeek(); - assertLoggedAllExpectedFeaturesToRanker(); // Avoid issues with double-tap detection by ensuring sequential taps // aren't treated as such. Double-tapping can also select words much as // longpress, in turn showing the pins and preventing contextual tap @@ -1570,12 +1586,9 @@ // for the selection to change. clickNode("states-near"); waitForSelectionToBe("StatesNear"); - assertLoggedAllExpectedOutcomesToRanker(); - assertLoggedAllExpectedFeaturesToRanker(); Thread.sleep(ViewConfiguration.getDoubleTapTimeout()); clickNode("states"); waitForSelectionToBe("States"); - assertLoggedAllExpectedOutcomesToRanker(); } /**
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java index ea34e07..e91ab55 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java
@@ -46,11 +46,6 @@ } @Override - public void tapGestureCommit() { - stubForWorkOnState(InternalState.TAP_GESTURE_COMMIT); - } - - @Override public void gatherSurroundingText() { stubForWorkOnState(InternalState.GATHERING_SURROUNDINGS); }
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc index c8e7b60..825220c 100644 --- a/chrome/app/chrome_crash_reporter_client_win.cc +++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -74,8 +74,6 @@ // For now these need to be kept relatively up to date with those in // chrome/common/crash_keys.cc::RegisterChromeCrashKeys(). static constexpr base::debug::CrashKey kFixedKeys[] = { - {kMetricsClientId, kSmallSize}, - {kChannel, kSmallSize}, {kActiveURL, kLargeSize}, {kNumVariations, kSmallSize}, {kVariations, kHugeSize}, @@ -97,8 +95,6 @@ std::vector<base::debug::CrashKey> keys(std::begin(kFixedKeys), std::end(kFixedKeys)); - crash_keys::GetCrashKeysForCommandLineSwitches(&keys); - // Register the extension IDs. { static char formatted_keys[kExtensionIDMaxCount]
diff --git a/chrome/app/md_extensions_strings.grdp b/chrome/app/md_extensions_strings.grdp index b54a2aa..fd8f48bc 100644 --- a/chrome/app/md_extensions_strings.grdp +++ b/chrome/app/md_extensions_strings.grdp
@@ -16,6 +16,12 @@ <message name="IDS_MD_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION" desc="The label indicating that an error was caused within an anonymous function in the code."> anonymous function </message> + <message name="IDS_MD_EXTENSIONS_ERROR_CONTEXT" desc="The label for the error's context url."> + Context + </message> + <message name="IDS_MD_EXTENSIONS_ERROR_CONTEXT_UNKNOWN" desc="The text displayed to the user if an error's context url is unknown."> + Unknown + </message> <message name="IDS_MD_EXTENSIONS_ERROR_CLEAR_ALL" desc="The label for the button that clears all the errors."> Clear all </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 83edea0..103189ed 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -3489,6 +3489,12 @@ <message name="IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_LABEL" desc="In Device Settings > Displays, the label for the Night Light feature (which controls the color temperature of the screen) section."> Night Light </message> + <message name="IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_ON_AT_SUNSET" desc="In Device Settings > Displays, the sub label for the automatic schedule which explains that Night Light will turn on automatically at sunset."> + Night Light will turn on automatically at sunset + </message> + <message name="IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_OFF_AT_SUNRISE" desc="In Device Settings > Displays, the sub label for the automatic schedule which explains that Night Light will turn off automatically at sunrise."> + Night Light will turn off automatically at sunrise + </message> <message name="IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_CUSTOM" desc="In Device Settings > Displays, the label of the option to set a custom schedule of the Night Light feature."> Custom </message>
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_decider.cc b/chrome/browser/android/oom_intervention/oom_intervention_decider.cc index fea0aae..ca6e3cf 100644 --- a/chrome/browser/android/oom_intervention/oom_intervention_decider.cc +++ b/chrome/browser/android/oom_intervention/oom_intervention_decider.cc
@@ -74,7 +74,7 @@ PrefService::PrefInitializationStatus pref_status = prefs_->GetInitializationStatus(); if (pref_status == PrefService::INITIALIZATION_STATUS_WAITING) { - prefs_->AddPrefInitObserver(base::BindRepeating( + prefs_->AddPrefInitObserver(base::BindOnce( &OomInterventionDecider::OnPrefInitialized, base::Unretained(this))); } else { OnPrefInitialized(pref_status ==
diff --git a/chrome/browser/autofill/autofill_provider_browsertest.cc b/chrome/browser/autofill/autofill_provider_browsertest.cc new file mode 100644 index 0000000..d905b2ae --- /dev/null +++ b/chrome/browser/autofill/autofill_provider_browsertest.cc
@@ -0,0 +1,187 @@ +// Copyright 2017 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 "base/macros.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/autofill/content/browser/content_autofill_driver_factory.h" +#include "components/autofill/core/browser/test_autofill_client.h" +#include "components/autofill/core/browser/test_autofill_provider.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/url_constants.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { +namespace { + +const base::FilePath::CharType kDocRoot[] = + FILE_PATH_LITERAL("chrome/test/data"); + +// The workaround class for MSVC warning C4373 +// see +// https://github.com/google/googlemock/blob/master/googlemock/docs/v1_5/FrequentlyAskedQuestions.md +class MockableAutofillProvider : public TestAutofillProvider { + public: + ~MockableAutofillProvider() override {} + bool OnWillSubmitForm(AutofillHandlerProxy* handler, + const FormData& form, + const base::TimeTicks timestamp) override { + return MockableOnWillSubmitForm(handler, form); + } + + virtual bool MockableOnWillSubmitForm(AutofillHandlerProxy* handler, + const FormData& form) = 0; +}; + +class MockAutofillProvider : public MockableAutofillProvider { + public: + MockAutofillProvider() {} + ~MockAutofillProvider() override {} + + MOCK_METHOD2(MockableOnWillSubmitForm, + bool(AutofillHandlerProxy* handler, const FormData& form)); +}; + +} // namespace + +class AutofillProviderBrowserTest : public InProcessBrowserTest { + public: + AutofillProviderBrowserTest() {} + ~AutofillProviderBrowserTest() override {} + + void SetUpOnMainThread() override { + content::WebContents* web_contents = WebContents(); + ASSERT_TRUE(web_contents != NULL); + + web_contents->RemoveUserData( + ContentAutofillDriverFactory:: + kContentAutofillDriverFactoryWebContentsUserDataKey); + + ContentAutofillDriverFactory::CreateForWebContentsAndDelegate( + web_contents, &autofill_client_, "en-US", + AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER, + &autofill_provider_); + + embedded_test_server()->AddDefaultHandlers(base::FilePath(kDocRoot)); + // Serve both a.com and b.com (and any other domain). + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + void TearDownOnMainThread() override { + testing::Mock::VerifyAndClearExpectations(&autofill_provider_); + } + + content::RenderViewHost* RenderViewHost() { + return WebContents()->GetRenderViewHost(); + } + + content::WebContents* WebContents() { + return browser()->tab_strip_model()->GetActiveWebContents(); + } + + void SimulateUserTypingInFocuedField() { + content::WebContents* web_contents = WebContents(); + + content::SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('O'), + ui::DomCode::US_O, ui::VKEY_O, false, false, + false, false); + content::SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('R'), + ui::DomCode::US_R, ui::VKEY_R, false, false, + false, false); + content::SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('A'), + ui::DomCode::US_A, ui::VKEY_A, false, false, + false, false); + content::SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('R'), + ui::DomCode::US_R, ui::VKEY_R, false, false, + false, false); + content::SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('Y'), + ui::DomCode::US_Y, ui::VKEY_Y, false, false, + false, false); + } + + protected: + MockAutofillProvider autofill_provider_; + + private: + TestAutofillClient autofill_client_; +}; + +IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest, + FrameDetachedOnFormlessSubmission) { + EXPECT_CALL(autofill_provider_, + MockableOnWillSubmitForm(testing::_, testing::_)) + .Times(1); + ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "/autofill/frame_detached_on_formless_submit.html")); + + // Need to pay attention for a message that XHR has finished since there + // is no navigation to wait for. + content::DOMMessageQueue message_queue; + + std::string focus( + "var iframe = document.getElementById('address_iframe');" + "var frame_doc = iframe.contentDocument;" + "frame_doc.getElementById('address_field').focus();"); + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), focus)); + + SimulateUserTypingInFocuedField(); + std::string fill_and_submit = + "var iframe = document.getElementById('address_iframe');" + "var frame_doc = iframe.contentDocument;" + "frame_doc.getElementById('submit_button').click();"; + + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); + std::string message; + while (message_queue.WaitForMessage(&message)) { + if (message == "\"SUBMISSION_FINISHED\"") + break; + } + EXPECT_EQ("\"SUBMISSION_FINISHED\"", message); +} + +IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest, + FrameDetachedOnFormSubmission) { + EXPECT_CALL(autofill_provider_, + MockableOnWillSubmitForm(testing::_, testing::_)) + .Times(1); + ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "/autofill/frame_detached_on_form_submit.html")); + + // Need to pay attention for a message that XHR has finished since there + // is no navigation to wait for. + content::DOMMessageQueue message_queue; + + std::string focus( + "var iframe = document.getElementById('address_iframe');" + "var frame_doc = iframe.contentDocument;" + "frame_doc.getElementById('address_field').focus();"); + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), focus)); + + SimulateUserTypingInFocuedField(); + std::string fill_and_submit = + "var iframe = document.getElementById('address_iframe');" + "var frame_doc = iframe.contentDocument;" + "frame_doc.getElementById('submit_button').click();"; + + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); + std::string message; + while (message_queue.WaitForMessage(&message)) { + if (message == "\"SUBMISSION_FINISHED\"") + break; + } + EXPECT_EQ("\"SUBMISSION_FINISHED\"", message); +} + +} // namespace autofill
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index 2016723..3a1d9671 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc
@@ -207,6 +207,10 @@ #include "chrome/browser/first_run/upgrade_util_linux.h" #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) +#if defined(OS_LINUX) +#include "components/crash/content/app/breakpad_linux.h" +#endif + #if defined(OS_MACOSX) #include <Security/Security.h> @@ -1125,8 +1129,7 @@ #if defined(OS_LINUX) || defined(OS_OPENBSD) // Set the product channel for crash reports. - base::debug::SetCrashKeyValue(crash_keys::kChannel, - chrome::GetChannelString()); + breakpad::SetChannelCrashKey(chrome::GetChannelString()); #endif // defined(OS_LINUX) || defined(OS_OPENBSD) #if defined(OS_MACOSX)
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc index 1f1ce2ed..0be08405 100644 --- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc +++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -137,6 +137,14 @@ } // namespace +ArcAccessibilityHelperBridge::CountedAXTree::CountedAXTree( + AXTreeSourceArc* ax_tree) + : count(1U) { + tree.reset(ax_tree); +} + +ArcAccessibilityHelperBridge::CountedAXTree::~CountedAXTree() {} + // static ArcAccessibilityHelperBridge* ArcAccessibilityHelperBridge::GetForBrowserContext( @@ -244,12 +252,11 @@ bool is_notification_event = event_data->notification_key.has_value(); if (is_notification_event) { std::string notification_key = event_data->notification_key.value(); - if (event_data->event_type == - arc::mojom::AccessibilityEventType::WINDOW_STATE_CHANGED) { - tree_source = CreateFromNotificationKey(notification_key); - } else { - tree_source = GetFromNotificationKey(notification_key); - } + bool increment_counter = + event_data->event_type == + arc::mojom::AccessibilityEventType::WINDOW_STATE_CHANGED; + tree_source = + GetOrCreateFromNotificationKey(notification_key, increment_counter); } else { if (event_data->task_id == kNoTaskId) return; @@ -317,21 +324,20 @@ return tree_source; } -AXTreeSourceArc* ArcAccessibilityHelperBridge::CreateFromNotificationKey( - const std::string& notification_key) { - CHECK_EQ(0U, notification_key_to_tree_.count(notification_key)); - - notification_key_to_tree_[notification_key].reset(new AXTreeSourceArc(this)); - return notification_key_to_tree_[notification_key].get(); -} - -AXTreeSourceArc* ArcAccessibilityHelperBridge::GetFromNotificationKey( - const std::string& notification_key) const { +AXTreeSourceArc* ArcAccessibilityHelperBridge::GetOrCreateFromNotificationKey( + const std::string& notification_key, + bool increment_counter) { auto tree_it = notification_key_to_tree_.find(notification_key); - if (tree_it == notification_key_to_tree_.end()) - return nullptr; + if (tree_it == notification_key_to_tree_.end()) { + notification_key_to_tree_[notification_key].reset( + new CountedAXTree(new AXTreeSourceArc(this))); + return notification_key_to_tree_[notification_key]->tree.get(); + } - return tree_it->second.get(); + if (increment_counter) + tree_it->second->count++; + + return tree_it->second->tree.get(); } AXTreeSourceArc* ArcAccessibilityHelperBridge::GetFromTreeId( @@ -346,9 +352,9 @@ for (auto notification_it = notification_key_to_tree_.begin(); notification_it != notification_key_to_tree_.end(); ++notification_it) { ui::AXTreeData tree_data; - notification_it->second->GetTreeData(&tree_data); + notification_it->second->tree->GetTreeData(&tree_data); if (tree_data.tree_id == tree_id) - return notification_it->second.get(); + return notification_it->second->tree.get(); } return nullptr; @@ -470,12 +476,14 @@ void ArcAccessibilityHelperBridge::OnNotificationSurfaceAdded( ArcNotificationSurface* surface) { const std::string& notification_key = surface->GetNotificationKey(); - auto tree_it = notification_key_to_tree_.find(notification_key); - if (tree_it == notification_key_to_tree_.end()) + + AXTreeSourceArc* tree = GetOrCreateFromNotificationKey( + notification_key, false /* increment_counter */); + if (!tree) return; ui::AXTreeData tree_data; - if (!tree_it->second->GetTreeData(&tree_data)) + if (!tree->GetTreeData(&tree_data)) return; surface->SetAXTreeId(tree_data.tree_id); @@ -495,7 +503,16 @@ void ArcAccessibilityHelperBridge::OnNotificationSurfaceRemoved( ArcNotificationSurface* surface) { const std::string& notification_key = surface->GetNotificationKey(); - notification_key_to_tree_.erase(notification_key); + auto it = notification_key_to_tree_.find(notification_key); + if (it == notification_key_to_tree_.end()) + return; + + it->second->count--; + + CHECK(it->second->count >= 0); + + if (it->second->count == 0) + notification_key_to_tree_.erase(notification_key); } } // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h index 5afd4967..aacc932 100644 --- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h +++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
@@ -42,6 +42,14 @@ public ArcAppListPrefs::Observer, public ArcNotificationSurfaceManager::Observer { public: + struct CountedAXTree { + explicit CountedAXTree(AXTreeSourceArc* ax_tree); + ~CountedAXTree(); + + uint32_t count; + std::unique_ptr<AXTreeSourceArc> tree; + }; + // Returns singleton instance for the given BrowserContext, // or nullptr if the browser |context| is not allowed to use ARC. static ArcAccessibilityHelperBridge* GetForBrowserContext( @@ -81,10 +89,15 @@ void OnNotificationSurfaceRemoved(ArcNotificationSurface* surface) override; const std::map<int32_t, std::unique_ptr<AXTreeSourceArc>>& - task_id_to_tree_for_test() { + task_id_to_tree_for_test() const { return task_id_to_tree_; } + const std::map<std::string, std::unique_ptr<CountedAXTree>>& + notification_key_to_tree_for_test() const { + return notification_key_to_tree_; + } + protected: virtual aura::Window* GetActiveWindow(); @@ -97,16 +110,15 @@ void OnActionResult(const ui::AXActionData& data, bool result) const; AXTreeSourceArc* GetOrCreateFromTaskId(int32_t task_id); - AXTreeSourceArc* CreateFromNotificationKey( - const std::string& notification_key); - AXTreeSourceArc* GetFromNotificationKey( - const std::string& notification_key) const; + AXTreeSourceArc* GetOrCreateFromNotificationKey( + const std::string& notification_key, + bool increment_counter); AXTreeSourceArc* GetFromTreeId(int32_t tree_id) const; Profile* const profile_; ArcBridgeService* const arc_bridge_service_; std::map<int32_t, std::unique_ptr<AXTreeSourceArc>> task_id_to_tree_; - std::map<std::string, std::unique_ptr<AXTreeSourceArc>> + std::map<std::string, std::unique_ptr<CountedAXTree>> notification_key_to_tree_; DISALLOW_COPY_AND_ASSIGN(ArcAccessibilityHelperBridge);
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc index ba9ac8b..71f32611a 100644 --- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc +++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -16,12 +16,19 @@ #include "components/exo/shell_surface.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/arc/notification/arc_notification_surface.h" #include "ui/aura/window.h" #include "ui/display/display.h" #include "ui/display/manager/managed_display_info.h" namespace arc { +namespace { + +const char kNotificationKey[] = "unit.test.notification"; + +} // namespace + class ArcAccessibilityHelperBridgeTest : public testing::Test { public: class TestArcAccessibilityHelperBridge : public ArcAccessibilityHelperBridge { @@ -46,6 +53,40 @@ DISALLOW_COPY_AND_ASSIGN(TestArcAccessibilityHelperBridge); }; + class ArcNotificationSurfaceTest : public ArcNotificationSurface { + public: + explicit ArcNotificationSurfaceTest(std::string notification_key) + : notification_key_(notification_key), ax_tree_id_(-1) {} + + gfx::Size GetSize() const override { return gfx::Size(); } + + aura::Window* GetWindow() const override { return nullptr; } + + aura::Window* GetContentWindow() const override { return nullptr; } + + const std::string& GetNotificationKey() const override { + return notification_key_; + } + + void Attach(views::NativeViewHost* native_view_host) override {} + + void Detach() override {} + + bool IsAttached() const override { return false; } + + views::NativeViewHost* GetAttachedHost() const override { return nullptr; } + + void FocusSurfaceWindow() override {} + + void SetAXTreeId(int32_t ax_tree_id) override { ax_tree_id_ = ax_tree_id; } + + int32_t GetAXTreeId() const override { return ax_tree_id_; } + + private: + const std::string notification_key_; + int32_t ax_tree_id_; + }; + ArcAccessibilityHelperBridgeTest() = default; void SetUp() override { @@ -153,4 +194,66 @@ ASSERT_EQ(0U, task_id_to_tree.size()); } +// Accessibility event and surface creation/removal are sent in different +// channels, mojo and wayland. Order of those events can be changed. This is the +// case where mojo events arrive earlier than surface creation/removal. +TEST_F(ArcAccessibilityHelperBridgeTest, NotificationEventArriveFirst) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + chromeos::switches::kEnableChromeVoxArcSupport); + + TestArcAccessibilityHelperBridge* helper_bridge = + accessibility_helper_bridge(); + + const auto& notification_key_to_tree_ = + helper_bridge->notification_key_to_tree_for_test(); + ASSERT_EQ(0U, notification_key_to_tree_.size()); + + // Dispatch an accessibility event for creation of notification. + auto event1 = arc::mojom::AccessibilityEventData::New(); + event1->event_type = arc::mojom::AccessibilityEventType::WINDOW_STATE_CHANGED; + event1->notification_key = base::make_optional<std::string>(kNotificationKey); + event1->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New()); + helper_bridge->OnAccessibilityEvent(event1.Clone()); + + EXPECT_EQ(1U, notification_key_to_tree_.size()); + + // Add notification surface for the first event. + ArcNotificationSurfaceTest test_surface(kNotificationKey); + helper_bridge->OnNotificationSurfaceAdded(&test_surface); + + // Confirm that axtree id is set to the surface. + auto it = notification_key_to_tree_.find(kNotificationKey); + EXPECT_NE(notification_key_to_tree_.end(), it); + AXTreeSourceArc* tree = it->second->tree.get(); + ui::AXTreeData tree_data; + tree->GetTreeData(&tree_data); + EXPECT_EQ(tree_data.tree_id, test_surface.GetAXTreeId()); + + // Dispatch second event for notification creation before surface is removed. + auto event2 = arc::mojom::AccessibilityEventData::New(); + event2->event_type = arc::mojom::AccessibilityEventType::WINDOW_STATE_CHANGED; + event2->notification_key = base::make_optional<std::string>(kNotificationKey); + event2->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New()); + helper_bridge->OnAccessibilityEvent(event2.Clone()); + + EXPECT_EQ(1U, notification_key_to_tree_.size()); + + // Remove notification surface for the first event. + helper_bridge->OnNotificationSurfaceRemoved(&test_surface); + + // Tree shouldn't be removed as a surface for the second event will come. + EXPECT_EQ(1U, notification_key_to_tree_.size()); + + // Add notification surface for the second event, and confirm that axtree id + // is set. + ArcNotificationSurfaceTest test_surface_2(kNotificationKey); + helper_bridge->OnNotificationSurfaceAdded(&test_surface_2); + EXPECT_EQ(tree_data.tree_id, test_surface_2.GetAXTreeId()); + + // Remove notification surface for the second event, and confirm that tree is + // deleted. + helper_bridge->OnNotificationSurfaceRemoved(&test_surface_2); + EXPECT_EQ(0U, notification_key_to_tree_.size()); +} + } // namespace arc
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 58be25e9..130b963 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -323,8 +323,8 @@ return; } else if (status == PrefService::INITIALIZATION_STATUS_WAITING) { GetLocalState()->AddPrefInitObserver( - base::Bind(&WizardController::OnLocalStateInitialized, - weak_factory_.GetWeakPtr())); + base::BindOnce(&WizardController::OnLocalStateInitialized, + weak_factory_.GetWeakPtr())); } }
diff --git a/chrome/browser/chromeos/system/timezone_resolver_manager.cc b/chrome/browser/chromeos/system/timezone_resolver_manager.cc index 6dd464d..a5829b12 100644 --- a/chrome/browser/chromeos/system/timezone_resolver_manager.cc +++ b/chrome/browser/chromeos/system/timezone_resolver_manager.cc
@@ -128,8 +128,8 @@ g_browser_process->local_state()->GetInitializationStatus() == PrefService::INITIALIZATION_STATUS_SUCCESS; g_browser_process->local_state()->AddPrefInitObserver( - base::Bind(&TimeZoneResolverManager::OnLocalStateInitialized, - weak_factory_.GetWeakPtr())); + base::BindOnce(&TimeZoneResolverManager::OnLocalStateInitialized, + weak_factory_.GetWeakPtr())); local_state_pref_change_registrar_.Init(g_browser_process->local_state()); local_state_pref_change_registrar_.Add(
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc index cbccab4..bc8703f 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
@@ -7,8 +7,11 @@ #include <stddef.h> #include "base/memory/ptr_util.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_service.h" +#include "crypto/sha2.h" #include "extensions/common/error_utils.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" @@ -19,6 +22,31 @@ "https://www.gstatic.com/securitykey/origins.json", "https://www.gstatic.com/securitykey/a/google.com/origins.json"}; +// ContainsAppIdByHash returns true iff the SHA-256 hash of one of the +// elements of |list| equals |hash|. +bool ContainsAppIdByHash(const base::ListValue& list, + const std::vector<char>& hash) { + if (hash.size() != crypto::kSHA256Length) { + return false; + } + + for (const auto& i : list) { + const std::string& s = i.GetString(); + if (s.find('/') == std::string::npos) { + // No slashes mean that this is a webauthn RP ID, not a U2F AppID. + continue; + } + + if (crypto::SHA256HashString(s).compare(0, crypto::kSHA256Length, + hash.data(), + crypto::kSHA256Length) == 0) { + return true; + } + } + + return false; +} + } // namespace namespace extensions { @@ -87,24 +115,27 @@ return RespondNow(OneArgument(base::MakeUnique<base::Value>(false))); } -// TODO(agl/mab): remove special casing for individual attestation in -// Javascript in favour of an enterprise policy, which can be accessed like -// this: -// -// #include "chrome/browser/profiles/profile.h" -// #include "components/prefs/pref_service.h" -// -// Profile* const profile = Profile::FromBrowserContext(browser_context()); -// const PrefService* const prefs = profile->GetPrefs(); -// const base::ListValue* const permit_attestation = -// prefs->GetList(prefs::kSecurityKeyPermitAttestation); -// -// for (size_t i = 0; i < permit_attestation->GetSize(); i++) { -// std::string value; -// if (!permit_attestation->GetString(i, &value)) { -// continue; -// } -// } +CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction:: + CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction() + : chrome_details_(this) {} + +ExtensionFunction::ResponseAction +CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction::Run() { + std::unique_ptr<cryptotoken_private::IsAppIdHashInEnterpriseContext::Params> + params( + cryptotoken_private::IsAppIdHashInEnterpriseContext::Params::Create( + *args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + Profile* const profile = Profile::FromBrowserContext(browser_context()); + const PrefService* const prefs = profile->GetPrefs(); + const base::ListValue* const permit_attestation = + prefs->GetList(prefs::kSecurityKeyPermitAttestation); + + return RespondNow(ArgumentList( + cryptotoken_private::IsAppIdHashInEnterpriseContext::Results::Create( + ContainsAppIdByHash(*permit_attestation, params->app_id_hash)))); +} } // namespace api } // namespace extensions
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.h b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.h index 55654b13..2642284 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.h +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.h
@@ -7,7 +7,9 @@ #include <memory> #include <string> +#include <vector> +#include "base/values.h" #include "chrome/browser/extensions/chrome_extension_function_details.h" #include "chrome/common/extensions/api/cryptotoken_private.h" #include "extensions/browser/extension_function.h" @@ -38,6 +40,22 @@ ChromeExtensionFunctionDetails chrome_details_; }; +class CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction + : public UIThreadExtensionFunction { + public: + CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction(); + DECLARE_EXTENSION_FUNCTION( + "cryptotokenPrivate.isAppIdHashInEnterpriseContext", + CRYPTOTOKENPRIVATE_ISAPPIDHASHINENTERPRISECONTEXT) + + protected: + ~CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction() override {} + ResponseAction Run() override; + + private: + ChromeExtensionFunctionDetails chrome_details_; +}; + } // namespace api } // namespace extensions
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc index 937d4010..e198d041 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
@@ -12,8 +12,12 @@ #include "chrome/browser/extensions/extension_api_unittest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/common/pref_names.h" +#include "crypto/sha2.h" #include "testing/gtest/include/gtest/gtest.h" +using crypto::SHA256HashString; + namespace extensions { namespace { @@ -30,64 +34,124 @@ UIThreadExtensionFunction* function, bool* result) { const base::ListValue* result_list = function->GetResultList(); if (!result_list) { - LOG(ERROR) << "Function has no result list."; + ADD_FAILURE() << "Function has no result list."; return false; } if (result_list->GetSize() != 1u) { - LOG(ERROR) << "Invalid number of results."; + ADD_FAILURE() << "Invalid number of results."; return false; } if (!result_list->GetBoolean(0, result)) { - LOG(ERROR) << "Result is not boolean."; + ADD_FAILURE() << "Result is not boolean."; return false; } return true; } bool GetCanOriginAssertAppIdResult(const std::string& origin, - const std::string& appId) { + const std::string& app_id, + bool *out_result) { scoped_refptr<api::CryptotokenPrivateCanOriginAssertAppIdFunction> function( new api::CryptotokenPrivateCanOriginAssertAppIdFunction()); function->set_has_callback(true); std::unique_ptr<base::ListValue> args(new base::ListValue); args->AppendString(origin); - args->AppendString(appId); + args->AppendString(app_id); - extension_function_test_utils::RunFunction( - function.get(), std::move(args), browser(), - extension_function_test_utils::NONE); + if (!extension_function_test_utils::RunFunction( + function.get(), std::move(args), browser(), + extension_function_test_utils::NONE)) { + return false; + } - bool result; - GetSingleBooleanResult(function.get(), &result); - return result; + return GetSingleBooleanResult(function.get(), out_result); + } + + bool GetAppIdHashInEnterpriseContext(const std::string& app_id, + bool* out_result) { + scoped_refptr<api::CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction> + function( + new api:: + CryptotokenPrivateIsAppIdHashInEnterpriseContextFunction()); + function->set_has_callback(true); + + auto args = std::make_unique<base::Value>(base::Value::Type::LIST); + args->GetList().emplace_back( + base::Value::BlobStorage(app_id.begin(), app_id.end())); + + if (!extension_function_test_utils::RunFunction( + function.get(), base::ListValue::From(std::move(args)), browser(), + extension_function_test_utils::NONE)) { + return false; + } + + return GetSingleBooleanResult(function.get(), out_result); } }; TEST_F(CryptoTokenPrivateApiTest, CanOriginAssertAppId) { std::string origin1("https://www.example.com"); - EXPECT_TRUE(GetCanOriginAssertAppIdResult(origin1, origin1)); + bool result; + ASSERT_TRUE(GetCanOriginAssertAppIdResult(origin1, origin1, &result)); + EXPECT_TRUE(result); std::string same_origin_appid("https://www.example.com/appId"); - EXPECT_TRUE(GetCanOriginAssertAppIdResult(origin1, same_origin_appid)); + ASSERT_TRUE( + GetCanOriginAssertAppIdResult(origin1, same_origin_appid, &result)); + EXPECT_TRUE(result); std::string same_etld_plus_one_appid("https://appid.example.com/appId"); - EXPECT_TRUE(GetCanOriginAssertAppIdResult(origin1, same_etld_plus_one_appid)); + ASSERT_TRUE(GetCanOriginAssertAppIdResult(origin1, same_etld_plus_one_appid, + &result)); + EXPECT_TRUE(result); std::string different_etld_plus_one_appid("https://www.different.com/appId"); - EXPECT_FALSE(GetCanOriginAssertAppIdResult(origin1, - different_etld_plus_one_appid)); + ASSERT_TRUE(GetCanOriginAssertAppIdResult( + origin1, different_etld_plus_one_appid, &result)); + EXPECT_FALSE(result); // For legacy purposes, google.com is allowed to use certain appIds hosted at // gstatic.com. // TODO(juanlang): remove once the legacy constraints are removed. std::string google_origin("https://accounts.google.com"); std::string gstatic_appid("https://www.gstatic.com/securitykey/origins.json"); - EXPECT_TRUE(GetCanOriginAssertAppIdResult(google_origin, gstatic_appid)); + ASSERT_TRUE( + GetCanOriginAssertAppIdResult(google_origin, gstatic_appid, &result)); + EXPECT_TRUE(result); // Not all gstatic urls are allowed, just those specifically whitelisted. std::string gstatic_otherurl("https://www.gstatic.com/foobar"); - EXPECT_FALSE(GetCanOriginAssertAppIdResult(google_origin, gstatic_otherurl)); + ASSERT_TRUE( + GetCanOriginAssertAppIdResult(google_origin, gstatic_otherurl, &result)); + EXPECT_FALSE(result); +} + +TEST_F(CryptoTokenPrivateApiTest, IsAppIdHashInEnterpriseContext) { + const std::string example_com("https://example.com/"); + const std::string example_com_hash(SHA256HashString(example_com)); + const std::string rp_id_hash(SHA256HashString("example.com")); + const std::string foo_com_hash(SHA256HashString("https://foo.com/")); + + bool result; + ASSERT_TRUE(GetAppIdHashInEnterpriseContext(example_com_hash, &result)); + EXPECT_FALSE(result); + ASSERT_TRUE(GetAppIdHashInEnterpriseContext(foo_com_hash, &result)); + EXPECT_FALSE(result); + ASSERT_TRUE(GetAppIdHashInEnterpriseContext(rp_id_hash, &result)); + EXPECT_FALSE(result); + + base::Value::ListStorage permitted_list; + permitted_list.emplace_back(example_com); + profile()->GetPrefs()->Set(prefs::kSecurityKeyPermitAttestation, + base::Value(permitted_list)); + + ASSERT_TRUE(GetAppIdHashInEnterpriseContext(example_com_hash, &result)); + EXPECT_TRUE(result); + ASSERT_TRUE(GetAppIdHashInEnterpriseContext(foo_com_hash, &result)); + EXPECT_FALSE(result); + ASSERT_TRUE(GetAppIdHashInEnterpriseContext(rp_id_hash, &result)); + EXPECT_FALSE(result); } } // namespace
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc index 7a0c2a93..8ec3206c 100644 --- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc +++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -281,7 +281,7 @@ device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMinProperty, base::Value("test_min")); device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kModelIDProperty, + shill::kModelIdProperty, base::Value("test_model_id")); device_test_->SetSimLocked(kCellularDevicePath, false);
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index aa40a61..e533e435 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc
@@ -525,7 +525,7 @@ // (successfully or not). Note that we can use base::Unretained // because the PrefService is owned by this class and lives on // the same thread. - prefs_->AddPrefInitObserver(base::Bind( + prefs_->AddPrefInitObserver(base::BindOnce( &ProfileImpl::OnPrefsLoaded, base::Unretained(this), create_mode)); } else { // Prefs were loaded synchronously so we can continue directly.
diff --git a/chrome/browser/resources/cryptotoken/usbenrollhandler.js b/chrome/browser/resources/cryptotoken/usbenrollhandler.js index 2d53b07..3b2f2c6 100644 --- a/chrome/browser/resources/cryptotoken/usbenrollhandler.js +++ b/chrome/browser/resources/cryptotoken/usbenrollhandler.js
@@ -207,12 +207,40 @@ this.removeWrongVersionGnubby_(gnubby); return; } + + var appIdHashBase64 = challenge['appIdHash']; + if (DEVICE_FACTORY_REGISTRY.getIndividualAttestation() + .requestIndividualAttestation(appIdHashBase64)) { + this.tryEnrollComplete_(gnubby, version, true); + return; + } + + if (!chrome.cryptotokenPrivate) { + this.tryEnrollComplete_(gnubby, version, false); + return; + } + + chrome.cryptotokenPrivate.isAppIdHashInEnterpriseContext( + decodeWebSafeBase64ToArray(appIdHashBase64), + this.tryEnrollComplete_.bind(this, gnubby, version)); +}; + +/** + * Attempts enrolling a particular gnubby with a challenge of the appropriate + * version. + * @param {Gnubby} gnubby Gnubby instance + * @param {string} version Protocol version + * @param {boolean} individualAttest whether to send the individual-attestation + * signal to the token. + * @private + */ +UsbEnrollHandler.prototype.tryEnrollComplete_ = function( + gnubby, version, individualAttest) { + var challenge = this.getChallengeOfVersion_(version); var challengeValue = B64_decode(challenge['challengeHash']); - var appIdHash = challenge['appIdHash']; - var individualAttest = DEVICE_FACTORY_REGISTRY.getIndividualAttestation() - .requestIndividualAttestation(appIdHash); + gnubby.enroll( - challengeValue, B64_decode(appIdHash), + challengeValue, B64_decode(challenge['appIdHash']), this.enrollCallback_.bind(this, gnubby, version), individualAttest); };
diff --git a/chrome/browser/resources/md_extensions/error_page.html b/chrome/browser/resources/md_extensions/error_page.html index d574676..847dc02 100644 --- a/chrome/browser/resources/md_extensions/error_page.html +++ b/chrome/browser/resources/md_extensions/error_page.html
@@ -103,35 +103,35 @@ word-break: break-all; } - #devtools-controls { + .devtools-controls { padding: 0 var(--cr-section-padding); } - #stack-trace-heading { + .details-heading { @apply(--cr-title-text); align-items: center; display: flex; height: var(--cr-section-min-height); } - #stack-trace-container { + .stack-trace-container { list-style: none; margin-top: 0; padding: 0; } - #stack-trace-container li { + .stack-trace-container li { cursor: pointer; font-family: monospace; padding: 4px; } - #stack-trace-container li.selected, - #stack-trace-container li:hover { + .stack-trace-container li.selected, + .stack-trace-container li:hover { background: var(--google-blue-100); } - #dev-tool-button { + .dev-tool-button { margin-bottom: 20px; max-width: 300px; } @@ -207,11 +207,18 @@ </div> <template is="dom-if" if="[[computeIsRuntimeError_(item)]]"> <iron-collapse opened="[[isOpened_(index, selectedEntry_)]]"> - <div id="devtools-controls"> - <div id="stack-trace-heading"> + <div class="devtools-controls"> + <div class="details-heading"> + $i18n{errorContext} + </div> + <span class="context-url"> + [[getContextUrl_( + item, '$i18nPolymer{errorContextUnknown}')]] + </span> + <div class="details-heading"> $i18n{stackTrace} </div> - <ul id="stack-trace-container"> + <ul class="stack-trace-container"> <template is="dom-repeat" items="[[item.stackTrace]]"> <li on-tap="onStackFrameTap_" hidden="[[!shouldDisplayFrame_(item.url)]]" @@ -221,7 +228,7 @@ </li> </template> </ul> - <paper-button id="dev-tool-button" class="action-button" + <paper-button class="devtool-button action-button" disabled="[[!item.canInspect]]" on-tap="onDevToolButtonTap_"> $i18n{openInDevtool}
diff --git a/chrome/browser/resources/md_extensions/error_page.js b/chrome/browser/resources/md_extensions/error_page.js index 1b9ec7b..f11cb5d 100644 --- a/chrome/browser/resources/md_extensions/error_page.js +++ b/chrome/browser/resources/md_extensions/error_page.js
@@ -91,6 +91,17 @@ }, /** + * @param {!ManifestError|!RuntimeError} error + * @param {string} unknown + * @return {string} + * @private + */ + getContextUrl_: function(error, unknown) { + return error.contextUrl ? getRelativeUrl(error.contextUrl, error) : + unknown; + }, + + /** * Watches for changes to |data| in order to fetch the corresponding * file source. * @private
diff --git a/chrome/browser/resources/settings/device_page/display.html b/chrome/browser/resources/settings/device_page/display.html index a97f335..7423e99 100644 --- a/chrome/browser/resources/settings/device_page/display.html +++ b/chrome/browser/resources/settings/device_page/display.html
@@ -227,8 +227,14 @@ </div> <!-- Schedule settings --> <div class="settings-box indented"> - <div id="nightLightScheduleLabel" class="start text-area"> - $i18n{displayNightLightScheduleLabel} + <div class="start text-area"> + <div id="nightLightScheduleLabel" class="label"> + $i18n{displayNightLightScheduleLabel} + </div> + <div id="nightLightScheduleSubLabel" class="secondary label" + hidden$="[[!nightLightScheduleSubLabel_]]"> + [[nightLightScheduleSubLabel_]] + </div> </div> <settings-dropdown-menu id="nightLightScheduleTypeDropDown"
diff --git a/chrome/browser/resources/settings/device_page/display.js b/chrome/browser/resources/settings/device_page/display.js index 0d18d5e..7b863b9 100644 --- a/chrome/browser/resources/settings/device_page/display.js +++ b/chrome/browser/resources/settings/device_page/display.js
@@ -143,10 +143,14 @@ type: Boolean, value: false, }, + + /** @private */ + nightLightScheduleSubLabel_: String, }, observers: [ - 'onScheduleTypeChanged_(prefs.ash.night_light.schedule_type.*)', + 'updateNightLightScheduleSettings_(prefs.ash.night_light.schedule_type.*,' + + ' prefs.ash.night_light.enabled.*)', ], /** @private {number} Selected mode index received from chrome. */ @@ -608,10 +612,24 @@ } }, - /** @private */ - onScheduleTypeChanged_: function() { + /** + * Invoked when the status of Night Light or its schedule type are changed, in + * order to update the schedule settings, such as whether to show the custom + * schedule slider, and the schedule sub label. + * @private + */ + updateNightLightScheduleSettings_: function() { + var scheduleType = this.getPref('ash.night_light.schedule_type').value; this.shouldOpenCustomScheduleCollapse_ = - this.getPref('ash.night_light.schedule_type').value == - NightLightScheduleType.CUSTOM; + scheduleType == NightLightScheduleType.CUSTOM; + + if (scheduleType == NightLightScheduleType.SUNSET_TO_SUNRISE) { + var nightLightStatus = this.getPref('ash.night_light.enabled').value; + this.nightLightScheduleSubLabel_ = nightLightStatus ? + this.i18n('displayNightLightOffAtSunrise') : + this.i18n('displayNightLightOnAtSunset'); + } else { + this.nightLightScheduleSubLabel_ = ''; + } }, });
diff --git a/chrome/browser/resources/settings/device_page/night_light_slider.html b/chrome/browser/resources/settings/device_page/night_light_slider.html index 641129f..d6dfdfa 100644 --- a/chrome/browser/resources/settings/device_page/night_light_slider.html +++ b/chrome/browser/resources/settings/device_page/night_light_slider.html
@@ -36,6 +36,7 @@ margin-top: -15px; position: absolute; width: 32px; + z-index: 3; } .knob:focus { @@ -47,8 +48,7 @@ border-radius: 6px; height: 12px; left: 0; - margin-left: 10px; - margin-top: 10px; + margin: 10px; position: absolute; width: 12px; z-index: 3;
diff --git a/chrome/browser/resources/settings/device_page/night_light_slider.js b/chrome/browser/resources/settings/device_page/night_light_slider.js index 239a9f42..c47496e 100644 --- a/chrome/browser/resources/settings/device_page/night_light_slider.js +++ b/chrome/browser/resources/settings/device_page/night_light_slider.js
@@ -83,6 +83,13 @@ this.isRTL_ = window.getComputedStyle(this).direction == 'rtl'; + this.$.sliderContainer.addEventListener('contextmenu', function(e) { + // Prevent the context menu from interfering with dragging the knobs using + // touch. + e.preventDefault(); + return false; + }); + this.async(function() { // This is needed to make sure that the positions of the knobs and their // label bubbles are correctly updated when the display settings page is
diff --git a/chrome/browser/sessions/better_session_restore_browsertest.cc b/chrome/browser/sessions/better_session_restore_browsertest.cc index 8934004..24946e0 100644 --- a/chrome/browser/sessions/better_session_restore_browsertest.cc +++ b/chrome/browser/sessions/better_session_restore_browsertest.cc
@@ -168,20 +168,18 @@ title_error_write_failed_(base::ASCIIToUTF16("ERROR_WRITE_FAILED")), title_error_empty_(base::ASCIIToUTF16("ERROR_EMPTY")) { // Set up the URL request filtering. - std::vector<std::string> test_files; - base::FilePath test_file_dir; - test_files.push_back("common.js"); - test_files.push_back("cookies.html"); - test_files.push_back("local_storage.html"); - test_files.push_back("post.html"); - test_files.push_back("post_with_password.html"); - test_files.push_back("session_cookies.html"); - test_files.push_back("session_storage.html"); - test_files.push_back("subdomain_cookies.html"); + test_files_.push_back("common.js"); + test_files_.push_back("cookies.html"); + test_files_.push_back("local_storage.html"); + test_files_.push_back("post.html"); + test_files_.push_back("post_with_password.html"); + test_files_.push_back("session_cookies.html"); + test_files_.push_back("session_storage.html"); + test_files_.push_back("subdomain_cookies.html"); - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &test_file_dir)); - test_file_dir = - test_file_dir.AppendASCII("chrome/test/data").AppendASCII(test_path_); + CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &test_file_dir_)); + test_file_dir_ = + test_file_dir_.AppendASCII("chrome/test/data").AppendASCII(test_path_); // We are adding a URLLoaderInterceptor here, instead of in // SetUpOnMainThread(), because during a session restore the restored tab @@ -193,11 +191,11 @@ [&](content::URLLoaderInterceptor::RequestParams* params) { std::string path = params->url_request.url.path(); std::string path_prefix = std::string("/") + test_path_; - for (auto& it : test_files) { + for (auto& it : test_files_) { std::string file = path_prefix + it; if (path == file) { base::ScopedAllowBlockingForTesting allow_io; - base::FilePath file_path = test_file_dir.AppendASCII(it); + base::FilePath file_path = test_file_dir_.AppendASCII(it); std::string contents; CHECK(base::ReadFileToString(file_path, &contents)); @@ -234,9 +232,9 @@ return; } - for (std::vector<std::string>::const_iterator it = test_files.begin(); - it != test_files.end(); ++it) { - base::FilePath path = test_file_dir.AppendASCII(*it); + for (std::vector<std::string>::const_iterator it = test_files_.begin(); + it != test_files_.end(); ++it) { + base::FilePath path = test_file_dir_.AppendASCII(*it); std::string contents; CHECK(base::ReadFileToString(path, &contents)); net::URLRequestFilter::GetInstance()->AddUrlInterceptor( @@ -429,6 +427,8 @@ private: std::string last_upload_bytes_; const std::string fake_server_address_; + std::vector<std::string> test_files_; + base::FilePath test_file_dir_; const std::string test_path_; const base::string16 title_pass_; const base::string16 title_storing_;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 9b1303d..b6c9ad4a 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -2518,6 +2518,8 @@ "views/tabs/window_finder_win.cc", "views/try_chrome_dialog_win/arrow_border.cc", "views/try_chrome_dialog_win/arrow_border.h", + "views/try_chrome_dialog_win/button_layout.cc", + "views/try_chrome_dialog_win/button_layout.h", "views/try_chrome_dialog_win/try_chrome_dialog.cc", "views/try_chrome_dialog_win/try_chrome_dialog.h", "views/uninstall_view.cc",
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/button_layout.cc b/chrome/browser/ui/views/try_chrome_dialog_win/button_layout.cc new file mode 100644 index 0000000..febe172 --- /dev/null +++ b/chrome/browser/ui/views/try_chrome_dialog_win/button_layout.cc
@@ -0,0 +1,100 @@ +// Copyright 2017 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 "chrome/browser/ui/views/try_chrome_dialog_win/button_layout.h" + +#include "base/logging.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/view.h" + +// static +ButtonLayout* ButtonLayout::CreateAndInstall(views::View* view, + int view_width) { + ButtonLayout* layout = new ButtonLayout(view_width); + view->SetLayoutManager(layout); + return layout; +} + +ButtonLayout::~ButtonLayout() = default; + +void ButtonLayout::Layout(views::View* host) { + const gfx::Size& host_size = host->bounds().size(); + + // Layout of the host within its parent must size it based on the |view_width| + // given to this layout manager at creation. If it happens to be different, + // the buttons will be sized and positioned based on the host's true size. + // This will result in either stretching or compressing the buttons, and may + // lead to elision of their text. + DCHECK_EQ(host_size.width(), view_width_); + + const gfx::Size max_child_size = GetMaxChildPreferredSize(host); + const bool use_wide_buttons = + UseWideButtons(host_size.width(), max_child_size.width()); + const bool has_two_buttons = HasTwoButtons(host); + + // The buttons are all equal-sized. + gfx::Size button_size(0, max_child_size.height()); + if (use_wide_buttons) + button_size.set_width(host_size.width()); + else + button_size.set_width((host_size.width() - kPaddingBetweenButtons) / 2); + + if (!use_wide_buttons) { + // The offset of the right-side narrow button. + int right_x = button_size.width() + kPaddingBetweenButtons; + if (has_two_buttons) { + host->child_at(0)->SetBoundsRect({{0, 0}, button_size}); + host->child_at(1)->SetBoundsRect({{right_x, 0}, button_size}); + } else { + // If there is only one narrow button, position it on the right. + host->child_at(0)->SetBoundsRect({{right_x, 0}, button_size}); + } + } else { + host->child_at(0)->SetBoundsRect({{0, 0}, button_size}); + if (has_two_buttons) { + int bottom_y = button_size.height() + kPaddingBetweenButtons; + host->child_at(1)->SetBoundsRect({{0, bottom_y}, button_size}); + } + } +} + +gfx::Size ButtonLayout::GetPreferredSize(const views::View* host) const { + const gfx::Size max_child_size = GetMaxChildPreferredSize(host); + + // |view_width_| is a hard limit; the buttons will be sized and positioned to + // fill it. + if (HasTwoButtons(host) && + UseWideButtons(view_width_, max_child_size.width())) { + // Two rows of equal height with padding between them. + return {view_width_, max_child_size.height() * 2 + kPaddingBetweenButtons}; + } + + // Only one button or the widest of two is sufficiently narrow, so only one + // row is needed. + return {view_width_, max_child_size.height()}; +} + +ButtonLayout::ButtonLayout(int view_width) : view_width_(view_width) {} + +// static +bool ButtonLayout::HasTwoButtons(const views::View* host) { + const int child_count = host->child_count(); + DCHECK_GE(child_count, 1); + DCHECK_LE(child_count, 2); + return child_count == 2; +} + +// static +gfx::Size ButtonLayout::GetMaxChildPreferredSize(const views::View* host) { + const bool has_two_buttons = HasTwoButtons(host); + gfx::Size max_child_size(host->child_at(0)->GetPreferredSize()); + if (has_two_buttons) + max_child_size.SetToMax(host->child_at(1)->GetPreferredSize()); + return max_child_size; +} + +// static +bool ButtonLayout::UseWideButtons(int host_width, int max_child_width) { + return max_child_width > (host_width - kPaddingBetweenButtons) / 2; +}
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/button_layout.h b/chrome/browser/ui/views/try_chrome_dialog_win/button_layout.h new file mode 100644 index 0000000..cd52355 --- /dev/null +++ b/chrome/browser/ui/views/try_chrome_dialog_win/button_layout.h
@@ -0,0 +1,71 @@ +// Copyright 2017 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 CHROME_BROWSER_UI_VIEWS_TRY_CHROME_DIALOG_WIN_BUTTON_LAYOUT_H_ +#define CHROME_BROWSER_UI_VIEWS_TRY_CHROME_DIALOG_WIN_BUTTON_LAYOUT_H_ + +#include "base/macros.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/layout/layout_manager.h" + +namespace views { +class View; +} + +// A LayoutManager that positions the TryChromeDialog's action button(s) based +// on the number and their width requirements. The possible layouts are: +// +// One narrow button: | [button 1]| +// +// Two narrow buttons: |[button 1] [button 2]| +// +// One wide button: |[ button 1 ]| +// +// Two wide buttons: |[ button 1 ]| +// |[ button 2 ]| +// +// An instance of this layout manager is expected to be installed on a View to +// which only the one or two action buttons are added. +class ButtonLayout : public views::LayoutManager { + public: + // Returns a new instance that has been made the manager of |view|. The + // dialog's one or two action button(s) must be the only children of |view|. + // |view_width| is the desired width of the view, which controls the width of + // the individual buttons as above. The layout manager of |view|'s parent must + // respect this width (by, for example, using SizeType::USE_PREF for the + // hosting column's size_type if it uses GridLayout). + static ButtonLayout* CreateAndInstall(views::View* view, int view_width); + + ~ButtonLayout() override; + + protected: + // views::LayoutManager: + void Layout(views::View* host) override; + gfx::Size GetPreferredSize(const views::View* host) const override; + + private: + friend class ButtonLayoutTest; + + // The horizontal or vertical space between two buttons. + enum { kPaddingBetweenButtons = 4 }; + + explicit ButtonLayout(int view_width); + + // Returns true if |host| contains two buttons, or false if it contains only + // one. + static bool HasTwoButtons(const views::View* host); + + // Returns the preferred size of the largest child of |host|. + static gfx::Size GetMaxChildPreferredSize(const views::View* host); + + // Returns true if wide buttons must be used based on the given widths. + static bool UseWideButtons(int host_width, int max_child_width); + + // The desired width of the view. + const int view_width_; + + DISALLOW_COPY_AND_ASSIGN(ButtonLayout); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_TRY_CHROME_DIALOG_WIN_BUTTON_LAYOUT_H_
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/button_layout_unittest.cc b/chrome/browser/ui/views/try_chrome_dialog_win/button_layout_unittest.cc new file mode 100644 index 0000000..fd851bb --- /dev/null +++ b/chrome/browser/ui/views/try_chrome_dialog_win/button_layout_unittest.cc
@@ -0,0 +1,198 @@ +// Copyright 2017 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 "chrome/browser/ui/views/try_chrome_dialog_win/button_layout.h" + +#include <algorithm> +#include <memory> + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/view.h" + +class ButtonLayoutTest + : public ::testing::TestWithParam<::testing::tuple<int, int>> { + private: + enum { + // The width of an imaginary host view in the test. + kFixedHostWidth = 100, + }; + + public: + // Various button widths to be tested. + enum { + // Magic width meaning no button at all. + kNoButton = 0, + + // Some small "narrow" button that fits within half of the test's host. + kNarrowButtonMin = 8, + + // The largest "narrow" button that could fit within the test's host. + kNarrowButtonMax = + (kFixedHostWidth - ButtonLayout::kPaddingBetweenButtons) / 2, + + // Some mid-sized "narrow" button that could fit within the test's host. + kNarrowButtonMid = (kNarrowButtonMin + kNarrowButtonMax) / 2, + + // The least wide "wide" button that could fit within the test's host. + kWideButtonMin = kNarrowButtonMax + 1, + + // The largest "wide" button that could fit within the test's host. + kWideButtonMax = kFixedHostWidth, + + // Some mid-sized "wide" button that could fit within the test's host. + kWideButtonMid = (kWideButtonMin + kWideButtonMax) / 2, + + // A button that is too big to fit within the host. + kSuperSizedButton = kWideButtonMax + 1, + }; + + protected: + ButtonLayoutTest() + : layout_(ButtonLayout::CreateAndInstall(&host_, kFixedHostWidth)), + button_1_width_(::testing::get<0>(GetParam())), + button_2_width_(::testing::get<1>(GetParam())) {} + + void SetUp() override { + ASSERT_NE(0, button_1_width_) << "Button 1 must always be present."; + } + + views::View* host() { return &host_; } + views::LayoutManager* layout() { return layout_; } + bool has_two_buttons() const { return button_2_width_; } + + // Adds one or two child views of widths specified by the test parameters. + void AddChildViews() { + auto view = std::make_unique<views::View>(); + view->SetPreferredSize({button_1_width_, kButtonHeight}); + host()->AddChildView(view.release()); + + if (has_two_buttons()) { + view = std::make_unique<views::View>(); + view->SetPreferredSize({button_2_width_, kButtonHeight}); + host()->AddChildView(view.release()); + } + } + + // Returns the (fixed) width of the host. + int GetExpectedWidth() const { return kFixedHostWidth; } + + // Returns the height of the host, which is dependent on the number and widths + // of the children. + int GetExpectedHeight() const { + if (!has_two_buttons()) + return kButtonHeight; + if (button_1_width_ <= kNarrowButtonMax && + button_2_width_ <= kNarrowButtonMax) { + return kButtonHeight; + } + return 2 * kButtonHeight + ButtonLayout::kPaddingBetweenButtons; + } + + // Returns the expected bounding rectangle for |child_number|. + gfx::Rect GetExpectedButtonBounds(int child_number) const { + gfx::Rect bounds; + + // Width is determined by the max of the two buttons being bigger than + // the max narrow button. + if (std::max(button_1_width_, button_2_width_) > kNarrowButtonMax) + bounds.set_width(kWideButtonMax); + else + bounds.set_width(kNarrowButtonMax); + + // All buttons have the same height. + bounds.set_height(kButtonHeight); + + // Position is based on which button we're talking about. + switch (child_number) { + case 1: + // Offset button 1 if there's only one button and it's narrow. + if (!has_two_buttons() && bounds.width() == kNarrowButtonMax) + bounds.set_x(kRightButtonXOffset); + break; + case 2: + // Offset button 2 horizontally if the buttons are narrow; vertically, + // otherwise. + if (bounds.width() == kNarrowButtonMax) + bounds.set_x(kRightButtonXOffset); + else + bounds.set_y(kBottomButtonYOffset); + break; + default: + ADD_FAILURE() << "child_number out of bounds"; + return gfx::Rect(); + } + + return bounds; + } + + // Expects that the bounds of |view| are equal to |bounds|. + void ExpectViewBoundsEquals(const views::View* view, + const gfx::Rect& bounds) { + const gfx::Rect& child_bounds = view->bounds(); + EXPECT_EQ(child_bounds.x(), bounds.x()); + EXPECT_EQ(child_bounds.y(), bounds.y()); + EXPECT_EQ(child_bounds.width(), bounds.width()); + EXPECT_EQ(child_bounds.height(), bounds.height()); + } + + private: + enum { + // The height of an imaginary button in the test. + kButtonHeight = 20, + + // The horizontal offset of the right-hand button for narrow button layouts. + kRightButtonXOffset = + kNarrowButtonMax + ButtonLayout::kPaddingBetweenButtons, + + // The vertical offset of the lower button for wide button layouts. + kBottomButtonYOffset = kButtonHeight + ButtonLayout::kPaddingBetweenButtons, + }; + + views::View host_; + ButtonLayout* const layout_; // Owned by |host_|. + const int button_1_width_; + const int button_2_width_; + + DISALLOW_COPY_AND_ASSIGN(ButtonLayoutTest); +}; + +TEST_P(ButtonLayoutTest, GetPreferredSize) { + AddChildViews(); + const gfx::Size preferred_size = layout()->GetPreferredSize(host()); + EXPECT_EQ(preferred_size.width(), GetExpectedWidth()); + EXPECT_EQ(preferred_size.height(), GetExpectedHeight()); +} + +TEST_P(ButtonLayoutTest, Layout) { + AddChildViews(); + host()->SetBounds(0, 0, GetExpectedWidth(), GetExpectedHeight()); + layout()->Layout(host()); + + ExpectViewBoundsEquals(host()->child_at(0), GetExpectedButtonBounds(1)); + if (has_two_buttons()) + ExpectViewBoundsEquals(host()->child_at(1), GetExpectedButtonBounds(2)); +} + +// Test all combinations of one or two buttons at many sizes. +INSTANTIATE_TEST_CASE_P( + , + ButtonLayoutTest, + ::testing::Combine(::testing::Values(ButtonLayoutTest::kNarrowButtonMin, + ButtonLayoutTest::kNarrowButtonMid, + ButtonLayoutTest::kNarrowButtonMax, + ButtonLayoutTest::kWideButtonMin, + ButtonLayoutTest::kWideButtonMid, + ButtonLayoutTest::kWideButtonMax, + ButtonLayoutTest::kSuperSizedButton), + ::testing::Values(ButtonLayoutTest::kNoButton, + ButtonLayoutTest::kNarrowButtonMin, + ButtonLayoutTest::kNarrowButtonMid, + ButtonLayoutTest::kNarrowButtonMax, + ButtonLayoutTest::kWideButtonMin, + ButtonLayoutTest::kWideButtonMid, + ButtonLayoutTest::kWideButtonMax, + ButtonLayoutTest::kSuperSizedButton)));
diff --git a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc index ae1d4a92..8069db6 100644 --- a/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc +++ b/chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.cc
@@ -17,6 +17,7 @@ #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/ui/views/harmony/chrome_typography.h" #include "chrome/browser/ui/views/try_chrome_dialog_win/arrow_border.h" +#include "chrome/browser/ui/views/try_chrome_dialog_win/button_layout.h" #include "chrome/browser/win/taskbar_icon_finder.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" @@ -1036,58 +1037,53 @@ // Padding around the left, top, and right of the logo. static constexpr int kLogoPadding = 10; static constexpr int kCloseButtonWidth = 24; + static constexpr int kCloseButtonTopPadding = 6; static constexpr int kCloseButtonRightPadding = 5; - static constexpr int kSpacingAfterHeadingHorizontal = - 40 - kCloseButtonWidth - kCloseButtonRightPadding; + static constexpr int kSpacingAfterHeadingHorizontal = 40; + static constexpr int kSpacingHeadingToClose = kSpacingAfterHeadingHorizontal - + kCloseButtonWidth - + kCloseButtonRightPadding; // Padding around all sides of the text buttons (but not between them). static constexpr int kTextButtonPadding = 12; - static constexpr int kPaddingBetweenButtons = 4; - // First row: [pad][logo][pad][text][pad][close button]. + // First two rows: [pad][logo][pad][text][pad][close button]. + // Only the close button is in the first row, spanning both. The logo and main + // header are in the second row. + const int kLabelWidth = kToastWidth - kLogoPadding - logo_size.width() - + kLogoPadding - kSpacingAfterHeadingHorizontal; columns = layout->AddColumnSet(0); columns->AddPaddingColumn(0, kLogoPadding - kBorderThickness); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, views::GridLayout::FIXED, logo_size.width(), logo_size.height()); columns->AddPaddingColumn(0, kLogoPadding); - columns->AddColumn(views::GridLayout::FILL, views::GridLayout::LEADING, 1, - views::GridLayout::USE_PREF, 0, 0); - columns->AddPaddingColumn(0, kSpacingAfterHeadingHorizontal); - columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 0, + columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, + views::GridLayout::FIXED, kLabelWidth, 0); + columns->AddPaddingColumn(0, kSpacingHeadingToClose); + columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, views::GridLayout::USE_PREF, 0, 0); columns->AddPaddingColumn(0, kCloseButtonRightPadding - kBorderThickness); - const int logo_padding = logo_size.width() + kLogoPadding; - // Optional second row: [pad][text]. + // Optional third row: [pad][text]. + const int logo_padding = logo_size.width() + kLogoPadding; columns = layout->AddColumnSet(1); columns->AddPaddingColumn(0, kLogoPadding - kBorderThickness + logo_padding); - columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, - views::GridLayout::USE_PREF, 0, 0); + columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, + views::GridLayout::FIXED, kLabelWidth, 0); - // Third row: [pad][optional button][pad][button]. + // Fourth row: [pad][buttons][pad]. columns = layout->AddColumnSet(2); columns->AddPaddingColumn(0, kTextButtonPadding - kBorderThickness); - columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, - views::GridLayout::USE_PREF, 0, 0); - columns->AddPaddingColumn(0, kPaddingBetweenButtons); columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, views::GridLayout::USE_PREF, 0, 0); columns->AddPaddingColumn(0, kTextButtonPadding - kBorderThickness); // First row. - layout->StartRowWithPadding(0, 0, 0, kLogoPadding - kBorderThickness); - layout->AddView(logo.release()); - // All variants have a main header. - auto header = base::MakeUnique<views::Label>( - l10n_util::GetStringUTF16(kExperiments[group_].heading_id), - CONTEXT_WINDOWS10_NATIVE); - header->SetBackgroundColor(kBackgroundColor); - header->SetEnabledColor(kHeaderColor); - header->SetMultiLine(true); - header->SetHorizontalAlignment(gfx::ALIGN_LEFT); - layout->AddView(header.release()); - + layout->AddPaddingRow(0, kCloseButtonTopPadding - kBorderThickness); + layout->StartRow(0, 0, kLogoPadding - kCloseButtonTopPadding); + layout->SkipColumns(1); + layout->SkipColumns(1); // Close button if included in the variant. if (kExperiments[group_].close_style == ExperimentVariations::CloseStyle::kCloseX || @@ -1100,13 +1096,27 @@ close_button->set_tag(static_cast<int>(ButtonTag::CLOSE_BUTTON)); close_button_ = close_button.get(); DCHECK_EQ(close_button->GetPreferredSize().width(), kCloseButtonWidth); - layout->AddView(close_button.release()); + layout->AddView(close_button.release(), 1, 2); close_button_->SetVisible(false); } else { layout->SkipColumns(1); } - // Second row: May have text or may be blank. + // Second row. + layout->StartRow(0, 0); + layout->AddView(logo.release()); + // All variants have a main header. + auto header = base::MakeUnique<views::Label>( + l10n_util::GetStringUTF16(kExperiments[group_].heading_id), + CONTEXT_WINDOWS10_NATIVE); + header->SetBackgroundColor(kBackgroundColor); + header->SetEnabledColor(kHeaderColor); + header->SetMultiLine(true); + header->SetHorizontalAlignment(gfx::ALIGN_LEFT); + layout->AddView(header.release()); + layout->SkipColumns(1); + + // Third row: May have text or may be blank. layout->StartRow(0, 1); const int body_string_id = kExperiments[group_].body_id; if (body_string_id) { @@ -1114,36 +1124,47 @@ l10n_util::GetStringUTF16(body_string_id), CONTEXT_WINDOWS10_NATIVE); body_text->SetBackgroundColor(kBackgroundColor); body_text->SetEnabledColor(kBodyColor); + body_text->SetMultiLine(true); + body_text->SetHorizontalAlignment(gfx::ALIGN_LEFT); layout->AddView(body_text.release()); } - // Third row: one or two buttons depending on group. - layout->StartRowWithPadding(0, 2, 0, kTextButtonPadding); - bool has_no_thanks_button = - kExperiments[group_].close_style == - ExperimentVariations::CloseStyle::kNoThanksButton || - kExperiments[group_].close_style == - ExperimentVariations::CloseStyle::kNoThanksButtonAndCloseX; - if (!has_no_thanks_button) - layout->SkipColumns(1); + // Fourth row: one or two buttons depending on group. + layout->AddPaddingRow(0, kTextButtonPadding); + + static constexpr int kButtonsViewWidth = + kToastWidth - kTextButtonPadding - kTextButtonPadding; + auto buttons = std::make_unique<views::View>(); + ButtonLayout::CreateAndInstall(buttons.get(), kButtonsViewWidth); + + layout->StartRow(0, 2); auto accept_button = CreateWin10StyleButton( this, l10n_util::GetStringUTF16(IDS_WIN10_TOAST_OPEN_CHROME), TryChromeButtonType::OPEN_CHROME); accept_button->set_tag(static_cast<int>(ButtonTag::OK_BUTTON)); - layout->AddView(accept_button.release()); + buttons->AddChildView(accept_button.release()); - if (has_no_thanks_button) { + if (kExperiments[group_].close_style == + ExperimentVariations::CloseStyle::kNoThanksButton || + kExperiments[group_].close_style == + ExperimentVariations::CloseStyle::kNoThanksButtonAndCloseX) { auto no_thanks_button = CreateWin10StyleButton( this, l10n_util::GetStringUTF16(IDS_WIN10_TOAST_NO_THANKS), TryChromeButtonType::NO_THANKS); no_thanks_button->set_tag(static_cast<int>(ButtonTag::NO_THANKS_BUTTON)); - layout->AddView(no_thanks_button.release()); + buttons->AddChildView(no_thanks_button.release()); } + layout->AddView(buttons.release()); + layout->AddPaddingRow(0, kTextButtonPadding - kBorderThickness); - const gfx::Size preferred = layout->GetPreferredSize(contents_view.get()); popup_->SetContentsView(contents_view.release()); + + // Compute the preferred size after attaching the contents view to the popup, + // as doing such causes the theme to propagate through the view hierarchy. + // This propagation can cause views to change their size requirements. + const gfx::Size preferred = popup_->GetContentsView()->GetPreferredSize(); popup_->SetBounds(context_->ComputePopupBounds(popup_, preferred)); popup_->SetAlwaysOnTop(true);
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc index 3953331..ba1d575 100644 --- a/chrome/browser/ui/webui/extensions/extensions_ui.cc +++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -160,6 +160,9 @@ source->AddLocalizedString("clearAll", IDS_MD_EXTENSIONS_ERROR_CLEAR_ALL); source->AddLocalizedString("anonymousFunction", IDS_MD_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION); + source->AddLocalizedString("errorContext", IDS_MD_EXTENSIONS_ERROR_CONTEXT); + source->AddLocalizedString("errorContextUnknown", + IDS_MD_EXTENSIONS_ERROR_CONTEXT_UNKNOWN); source->AddLocalizedString("openInDevtool", IDS_MD_EXTENSIONS_ERROR_LAUNCH_DEVTOOLS); source->AddLocalizedString("stackTrace", IDS_MD_EXTENSIONS_ERROR_STACK_TRACE);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index f8ff39efd..8243e46 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -600,6 +600,10 @@ {"displayMirror", IDS_SETTINGS_DISPLAY_MIRROR}, {"displayMirrorDisplayName", IDS_SETTINGS_DISPLAY_MIRROR_DISPLAY_NAME}, {"displayNightLightLabel", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_LABEL}, + {"displayNightLightOnAtSunset", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_ON_AT_SUNSET}, + {"displayNightLightOffAtSunrise", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_OFF_AT_SUNRISE}, {"displayNightLightScheduleCustom", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_CUSTOM}, {"displayNightLightScheduleLabel",
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc index 9f8d130..a611c1c 100644 --- a/chrome/common/crash_keys.cc +++ b/chrome/common/crash_keys.cc
@@ -47,12 +47,6 @@ // RegisterWebViewCrashKeys(), // chromecast/crash/cast_crash_keys.cc::RegisterCastCrashKeys(). base::debug::CrashKey fixed_keys[] = { -#if defined(OS_MACOSX) || defined(OS_WIN) - {kMetricsClientId, kSmallSize}, -#else - {kClientId, kSmallSize}, -#endif - {kChannel, kSmallSize}, {kActiveURL, kLargeSize}, {kNumVariations, kSmallSize}, {kVariations, kHugeSize}, @@ -71,8 +65,6 @@ std::vector<base::debug::CrashKey> keys( fixed_keys, fixed_keys + arraysize(fixed_keys)); - crash_keys::GetCrashKeysForCommandLineSwitches(&keys); - // Register the extension IDs. { static char formatted_keys[kExtensionIDMaxCount][sizeof(kExtensionID) + 1] =
diff --git a/chrome/common/crash_keys_unittest.cc b/chrome/common/crash_keys_unittest.cc index ea57a03..5d88b0d 100644 --- a/chrome/common/crash_keys_unittest.cc +++ b/chrome/common/crash_keys_unittest.cc
@@ -17,9 +17,12 @@ #include "components/crash/core/common/crash_key.h" #include "testing/gtest/include/gtest/gtest.h" +using crash_reporter::GetCrashKeyValue; + class CrashKeysTest : public testing::Test { public: void SetUp() override { + crash_reporter::ResetCrashKeysForTesting(); crash_reporter::InitializeCrashKeys(); self_ = this; base::debug::SetCrashKeyReportingFunctions( @@ -29,6 +32,7 @@ void TearDown() override { base::debug::ResetCrashLoggingForTesting(); + crash_reporter::ResetCrashKeysForTesting(); self_ = NULL; } @@ -137,9 +141,10 @@ crash_keys::SetCrashKeysFromCommandLine(command_line); - EXPECT_EQ("--vv=1", GetKeyValue("switch-1")); - EXPECT_EQ("--vvv", GetKeyValue("switch-2")); - EXPECT_EQ("--enable-multi-profiles", GetKeyValue("switch-3")); - EXPECT_EQ("--device-management-url=https://foo/bar", GetKeyValue("switch-4")); - EXPECT_FALSE(HasCrashKey("switch-5")); + EXPECT_EQ("--vv=1", GetCrashKeyValue("switch-1")); + EXPECT_EQ("--vvv", GetCrashKeyValue("switch-2")); + EXPECT_EQ("--enable-multi-profiles", GetCrashKeyValue("switch-3")); + EXPECT_EQ("--device-management-url=https://foo/bar", + GetCrashKeyValue("switch-4")); + EXPECT_TRUE(GetCrashKeyValue("switch-5").empty()); }
diff --git a/chrome/common/extensions/api/cryptotoken_private.idl b/chrome/common/extensions/api/cryptotoken_private.idl index 8a091bb..23544d0 100644 --- a/chrome/common/extensions/api/cryptotoken_private.idl +++ b/chrome/common/extensions/api/cryptotoken_private.idl
@@ -19,5 +19,13 @@ static void canOriginAssertAppId(DOMString securityOrigin, DOMString appIdUrl, AppIdCallback callback); + + // Checks whether the given appId is specified in the + // SecurityKeyPermitAttestation policy. This causes a signal to be sent to + // the token that informs it that an individually-identifying attestation + // certificate may be used. Without that signal, the token is required to + // use its batch attestation certificate. + static void isAppIdHashInEnterpriseContext(ArrayBuffer appIdHash, + AppIdCallback callback); }; };
diff --git a/chrome/installer/setup/installer_crash_reporter_client.cc b/chrome/installer/setup/installer_crash_reporter_client.cc index 791898e..5c4db7d 100644 --- a/chrome/installer/setup/installer_crash_reporter_client.cc +++ b/chrome/installer/setup/installer_crash_reporter_client.cc
@@ -101,7 +101,7 @@ } size_t InstallerCrashReporterClient::RegisterCrashKeys() { - return installer::RegisterCrashKeys(); + return 0; } bool InstallerCrashReporterClient::IsRunningUnattended() {
diff --git a/chrome/installer/setup/installer_crash_reporting.cc b/chrome/installer/setup/installer_crash_reporting.cc index 9ac63cb9..c8a6824 100644 --- a/chrome/installer/setup/installer_crash_reporting.cc +++ b/chrome/installer/setup/installer_crash_reporting.cc
@@ -93,17 +93,6 @@ crash_keys::SetMetricsClientIdFromGUID(client_info->client_id); } -size_t RegisterCrashKeys() { - static constexpr base::debug::CrashKey kFixedKeys[] = { - {crash_keys::kMetricsClientId, crash_keys::kSmallSize}, - }; - std::vector<base::debug::CrashKey> keys(std::begin(kFixedKeys), - std::end(kFixedKeys)); - crash_keys::GetCrashKeysForCommandLineSwitches(&keys); - return base::debug::InitCrashKeys(keys.data(), keys.size(), - crash_keys::kChunkMaxLength); -} - void SetInitialCrashKeys(const InstallerState& state) { using crash_reporter::CrashKeyString;
diff --git a/chrome/installer/setup/installer_crash_reporting.h b/chrome/installer/setup/installer_crash_reporting.h index 6128b61a..34bbac1 100644 --- a/chrome/installer/setup/installer_crash_reporting.h +++ b/chrome/installer/setup/installer_crash_reporting.h
@@ -19,9 +19,6 @@ // Sets up the crash reporting system for the installer. void ConfigureCrashReporting(const InstallerState& installer_state); -// Registers all crash keys used by the installer. -size_t RegisterCrashKeys(); - // Sets all crash keys that are available during process startup. These do not // vary during execution so this function will not need to be called more than // once.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 66c0f67..39d64e256 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -444,6 +444,7 @@ "../browser/autocomplete/autocomplete_browsertest.cc", "../browser/autofill/autofill_browsertest.cc", "../browser/autofill/autofill_metrics_browsertest.cc", + "../browser/autofill/autofill_provider_browsertest.cc", "../browser/autofill/autofill_server_browsertest.cc", "../browser/autofill/content_autofill_driver_browsertest.cc", "../browser/autofill/form_structure_browsertest.cc", @@ -2563,9 +2564,11 @@ ] if (is_win) { + assert(toolkit_views) sources += [ "../browser/notifications/mock_notification_image_retainer.cc", "../browser/notifications/mock_notification_image_retainer.h", + "../browser/ui/views/try_chrome_dialog_win/button_layout_unittest.cc", ] }
diff --git a/chrome/test/data/autofill/frame_detached_on_form_submit.html b/chrome/test/data/autofill/frame_detached_on_form_submit.html new file mode 100644 index 0000000..b68d127dc --- /dev/null +++ b/chrome/test/data/autofill/frame_detached_on_form_submit.html
@@ -0,0 +1,25 @@ +<html> +<body> + +<script> + +function delayedUpload() { + window.domAutomationController.send("SUBMISSION_FINISHED"); +} + +function receiveMessage(event) { + var address_iframe = document.getElementById('address_iframe'); + address_iframe.parentNode.removeChild(address_iframe); + setTimeout(delayedUpload, 0); +} + +window.addEventListener("message", receiveMessage, false); + +</script> + +<iframe src="inner_frame_address_form.html" id="address_iframe" name="address_iframe"> +</iframe> + + +</body> +</html>
diff --git a/chrome/test/data/autofill/frame_detached_on_formless_submit.html b/chrome/test/data/autofill/frame_detached_on_formless_submit.html new file mode 100644 index 0000000..85b9be7 --- /dev/null +++ b/chrome/test/data/autofill/frame_detached_on_formless_submit.html
@@ -0,0 +1,25 @@ +<html> +<body> + +<script> + +function delayedUpload() { + window.domAutomationController.send("SUBMISSION_FINISHED"); +} + +function receiveMessage(event) { + var address_iframe = document.getElementById('address_iframe'); + address_iframe.parentNode.removeChild(address_iframe); + setTimeout(delayedUpload, 0); +} + +window.addEventListener("message", receiveMessage, false); + +</script> + +<iframe src="inner_frame_address_formless.html" id="address_iframe" name="address_iframe"> +</iframe> + + +</body> +</html>
diff --git a/chrome/test/data/autofill/inner_frame_address_form.html b/chrome/test/data/autofill/inner_frame_address_form.html new file mode 100644 index 0000000..cfa42da6 --- /dev/null +++ b/chrome/test/data/autofill/inner_frame_address_form.html
@@ -0,0 +1,15 @@ +<html> +<body> + +<script> +function send_post() { + window.parent.postMessage("SubmitComplete", "*"); +} +</script> +<form action="inner_frame_address_form.html" onsubmit="send_post(); return false;" + id="deleting_form"> + <input type="text" id="address_field" name="address" autocomplete="on"> + <input type="submit" id="submit_button" name="submit_button"> +</form> +</body> +</html>
diff --git a/chrome/test/data/autofill/inner_frame_address_formless.html b/chrome/test/data/autofill/inner_frame_address_formless.html new file mode 100644 index 0000000..76df33c1 --- /dev/null +++ b/chrome/test/data/autofill/inner_frame_address_formless.html
@@ -0,0 +1,12 @@ +<html> +<body> + +<script> +function send_post() { + window.parent.postMessage("SubmitComplete", "*"); +} +</script> + <input type="text" id="address_field" name="address" autocomplete="on"> + <input type="button" id="submit_button" name="submit_button" onclick="send_post()"> +</body> +</html>
diff --git a/chrome/test/data/webui/extensions/extension_error_page_test.js b/chrome/test/data/webui/extensions/extension_error_page_test.js index b4f74823..30ecbb5 100644 --- a/chrome/test/data/webui/extensions/extension_error_page_test.js +++ b/chrome/test/data/webui/extensions/extension_error_page_test.js
@@ -160,11 +160,8 @@ renderProcessId: 111, renderViewId: 222, canInspect: true, - stackTrace: [{ - url: 'url', - lineNumber: 123, - columnNumber: 321 - }], + contextUrl: 'http://test.com', + stackTrace: [{url: 'url', lineNumber: 123, columnNumber: 321}], }, runtimeErrorBase); // Add a new runtime error to the end. @@ -210,6 +207,13 @@ lineNumber: 123, columnNumber: 321, }); + + expectEquals( + 'Unknown', + ironCollapses[0].querySelector('.context-url').textContent.trim()); + expectEquals( + nextRuntimeError.contextUrl, + ironCollapses[1].querySelector('.context-url').textContent.trim()); }); });
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc index 15d9c1e..b2d513ff 100644 --- a/chromecast/crash/cast_crash_keys.cc +++ b/chromecast/crash/cast_crash_keys.cc
@@ -16,8 +16,6 @@ // chrome/common/crash_keys.cc. When http://crbug.com/598854 is fixed, // remove these and refactor as necessary. - {::crash_keys::kClientId, ::crash_keys::kSmallSize}, - {::crash_keys::kChannel, ::crash_keys::kSmallSize}, {"url-chunk", ::crash_keys::kLargeSize}, {::crash_keys::kNumVariations, ::crash_keys::kSmallSize}, {::crash_keys::kVariations, ::crash_keys::kHugeSize},
diff --git a/chromeos/network/onc/onc_translation_tables.cc b/chromeos/network/onc/onc_translation_tables.cc index a3e8acc..2266cf6 100644 --- a/chromeos/network/onc/onc_translation_tables.cc +++ b/chromeos/network/onc/onc_translation_tables.cc
@@ -401,7 +401,7 @@ {::onc::cellular::kMDN, shill::kMdnProperty}, {::onc::cellular::kMEID, shill::kMeidProperty}, {::onc::cellular::kMIN, shill::kMinProperty}, - {::onc::cellular::kModelID, shill::kModelIDProperty}, + {::onc::cellular::kModelID, shill::kModelIdProperty}, {::onc::cellular::kPRLVersion, shill::kPRLVersionProperty}, {::onc::cellular::kScanning, shill::kScanningProperty}, // This field is converted during translation, see onc_translator_*.
diff --git a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc index 494312c3..cb3899b7 100644 --- a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc +++ b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -370,15 +370,9 @@ auto result = InitializeTask(std::move(config)); // Report initialization status to UMA. - // TODO(hiroh): crbug.com/793251. - // Not report "Media.ChromeArcVDA.InitializeResult" after - // autototests will be updated as not using ChromeArcVDA. const int RESULT_MAX = static_cast<int>(mojom::VideoDecodeAccelerator::Result::RESULT_MAX); UMA_HISTOGRAM_ENUMERATION( - "Media.ChromeArcVideoDecodeAccelerator.InitializeResult", - static_cast<int>(result), RESULT_MAX); - UMA_HISTOGRAM_ENUMERATION( "Media.GpuArcVideoDecodeAccelerator.InitializeResult", static_cast<int>(result), RESULT_MAX); std::move(callback).Run(result);
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc index cc96c2eb..e60334a 100644 --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -410,7 +410,7 @@ was_query_node_autofilled_ = element_.IsAutofilled(); form_util::FillForm(form, element_); if (!element_.Form().IsNull()) - last_interacted_form_ = element_.Form(); + UpdateLastInteractedForm(element_.Form()); GetAutofillDriver()->DidFillAutofillFormData(form, base::TimeTicks::Now()); } @@ -794,10 +794,10 @@ ElementChangeSource source) { // Remember the last form the user interacted with. if (source == ElementChangeSource::WILL_SEND_SUBMIT_EVENT) { - last_interacted_form_ = form; + UpdateLastInteractedForm(form); } else if (source == ElementChangeSource::TEXTFIELD_CHANGED) { if (!element.Form().IsNull()) { - last_interacted_form_ = element.Form(); + UpdateLastInteractedForm(element.Form()); } else { // Remove invisible elements for (auto it = formless_elements_user_edited_.begin(); @@ -809,9 +809,9 @@ } } formless_elements_user_edited_.insert(element); - constructed_form_.reset(new FormData()); - if (!CollectFormlessElements(constructed_form_.get())) { - constructed_form_.reset(); + provisionally_saved_form_ = std::make_unique<FormData>(); + if (!CollectFormlessElements(provisionally_saved_form_.get())) { + provisionally_saved_form_.reset(); } else { last_interacted_form_.Reset(); } @@ -847,18 +847,13 @@ if (source == SubmissionSource::FRAME_DETACHED) { // Should not access the frame because it is now detached. Instead, use - // |constructed_form_| or |last_interacted_form_| depending on whether the - // form is formless or not. - if (!last_interacted_form_.IsNull()) { - FireHostSubmitEvents(last_interacted_form_, /*known_success=*/true); - } else if (constructed_form_) { - FireHostSubmitEvents(*constructed_form_, /*known_success=*/true); - } + // |provisionally_saved_form_|. + if (provisionally_saved_form_) + FireHostSubmitEvents(*provisionally_saved_form_, /*known_success=*/true); } else { FormData form_data; - if (GetSubmittedForm(&form_data)) { + if (GetSubmittedForm(&form_data)) FireHostSubmitEvents(form_data, /*known_success=*/true); - } } ResetLastInteractedElements(); } @@ -873,17 +868,23 @@ bool AutofillAgent::GetSubmittedForm(FormData* form) { if (!last_interacted_form_.IsNull()) { - return form_util::ExtractFormData(last_interacted_form_, form); + if (form_util::ExtractFormData(last_interacted_form_, form)) { + return true; + } else if (provisionally_saved_form_) { + *form = *provisionally_saved_form_; + return true; + } } else if (formless_elements_user_edited_.size() != 0 && !form_util::IsSomeControlElementVisible( formless_elements_user_edited_)) { // we check if all the elements the user has interacted with are gone, - // to decide if submission has occurred, and use the constructed_form_ - // saved in OnProvisionallySaveForm() if fail to construct form. + // to decide if submission has occurred, and use the + // provisionally_saved_form_ saved in OnProvisionallySaveForm() if fail to + // construct form. if (CollectFormlessElements(form)) { return true; - } else if (constructed_form_) { - *form = *constructed_form_; + } else if (provisionally_saved_form_) { + *form = *provisionally_saved_form_; return true; } } @@ -893,7 +894,16 @@ void AutofillAgent::ResetLastInteractedElements() { last_interacted_form_.Reset(); formless_elements_user_edited_.clear(); - constructed_form_.reset(); + provisionally_saved_form_.reset(); +} + +void AutofillAgent::UpdateLastInteractedForm(blink::WebFormElement form) { + last_interacted_form_ = form; + provisionally_saved_form_ = std::make_unique<FormData>(); + if (!form_util::ExtractFormData(last_interacted_form_, + provisionally_saved_form_.get())) { + provisionally_saved_form_.reset(); + } } const mojom::AutofillDriverPtr& AutofillAgent::GetAutofillDriver() {
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h index f289417..eeff3d2 100644 --- a/components/autofill/content/renderer/autofill_agent.h +++ b/components/autofill/content/renderer/autofill_agent.h
@@ -245,10 +245,11 @@ virtual bool IsUserGesture() const; // Attempt to get submitted FormData from last_interacted_form_ or - // constructed_form_, return true if |form| is set. + // provisionally_saved_form_, return true if |form| is set. bool GetSubmittedForm(FormData* form); void ResetLastInteractedElements(); + void UpdateLastInteractedForm(blink::WebFormElement form); // Formerly cached forms for all frames, now only caches forms for the current // frame. @@ -273,7 +274,10 @@ // When dealing with forms that don't use a <form> tag, we keep track of the // elements the user has modified so we can determine when submission occurs. std::set<blink::WebInputElement> formless_elements_user_edited_; - std::unique_ptr<FormData> constructed_form_; + + // The form user interacted, it is used if last_interacted_form_ or formless + // form can't be converted to FormData at the time of form submission. + std::unique_ptr<FormData> provisionally_saved_form_; // Was the query node autofilled prior to previewing the form? bool was_query_node_autofilled_;
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 5735775c..c9a2e82 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -290,6 +290,8 @@ "test_autofill_driver.h", "test_autofill_external_delegate.cc", "test_autofill_external_delegate.h", + "test_autofill_provider.cc", + "test_autofill_provider.h", "test_personal_data_manager.cc", "test_personal_data_manager.h", "test_region_data_loader.cc",
diff --git a/components/autofill/core/browser/test_autofill_provider.cc b/components/autofill/core/browser/test_autofill_provider.cc new file mode 100644 index 0000000..6ed1370 --- /dev/null +++ b/components/autofill/core/browser/test_autofill_provider.cc
@@ -0,0 +1,50 @@ +// Copyright 2017 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 "components/autofill/core/browser/test_autofill_provider.h" + +namespace autofill { + +void TestAutofillProvider::OnQueryFormFieldAutofill( + AutofillHandlerProxy* handler, + int32_t id, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) {} + +void TestAutofillProvider::OnTextFieldDidChange( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp) {} + +void TestAutofillProvider::OnTextFieldDidScroll( + AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) {} + +bool TestAutofillProvider::OnWillSubmitForm(AutofillHandlerProxy* handler, + const FormData& form, + const base::TimeTicks timestamp) { + return false; +} + +void TestAutofillProvider::OnFocusNoLongerOnForm( + AutofillHandlerProxy* handler) {} + +void TestAutofillProvider::OnFocusOnFormField(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) {} + +void TestAutofillProvider::OnDidFillAutofillFormData( + AutofillHandlerProxy* handler, + const FormData& form, + base::TimeTicks timestamp) {} + +void TestAutofillProvider::Reset(AutofillHandlerProxy* handler) {} + +} // namespace autofill
diff --git a/components/autofill/core/browser/test_autofill_provider.h b/components/autofill/core/browser/test_autofill_provider.h new file mode 100644 index 0000000..7e140a80 --- /dev/null +++ b/components/autofill/core/browser/test_autofill_provider.h
@@ -0,0 +1,47 @@ +// Copyright 2017 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 COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_PROVIDER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_PROVIDER_H_ + +#include "components/autofill/core/browser/autofill_provider.h" + +namespace autofill { + +class TestAutofillProvider : public AutofillProvider { + public: + ~TestAutofillProvider() override{}; + + // AutofillProvider: + void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler, + int32_t id, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) override; + void OnTextFieldDidChange(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp) override; + void OnTextFieldDidScroll(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) override; + bool OnWillSubmitForm(AutofillHandlerProxy* handler, + const FormData& form, + const base::TimeTicks timestamp) override; + void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) override; + void OnFocusOnFormField(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) override; + void OnDidFillAutofillFormData(AutofillHandlerProxy* handler, + const FormData& form, + base::TimeTicks timestamp) override; + void Reset(AutofillHandlerProxy* handler) override; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_PROVIDER_H_
diff --git a/components/crash/content/app/breakpad_linux.cc b/components/crash/content/app/breakpad_linux.cc index 42a50e8..620a9b9c 100644 --- a/components/crash/content/app/breakpad_linux.cc +++ b/components/crash/content/app/breakpad_linux.cc
@@ -247,7 +247,7 @@ if (!GetEnableCrashReporterSwitchParts(command_line, &switch_parts)) return; - base::debug::SetCrashKeyValue(crash_keys::kChannel, switch_parts[1]); + SetChannelCrashKey(switch_parts[1]); } #endif @@ -2017,6 +2017,11 @@ PostEnableBreakpadInitialization(); } +void SetChannelCrashKey(const std::string& channel) { + static crash_reporter::CrashKeyString<16> channel_key("channel"); + channel_key.Set(channel); +} + #if defined(OS_ANDROID) void InitNonBrowserCrashReporterForAndroid(const std::string& process_type) { SanitizationInfo sanitization_info;
diff --git a/components/crash/content/app/breakpad_linux.h b/components/crash/content/app/breakpad_linux.h index 4a2a429..9ee8555 100644 --- a/components/crash/content/app/breakpad_linux.h +++ b/components/crash/content/app/breakpad_linux.h
@@ -16,6 +16,9 @@ // Turns on the crash reporter in any process. extern void InitCrashReporter(const std::string& process_type); +// Sets the product/distribution channel crash key. +void SetChannelCrashKey(const std::string& channel); + #if defined(OS_ANDROID) extern void InitCrashKeysForTesting();
diff --git a/components/crash/core/DEPS b/components/crash/core/DEPS index 05041de..40c8ac8 100644 --- a/components/crash/core/DEPS +++ b/components/crash/core/DEPS
@@ -5,6 +5,7 @@ "+components/crash/core", "+third_party/crashpad/crashpad/client/annotation.h", "+third_party/crashpad/crashpad/client/annotation_list.h", + "+third_party/crashpad/crashpad/client/crashpad_info.h", "+third_party/breakpad/breakpad/src/common/simple_string_dictionary.h", "-net", ]
diff --git a/components/crash/core/common/BUILD.gn b/components/crash/core/common/BUILD.gn index e995b54..4f67529f 100644 --- a/components/crash/core/common/BUILD.gn +++ b/components/crash/core/common/BUILD.gn
@@ -35,7 +35,7 @@ "crash_key_base_support.h", ] - defines = [ "CRASH_CORE_COMMON_IMPLEMENTATION" ] + defines = [] # This target is not always a component, depending on the implementation. # When it is not a component, annotating functions with the standard @@ -44,7 +44,10 @@ # wrapper macro CRASH_KEY_EXPORT that only evaluates to CRASH_EXPORT if this # target is really a component. if (crash_key_target_type == "component") { - defines += [ "CRASH_KEY_EXPORT=CRASH_EXPORT" ] + defines += [ + "CRASH_KEY_EXPORT=CRASH_EXPORT", + "CRASH_CORE_COMMON_IMPLEMENTATION", + ] } deps = [
diff --git a/components/crash/core/common/crash_key.h b/components/crash/core/common/crash_key.h index a6eab451..951c7e9 100644 --- a/components/crash/core/common/crash_key.h +++ b/components/crash/core/common/crash_key.h
@@ -217,6 +217,16 @@ // Initializes the crash key subsystem if it is required. CRASH_KEY_EXPORT void InitializeCrashKeys(); +#if defined(UNIT_TEST) || defined(CRASH_CORE_COMMON_IMPLEMENTATION) +// Returns a value for the crash key named |key_name|. For Crashpad-based +// clients, this returns the first instance found of the name. +CRASH_KEY_EXPORT std::string GetCrashKeyValue(const std::string& key_name); + +// Resets crash key state and, depending on the platform, de-initializes +// the system. +CRASH_KEY_EXPORT void ResetCrashKeysForTesting(); +#endif + } // namespace crash_reporter #undef USE_CRASHPAD_ANNOTATION
diff --git a/components/crash/core/common/crash_key_breakpad.cc b/components/crash/core/common/crash_key_breakpad.cc index 47b9597..dcd406c 100644 --- a/components/crash/core/common/crash_key_breakpad.cc +++ b/components/crash/core/common/crash_key_breakpad.cc
@@ -7,6 +7,7 @@ #include "components/crash/core/common/crash_key.h" +#include "base/debug/crash_logging.h" #include "base/format_macros.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" @@ -107,4 +108,17 @@ InitializeCrashKeyBaseSupport(); } +std::string GetCrashKeyValue(const std::string& key_name) { + const char* value = + internal::GetCrashKeyStorage()->GetValueForKey(key_name.c_str()); + if (value) + return value; + return std::string(); +} + +void ResetCrashKeysForTesting() { + internal::ResetCrashKeyStorageForTesting(); + base::debug::SetCrashKeyImplementation(nullptr); +} + } // namespace crash_reporter
diff --git a/components/crash/core/common/crash_key_breakpad_ios.mm b/components/crash/core/common/crash_key_breakpad_ios.mm index b476937..288453b 100644 --- a/components/crash/core/common/crash_key_breakpad_ios.mm +++ b/components/crash/core/common/crash_key_breakpad_ios.mm
@@ -44,7 +44,7 @@ NSString* value_ns = base::SysUTF8ToNSString(value.as_string()); WithBreakpadRefSync(^(BreakpadRef ref) { - BreakpadSetKeyValue(ref, key, value_ns); + BreakpadAddUploadParameter(ref, key, value_ns); }); } @@ -52,13 +52,14 @@ NSString* key = base::SysUTF8ToNSString(name_); WithBreakpadRefSync(^(BreakpadRef ref) { - BreakpadRemoveKeyValue(ref, key); + BreakpadRemoveUploadParameter(ref, key); }); } bool CrashKeyStringImpl::is_set() const { __block bool is_set = false; - NSString* key = base::SysUTF8ToNSString(name_); + NSString* key = base::SysUTF8ToNSString( + std::string(BREAKPAD_SERVER_PARAMETER_PREFIX) + name_); WithBreakpadRefSync(^(BreakpadRef ref) { is_set = BreakpadKeyValue(ref, key) != nil; @@ -73,4 +74,21 @@ InitializeCrashKeyBaseSupport(); } +std::string GetCrashKeyValue(const std::string& key_name) { + __block NSString* value; + NSString* key = base::SysUTF8ToNSString( + std::string(BREAKPAD_SERVER_PARAMETER_PREFIX) + key_name); + + internal::WithBreakpadRefSync(^(BreakpadRef ref) { + value = BreakpadKeyValue(ref, key); + }); + + return base::SysNSStringToUTF8(value); +} + +void ResetCrashKeysForTesting() { + // There's no way to do this on iOS without tearing down the + // BreakpadController. +} + } // namespace crash_reporter
diff --git a/components/crash/core/common/crash_key_crashpad.cc b/components/crash/core/common/crash_key_crashpad.cc index 7cd7f8df..929392e 100644 --- a/components/crash/core/common/crash_key_crashpad.cc +++ b/components/crash/core/common/crash_key_crashpad.cc
@@ -7,8 +7,10 @@ #include "components/crash/core/common/crash_key.h" +#include "base/debug/crash_logging.h" #include "components/crash/core/common/crash_key_base_support.h" #include "third_party/crashpad/crashpad/client/annotation_list.h" +#include "third_party/crashpad/crashpad/client/crashpad_info.h" namespace crash_reporter { @@ -17,4 +19,33 @@ InitializeCrashKeyBaseSupport(); } +// Returns a value for the crash key named |key_name|. For Crashpad-based +// clients, this returns the first instance found of the name. +std::string GetCrashKeyValue(const std::string& key_name) { + auto* annotation_list = crashpad::AnnotationList::Get(); + if (annotation_list) { + for (crashpad::Annotation* annotation : *annotation_list) { + if (key_name == annotation->name()) { + return std::string(static_cast<const char*>(annotation->value()), + annotation->size()); + } + } + } + + return std::string(); +} + +void ResetCrashKeysForTesting() { + // The AnnotationList should not be deleted because the static Annotation + // object data still reference the link nodes. + auto* annotation_list = crashpad::AnnotationList::Get(); + if (annotation_list) { + for (crashpad::Annotation* annotation : *annotation_list) { + annotation->Clear(); + } + } + + base::debug::SetCrashKeyImplementation(nullptr); +} + } // namespace crash_reporter
diff --git a/components/crash/core/common/crash_key_stubs.cc b/components/crash/core/common/crash_key_stubs.cc index deb145f..f978394 100644 --- a/components/crash/core/common/crash_key_stubs.cc +++ b/components/crash/core/common/crash_key_stubs.cc
@@ -28,4 +28,10 @@ void InitializeCrashKeys() {} +std::string GetCrashKeyValue(const std::string& key_name) { + return std::string(); +} + +void ResetCrashKeysForTesting() {} + } // namespace crash_reporter
diff --git a/components/crash/core/common/crash_keys.cc b/components/crash/core/common/crash_keys.cc index d6d43f2a..3fcbc16 100644 --- a/components/crash/core/common/crash_keys.cc +++ b/components/crash/core/common/crash_keys.cc
@@ -8,6 +8,7 @@ #include "base/debug/crash_logging.h" #include "base/format_macros.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -17,29 +18,26 @@ namespace crash_keys { -#if defined(OS_MACOSX) -// Crashpad owns the "guid" key. Chrome's metrics client ID is a separate ID -// carried in a distinct "metrics_client_id" field. +namespace { + +#if defined(OS_MACOSX) || defined(OS_WIN) +// When using Crashpad, the crash reporting client ID is the responsibility of +// Crashpad. It is not set directly by Chrome. To make the metrics client ID +// available on the server, it's stored in a distinct key. const char kMetricsClientId[] = "metrics_client_id"; -#elif defined(OS_WIN) -// TODO(scottmg): While transitioning to Crashpad, there are some executables -// that use Crashpad (which use kMetricsClientId), and some that use Breakpad -// (kClientId), and they both use this file. For now we define both, but once -// Breakpad is no longer used on Windows, we will no longer need kClientId, and -// this can be combined with the OS_MACOSX block above. -const char kMetricsClientId[] = "metrics_client_id"; -const char kClientId[] = "guid"; #else -const char kClientId[] = "guid"; +// When using Breakpad instead of Crashpad, the crash reporting client ID is the +// same as the metrics client ID. +const char kMetricsClientId[] = "guid"; #endif -const char kChannel[] = "channel"; +crash_reporter::CrashKeyString<40> client_id_key(kMetricsClientId); + +} // namespace const char kNumVariations[] = "num-experiments"; const char kVariations[] = "variations"; -const char kSwitchFormat[] = "switch-%" PRIuS; - void SetMetricsClientIdFromGUID(const std::string& metrics_client_guid) { std::string stripped_guid(metrics_client_guid); // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY. @@ -48,15 +46,7 @@ if (stripped_guid.empty()) return; -#if defined(OS_MACOSX) || defined(OS_WIN) - // The crash client ID is maintained by Crashpad and is distinct from the - // metrics client ID, which is carried in its own key. - base::debug::SetCrashKeyValue(kMetricsClientId, stripped_guid); -#else - // The crash client ID is set by the application when Breakpad is in use. - // The same ID as the metrics client ID is used. - base::debug::SetCrashKeyValue(kClientId, stripped_guid); -#endif + client_id_key.Set(stripped_guid); } void ClearMetricsClientId() { @@ -73,7 +63,7 @@ // it needs to use the metrics client ID as its stable crash client ID, so // leave its client ID intact even when metrics reporting is disabled while // the application is running. - base::debug::ClearCrashKey(kMetricsClientId); + client_id_key.Clear(); #endif } @@ -96,46 +86,39 @@ base::debug::SetCrashKeyValue(kVariations, variations_string); } -void GetCrashKeysForCommandLineSwitches( - std::vector<base::debug::CrashKey>* keys) { - DCHECK(keys); +using SwitchesCrashKey = crash_reporter::CrashKeyString<64>; +static SwitchesCrashKey switches_keys[] = { + {"switch-1", SwitchesCrashKey::Tag::kArray}, + {"switch-2", SwitchesCrashKey::Tag::kArray}, + {"switch-3", SwitchesCrashKey::Tag::kArray}, + {"switch-4", SwitchesCrashKey::Tag::kArray}, + {"switch-5", SwitchesCrashKey::Tag::kArray}, + {"switch-6", SwitchesCrashKey::Tag::kArray}, + {"switch-7", SwitchesCrashKey::Tag::kArray}, + {"switch-8", SwitchesCrashKey::Tag::kArray}, + {"switch-9", SwitchesCrashKey::Tag::kArray}, + {"switch-10", SwitchesCrashKey::Tag::kArray}, + {"switch-11", SwitchesCrashKey::Tag::kArray}, + {"switch-12", SwitchesCrashKey::Tag::kArray}, + {"switch-13", SwitchesCrashKey::Tag::kArray}, + {"switch-14", SwitchesCrashKey::Tag::kArray}, + {"switch-15", SwitchesCrashKey::Tag::kArray}, +}; - // Use static storage for formatted key names, since they will persist for - // the duration of the program. - static char formatted_keys[kSwitchesMaxCount][sizeof(kSwitchFormat) + 1] = - {{ 0 }}; - const size_t formatted_key_len = sizeof(formatted_keys[0]); - - // sizeof(kSwitchFormat) is platform-dependent, so make sure formatted_keys - // actually have space for a 2-digit switch number plus null-terminator. - static_assert(formatted_key_len >= 10, - "insufficient space for \"switch-NN\""); - - for (size_t i = 0; i < kSwitchesMaxCount; ++i) { - // Name the keys using 1-based indexing. - int n = base::snprintf(formatted_keys[i], formatted_key_len, kSwitchFormat, - i + 1); - DCHECK_GT(n, 0); - base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; - keys->push_back(crash_key); - } -} +static crash_reporter::CrashKeyString<4> num_switches_key("num-switches"); void SetSwitchesFromCommandLine(const base::CommandLine& command_line, SwitchFilterFunction skip_filter) { const base::CommandLine::StringVector& argv = command_line.argv(); // Set the number of switches in case size > kNumSwitches. - // num-switches is capped at 15 entries, so only two digits are stored. - static crash_reporter::CrashKeyString<2> num_switches_key("num-switches"); - num_switches_key.Set(base::StringPrintf("%" PRIuS, argv.size() - 1)); + num_switches_key.Set(base::NumberToString(argv.size() - 1)); - size_t key_i = 1; // Key names are 1-indexed. + size_t key_i = 0; // Go through the argv, skipping the exec path. Stop if there are too many // switches to hold in crash keys. - for (size_t i = 1; i < argv.size() && key_i <= crash_keys::kSwitchesMaxCount; - ++i) { + for (size_t i = 1; i < argv.size() && key_i < arraysize(switches_keys); ++i) { #if defined(OS_WIN) std::string switch_str = base::WideToUTF8(argv[i]); #else @@ -146,13 +129,19 @@ if (skip_filter && (*skip_filter)(switch_str)) continue; - std::string key = base::StringPrintf(kSwitchFormat, key_i++); - base::debug::SetCrashKeyValue(key, switch_str); + switches_keys[key_i++].Set(switch_str); } // Clear any remaining switches. - for (; key_i <= kSwitchesMaxCount; ++key_i) - base::debug::ClearCrashKey(base::StringPrintf(kSwitchFormat, key_i)); + for (; key_i < arraysize(switches_keys); ++key_i) + switches_keys[key_i].Clear(); +} + +void ResetCommandLineForTesting() { + num_switches_key.Clear(); + for (auto& key : switches_keys) { + key.Clear(); + } } } // namespace crash_keys
diff --git a/components/crash/core/common/crash_keys.h b/components/crash/core/common/crash_keys.h index 39cce75..73f1385 100644 --- a/components/crash/core/common/crash_keys.h +++ b/components/crash/core/common/crash_keys.h
@@ -12,6 +12,7 @@ #include "base/debug/crash_logging.h" #include "build/build_config.h" +#include "components/crash/core/common/crash_key.h" namespace base { class CommandLine; @@ -28,10 +29,6 @@ // Sets the list of active experiment/variations info. void SetVariationsList(const std::vector<std::string>& variations); -// Adds a common set of crash keys for holding command-line switches to |keys|. -void GetCrashKeysForCommandLineSwitches( - std::vector<base::debug::CrashKey>* keys); - // A function returning true if |flag| is a switch that should be filtered out // of crash keys. using SwitchFilterFunction = bool (*)(const std::string& flag); @@ -42,6 +39,9 @@ void SetSwitchesFromCommandLine(const base::CommandLine& command_line, SwitchFilterFunction skip_filter); +// Clears all the CommandLine-related crash keys. +void ResetCommandLineForTesting(); + // Crash Key Constants ///////////////////////////////////////////////////////// // kChunkMaxLength is the platform-specific maximum size that a value in a @@ -71,24 +71,6 @@ // Crash Key Name Constants //////////////////////////////////////////////////// -// The GUID used to identify this client to the crash system. -#if defined(OS_MACOSX) -// When using Crashpad, the crash reporting client ID is the responsibility of -// Crashpad. It is not set directly by Chrome. To make the metrics client ID -// available on the server, it's stored in a distinct key. -extern const char kMetricsClientId[]; -#elif defined(OS_WIN) -extern const char kMetricsClientId[]; -extern const char kClientId[]; -#else -// When using Breakpad instead of Crashpad, the crash reporting client ID is the -// same as the metrics client ID. -extern const char kClientId[]; -#endif - -// The product release/distribution channel. -extern const char kChannel[]; - // The total number of experiments the instance has. extern const char kNumVariations[]; @@ -96,14 +78,6 @@ // typically set by SetExperimentList. extern const char kVariations[]; -// The maximum number of command line switches to process. |kSwitchFormat| -// should be formatted with an integer in the range [1, kSwitchesMaxCount]. -const size_t kSwitchesMaxCount = 15; - -// A printf-style format string naming the set of crash keys corresponding to -// at most |kSwitchesMaxCount| command line switches. -extern const char kSwitchFormat[]; - } // namespace crash_keys #endif // COMPONENTS_CRASH_CORE_COMMON_CRASH_KEYS_H_
diff --git a/components/crash/core/common/crash_keys_unittest.cc b/components/crash/core/common/crash_keys_unittest.cc index 466c5fe..eca2879 100644 --- a/components/crash/core/common/crash_keys_unittest.cc +++ b/components/crash/core/common/crash_keys_unittest.cc
@@ -18,21 +18,22 @@ #include "components/crash/core/common/crash_key.h" #include "testing/gtest/include/gtest/gtest.h" +using crash_reporter::GetCrashKeyValue; + +// The number of switch-N keys declared in SetSwitchesFromCommandLine(). +constexpr int kSwitchesMaxCount = 15; + class CrashKeysTest : public testing::Test { public: void SetUp() override { + ResetData(); + crash_reporter::InitializeCrashKeys(); self_ = this; base::debug::SetCrashKeyReportingFunctions( &SetCrashKeyValue, &ClearCrashKey); } - bool InitSwitchesCrashKeys() { - std::vector<base::debug::CrashKey> keys; - crash_keys::GetCrashKeysForCommandLineSwitches(&keys); - return InitCrashKeys(keys); - } - bool InitVariationsCrashKeys() { std::vector<base::debug::CrashKey> keys = { {crash_keys::kNumVariations, crash_keys::kSmallSize}, @@ -42,6 +43,7 @@ void TearDown() override { base::debug::ResetCrashLoggingForTesting(); + ResetData(); self_ = nullptr; } @@ -72,6 +74,11 @@ self_->keys_.erase(key.as_string()); } + void ResetData() { + crash_keys::ResetCommandLineForTesting(); + crash_reporter::ResetCrashKeysForTesting(); + } + static CrashKeysTest* self_; std::map<std::string, std::string> keys_; @@ -80,49 +87,46 @@ CrashKeysTest* CrashKeysTest::self_ = nullptr; TEST_F(CrashKeysTest, Switches) { - ASSERT_TRUE(InitSwitchesCrashKeys()); - // Set three switches. { base::CommandLine command_line(base::CommandLine::NO_PROGRAM); for (size_t i = 1; i <= 3; ++i) command_line.AppendSwitch(base::StringPrintf("--flag-%" PRIuS, i)); crash_keys::SetSwitchesFromCommandLine(command_line, nullptr); - EXPECT_EQ("--flag-1", GetKeyValue("switch-1")); - EXPECT_EQ("--flag-2", GetKeyValue("switch-2")); - EXPECT_EQ("--flag-3", GetKeyValue("switch-3")); - EXPECT_FALSE(HasCrashKey("switch-4")); + EXPECT_EQ("--flag-1", GetCrashKeyValue("switch-1")); + EXPECT_EQ("--flag-2", GetCrashKeyValue("switch-2")); + EXPECT_EQ("--flag-3", GetCrashKeyValue("switch-3")); + EXPECT_TRUE(GetCrashKeyValue("switch-4").empty()); } // Set more than the max switches. { base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - const size_t kMax = crash_keys::kSwitchesMaxCount + 2; + const size_t kMax = kSwitchesMaxCount + 2; EXPECT_GT(kMax, static_cast<size_t>(15)); for (size_t i = 1; i <= kMax; ++i) command_line.AppendSwitch(base::StringPrintf("--many-%" PRIuS, i)); crash_keys::SetSwitchesFromCommandLine(command_line, nullptr); - EXPECT_EQ("--many-1", GetKeyValue("switch-1")); - EXPECT_EQ("--many-9", GetKeyValue("switch-9")); - EXPECT_EQ("--many-15", GetKeyValue("switch-15")); - EXPECT_FALSE(HasCrashKey("switch-16")); - EXPECT_FALSE(HasCrashKey("switch-17")); + EXPECT_EQ("--many-1", GetCrashKeyValue("switch-1")); + EXPECT_EQ("--many-9", GetCrashKeyValue("switch-9")); + EXPECT_EQ("--many-15", GetCrashKeyValue("switch-15")); + EXPECT_TRUE(GetCrashKeyValue("switch-16").empty()); + EXPECT_TRUE(GetCrashKeyValue("switch-17").empty()); } // Set fewer to ensure that old ones are erased. { base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - for (size_t i = 1; i <= 5; ++i) - command_line.AppendSwitch(base::StringPrintf("--fewer-%" PRIuS, i)); + for (int i = 1; i <= 5; ++i) + command_line.AppendSwitch(base::StringPrintf("--fewer-%d", i)); crash_keys::SetSwitchesFromCommandLine(command_line, nullptr); - EXPECT_EQ("--fewer-1", GetKeyValue("switch-1")); - EXPECT_EQ("--fewer-2", GetKeyValue("switch-2")); - EXPECT_EQ("--fewer-3", GetKeyValue("switch-3")); - EXPECT_EQ("--fewer-4", GetKeyValue("switch-4")); - EXPECT_EQ("--fewer-5", GetKeyValue("switch-5")); - for (size_t i = 6; i < 20; ++i) - EXPECT_FALSE(HasCrashKey(base::StringPrintf(crash_keys::kSwitchFormat, - i))); + EXPECT_EQ("--fewer-1", GetCrashKeyValue("switch-1")); + EXPECT_EQ("--fewer-2", GetCrashKeyValue("switch-2")); + EXPECT_EQ("--fewer-3", GetCrashKeyValue("switch-3")); + EXPECT_EQ("--fewer-4", GetCrashKeyValue("switch-4")); + EXPECT_EQ("--fewer-5", GetCrashKeyValue("switch-5")); + for (int i = 6; i < 20; ++i) + EXPECT_TRUE(GetCrashKeyValue(base::StringPrintf("switch-%d", i)).empty()); } } @@ -135,10 +139,6 @@ } // namespace TEST_F(CrashKeysTest, FilterFlags) { - ASSERT_TRUE(InitSwitchesCrashKeys()); - - using crash_keys::kSwitchesMaxCount; - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); command_line.AppendSwitch("--not-boring-1"); command_line.AppendSwitch("--boring"); @@ -152,11 +152,11 @@ // If the boring keys are filtered out, every single key should now be // not-boring. - for (size_t i = 1; i <= kSwitchesMaxCount; ++i) { - std::string switch_name = base::StringPrintf(crash_keys::kSwitchFormat, i); - std::string switch_value = base::StringPrintf("--not-boring-%" PRIuS, i); - EXPECT_EQ(switch_value, GetKeyValue(switch_name)) << "switch_name is " << - switch_name; + for (int i = 1; i <= kSwitchesMaxCount; ++i) { + std::string switch_name = base::StringPrintf("switch-%d", i); + std::string switch_value = base::StringPrintf("--not-boring-%d", i); + EXPECT_EQ(switch_value, GetCrashKeyValue(switch_name)) + << "switch_name is " << switch_name; } }
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc index f49eefc..fe01db1eb 100644 --- a/components/metrics/file_metrics_provider.cc +++ b/components/metrics/file_metrics_provider.cc
@@ -157,6 +157,12 @@ } ~SourceInfo() {} + struct FoundFile { + base::FilePath path; + base::FileEnumerator::FileInfo info; + }; + using FoundFiles = base::flat_map<base::Time, FoundFile>; + // How to access this source (file/dir, atomic/active). const SourceType type; @@ -167,6 +173,9 @@ // a directory is being monitored. base::FilePath directory; + // The files found in the above directory, ordered by last-modified. + std::unique_ptr<FoundFiles> found_files; + // Where on disk the file is located. If a directory is being monitored, // this will be updated for whatever file is being read. base::FilePath path; @@ -272,69 +281,75 @@ DCHECK_EQ(SOURCE_HISTOGRAMS_ATOMIC_DIR, source->type); DCHECK(!source->directory.empty()); - // Open the directory and find all the files, remembering the last-modified - // time of each. - struct FoundFile { - base::FilePath path; - base::FileEnumerator::FileInfo info; - }; - base::flat_map<base::Time, FoundFile> found_files; - base::FilePath file_path; - base::Time now_time = base::Time::Now(); + // Cumulative directory stats. These will remain zero if the directory isn't + // scanned but that's okay since any work they would cause to be done below + // would have been done during the first call where the directory was fully + // scanned. size_t total_size_kib = 0; // Using KiB allows 4TiB even on 32-bit builds. size_t file_count = 0; size_t delete_count = 0; - base::FileEnumerator file_iter(source->directory, /*recursive=*/false, - base::FileEnumerator::FILES); - FoundFile found_file; - for (found_file.path = file_iter.Next(); !found_file.path.empty(); - found_file.path = file_iter.Next()) { - found_file.info = file_iter.GetInfo(); - // Ignore directories and zero-sized files. - if (found_file.info.IsDirectory() || found_file.info.GetSize() == 0) - continue; + base::Time now_time = base::Time::Now(); + if (!source->found_files) { + source->found_files = base::MakeUnique<SourceInfo::FoundFiles>(); + base::FileEnumerator file_iter(source->directory, /*recursive=*/false, + base::FileEnumerator::FILES); + SourceInfo::FoundFile found_file; - // Ignore temporary files. - base::FilePath::CharType first_character = - found_file.path.BaseName().value().front(); - if (first_character == FILE_PATH_LITERAL('.') || - first_character == FILE_PATH_LITERAL('_')) { - continue; + // Open the directory and find all the files, remembering the last-modified + // time of each. + for (found_file.path = file_iter.Next(); !found_file.path.empty(); + found_file.path = file_iter.Next()) { + found_file.info = file_iter.GetInfo(); + + // Ignore directories. + if (found_file.info.IsDirectory()) + continue; + + // Ignore temporary files. + base::FilePath::CharType first_character = + found_file.path.BaseName().value().front(); + if (first_character == FILE_PATH_LITERAL('.') || + first_character == FILE_PATH_LITERAL('_')) { + continue; + } + + // Ignore non-PMA (Persistent Memory Allocator) files. + if (found_file.path.Extension() != + base::PersistentMemoryAllocator::kFileExtension) { + continue; + } + + // Process real files. + total_size_kib += found_file.info.GetSize() >> 10; + base::Time modified = found_file.info.GetLastModifiedTime(); + if (modified > source->last_seen) { + // This file hasn't been read. Remember it (unless from the future). + if (modified <= now_time) + source->found_files->emplace(modified, std::move(found_file)); + ++file_count; + } else { + // This file has been read. Try to delete it. Ignore any errors because + // the file may be un-removeable by this process. It could, for example, + // have been created by a privileged process like setup.exe. Even if it + // is not removed, it will continue to be ignored bacuse of the older + // modification time. + base::DeleteFile(found_file.path, /*recursive=*/false); + ++delete_count; + } } - // Ignore non-PMA (Persistent Memory Allocator) files. - if (found_file.path.Extension() != - base::PersistentMemoryAllocator::kFileExtension) { - continue; - } - - // Process real files. - total_size_kib += found_file.info.GetSize() >> 10; - base::Time modified = found_file.info.GetLastModifiedTime(); - if (modified > source->last_seen) { - // This file hasn't been read. Remember it (unless it's from the future). - if (modified <= now_time) - found_files.emplace(modified, std::move(found_file)); - ++file_count; - } else { - // This file has been read. Try to delete it. Ignore any errors because - // the file may be un-removeable by this process. It could, for example, - // have been created by a privileged process like setup.exe. Even if it - // is not removed, it will continue to be ignored bacuse of the older - // modification time. - base::DeleteFile(found_file.path, /*recursive=*/false); - ++delete_count; - } + UMA_HISTOGRAM_COUNTS_100("UMA.FileMetricsProvider.DirectoryFiles", + file_count); } - UMA_HISTOGRAM_COUNTS_100("UMA.FileMetricsProvider.DirectoryFiles", - file_count); - // Filter files from the front until one is found for processing. bool have_file = false; - while (!found_files.empty()) { - const FoundFile& found = found_files.begin()->second; + while (!source->found_files->empty()) { + SourceInfo::FoundFile found = + std::move(source->found_files->begin()->second); + source->found_files->erase(source->found_files->begin()); + bool too_many = source->max_dir_files > 0 && file_count > source->max_dir_files; bool too_big = @@ -350,7 +365,6 @@ RecordAccessResult(too_many ? ACCESS_RESULT_TOO_MANY_FILES : too_big ? ACCESS_RESULT_TOO_MANY_BYTES : ACCESS_RESULT_TOO_OLD); - found_files.erase(found_files.begin()); continue; } @@ -364,7 +378,6 @@ // Record the result. Success will be recorded by the caller. if (result != ACCESS_RESULT_THIS_PID) RecordAccessResult(result); - found_files.erase(found_files.begin()); } UMA_HISTOGRAM_COUNTS_100("UMA.FileMetricsProvider.DeletedFiles", @@ -442,6 +455,11 @@ // When there are no more files, ACCESS_RESULT_DOESNT_EXIST will be // returned and the loop will exit above. } while (result != ACCESS_RESULT_SUCCESS && !source->directory.empty()); + + // If the set of known files is empty, clear the object so the next run + // will do a fresh scan of the directory. + if (source->found_files && source->found_files->empty()) + source->found_files.reset(); } }
diff --git a/components/metrics/file_metrics_provider_unittest.cc b/components/metrics/file_metrics_provider_unittest.cc index f668955e..350ce67 100644 --- a/components/metrics/file_metrics_provider_unittest.cc +++ b/components/metrics/file_metrics_provider_unittest.cc
@@ -523,6 +523,14 @@ base::PersistentMemoryAllocator::MEMORY_DELETED); }); + { + base::File empty(metrics_files.GetPath().AppendASCII("h4.pma"), + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + } + base::TouchFile(metrics_files.GetPath().AppendASCII("h4.pma"), + base_time + base::TimeDelta::FromMinutes(4), + base_time + base::TimeDelta::FromMinutes(4)); + // Register the file and allow the "checker" task to run. provider()->RegisterSource(FileMetricsProvider::Params( metrics_files.GetPath(), @@ -534,6 +542,7 @@ EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h1.pma"))); EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma"))); EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma"))); + EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma"))); // H1 should be skipped and H2 available. OnDidCreateMetricsLog(); @@ -542,13 +551,16 @@ EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h1.pma"))); EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma"))); EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma"))); + EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma"))); - // Nothing else should be found. + // Nothing else should be found but the last (valid but empty) file will + // stick around to be processed later (should it get expanded). OnDidCreateMetricsLog(); RunTasks(); EXPECT_EQ(0U, GetIndependentHistogramCount()); EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma"))); EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma"))); + EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma"))); } TEST_P(FileMetricsProviderTest, AccessTimeLimitedDirectory) {
diff --git a/components/prefs/pref_notifier_impl.cc b/components/prefs/pref_notifier_impl.cc index 0e4d0cb..525e146 100644 --- a/components/prefs/pref_notifier_impl.cc +++ b/components/prefs/pref_notifier_impl.cc
@@ -71,8 +71,8 @@ all_prefs_pref_observers_.RemoveObserver(observer); } -void PrefNotifierImpl::AddInitObserver(base::Callback<void(bool)> obs) { - init_observers_.push_back(obs); +void PrefNotifierImpl::AddInitObserver(base::OnceCallback<void(bool)> obs) { + init_observers_.push_back(std::move(obs)); } void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) { @@ -82,14 +82,14 @@ void PrefNotifierImpl::OnInitializationCompleted(bool succeeded) { DCHECK(thread_checker_.CalledOnValidThread()); - // We must make a copy of init_observers_ and clear it before we run + // We must move init_observers_ to a local variable before we run // observers, or we can end up in this method re-entrantly before // clearing the observers list. - PrefInitObserverList observers(init_observers_); - init_observers_.clear(); + PrefInitObserverList observers; + std::swap(observers, init_observers_); for (auto& observer : observers) - observer.Run(succeeded); + std::move(observer).Run(succeeded); } void PrefNotifierImpl::FireObservers(const std::string& path) {
diff --git a/components/prefs/pref_notifier_impl.h b/components/prefs/pref_notifier_impl.h index 68a2408..e0c6035 100644 --- a/components/prefs/pref_notifier_impl.h +++ b/components/prefs/pref_notifier_impl.h
@@ -43,7 +43,7 @@ // We run the callback once, when initialization completes. The bool // parameter will be set to true for successful initialization, // false for unsuccessful. - void AddInitObserver(base::Callback<void(bool)> observer); + void AddInitObserver(base::OnceCallback<void(bool)> observer); void SetPrefService(PrefService* pref_service); @@ -59,7 +59,7 @@ typedef base::hash_map<std::string, std::unique_ptr<PrefObserverList>> PrefObserverMap; - typedef std::list<base::Callback<void(bool)>> PrefInitObserverList; + typedef std::list<base::OnceCallback<void(bool)>> PrefInitObserverList; const PrefObserverMap* pref_observers() const { return &pref_observers_; }
diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc index e913ecf1..1adbb77 100644 --- a/components/prefs/pref_service.cc +++ b/components/prefs/pref_service.cc
@@ -365,8 +365,8 @@ pref_notifier_->RemovePrefObserver(path, obs); } -void PrefService::AddPrefInitObserver(base::Callback<void(bool)> obs) { - pref_notifier_->AddInitObserver(obs); +void PrefService::AddPrefInitObserver(base::OnceCallback<void(bool)> obs) { + pref_notifier_->AddInitObserver(std::move(obs)); } PrefRegistry* PrefService::DeprecatedGetPrefRegistry() {
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h index 2b46b19..2a013aa 100644 --- a/components/prefs/pref_service.h +++ b/components/prefs/pref_service.h
@@ -296,7 +296,7 @@ // We run the callback once, when initialization completes. The bool // parameter will be set to true for successful initialization, // false for unsuccessful. - void AddPrefInitObserver(base::Callback<void(bool)> callback); + void AddPrefInitObserver(base::OnceCallback<void(bool)> callback); // Returns the PrefRegistry object for this service. You should not // use this; the intent is for no registrations to take place after
diff --git a/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc b/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc index ab3d063..33cef50 100644 --- a/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc +++ b/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc
@@ -205,9 +205,8 @@ uuid_list.push_back(advertisement_uuid); device::BluetoothDevice::ServiceDataMap service_data_map; service_data_map[advertisement_uuid] = eid_vector; - device_->UpdateAdvertisementData(kRssi, uuid_list, service_data_map, - nullptr); + {} /* manufacturer_data */, nullptr); } scoped_refptr<device::MockBluetoothAdapter> adapter_;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc index 9d3bca65..dd45e718 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "content/browser/accessibility/accessibility_tree_formatter_blink.h" + +#include <math.h> #include <stddef.h> #include <utility> @@ -12,7 +15,6 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" -#include "content/browser/accessibility/accessibility_tree_formatter_blink.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "ui/accessibility/ax_enums.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -190,7 +192,7 @@ attr_index <= ui::AX_FLOAT_ATTRIBUTE_LAST; ++attr_index) { auto attr = static_cast<ui::AXFloatAttribute>(attr_index); - if (node.HasFloatAttribute(attr)) + if (node.HasFloatAttribute(attr) && isfinite(node.GetFloatAttribute(attr))) dict->SetDouble(ui::ToString(attr), node.GetFloatAttribute(attr)); }
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc index d370745..b7af54ee 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_win.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -4,6 +4,7 @@ #include "content/browser/accessibility/accessibility_tree_formatter.h" +#include <math.h> #include <oleacc.h> #include <stddef.h> #include <stdint.h> @@ -837,17 +838,23 @@ if (S_OK != QueryIAccessibleValue(node.Get(), ia2value.GetAddressOf())) return; // No IA2Value, we are finished with this node. - base::win::ScopedVariant currentValue; - if (ia2value->get_currentValue(currentValue.Receive()) == S_OK) - dict->SetDouble("currentValue", V_R8(currentValue.ptr())); + base::win::ScopedVariant current_value; + if (ia2value->get_currentValue(current_value.Receive()) == S_OK && + isfinite(V_R8(current_value.ptr()))) { + dict->SetDouble("currentValue", V_R8(current_value.ptr())); + } - base::win::ScopedVariant minimumValue; - if (ia2value->get_minimumValue(minimumValue.Receive()) == S_OK) - dict->SetDouble("minimumValue", V_R8(minimumValue.ptr())); + base::win::ScopedVariant minimum_value; + if (ia2value->get_minimumValue(minimum_value.Receive()) == S_OK && + isfinite(V_R8(minimum_value.ptr()))) { + dict->SetDouble("minimumValue", V_R8(minimum_value.ptr())); + } - base::win::ScopedVariant maximumValue; - if (ia2value->get_maximumValue(maximumValue.Receive()) == S_OK) - dict->SetDouble("maximumValue", V_R8(maximumValue.ptr())); + base::win::ScopedVariant maximum_value; + if (ia2value->get_maximumValue(maximum_value.Receive()) == S_OK && + isfinite(V_R8(maximum_value.ptr()))) { + dict->SetDouble("maximumValue", V_R8(maximum_value.ptr())); + } } base::string16 AccessibilityTreeFormatterWin::ProcessTreeForOutput(
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index a49c9b7..b817c9f 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -2739,7 +2739,8 @@ if (IsMenuRelated(browserAccessibility_->GetRole())) [actions addObject:NSAccessibilityCancelAction]; - if ([self internalRole] == ui::AX_ROLE_SLIDER) { + if ([self internalRole] == ui::AX_ROLE_SLIDER || + [self internalRole] == ui::AX_ROLE_SPIN_BUTTON) { [actions addObjectsFromArray:@[ NSAccessibilityIncrementAction, NSAccessibilityDecrementAction ]];
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc index a4dad94..8d245a2a 100644 --- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -189,6 +189,11 @@ } IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, + AccessibilityEventsAriaSliderValueBothChange) { + RunEventTest(FILE_PATH_LITERAL("aria-slider-value-both-change.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, AccessibilityEventsAriaSliderValueChange) { RunEventTest(FILE_PATH_LITERAL("aria-slider-value-change.html")); } @@ -199,6 +204,21 @@ } IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, + AccessibilityEventsAriaSpinButtonValueBothChange) { + RunEventTest(FILE_PATH_LITERAL("aria-spinbutton-value-both-change.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, + AccessibilityEventsAriaSpinButtonValueChange) { + RunEventTest(FILE_PATH_LITERAL("aria-spinbutton-value-change.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, + AccessibilityEventsAriaSpinButtonValueTextChange) { + RunEventTest(FILE_PATH_LITERAL("aria-spinbutton-valuetext-change.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, AccessibilityEventsAddAlert) { RunEventTest(FILE_PATH_LITERAL("add-alert.html")); }
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h index bddb5857..cc3a0206 100644 --- a/content/browser/bad_message.h +++ b/content/browser/bad_message.h
@@ -216,6 +216,7 @@ RFH_INTERFACE_PROVIDER_SUPERFLUOUS = 189, AIRH_UNEXPECTED_BITSTREAM = 190, ARH_UNEXPECTED_BITSTREAM = 191, + RDH_NULL_CLIENT = 192, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 67035a5..a677791 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -2228,6 +2228,12 @@ const net::NetworkTrafficAnnotationTag& traffic_annotation) { DCHECK_EQ(mojom::kURLLoadOptionNone, options & ~mojom::kURLLoadOptionSynchronous); + if (!url_loader_client) { + VLOG(1) << "Killed renderer for null client"; + bad_message::ReceivedBadMessage(requester_info->filter(), + bad_message::RDH_NULL_CLIENT); + return; + } bool is_sync_load = options & mojom::kURLLoadOptionSynchronous; OnRequestResourceInternal(requester_info, routing_id, request_id, is_sync_load, request, std::move(mojo_request),
diff --git a/content/browser/media/session/media_session_controller.h b/content/browser/media/session/media_session_controller.h index ca3a8874..3a9ea7c 100644 --- a/content/browser/media/session/media_session_controller.h +++ b/content/browser/media/session/media_session_controller.h
@@ -44,7 +44,7 @@ // Must be called when a pause occurs on the renderer side media player; keeps // the MediaSession instance in sync with renderer side behavior. - void OnPlaybackPaused(); + virtual void OnPlaybackPaused(); // MediaSessionObserver implementation. void OnSuspend(int player_id) override;
diff --git a/content/browser/media/session/media_session_controllers_manager.cc b/content/browser/media/session/media_session_controllers_manager.cc index c0eecf5..6eb3dd4 100644 --- a/content/browser/media/session/media_session_controllers_manager.cc +++ b/content/browser/media/session/media_session_controllers_manager.cc
@@ -28,8 +28,7 @@ MediaSessionControllersManager::MediaSessionControllersManager( MediaWebContentsObserver* media_web_contents_observer) - : media_web_contents_observer_(media_web_contents_observer) { -} + : media_web_contents_observer_(media_web_contents_observer) {} MediaSessionControllersManager::~MediaSessionControllersManager() = default;
diff --git a/content/browser/media/session/media_session_controllers_manager.h b/content/browser/media/session/media_session_controllers_manager.h index 11e248e9..cf991cc 100644 --- a/content/browser/media/session/media_session_controllers_manager.h +++ b/content/browser/media/session/media_session_controllers_manager.h
@@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/time/time.h" +#include "content/common/content_export.h" #include "content/public/browser/web_contents_observer.h" // For MediaPlayerId. namespace media { @@ -25,9 +26,11 @@ // MediaSessionControllersManager is a delegate of MediaWebContentsObserver that // handles MediaSessionController instances. -class MediaSessionControllersManager { +class CONTENT_EXPORT MediaSessionControllersManager { public: using MediaPlayerId = WebContentsObserver::MediaPlayerId; + using ControllersMap = + std::map<MediaPlayerId, std::unique_ptr<MediaSessionController>>; explicit MediaSessionControllersManager( MediaWebContentsObserver* media_web_contents_observer); @@ -52,11 +55,11 @@ void OnEnd(const MediaPlayerId& id); private: + friend class MediaSessionControllersManagerTest; + // Weak pointer because |this| is owned by |media_web_contents_observer_|. MediaWebContentsObserver* const media_web_contents_observer_; - using ControllersMap = - std::map<MediaPlayerId, std::unique_ptr<MediaSessionController>>; ControllersMap controllers_map_; DISALLOW_COPY_AND_ASSIGN(MediaSessionControllersManager);
diff --git a/content/browser/media/session/media_session_controllers_manager_unittest.cc b/content/browser/media/session/media_session_controllers_manager_unittest.cc new file mode 100644 index 0000000..6177149 --- /dev/null +++ b/content/browser/media/session/media_session_controllers_manager_unittest.cc
@@ -0,0 +1,209 @@ +// Copyright 2017 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/media/session/media_session_controllers_manager.h" + +#include "base/command_line.h" +#include "build/build_config.h" +#include "content/browser/media/session/media_session_controller.h" +#include "content/test/test_render_view_host.h" +#include "content/test/test_web_contents.h" +#include "media/base/media_content_type.h" +#include "media/base/media_switches.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::StrictMock; + +namespace content { + +namespace { + +class MockMediaSessionController : public MediaSessionController { + public: + MockMediaSessionController( + const WebContentsObserver::MediaPlayerId& id, + MediaWebContentsObserver* media_web_contents_observer) + : MediaSessionController(id, media_web_contents_observer) {} + + MOCK_METHOD0(OnPlaybackPaused, void()); +}; + +} // namespace + +class MediaSessionControllersManagerTest + : public RenderViewHostImplTestHarness, + public ::testing::WithParamInterface<std::tuple<bool, bool>> { + public: + // Indices of the tuple parameters. + static const int kIsInternalMediaSessionEnabled = 0; + static const int kIsAudioFocusEnabled = 1; + + void SetUp() override { + RenderViewHostImplTestHarness::SetUp(); + +#if !defined(OS_ANDROID) + if (IsInternalMediaSessionEnabled()) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableInternalMediaSession); + } + if (IsAudioFocusEnabled()) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableAudioFocus); + } +#endif + + media_player_id_ = MediaSessionControllersManager::MediaPlayerId( + contents()->GetMainFrame(), 1); + mock_media_session_controller_ = + std::make_unique<StrictMock<MockMediaSessionController>>( + media_player_id_, contents()->media_web_contents_observer()); + mock_media_session_controller_ptr_ = mock_media_session_controller_.get(); + manager_ = std::make_unique<MediaSessionControllersManager>( + contents()->media_web_contents_observer()); + } + + bool IsInternalMediaSessionEnabled() const { + return std::get<kIsInternalMediaSessionEnabled>(GetParam()); + } + + bool IsAudioFocusEnabled() const { + return std::get<kIsAudioFocusEnabled>(GetParam()); + } + + bool IsMediaSessionEnabled() const { +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) + return true; +#else + return IsInternalMediaSessionEnabled() || IsAudioFocusEnabled(); +#endif + } + + MediaSessionControllersManager::ControllersMap* GetControllersMap() { + return &manager_->controllers_map_; + } + + void TearDown() override { + manager_.reset(); + RenderViewHostImplTestHarness::TearDown(); + } + + protected: + MediaSessionControllersManager::MediaPlayerId media_player_id_; + std::unique_ptr<StrictMock<MockMediaSessionController>> + mock_media_session_controller_; + StrictMock<MockMediaSessionController>* mock_media_session_controller_ptr_ = + nullptr; + std::unique_ptr<MediaSessionControllersManager> manager_; +}; + +TEST_P(MediaSessionControllersManagerTest, RequestPlayAddsSessionsToMap) { + EXPECT_TRUE(GetControllersMap()->empty()); + + EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false, + media::MediaContentType::Transient)); + if (!IsMediaSessionEnabled()) { + EXPECT_TRUE(GetControllersMap()->empty()); + } else { + EXPECT_EQ(1U, GetControllersMap()->size()); + EXPECT_TRUE( + manager_->RequestPlay(MediaSessionControllersManager::MediaPlayerId( + contents()->GetMainFrame(), 2), + true, false, media::MediaContentType::Transient)); + EXPECT_EQ(2U, GetControllersMap()->size()); + } +} + +TEST_P(MediaSessionControllersManagerTest, RepeatAddsOfInitializablePlayer) { + // If not enabled, no adds will occur, as RequestPlay returns early. + if (!IsMediaSessionEnabled()) + return; + + EXPECT_TRUE(GetControllersMap()->empty()); + + EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false, + media::MediaContentType::Transient)); + EXPECT_EQ(1U, GetControllersMap()->size()); + + EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false, + media::MediaContentType::Transient)); + EXPECT_EQ(1U, GetControllersMap()->size()); +} + +TEST_P(MediaSessionControllersManagerTest, RenderFrameDeletedRemovesHost) { + EXPECT_TRUE(GetControllersMap()->empty()); + + // Nothing should be removed if not enabled. + if (!IsMediaSessionEnabled()) { + // Artifically add controller to show early return. + GetControllersMap()->insert(std::make_pair( + media_player_id_, std::move(mock_media_session_controller_))); + EXPECT_EQ(1U, GetControllersMap()->size()); + + manager_->RenderFrameDeleted(contents()->GetMainFrame()); + EXPECT_EQ(1U, GetControllersMap()->size()); + } else { + EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false, + media::MediaContentType::Transient)); + EXPECT_EQ(1U, GetControllersMap()->size()); + + manager_->RenderFrameDeleted(contents()->GetMainFrame()); + EXPECT_TRUE(GetControllersMap()->empty()); + } +} + +TEST_P(MediaSessionControllersManagerTest, OnPauseCallsPlaybackPaused) { + // Artifically add controller to show early return. + GetControllersMap()->insert(std::make_pair( + media_player_id_, std::move(mock_media_session_controller_))); + if (IsMediaSessionEnabled()) + EXPECT_CALL(*mock_media_session_controller_ptr_, OnPlaybackPaused()); + + manager_->OnPause(media_player_id_); +} + +TEST_P(MediaSessionControllersManagerTest, OnPauseIdNotFound) { + // If MediaSession is not enabled, we don't remove anything, nothing to check. + if (!IsMediaSessionEnabled()) + return; + + // Artifically add controller to show early return. + GetControllersMap()->insert(std::make_pair( + media_player_id_, std::move(mock_media_session_controller_))); + + MediaSessionControllersManager::MediaPlayerId id2 = + MediaSessionControllersManager::MediaPlayerId(contents()->GetMainFrame(), + 2); + manager_->OnPause(id2); +} + +TEST_P(MediaSessionControllersManagerTest, OnEndRemovesMediaPlayerId) { + EXPECT_TRUE(GetControllersMap()->empty()); + + // No op if not enabled. + if (!IsMediaSessionEnabled()) { + // Artifically add controller to show early return. + GetControllersMap()->insert(std::make_pair( + media_player_id_, std::move(mock_media_session_controller_))); + EXPECT_EQ(1U, GetControllersMap()->size()); + + manager_->OnEnd(media_player_id_); + EXPECT_EQ(1U, GetControllersMap()->size()); + } else { + EXPECT_TRUE(manager_->RequestPlay(media_player_id_, true, false, + media::MediaContentType::Transient)); + EXPECT_EQ(1U, GetControllersMap()->size()); + + manager_->OnEnd(media_player_id_); + EXPECT_TRUE(GetControllersMap()->empty()); + } +} + +// First bool is to indicate whether InternalMediaSession is enabled. +// Second bool is to indicate whether AudioFocus is enabled. +INSTANTIATE_TEST_CASE_P(MediaSessionEnabledTestInstances, + MediaSessionControllersManagerTest, + ::testing::Combine(::testing::Bool(), + ::testing::Bool())); +} // namespace content
diff --git a/content/network/http_server_properties_pref_delegate.cc b/content/network/http_server_properties_pref_delegate.cc index 10fd007..af2aebc9 100644 --- a/content/network/http_server_properties_pref_delegate.cc +++ b/content/network/http_server_properties_pref_delegate.cc
@@ -43,7 +43,7 @@ // too. if (pref_service_->GetInitializationStatus() == PrefService::INITIALIZATION_STATUS_WAITING) { - pref_service_->AddPrefInitObserver(base::Bind( + pref_service_->AddPrefInitObserver(base::BindOnce( [](const base::Closure& callback, bool) { callback.Run(); }, callback)); } }
diff --git a/content/public/test/test_url_loader_client.h b/content/public/test/test_url_loader_client.h index e73cfea..2b20d18f 100644 --- a/content/public/test/test_url_loader_client.h +++ b/content/public/test/test_url_loader_client.h
@@ -26,7 +26,7 @@ // TestURLLoaderClient client; // factory_->CreateLoaderAndStart(..., client.CreateInterfacePtr(), ...); // client.RunUntilComplete(); -// EXPECT_EQ(net::OK, client.status().error_code); +// EXPECT_EQ(net::OK, client.completion_status().error_code); // ... class TestURLLoaderClient final : public mojom::URLLoaderClient { public:
diff --git a/content/shell/browser/layout_test/devtools_protocol_test_bindings.cc b/content/shell/browser/layout_test/devtools_protocol_test_bindings.cc index deedca5d..9653884 100644 --- a/content/shell/browser/layout_test/devtools_protocol_test_bindings.cc +++ b/content/shell/browser/layout_test/devtools_protocol_test_bindings.cc
@@ -4,6 +4,7 @@ #include "content/shell/browser/layout_test/devtools_protocol_test_bindings.h" +#include "base/command_line.h" #include "base/json/json_reader.h" #include "base/json/string_escape.h" #include "base/strings/string_number_conversions.h" @@ -13,6 +14,7 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" +#include "content/shell/common/layout_test/layout_test_switches.h" #if !defined(OS_ANDROID) #include "content/public/browser/devtools_frontend_host.h" @@ -52,7 +54,10 @@ if (spec.rfind(".js") != spec.length() - 3) return test_url; spec = spec.substr(0, pos + dir.length()) + - "resources/inspector-protocol-test.html?" + spec; + "resources/inspector-protocol-test.html?test=" + spec; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDebugDevTools)) + spec += "&debug=true"; *is_protocol_test = true; return GURL(spec); }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 9e7e596d..f92337c3 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1326,6 +1326,7 @@ "../browser/media/midi_host_unittest.cc", "../browser/media/session/audio_focus_manager_unittest.cc", "../browser/media/session/media_session_controller_unittest.cc", + "../browser/media/session/media_session_controllers_manager_unittest.cc", "../browser/media/session/media_session_impl_service_routing_unittest.cc", "../browser/media/session/media_session_impl_uma_unittest.cc", "../browser/media/session/media_session_uma_helper_unittest.cc",
diff --git a/content/test/data/accessibility/aria/aria-spinbutton-expected-android.txt b/content/test/data/accessibility/aria/aria-spinbutton-expected-android.txt index b2cdc66a..6c8e6bc6 100644 --- a/content/test/data/accessibility/aria/aria-spinbutton-expected-android.txt +++ b/content/test/data/accessibility/aria/aria-spinbutton-expected-android.txt
@@ -1,2 +1,3 @@ android.webkit.WebView focusable focused scrollable ++android.widget.EditText role_description='spin button' clickable focusable name='Inner text' range_current_value=5 +++android.widget.EditText role_description='spin button' clickable focusable name='Inner text' range_min=1 range_max=10 range_current_value=5
diff --git a/content/test/data/accessibility/aria/aria-spinbutton-expected-mac.txt b/content/test/data/accessibility/aria/aria-spinbutton-expected-mac.txt index 41e3b9a4..22a5970 100644 --- a/content/test/data/accessibility/aria/aria-spinbutton-expected-mac.txt +++ b/content/test/data/accessibility/aria/aria-spinbutton-expected-mac.txt
@@ -1,3 +1,5 @@ AXWebArea AXRoleDescription='HTML content' ++AXIncrementor AXRoleDescription='stepper' AXValue='5' ++++AXStaticText AXRoleDescription='text' AXValue='Inner text' +++AXIncrementor AXRoleDescription='stepper' AXValue='5' +++++AXStaticText AXRoleDescription='text' AXValue='Inner text'
diff --git a/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt b/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt index 8241a5f..91294fe 100644 --- a/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt +++ b/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt
@@ -1,3 +1,5 @@ -ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>' -++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE valuetext:5 ia2_hypertext='Inner text' +ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1>' +++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE valuetext:5 ia2_hypertext='Inner text' currentValue=5.00 +++++ROLE_SYSTEM_STATICTEXT name='Inner text' ia2_hypertext='Inner text' +++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE valuetext:5 ia2_hypertext='Inner text' currentValue=5.00 minimumValue=1.00 maximumValue=10.00 ++++ROLE_SYSTEM_STATICTEXT name='Inner text' ia2_hypertext='Inner text'
diff --git a/content/test/data/accessibility/aria/aria-spinbutton.html b/content/test/data/accessibility/aria/aria-spinbutton.html index ae5d52c2..f5fa0ab 100644 --- a/content/test/data/accessibility/aria/aria-spinbutton.html +++ b/content/test/data/accessibility/aria/aria-spinbutton.html
@@ -3,9 +3,15 @@ @WIN-ALLOW:ia2_hypertext=* @WIN-ALLOW:value* @WIN-DENY:value='http://*' +@WIN-ALLOW:currentValue* +@WIN-ALLOW:maximumValue* +@WIN-ALLOW:minimumValue* --> <html> <body> <div tabindex=0 role="spinbutton" aria-valuenow="5">Inner text</div> +<div tabindex=0 role="spinbutton" aria-valuenow="5" aria-valuemin="1" aria-valuemax="10"> + Inner text +</div> </body> </html>
diff --git a/content/test/data/accessibility/event/aria-slider-value-both-change.html b/content/test/data/accessibility/event/aria-slider-value-both-change.html index 7970e4c..7547c5aa 100644 --- a/content/test/data/accessibility/event/aria-slider-value-both-change.html +++ b/content/test/data/accessibility/event/aria-slider-value-both-change.html
@@ -6,8 +6,9 @@ <!DOCTYPE html> <html> <body> -<div id="slider" role="slider" aria-valuenow="1" aria-valuetext="1%" +<div id="slider" role="slider" aria-valuenow="1" aria-valuetext="1%" aria-orientation="vertical"> +</div> <script> function go() { document.getElementById('slider').setAttribute('aria-valuenow', '2');
diff --git a/content/test/data/accessibility/event/aria-slider-value-change.html b/content/test/data/accessibility/event/aria-slider-value-change.html index 7e241eb3..b8fc94f 100644 --- a/content/test/data/accessibility/event/aria-slider-value-change.html +++ b/content/test/data/accessibility/event/aria-slider-value-change.html
@@ -7,6 +7,7 @@ <html> <body> <div id="slider" role="slider" aria-valuenow="1"> +</div> <script> function go() { document.getElementById('slider').setAttribute('aria-valuenow', '2');
diff --git a/content/test/data/accessibility/event/aria-slider-valuetext-change.html b/content/test/data/accessibility/event/aria-slider-valuetext-change.html index 4dcda29..d8c46bdc 100644 --- a/content/test/data/accessibility/event/aria-slider-valuetext-change.html +++ b/content/test/data/accessibility/event/aria-slider-valuetext-change.html
@@ -6,8 +6,9 @@ <!DOCTYPE html> <html> <body> -<div id="slider" role="slider" aria-valuenow="1" aria-valuetext="1%" +<div id="slider" role="slider" aria-valuenow="1" aria-valuetext="1%" aria-orientation="vertical"> +</div> <script> function go() { document.getElementById('slider').setAttribute('aria-valuetext', '2%');
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-mac.txt b/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-mac.txt new file mode 100644 index 0000000..f162f1c --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-mac.txt
@@ -0,0 +1 @@ +AXValueChanged on AXIncrementor
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-win.txt b/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-win.txt new file mode 100644 index 0000000..2e7b489 --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-win.txt
@@ -0,0 +1 @@ +EVENT_OBJECT_VALUECHANGE on role=ROLE_SYSTEM_SPINBUTTON value="2%"
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-both-change.html b/content/test/data/accessibility/event/aria-spinbutton-value-both-change.html new file mode 100644 index 0000000..d87eac2c --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-value-both-change.html
@@ -0,0 +1,18 @@ +<!DOCTYPE html> +<!-- +@WIN-DENY:* +@WIN-ALLOW:EVENT_OBJECT_VALUECHANGE* +@MAC-DENY:AXLayoutComplete* +--> +<html> +<body> +<div id="spinbutton" role="spinbutton" aria-valuenow="1" aria-valuetext="1%"> +</div> +<script> + function go() { + document.getElementById('spinbutton').setAttribute('aria-valuenow', '2'); + document.getElementById('spinbutton').setAttribute('aria-valuetext', '2%'); + } +</script> +</body> +</html>
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-mac.txt b/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-mac.txt new file mode 100644 index 0000000..f162f1c --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-mac.txt
@@ -0,0 +1 @@ +AXValueChanged on AXIncrementor
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-win.txt b/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-win.txt new file mode 100644 index 0000000..4f40b59 --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-win.txt
@@ -0,0 +1 @@ +EVENT_OBJECT_VALUECHANGE on role=ROLE_SYSTEM_SPINBUTTON value="2"
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-change.html b/content/test/data/accessibility/event/aria-spinbutton-value-change.html new file mode 100644 index 0000000..85cf91c --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-value-change.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<!-- +@WIN-DENY:* +@WIN-ALLOW:EVENT_OBJECT_VALUECHANGE* +@MAC-DENY:AXLayoutComplete* +--> +<html> +<body> +<div id="spinbutton" role="spinbutton" aria-valuenow="1"> +</div> +<script> + function go() { + document.getElementById('spinbutton').setAttribute('aria-valuenow', '2'); + } +</script> +</body> +</html>
diff --git a/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-mac.txt b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-mac.txt new file mode 100644 index 0000000..f162f1c --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-mac.txt
@@ -0,0 +1 @@ +AXValueChanged on AXIncrementor
diff --git a/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-win.txt b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-win.txt new file mode 100644 index 0000000..2e7b489 --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-win.txt
@@ -0,0 +1 @@ +EVENT_OBJECT_VALUECHANGE on role=ROLE_SYSTEM_SPINBUTTON value="2%"
diff --git a/content/test/data/accessibility/event/aria-spinbutton-valuetext-change.html b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change.html new file mode 100644 index 0000000..b0f299e --- /dev/null +++ b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<!-- +@WIN-DENY:* +@WIN-ALLOW:EVENT_OBJECT_VALUECHANGE* +@MAC-DENY:AXLayoutComplete* +--> +<html> +<body> +<div id="spinbutton" role="spinbutton" aria-valuenow="1" aria-valuetext="1%"> +</div> +<script> + function go() { + document.getElementById('spinbutton').setAttribute('aria-valuetext', '2%'); + } +</script> +</body> +</html>
diff --git a/content/test/data/accessibility/html/input-number-expected-android.txt b/content/test/data/accessibility/html/input-number-expected-android.txt index 8ee77f0..28fe070 100644 --- a/content/test/data/accessibility/html/input-number-expected-android.txt +++ b/content/test/data/accessibility/html/input-number-expected-android.txt
@@ -1,3 +1,4 @@ android.webkit.WebView focusable focused scrollable ++android.view.View -++++android.widget.EditText role_description='spin button' clickable editable_text focusable has_non_empty_value name='1' input_type=2 text_change_added_count=1 +++++android.widget.EditText role_description='spin button' clickable editable_text focusable has_non_empty_value name='1' input_type=2 range_current_value=1 text_change_added_count=1 +++++android.widget.EditText role_description='spin button' clickable editable_text focusable has_non_empty_value name='6' input_type=2 range_min=5 range_max=10 range_current_value=6 text_change_added_count=1
diff --git a/content/test/data/accessibility/html/input-number-expected-blink.txt b/content/test/data/accessibility/html/input-number-expected-blink.txt index d46e2cb..787e16a 100644 --- a/content/test/data/accessibility/html/input-number-expected-blink.txt +++ b/content/test/data/accessibility/html/input-number-expected-blink.txt
@@ -1,9 +1,16 @@ rootWebArea ++genericContainer -++++spinButton value='1' +++++spinButton value='1' valueForRange=1.00 ++++++genericContainer ++++++++staticText name='1' ++++++++++inlineTextBox name='1' ++++++spinButton ++++++++button ++++++++button +++++spinButton value='6' valueForRange=6.00 minValueForRange=5.00 maxValueForRange=10.00 +++++++genericContainer +++++++++staticText name='6' +++++++++++inlineTextBox name='6' +++++++spinButton +++++++++button +++++++++button
diff --git a/content/test/data/accessibility/html/input-number-expected-mac.txt b/content/test/data/accessibility/html/input-number-expected-mac.txt index 2a7f16a0..f5cf1d4 100644 --- a/content/test/data/accessibility/html/input-number-expected-mac.txt +++ b/content/test/data/accessibility/html/input-number-expected-mac.txt
@@ -1,3 +1,4 @@ AXWebArea AXRoleDescription='HTML content' ++AXGroup AXRoleDescription='group' ++++AXIncrementor AXRoleDescription='stepper' AXValue='1' +++++AXIncrementor AXRoleDescription='stepper' AXValue='6'
diff --git a/content/test/data/accessibility/html/input-number-expected-win.txt b/content/test/data/accessibility/html/input-number-expected-win.txt index d3c5ac1c..0d89d868 100644 --- a/content/test/data/accessibility/html/input-number-expected-win.txt +++ b/content/test/data/accessibility/html/input-number-expected-win.txt
@@ -1,3 +1,4 @@ ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>' -++IA2_ROLE_SECTION ia2_hypertext='<obj0>' -++++ROLE_SYSTEM_SPINBUTTON value='1' FOCUSABLE valuetext:1 text-input-type:number ia2_hypertext='1' +++IA2_ROLE_SECTION ia2_hypertext='<obj0><obj1>' +++++ROLE_SYSTEM_SPINBUTTON value='1' FOCUSABLE valuetext:1 text-input-type:number ia2_hypertext='1' currentValue=1.00 +++++ROLE_SYSTEM_SPINBUTTON value='6' FOCUSABLE valuetext:6 text-input-type:number ia2_hypertext='6' currentValue=6.00 minimumValue=5.00 maximumValue=10.00
diff --git a/content/test/data/accessibility/html/input-number.html b/content/test/data/accessibility/html/input-number.html index 7137262..2433542 100644 --- a/content/test/data/accessibility/html/input-number.html +++ b/content/test/data/accessibility/html/input-number.html
@@ -5,11 +5,19 @@ @WIN-ALLOW:text-input-type* @WIN-ALLOW:value* @WIN-DENY:value='http://*' +@WIN-ALLOW:currentValue* +@WIN-ALLOW:maximumValue* +@WIN-ALLOW:minimumValue* +@WIN-ALLOW:valuetext* @BLINK-ALLOW:value* -@BLINK-DENY:value='http://* +@BLINK-ALLOW:currentValue* +@BLINK-ALLOW:maximumValue* +@BLINK-ALLOW:minimumValue* +@BLINK-ALLOW:valuetext* --> <html> <body> <input type="number" value="1"> + <input type="number" value="6" min="5" max="10"> </body> </html>
diff --git a/content/test/data/accessibility/html/input-range-expected-win.txt b/content/test/data/accessibility/html/input-range-expected-win.txt index 63fae26..d24877b 100644 --- a/content/test/data/accessibility/html/input-range-expected-win.txt +++ b/content/test/data/accessibility/html/input-range-expected-win.txt
@@ -1,3 +1,3 @@ ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>' ++IA2_ROLE_SECTION ia2_hypertext='<obj0>' -++++ROLE_SYSTEM_SLIDER FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:slider valuetext:5 currentValue=5.00 \ No newline at end of file +++++ROLE_SYSTEM_SLIDER FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:slider valuetext:5 currentValue=5.00 minimumValue=1.00 maximumValue=10.00
diff --git a/content/test/data/accessibility/html/input-range.html b/content/test/data/accessibility/html/input-range.html index b5f2aee..e5699fb 100644 --- a/content/test/data/accessibility/html/input-range.html +++ b/content/test/data/accessibility/html/input-range.html
@@ -2,11 +2,15 @@ @WIN-DENY:description* @WIN-DENY:name* @WIN-ALLOW:currentValue* +@WIN-ALLOW:maximumValue* +@WIN-ALLOW:minimumValue* @WIN-ALLOW:ia2_hypertext=* @WIN-ALLOW:valuetext* @WIN-ALLOW:xml-roles:* @BLINK-ALLOW:value* @BLINK-ALLOW:currentValue* +@BLINK-ALLOW:maximumValue* +@BLINK-ALLOW:minimumValue* @BLINK-ALLOW:valuetext* --> <html>
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java index dbe0e697..0f94a8e 100644 --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java
@@ -13,6 +13,7 @@ import android.content.IntentFilter; import android.os.Build; import android.os.ParcelUuid; +import android.util.SparseArray; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; @@ -20,6 +21,7 @@ import org.chromium.components.location.LocationUtils; import java.util.List; +import java.util.Map; /** * Exposes android.bluetooth.BluetoothAdapter as necessary for C++ @@ -254,9 +256,45 @@ } } - nativeCreateOrUpdateDeviceOnScan(mNativeBluetoothAdapterAndroid, - result.getDevice().getAddress(), result.getDevice(), result.getRssi(), - uuid_strings, result.getScanRecord_getTxPowerLevel()); + String[] serviceDataKeys; + byte[][] serviceDataValues; + Map<ParcelUuid, byte[]> serviceData = result.getScanRecord_getServiceData(); + if (serviceData == null) { + serviceDataKeys = new String[] {}; + serviceDataValues = new byte[][] {}; + } else { + serviceDataKeys = new String[serviceData.size()]; + serviceDataValues = new byte[serviceData.size()][]; + int i = 0; + for (Map.Entry<ParcelUuid, byte[]> serviceDataItem : serviceData.entrySet()) { + serviceDataKeys[i] = serviceDataItem.getKey().toString(); + serviceDataValues[i++] = serviceDataItem.getValue(); + } + } + + int[] manufacturerDataKeys; + byte[][] manufacturerDataValues; + SparseArray<byte[]> manufacturerData = + result.getScanRecord_getManufacturerSpecificData(); + if (manufacturerData == null) { + manufacturerDataKeys = new int[] {}; + manufacturerDataValues = new byte[][] {}; + } else { + manufacturerDataKeys = new int[manufacturerData.size()]; + manufacturerDataValues = new byte[manufacturerData.size()][]; + for (int i = 0; i < manufacturerData.size(); i++) { + manufacturerDataKeys[i] = manufacturerData.keyAt(i); + manufacturerDataValues[i] = manufacturerData.valueAt(i); + } + } + + // Object can be destroyed, but Android keeps calling onScanResult. + if (mNativeBluetoothAdapterAndroid != 0) { + nativeCreateOrUpdateDeviceOnScan(mNativeBluetoothAdapterAndroid, + result.getDevice().getAddress(), result.getDevice(), result.getRssi(), + uuid_strings, result.getScanRecord_getTxPowerLevel(), serviceDataKeys, + serviceDataValues, manufacturerDataKeys, manufacturerDataValues); + } } @Override @@ -317,7 +355,8 @@ // http://crbug.com/505554 private native void nativeCreateOrUpdateDeviceOnScan(long nativeBluetoothAdapterAndroid, String address, Object bluetoothDeviceWrapper, int rssi, String[] advertisedUuids, - int txPower); + int txPower, String[] serviceDataKeys, Object[] serviceDataValues, + int[] manufacturerDataKeys, Object[] manufacturerDataValues); // Binds to BluetoothAdapterAndroid::nativeOnAdapterStateChanged private native void nativeOnAdapterStateChanged(
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java index 66f0f68..c8bf2f82 100644 --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java
@@ -23,6 +23,7 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.ParcelUuid; +import android.util.SparseArray; import org.chromium.base.ContextUtils; import org.chromium.base.Log; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; /** @@ -297,6 +299,14 @@ return mScanResult.getScanRecord().getServiceUuids(); } + public Map<ParcelUuid, byte[]> getScanRecord_getServiceData() { + return mScanResult.getScanRecord().getServiceData(); + } + + public SparseArray<byte[]> getScanRecord_getManufacturerSpecificData() { + return mScanResult.getScanRecord().getManufacturerSpecificData(); + } + public int getScanRecord_getTxPowerLevel() { return mScanResult.getScanRecord().getTxPowerLevel(); }
diff --git a/device/bluetooth/bluetooth_adapter_android.cc b/device/bluetooth/bluetooth_adapter_android.cc index e3aa67f6..d034e36 100644 --- a/device/bluetooth/bluetooth_adapter_android.cc +++ b/device/bluetooth/bluetooth_adapter_android.cc
@@ -16,6 +16,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "device/bluetooth/android/wrappers.h" #include "device/bluetooth/bluetooth_advertisement.h" +#include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_device_android.h" #include "device/bluetooth/bluetooth_discovery_session_outcome.h" #include "jni/ChromeBluetoothAdapter_jni.h" @@ -25,6 +26,9 @@ using base::android::AppendJavaStringArrayToStringVector; using base::android::JavaParamRef; using base::android::JavaRef; +using base::android::JavaByteArrayToByteVector; +using base::android::JavaArrayOfByteArrayToStringVector; +using base::android::JavaIntArrayToIntVector; namespace { // The poll interval in ms when there is no active discovery. This @@ -176,7 +180,13 @@ bluetooth_device_wrapper, // Java Type: bluetoothDeviceWrapper int32_t rssi, const JavaParamRef<jobjectArray>& advertised_uuids, // Java Type: String[] - int32_t tx_power) { + int32_t tx_power, + const JavaParamRef<jobjectArray>& service_data_keys, // Java Type: String[] + const JavaParamRef<jobjectArray>& service_data_values, // Java Type: byte[] + const JavaParamRef<jintArray>& manufacturer_data_keys, // Java Type: int[] + const JavaParamRef<jobjectArray>& + manufacturer_data_values // Java Type: byte[] + ) { std::string device_address = ConvertJavaStringToUTF8(env, address); auto iter = devices_.find(device_address); @@ -204,11 +214,39 @@ advertised_bluetooth_uuids.push_back(BluetoothUUID(std::move(uuid))); } + std::vector<std::string> service_data_keys_vector; + std::vector<std::string> service_data_values_vector; + AppendJavaStringArrayToStringVector(env, service_data_keys, + &service_data_keys_vector); + JavaArrayOfByteArrayToStringVector(env, service_data_values, + &service_data_values_vector); + BluetoothDeviceAndroid::ServiceDataMap service_data_map; + for (size_t i = 0; i < service_data_keys_vector.size(); i++) { + service_data_map.insert( + {BluetoothUUID(service_data_keys_vector[i]), + std::vector<uint8_t>(service_data_values_vector[i].begin(), + service_data_values_vector[i].end())}); + } + + std::vector<jint> manufacturer_data_keys_vector; + std::vector<std::string> manufacturer_data_values_vector; + JavaIntArrayToIntVector(env, manufacturer_data_keys, + &manufacturer_data_keys_vector); + JavaArrayOfByteArrayToStringVector(env, manufacturer_data_values, + &manufacturer_data_values_vector); + BluetoothDeviceAndroid::ManufacturerDataMap manufacturer_data_map; + for (size_t i = 0; i < manufacturer_data_keys_vector.size(); i++) { + manufacturer_data_map.insert( + {static_cast<uint16_t>(manufacturer_data_keys_vector[i]), + std::vector<uint8_t>(manufacturer_data_values_vector[i].begin(), + manufacturer_data_values_vector[i].end())}); + } + int8_t clamped_tx_power = BluetoothDevice::ClampPower(tx_power); device_android->UpdateAdvertisementData( BluetoothDevice::ClampPower(rssi), std::move(advertised_bluetooth_uuids), - {} /* service_data */, + service_data_map, manufacturer_data_map, // Android uses INT32_MIN to indicate no Advertised Tx Power. // https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel() tx_power == INT32_MIN ? nullptr : &clamped_tx_power);
diff --git a/device/bluetooth/bluetooth_adapter_android.h b/device/bluetooth/bluetooth_adapter_android.h index d6c2ba3..5c04c34a 100644 --- a/device/bluetooth/bluetooth_adapter_android.h +++ b/device/bluetooth/bluetooth_adapter_android.h
@@ -102,7 +102,16 @@ int32_t rssi, const base::android::JavaParamRef<jobjectArray>& advertised_uuids, // Java Type: String[] - int32_t tx_power); + int32_t tx_power, + const base::android::JavaParamRef<jobjectArray>& + service_data_keys, // Java Type: String[] + const base::android::JavaParamRef<jobjectArray>& + service_data_values, // Java Type: byte[] + const base::android::JavaParamRef<jintArray>& + manufacturer_data_keys, // Java Type: int[] + const base::android::JavaParamRef<jobjectArray>& + manufacturer_data_values // Java Type: byte[] + ); protected: BluetoothAdapterAndroid();
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm index b01cc27c..9384b8f 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -523,6 +523,8 @@ << base::SysNSStringToUTF8([advertisement_data description]); // Get Advertised UUIDs + // Core Specification Supplement (CSS) v7, Part 1.1 + // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataserviceuuidskey BluetoothDevice::UUIDList advertised_uuids; NSArray* service_uuids = [advertisement_data objectForKey:CBAdvertisementDataServiceUUIDsKey]; @@ -538,6 +540,8 @@ } // Get Service Data. + // Core Specification Supplement (CSS) v7, Part 1.11 + // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataservicedatakey BluetoothDevice::ServiceDataMap service_data_map; NSDictionary* service_data = [advertisement_data objectForKey:CBAdvertisementDataServiceDataKey]; @@ -549,14 +553,36 @@ std::vector<uint8_t>(bytes, bytes + length)); } + // Get Manufacturer Data. + // "Size: 2 or more octets + // The first 2 octets contain the Company Identifier Code followed + // by additional manufacturer specific data" + // Core Specification Supplement (CSS) v7, Part 1.4 + // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatamanufacturerdatakey + // + BluetoothDevice::ManufacturerDataMap manufacturer_data_map; + NSData* manufacturer_data = + [advertisement_data objectForKey:CBAdvertisementDataManufacturerDataKey]; + const uint8_t* bytes = static_cast<const uint8_t*>([manufacturer_data bytes]); + size_t length = [manufacturer_data length]; + if (length > 1) { + const uint16_t manufacturer_id = bytes[0] | bytes[1] << 8; + manufacturer_data_map.emplace( + manufacturer_id, std::vector<uint8_t>(bytes + 2, bytes + length)); + } + // Get Tx Power. + // "Size: 1 octet + // 0xXX: -127 to +127 dBm" + // Core Specification Supplement (CSS) v7, Part 1.5 + // https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatatxpowerlevelkey NSNumber* tx_power = [advertisement_data objectForKey:CBAdvertisementDataTxPowerLevelKey]; int8_t clamped_tx_power = BluetoothDevice::ClampPower([tx_power intValue]); device_mac->UpdateAdvertisementData( BluetoothDevice::ClampPower(rssi), std::move(advertised_uuids), - std::move(service_data_map), + std::move(service_data_map), std::move(manufacturer_data_map), tx_power == nil ? nullptr : &clamped_tx_power); if (is_new_device) {
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc index aa3e976..b8988d23 100644 --- a/device/bluetooth/bluetooth_device.cc +++ b/device/bluetooth/bluetooth_device.cc
@@ -413,16 +413,19 @@ std::string BluetoothDevice::GetIdentifier() const { return GetAddress(); } -void BluetoothDevice::UpdateAdvertisementData(int8_t rssi, - UUIDList advertised_uuids, - ServiceDataMap service_data, - const int8_t* tx_power) { +void BluetoothDevice::UpdateAdvertisementData( + int8_t rssi, + UUIDList advertised_uuids, + ServiceDataMap service_data, + ManufacturerDataMap manufacturer_data, + const int8_t* tx_power) { UpdateTimestamp(); inquiry_rssi_ = rssi; device_uuids_.ReplaceAdvertisedUUIDs(std::move(advertised_uuids)); service_data_ = std::move(service_data); + manufacturer_data_ = std::move(manufacturer_data); if (tx_power != nullptr) { inquiry_tx_power_ = *tx_power; @@ -435,6 +438,7 @@ inquiry_rssi_ = base::nullopt; device_uuids_.ClearAdvertisedUUIDs(); service_data_.clear(); + manufacturer_data_.clear(); inquiry_tx_power_ = base::nullopt; GetAdapter()->NotifyDeviceChanged(this); }
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h index 91334697..69efd03 100644 --- a/device/bluetooth/bluetooth_device.h +++ b/device/bluetooth/bluetooth_device.h
@@ -547,6 +547,7 @@ void UpdateAdvertisementData(int8_t rssi, UUIDList advertised_uuids, ServiceDataMap service_data, + ManufacturerDataMap manufacturer_data, const int8_t* tx_power); // Called by BluetoothAdapter when it stops discoverying.
diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc index 6f331c13..5d62ca3 100644 --- a/device/bluetooth/bluetooth_device_unittest.cc +++ b/device/bluetooth/bluetooth_device_unittest.cc
@@ -45,6 +45,7 @@ using UUIDSet = BluetoothDevice::UUIDSet; using ServiceDataMap = BluetoothDevice::ServiceDataMap; +using ManufacturerDataMap = BluetoothDevice::ManufacturerDataMap; class BluetoothGetServiceTest : public BluetoothTest { public: @@ -174,9 +175,8 @@ EXPECT_EQ(0u, uuids.size()); } -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_LINUX) -// TODO(ortuno): Enable on Android once it supports Service Data. -// http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \ + defined(OS_ANDROID) TEST_F(BluetoothTest, GetServiceDataUUIDs_GetServiceDataForUUID) { if (!PlatformSupportsLowEnergy()) { LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; @@ -194,6 +194,7 @@ BluetoothDevice* device1 = SimulateLowEnergyDevice(4); EXPECT_TRUE(device1->GetServiceData().empty()); EXPECT_TRUE(device1->GetServiceDataUUIDs().empty()); + EXPECT_TRUE(device1->GetManufacturerData().empty()); // Receive Advertisement with service data. BluetoothDevice* device2 = SimulateLowEnergyDevice(1); @@ -204,8 +205,9 @@ device2->GetServiceDataUUIDs()); EXPECT_EQ(std::vector<uint8_t>({1}), *device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate))); - - // Receive Advertisement with no service data. + EXPECT_EQ(std::vector<uint8_t>({1, 2, 3, 4}), + *device2->GetManufacturerDataForID(kTestManufacturerId)); + // Receive Advertisement with no service and manufacturer data. SimulateLowEnergyDevice(3); // TODO(crbug.com/707039): Remove #if once the BlueZ caching behavior is @@ -223,11 +225,12 @@ #else EXPECT_TRUE(device2->GetServiceData().empty()); EXPECT_TRUE(device2->GetServiceDataUUIDs().empty()); + EXPECT_TRUE(device2->GetManufacturerData().empty()); EXPECT_EQ(nullptr, device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate))); #endif - // Receive Advertisement with new service data. + // Receive Advertisement with new service data and empty manufacturer data. SimulateLowEnergyDevice(2); EXPECT_EQ(ServiceDataMap( @@ -239,6 +242,8 @@ device2->GetServiceDataUUIDs()); EXPECT_EQ(std::vector<uint8_t>({}), *device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate))); + EXPECT_EQ(std::vector<uint8_t>({}), + *device2->GetManufacturerDataForID(kTestManufacturerId)); EXPECT_EQ( std::vector<uint8_t>({0, 2}), *device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDImmediateAlert))); @@ -260,7 +265,8 @@ BluetoothUUID(kTestUUIDImmediateAlert))); #endif // !defined(OS_LINUX) && !defined(OS_CHROMEOS) } -#endif // defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_LINUX) +#endif // defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_LINUX) || + // defined(OS_ANDROID) #if defined(OS_ANDROID) || defined(OS_MACOSX) // Tests that the Advertisement Data fields are correctly updated during @@ -288,12 +294,10 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess), BluetoothUUID(kTestUUIDGenericAttribute)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) + EXPECT_EQ(ManufacturerDataMap({{kTestManufacturerId, {1, 2, 3, 4}}}), + device->GetManufacturerData()); EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value()); // Receive Advertisement with no UUIDs, Service Data, or Tx Power, should @@ -307,11 +311,8 @@ EXPECT_EQ(ToInt8(TestRSSI::LOW), device->GetInquiryRSSI().value()); EXPECT_TRUE(device->GetUUIDs().empty()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 EXPECT_TRUE(device->GetServiceData().empty()); -#endif // defined(OS_MACOSX) + EXPECT_TRUE(device->GetManufacturerData().empty()); EXPECT_FALSE(device->GetInquiryTxPower()); // Receive Advertisement with different UUIDs, Service Data, and Tx Power, @@ -327,14 +328,13 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDImmediateAlert), BluetoothUUID(kTestUUIDLinkLoss)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 + EXPECT_EQ(ServiceDataMap( {{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})}, {BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) + EXPECT_EQ(ManufacturerDataMap({{kTestManufacturerId, {}}}), + device->GetManufacturerData()); EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value()); // Stop discovery session, should notify of device changed. @@ -342,6 +342,7 @@ // discovering. // - GetUUIDs: Should not return any UUIDs. // - GetServiceData: Should return empty map. + // - GetMAnufacturerData: Should return empty map. // - GetInquiryTxPower: Should return nullopt because we are no longer // discovering. discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED), @@ -353,11 +354,8 @@ EXPECT_FALSE(device->GetInquiryRSSI()); EXPECT_TRUE(device->GetUUIDs().empty()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 EXPECT_TRUE(device->GetServiceData().empty()); -#endif // defined(OS_MACOSX) + EXPECT_TRUE(device->GetManufacturerData().empty()); EXPECT_FALSE(device->GetInquiryTxPower()); // Discover the device again with different UUIDs, should notify of device @@ -375,12 +373,10 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess), BluetoothUUID(kTestUUIDGenericAttribute)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) + EXPECT_EQ(ManufacturerDataMap({{kTestManufacturerId, {1, 2, 3, 4}}}), + device->GetManufacturerData()); EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value()); } #endif // defined(OS_ANDROID) || defined(OS_MACOSX) @@ -622,12 +618,10 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess), BluetoothUUID(kTestUUIDGenericAttribute)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value()); // Discover services, should notify of device changed. @@ -657,14 +651,12 @@ BluetoothUUID(kTestUUIDImmediateAlert), BluetoothUUID(kTestUUIDHeartRate)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap( {{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})}, {BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value()); // Stop discovery session, should notify of device changed. @@ -683,11 +675,9 @@ EXPECT_EQ(4, observer.device_changed_count()); EXPECT_FALSE(device->GetInquiryRSSI()); EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap(), device->GetServiceData()); -#endif // defined(OS_MACOSX) +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_FALSE(device->GetInquiryTxPower()); // Disconnect device, should notify of device changed. @@ -731,9 +721,7 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess), BluetoothUUID(kTestUUIDGenericAttribute)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}), device->GetServiceData()); #endif // defined(OS_MACOSX) @@ -764,14 +752,12 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss), BluetoothUUID(kTestUUIDImmediateAlert)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap( {{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})}, {BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value()); // Discover Services, should notify of device changed. @@ -804,9 +790,7 @@ BluetoothUUID(kTestUUIDImmediateAlert)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap( {{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})}, {BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}), @@ -826,12 +810,10 @@ EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess), BluetoothUUID(kTestUUIDGenericAttribute)}), device->GetUUIDs()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}), device->GetServiceData()); -#endif // defined(OS_MACOSX) +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value()); // Stop discovery session, should notify of device changed. @@ -848,11 +830,9 @@ EXPECT_FALSE(device->GetInquiryRSSI()); EXPECT_TRUE(device->GetUUIDs().empty()); -#if defined(OS_MACOSX) - // TODO(ortuno): Enable on Android once it supports Service Data. - // http://crbug.com/639408 +#if defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_TRUE(device->GetServiceData().empty()); -#endif // defined(OS_MACOSX) +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) EXPECT_FALSE(device->GetInquiryTxPower()); } #endif // defined(OS_ANDROID) || defined(OS_MACOSX)
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/device/bluetooth/dbus/fake_bluetooth_device_client.cc index b5810ab..4b11c3f 100644 --- a/device/bluetooth/dbus/fake_bluetooth_device_client.cc +++ b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -1563,9 +1563,11 @@ properties->rssi.ReplaceValue(rssi); } -void FakeBluetoothDeviceClient::UpdateServiceData( +void FakeBluetoothDeviceClient::UpdateServiceAndManufacturerData( const dbus::ObjectPath& object_path, - const std::unordered_map<std::string, std::vector<uint8_t>>& service_data) { + const std::unordered_map<std::string, std::vector<uint8_t>>& service_data, + const std::unordered_map<uint16_t, std::vector<uint8_t>>& + manufacturer_data) { PropertiesMap::const_iterator iter = properties_map_.find(object_path); if (iter == properties_map_.end()) { VLOG(2) << "Fake device does not exist: " << object_path.value(); @@ -1574,16 +1576,23 @@ Properties* properties = iter->second.get(); DCHECK(properties); properties->service_data.set_valid(true); + properties->manufacturer_data.set_valid(true); // BlueZ caches all the previously received advertisements. To mimic BlueZ // caching behavior, merge the new data here with the existing data. // TODO(crbug.com/707039): once the BlueZ caching behavior is changed, this // needs to be updated as well. - std::unordered_map<std::string, std::vector<uint8_t>> merged_data = + std::unordered_map<std::string, std::vector<uint8_t>> merged_service_data = service_data; - merged_data.insert(properties->service_data.value().begin(), - properties->service_data.value().end()); - properties->service_data.ReplaceValue(merged_data); + merged_service_data.insert(properties->service_data.value().begin(), + properties->service_data.value().end()); + properties->service_data.ReplaceValue(merged_service_data); + + std::unordered_map<uint16_t, std::vector<uint8_t>> merged_manufacturer_data = + manufacturer_data; + merged_manufacturer_data.insert(properties->manufacturer_data.value().begin(), + properties->manufacturer_data.value().end()); + properties->manufacturer_data.ReplaceValue(merged_manufacturer_data); } void FakeBluetoothDeviceClient::UpdateConnectionInfo( @@ -1811,7 +1820,9 @@ const std::string device_address, const std::vector<std::string>& service_uuids, device::BluetoothTransport type, - const std::unordered_map<std::string, std::vector<uint8_t>>& service_data) { + const std::unordered_map<std::string, std::vector<uint8_t>>& service_data, + const std::unordered_map<uint16_t, std::vector<uint8_t>>& + manufacturer_data) { // Create a random device path. dbus::ObjectPath device_path; std::string id; @@ -1858,6 +1869,11 @@ properties->service_data.set_valid(true); } + if (!manufacturer_data.empty()) { + properties->manufacturer_data.ReplaceValue(manufacturer_data); + properties->manufacturer_data.set_valid(true); + } + properties_map_.insert(std::make_pair(device_path, std::move(properties))); device_list_.push_back(device_path); for (auto& observer : observers_)
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.h b/device/bluetooth/dbus/fake_bluetooth_device_client.h index 37446d2..2a91399c 100644 --- a/device/bluetooth/dbus/fake_bluetooth_device_client.h +++ b/device/bluetooth/dbus/fake_bluetooth_device_client.h
@@ -167,8 +167,9 @@ const std::string device_address, const std::vector<std::string>& service_uuids, device::BluetoothTransport type, - const std::unordered_map<std::string, std::vector<uint8_t>>& - service_data); + const std::unordered_map<std::string, std::vector<uint8_t>>& service_data, + const std::unordered_map<uint16_t, std::vector<uint8_t>>& + manufacturer_data); void set_delay_start_discovery(bool value) { delay_start_discovery_ = value; } @@ -176,13 +177,14 @@ // |object_path| to |rssi|, if the fake device exists. void UpdateDeviceRSSI(const dbus::ObjectPath& object_path, int16_t rssi); - // Updates the service data property of fake device with object path - // |object_path| to merge |service_data| into the existing data, + // Updates the service and manufacturer data property of fake device with + // object path |object_path| to merge |service_data| into the existing data, // if the fake device exists. - void UpdateServiceData( + void UpdateServiceAndManufacturerData( const dbus::ObjectPath& object_path, - const std::unordered_map<std::string, std::vector<uint8_t>>& - service_data); + const std::unordered_map<std::string, std::vector<uint8_t>>& service_data, + const std::unordered_map<uint16_t, std::vector<uint8_t>>& + manufacturer_data); static const char kTestPinCode[]; static const int kTestPassKey;
diff --git a/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java b/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java index b045395..805cc91 100644 --- a/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java +++ b/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java
@@ -15,6 +15,7 @@ import android.os.Build; import android.os.ParcelUuid; import android.test.mock.MockContext; +import android.util.SparseArray; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; @@ -26,6 +27,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; /** @@ -140,10 +142,18 @@ uuids.add(ParcelUuid.fromString("00001800-0000-1000-8000-00805f9b34fb")); uuids.add(ParcelUuid.fromString("00001801-0000-1000-8000-00805f9b34fb")); + HashMap<ParcelUuid, byte[]> serviceData = new HashMap<>(); + serviceData.put(ParcelUuid.fromString("0000180d-0000-1000-8000-00805f9b34fb"), + new byte[] {1}); + + SparseArray<byte[]> manufacturerData = new SparseArray<>(); + manufacturerData.put(0x00E0, new byte[] {0x01, 0x02, 0x03, 0x04}); + mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, new FakeScanResult(new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", "FakeBluetoothDevice"), - TestRSSI.LOWEST, uuids, TestTxPower.LOWEST)); + TestRSSI.LOWEST, uuids, TestTxPower.LOWEST, serviceData, + manufacturerData)); break; } case 2: { @@ -151,10 +161,20 @@ uuids.add(ParcelUuid.fromString("00001802-0000-1000-8000-00805f9b34fb")); uuids.add(ParcelUuid.fromString("00001803-0000-1000-8000-00805f9b34fb")); + HashMap<ParcelUuid, byte[]> serviceData = new HashMap<>(); + serviceData.put(ParcelUuid.fromString("0000180d-0000-1000-8000-00805f9b34fb"), + new byte[] {}); + serviceData.put(ParcelUuid.fromString("00001802-0000-1000-8000-00805f9b34fb"), + new byte[] {0, 2}); + + SparseArray<byte[]> manufacturerData = new SparseArray<>(); + manufacturerData.put(0x00E0, new byte[] {}); + mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, new FakeScanResult(new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", "FakeBluetoothDevice"), - TestRSSI.LOWER, uuids, TestTxPower.LOWER)); + TestRSSI.LOWER, uuids, TestTxPower.LOWER, serviceData, + manufacturerData)); break; } case 3: { @@ -162,7 +182,7 @@ mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, new FakeScanResult( new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", ""), - TestRSSI.LOW, uuids, NO_TX_POWER)); + TestRSSI.LOW, uuids, NO_TX_POWER, null, null)); break; } @@ -171,7 +191,7 @@ mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, new FakeScanResult( new FakeBluetoothDevice(this, "02:00:00:8B:74:63", ""), - TestRSSI.MEDIUM, uuids, NO_TX_POWER)); + TestRSSI.MEDIUM, uuids, NO_TX_POWER, null, null)); break; } @@ -180,7 +200,29 @@ mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, new FakeScanResult( new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", null), - TestRSSI.HIGH, uuids, NO_TX_POWER)); + TestRSSI.HIGH, uuids, NO_TX_POWER, null, null)); + break; + } + case 6: { + ArrayList<ParcelUuid> uuids = null; + mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, + new FakeScanResult( + new FakeBluetoothDevice(this, "02:00:00:8B:74:63", null), + TestRSSI.LOWEST, uuids, NO_TX_POWER, null, null)); + break; + } + case 7: { + ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(2); + uuids.add(ParcelUuid.fromString("f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb")); + + HashMap<ParcelUuid, byte[]> serviceData = new HashMap<>(); + serviceData.put(ParcelUuid.fromString("f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb"), + new byte[] {0, 20}); + + mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, + new FakeScanResult(new FakeBluetoothDevice( + this, "01:00:00:90:1E:BE", "U2F FakeDevice"), + TestRSSI.LOWEST, uuids, NO_TX_POWER, serviceData, null)); break; } } @@ -309,14 +351,19 @@ private final int mRssi; private final int mTxPower; private final ArrayList<ParcelUuid> mUuids; + private final Map<ParcelUuid, byte[]> mServiceData; + private final SparseArray<byte[]> mManufacturerData; - FakeScanResult( - FakeBluetoothDevice device, int rssi, ArrayList<ParcelUuid> uuids, int txPower) { + FakeScanResult(FakeBluetoothDevice device, int rssi, ArrayList<ParcelUuid> uuids, + int txPower, Map<ParcelUuid, byte[]> serviceData, + SparseArray<byte[]> manufacturerData) { super(null); mDevice = device; mRssi = rssi; mUuids = uuids; mTxPower = txPower; + mServiceData = serviceData; + mManufacturerData = manufacturerData; } @Override @@ -338,6 +385,16 @@ public int getScanRecord_getTxPowerLevel() { return mTxPower; } + + @Override + public Map<ParcelUuid, byte[]> getScanRecord_getServiceData() { + return mServiceData; + } + + @Override + public SparseArray<byte[]> getScanRecord_getManufacturerSpecificData() { + return mManufacturerData; + } } /**
diff --git a/device/bluetooth/test/bluetooth_test.cc b/device/bluetooth/test/bluetooth_test.cc index 64437f27..3d9b41c 100644 --- a/device/bluetooth/test/bluetooth_test.cc +++ b/device/bluetooth/test/bluetooth_test.cc
@@ -59,6 +59,8 @@ "00002903-0000-1000-8000-00805f9b34fb"; const char BluetoothTestBase::kTestUUIDCharacteristicPresentationFormat[] = "00002904-0000-1000-8000-00805f9b34fb"; +// Manufacturer kTestAdapterAddress +const unsigned short BluetoothTestBase::kTestManufacturerId = 0x00E0; BluetoothTestBase::BluetoothTestBase() : weak_factory_(this) {}
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h index 562a528..7a68780 100644 --- a/device/bluetooth/test/bluetooth_test.h +++ b/device/bluetooth/test/bluetooth_test.h
@@ -109,6 +109,8 @@ static const char kTestUUIDClientCharacteristicConfiguration[]; static const char kTestUUIDServerCharacteristicConfiguration[]; static const char kTestUUIDCharacteristicPresentationFormat[]; + // Manufacturer data + static const unsigned short kTestManufacturerId; BluetoothTestBase(); ~BluetoothTestBase() override; @@ -164,6 +166,7 @@ // RSSI: kTestRSSI1 // Advertised UUIDs: {kTestUUIDGenericAccess, kTestUUIDGenericAttribute} // Service Data: {kTestUUIDHeartRate: [1]} + // ManufacturerData: {kTestManufacturerId: [1, 2, 3, 4]} // Tx Power: kTestTxPower1 // 2: Name: kTestDeviceName // Address: kTestDeviceAddress1 @@ -171,18 +174,21 @@ // Advertised UUIDs: {kTestUUIDImmediateAlert, kTestUUIDLinkLoss} // Service Data: {kTestUUIDHeartRate: [], // kTestUUIDImmediateAlert: [0, 2]} + // ManufacturerData: {kTestManufacturerId: []} // Tx Power: kTestTxPower2 // 3: Name: kTestDeviceNameEmpty // Address: kTestDeviceAddress1 // RSSI: kTestRSSI3 // No Advertised UUIDs // No Service Data + // No Manufacturer Data // No Tx Power // 4: Name: kTestDeviceNameEmpty // Address: kTestDeviceAddress2 // RSSI: kTestRSSI4 // No Advertised UUIDs // No Service Data + // No Manufacturer Data // No Tx Power // 5: No name device // Address: kTestDeviceAddress1 @@ -195,6 +201,7 @@ // RSSI: kTestRSSI1, // No Advertised UUIDs // No Service Data + // No Manufacturer Data // No Tx Power // Supports BR/EDR and LE. // 7: Name: kTestDeviceNameU2f @@ -202,6 +209,7 @@ // RSSI: kTestRSSI1, // Advertised UUIDs: {kTestUUIDU2fControlPointLength} // Service Data: {kTestUUIDU2fControlPointLength: [0, 20]} + // No Manufacturer Data // No Tx Power // Supports LE. virtual BluetoothDevice* SimulateLowEnergyDevice(int device_ordinal);
diff --git a/device/bluetooth/test/bluetooth_test_bluez.cc b/device/bluetooth/test/bluetooth_test_bluez.cc index 9fe32b4..32134dd 100644 --- a/device/bluetooth/test/bluetooth_test_bluez.cc +++ b/device/bluetooth/test/bluetooth_test_bluez.cc
@@ -123,18 +123,21 @@ std::vector<std::string> service_uuids; BluetoothTransport device_type = BLUETOOTH_TRANSPORT_LE; std::unordered_map<std::string, std::vector<uint8_t>> service_data; + std::unordered_map<uint16_t, std::vector<uint8_t>> manufacturer_data; switch (device_ordinal) { case 1: service_uuids.push_back(kTestUUIDGenericAccess); service_uuids.push_back(kTestUUIDGenericAttribute); service_data[kTestUUIDHeartRate] = {0x01}; + manufacturer_data[kTestManufacturerId] = {1, 2, 3, 4}; break; case 2: service_uuids.push_back(kTestUUIDImmediateAlert); service_uuids.push_back(kTestUUIDLinkLoss); service_data[kTestUUIDHeartRate] = {}; service_data[kTestUUIDImmediateAlert] = {0x00, 0x02}; + manufacturer_data[kTestManufacturerId] = {}; break; case 3: device_name = std::string(kTestDeviceNameEmpty); @@ -158,8 +161,8 @@ BluetoothDevice* device = adapter_->GetDevice(device_address); if (device) { - fake_bluetooth_device_client_->UpdateServiceData(GetDevicePath(device), - service_data); + fake_bluetooth_device_client_->UpdateServiceAndManufacturerData( + GetDevicePath(device), service_data, manufacturer_data); return device; } @@ -167,7 +170,7 @@ dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), /* name */ device_name, /* alias */ device_name.value_or("") + "(alias)", device_address, - service_uuids, device_type, service_data); + service_uuids, device_type, service_data, manufacturer_data); return adapter_->GetDevice(device_address); } @@ -177,12 +180,14 @@ std::string device_address = kTestDeviceAddress3; std::vector<std::string> service_uuids; std::unordered_map<std::string, std::vector<uint8_t>> service_data; + std::unordered_map<uint16_t, std::vector<uint8_t>> manufacturer_data; if (!adapter_->GetDevice(device_address)) { fake_bluetooth_device_client_->CreateTestDevice( dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), device_name /* name */, device_name /* alias */, device_address, - service_uuids, BLUETOOTH_TRANSPORT_CLASSIC, service_data); + service_uuids, BLUETOOTH_TRANSPORT_CLASSIC, service_data, + manufacturer_data); } return adapter_->GetDevice(device_address); }
diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm index 463b93d..dd52798 100644 --- a/device/bluetooth/test/bluetooth_test_mac.mm +++ b/device/bluetooth/test/bluetooth_test_mac.mm
@@ -51,6 +51,7 @@ NSString* name, NSArray* uuids, NSDictionary* service_data, + NSData* manufacturer_data, NSNumber* tx_power) { NSMutableDictionary* advertisement_data( [NSMutableDictionary dictionaryWithDictionary:@{ @@ -71,6 +72,11 @@ forKey:CBAdvertisementDataServiceDataKey]; } + if (service_data) { + [advertisement_data setObject:manufacturer_data + forKey:CBAdvertisementDataManufacturerDataKey]; + } + if (tx_power) { [advertisement_data setObject:tx_power forKey:CBAdvertisementDataTxPowerLevelKey]; @@ -164,6 +170,7 @@ NSArray* uuids; NSNumber* rssi; NSDictionary* service_data; + NSData* manufacturer_data; NSNumber* tx_power; switch (device_ordinal) { @@ -179,6 +186,9 @@ [CBUUID UUIDWithString:@(kTestUUIDHeartRate)] : [NSData dataWithBytes:(unsigned char[]){1} length:1] }; + manufacturer_data = + [NSData dataWithBytes:(unsigned char[]){0xE0, 0x00, 1, 2, 3, 4} + length:6]; tx_power = @(static_cast<int8_t>(TestTxPower::LOWEST)); break; case 2: @@ -195,6 +205,8 @@ [CBUUID UUIDWithString:@(kTestUUIDImmediateAlert)] : [NSData dataWithBytes:(unsigned char[]){0, 2} length:2] }; + manufacturer_data = + [NSData dataWithBytes:(unsigned char[]){0xE0, 0x00} length:2]; tx_power = @(static_cast<int8_t>(TestTxPower::LOWER)); break; case 3: @@ -203,6 +215,7 @@ rssi = @(static_cast<int8_t>(TestRSSI::LOW)); uuids = nil; service_data = nil; + manufacturer_data = nil; tx_power = nil; break; case 4: @@ -211,6 +224,7 @@ rssi = @(static_cast<int8_t>(TestRSSI::MEDIUM)); uuids = nil; service_data = nil; + manufacturer_data = nil; tx_power = nil; break; case 5: @@ -219,6 +233,7 @@ rssi = @(static_cast<int8_t>(TestRSSI::HIGH)); uuids = nil; service_data = nil; + manufacturer_data = nil; tx_power = nil; break; default: @@ -229,6 +244,7 @@ rssi = nil; uuids = nil; service_data = nil; + manufacturer_data = nil; tx_power = nil; } scoped_nsobject<MockCBPeripheral> mock_peripheral([[MockCBPeripheral alloc] @@ -239,7 +255,7 @@ centralManager:central_manager didDiscoverPeripheral:[mock_peripheral peripheral] advertisementData:CreateAdvertisementData(name, uuids, service_data, - tx_power) + manufacturer_data, tx_power) RSSI:rssi]; return observer.last_device(); }
diff --git a/docs/README.md b/docs/README.md index 37e256bb..dc5b5826 100644 --- a/docs/README.md +++ b/docs/README.md
@@ -102,6 +102,8 @@ * [Code Reviews](code_reviews.md) - Code review requirements and guidelines * [Respectful Code Reviews](cr_respect.md) - A guide for code reviewers * [Respectful Changes](cl_respect.md) - A guide for code authors +* [Tour of Continuous Integration UI](tour_of_luci_ui.md) - A tour of our + the user interface for LUCI, our continuous integration system. * [Closure Compilation](closure_compilation.md) - The _Closure_ JavaScript compiler * [Threading and Tasks in Chrome](threading_and_tasks.md) - How to run tasks
diff --git a/docs/images/LUCI-Build.png b/docs/images/LUCI-Build.png new file mode 100644 index 0000000..92e9a82 --- /dev/null +++ b/docs/images/LUCI-Build.png Binary files differ
diff --git a/docs/images/LUCI-Builder.png b/docs/images/LUCI-Builder.png new file mode 100644 index 0000000..4fedca1 --- /dev/null +++ b/docs/images/LUCI-Builder.png Binary files differ
diff --git a/docs/images/LUCI-Builders-View.png b/docs/images/LUCI-Builders-View.png new file mode 100644 index 0000000..f4410c2 --- /dev/null +++ b/docs/images/LUCI-Builders-View.png Binary files differ
diff --git a/docs/images/LUCI-Console-View.png b/docs/images/LUCI-Console-View.png new file mode 100644 index 0000000..185d505c --- /dev/null +++ b/docs/images/LUCI-Console-View.png Binary files differ
diff --git a/docs/images/LUCI-Home-Hierarchy.png b/docs/images/LUCI-Home-Hierarchy.png new file mode 100644 index 0000000..4f6b582 --- /dev/null +++ b/docs/images/LUCI-Home-Hierarchy.png Binary files differ
diff --git a/docs/images/LUCI-Home.png b/docs/images/LUCI-Home.png new file mode 100644 index 0000000..2d817a7 --- /dev/null +++ b/docs/images/LUCI-Home.png Binary files differ
diff --git a/docs/images/LUCI-Project-Builders.png b/docs/images/LUCI-Project-Builders.png new file mode 100644 index 0000000..77b6d46 --- /dev/null +++ b/docs/images/LUCI-Project-Builders.png Binary files differ
diff --git a/docs/images/LUCI-Project-Diagram.png b/docs/images/LUCI-Project-Diagram.png new file mode 100644 index 0000000..6165eee --- /dev/null +++ b/docs/images/LUCI-Project-Diagram.png Binary files differ
diff --git a/docs/images/LUCI-Project.png b/docs/images/LUCI-Project.png new file mode 100644 index 0000000..ee87b49 --- /dev/null +++ b/docs/images/LUCI-Project.png Binary files differ
diff --git a/docs/images/LUCI-Search.png b/docs/images/LUCI-Search.png new file mode 100644 index 0000000..99c42c0 --- /dev/null +++ b/docs/images/LUCI-Search.png Binary files differ
diff --git a/docs/images/LUCI-Site-Project.png b/docs/images/LUCI-Site-Project.png new file mode 100644 index 0000000..02b008f --- /dev/null +++ b/docs/images/LUCI-Site-Project.png Binary files differ
diff --git a/docs/tour_of_luci_ui.md b/docs/tour_of_luci_ui.md new file mode 100644 index 0000000..e4e8fae --- /dev/null +++ b/docs/tour_of_luci_ui.md
@@ -0,0 +1,371 @@ +# A Tour of Continuous Integration UI + +This document details a tour of page layouts and site hierarchy for [LUCI +UI](https://ci.chromium.org/p/chromium), Chromium's continuous integration user +interface. Currently, LUCI shows both Buildbot and LUCI builds. In the near +future, LUCI will replace Buildbot as the default continuous integration system. +Read this document to learn how to navigate LUCI and to better understand the +new UX concepts LUCI introduces. Refer to the [FAQ](#FAQ) to quickly jump to +sections. + +[TOC] + +## FAQ + +**Where can I see the console view for my builders?** + +*Using this URL schema, `ci.chromium.org/p/<project_id>/g/<group_id>/console`, +replace `<project_id>` with the project ID and `<group_id>` with the group ID. +For example, [Chromium Main](http://ci.chromium.org/p/chromium/g/main/console). +See [console view page](#Console-view-page) section below for details on the +page.* + +**Why are we replacing Buildbot with LUCI?** + +*Buildbot is slow and doesn't scale well. LUCI fixes this and over time, we have +already been swapping out Buildbot responsibilities with LUCI services. See +[Background section](#Background) for more details.* + +**What are the LUCI pages I should expect to see?** + +*[Jump](#Tour-of-LUCI-Pages) to the Tour of LUCI Pages section to see details on +each page.* + +**Is LUCI only for the Chromium project?** + +_The overall goal is to have LUCI serve all projects (wherever possible) that +Buildbot currently serves. The 11/30 UI rollout is limited to chromium.* masters +and tryserver.chromium.* + tryserver.blink masters. Other projects/masters will +switch to LUCI UI at later notice._ + +**Is there a list of Known Issues?** + +_A list of known user interface issues can be viewed in [Chromium +bugs](https://bugs.chromium.org/p/chromium/issues/list?q=label%3Aluci-knownissues-ui)._ + +**What happened to Masters?** + +_LUCI no longer uses Masters and distributes the responsibility of scheduling, +distributing, collecting, archiving and logging builds into separate independent +services. As a UI concept, "masters" are replaced with "groups" and "views". See +[Background section](#Background) for more details._ + +**What is a Group?** + +_Builders of each project are organized into groups of ordered builders (A +single builder can be referenced by multiple groups). See [Site Hierarchy +section](#Site-Hierarchy) for more details._ + +**What is a View?** + +*A group of builders and their builds can be visualized in a couple ways; we +call these views. See [Site Hierarchy section](#Site-Hierarchy) for more +details.* + +**Are URLs final?** + +*No. URLs and pages are subject to change. Our initial goal is to provide +Buildbot functionality parity, but we are committed to building additional +enhanced user experiences on top of the new LUCI UI.* + +**How do I provide feedback?** + +*LUCI UI is still in development and we would love to get feedback, please see +[feedback section](#Feedback) for links.* + + +## Tour of LUCI Pages + +**Please note** that URLs and pages are subject to change. Our initial goal is +to provide Buildbot functionality, but we are committed to building additional +enhanced user experiences on top of the new LUCI UI. + + +### High level pages + + +#### Home + +**URL:** [ci.chromium.org](http://ci.chromium.org/) + +This is the "Home" page for LUCI. It contains a listing of all of the projects +configured in LUCI. + + + + +#### Search + +**URL:** [ci.chromium.org/search](http://ci.chromium.org/search) + +This is the builder search page for LUCI. Find a specific builder serviced by +LUCI by name. Search results are sorted by bucket and groups. This can also be +accessed by typing "ci.<tab>" in the Chrome Omnibox. + + + +#### Project page + +**URL:** `ci.chromium.org/p/<project_id>` + +Example: [ci.chromium.org/p/chromium](http://ci.chromium.org/p/chromium) + +A list of the groups defined for the project. A group is an ordered list of +builders (Builders can be referenced by multiple groups). This page contains +links to the default view defined for each group and the +last-completed-build-status for each builder. + +Refer to the build results [color key](#Color-Key) on how to interpret the build +status. + +**Note:** Initially, we have defined "groups" of builders corresponding to a +Buildbot master and included builders that used to be attached to it. + + + + +#### Builders list page per project + +**URL:** `ci.chromium.org/p/<project_id>/builders` + +Example: +[ci.chromium.org/p/chromium/builders](http://ci.chromium.org/p/chromium/builders) + +Shows a listing of all builders belonging to the *`<project_id>`*. Each builder +shows number of builds pending, in-progress and the build statuses of the last +30 recently completed builds by default (option available to show more). + +Refer to the build results [color key](#Color-Key) on how to interpret the build +status. + + + + +### Project Resource pages + +These pages display a singular resource that *belongs to* the project +(currently, Builders and Build Results). + + +#### Builder page + +**Buildbot Builder URL:** `ci.chromium.org/buildbot/<group_id>/<builder_name>` + +**LUCI Builder URL:** +`ci.chromium.org/p/<project_id>/builders/<bucket>/<builder_name>` + +This is the page describing the builder and lists machine pool, current builds, +pending builds and recent builds completed. The layout is equivalent to Buildbot +layout of builder pages. + + + + +#### Build Results page + +**Buildbot Build URL:** ` +ci.chromium.org/buildbot/<group_id>/<builder_name>/<build_id>` + +**LUCI Build URL:** +`ci.chromium.org/p/<project_id>/builders/<bucket>/<builder_name>/<build_id>` + +This is the page describing the build and results. Contains build info, +properties, result status, blame-list, steps and links to log files. The layout +is equivalent to Buildbot layout of build result pages + + + + +### View pages + +Each group has multiple views (currently "console" and "builders" views). +Later, we may add additional views (e.g. "stats"). Views are the primary reason +to create a group; For example, you put builders into the "main" group so that +they show up on main/console. + + +#### Console view page + +**URL:** `ci.chromium.org/p/<project_id>/g/<group_id>/console` + +Example: +[ci.chromium.org/p/chromium/g/main/console](http://ci.chromium.org/p/chromium/g/main/console) + +A high-level overview of the recently completed builds. Contains most relevant +information on the Group, including tree status, on-call information, important +links, sub groups, and builds ordered by latest commits by users over all +platform builders. Refer to the build results [color key](#Color-Key) on how to +interpret the build status. + + + + +**Tree Status** + +The "tree" represents the various source repositories used to build the project, +e.g. chromium/src.git plus its +[DEPS](http://src.chromium.org/viewvc/chrome/trunk/src/DEPS?view=markup) file. +**Tree status **displays the state of the tree corresponding to the project and +determines whether or not developers are allowed to commit to the repositories. +The tree can be "open", "closed" or "throttled". The normal state is open. When +vital builders fail or tests break, the tree is closed by putting the word +"closed" in the tree status; +[PRESUBMIT.py](http://src.chromium.org/viewvc/chrome/trunk/src/PRESUBMIT.py?view=markup) +checks the status and will block commits, and the build sheriff will act to fix +the tree. When the tree is throttled, commits are only allowed with specific +permission from the build sheriff, generally because the sheriff wants to make +sure the tree is stable before opening it up to unlimited commits. + + +**On-call Info** + +This is the list of list the current build sheriffs and troopers. The sheriffs +have overall responsibility in case someone else is away or not paying +attention. + + +**Commits/CLs** + +Each time someone lands a change, the scheduler gathers changes and schedules +builds and tests on all relevant builders. Each row in the table represents a +commit and resulting builds across the various builders. The columns are sorted +by build configuration and platform. A build can span multiple commits, in the +event that commits land faster than the builder can cycle. At the start of each +build, a yellow box is displayed. Clicking on the box shows more information +about the build, including the "blamelist" of changes that went into it and +detailed step/log information for the build (See [build +results](#Build-Results-page) page for more details). The times shown in the +table are in U.S. Pacific time. + +Refer to the build results [color key](#Color-Key) on how to interpret the build +status. + + +#### Builders view page + +**URL:** `ci.chromium.org/p/<project_id>/g/<group_id>/builders` + +Example: +[ci.chromium.org/p/chromium/g/main/builders](http://ci.chromium.org/p/chromium/g/main/builders) + +Builders view page for the group_id under project_id. Shows all builders of this +group. Each builder shows number of builds pending, in-progress and the build +statuses of the last 30 recently completed builds by default (option available +to show more). + + + + +### Color Key + +Throughout LUCI, we visualize a build results using colored boxes, where the +color signifies the build result status. Below is the color key mapping. + +* <span style="color:yellow">Yellow</span> = in progress +* <span style="color:green">Green</span> = finished successfully +* <span style="color:red">Red</span> = finished with errors +* <span style="color:purple">Purple</span> = internal error. + +## Background + +LUCI which stands for Layered Universal Continuous Integration is a replacement +for Buildbot (our existing single-threaded monolithic continuous integration +system). LUCI is made up of a number of independent services that work together, +each service dealing with one part of the continuous integration stack. Over +time, we have been swapping out Buildbot responsibilities with LUCI services. As +such, most builds today on Buildbot are already running using LUCI services. + +LUCI tries to separate each CI concern into a separate service. Swarming, for +example, is one such service in LUCI, and handles job distribution. One of the +technical limitations of Buildbot is that each set of builders needed to be +owned by a single "master" process. This master would be responsible for +scheduling, distributing, collecting, archiving and logging builds. LUCI +distributes the responsibility of this work into separate independent services, +negating the need for a master. For ease of migration from Buildbot masters to +LUCI builders, we have defined "groups" of builders that each correspond to a +single Buildbot master, and we have kept the LUCI Builder names the same as they +were in Buildbot. + +For more an overview of the services that make up LUCI, take a look at the [LUCI +Overview +presentation](https://docs.google.com/presentation/d/1dhUecmBf7IZ3moy_SflNT7yBYeiJbvj7m9kAxqeN9-A/preview?slide=id.g6571e2aaf_0_95). + + +## Site Hierarchy + +On the highest level, LUCI is organized by **projects**. A project contains all +the configuration necessary to do development on a given repo, for example, +Chromium is one of these projects (corresponding to chromium/src.git). Each +project contains **builders**, which describe how a given builder works (i.e. +recipe to run, gn args to use, etc.). Each **builder** has **builds**, which +contain build information, build properties details, success or failure status, +blamelists, steps and links to log files. + + + + +For the LUCI UI, to organize builders in each project, we introduce the idea of +**groups**. Builders of each project are organized into **groups **of ordered +builders (A single builder can be referenced by multiple groups). For example, +"main" might contain all of the main builders, and "gpu" might contain all of +the "gpu" builders but since groups can overlap, (so "main" can contain some +subset of the "gpu" builders). Notably, groups don't own any project resources, +they're simply a mechanism to group them for display purposes. Projects may have +as many groups as they like, for whatever purpose they need them. + +A group of builders and their builds can be visualized in a couple ways; we call +these **views**. We have currently implemented 2 views; a **console view** and a +**builders view** for each group. + + + + +To get started with LUCI, go to [ci.chromium.org](http://ci.chromium.org). Drill +down to build results from the list of projects. The search page is available to +find a specific builder by name. + + + + +## Known Issues + +A list of [known +issues](https://bugs.chromium.org/p/chromium/issues/list?q=label%3Aluci-knownissues-ui) +for the user interface of LUCI is available under Chromium bugs. + +Note: URLs and pages are subject to change. Our initial goal is to provide +Buildbot user functionality parity, but we are committed to building additional +enhanced user experiences on top of the new LUCI UI. + +## Help + +The Chrome Operations Foundation team is responsible for the design and +development of LUCI. If you have any questions or need help on usage, feel free +to reach out to the Chrome Operations team by emailing us at +[infra-dev@chromium.org](mailto:infra-dev@chromium.org) + + +## Feedback + +LUCI UI is still in development. Our initial goal is to provide Buildbot +functionality parity, but we are committed to building additional enhanced user +experiences on top of the new LUCI UI once we no longer need to support +Buildbot. + +If you have specific feedback you would like to share with us, we would love to +hear it and incorporate it into our ongoing UI improvements. + +Use the **feedback button** on a LUCI page. + +For **feature requests or bugs**, please file a crbug using the following +[template](https://bugs.chromium.org/p/chromium/issues/entry?labels=LUCI-M0-Backlog&summary=[LUCI-Feedback-UI]%20Enter%20an%20one-line%20summary&components=Infra>Platform>Milo&cc=efoo@chromium.org,estaab@chromium.org,nodir@chromium.org&description=Please%20use%20this%20to%20template%20to%20file%20a%20feature%20request%20into%20LUCI%20backlog.%20%20%0A%0AReminder%20to%20include%20the%20following%3A%0A-%20Description%0A-%20Priority%0A-%20Why%20this%20feature%20is%20needed). + +To **share your feedback**, please fill out this [short +survey](https://goo.gl/forms/YPO6XCQ3q47r00iw2). + +**Contact us** directly by emailing us at +[infra-dev@chromium.org](mailto:infra-dev@chromium.org). +
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc b/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc index b0e095a3..3fe9764 100644 --- a/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc +++ b/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc
@@ -221,7 +221,7 @@ device_test_->SetDeviceProperty(kCellularDevicePath, shill::kMinProperty, base::Value("test_min")); device_test_->SetDeviceProperty(kCellularDevicePath, - shill::kModelIDProperty, + shill::kModelIdProperty, base::Value("test_model_id")); std::unique_ptr<base::DictionaryValue> apn = DictionaryBuilder()
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 9d3aa023..93c5a483 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1269,6 +1269,7 @@ DEVELOPERPRIVATE_NOTIFYDRAGINSTALLINPROGRESS, AUTOTESTPRIVATE_GETPRINTERLIST, DEVELOPERPRIVATE_GETEXTENSIONSIZE, + CRYPTOTOKENPRIVATE_ISAPPIDHASHINENTERPRISECONTEXT, // Last entry: Add new entries above, then run: // python tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/ios/chrome/app/chrome_exe_main.mm b/ios/chrome/app/chrome_exe_main.mm index 5c49215..d6cbbb0f 100644 --- a/ios/chrome/app/chrome_exe_main.mm +++ b/ios/chrome/app/chrome_exe_main.mm
@@ -6,6 +6,7 @@ #include "base/at_exit.h" #include "base/debug/crash_logging.h" +#include "base/strings/sys_string_conversions.h" #include "components/crash/core/common/crash_keys.h" #include "ios/chrome/app/startup/ios_chrome_main.h" #include "ios/chrome/browser/crash_report/breakpad_helper.h" @@ -24,9 +25,9 @@ void StartCrashController() { @autoreleasepool { std::string channel_string = GetChannelString(); - RegisterChromeIOSCrashKeys(); - base::debug::SetCrashKeyValue(crash_keys::kChannel, channel_string); + breakpad_helper::AddReportParameter( + @"channel", base::SysUTF8ToNSString(channel_string), false); breakpad_helper::Start(channel_string); } }
diff --git a/ios/chrome/browser/crash_report/crash_keys.cc b/ios/chrome/browser/crash_report/crash_keys.cc index e2886be..9294980 100644 --- a/ios/chrome/browser/crash_report/crash_keys.cc +++ b/ios/chrome/browser/crash_report/crash_keys.cc
@@ -14,8 +14,6 @@ // The following keys may be chunked by the underlying crash logging system, // but ultimately constitute a single key-value pair. base::debug::CrashKey fixed_keys[] = { - {crash_keys::kChannel, crash_keys::kSmallSize}, - {crash_keys::kMetricsClientId, crash_keys::kSmallSize}, {crash_keys::kNumVariations, crash_keys::kSmallSize}, {crash_keys::kVariations, crash_keys::kHugeSize}, };
diff --git a/ios/web/download/download_task_impl.h b/ios/web/download/download_task_impl.h index 3faea62..8a5948e 100644 --- a/ios/web/download/download_task_impl.h +++ b/ios/web/download/download_task_impl.h
@@ -55,6 +55,7 @@ void ShutDown(); // DownloadTask overrides: + DownloadTask::State GetState() const override; void Start(std::unique_ptr<net::URLFetcherResponseWriter> writer) override; net::URLFetcherResponseWriter* GetResponseWriter() const override; NSString* GetIndentifier() const override; @@ -99,9 +100,9 @@ base::ObserverList<DownloadTaskObserver, true> observers_; // Back up corresponding public methods of DownloadTask interface. + State state_ = State::kNotStarted; std::unique_ptr<net::URLFetcherResponseWriter> writer_; GURL original_url_; - bool is_done_ = false; int error_code_ = 0; int http_code_ = -1; int64_t total_bytes_ = -1;
diff --git a/ios/web/download/download_task_impl.mm b/ios/web/download/download_task_impl.mm index a80eb07e..7bee9bc 100644 --- a/ios/web/download/download_task_impl.mm +++ b/ios/web/download/download_task_impl.mm
@@ -161,11 +161,18 @@ delegate_ = nullptr; } +DownloadTask::State DownloadTaskImpl::GetState() const { + DCHECK_CURRENTLY_ON(web::WebThread::UI); + return state_; +} + void DownloadTaskImpl::Start( std::unique_ptr<net::URLFetcherResponseWriter> writer) { DCHECK_CURRENTLY_ON(web::WebThread::UI); DCHECK(!writer_); + DCHECK_EQ(state_, State::kNotStarted); writer_ = std::move(writer); + state_ = State::kInProgress; GetCookies(base::Bind(&DownloadTaskImpl::StartWithCookies, weak_factory_.GetWeakPtr())); } @@ -187,7 +194,7 @@ bool DownloadTaskImpl::IsDone() const { DCHECK_CURRENTLY_ON(web::WebThread::UI); - return is_done_; + return state_ == State::kComplete; } int DownloadTaskImpl::GetErrorCode() const { @@ -329,7 +336,7 @@ } void DownloadTaskImpl::OnDownloadFinished(int error_code) { - is_done_ = true; + state_ = State::kComplete; session_task_ = nil; OnDownloadUpdated(); }
diff --git a/ios/web/download/download_task_impl_unittest.mm b/ios/web/download/download_task_impl_unittest.mm index 1c815e0..52a9b3e 100644 --- a/ios/web/download/download_task_impl_unittest.mm +++ b/ios/web/download/download_task_impl_unittest.mm
@@ -191,6 +191,7 @@ // Tests DownloadTaskImpl default state after construction. TEST_F(DownloadTaskImplTest, DefaultState) { + EXPECT_EQ(DownloadTask::State::kNotStarted, task_->GetState()); EXPECT_FALSE(task_->GetResponseWriter()); EXPECT_NSEQ(task_delegate_.configuration().identifier, task_->GetIndentifier()); @@ -222,6 +223,7 @@ ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ return task_->IsDone(); })); + EXPECT_EQ(DownloadTask::State::kComplete, task_->GetState()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(0, task_->GetTotalBytes()); EXPECT_EQ(100, task_->GetPercentComplete()); @@ -244,6 +246,7 @@ session_task.countOfBytesExpectedToReceive = kDataSize; SimulateDataDownload(session_task, kData); testing::Mock::VerifyAndClearExpectations(&task_observer_); + EXPECT_EQ(DownloadTask::State::kInProgress, task_->GetState()); EXPECT_FALSE(task_->IsDone()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(kDataSize, task_->GetTotalBytes()); @@ -257,6 +260,7 @@ ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ return task_->IsDone(); })); + EXPECT_EQ(DownloadTask::State::kComplete, task_->GetState()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(kDataSize, task_->GetTotalBytes()); EXPECT_EQ(100, task_->GetPercentComplete()); @@ -282,6 +286,7 @@ session_task.countOfBytesExpectedToReceive = kData1Size + kData2Size; SimulateDataDownload(session_task, kData1); testing::Mock::VerifyAndClearExpectations(&task_observer_); + EXPECT_EQ(DownloadTask::State::kInProgress, task_->GetState()); EXPECT_FALSE(task_->IsDone()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(kData1Size + kData2Size, task_->GetTotalBytes()); @@ -294,6 +299,7 @@ EXPECT_CALL(task_observer_, OnDownloadUpdated(task_.get())); SimulateDataDownload(session_task, kData2); testing::Mock::VerifyAndClearExpectations(&task_observer_); + EXPECT_EQ(DownloadTask::State::kInProgress, task_->GetState()); EXPECT_FALSE(task_->IsDone()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(kData1Size + kData2Size, task_->GetTotalBytes()); @@ -307,6 +313,7 @@ ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ return task_->IsDone(); })); + EXPECT_EQ(DownloadTask::State::kComplete, task_->GetState()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(kData1Size + kData2Size, task_->GetTotalBytes()); EXPECT_EQ(100, task_->GetPercentComplete()); @@ -332,6 +339,7 @@ ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ return task_->IsDone(); })); + EXPECT_EQ(DownloadTask::State::kComplete, task_->GetState()); EXPECT_TRUE(task_->GetErrorCode() == net::ERR_INTERNET_DISCONNECTED); EXPECT_EQ(0, task_->GetTotalBytes()); EXPECT_EQ(100, task_->GetPercentComplete()); @@ -355,6 +363,7 @@ session_task.countOfBytesExpectedToReceive = kExpectedDataSize; SimulateDataDownload(session_task, kReceivedData); testing::Mock::VerifyAndClearExpectations(&task_observer_); + EXPECT_EQ(DownloadTask::State::kInProgress, task_->GetState()); EXPECT_FALSE(task_->IsDone()); EXPECT_EQ(0, task_->GetErrorCode()); EXPECT_EQ(kExpectedDataSize, task_->GetTotalBytes()); @@ -372,6 +381,7 @@ ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ return task_->IsDone(); })); + EXPECT_EQ(DownloadTask::State::kComplete, task_->GetState()); EXPECT_TRUE(task_->GetErrorCode() == net::ERR_INTERNET_DISCONNECTED); EXPECT_EQ(kExpectedDataSize, task_->GetTotalBytes()); EXPECT_EQ(23, task_->GetPercentComplete());
diff --git a/ios/web/public/download/download_task.h b/ios/web/public/download/download_task.h index 6cd5f66..6472ce0 100644 --- a/ios/web/public/download/download_task.h +++ b/ios/web/public/download/download_task.h
@@ -27,6 +27,20 @@ // stores all the state for a download. Must be used on the UI thread. class DownloadTask { public: + enum class State { + // Download has not started yet. + kNotStarted = 0, + + // Download is actively progressing. + kInProgress, + + // Download is completely finished. + kComplete, + }; + + // Returns the download task state. + virtual State GetState() const = 0; + // Starts the download. |writer| allows clients to perform in-memory or // in-file downloads and must not be null. Start() can only be called once. virtual void Start(std::unique_ptr<net::URLFetcherResponseWriter> writer) = 0;
diff --git a/ios/web/public/test/fakes/fake_download_task.h b/ios/web/public/test/fakes/fake_download_task.h index 0f24728..30f94dff 100644 --- a/ios/web/public/test/fakes/fake_download_task.h +++ b/ios/web/public/test/fakes/fake_download_task.h
@@ -21,6 +21,7 @@ ~FakeDownloadTask() override; // DownloadTask overrides: + DownloadTask::State GetState() const override; void Start(std::unique_ptr<net::URLFetcherResponseWriter> writer) override; net::URLFetcherResponseWriter* GetResponseWriter() const override; NSString* GetIndentifier() const override; @@ -52,9 +53,9 @@ base::ObserverList<DownloadTaskObserver, true> observers_; + State state_ = State::kNotStarted; std::unique_ptr<net::URLFetcherResponseWriter> writer_; GURL original_url_; - bool is_done_ = false; int error_code_ = 0; int http_code_ = -1; std::string content_disposition_;
diff --git a/ios/web/public/test/fakes/fake_download_task.mm b/ios/web/public/test/fakes/fake_download_task.mm index 3eb5213..57082b6 100644 --- a/ios/web/public/test/fakes/fake_download_task.mm +++ b/ios/web/public/test/fakes/fake_download_task.mm
@@ -19,6 +19,10 @@ FakeDownloadTask::~FakeDownloadTask() = default; +DownloadTask::State FakeDownloadTask::GetState() const { + return state_; +} + void FakeDownloadTask::Start( std::unique_ptr<net::URLFetcherResponseWriter> writer) { writer_ = std::move(writer); @@ -38,7 +42,7 @@ } bool FakeDownloadTask::IsDone() const { - return is_done_; + return state_ == State::kComplete; } int FakeDownloadTask::GetErrorCode() const { @@ -80,7 +84,7 @@ } void FakeDownloadTask::SetDone(bool done) { - is_done_ = done; + state_ = State::kComplete; OnDownloadUpdated(); }
diff --git a/services/preferences/public/cpp/pref_service_factory.cc b/services/preferences/public/cpp/pref_service_factory.cc index 0198885..38a65f58 100644 --- a/services/preferences/public/cpp/pref_service_factory.cc +++ b/services/preferences/public/cpp/pref_service_factory.cc
@@ -123,9 +123,9 @@ base::Bind(&DoNothingHandleReadError), true); switch (pref_service->GetAllPrefStoresInitializationStatus()) { case PrefService::INITIALIZATION_STATUS_WAITING: - pref_service->AddPrefInitObserver(base::Bind(&OnPrefServiceInit, - base::Passed(&pref_service), - base::Passed(&callback))); + pref_service->AddPrefInitObserver( + base::BindOnce(&OnPrefServiceInit, base::Passed(&pref_service), + base::Passed(&callback))); break; case PrefService::INITIALIZATION_STATUS_SUCCESS: case PrefService::INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE:
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console-cd-completions.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console-cd-completions.js index 9dfcec7d..f19a8eb 100644 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/console-cd-completions.js +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console-cd-completions.js
@@ -99,7 +99,7 @@ ConsoleTestRunner.changeExecutionContext('myIFrame'); - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', 'myGlob').then(checkCompletions.bind(this)); + ObjectUI.javaScriptAutocomplete._completionsForExpression('', 'myGlob').then(checkCompletions.bind(this)); function checkCompletions(completions) { TestRunner.addResult('myGlob completions:'); dumpCompletions(completions, ['myGlobalVar', 'myGlobalFunction']); @@ -108,7 +108,7 @@ function requestIFrameCompletions() { ConsoleTestRunner.changeExecutionContext('top'); - ObjectUI.JavaScriptAutocomplete.completionsForExpression('myIFrame.', '').then(checkIframeCompletions.bind(this)); + ObjectUI.javaScriptAutocomplete._completionsForExpression('myIFrame.', '').then(checkIframeCompletions.bind(this)); } function checkIframeCompletions(completions) { @@ -120,7 +120,7 @@ function requestProxyCompletions() { ConsoleTestRunner.changeExecutionContext('top'); - ObjectUI.JavaScriptAutocomplete.completionsForExpression('window.proxy2.', '') + ObjectUI.javaScriptAutocomplete._completionsForExpression('window.proxy2.', '') .then(checkProxyCompletions.bind(this)); } @@ -138,7 +138,7 @@ function requestMyClassWithMixinCompletions() { ConsoleTestRunner.changeExecutionContext('top'); - ObjectUI.JavaScriptAutocomplete.completionsForExpression('window.x.', '') + ObjectUI.javaScriptAutocomplete._completionsForExpression('window.x.', '') .then(checkMyClassWithMixinCompletions.bind(this)); } @@ -151,7 +151,7 @@ function requestObjectCompletions() { ConsoleTestRunner.changeExecutionContext('top'); - ObjectUI.JavaScriptAutocomplete.completionsForExpression('Object.', '').then(checkObjectCompletions.bind(this)); + ObjectUI.javaScriptAutocomplete._completionsForExpression('Object.', '').then(checkObjectCompletions.bind(this)); } function checkObjectCompletions(completions) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console-completions.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console-completions.js index b5d1d5ba..3e570918 100644 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/console-completions.js +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console-completions.js
@@ -43,12 +43,12 @@ `); TestRunner.addResult('Completions for objectC.:'); - let completions = await ObjectUI.JavaScriptAutocomplete.completionsForExpression('objectC.', 'e'); + let completions = await ObjectUI.javaScriptAutocomplete._completionsForExpression('objectC.', 'e'); for (var completion of completions) TestRunner.addObject(completion); TestRunner.addResult('Completions for prefix:'); - completions = await ObjectUI.JavaScriptAutocomplete.completionsForExpression('', 'prefix'); + completions = await ObjectUI.javaScriptAutocomplete._completionsForExpression('', 'prefix'); for (var completion of completions) TestRunner.addObject(completion);
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/debugger-completions-on-call-frame.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/debugger-completions-on-call-frame.js index a80dd3a..43d34c3 100644 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/debugger-completions-on-call-frame.js +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/debugger-completions-on-call-frame.js
@@ -32,72 +32,72 @@ }, function step2(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', 'var').then( + ObjectUI.javaScriptAutocomplete._completionsForExpression('', 'var').then( checkAgainstGolden.bind(this, ['var1', 'var2'], [], next)); }, function step3(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', 'di').then( + ObjectUI.javaScriptAutocomplete._completionsForExpression('', 'di').then( checkAgainstGolden.bind(this, ['dir', 'dirxml'], [], next)); }, function step4(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', 'win').then( + ObjectUI.javaScriptAutocomplete._completionsForExpression('', 'win').then( checkAgainstGolden.bind(this, ['window'], [], next)); }, function step5(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', 't').then( + ObjectUI.javaScriptAutocomplete._completionsForExpression('', 't').then( checkAgainstGolden.bind(this, ['this'], [], next)); }, function step6(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('var1.', 'toExp') + ObjectUI.javaScriptAutocomplete._completionsForExpression('var1.', 'toExp') .then(checkAgainstGolden.bind(this, ['toExponential'], [], next)); }, function step7(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('123.', 'toExp') + ObjectUI.javaScriptAutocomplete._completionsForExpression('123.', 'toExp') .then(checkAgainstGolden.bind(this, [], ['toExponential'], next)); }, function step8(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', '').then( + ObjectUI.javaScriptAutocomplete._completionsForExpression('', '').then( checkAgainstGolden.bind(this, [], ['$'], next)); }, function step9(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('', '', true) + ObjectUI.javaScriptAutocomplete._completionsForExpression('', '', true) .then(checkAgainstGolden.bind(this, ['$', 'window'], [], next)); }, function step10(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('console.', 'log(\'bar\');') + ObjectUI.javaScriptAutocomplete._completionsForExpression('console.', 'log(\'bar\');') .then(checkAgainstGolden.bind(this, [], ['$'], next)); }, function step11(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('arr1.', '') + ObjectUI.javaScriptAutocomplete._completionsForExpression('arr1.', '') .then(checkAgainstGolden.bind(this, ['length'], ['1', '2', '3'], next)); }, function step12(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('arr1[', '') + ObjectUI.javaScriptAutocomplete._completionsForExpression('arr1[', '') .then(checkAgainstGolden.bind(this, ['"length"]'], ['3]'], next)); }, function step13_ShouldNotCrash(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('arr2.', '') + ObjectUI.javaScriptAutocomplete._completionsForExpression('arr2.', '') .then(checkAgainstGolden.bind(this, ['length'], ['1', '2', '3'], next)); }, function step14(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('document\n', 'E') + ObjectUI.javaScriptAutocomplete._completionsForExpression('document\n', 'E') .then(checkAgainstGolden.bind(this, ['Element'], ['ELEMENT_NODE'], next)); }, function step15_ShouldNotCrash(next) { - ObjectUI.JavaScriptAutocomplete.completionsForExpression('arr3.', '') + ObjectUI.javaScriptAutocomplete._completionsForExpression('arr3.', '') .then(checkAgainstGolden.bind(this, ['length'], ['1', '2', '3'], next)); } ]);
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js index 8099817..a827c81 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
@@ -492,25 +492,6 @@ }); }; -function debugTest(testFunction) { - var dispatch = DevToolsAPI.dispatchMessage; - var messages = []; - DevToolsAPI.dispatchMessage = message => { - if (!messages.length) { - setTimeout(() => { - for (var message of messages.splice(0)) - dispatch(message); - }, 0); - } - messages.push(message); - }; - return testRunner => { - testRunner.log = console.log; - testRunner.completeTest = () => console.log('Test completed'); - window.test = () => testFunction(testRunner); - }; -}; - DevToolsAPI._fetch = function(url) { return new Promise(fulfill => { var xhr = new XMLHttpRequest(); @@ -532,11 +513,29 @@ window.testRunner.setCanOpenWindows(true); window.addEventListener('load', () => { - var testScriptURL = window.location.search.substring(1); + var params = new URLSearchParams(window.location.search); + var testScriptURL = params.get('test'); var baseURL = testScriptURL.substring(0, testScriptURL.lastIndexOf('/') + 1); DevToolsAPI._fetch(testScriptURL).then(testScript => { var testRunner = new TestRunner(baseURL, DevToolsAPI._log, DevToolsAPI._completeTest, DevToolsAPI._fetch); var testFunction = eval(`${testScript}\n//# sourceURL=${testScriptURL}`); + if (params.get('debug')) { + var dispatch = DevToolsAPI.dispatchMessage; + var messages = []; + DevToolsAPI.dispatchMessage = message => { + if (!messages.length) { + setTimeout(() => { + for (var message of messages.splice(0)) + dispatch(message); + }, 0); + } + messages.push(message); + }; + testRunner.log = console.log; + testRunner.completeTest = () => console.log('Test completed'); + window.test = () => testFunction(testRunner); + return; + } return testFunction(testRunner); }).catch(reason => { DevToolsAPI._log(`Error while executing test script: ${reason}\n${reason.stack}`);
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/computed-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed-expected.txt rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/computed-expected.txt
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/computed.html similarity index 93% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/computed.html index 241a0b8..8e085f1 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/computed.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>Computed StylePropertyMap tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#computed-stylepropertymapreadonly-objects"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <style>#target { height: 10px; --foo: auto; }</style> <div style="width: 50px"> <div id="target" style="top: 5px; --bar: 5; width: 50%;"></div>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/get.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/get.html similarity index 78% copy from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/get.html copy to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/get.html index a835b872c..48fbc570 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/get.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/get.html
@@ -2,24 +2,20 @@ <meta charset="utf-8"> <title>StylePropertyMap.get tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#get-a-value-from-a-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <div id="target" style="width: 10px; --foo: auto; transition-duration: 1s, 2s;"></div> <script> 'use strict'; -const styleMap = document.getElementById('target').attributeStyleMap; +const styleMap = document.getElementById('target').computedStyleMap(); test(() => { assert_throws(new TypeError(), () => styleMap.get('lemon')); }, 'Calling StylePropertyMap.get with an unsupported property throws a TypeError'); test(() => { - assert_equals(styleMap.get('height'), null); -}, 'Calling StylePropertyMap.get with a property not in the property model returns null'); - -test(() => { assert_equals(styleMap.get('--Foo'), null); }, 'Calling StylePropertyMap.get with a custom property not in the property model returns null');
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getAll.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getAll.html similarity index 77% copy from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getAll.html copy to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getAll.html index 6cfbe75b1..35f80a3 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getAll.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getAll.html
@@ -2,25 +2,20 @@ <meta charset="utf-8"> <title>StylePropertyMap.getAll tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <div id="target" style="width: 10px; --foo: auto; transition-duration: 1s, 2s;"></div> <script> 'use strict'; -const styleMap = document.getElementById('target').attributeStyleMap; +const styleMap = document.getElementById('target').computedStyleMap(); test(() => { assert_throws(new TypeError(), () => styleMap.getAll('lemon')); }, 'Calling StylePropertyMap.getAll with an unsupported property throws a TypeError'); test(() => { - const result = styleMap.getAll('height'); - assert_equals(result.length, 0); -}, 'Calling StylePropertyMap.getAll with a property not in the property model returns an empty list'); - -test(() => { const result = styleMap.getAll('--Foo'); assert_equals(result.length, 0); }, 'Calling StylePropertyMap.getAll with a custom property not in the property model returns an empty list');
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getProperties-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getProperties-expected.txt new file mode 100644 index 0000000..3ee9bd2881 --- /dev/null +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getProperties-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL StylePropertyMap.getProperties returns property names in correct order assert_array_equals: lengths differ, expected 297 got 294 +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getProperties.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getProperties.html new file mode 100644 index 0000000..87e9f85c --- /dev/null +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/getProperties.html
@@ -0,0 +1,19 @@ +<!doctype html> +<meta charset="utf-8"> +<title>StylePropertyMap.getProperties tests</title> +<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-getproperties"> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<div id="target" style="--A: A; width: 0px; --C: C; transition-duration: 1s, 2s; color: red; --B: B;"></div> +<script> +'use strict'; + +const target = document.getElementById('target'); + +test(() => { + const styleMap = target.computedStyleMap(); + const expectedProperties = [...getComputedStyle(target)].sort().concat('--A', '--B', '--C'); + assert_array_equals(styleMap.getProperties(), expectedProperties); +}, 'StylePropertyMap.getProperties returns property names in correct order'); + +</script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/has.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/has.html similarity index 80% copy from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/has.html copy to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/has.html index 0578094..53d6f6e 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/has.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/has.html
@@ -2,20 +2,19 @@ <meta charset="utf-8"> <title>StylePropertyMap.has tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#check-if-stylepropertymap-has-a-property"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> <div id="target" style="width: 10px; --foo: auto; background-image: url('A'), url('B')"></div> <script> 'use strict'; -const styleMap = document.getElementById('target').attributeStyleMap; +const styleMap = document.getElementById('target').computedStyleMap(); test(() => { assert_throws(new TypeError(), () => styleMap.has('lemon')); }, 'Calling StylePropertyMap.has with an unsupported property throws a TypeError'); const gTestCases = [ - { property: 'height', expected: false, desc: 'a property not in the property model' }, { property: '--Foo', expected: false, desc: 'a custom property not in the property model' }, { property: 'width', expected: true, desc: 'a valid property' }, { property: 'wIdTh', expected: true, desc: 'a valid property in mixed case' },
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/iterable-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/iterable-expected.txt new file mode 100644 index 0000000..28bb1a4a --- /dev/null +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/iterable-expected.txt
@@ -0,0 +1,7 @@ +This is a testharness.js-based test. +FAIL StylePropertyMap iterates properties in correct order assert_array_equals: lengths differ, expected 297 got 294 +PASS StylePropertyMap iterator returns CSS properties with the correct CSSStyleValue +PASS StylePropertyMap iterator returns list-valued properties with the correct CSSStyleValue +FAIL StylePropertyMap iterator returns custom properties with the correct CSSStyleValue assert_equals: expected object "--A" but got null +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/iterable.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/iterable.html new file mode 100644 index 0000000..498d2ec --- /dev/null +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/computed/iterable.html
@@ -0,0 +1,41 @@ +<!doctype html> +<meta charset="utf-8"> +<title>StylePropertyMap iterable tests</title> +<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#the-stylepropertymap"> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> +<div id="target" style="--A: A; width: 10px; --C: C; transition-duration: 1s, 2s; color: red; --B: B;"></div> +<script> +'use strict'; + +function findInStyleMap(styleMap, property) { + const index = [...styleMap.keys()].indexOf(property); + if (index == -1) + return null; + return [...styleMap.values()][index]; +} + +const target = document.getElementById('target'); +const styleMap = target.computedStyleMap(); + +test(() => { + const expectedKeys = [...getComputedStyle(target)].sort().concat('--A', '--B', '--C'); + assert_array_equals([...styleMap], expectedKeys); +}, 'StylePropertyMap iterates properties in correct order'); + +test(() => { + assert_style_value_equals(findInStyleMap(styleMap, 'width'), CSS.px(10)); +}, 'StylePropertyMap iterator returns CSS properties with the correct CSSStyleValue'); + +test(() => { + assert_style_value_array_equals(findInStyleMap(styleMap, 'transition-duration'), [CSS.s(1), CSS.s(2)]); +}, 'StylePropertyMap iterator returns list-valued properties with the correct CSSStyleValue'); + +test(() => { + assert_style_value_equals(findInStyleMap(styleMap, '--A'), new CSSUnparsedValue('--A')); + assert_style_value_equals(findInStyleMap(styleMap, '--B'), new CSSUnparsedValue('--B')); + assert_style_value_equals(findInStyleMap(styleMap, '--C'), new CSSUnparsedValue('--C')); +}, 'StylePropertyMap iterator returns custom properties with the correct CSSStyleValue'); + +</script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/append.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/append.html similarity index 91% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/append.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/append.html index 68c8360..f06f394 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/append.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/append.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap.append tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#append-to-a-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/delete-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/delete-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/delete-expected.txt rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/delete-expected.txt
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/delete.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/delete.html similarity index 88% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/delete.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/delete.html index 55cbf6f2..0f65357 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/delete.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/delete.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap.delete tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#delete-a-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/get.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/get.html similarity index 90% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/get.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/get.html index a835b872c..089c5ae5 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/get.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/get.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap.get tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#get-a-value-from-a-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <div id="target" style="width: 10px; --foo: auto; transition-duration: 1s, 2s;"></div> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getAll.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/getAll.html similarity index 90% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getAll.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/getAll.html index 6cfbe75b1..5c72c22 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getAll.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/getAll.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap.getAll tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <div id="target" style="width: 10px; --foo: auto; transition-duration: 1s, 2s;"></div> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getProperties.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/getProperties.html similarity index 88% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getProperties.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/getProperties.html index 8f7b2ca..95bde5a 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/getProperties.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/getProperties.html
@@ -2,8 +2,8 @@ <meta charset="utf-8"> <title>StylePropertyMap.getProperties tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-getproperties"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> <div id="target-empty"></div> <div id="target-multiple" style="--A: A; width: 0px; --C: C; transition-duration: 1s, 2s; color: red; --B: B;"></div> <script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/has.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/has.html similarity index 91% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/has.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/has.html index 0578094..71ee151a 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/has.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/has.html
@@ -2,8 +2,8 @@ <meta charset="utf-8"> <title>StylePropertyMap.has tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#check-if-stylepropertymap-has-a-property"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> <div id="target" style="width: 10px; --foo: auto; background-image: url('A'), url('B')"></div> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/iterable.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/iterable.html similarity index 92% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/iterable.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/iterable.html index 59a8e5b0..3face508 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/iterable.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/iterable.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap iterable tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#the-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <div id="target-empty"></div> <div id="target-multiple" style="--A: A; width: 10px; --C: C; transition-duration: 1s, 2s; color: red; --B: B;"></div> <script>
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/set-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/set-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/set-expected.txt rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/set-expected.txt
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/set.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/set.html similarity index 93% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/set.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/set.html index 7c04c5e..d0ba3f1 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/set.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/set.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap.set</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#set-a-value-on-a-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/update-expected.txt b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/update-expected.txt similarity index 100% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/update-expected.txt rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/update-expected.txt
diff --git a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/update.html b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/update.html similarity index 94% rename from third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/update.html rename to third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/update.html index ccb9c74..49760bc 100644 --- a/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/update.html +++ b/third_party/WebKit/LayoutTests/typedcssom/the-stylepropertymap/inline/update.html
@@ -2,9 +2,9 @@ <meta charset="utf-8"> <title>StylePropertyMap.update tests</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#update-a-value-in-a-stylepropertymap"> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../resources/testhelper.js"></script> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<script src="../../resources/testhelper.js"></script> <script> 'use strict';
diff --git a/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html b/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html index 1a01af6..9a8a286 100644 --- a/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html +++ b/third_party/WebKit/LayoutTests/vr/VRDisplay_rAF_fires_with_window_rAF.html
@@ -5,10 +5,11 @@ <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> <script src="file:///gen/device/vr/vr_service.mojom.js"></script> <script src="resources/mock-vr-service.js"></script> +<script src="resources/test-constants.js"></script> <script> let fakeDisplays = fakeVRDisplays(); -vr_test( (t) => { +vr_test( (t, mock_service) => { return navigator.getVRDisplays().then( (displays) => { let display = displays[0]; let counter = 0; @@ -53,10 +54,18 @@ counter++; } - window.requestAnimationFrame(onWindowAnimationFrame1); - display.requestAnimationFrame(onDisplayAnimationFrame1); - display.requestAnimationFrame(onDisplayAnimationFrame2); - window.requestAnimationFrame(onWindowAnimationFrame2); + function onFirstAnimationFrame() { + window.requestAnimationFrame(onWindowAnimationFrame1); + display.requestAnimationFrame(onDisplayAnimationFrame1); + display.requestAnimationFrame(onDisplayAnimationFrame2); + window.requestAnimationFrame(onWindowAnimationFrame2); + } + + // Ensure that there's a non-null pose available for the mock framework to + // return, and wait for one rAF callback to let it propagate. This is a + // workaround for delayed promise execution in the mocking framework. + mock_service.mockVRDisplays_[0].setPose(VALID_POSE); + display.requestAnimationFrame(onFirstAnimationFrame); }, (err) => { t.step( () => { assert_unreached("getVRDisplays rejected");
diff --git a/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html b/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html index 8e370e8d..442bb12 100644 --- a/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html +++ b/third_party/WebKit/LayoutTests/vr/getFrameData_oneframeupdate.html
@@ -17,7 +17,6 @@ var counter = 0; function onFrame() { - display.requestAnimationFrame(onFrame); if (counter == 0) { t.step( () => { assert_false(display.getFrameData(fd)); @@ -27,14 +26,6 @@ assert_false(display.getFrameData(fd)); }, "Does not update within the same frame"); } else { - // TODO(mthiesse): This is a workaround for crbug.com/787196. Either - // remove the workaround once fixed or remove this todo. - // Let several animation frames get triggered so we're sure to have a - // pose - if (counter <= 2) { - counter++; - return; - } t.step( () => { assert_true(display.getFrameData(fd)); assert_not_equals(fd.pose, null); @@ -49,6 +40,10 @@ t.done(); } counter++; + // Use rAF late so that the mock's setPose above can supply its data + // before RequestVSync calls GetPose. This is an artifact of the mocking + // framework, the real implementation doesn't have this restriction. + display.requestAnimationFrame(onFrame); } display.requestAnimationFrame(onFrame);
diff --git a/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html b/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html index 559981d..b5bf29ac 100644 --- a/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html +++ b/third_party/WebKit/LayoutTests/vr/getFrameData_samewithinframe.html
@@ -16,16 +16,9 @@ var fd1 = new VRFrameData(); var fd2 = new VRFrameData(); mock_service.mockVRDisplays_[0].setPose(expected_pose); - var numFrames = 0; function onFrame() { display.requestAnimationFrame(onFrame); - numFrames++; - // Let one rAF run before checking in order to ensure we actually get - // frame data - if (numFrames == 1) { - return; - } t.step( () => { assert_true(display.getFrameData(fd1));
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp index 6819999..3ce6b91b 100644 --- a/third_party/WebKit/Source/core/dom/Document.cpp +++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -608,7 +608,6 @@ visually_ordered_(false), ready_state_(kComplete), parsing_state_(kFinishedParsing), - goto_anchor_needed_after_stylesheets_load_(false), contains_validity_style_rules_(false), contains_plugins_(false), ignore_destructive_write_count_(0), @@ -2387,9 +2386,6 @@ if (frame_view->NeedsLayout()) frame_view->UpdateLayout(); - if (goto_anchor_needed_after_stylesheets_load_) - frame_view->ProcessUrlFragment(url_); - if (Lifecycle().GetState() < DocumentLifecycle::kLayoutClean) Lifecycle().AdvanceTo(DocumentLifecycle::kLayoutClean); @@ -3269,12 +3265,6 @@ (!GetLayoutViewItem().FirstChild() || GetLayoutViewItem().NeedsLayout())) View()->UpdateLayout(); - - // TODO(bokan): This is a temporary fix to https://crbug.com/788486. - // There's some better cleanups that should be done to follow-up: - // https://crbug.com/795381. - if (View() && goto_anchor_needed_after_stylesheets_load_) - View()->ProcessUrlFragment(url_); } load_event_progress_ = kLoadEventCompleted;
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h index 2629fb7..93965f1 100644 --- a/third_party/WebKit/Source/core/dom/Document.h +++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -479,13 +479,6 @@ return *style_engine_.Get(); } - bool GotoAnchorNeededAfterStylesheetsLoad() { - return goto_anchor_needed_after_stylesheets_load_; - } - void SetGotoAnchorNeededAfterStylesheetsLoad(bool b) { - goto_anchor_needed_after_stylesheets_load_ = b; - } - void ScheduleUseShadowTreeUpdate(SVGUseElement&); void UnscheduleUseShadowTreeUpdate(SVGUseElement&); @@ -1642,7 +1635,6 @@ DocumentReadyState ready_state_; ParsingState parsing_state_; - bool goto_anchor_needed_after_stylesheets_load_; bool is_dns_prefetch_enabled_; bool have_explicitly_disabled_dns_prefetch_; bool contains_validity_style_rules_;
diff --git a/third_party/WebKit/Source/core/frame/Frame.h b/third_party/WebKit/Source/core/frame/Frame.h index 09e4d9c..4bc6821b 100644 --- a/third_party/WebKit/Source/core/frame/Frame.h +++ b/third_party/WebKit/Source/core/frame/Frame.h
@@ -123,11 +123,6 @@ // otherwise. virtual bool PrepareForCommit() = 0; - // TODO(japhet): These should all move to LocalFrame. - virtual void PrintNavigationErrorMessage(const Frame&, - const char* reason) = 0; - virtual void PrintNavigationWarning(const String&) = 0; - // TODO(pilgrim): Replace all instances of ownerLayoutObject() with // ownerLayoutItem(), https://crbug.com/499321 LayoutEmbeddedContent* OwnerLayoutObject()
diff --git a/third_party/WebKit/Source/core/frame/FrameClient.h b/third_party/WebKit/Source/core/frame/FrameClient.h index 478940ef..8e64265 100644 --- a/third_party/WebKit/Source/core/frame/FrameClient.h +++ b/third_party/WebKit/Source/core/frame/FrameClient.h
@@ -18,9 +18,6 @@ public: virtual bool InShadowTree() const = 0; - // TODO(dcheng): Move this into LocalFrameClient, since remote frames don't - // need this. - virtual void WillBeDetached() = 0; virtual void Detached(FrameDetachType) = 0; virtual Frame* Opener() const = 0;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.h b/third_party/WebKit/Source/core/frame/LocalFrame.h index 94398c3a..0a7818c 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrame.h +++ b/third_party/WebKit/Source/core/frame/LocalFrame.h
@@ -119,8 +119,8 @@ void Detach(FrameDetachType) override; bool ShouldClose() override; SecurityContext* GetSecurityContext() const override; - void PrintNavigationErrorMessage(const Frame&, const char* reason) override; - void PrintNavigationWarning(const String&) override; + void PrintNavigationErrorMessage(const Frame&, const char* reason); + void PrintNavigationWarning(const String&); bool PrepareForCommit() override; void DidChangeVisibilityState() override; void DidFreeze() override;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameClient.h b/third_party/WebKit/Source/core/frame/LocalFrameClient.h index 3b9d725d..1b1a697 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrameClient.h +++ b/third_party/WebKit/Source/core/frame/LocalFrameClient.h
@@ -110,6 +110,7 @@ virtual bool HasWebView() const = 0; // mainly for assertions + virtual void WillBeDetached() = 0; virtual void DispatchWillSendRequest(ResourceRequest&) = 0; virtual void DispatchDidReceiveResponse(const ResourceResponse&) = 0; virtual void DispatchDidLoadResourceFromMemoryCache(
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp index 2ce6766..411a4d0 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp +++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -240,6 +240,7 @@ needs_intersection_observation_(false), needs_forced_compositing_update_(false), scroll_gesture_region_is_dirty_(false), + needs_focus_on_fragment_(false), main_thread_scrolling_reasons_(0), paint_frame_count_(0), unique_id_(NewUniqueObjectId()) { @@ -1917,14 +1918,6 @@ UrlFragmentBehavior behavior) { DCHECK(frame_->GetDocument()); - if (behavior == kUrlFragmentScroll && - !frame_->GetDocument()->IsRenderingReady()) { - frame_->GetDocument()->SetGotoAnchorNeededAfterStylesheetsLoad(true); - return false; - } - - frame_->GetDocument()->SetGotoAnchorNeededAfterStylesheetsLoad(false); - Element* anchor_node = frame_->GetDocument()->FindAnchor(name); // Setting to null will clear the current target. @@ -1935,12 +1928,12 @@ ToSVGSVGElementOrNull(frame_->GetDocument()->documentElement())) { svg->SetupInitialView(name, anchor_node); if (!anchor_node) - return true; + return false; } // If this is not the top-level frame, then don't scroll to the // anchor position. if (!frame_->IsMainFrame()) - behavior = kUrlFragmentDontScroll; + return false; } // Implement the rule that "" and "top" both mean top of page as in other @@ -1949,46 +1942,33 @@ !(name.IsEmpty() || DeprecatedEqualIgnoringCase(name, "top"))) return false; - if (behavior == kUrlFragmentScroll) { - SetFragmentAnchor(anchor_node ? static_cast<Node*>(anchor_node) - : frame_->GetDocument()); + if (behavior == kUrlFragmentDontScroll) + return true; + + if (!anchor_node) { + fragment_anchor_ = frame_->GetDocument(); + needs_focus_on_fragment_ = false; + } else { + fragment_anchor_ = anchor_node; + needs_focus_on_fragment_ = true; } - // If the anchor accepts keyboard focus and fragment scrolling is allowed, - // move focus there to aid users relying on keyboard navigation. - // If anchorNode is not focusable or fragment scrolling is not allowed, - // clear focus, which matches the behavior of other browsers. - if (anchor_node) { - frame_->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); - if (behavior == kUrlFragmentScroll && anchor_node->IsFocusable()) { - anchor_node->focus(); - } else { - if (behavior == kUrlFragmentScroll) { - frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint( - anchor_node); - } - frame_->GetDocument()->ClearFocusedElement(); - } + // If rendering is blocked, we'll necessarily have a layout to kick off the + // scroll and focus. + if (frame_->GetDocument()->IsRenderingReady()) { + frame_->GetDocument()->UpdateStyleAndLayoutTree(); + + // If layout is needed, we will scroll in performPostLayoutTasks. Otherwise, + // scroll and focus immediately. + if (NeedsLayout()) + UpdateLayout(); + else + ScrollAndFocusFragmentAnchor(); } + return true; } -void LocalFrameView::SetFragmentAnchor(Node* anchor_node) { - DCHECK(anchor_node); - fragment_anchor_ = anchor_node; - - // We need to update the layout tree before scrolling. - frame_->GetDocument()->UpdateStyleAndLayoutTree(); - - // If layout is needed, we will scroll in performPostLayoutTasks. Otherwise, - // scroll immediately. - LayoutViewItem layout_view_item = this->GetLayoutViewItem(); - if (!layout_view_item.IsNull() && layout_view_item.NeedsLayout()) - UpdateLayout(); - else - ScrollToFragmentAnchor(); -} - void LocalFrameView::ClearFragmentAnchor() { fragment_anchor_ = nullptr; } @@ -2463,7 +2443,7 @@ }); } -void LocalFrameView::ScrollToFragmentAnchor() { +void LocalFrameView::ScrollAndFocusFragmentAnchor() { Node* anchor_node = fragment_anchor_; if (!anchor_node) return; @@ -2506,6 +2486,22 @@ if (AXObjectCache* cache = frame_->GetDocument()->ExistingAXObjectCache()) cache->HandleScrolledToAnchor(anchor_node); + + // If the anchor accepts keyboard focus and fragment scrolling is allowed, + // move focus there to aid users relying on keyboard navigation. + // If anchorNode is not focusable or fragment scrolling is not allowed, + // clear focus, which matches the behavior of other browsers. + if (needs_focus_on_fragment_) { + if (anchor_node->IsElementNode() && + ToElement(anchor_node)->IsFocusable()) { + ToElement(anchor_node)->focus(); + } else { + frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint( + anchor_node); + frame_->GetDocument()->ClearFocusedElement(); + } + needs_focus_on_fragment_ = false; + } } // The fragment anchor should only be maintained while the frame is still @@ -2632,7 +2628,7 @@ // If we're restoring a scroll position from history, that takes precedence // over scrolling to the anchor in the URL. - ScrollToFragmentAnchor(); + ScrollAndFocusFragmentAnchor(); GetFrame().Loader().RestoreScrollPositionAndViewState(); SendResizeEventIfNeeded(); } @@ -4267,8 +4263,8 @@ Document* document = frame_->GetDocument(); document->EnqueueScrollEventForNode(document); - frame_->GetEventHandler().DispatchFakeMouseMoveEventSoon( - MouseEventManager::FakeMouseMoveReason::kDuringScroll); + GetLayoutView()->DispatchFakeMouseMoveEventSoon(GetFrame().GetEventHandler()); + if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) { Page* page = GetFrame().GetPage(); if (page)
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h index 204cc74..e52d3b5 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrameView.h +++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -352,8 +352,8 @@ enum UrlFragmentBehavior { kUrlFragmentScroll, kUrlFragmentDontScroll }; // Updates the fragment anchor element based on URL's fragment identifier. // Updates corresponding ':target' CSS pseudo class on the anchor element. - // If |UrlFragmentScroll| is passed in then makes the anchor element - // focused and also visible by scrolling to it. The scroll offset is + // If |UrlFragmentScroll| is passed in sets the the anchor element so that it + // will be focused and scrolled into view during layout. The scroll offset is // maintained during the frame loading process. void ProcessUrlFragment(const KURL&, UrlFragmentBehavior = kUrlFragmentScroll); @@ -1103,7 +1103,7 @@ bool ProcessUrlFragmentHelper(const String&, UrlFragmentBehavior); void SetFragmentAnchor(Node*); - void ScrollToFragmentAnchor(); + void ScrollAndFocusFragmentAnchor(); void DidScrollTimerFired(TimerBase*); void UpdateLayersAndCompositingAfterScrollIfNeeded(); @@ -1323,6 +1323,8 @@ bool needs_forced_compositing_update_; bool scroll_gesture_region_is_dirty_; + bool needs_focus_on_fragment_; + Member<ElementVisibilityObserver> visibility_observer_; IntRect remote_viewport_intersection_;
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp index 1c14d43..e8c12aac 100644 --- a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp +++ b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
@@ -97,7 +97,6 @@ // the parent is a local frame. if (view_) view_->Dispose(); - Client()->WillBeDetached(); GetWindowProxyManager()->ClearForClose(); SetView(nullptr); // ... the RemoteDOMWindow will need to be informed of detachment,
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.h b/third_party/WebKit/Source/core/frame/RemoteFrame.h index 3038478..472803d 100644 --- a/third_party/WebKit/Source/core/frame/RemoteFrame.h +++ b/third_party/WebKit/Source/core/frame/RemoteFrame.h
@@ -35,8 +35,6 @@ void AddResourceTiming(const ResourceTimingInfo&) override; void Detach(FrameDetachType) override; RemoteSecurityContext* GetSecurityContext() const override; - void PrintNavigationErrorMessage(const Frame&, const char* reason) override {} - void PrintNavigationWarning(const String&) override {} bool PrepareForCommit() override; bool ShouldClose() override; void DidFreeze() override;
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.cpp b/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.cpp index fce0de9df..2e74eff8 100644 --- a/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.cpp +++ b/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.cpp
@@ -55,8 +55,6 @@ return web_frame_->InShadowTree(); } -void RemoteFrameClientImpl::WillBeDetached() {} - void RemoteFrameClientImpl::Detached(FrameDetachType type) { // Alert the client that the frame is being detached. WebRemoteFrameClient* client = web_frame_->Client();
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.h b/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.h index 823a00bd..a3b9e1f 100644 --- a/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.h +++ b/third_party/WebKit/Source/core/frame/RemoteFrameClientImpl.h
@@ -18,7 +18,6 @@ // FrameClient overrides: bool InShadowTree() const override; - void WillBeDetached() override; void Detached(FrameDetachType) override; Frame* Opener() const override; void SetOpener(Frame*) override;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index 235bc6fe..21f23f8 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -1165,6 +1165,14 @@ : nullptr; } +void LayoutBox::DispatchFakeMouseMoveEventSoon(EventHandler& event_handler) { + const LayoutBoxModelObject& container = ContainerForPaintInvalidation(); + FloatQuad quad = + FloatQuad(FloatRect(VisualRectIncludingCompositedScrolling(container))); + quad = container.LocalToAbsoluteQuad(quad); + event_handler.DispatchFakeMouseMoveEventSoonInQuad(quad); +} + void LayoutBox::ScrollByRecursively(const ScrollOffset& delta) { if (delta.IsZero()) return;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h index b0c5387..ae03b92 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.h +++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -34,6 +34,7 @@ namespace blink { +class EventHandler; class LayoutBlockFlow; class LayoutMultiColumnSpannerPlaceholder; struct NGPhysicalBoxStrut; @@ -1002,6 +1003,7 @@ const IntPoint& point_in_root_frame) const; static LayoutBox* FindAutoscrollable(LayoutObject*); virtual void StopAutoscroll() {} + virtual void DispatchFakeMouseMoveEventSoon(EventHandler&); DISABLE_CFI_PERF bool HasAutoVerticalScrollbar() const { return HasOverflowClip() && Style()->HasAutoVerticalScroll();
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp index b063cba..8dbda507 100644 --- a/third_party/WebKit/Source/core/layout/LayoutView.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -29,6 +29,7 @@ #include "core/frame/LocalFrameView.h" #include "core/frame/Settings.h" #include "core/html/HTMLIFrameElement.h" +#include "core/input/EventHandler.h" #include "core/layout/HitTestResult.h" #include "core/layout/LayoutCounter.h" #include "core/layout/LayoutEmbeddedContent.h" @@ -733,6 +734,11 @@ #undef RETURN_SCROLLBAR_MODE } +void LayoutView::DispatchFakeMouseMoveEventSoon(EventHandler& event_handler) { + event_handler.DispatchFakeMouseMoveEventSoon( + MouseEventManager::FakeMouseMoveReason::kDuringScroll); +} + IntRect LayoutView::DocumentRect() const { LayoutRect overflow_rect(LayoutOverflowRect()); FlipForWritingMode(overflow_rect);
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.h b/third_party/WebKit/Source/core/layout/LayoutView.h index f0148d44..2600b60 100644 --- a/third_party/WebKit/Source/core/layout/LayoutView.h +++ b/third_party/WebKit/Source/core/layout/LayoutView.h
@@ -161,6 +161,8 @@ void CalculateScrollbarModes(ScrollbarMode& h_mode, ScrollbarMode& v_mode) const; + void DispatchFakeMouseMoveEventSoon(EventHandler&) override; + LayoutState* GetLayoutState() const { return layout_state_; } void UpdateHitTestResult(HitTestResult&, const LayoutPoint&) override;
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.h b/third_party/WebKit/Source/core/loader/EmptyClients.h index e911dcde..ab86c73 100644 --- a/third_party/WebKit/Source/core/loader/EmptyClients.h +++ b/third_party/WebKit/Source/core/loader/EmptyClients.h
@@ -439,7 +439,6 @@ // FrameClient implementation. bool InShadowTree() const override { return false; } - void WillBeDetached() override {} void Detached(FrameDetachType) override {} Frame* Opener() const override { return nullptr; } void SetOpener(Frame*) override {}
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp index 104dabd..e53b5f3f 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -433,18 +433,7 @@ UpdateCompositingLayersAfterScroll(); } - const LayoutBoxModelObject& paint_invalidation_container = - Box().ContainerForPaintInvalidation(); - - FloatQuad quad_for_fake_mouse_move_event = FloatQuad(FloatRect( - Layer()->GetLayoutObject().VisualRectIncludingCompositedScrolling( - paint_invalidation_container))); - - quad_for_fake_mouse_move_event = - paint_invalidation_container.LocalToAbsoluteQuad( - quad_for_fake_mouse_move_event); - frame->GetEventHandler().DispatchFakeMouseMoveEventSoonInQuad( - quad_for_fake_mouse_move_event); + Box().DispatchFakeMouseMoveEventSoon(frame->GetEventHandler()); if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) { Page* page = frame->GetPage();
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js b/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js index 96629b5a..fcaac7b 100644 --- a/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js +++ b/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
@@ -286,7 +286,7 @@ if (excludedTokens.has(token.type)) return Promise.resolve(historyWords); } - return ObjectUI.JavaScriptAutocomplete.completionsForTextInCurrentContext(before, query, force) + return ObjectUI.javaScriptAutocomplete.completionsForTextInCurrentContext(before, query, force) .then(words => words.concat(historyWords)); }
diff --git a/third_party/WebKit/Source/devtools/front_end/object_ui/JavaScriptAutocomplete.js b/third_party/WebKit/Source/devtools/front_end/object_ui/JavaScriptAutocomplete.js index c8871af2..355a312 100644 --- a/third_party/WebKit/Source/devtools/front_end/object_ui/JavaScriptAutocomplete.js +++ b/third_party/WebKit/Source/devtools/front_end/object_ui/JavaScriptAutocomplete.js
@@ -2,185 +2,86 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -ObjectUI.JavaScriptAutocomplete = {}; - -/** @typedef {{title:(string|undefined), items:Array<string>}} */ -ObjectUI.JavaScriptAutocomplete.CompletionGroup; - -/** - * @param {string} text - * @param {string} query - * @param {boolean=} force - * @return {!Promise<!UI.SuggestBox.Suggestions>} - */ -ObjectUI.JavaScriptAutocomplete.completionsForTextInCurrentContext = function(text, query, force) { - var clippedExpression = ObjectUI.JavaScriptAutocomplete._clipExpression(text, true); - var mapCompletionsPromise = ObjectUI.JavaScriptAutocomplete._mapCompletions(text, query); - return ObjectUI.JavaScriptAutocomplete.completionsForExpression(clippedExpression, query, force) - .then(completions => mapCompletionsPromise.then(mapCompletions => mapCompletions.concat(completions))); -}; - -/** - * @param {string} text - * @param {boolean=} allowEndingBracket - * @return {string} - */ -ObjectUI.JavaScriptAutocomplete._clipExpression = function(text, allowEndingBracket) { - var index; - var stopChars = new Set('=:({;,!+-*/&|^<>`'.split('')); - var whiteSpaceChars = new Set(' \r\n\t'.split('')); - var continueChars = new Set('[. \r\n\t'.split('')); - - for (index = text.length - 1; index >= 0; index--) { - if (stopChars.has(text.charAt(index))) - break; - if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charAt(index - 1))) - break; +ObjectUI.JavaScriptAutocomplete = class { + constructor() { + /** @type {!Map<string, {date: number, value: !Promise<?Object>}>} */ + this._expressionCache = new Map(); + ConsoleModel.consoleModel.addEventListener( + ConsoleModel.ConsoleModel.Events.CommandEvaluated, this._clearCache, this); + SDK.targetManager.addModelListener( + SDK.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextChanged, this._clearCache, this); + SDK.targetManager.addModelListener( + SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this._clearCache, this); } - var clippedExpression = text.substring(index + 1).trim(); - var bracketCount = 0; - index = clippedExpression.length - 1; - while (index >= 0) { - var character = clippedExpression.charAt(index); - if (character === ']') - bracketCount++; - // Allow an open bracket at the end for property completion. - if (character === '[' && (index < clippedExpression.length - 1 || !allowEndingBracket)) { - bracketCount--; - if (bracketCount < 0) + _clearCache() { + this._expressionCache.clear(); + } + + /** + * @param {string} text + * @param {string} query + * @param {boolean=} force + * @return {!Promise<!UI.SuggestBox.Suggestions>} + */ + completionsForTextInCurrentContext(text, query, force) { + var clippedExpression = this._clipExpression(text, true); + var mapCompletionsPromise = this._mapCompletions(text, query); + return this._completionsForExpression(clippedExpression, query, force) + .then(completions => mapCompletionsPromise.then(mapCompletions => mapCompletions.concat(completions))); + } + + /** + * @param {string} text + * @param {boolean=} allowEndingBracket + * @return {string} + */ + _clipExpression(text, allowEndingBracket) { + var index; + var stopChars = new Set('=:({;,!+-*/&|^<>`'.split('')); + var whiteSpaceChars = new Set(' \r\n\t'.split('')); + var continueChars = new Set('[. \r\n\t'.split('')); + + for (index = text.length - 1; index >= 0; index--) { + if (stopChars.has(text.charAt(index))) + break; + if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charAt(index - 1))) break; } - index--; - } - return clippedExpression.substring(index + 1).trim(); -}; + var clippedExpression = text.substring(index + 1).trim(); + var bracketCount = 0; -/** - * @param {string} text - * @param {string} query - * @return {!Promise<!UI.SuggestBox.Suggestions>} - */ -ObjectUI.JavaScriptAutocomplete._mapCompletions = async function(text, query) { - var mapMatch = text.match(/\.\s*(get|set|delete)\s*\(\s*$/); - var executionContext = UI.context.flavor(SDK.ExecutionContext); - if (!executionContext || !mapMatch) - return []; - - var clippedExpression = ObjectUI.JavaScriptAutocomplete._clipExpression(text.substring(0, mapMatch.index)); - var result = await executionContext.evaluate( - { - expression: clippedExpression, - objectGroup: 'completion', - includeCommandLineAPI: true, - silent: true, - returnByValue: false, - generatePreview: false - }, - /* userGesture */ false, /* awaitPromise */ false); - if (result.error || !!result.exceptionDetails || result.object.subtype !== 'map') - return []; - var properties = await result.object.getOwnPropertiesPromise(false); - var internalProperties = properties.internalProperties || []; - var entriesProperty = internalProperties.find(property => property.name === '[[Entries]]'); - if (!entriesProperty) - return []; - var keysObj = await entriesProperty.value.callFunctionJSONPromise(getEntries); - return gotKeys(Object.keys(keysObj)); - - /** - * @suppressReceiverCheck - * @this {!Array<{key:?, value:?}>} - * @return {!Object} - */ - function getEntries() { - var result = {__proto__: null}; - for (var i = 0; i < this.length; i++) { - if (typeof this[i].key === 'string') - result[this[i].key] = true; + index = clippedExpression.length - 1; + while (index >= 0) { + var character = clippedExpression.charAt(index); + if (character === ']') + bracketCount++; + // Allow an open bracket at the end for property completion. + if (character === '[' && (index < clippedExpression.length - 1 || !allowEndingBracket)) { + bracketCount--; + if (bracketCount < 0) + break; + } + index--; } - return result; + return clippedExpression.substring(index + 1).trim(); } /** - * @param {!Array<string>} rawKeys - * @return {!UI.SuggestBox.Suggestions} + * @param {string} text + * @param {string} query + * @return {!Promise<!UI.SuggestBox.Suggestions>} */ - function gotKeys(rawKeys) { - var caseSensitivePrefix = []; - var caseInsensitivePrefix = []; - var caseSensitiveAnywhere = []; - var caseInsensitiveAnywhere = []; - var quoteChar = '"'; - if (query.startsWith('\'')) - quoteChar = '\''; - var endChar = ')'; - if (mapMatch[0].indexOf('set') !== -1) - endChar = ', '; + async _mapCompletions(text, query) { + var mapMatch = text.match(/\.\s*(get|set|delete)\s*\(\s*$/); + var executionContext = UI.context.flavor(SDK.ExecutionContext); + if (!executionContext || !mapMatch) + return []; - var sorter = rawKeys.length < 1000 ? String.naturalOrderComparator : undefined; - var keys = rawKeys.sort(sorter).map(key => quoteChar + key + quoteChar); - - for (var key of keys) { - if (key.length < query.length) - continue; - if (query.length && key.toLowerCase().indexOf(query.toLowerCase()) === -1) - continue; - // Substitute actual newlines with newline characters. @see crbug.com/498421 - var title = key.split('\n').join('\\n'); - var text = title + endChar; - - if (key.startsWith(query)) - caseSensitivePrefix.push({text: text, title: title, priority: 4}); - else if (key.toLowerCase().startsWith(query.toLowerCase())) - caseInsensitivePrefix.push({text: text, title: title, priority: 3}); - else if (key.indexOf(query) !== -1) - caseSensitiveAnywhere.push({text: text, title: title, priority: 2}); - else - caseInsensitiveAnywhere.push({text: text, title: title, priority: 1}); - } - var suggestions = caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere); - if (suggestions.length) - suggestions[0].subtitle = Common.UIString('Keys'); - return suggestions; - } -}; - -/** - * @param {string} expressionString - * @param {string} query - * @param {boolean=} force - * @return {!Promise<!UI.SuggestBox.Suggestions>} - */ -ObjectUI.JavaScriptAutocomplete.completionsForExpression = async function(expressionString, query, force) { - var executionContext = UI.context.flavor(SDK.ExecutionContext); - if (!executionContext) - return []; - - var lastIndex = expressionString.length - 1; - - var dotNotation = (expressionString[lastIndex] === '.'); - var bracketNotation = (expressionString.length > 1 && expressionString[lastIndex] === '['); - - if (dotNotation || bracketNotation) - expressionString = expressionString.substr(0, lastIndex); - else - expressionString = ''; - - // User is entering float value, do not suggest anything. - if ((expressionString && !isNaN(expressionString)) || (!expressionString && query && !isNaN(query))) - return []; - - - if (!query && !expressionString && !force) - return []; - var selectedFrame = executionContext.debuggerModel.selectedCallFrame(); - if (!expressionString && selectedFrame) { - return completionsOnPause(selectedFrame); - } else { + var clippedExpression = this._clipExpression(text.substring(0, mapMatch.index)); var result = await executionContext.evaluate( { - expression: expressionString, + expression: clippedExpression, objectGroup: 'completion', includeCommandLineAPI: true, silent: true, @@ -188,132 +89,266 @@ generatePreview: false }, /* userGesture */ false, /* awaitPromise */ false); - var completionGroups = await completionsOnGlobal(result); - return receivedPropertyNames(completionGroups); + if (result.error || !!result.exceptionDetails || result.object.subtype !== 'map') + return []; + var properties = await result.object.getOwnPropertiesPromise(false); + var internalProperties = properties.internalProperties || []; + var entriesProperty = internalProperties.find(property => property.name === '[[Entries]]'); + if (!entriesProperty) + return []; + var keysObj = await entriesProperty.value.callFunctionJSONPromise(getEntries); + return gotKeys(Object.keys(keysObj)); + + /** + * @suppressReceiverCheck + * @this {!Array<{key:?, value:?}>} + * @return {!Object} + */ + function getEntries() { + var result = {__proto__: null}; + for (var i = 0; i < this.length; i++) { + if (typeof this[i].key === 'string') + result[this[i].key] = true; + } + return result; + } + + /** + * @param {!Array<string>} rawKeys + * @return {!UI.SuggestBox.Suggestions} + */ + function gotKeys(rawKeys) { + var caseSensitivePrefix = []; + var caseInsensitivePrefix = []; + var caseSensitiveAnywhere = []; + var caseInsensitiveAnywhere = []; + var quoteChar = '"'; + if (query.startsWith('\'')) + quoteChar = '\''; + var endChar = ')'; + if (mapMatch[0].indexOf('set') !== -1) + endChar = ', '; + + var sorter = rawKeys.length < 1000 ? String.naturalOrderComparator : undefined; + var keys = rawKeys.sort(sorter).map(key => quoteChar + key + quoteChar); + + for (var key of keys) { + if (key.length < query.length) + continue; + if (query.length && key.toLowerCase().indexOf(query.toLowerCase()) === -1) + continue; + // Substitute actual newlines with newline characters. @see crbug.com/498421 + var title = key.split('\n').join('\\n'); + var text = title + endChar; + + if (key.startsWith(query)) + caseSensitivePrefix.push({text: text, title: title, priority: 4}); + else if (key.toLowerCase().startsWith(query.toLowerCase())) + caseInsensitivePrefix.push({text: text, title: title, priority: 3}); + else if (key.indexOf(query) !== -1) + caseSensitiveAnywhere.push({text: text, title: title, priority: 2}); + else + caseInsensitiveAnywhere.push({text: text, title: title, priority: 1}); + } + var suggestions = + caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere); + if (suggestions.length) + suggestions[0].subtitle = Common.UIString('Keys'); + return suggestions; + } } /** - * @param {!SDK.RuntimeModel.EvaluationResult} result - * @return {!Promise<!Array<!ObjectUI.JavaScriptAutocomplete.CompletionGroup>>} + * @param {string} expressionString + * @param {string} query + * @param {boolean=} force + * @return {!Promise<!UI.SuggestBox.Suggestions>} */ - async function completionsOnGlobal(result) { - if (result.error || !!result.exceptionDetails || !result.object) + async _completionsForExpression(expressionString, query, force) { + var executionContext = UI.context.flavor(SDK.ExecutionContext); + if (!executionContext) return []; - var object = result.object; - while (object && object.type === 'object' && object.subtype === 'proxy') { - var properties = await object.getOwnPropertiesPromise(false /* generatePreview */); - var internalProperties = properties.internalProperties || []; - var target = internalProperties.find(property => property.name === '[[Target]]'); - object = target ? target.value : null; - } - if (!object) + var lastIndex = expressionString.length - 1; + + var dotNotation = (expressionString[lastIndex] === '.'); + var bracketNotation = (expressionString.length > 1 && expressionString[lastIndex] === '['); + + if (dotNotation || bracketNotation) + expressionString = expressionString.substr(0, lastIndex); + else + expressionString = ''; + + // User is entering float value, do not suggest anything. + if ((expressionString && !isNaN(expressionString)) || (!expressionString && query && !isNaN(query))) return []; - var completions = []; - if (object.type === 'object' || object.type === 'function') { - completions = - await object.callFunctionJSONPromise(getCompletions, [SDK.RemoteObject.toCallArgument(object.subtype)]); - } else if (object.type === 'string' || object.type === 'number' || object.type === 'boolean') { - var evaluateResult = await executionContext.evaluate( + + + if (!query && !expressionString && !force) + return []; + var selectedFrame = executionContext.debuggerModel.selectedCallFrame(); + var completionGroups; + var TEN_SECONDS = 10000; + var cache = this._expressionCache.get(expressionString); + if (cache && cache.date + TEN_SECONDS > Date.now()) { + completionGroups = await cache.value; + } else if (!expressionString && selectedFrame) { + cache = {date: Date.now(), value: completionsOnPause(selectedFrame)}; + this._expressionCache.set(expressionString, cache); + completionGroups = await cache.value; + } else { + var resultPromise = executionContext.evaluate( { - expression: '(' + getCompletions + ')("' + object.type + '")', + expression: expressionString, objectGroup: 'completion', - includeCommandLineAPI: false, + includeCommandLineAPI: true, silent: true, - returnByValue: true, + returnByValue: false, generatePreview: false }, - /* userGesture */ false, - /* awaitPromise */ false); - if (evaluateResult.object && !evaluateResult.exceptionDetails) - completions = evaluateResult.object.value; + /* userGesture */ false, /* awaitPromise */ false); + cache = {date: Date.now(), value: resultPromise.then(result => completionsOnGlobal.call(this, result))}; + this._expressionCache.set(expressionString, cache); + completionGroups = await cache.value; } - executionContext.runtimeModel.releaseObjectGroup('completion'); - - if (!expressionString) { - var globalNames = await executionContext.globalLexicalScopeNames(); - // Merge lexical scope names with first completion group on global object: var a and let b should be in the same group. - if (completions.length) - completions[0].items = completions[0].items.concat(globalNames); - else - completions.push({items: globalNames, title: Common.UIString('Lexical scope variables')}); - } - return completions; + return this._receivedPropertyNames(completionGroups, dotNotation, bracketNotation, expressionString, query); /** - * @param {string=} type - * @return {!Object} - * @suppressReceiverCheck - * @this {Object} + * @this {ObjectUI.JavaScriptAutocomplete} + * @param {!SDK.RuntimeModel.EvaluationResult} result + * @return {!Promise<!Array<!ObjectUI.JavaScriptAutocomplete.CompletionGroup>>} */ - function getCompletions(type) { - var object; - if (type === 'string') - object = new String(''); - else if (type === 'number') - object = new Number(0); - else if (type === 'boolean') - object = new Boolean(false); - else - object = this; + async function completionsOnGlobal(result) { + if (result.error || !!result.exceptionDetails || !result.object) + return []; - var result = []; - try { - for (var o = object; o; o = Object.getPrototypeOf(o)) { - if ((type === 'array' || type === 'typedarray') && o === object && o.length > 9999) - continue; - - var group = {items: [], __proto__: null}; - try { - if (typeof o === 'object' && o.constructor && o.constructor.name) - group.title = o.constructor.name; - } catch (ee) { - // we could break upon cross origin check. - } - result[result.length] = group; - var names = Object.getOwnPropertyNames(o); - var isArray = Array.isArray(o); - for (var i = 0; i < names.length; ++i) { - // Skip array elements indexes. - if (isArray && /^[0-9]/.test(names[i])) - continue; - group.items[group.items.length] = names[i]; - } - } - } catch (e) { + var object = result.object; + while (object && object.type === 'object' && object.subtype === 'proxy') { + var properties = await object.getOwnPropertiesPromise(false /* generatePreview */); + var internalProperties = properties.internalProperties || []; + var target = internalProperties.find(property => property.name === '[[Target]]'); + object = target ? target.value : null; } + if (!object) + return []; + var completions = []; + if (object.type === 'object' || object.type === 'function') { + completions = + await object.callFunctionJSONPromise(getCompletions, [SDK.RemoteObject.toCallArgument(object.subtype)]) || + []; + } else if (object.type === 'string' || object.type === 'number' || object.type === 'boolean') { + var evaluateResult = await executionContext.evaluate( + { + expression: '(' + getCompletions + ')("' + object.type + '")', + objectGroup: 'completion', + includeCommandLineAPI: false, + silent: true, + returnByValue: true, + generatePreview: false + }, + /* userGesture */ false, + /* awaitPromise */ false); + if (evaluateResult.object && !evaluateResult.exceptionDetails) + completions = evaluateResult.object.value || []; + } + executionContext.runtimeModel.releaseObjectGroup('completion'); + + if (!expressionString) { + var globalNames = await executionContext.globalLexicalScopeNames(); + // Merge lexical scope names with first completion group on global object: var a and let b should be in the same group. + if (completions.length) + completions[0].items = completions[0].items.concat(globalNames); + else + completions.push({items: globalNames.sort(), title: Common.UIString('Lexical scope variables')}); + } + + for (var group of completions) { + for (var i = 0; i < group.items.length; i++) + group.items[i] = group.items[i].replace(/\n/g, '\\n'); + + group.items.sort(group.items.length < 1000 ? this._itemComparator : undefined); + } + + return completions; + + /** + * @param {string=} type + * @return {!Object} + * @suppressReceiverCheck + * @this {Object} + */ + function getCompletions(type) { + var object; + if (type === 'string') + object = new String(''); + else if (type === 'number') + object = new Number(0); + else if (type === 'boolean') + object = new Boolean(false); + else + object = this; + + var result = []; + try { + for (var o = object; o; o = Object.getPrototypeOf(o)) { + if ((type === 'array' || type === 'typedarray') && o === object && o.length > 9999) + continue; + + var group = {items: [], __proto__: null}; + try { + if (typeof o === 'object' && o.constructor && o.constructor.name) + group.title = o.constructor.name; + } catch (ee) { + // we could break upon cross origin check. + } + result[result.length] = group; + var names = Object.getOwnPropertyNames(o); + var isArray = Array.isArray(o); + for (var i = 0; i < names.length; ++i) { + // Skip array elements indexes. + if (isArray && /^[0-9]/.test(names[i])) + continue; + group.items[group.items.length] = names[i]; + } + } + } catch (e) { + } + return result; + } + } + + /** + * @param {!SDK.DebuggerModel.CallFrame} callFrame + * @return {!Promise<?Object>} + */ + async function completionsOnPause(callFrame) { + var result = [{items: ['this']}]; + var scopeChain = callFrame.scopeChain(); + var groupPromises = []; + for (var scope of scopeChain) { + groupPromises.push(scope.object() + .getAllPropertiesPromise(false /* accessorPropertiesOnly */, false /* generatePreview */) + .then(result => ({properties: result.properties, name: scope.name()}))); + } + var fullScopes = await Promise.all(groupPromises); + executionContext.runtimeModel.releaseObjectGroup('completion'); + for (var scope of fullScopes) + result.push({title: scope.name, items: scope.properties.map(property => property.name).sort()}); return result; } } /** - * @param {!SDK.DebuggerModel.CallFrame} callFrame - * @return {!Promise<!UI.SuggestBox.Suggestions>} - */ - async function completionsOnPause(callFrame) { - var result = [{items: ['this']}]; - var scopeChain = callFrame.scopeChain(); - var groupPromises = []; - for (var scope of scopeChain) { - groupPromises.push(scope.object() - .getAllPropertiesPromise(false /* accessorPropertiesOnly */, false /* generatePreview */) - .then(result => ({properties: result.properties, name: scope.name()}))); - } - var fullScopes = await Promise.all(groupPromises); - executionContext.runtimeModel.releaseObjectGroup('completion'); - for (var scope of fullScopes) - result.push({title: scope.name, items: scope.properties.map(property => property.name)}); - return receivedPropertyNames(result); - } - - /** - * @param {?Object} completions + * @param {?Array<!ObjectUI.JavaScriptAutocomplete.CompletionGroup>} propertyGroups + * @param {boolean} dotNotation + * @param {boolean} bracketNotation + * @param {string} expressionString + * @param {string} query * @return {!UI.SuggestBox.Suggestions} */ - function receivedPropertyNames(completions) { - if (!completions) + _receivedPropertyNames(propertyGroups, dotNotation, bracketNotation, expressionString, query) { + if (!propertyGroups) return []; - var propertyGroups = /** @type {!Array<!ObjectUI.JavaScriptAutocomplete.CompletionGroup>} */ (completions); var includeCommandLineAPI = (!dotNotation && !bracketNotation); if (includeCommandLineAPI) { const commandLineAPI = [ @@ -341,100 +376,98 @@ ]; propertyGroups.push({items: commandLineAPI}); } - return ObjectUI.JavaScriptAutocomplete._completionsForQuery( - dotNotation, bracketNotation, expressionString, query, propertyGroups); + return this._completionsForQuery(dotNotation, bracketNotation, expressionString, query, propertyGroups); } -}; - -/** - * @param {boolean} dotNotation - * @param {boolean} bracketNotation - * @param {string} expressionString - * @param {string} query - * @param {!Array<!ObjectUI.JavaScriptAutocomplete.CompletionGroup>} propertyGroups - * @return {!UI.SuggestBox.Suggestions} - */ -ObjectUI.JavaScriptAutocomplete._completionsForQuery = function( - dotNotation, bracketNotation, expressionString, query, propertyGroups) { - if (bracketNotation) { - if (query.length && query[0] === '\'') - var quoteUsed = '\''; - else - var quoteUsed = '"'; - } - - if (!expressionString) { - const keywords = [ - 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', 'else', 'finally', - 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', - 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with' - ]; - propertyGroups.push({title: Common.UIString('keywords'), items: keywords}); - } - - var result = []; - var lastGroupTitle; - for (var group of propertyGroups) { - group.items.sort(itemComparator.bind(null, group.items.length > 1000)); - var caseSensitivePrefix = []; - var caseInsensitivePrefix = []; - var caseSensitiveAnywhere = []; - var caseInsensitiveAnywhere = []; - - for (var property of group.items) { - // Assume that all non-ASCII characters are letters and thus can be used as part of identifier. - if (!bracketNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/.test(property)) - continue; - - if (bracketNotation) { - if (!/^[0-9]+$/.test(property)) - property = quoteUsed + property.escapeCharacters(quoteUsed + '\\') + quoteUsed; - property += ']'; - } - - if (property.length < query.length) - continue; - if (query.length && property.toLowerCase().indexOf(query.toLowerCase()) === -1) - continue; - // Substitute actual newlines with newline characters. @see crbug.com/498421 - var prop = property.split('\n').join('\\n'); - - if (property.startsWith(query)) - caseSensitivePrefix.push({text: prop, priority: 4}); - else if (property.toLowerCase().startsWith(query.toLowerCase())) - caseInsensitivePrefix.push({text: prop, priority: 3}); - else if (property.indexOf(query) !== -1) - caseSensitiveAnywhere.push({text: prop, priority: 2}); - else - caseInsensitiveAnywhere.push({text: prop, priority: 1}); - } - var structuredGroup = - caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere); - if (structuredGroup.length && group.title !== lastGroupTitle) { - structuredGroup[0].subtitle = group.title; - lastGroupTitle = group.title; - } - result = result.concat(structuredGroup); - result.forEach(item => { - if (item.text.endsWith(']')) - item.title = item.text.substring(0, item.text.length - 1); - }); - } - return result; /** - * @param {boolean} naturalOrder + * @param {boolean} dotNotation + * @param {boolean} bracketNotation + * @param {string} expressionString + * @param {string} query + * @param {!Array<!ObjectUI.JavaScriptAutocomplete.CompletionGroup>} propertyGroups + * @return {!UI.SuggestBox.Suggestions} + */ + _completionsForQuery(dotNotation, bracketNotation, expressionString, query, propertyGroups) { + var quoteUsed = (bracketNotation && query.startsWith('\'')) ? '\'' : '"'; + + if (!expressionString) { + const keywords = [ + 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', 'else', 'finally', + 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', + 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with' + ]; + propertyGroups.push({title: Common.UIString('keywords'), items: keywords}); + } + + var result = []; + var lastGroupTitle; + var regex = /^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/; + var lowerCaseQuery = query.toLowerCase(); + for (var group of propertyGroups) { + var caseSensitivePrefix = []; + var caseInsensitivePrefix = []; + var caseSensitiveAnywhere = []; + var caseInsensitiveAnywhere = []; + + for (var i = 0; i < group.items.length; i++) { + var property = group.items[i]; + // Assume that all non-ASCII characters are letters and thus can be used as part of identifier. + if (!bracketNotation && !regex.test(property)) + continue; + + if (bracketNotation) { + if (!/^[0-9]+$/.test(property)) + property = quoteUsed + property.escapeCharacters(quoteUsed + '\\') + quoteUsed; + property += ']'; + } + + if (property.length < query.length) + continue; + var lowerCaseProperty = property.toLowerCase(); + if (query.length && lowerCaseProperty.indexOf(lowerCaseQuery) === -1) + continue; + + if (property.startsWith(query)) + caseSensitivePrefix.push({text: property, priority: 4}); + else if (lowerCaseProperty.startsWith(lowerCaseQuery)) + caseInsensitivePrefix.push({text: property, priority: 3}); + else if (property.indexOf(query) !== -1) + caseSensitiveAnywhere.push({text: property, priority: 2}); + else + caseInsensitiveAnywhere.push({text: property, priority: 1}); + } + var structuredGroup = + caseSensitivePrefix.concat(caseInsensitivePrefix, caseSensitiveAnywhere, caseInsensitiveAnywhere); + if (structuredGroup.length && group.title !== lastGroupTitle) { + structuredGroup[0].subtitle = group.title; + lastGroupTitle = group.title; + } + result = result.concat(structuredGroup); + result.forEach(item => { + if (item.text.endsWith(']')) + item.title = item.text.substring(0, item.text.length - 1); + }); + } + return result; + } + + /** * @param {string} a * @param {string} b * @return {number} */ - function itemComparator(naturalOrder, a, b) { + _itemComparator(a, b) { var aStartsWithUnderscore = a.startsWith('_'); var bStartsWithUnderscore = b.startsWith('_'); if (aStartsWithUnderscore && !bStartsWithUnderscore) return 1; if (bStartsWithUnderscore && !aStartsWithUnderscore) return -1; - return naturalOrder ? String.naturalOrderComparator(a, b) : a.localeCompare(b); + return String.naturalOrderComparator(a, b); } }; + +/** @typedef {{title:(string|undefined), items:Array<string>}} */ +ObjectUI.JavaScriptAutocomplete.CompletionGroup; + +ObjectUI.javaScriptAutocomplete = new ObjectUI.JavaScriptAutocomplete(); \ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js b/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js index f12b43cc..a3ab3f6e 100644 --- a/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js +++ b/third_party/WebKit/Source/devtools/front_end/object_ui/ObjectPropertiesSection.js
@@ -1267,7 +1267,7 @@ ObjectUI.ObjectPropertyPrompt = class extends UI.TextPrompt { constructor() { super(); - this.initialize(ObjectUI.JavaScriptAutocomplete.completionsForTextInCurrentContext); + this.initialize(ObjectUI.javaScriptAutocomplete.completionsForTextInCurrentContext); } };
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp index b3670ffb..ad507f6 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -28,6 +28,8 @@ #include "modules/accessibility/AXNodeObject.h" +#include <math.h> + #include "core/dom/AccessibleNode.h" #include "core/dom/Element.h" #include "core/dom/FlatTreeTraversal.h" @@ -93,8 +95,8 @@ DCHECK(!node_); } -void AXNodeObject::AlterSliderValue(bool increase) { - if (RoleValue() != kSliderRole) +void AXNodeObject::AlterSliderOrSpinButtonValue(bool increase) { + if (!IsSlider() && !IsSpinButton()) return; float value; @@ -957,12 +959,22 @@ return RoleValue() == kSliderRole; } +bool AXNodeObject::IsSpinButton() const { + return RoleValue() == kSpinButtonRole; +} + bool AXNodeObject::IsNativeSlider() const { if (auto* input = ToHTMLInputElementOrNull(GetNode())) return input->type() == InputTypeNames::range; return false; } +bool AXNodeObject::IsNativeSpinButton() const { + if (auto* input = ToHTMLInputElementOrNull(GetNode())) + return input->type() == InputTypeNames::number; + return false; +} + bool AXNodeObject::IsMoveableSplitter() const { return RoleValue() == kSplitterRole && CanSetFocusAttribute(); } @@ -1545,7 +1557,7 @@ if (IsNativeSlider() || IsNativeSpinButton()) { *out_value = ToHTMLInputElement(*GetNode()).valueAsNumber(); - return true; + return isfinite(*out_value); } if (auto* meter = ToHTMLMeterElementOrNull(GetNode())) { @@ -1588,9 +1600,9 @@ return true; } - if (IsNativeSlider()) { - *out_value = ToHTMLInputElement(*GetNode()).Maximum(); - return true; + if (IsNativeSlider() || IsNativeSpinButton()) { + *out_value = static_cast<float>(ToHTMLInputElement(*GetNode()).Maximum()); + return isfinite(*out_value); } if (auto* meter = ToHTMLMeterElementOrNull(GetNode())) { @@ -1621,9 +1633,9 @@ return true; } - if (IsNativeSlider()) { - *out_value = ToHTMLInputElement(*GetNode()).Minimum(); - return true; + if (IsNativeSlider() || IsNativeSpinButton()) { + *out_value = static_cast<float>(ToHTMLInputElement(*GetNode()).Minimum()); + return isfinite(*out_value); } if (auto* meter = ToHTMLMeterElementOrNull(GetNode())) { @@ -1648,11 +1660,11 @@ } bool AXNodeObject::StepValueForRange(float* out_value) const { - if (IsNativeSlider()) { + if (IsNativeSlider() || IsNativeSpinButton()) { Decimal step = ToHTMLInputElement(*GetNode()).CreateStepRange(kRejectAny).Step(); *out_value = step.ToString().ToFloat(); - return true; + return isfinite(*out_value); } switch (AriaRoleAttribute()) { @@ -2342,7 +2354,7 @@ LocalFrame* frame = GetDocument() ? GetDocument()->GetFrame() : nullptr; std::unique_ptr<UserGestureIndicator> gesture_indicator = Frame::NotifyUserActivation(frame, UserGestureToken::kNewGesture); - AlterSliderValue(true); + AlterSliderOrSpinButtonValue(true); return true; } @@ -2350,7 +2362,7 @@ LocalFrame* frame = GetDocument() ? GetDocument()->GetFrame() : nullptr; std::unique_ptr<UserGestureIndicator> gesture_indicator = Frame::NotifyUserActivation(frame, UserGestureToken::kNewGesture); - AlterSliderValue(false); + AlterSliderOrSpinButtonValue(false); return true; }
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h index 3175167..6a900f8 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h +++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
@@ -61,7 +61,7 @@ virtual AccessibilityRole NativeAccessibilityRoleIgnoringAria() const; String AccessibilityDescriptionForElements( HeapVector<Member<Element>>& elements) const; - void AlterSliderValue(bool increase); + void AlterSliderOrSpinButtonValue(bool increase); AXObject* ActiveDescendant() override; String AriaAccessibilityDescription() const; String AriaAutoComplete() const; @@ -116,7 +116,9 @@ bool IsProgressIndicator() const override; bool IsRichlyEditable() const override; bool IsSlider() const override; + bool IsSpinButton() const override; bool IsNativeSlider() const override; + bool IsNativeSpinButton() const override; bool IsMoveableSplitter() const override; // Check object state.
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp index 7db31d0..c8d1813 100644 --- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp +++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -47,6 +47,12 @@ namespace { +// Threshold for rejecting stored magic window poses as being too old. +// If it's exceeded, defer magic window rAF callback execution until +// a fresh pose is received. +constexpr WTF::TimeDelta kMagicWindowPoseAgeThreshold = + WTF::TimeDelta::FromMilliseconds(250); + VREye StringToVREye(const String& which_eye) { if (which_eye == "left") return kVREyeLeft; @@ -214,6 +220,7 @@ if (!is_presenting_) { if (pending_magic_window_vsync_) return; + magic_window_vsync_waiting_for_pose_.Reset(); magic_window_provider_->GetPose( WTF::Bind(&VRDisplay::OnMagicWindowPose, WrapWeakPersistent(this))); pending_magic_window_vsync_ = true; @@ -1042,15 +1049,31 @@ if (is_presenting_) return; vr_frame_id_ = -1; - ProcessScheduledAnimations(timestamp); + WTF::TimeDelta pose_age = WTF::CurrentTimeTicks() - magic_window_pose_time_; + if (pose_age < kMagicWindowPoseAgeThreshold) { + ProcessScheduledAnimations(timestamp); + } else { + // The VSync got triggered before ever getting a pose, or the pose is + // stale. Defer the animation until a pose arrives to avoid passing null + // poses to the application. + magic_window_vsync_waiting_for_pose_ = + WTF::Bind(&VRDisplay::ProcessScheduledAnimations, + WrapWeakPersistent(this), timestamp); + } } void VRDisplay::OnMagicWindowPose(device::mojom::blink::VRPosePtr pose) { + magic_window_pose_time_ = WTF::CurrentTimeTicks(); if (!in_animation_frame_) { frame_pose_ = std::move(pose); } else { pending_pose_ = std::move(pose); } + if (magic_window_vsync_waiting_for_pose_) { + // We have a vsync waiting for a pose, run it now. + std::move(magic_window_vsync_waiting_for_pose_).Run(); + magic_window_vsync_waiting_for_pose_.Reset(); + } } void VRDisplay::OnPresentationProviderConnectionError() {
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.h b/third_party/WebKit/Source/modules/vr/VRDisplay.h index 39d3f86..28d0d67 100644 --- a/third_party/WebKit/Source/modules/vr/VRDisplay.h +++ b/third_party/WebKit/Source/modules/vr/VRDisplay.h
@@ -16,6 +16,7 @@ #include "platform/Timer.h" #include "platform/heap/Handle.h" #include "platform/wtf/Forward.h" +#include "platform/wtf/Functional.h" #include "platform/wtf/text/WTFString.h" #include "public/platform/WebGraphicsContext3DProvider.h" @@ -246,6 +247,8 @@ bool pending_presenting_vsync_ = false; bool pending_magic_window_vsync_ = false; int pending_magic_window_vsync_id_ = -1; + base::OnceClosure magic_window_vsync_waiting_for_pose_; + WTF::TimeTicks magic_window_pose_time_; bool in_animation_frame_ = false; bool did_submit_this_frame_ = false; bool display_blurred_ = false;
diff --git a/third_party/WebKit/Source/platform/graphics/CanvasResource.cpp b/third_party/WebKit/Source/platform/graphics/CanvasResource.cpp index 98e1d9e..a33dc2cc 100644 --- a/third_party/WebKit/Source/platform/graphics/CanvasResource.cpp +++ b/third_party/WebKit/Source/platform/graphics/CanvasResource.cpp
@@ -272,6 +272,9 @@ if (gpu_mailbox_.IsZero() && gl) { gl->GenMailboxCHROMIUM(gpu_mailbox_.name); gl->ProduceTextureDirectCHROMIUM(texture_id_, gpu_mailbox_.name); + } + if (mailbox_needs_new_sync_token_) { + mailbox_needs_new_sync_token_ = false; gl->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData()); } return gpu_mailbox_; @@ -295,6 +298,7 @@ source_texture, 0 /*sourceLevel*/, TextureTarget(), texture_id_, 0 /*destLevel*/, format, type, false /*unpackFlipY*/, false /*unpackPremultiplyAlpha*/, false /*unpackUnmultiplyAlpha*/); + mailbox_needs_new_sync_token_ = true; } base::WeakPtr<WebGraphicsContext3DProviderWrapper>
diff --git a/third_party/WebKit/Source/platform/graphics/CanvasResource.h b/third_party/WebKit/Source/platform/graphics/CanvasResource.h index 64963f7..668b2c1 100644 --- a/third_party/WebKit/Source/platform/graphics/CanvasResource.h +++ b/third_party/WebKit/Source/platform/graphics/CanvasResource.h
@@ -145,6 +145,7 @@ gpu::Mailbox gpu_mailbox_; gpu::SyncToken sync_token_; + bool mailbox_needs_new_sync_token_ = true; base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_; std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_; GLuint image_id_ = 0;
diff --git a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 b/third_party/WebKit/Source/platform/runtime_enabled_features.json5 index b23d6796..4284f35 100644 --- a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 +++ b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
@@ -334,7 +334,7 @@ }, { name: "DisplayNoneIFrameCreatesNoLayoutObject", - status: "stable", + status: "experimental", }, { name: "DocumentCookie",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index f95e23c..3a2515d 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -2874,6 +2874,7 @@ <int value="189" label="RFH_INTERFACE_PROVIDER_SUPERFLUOUS"/> <int value="190" label="AIRH_UNEXPECTED_BITSTREAM"/> <int value="191" label="ARH_UNEXPECTED_BITSTREAM"/> + <int value="192" label="RDH_NULL_CLIENT"/> </enum> <enum name="BadMessageReasonExtensions"> @@ -14274,6 +14275,7 @@ <int value="1206" label="DEVELOPERPRIVATE_NOTIFYDRAGINSTALLINPROGRESS"/> <int value="1207" label="AUTOTESTPRIVATE_GETPRINTERLIST"/> <int value="1208" label="DEVELOPERPRIVATE_GETEXTENSIONSIZE"/> + <int value="1209" label="CRYPTOTOKENPRIVATE_ISAPPIDHASHINENTERPRISECONTEXT"/> </enum> <enum name="ExtensionIconState">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index c96114f..10623c5 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -33366,6 +33366,10 @@ <histogram name="Media.ChromeArcVideoDecodeAccelerator.InitializeResult" enum="ArcVideoDecodeAcceleratorResult"> + <obsolete> + Deprecated as of Dec 18, 2017. Replaced by + Media.GpuArcVideoDecodeAccelerator.InitializeResult. + </obsolete> <owner>johnylin@chromium.org</owner> <summary> Counts of status values returned from calls to
diff --git a/ui/message_center/notification_delegate.cc b/ui/message_center/notification_delegate.cc index e07e48b..27cc820 100644 --- a/ui/message_center/notification_delegate.cc +++ b/ui/message_center/notification_delegate.cc
@@ -29,12 +29,13 @@ // HandleNotificationClickDelegate: HandleNotificationClickDelegate::HandleNotificationClickDelegate( - const base::Closure& callback) { + const base::RepeatingClosure& callback) { if (!callback.is_null()) { // Create a callback that consumes and ignores the button index parameter, // and just runs the provided closure. callback_ = base::Bind( - [](const base::Closure& closure, base::Optional<int> button_index) { + [](const base::RepeatingClosure& closure, + base::Optional<int> button_index) { DCHECK(!button_index); closure.Run(); },
diff --git a/ui/message_center/notification_delegate.h b/ui/message_center/notification_delegate.h index 399e237..9628854 100644 --- a/ui/message_center/notification_delegate.h +++ b/ui/message_center/notification_delegate.h
@@ -60,14 +60,16 @@ public: // The parameter is the index of the button that was clicked, or nullopt if // the body was clicked. - using ButtonClickCallback = base::Callback<void(base::Optional<int>)>; + using ButtonClickCallback = + base::RepeatingCallback<void(base::Optional<int>)>; // Creates a delegate that handles clicks on a button or on the body. explicit HandleNotificationClickDelegate(const ButtonClickCallback& callback); // Creates a delegate that only handles clicks on the body of the // notification. - explicit HandleNotificationClickDelegate(const base::Closure& closure); + explicit HandleNotificationClickDelegate( + const base::RepeatingClosure& closure); // message_center::NotificationDelegate overrides: void Click() override;