|
|
Chromium Code Reviews|
Created:
3 years, 9 months ago by Julia Tuttle Modified:
3 years, 8 months ago CC:
cbentzel+watch_chromium.org, chromium-reviews, net-reviews_chromium.org Target Ref:
refs/heads/master Project:
chromium Visibility:
Public. |
DescriptionReporting: Implement delivery agent.
Reporting is a spec for delivering out-of-band reports from various
other parts of the browser. See http://wicg.github.io/reporting/ for
the spec, or https://goo.gl/pygX5I for details of the planned
implementation in Chromium.
The delivery agent takes the queued reports in the cache, figures out
(with the help of the EndpointManager) where to deliver them, and sends
the serialized reports using the Uploader.
BUG=704259
Review-Url: https://codereview.chromium.org/2739483002
Cr-Commit-Position: refs/heads/master@{#462584}
Committed: https://chromium.googlesource.com/chromium/src/+/b54a1a8b7fe040cdf0d5cd6799cb0cb0a4bd8ddb
Patch Set 1 #Patch Set 2 : Add unittests. #Patch Set 3 : DISALLOW_COPY_AND_ASSIGN #Patch Set 4 : rebase, &c. #
Total comments: 1
Patch Set 5 : Make requested changes. #
Total comments: 10
Patch Set 6 : Make requested changes. #
Total comments: 12
Patch Set 7 : rebase #Patch Set 8 : Make requested changes. #
Total comments: 8
Patch Set 9 : Make requested changes. #
Messages
Total messages: 45 (32 generated)
juliatuttle@chromium.org changed reviewers: + shivanisha@chromium.org
PTAL, shivanisha.
Description was changed from ========== Reporting: Create DeliveryAgent. ========== to ========== Reporting: Create DeliveryAgent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/. The DeliveryAgent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. ==========
The CQ bit was checked by juliatuttle@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
The CQ bit was checked by juliatuttle@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
Description was changed from ========== Reporting: Create DeliveryAgent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/. The DeliveryAgent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. ========== to ========== Reporting: Create delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/. The DeliveryAgent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. ==========
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
The CQ bit was checked by juliatuttle@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-device-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-device-xcode-...) ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...) ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
Description was changed from ========== Reporting: Create delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/. The DeliveryAgent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. ========== to ========== Reporting: Implement delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/ for the spec, or https://goo.gl/pygX5I for details of the planned implementation in Chromium. The delivery agent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. ==========
Description was changed from ========== Reporting: Implement delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/ for the spec, or https://goo.gl/pygX5I for details of the planned implementation in Chromium. The delivery agent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. ========== to ========== Reporting: Implement delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/ for the spec, or https://goo.gl/pygX5I for details of the planned implementation in Chromium. The delivery agent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. BUG=704259 ==========
https://codereview.chromium.org/2739483002/diff/60001/net/reporting/reporting... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/60001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:58: const BackoffEntry::Policy* endpoint_backoff_policy); Document lifetime assumptions for clock, cache, uploader and endpoint_backoff_policy since raw pointers to those will be stored in this object. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... File net/reporting/reporting_delivery_agent.cc (right): https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.cc:81: // Sort reports into (origin, group) buckets. Since we want to key the reports with <origin, group> pair, how about using the pair as the key. That should help in removing the nested loops in using this map below. std::map < OriginGroup, std::vector<const ReportingReport*> > https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.cc:93: std::map<GURL, std::vector<const ReportingReport*>> endpoint_reports; Since its not allowed for multiple reports for the same endpoint to be sent at the same time, why do we have a vector of reports here instead of a single report? https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:58: const BackoffEntry::Policy* endpoint_backoff_policy); Document the lifetime assumptions of clock, cache, uploader and endpoint_backoff_policy. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:79: // Would be an unordered_set, but there's no hash on pair. new line before the comment. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:80: std::set<OriginGroup> pending_origin_groups_; A comment here on how pending_origin_groups_ would be used.
PTAL, shivanisha. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... File net/reporting/reporting_delivery_agent.cc (right): https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.cc:81: // Sort reports into (origin, group) buckets. On 2017/03/30 20:16:11, shivanisha wrote: > Since we want to key the reports with <origin, group> pair, how about using the > pair as the key. That should help in removing the nested loops in using this map > below. > std::map < OriginGroup, std::vector<const ReportingReport*> > Good call -- I wrote this before I added pending_origin_groups_, which is when I added OriginGroup. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.cc:93: std::map<GURL, std::vector<const ReportingReport*>> endpoint_reports; On 2017/03/30 20:16:11, shivanisha wrote: > Since its not allowed for multiple reports for the same endpoint to be sent at > the same time, why do we have a vector of reports here instead of a single > report? What is disallowed is multiple *uploads*, not multiple *reports*. An upload can contain an infinite number of reports. (Actually, that might be something we want to cap at some point.) I've added a note below the concurrency explanation to clarify that an upload can contain multiple reports, in case anyone else has this question. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:58: const BackoffEntry::Policy* endpoint_backoff_policy); On 2017/03/30 20:16:11, shivanisha wrote: > Document the lifetime assumptions of clock, cache, uploader and > endpoint_backoff_policy. Done. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:79: // Would be an unordered_set, but there's no hash on pair. On 2017/03/30 20:16:11, shivanisha wrote: > new line before the comment. Done. https://codereview.chromium.org/2739483002/diff/80001/net/reporting/reporting... net/reporting/reporting_delivery_agent.h:80: std::set<OriginGroup> pending_origin_groups_; On 2017/03/30 20:16:11, shivanisha wrote: > A comment here on how pending_origin_groups_ would be used. Done.
The CQ bit was checked by juliatuttle@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.cc (right): https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent.cc:39: report_value->Set("report", report->body->DeepCopy()); Does "group" need to be set as well? May be a add a comment in ReportingReport class header mentioning the fields that would be sent on wire? https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent.cc:96: if (base::ContainsKey(pending_origin_groups_, origin_group)) nit: To me it seems more consistent to use std:: methods on std:: collections, any particular reason to use this instead? https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:56: // (Note that a single delivery can contain an infinite number of reports.) include a TODO for putting a cap on the number of reports? https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... File net/reporting/reporting_delivery_agent_unittest.cc (right): https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent_unittest.cc:244: cache_.GetReports(&reports); IsReportDoomedForTesting() should return true. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent_unittest.cc:249: cache_.GetReports(&reports); IsReportDoomedForTesting() should return false here. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent_unittest.cc:348: TEST_F(ReportingDeliveryAgentTest, ParallelizeUploadsAcrossGroups) { The comments in this and previous tests are same?
The CQ bit was checked by juliatuttle@chromium.org to run a CQ dry run
PTAL, shivanisha. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.cc (right): https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent.cc:39: report_value->Set("report", report->body->DeepCopy()); On 2017/03/31 18:58:51, shivanisha wrote: > Does "group" need to be set as well? May be a add a comment in ReportingReport > class header mentioning the fields that would be sent on wire? No, group just determines which endpoints the reports can be sent to. I added comments to ReportingReport. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent.cc:96: if (base::ContainsKey(pending_origin_groups_, origin_group)) On 2017/03/31 18:58:51, shivanisha wrote: > nit: To me it seems more consistent to use std:: methods on std:: collections, > any particular reason to use this instead? jkarlin suggested "base::ContainsKey(collection, key)" over "collection.count(key) > 0u" in a previous CL, and I think it's clearer too. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:56: // (Note that a single delivery can contain an infinite number of reports.) On 2017/03/31 18:58:51, shivanisha wrote: > include a TODO for putting a cap on the number of reports? Done. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... File net/reporting/reporting_delivery_agent_unittest.cc (right): https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent_unittest.cc:244: cache_.GetReports(&reports); On 2017/03/31 18:58:52, shivanisha wrote: > IsReportDoomedForTesting() should return true. Done. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent_unittest.cc:249: cache_.GetReports(&reports); On 2017/03/31 18:58:52, shivanisha wrote: > IsReportDoomedForTesting() should return false here. Done. https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... net/reporting/reporting_delivery_agent_unittest.cc:348: TEST_F(ReportingDeliveryAgentTest, ParallelizeUploadsAcrossGroups) { On 2017/03/31 18:58:52, shivanisha wrote: > The comments in this and previous tests are same? Oops! The tests also didn't do what they were supposed to. I've fixed both the code and the comments.
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
On 2017/04/04 at 18:09:32, juliatuttle wrote: > PTAL, shivanisha. > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > File net/reporting/reporting_delivery_agent.cc (right): > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > net/reporting/reporting_delivery_agent.cc:39: report_value->Set("report", report->body->DeepCopy()); > On 2017/03/31 18:58:51, shivanisha wrote: > > Does "group" need to be set as well? May be a add a comment in ReportingReport > > class header mentioning the fields that would be sent on wire? > > No, group just determines which endpoints the reports can be sent to. > > I added comments to ReportingReport. > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > net/reporting/reporting_delivery_agent.cc:96: if (base::ContainsKey(pending_origin_groups_, origin_group)) > On 2017/03/31 18:58:51, shivanisha wrote: > > nit: To me it seems more consistent to use std:: methods on std:: collections, > > any particular reason to use this instead? > > jkarlin suggested "base::ContainsKey(collection, key)" over "collection.count(key) > 0u" in a previous CL, and I think it's clearer too. > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > File net/reporting/reporting_delivery_agent.h (right): > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > net/reporting/reporting_delivery_agent.h:56: // (Note that a single delivery can contain an infinite number of reports.) > On 2017/03/31 18:58:51, shivanisha wrote: > > include a TODO for putting a cap on the number of reports? > > Done. > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > File net/reporting/reporting_delivery_agent_unittest.cc (right): > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > net/reporting/reporting_delivery_agent_unittest.cc:244: cache_.GetReports(&reports); > On 2017/03/31 18:58:52, shivanisha wrote: > > IsReportDoomedForTesting() should return true. > > Done. > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > net/reporting/reporting_delivery_agent_unittest.cc:249: cache_.GetReports(&reports); > On 2017/03/31 18:58:52, shivanisha wrote: > > IsReportDoomedForTesting() should return false here. > > Done. > > https://codereview.chromium.org/2739483002/diff/100001/net/reporting/reportin... > net/reporting/reporting_delivery_agent_unittest.cc:348: TEST_F(ReportingDeliveryAgentTest, ParallelizeUploadsAcrossGroups) { > On 2017/03/31 18:58:52, shivanisha wrote: > > The comments in this and previous tests are same? > > Oops! The tests also didn't do what they were supposed to. > > I've fixed both the code and the comments. lgtm
juliatuttle@chromium.org changed reviewers: + rdsmith@chromium.org
Thanks shivanisha! PTAL, rdsmith for committer review.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
LGTM with suggestions. https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.cc (right): https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.cc:48: return reports_json; nit, suggestion: Take an out pointer argument and construct the string there so you don't have an extra copy constructor on a particularly long string at this point. https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:44: // endpoint, it won't be chosen. This may mean that the new reports are not The antecedent of "it" isn't clear to me in this sentence. Does it refer to the reports that could be delivered? (This is a request for a comment clarification.) https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:73: void SendReports(); Why is this controlled by a consumer rather than being on a timer or triggered by the cache notifying the agent of changes? I'm not certain that would be a better design, but the problem of this type of salami CL approach is that it's hard to evaluate, so I figured I'd ask. https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:90: // (Would be an unordered_set, but there's no hash on pair.) Suggestion: I'd erase this last sentence. It invites a bikeshed, which starts with what I was about to write, which is "You could totally define your own hash", to which the right response is "This is totally not a design choice that matters in any way shape or form", which would then be responded to with "So is the parenthetical note worth the comment space?" :-}
Thanks, rdsmith! https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.cc (right): https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.cc:48: return reports_json; On 2017/04/05 19:02:38, Randy Smith (Not in Mondays) wrote: > nit, suggestion: Take an out pointer argument and construct the string there so > you don't have an extra copy constructor on a particularly long string at this > point. Done. https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... File net/reporting/reporting_delivery_agent.h (right): https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:44: // endpoint, it won't be chosen. This may mean that the new reports are not On 2017/04/05 19:02:38, Randy Smith (Not in Mondays) wrote: > The antecedent of "it" isn't clear to me in this sentence. Does it refer to the > reports that could be delivered? (This is a request for a comment > clarification.) Done. https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:73: void SendReports(); On 2017/04/05 19:02:38, Randy Smith (Not in Mondays) wrote: > Why is this controlled by a consumer rather than being on a timer or triggered > by the cache notifying the agent of changes? I'm not certain that would be a > better design, but the problem of this type of salami CL approach is that it's > hard to evaluate, so I figured I'd ask. It'll be doing exactly that eventually, but I wanted to land the basic delivery code first. https://codereview.chromium.org/2739483002/diff/140001/net/reporting/reportin... net/reporting/reporting_delivery_agent.h:90: // (Would be an unordered_set, but there's no hash on pair.) On 2017/04/05 19:02:38, Randy Smith (Not in Mondays) wrote: > Suggestion: I'd erase this last sentence. It invites a bikeshed, which starts > with what I was about to write, which is "You could totally define your own > hash", to which the right response is "This is totally not a design choice that > matters in any way shape or form", which would then be responded to with "So is > the parenthetical note worth the comment space?" :-} The parenthetical note is there because otherwise Shivani (and presumably others) will wonder why I didn't use unordered_set. Adding a hash is Not My Problem, but using the best data structure available in my code is.
The CQ bit was checked by juliatuttle@chromium.org
The patchset sent to the CQ was uploaded after l-g-t-m from shivanisha@chromium.org, rdsmith@chromium.org Link to the patchset: https://codereview.chromium.org/2739483002/#ps160001 (title: "Make requested changes.")
CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
The CQ bit was checked by juliatuttle@chromium.org
CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
CQ is committing da patch.
Bot data: {"patchset_id": 160001, "attempt_start_ts": 1491501729752570,
"parent_rev": "62b2a22982eda9ca564624fed6b24d41cfa4b844", "commit_rev":
"b54a1a8b7fe040cdf0d5cd6799cb0cb0a4bd8ddb"}
Message was sent while issue was closed.
Description was changed from ========== Reporting: Implement delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/ for the spec, or https://goo.gl/pygX5I for details of the planned implementation in Chromium. The delivery agent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. BUG=704259 ========== to ========== Reporting: Implement delivery agent. Reporting is a spec for delivering out-of-band reports from various other parts of the browser. See http://wicg.github.io/reporting/ for the spec, or https://goo.gl/pygX5I for details of the planned implementation in Chromium. The delivery agent takes the queued reports in the cache, figures out (with the help of the EndpointManager) where to deliver them, and sends the serialized reports using the Uploader. BUG=704259 Review-Url: https://codereview.chromium.org/2739483002 Cr-Commit-Position: refs/heads/master@{#462584} Committed: https://chromium.googlesource.com/chromium/src/+/b54a1a8b7fe040cdf0d5cd6799cb... ==========
Message was sent while issue was closed.
Committed patchset #9 (id:160001) as https://chromium.googlesource.com/chromium/src/+/b54a1a8b7fe040cdf0d5cd6799cb... |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
