
Sebagian besar orang terbiasa dengan fakta bahwa Chromium adalah peramban dan dasar bagi peramban lain. Sampai baru-baru ini, saya juga berpikir begitu, tetapi, mempelajari topik ini selama beberapa bulan, saya mulai menemukan dunia lain yang indah. Chromium adalah ekosistem besar di mana ada segalanya: sistem ketergantungan, sistem pembangunan lintas platform, dan komponen untuk hampir semua kesempatan. Jadi mengapa tidak mencoba membuat aplikasi sendiri menggunakan semua kekuatan ini?
Di bawah kat, panduan kecil tentang cara mulai melakukan ini.
Persiapan lingkungan
Dalam artikel saya akan menggunakan Ubuntu 18.04, prosedur untuk OS lain dapat ditemukan dalam dokumentasi:
Langkah-langkah berikut membutuhkan Git dan Python. Jika mereka tidak diinstal, maka mereka harus diinstal menggunakan perintah:
sudo apt install git python 
Pengaturan depot_tools
depot_tools adalah toolkit pengembangan Chromium. Untuk menginstalnya, Anda harus melakukan:
 git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git 
Dan tambahkan path ke variabel lingkungan PATH:
 export PATH="$PATH:/path/to/depot_tools" 
Penting: jika 
depot_tools diunduh ke folder rumah Anda, jangan gunakan 
~ dalam variabel 
PATH , jika tidak, masalah dapat terjadi. Anda harus menggunakan variabel 
$HOME :
 export PATH="$PATH:${HOME}/depot_tools" 
Pengambilan kode
Pertama, Anda perlu membuat folder untuk sumbernya. Misalnya, di direktori home (dibutuhkan ruang kosong sekitar 30 GB):
 mkdir ~/chromium && cd ~/chromium 
Setelah itu, Anda dapat mengunduh sumber menggunakan utilitas 
fetch dari 
depot_tools :
 fetch --nohooks --no-history chromium 
Sekarang Anda bisa minum teh / kopi, karena prosedurnya tidak cepat. Untuk percobaan, tidak diperlukan riwayat, sehingga bendera 
--no-history digunakan. Ceritanya akan lebih panjang.
Instalasi Ketergantungan
Semua sumber ada di folder 
src , buka:
 cd src 
Sekarang Anda harus meletakkan semua dependensi menggunakan skrip:
 ./build/install-build-deps.sh 
Dan jalankan hooks:
 gclient runhooks 
Ini melengkapi persiapan lingkungan.
Membangun sistem
Ninja digunakan sebagai sistem perakitan utama untuk Chromium, dan utilitas 
GN digunakan untuk menghasilkan file 
.ninja .
Untuk memahami cara menggunakan alat ini, saya mengusulkan untuk membuat contoh utilitas pengujian. Untuk melakukan ini, buat 
example subfolder di folder 
src :
 mkdir example 
Kemudian, di folder 
src/example , buat file 
BUILD.gn , yang berisi:
 executable("example") { sources = [ "example.cc", ] } 
BUILD.gn terdiri dari target ( 
example executable) dan daftar file yang diperlukan untuk membangun target.
Langkah selanjutnya adalah membuat file 
example.cc itu sendiri. Untuk memulai, saya mengusulkan untuk membuat aplikasi klasik "Hello world":
 #include <iostream> int main(int argc, char **argv) { std::cout << "Hello world" << std::endl; return 0; } 
