Initial layout of the passport authorization form.

This commit is contained in:
John Preston 2018-03-19 20:22:27 +04:00
parent ddb4527159
commit 07e8a2bd85
14 changed files with 676 additions and 29 deletions

View File

@ -1502,6 +1502,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_terms_decline" = "Decline";
"lng_terms_signup_sorry" = "We're very sorry, but this means you can't sign up for Telegram.\n\nUnlike others, we don't use your data for ad targeting or other commercial purposes. Telegram only stores the information it needs to function as a feature-rich cloud service. You can adjust how we use your data in Privacy & Security settings.\n\nBut if you're generally not OK with Telegram's modest needs, it won't be possible for us to provide this service.";
"lng_passport_title" = "Telegram passport";
"lng_passport_request" = "{domain} requests access to your personal data\nto sign you up for their services";
"lng_passport_password_request" = "Please provide the password for\naccessing your personal data.";
"lng_passport_password_placeholder" = "Enter password";
"lng_passport_password_wrong" = "Wrong password";
"lng_passport_header" = "Requested information";
"lng_passport_identity_title" = "Identity document";
"lng_passport_identity_description" = "Upload scan of your passport or other ID";
"lng_passport_identity_passport" = "Passport";
"lng_passport_identity_card" = "Identity card";
"lng_passport_identity_license" = "Driver's license";
"lng_passport_address_title" = "Residential address";
"lng_passport_address_description" = "Upload proof of your address";
"lng_passport_phone_title" = "Phone number";
"lng_passport_email_title" = "E-mail";
"lng_passport_email_description" = "Specify your e-mail address";
"lng_passport_accept_allow" = "You accept {policy} and allow their {bot} to send messages to you.";
"lng_passport_accept" = "You accept {policy}.";
"lng_passport_policy" = "{domain} privacy policy";
"lng_passport_authorize" = "Authorize";
"lng_passport_form_error" = "Could not get authorization form.";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@ -223,6 +223,10 @@ inline int FillRandom(base::byte_span bytes) {
return RAND_bytes(reinterpret_cast<unsigned char*>(bytes.data()), bytes.size());
}
inline void AddRandomSeed(base::const_byte_span bytes) {
RAND_seed(bytes.data(), bytes.size());
}
} // namespace openssl
namespace bytes {

View File

@ -76,6 +76,8 @@ void BoxContent::finishPrepare() {
void BoxContent::finishScrollCreate() {
Expects(_scroll != nullptr);
_scroll->show();
updateScrollAreaGeometry();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));

View File

@ -323,7 +323,9 @@ void PasscodeBox::save(bool force) {
if (!_oldPasscode->isHidden()) {
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
}
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt | MTPDaccount_passwordInputSettings::Flag::f_new_password_hash | MTPDaccount_passwordInputSettings::Flag::f_hint;
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint;
if (_oldPasscode->isHidden() || _newPasscode->isHidden()) {
flags |= MTPDaccount_passwordInputSettings::Flag::f_email;
}

View File

@ -923,6 +923,7 @@ bool Messenger::openLocalUrl(const QString &url) {
scope,
callback,
publicKey));
return true;
}
}
}

View File

@ -0,0 +1,39 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/widgets/widgets.style";
using "boxes/boxes.style";
using "intro/intro.style";
passportPasswordPadding: margins(20px, 30px, 20px, 40px);
passportPasswordForgotSkip: 5px;
passportPasswordAboutSkip: 15px;
passportPasswordLabel: FlatLabel(boxLabel) {
minWidth: 275px;
}
passportPasswordHintLabel: passportPasswordLabel;
passportErrorLabel: FlatLabel(passportPasswordLabel) {
textFg: boxTextFgError;
}
passportRowPadding: margins(20px, 10px, 20px, 10px);
passportRowSkip: 5px;
passportRowRipple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
passportRowCheckbox: IconButton(defaultIconButton) {
width: 38px;
height: 38px;
iconPosition: point(10px, 13px);
icon: icon {{ "send_control_save", menuIconFg }};
iconOver: icon {{ "send_control_save", menuIconFgOver }};
rippleAreaPosition: point(0px, 0px);
rippleAreaSize: 38px;
ripple: passportRowRipple;
}

View File

