summaryrefslogtreecommitdiff
path: root/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
blob: bc4c93b79ba5887dfa6e15c7814bc61fefb8c407 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// Copyright 2020 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/webdata/autofill_wallet_offer_sync_bridge.h"

#include <utility>

#include "base/callback_helpers.h"
#include "base/logging.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/sync/base/hash_util.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
#include "components/sync/model_impl/sync_metadata_store_change_list.h"

namespace autofill {

namespace {

// Address to this variable used as the user data key.
static int kAutofillWalletOfferSyncBridgeUserDataKey = 0;

std::string GetClientTagFromSpecifics(
    const sync_pb::AutofillOfferSpecifics& specifics) {
  return syncer::GetUnhashedClientTagFromAutofillOfferSpecifics(specifics);
}

std::string GetStorageKeyFromSpecifics(
    const sync_pb::AutofillOfferSpecifics& specifics) {
  // Use client tag as the storage key.
  return GetClientTagFromSpecifics(specifics);
}

}  // namespace

// static
void AutofillWalletOfferSyncBridge::CreateForWebDataServiceAndBackend(
    AutofillWebDataBackend* web_data_backend,
    AutofillWebDataService* web_data_service) {
  web_data_service->GetDBUserData()->SetUserData(
      &kAutofillWalletOfferSyncBridgeUserDataKey,
      std::make_unique<AutofillWalletOfferSyncBridge>(
          std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
              syncer::AUTOFILL_WALLET_OFFER,
              /*dump_stack=*/base::DoNothing()),
          web_data_backend));
}

// static
syncer::ModelTypeSyncBridge* AutofillWalletOfferSyncBridge::FromWebDataService(
    AutofillWebDataService* web_data_service) {
  return static_cast<AutofillWalletOfferSyncBridge*>(
      web_data_service->GetDBUserData()->GetUserData(
          &kAutofillWalletOfferSyncBridgeUserDataKey));
}

AutofillWalletOfferSyncBridge::AutofillWalletOfferSyncBridge(
    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
    AutofillWebDataBackend* web_data_backend)
    : ModelTypeSyncBridge(std::move(change_processor)),
      web_data_backend_(web_data_backend) {
  DCHECK(web_data_backend_);

  LoadAutofillOfferMetadata();
}

AutofillWalletOfferSyncBridge::~AutofillWalletOfferSyncBridge() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

std::unique_ptr<syncer::MetadataChangeList>
AutofillWalletOfferSyncBridge::CreateMetadataChangeList() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return std::make_unique<syncer::SyncMetadataStoreChangeList>(
      GetAutofillTable(), syncer::AUTOFILL_WALLET_OFFER);
}

base::Optional<syncer::ModelError> AutofillWalletOfferSyncBridge::MergeSyncData(
    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
    syncer::EntityChangeList entity_data) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // All metadata changes have been already written, return early for an error.
  base::Optional<syncer::ModelError> error =
      static_cast<syncer::SyncMetadataStoreChangeList*>(
          metadata_change_list.get())
          ->TakeError();
  if (error) {
    return error;
  }

  MergeRemoteData(std::move(entity_data));
  return base::nullopt;
}

base::Optional<syncer::ModelError>
AutofillWalletOfferSyncBridge::ApplySyncChanges(
    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
    syncer::EntityChangeList entity_data) {
  // This bridge does not support incremental updates, so whenever this is
  // called, the change list should be empty.
  DCHECK(entity_data.empty()) << "Received an unsupported incremental update.";
  return base::nullopt;
}

void AutofillWalletOfferSyncBridge::GetData(StorageKeyList storage_keys,
                                            DataCallback callback) {}

void AutofillWalletOfferSyncBridge::GetAllDataForDebugging(
    DataCallback callback) {
  GetAllDataImpl(std::move(callback));
}

std::string AutofillWalletOfferSyncBridge::GetClientTag(
    const syncer::EntityData& entity_data) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(entity_data.specifics.has_autofill_offer());
  return GetClientTagFromSpecifics(entity_data.specifics.autofill_offer());
}