Kode sumber dapat ditemukan di 
GitHub .
Agar GN dapat mempelajari tentang proyek baru, dalam file 
BUILD.gn terletak di 
src , tambahkan baris 
"//example" di bagian 
deps :
 ... group("gn_all") { testonly = true deps = [ ":gn_visibility", "//base:base_perftests", "//base:base_unittests", "//base/util:base_util_unittests", "//chrome/installer", "//chrome/updater", "//net:net_unittests", "//services:services_unittests", "//services/service_manager/public/cpp", "//skia:skia_unittests", "//sql:sql_unittests", "//third_party/flatbuffers:flatbuffers_unittests", "//tools/binary_size:binary_size_trybot_py", "//tools/ipc_fuzzer:ipc_fuzzer_all", "//tools/metrics:metrics_metadata", "//ui/base:ui_base_unittests", "//ui/gfx:gfx_unittests", "//url:url_unittests", # ↓↓↓↓↓↓↓↓ "//example", ] ... 
Sekarang Anda perlu kembali ke folder 
src dan menghasilkan proyek menggunakan perintah:
 gn gen out/Default 
GN juga memungkinkan Anda menyiapkan proyek untuk salah satu IDE yang didukung:
- gerhana
 
- vs.
 
- vs2013
 
- vs2015
 
- vs2017
 
- vs2019
 
- xcode
 
- qtcreator
 
- json
 
Informasi lebih lanjut dapat diperoleh dengan menggunakan perintah:
 gn help gen 
Misalnya, untuk bekerja dengan proyek 
example di 
QtCreator, Anda perlu menjalankan perintah:
 gn gen --ide=qtcreator --root-target=example out/Default 
Setelah itu, Anda dapat membuka proyek di QtCreator:
 qtcreator out/Default/qtcreator_project/all.creator 
Langkah terakhir adalah membangun proyek menggunakan Ninja:
 autoninja -C out/Default example 
Pengantar singkat tentang sistem perakitan ini dapat diselesaikan.
Aplikasi dapat diluncurkan menggunakan perintah:
 ./out/Default/example 
Dan lihat Hello world. Bahkan, Anda dapat menulis artikel terpisah tentang sistem perakitan di Chromium. Mungkin bukan satu.
Bekerja dengan baris perintah
Sebagai contoh pertama menggunakan basis kode Chromium sebagai kerangka kerja, saya sarankan bermain-main dengan baris perintah.
Tugas: 
menampilkan semua argumen yang diteruskan ke aplikasi dalam gaya Chromium.Untuk bekerja dengan baris perintah, Anda harus menyertakan file header di example.cc:
 
Dan juga kita tidak boleh lupa untuk menambahkan ketergantungan pada proyek 
base di 
BUILD.gn . 
BUILD.gn akan terlihat seperti ini:
 executable("example") { sources = [ "example.cc", ] deps = [ "//base", ] } 
Sekarang semua yang Anda butuhkan akan terhubung ke 
example .
Untuk bekerja dengan baris perintah, Chromium menyediakan 
base::CommandLine tunggal 
base::CommandLine . Untuk mendapatkan tautan ke sana, Anda perlu menggunakan 
base::CommandLine::ForCurrentProcess metode statis 
base::CommandLine::ForCurrentProcess , tetapi pertama-tama Anda perlu menginisialisasi menggunakan 
base::CommandLine::Init Metode 
base::CommandLine::Init :
 base::CommandLine::Init(argc, argv); auto *cmd_line = base::CommandLine::ForCurrentProcess(); 
Semua argumen yang diteruskan ke aplikasi pada baris perintah dan dimulai dengan a 
- dikembalikan sebagai 
base::SwitchMap (pada dasarnya 
map<string, string> ) menggunakan metode 
GetSwitches . Semua argumen lain dikembalikan sebagai 
base::StringVector (pada dasarnya 
vectr<strig> ). Pengetahuan ini cukup untuk mengimplementasikan kode untuk tugas:
 for (const auto &sw : cmd_line->GetSwitches()) { std::cout << "Switch " << sw.first << ": " << sw.second << std::endl; } for (const auto &arg: cmd_line->GetArgs()) { std::cout << "Arg " << arg << std::endl; } 
Versi lengkap dapat ditemukan di 
GitHub .
Untuk membangun dan menjalankan aplikasi, Anda perlu menjalankan:
 autoninja -C out/Default example ./out/Default/example arg1 --sw1=val1 --sw2 arg2 
Layar akan menampilkan:
 Switch sw1: val1 Switch sw2: Arg arg1 Arg arg2 
Jaringan
Sebagai contoh kedua dan terakhir untuk hari ini, saya mengusulkan untuk bekerja dengan bagian jaringan Chromium.
Tugas: 
menampilkan konten URL yang diteruskan sebagai argumen .
Subsistem Jaringan Chromium
Subsistem jaringan cukup besar dan kompleks. Titik masuk untuk permintaan HTTP, HTTPS, FTP dan sumber daya data lainnya adalah 
URLRequest , yang sudah menentukan klien mana yang akan digunakan. Diagram yang disederhanakan terlihat seperti ini:
 Versi lengkap dapat ditemukan di dokumentasi .
Versi lengkap dapat ditemukan di dokumentasi .Untuk membuat 
URLRequest Anda harus menggunakan 
URLRequestContext . Membuat konteks adalah operasi yang agak rumit, oleh karena itu disarankan untuk menggunakan 
URLRequestContextBuilder . Ini akan menginisialisasi semua variabel yang diperlukan dengan nilai default, tetapi, jika diinginkan, mereka dapat diubah sendiri, misalnya:
 net::URLRequestContextBuilder context_builder; context_builder.DisableHttpCache(); context_builder.SetSpdyAndQuicEnabled(true , false ); context_builder.SetCookieStore(nullptr); 
Multithreading
Tumpukan jaringan Chromium dirancang untuk bekerja di lingkungan multi-utas, sehingga Anda tidak dapat melewati topik ini. Objek dasar untuk bekerja dengan multithreading di Chromium adalah:
- Tugas - tugas untuk dieksekusi, di Chromium itu adalah fungsi dari tipe base::Callback, yang dapat dibuat menggunakanbase::Bind.
 
- Antrian tugas - antrian tugas untuk dieksekusi.
 
- Fisik thread - pembungkus lintas platform di atas sistem operasi thread ( pthreadpada POSIX atauCreateThread()pada Windows). Diterapkan dalambase::PlatformThreadkelasbase::PlatformThread, jangan gunakan secara langsung.
 
- base :: Utas - utas nyata yang memproses pesan dari antrian tugas khusus tanpa henti; Tidak disarankan untuk membuatnya secara langsung.
 
- Thread pool - pool thread dengan antrian tugas umum. Diimplementasikan dalam base::ThreadPoolkelasbase::ThreadPool. Sebagai aturan, buat satu instance. Tugas dikirim ke sana menggunakan fungsi daribase/task/post_task.h.
 
- Urutan atau Urutan virtual - utas virtual yang menggunakan utas nyata dan dapat beralih di antara mereka.
 
- Pelari tugas - antarmuka untuk mengatur tugas, diimplementasikan di base::TaskRunner.
 
- Pelari tugas berurutan - antarmuka untuk mengatur tugas, yang memastikan bahwa tugas akan dieksekusi dalam urutan yang sama dengan saat mereka tiba. Diimplementasikan dalam base::SequencedTaskRunnerkelasbase::SequencedTaskRunner.
 
- Pelari tugas utas tunggal - mirip dengan yang sebelumnya, tetapi menjamin bahwa semua tugas akan dilakukan dalam satu utas OS. Diterapkan dalam base::SingleThreadTaskRunner.
 
Implementasi
Beberapa komponen Chromium memerlukan kehadiran 
base::AtExitManager - ini adalah kelas yang memungkinkan Anda untuk mendaftarkan operasi yang harus dilakukan ketika aplikasi berakhir. Menggunakannya sangat sederhana, Anda perlu membuat objek di stack:
 base::AtExitManager exit_manager; 
Ketika 
exit_manager keluar dari ruang lingkup, semua panggilan balik yang terdaftar akan dieksekusi.
Sekarang Anda perlu menjaga ketersediaan semua komponen multithreading yang diperlukan untuk subsistem jaringan. Untuk melakukan ini, buat kumpulan 
Thread pool , 
Message loop dengan tipe 
TYPE_IO untuk memproses pesan jaringan, dan 
Run loop - loop program utama:
 base::ThreadPool::CreateAndStartWithDefaultParams("downloader"); base::MessageLoop msg_loop(base::MessageLoop::TYPE_IO); base::RunLoop run_loop; 
Selanjutnya, gunakan 
Context builder untuk membuat 
Context :
 auto ctx = net::URLRequestContextBuilder().Build(); 
Untuk mengirim permintaan, Anda harus membuat objek 
URLRequest menggunakan metode 
CreateRequest dari objek 
ctx . Parameter berikut dilewatkan:
- URL, string dengan tipe GURL;
 
- prioritas;
 
- delegasikan yang menangani acara.
 
Delegasi adalah kelas yang mengimplementasikan antarmuka 
net::URLRequest::Delegate . Untuk tugas ini, mungkin terlihat seperti ini:
 class MyDelegate : public net::URLRequest::Delegate { public: explicit MyDelegate(base::Closure quit_closure) : quit_closure_(std::move(quit_closure)), buf_(base::MakeRefCounted<net::IOBuffer>(BUF_SZ)) {} void OnReceivedRedirect(net::URLRequest *request, const net::RedirectInfo &redirect_info, bool *defer_redirect) override { std::cerr << "redirect to " << redirect_info.new_url << std::endl; } void OnAuthRequired(net::URLRequest* request, const net::AuthChallengeInfo& auth_info) override { std::cerr << "auth req" << std::endl; } void OnCertificateRequested(net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info) override { std::cerr << "cert req" << std::endl; } void OnSSLCertificateError(net::URLRequest* request, int net_error, const net::SSLInfo& ssl_info, bool fatal) override { std::cerr << "cert err" << std::endl; } void OnResponseStarted(net::URLRequest *request, int net_error) override { std::cerr << "resp started" << std::endl; while (true) { auto n = request->Read(buf_.get(), BUF_SZ); std::cerr << "resp read " << n << std::endl; if (n == net::ERR_IO_PENDING) return; if (n <= 0) { OnReadCompleted(request, n); return; } std::cout << std::string(buf_->data(), n) << std::endl; } } void OnReadCompleted(net::URLRequest *request, int bytes_read) override { std::cerr << "completed" << std::endl; quit_closure_.Run(); } private: base::Closure quit_closure_; scoped_refptr<net::IOBuffer> buf_; }; 
Semua logika utama ada di event 
OnResponseStarted : isi respons dikurangi sampai kesalahan terjadi atau tidak ada yang dibaca. Karena setelah membaca respons yang Anda butuhkan untuk menyelesaikan aplikasi, delegasi harus memiliki akses ke fungsi yang akan mengganggu 
Run loop utama, dalam hal ini panggilan balik dari tipe 
base::Closure digunakan.
Sekarang semuanya siap untuk mengirim permintaan:
 MyDelegate delegate(run_loop.QuitClosure()); auto req = ctx->CreateRequest(GURL(args[0]), net::RequestPriority::DEFAULT_PRIORITY, &delegate); req->Start(); 
Untuk permintaan untuk mulai memproses, Anda perlu menjalankan 
Run loop :
 run_loop.Run(); 
Versi lengkap dapat ditemukan di 
GitHub .
Untuk membangun dan menjalankan aplikasi, Anda perlu menjalankan:
 autoninja -C out/Default example out/Default/example "https://example.com/" 
Terakhir
Bahkan, di Chromium Anda dapat menemukan banyak kubus dan batu bata yang berguna untuk membangun aplikasi. Ini terus berkembang, yang, di satu sisi, merupakan nilai tambah, dan di sisi lain, perubahan reguler pada API tidak membuat Anda rileks. Misalnya, dalam rilis terbaru, 
base::TaskScheduler berubah menjadi 
base::ThreadPool , untungnya, tanpa mengubah API.
PS Kami mencari programmer C ++ terkemuka di tim kami! Jika Anda merasakan kekuatan dalam diri Anda, maka keinginan kami dijelaskan di sini: 
team.mail.ru/vacancy/4641/ . Ada juga tombol "Tanggapi".