@ -0,0 +1,261 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_form_box.h"
#include "passport/passport_form_controller.h"
#include "passport/passport_form_row.h"
#include "lang/lang_keys.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/text_options.h"
#include "styles/style_boxes.h"
#include "styles/style_widgets.h"
#include "styles/style_passport.h"
namespace Passport {
class FormBox::CheckWidget : public Ui::RpWidget {
public:
CheckWidget(QWidget *parent, not_null<FormController*> controller);
void setInnerFocus();
void submit();
protected:
int resizeGetHeight(int newWidth) override;
private:
void showError(const QString &error);
void hideError();
not_null<FormController*> _controller;
object_ptr<Ui::PasswordInput> _password;
object_ptr<Ui::FlatLabel> _hint = { nullptr };
object_ptr<Ui::FlatLabel> _error = { nullptr };
object_ptr<Ui::LinkButton> _forgot;
object_ptr<Ui::FlatLabel> _about;
};
class FormBox::Inner : public Ui::RpWidget {
public:
Inner(QWidget *parent, not_null<FormController*> controller);
void refresh();
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
not_null<FormController*> _controller;
std::vector<object_ptr<FormRow>> _rows;
};
FormBox::CheckWidget::CheckWidget(
QWidget *parent,
not_null<FormController*> controller)
: RpWidget(parent)
, _controller(controller)
, _password(
this,
st::defaultInputField,
langFactory(lng_passport_password_placeholder))
, _forgot(this, lang(lng_signin_recover), st::boxLinkButton)
, _about(
this,
lang(lng_passport_password_request),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordLabel) {
connect(_password, &Ui::PasswordInput::submitted, this, [=] {
submit();
});
connect(_password, &Ui::PasswordInput::changed, this, [=] {
hideError();
});
if (const auto hint = _controller->passwordHint(); !hint.isEmpty()) {
_hint.create(
this,
hint,
Ui::FlatLabel::InitType::Simple,
st::passportPasswordHintLabel);
}
_controller->passwordError(
) | rpl::start_with_next([=](const QString &error) {
showError(error);
}, lifetime());
}
void FormBox::CheckWidget::showError(const QString &error) {
_password->showError();
_error.create(
this,
error,
Ui::FlatLabel::InitType::Simple,
st::passportErrorLabel);
_error->show();
if (_hint) {
_hint->hide();
}
resizeToWidth(width());
}
void FormBox::CheckWidget::hideError() {
_error.destroy();
if (_hint) {
_hint->show();
}
}
void FormBox::CheckWidget::submit() {
_controller->submitPassword(_password->getLastText());
}
void FormBox::CheckWidget::setInnerFocus() {
_password->setFocusFast();
}
int FormBox::CheckWidget::resizeGetHeight(int newWidth) {
const auto padding = st::passportPasswordPadding;
const auto availableWidth = newWidth
- st::boxPadding.left()
- st::boxPadding.right();
auto top = padding.top();
_about->resizeToWidth(availableWidth);
_about->moveToLeft(padding.left(), top);
top += _about->height();
_password->resize(availableWidth, _password->height());
_password->moveToLeft(padding.left(), top);
top += _password->height();
if (_error) {
_error->resizeToWidth(availableWidth);
_error->moveToLeft(padding.left(), top);
}
if (_hint) {
_hint->resizeToWidth(availableWidth);
_hint->moveToLeft(padding.left(), top);
top += _hint->height();
} else {
top += st::passportPasswordHintLabel.style.font->height;
}
_forgot->moveToLeft(padding.left(), top);
top += _forgot->height();
return top + padding.bottom();
}
FormBox::Inner::Inner(
QWidget *parent,
not_null<FormController*> controller)
: RpWidget(parent)
, _controller(controller) {
refresh();
}
void FormBox::Inner::refresh() {
auto index = 0;
_controller->fillRows([&](
QString title,
QString description,
bool ready) {
if (_rows.size() <= index) {
_rows.push_back(object_ptr<FormRow>(this, title, description));
}
_rows[index++]->setReady(ready);
});
while (_rows.size() > index) {
_rows.pop_back();
}
resizeToWidth(width());
}
int FormBox::Inner::resizeGetHeight(int newWidth) {
auto result = 0;
for (auto &row : _rows) {
row->resizeToWidth(newWidth);
row->moveToLeft(0, result);
result += row->height();
}
return result;
}
void FormBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
}
FormBox::FormBox(QWidget*, not_null<FormController*> controller)
: _controller(controller) {
}
void FormBox::prepare() {
setTitle(langFactory(lng_passport_title));
addButton(langFactory(lng_create_group_next), [=] {
submitPassword();
});
addButton(langFactory(lng_cancel), [=] {
closeBox();
});
_passwordCheck = setInnerWidget(
object_ptr<CheckWidget>(this, _controller));
_passwordCheck->resizeToWidth(st::boxWideWidth);
_innerCached.create(this, _controller);
_innerCached->resizeToWidth(st::boxWideWidth);
const auto desiredHeight = std::min(
std::max(_innerCached->height(), _passwordCheck->height()),
st::boxMaxListHeight);
setDimensions(st::boxWideWidth, desiredHeight);
_innerCached->hide();
_controller->secretReadyEvents(
) | rpl::start_with_next([=] {
showForm();
}, lifetime());
}
void FormBox::setInnerFocus() {
if (_passwordCheck) {
_passwordCheck->setInnerFocus();
} else {
_inner->setFocus();
}
}
void FormBox::submitPassword() {
Expects(_passwordCheck != nullptr);
_passwordCheck->submit();
}
void FormBox::showForm() {
clearButtons();
addButton(langFactory(lng_passport_authorize), [=] {
submitForm();
});
addButton(langFactory(lng_cancel), [=] {
closeBox();
});
_inner = setInnerWidget(std::move(_innerCached));
_inner->show();
}
void FormBox::submitForm() {
}
} // namespace Passport

