blob: e97f18c559e0b676d5662fa298a5f4ed6f18c519 [file] [log] [blame]
// Copyright 2019 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/sms/sms_fetcher_impl.h"
#include "base/callback.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/sms/sms_parser.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
namespace content {
const char kSmsFetcherImplKeyName[] = "sms_fetcher";
SmsFetcherImpl::SmsFetcherImpl(SmsProvider* provider) : provider_(provider) {
if (provider_)
provider_->AddObserver(this);
}
SmsFetcherImpl::~SmsFetcherImpl() {
if (provider_)
provider_->RemoveObserver(this);
}
// static
SmsFetcher* SmsFetcher::Get(BrowserContext* context) {
if (!context->GetUserData(kSmsFetcherImplKeyName)) {
auto fetcher = std::make_unique<SmsFetcherImpl>(
BrowserMainLoop::GetInstance()->GetSmsProvider());
context->SetUserData(kSmsFetcherImplKeyName, std::move(fetcher));
}
return static_cast<SmsFetcherImpl*>(
context->GetUserData(kSmsFetcherImplKeyName));
}
void SmsFetcherImpl::Subscribe(const OriginList& origin_list,
SmsQueue::Subscriber* subscriber) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(subscriber);
// Should not be called multiple times for the same subscriber and origin.
DCHECK(!subscribers_.HasSubscriber(origin_list, subscriber));
subscribers_.Push(origin_list, subscriber);
if (provider_)
provider_->Retrieve(nullptr, SmsFetchType::kRemote);
}
void SmsFetcherImpl::Subscribe(const OriginList& origin_list,
SmsQueue::Subscriber* subscriber,
RenderFrameHost* render_frame_host) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(subscriber);
DCHECK(render_frame_host);
// Should not be called multiple times for the same subscriber.
DCHECK(!remote_cancel_callbacks_.count(subscriber));
DCHECK(!subscribers_.HasSubscriber(origin_list, subscriber));
subscribers_.Push(origin_list, subscriber);
// Fetches a remote SMS.
// TODO(crbug.com/1015645): Support iframe in cross-device WebOTP.
base::OnceClosure cancel_callback =
GetContentClient()->browser()->FetchRemoteSms(
WebContents::FromRenderFrameHost(render_frame_host), origin_list[0],
base::BindOnce(&SmsFetcherImpl::OnRemote,
weak_ptr_factory_.GetWeakPtr()));
if (cancel_callback)
remote_cancel_callbacks_[subscriber] = std::move(cancel_callback);
// Fetches a local SMS.
if (provider_)
provider_->Retrieve(render_frame_host, SmsFetchType::kLocal);
}
void SmsFetcherImpl::Unsubscribe(const OriginList& origin_list,
SmsQueue::Subscriber* subscriber) {
// Unsubscribe does not make a call to the provider because currently there
// is no mechanism to cancel a subscription.
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
subscribers_.Remove(origin_list, subscriber);
// A subscriber does not have a remote cancel callback in the map when
// 1. it has been unsubscribed before. e.g. we unsubscribe a subscriber when
// a verification flow is successful and when the subscriber is destroyed.
// 2. TODO(crbug.com/1015645): no need to keep cancel callback when we don't
// fetch a remote sms. e.g. when kWebOTPCrossDevice is disabled.
auto it = remote_cancel_callbacks_.find(subscriber);
if (it == remote_cancel_callbacks_.end())
return;
std::move(it->second).Run();
remote_cancel_callbacks_.erase(it);
}
bool SmsFetcherImpl::Notify(const OriginList& origin_list,
const std::string& one_time_code,
UserConsent consent_requirement) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The received OTP is returned to the first subscriber for the origin.
auto* subscriber = subscribers_.Pop(origin_list);
if (!subscriber)
return false;
subscriber->OnReceive(origin_list, one_time_code, consent_requirement);
return true;
}
void SmsFetcherImpl::OnRemote(base::Optional<OriginList> origin_list,
base::Optional<std::string> one_time_code,
base::Optional<FailureType> failure_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (failure_type) {
OnFailure(failure_type.value());
return;
}
if (!origin_list || !one_time_code)
return;
Notify(origin_list.value(), one_time_code.value(), UserConsent::kObtained);
}
bool SmsFetcherImpl::OnReceive(const OriginList& origin_list,
const std::string& one_time_code,
UserConsent consent_requirement) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return Notify(origin_list, one_time_code, consent_requirement);
}
bool SmsFetcherImpl::OnFailure(FailureType failure_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return subscribers_.NotifyFailure(failure_type);
}
bool SmsFetcherImpl::HasSubscribers() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return subscribers_.HasSubscribers();
}
} // namespace content