std::string AutofillWalletOfferSyncBridge::GetStorageKey(
    const syncer::EntityData& entity_data) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(entity_data.specifics.has_autofill_offer());
  return GetStorageKeyFromSpecifics(entity_data.specifics.autofill_offer());
}

bool AutofillWalletOfferSyncBridge::SupportsIncrementalUpdates() const {
  return false;
}

void AutofillWalletOfferSyncBridge::ApplyStopSyncChanges(
    std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) {
  // If a metadata change list gets passed in, that means sync is actually
  // disabled, so we want to delete the payments data.
  if (delete_metadata_change_list) {
    MergeRemoteData(syncer::EntityChangeList());
  }
}

void AutofillWalletOfferSyncBridge::GetAllDataImpl(DataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  std::vector<std::unique_ptr<AutofillOfferData>> offers;
  if (!GetAutofillTable()->GetCreditCardOffers(&offers)) {
    change_processor()->ReportError(
        {FROM_HERE, "Failed to load offer data from table."});
    return;
  }

  auto batch = std::make_unique<syncer::MutableDataBatch>();
  for (const std::unique_ptr<AutofillOfferData>& offer : offers) {
    auto entity_data = std::make_unique<syncer::EntityData>();
    sync_pb::AutofillOfferSpecifics* offer_specifics =
        entity_data->specifics.mutable_autofill_offer();
    SetAutofillOfferSpecificsFromOfferData(*offer, offer_specifics);

    entity_data->name =
        "Offer " +
        GetBase64EncodedId(GetClientTagFromSpecifics(*offer_specifics));

    batch->Put(GetStorageKeyFromSpecifics(*offer_specifics),
               std::move(entity_data));
  }
  std::move(callback).Run(std::move(batch));
}

void AutofillWalletOfferSyncBridge::MergeRemoteData(
    const syncer::EntityChangeList& entity_data) {
  std::vector<AutofillOfferData> offer_data;
  for (const std::unique_ptr<syncer::EntityChange>& change : entity_data) {
    DCHECK(change->data().specifics.has_autofill_offer());
    const sync_pb::AutofillOfferSpecifics specifics =
        change->data().specifics.autofill_offer();
    bool offer_valid = IsOfferSpecificsValid(specifics);
    if (offer_valid) {
      offer_data.push_back(AutofillOfferDataFromOfferSpecifics(specifics));
    }
    AutofillMetrics::LogSyncedOfferDataBeingValid(offer_valid);
  }

  AutofillTable* table = GetAutofillTable();

  // Only do a write operation if there is any difference between server data
  // and local data.
  std::vector<std::unique_ptr<AutofillOfferData>> existing_offers;
  table->GetCreditCardOffers(&existing_offers);

  bool offer_data_changed = AreAnyItemsDifferent(existing_offers, offer_data);
  if (offer_data_changed) {
    table->SetCreditCardOffers(offer_data);
  }

  // Commit the transaction to make sure the data and the metadata with the
  // new progress marker is written down (especially on Android where we
  // cannot rely on committing transactions on shutdown). We need to commit
  // even if the wallet data has not changed because the model type state incl.
  // the progress marker always changes.
  web_data_backend_->CommitChanges();

  if (offer_data_changed) {
    // TODO(crbug.com/1112095): Add enum to indicate what actually changed.
    web_data_backend_->NotifyOfMultipleAutofillChanges();
  }
}

AutofillTable* AutofillWalletOfferSyncBridge::GetAutofillTable() {
  return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
}

void AutofillWalletOfferSyncBridge::LoadAutofillOfferMetadata() {
  if (!web_data_backend_->GetDatabase() || !GetAutofillTable()) {
    change_processor()->ReportError(
        {FROM_HERE, "Failed to load Autofill table."});
    return;
  }

  auto batch = std::make_unique<syncer::MetadataBatch>();
  if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL_WALLET_OFFER,
                                              batch.get())) {
    change_processor()->ReportError(
        {FROM_HERE,
         "Failed reading autofill offer metadata from WebDatabase."});
    return;
  }
  change_processor()->ModelReadyToSync(std::move(batch));
}

}  // namespace autofill