View File

@ -0,0 +1,39 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "boxes/abstract_box.h"
namespace Passport {
class FormController;
class FormBox : public BoxContent {
public:
FormBox(QWidget*, not_null<FormController*> controller);
protected:
void prepare() override;
void setInnerFocus() override;
private:
class CheckWidget;
class Inner;
void submitPassword();
void showForm();
void submitForm();
not_null<FormController*> _controller;
object_ptr<Inner> _innerCached = { nullptr };
QPointer<CheckWidget> _passwordCheck;
QPointer<Inner> _inner;
};
} // namespace Passport

View File

@ -7,8 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_form_controller.h"
#include "passport/passport_form_box.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "base/openssl_help.h"
#include "mainwindow.h"
namespace Passport {
@ -42,6 +44,95 @@ void FormController::show() {
requestPassword();
}
void FormController::submitPassword(const QString &password) {
Expects(!_password.salt.isEmpty());
if (_passwordCheckRequestId) {
return;
}
const auto data = _password.salt + password.toUtf8() + _password.salt;
const auto hash = hashSha256(data.constData(), data.size());
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
MTP_bytes(gsl::as_bytes(gsl::make_span(hash)))
)).handleFloodErrors(
).done([=](const MTPaccount_PasswordSettings &result) {
Expects(result.type() == mtpc_account_passwordSettings);
_passwordCheckRequestId = 0;
const auto &data = result.c_account_passwordSettings();
_passwordEmail = qs(data.vemail);
_secret = byteVectorFromMTP(data.vsecure_secret);
_secretReady.fire({});
}).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0;
if (MTP::isFloodError(error)) {
_passwordError.fire(lang(lng_flood_error));
} else if (error.type() == qstr("PASSWORD_HASH_INVALID")) {
_passwordError.fire(lang(lng_passport_password_wrong));
} else {
_passwordError.fire_copy(error.type());
}
}).send();
}
rpl::producer<QString> FormController::passwordError() const {
return _passwordError.events();
}
QString FormController::passwordHint() const {
return _password.hint;
}
rpl::producer<> FormController::secretReadyEvents() const {
return _secretReady.events();
}
QString FormController::defaultEmail() const {
return _passwordEmail;
}
QString FormController::defaultPhoneNumber() const {
if (const auto self = App::self()) {
return self->phone();
}
return QString();
}
void FormController::fillRows(
base::lambda<void(
QString title,
QString description,
bool ready)> callback) {
for (const auto &field : _form.fields) {
switch (field.type) {
case Field::Type::Identity:
callback(
lang(lng_passport_identity_title),
lang(lng_passport_identity_description),
false);
break;
case Field::Type::Address:
callback(
lang(lng_passport_address_title),
lang(lng_passport_address_description),
false);
break;
case Field::Type::Phone:
callback(
lang(lng_passport_phone_title),
App::self()->phone(),
true);
break;
case Field::Type::Email:
callback(
lang(lng_passport_email_title),
lang(lng_passport_email_description),
false);
break;
}
}
}
void FormController::requestForm() {
auto scope = QVector<MTPstring>();
scope.reserve(_request.scope.size());
@ -51,7 +142,7 @@ void FormController::requestForm() {
auto normalizedKey = _request.publicKey;
normalizedKey.replace("\r\n", "\n");
const auto bytes = normalizedKey.toUtf8();
request(MTPaccount_GetAuthorizationForm(
_formRequestId = request(MTPaccount_GetAuthorizationForm(
MTP_flags(MTPaccount_GetAuthorizationForm::Flag::f_origin
| MTPaccount_GetAuthorizationForm::Flag::f_public_key),
MTP_int(_request.botId),
@ -61,6 +152,7 @@ void FormController::requestForm() {
MTPstring(), // bundle_id
MTP_bytes(bytes)
)).done([=](const MTPaccount_AuthorizationForm &result) {
_formRequestId = 0;
formDone(result);
}).fail([=](const RPCError &error) {
formFail(error);
@ -117,7 +209,9 @@ auto FormController::convertValue(
void FormController::formDone(const MTPaccount_AuthorizationForm &result) {
parseForm(result);
showForm();
if (!_passwordRequestId) {
showForm();
}
}
void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
@ -160,57 +254,57 @@ void FormController::showForm() {
Ui::show(Box<InformBox>("Could not get authorization bot."));
return;
}
auto text = QString("Auth request:\n");
for (const auto &field : _form.fields) {
switch (field.type) {
case Field::Type::Identity: text += "- identity\n"; break;
case Field::Type::Address: text += "- address\n"; break;
case Field::Type::Email: text += "- email\n"; break;
case Field::Type::Phone: text += "- phone\n"; break;
}
}
if (_form.requestWrite) {
text += "and bot @" + _bot->username + " requests write permission.";
} else {
text += "and bot @" + _bot->username + " does not request write permission.";
}
Ui::show(Box<InformBox>(text));
// Ui::show(Box<FormBox>(this));
Ui::show(Box<FormBox>(this));
}
void FormController::formFail(const RPCError &error) {
// #TODO langs
Ui::show(Box<InformBox>("Could not get authorization form."));
Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
}
void FormController::requestPassword() {
request(MTPaccount_GetPassword(
_passwordRequestId = request(MTPaccount_GetPassword(
)).done([=](const MTPaccount_Password &result) {
_passwordRequestId = 0;
passwordDone(result);
}).fail([=](const RPCError &error) {
formFail(error);
}).send();
}
void FormController::passwordDone(const MTPaccount_Password &result) {
switch (result.type()) {
case mtpc_account_noPassword:
createPassword(result.c_account_noPassword());
parsePassword(result.c_account_noPassword());
break;
case mtpc_account_password:
checkPassword(result.c_account_password());
parsePassword(result.c_account_password());
break;
}
if (!_formRequestId) {
showForm();
}
}
void FormController::passwordFail(const RPCError &error) {
Ui::show(Box<InformBox>("Could not get authorization form."));
}
void FormController::createPassword(const MTPDaccount_noPassword &result) {
Ui::show(Box<InformBox>("You need 2fa password!")); // #TODO
void FormController::parsePassword(const MTPDaccount_noPassword &result) {
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
_password.newSalt = result.vnew_salt.v;
openssl::AddRandomSeed(
gsl::as_bytes(gsl::make_span(result.vsecret_random.v)));
}
void FormController::checkPassword(const MTPDaccount_password &result) {
void FormController::parsePassword(const MTPDaccount_password &result) {
_password.hint = qs(result.vhint);
_password.hasRecovery = mtpIsTrue(result.vhas_recovery);
_password.salt = result.vcurrent_salt.v;
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
_password.newSalt = result.vnew_salt.v;
openssl::AddRandomSeed(
gsl::as_bytes(gsl::make_span(result.vsecret_random.v)));
}
} // namespace Passport

View File

@ -31,12 +31,32 @@ struct FormRequest {
class FormController : private MTP::Sender {
public:
struct PasswordCheckResult {
QByteArray secret;
};
FormController(
not_null<Window::Controller*> controller,
const FormRequest &request);
void show();
void submitPassword(const QString &password);
rpl::producer<QString> passwordError() const;
QString passwordHint() const;
rpl::producer<> secretReadyEvents() const;
QString defaultEmail() const;
QString defaultPhoneNumber() const;
void fillRows(
base::lambda<void(
QString title,
QString description,
bool ready)> callback);
private:
struct File {
uint64 id = 0;
@ -76,6 +96,13 @@ private:
bool requestWrite = false;
std::vector<Field> fields;
};
struct PasswordSettings {
QByteArray salt;
QByteArray newSalt;
QString hint;
QString unconfirmedPattern;
bool hasRecovery = false;
};
Value convertValue(const MTPSecureValue &value) const;
void requestForm();
@ -88,16 +115,26 @@ private:
void passwordDone(const MTPaccount_Password &result);
void passwordFail(const RPCError &error);
void createPassword(const MTPDaccount_noPassword &settings);
void checkPassword(const MTPDaccount_password &settings);
void parsePassword(const MTPDaccount_noPassword &settings);
void parsePassword(const MTPDaccount_password &settings);
not_null<Window::Controller*> _controller;
FormRequest _request;
UserData *_bot = nullptr;
QString _origin;
mtpRequestId _formRequestId = 0;
mtpRequestId _passwordRequestId = 0;
mtpRequestId _passwordCheckRequestId = 0;
PasswordSettings _password;
Form _form;
base::byte_vector _secret;
QString _passwordEmail;
rpl::event_stream<> _secretReady;
rpl::event_stream<QString> _passwordError;
};
} // namespace Passport

View File

@ -0,0 +1,96 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_form_row.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/text_options.h"
#include "styles/style_boxes.h"
#include "styles/style_passport.h"
namespace Passport {
FormRow::FormRow(
QWidget *parent,
const QString &title,
const QString &description)
: RippleButton(parent, st::passportRowRipple)
, _title(
st::semiboldTextStyle,
title,
Ui::NameTextOptions(),
st::boxWideWidth / 2)
, _description(
st::defaultTextStyle,
description,
Ui::NameTextOptions(),
st::boxWideWidth / 2) {
}
void FormRow::setReady(bool ready) {
if (ready) {
_checkbox.create(this, object_ptr<Ui::IconButton>(
this,
st::passportRowCheckbox));
_checkbox->show(anim::type::instant);
_checkbox->entity()->addClickHandler([=] {
_checkbox->hide(anim::type::normal);
});
} else {
_checkbox.destroy();
}
resizeToWidth(width());
}
int FormRow::resizeGetHeight(int newWidth) {
const auto availableWidth = countAvailableWidth(newWidth);
_titleHeight = _title.countHeight(availableWidth);
_descriptionHeight = _description.countHeight(availableWidth);
const auto result = st::passportRowPadding.top()
+ _titleHeight
+ st::passportRowSkip
+ _descriptionHeight
+ st::passportRowPadding.bottom();
if (_checkbox) {
const auto right = st::passportRowPadding.right();
_checkbox->moveToRight(
right,
(result - _checkbox->height()) / 2,
newWidth);
}
return result;
}
int FormRow::countAvailableWidth(int newWidth) const {
return newWidth
- st::passportRowPadding.left()
- st::passportRowPadding.right()
- (_checkbox ? _checkbox->width() : 0);
}
int FormRow::countAvailableWidth() const {
return countAvailableWidth(width());
}
void FormRow::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto ms = getms();
paintRipple(p, 0, 0, ms);
const auto left = st::passportRowPadding.left();
const auto availableWidth = countAvailableWidth();
auto top = st::passportRowPadding.top();
_title.drawLeft(p, left, top, availableWidth, width());
top += _titleHeight + st::passportRowSkip;
_description.drawLeft(p, left, top, availableWidth, width());
top += _descriptionHeight + st::passportRowPadding.bottom();
}
} // namespace Passport

View File

@ -0,0 +1,45 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/widgets/buttons.h"
namespace Ui {
template <typename Widget>
class FadeWrapScaled;
} // namespace Ui
namespace Passport {
class FormRow : public Ui::RippleButton {
public:
FormRow(
QWidget *parent,
const QString &title,
const QString &description);
void setReady(bool ready);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
int countAvailableWidth() const;
int countAvailableWidth(int newWidth) const;
Text _title;
Text _description;
int _titleHeight = 0;
int _descriptionHeight = 0;
object_ptr<Ui::FadeWrapScaled<Ui::IconButton>> _checkbox = { nullptr };
};
} // namespace Passport

View File

@ -33,6 +33,7 @@
'<(src_loc)/media/view/mediaview.style',
'<(src_loc)/media/player/media_player.style',
'<(src_loc)/overview/overview.style',
'<(src_loc)/passport/passport.style',
'<(src_loc)/profile/profile.style',
'<(src_loc)/settings/settings.style',
'<(src_loc)/chat_helpers/chat_helpers.style',

View File

@ -454,8 +454,12 @@
<(src_loc)/mtproto/type_utils.h
<(src_loc)/overview/overview_layout.cpp
<(src_loc)/overview/overview_layout.h
<(src_loc)/passport/passport_form_box.cpp
<(src_loc)/passport/passport_form_box.h
<(src_loc)/passport/passport_form_controller.cpp
<(src_loc)/passport/passport_form_controller.h
<(src_loc)/passport/passport_form_row.cpp
<(src_loc)/passport/passport_form_row.h
<(src_loc)/platform/linux/linux_desktop_environment.cpp
<(src_loc)/platform/linux/linux_desktop_environment.h
<(src_loc)/platform/linux/linux_gdk_helper.cpp