Initial check-in

Based on Chromium revision f3b63e7356ad0846045fe69dd640781e95728486
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..13d16fb
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,1090 @@
+# Names should be added to this file with this pattern:
+#
+# For individuals:
+#   Name <email address>
+#
+# For organizations:
+#   Organization <fnmatch pattern>
+#
+# See python fnmatch module documentation for more information.
+
+Aaron Boushley <boushley@gmail.com>
+Aaron Jacobs <samusaaron3@gmail.com>
+Aaron Leventhal <aaronlevbugs@gmail.com>
+Aaron Randolph <aaron.randolph@gmail.com>
+Aaryaman Vasishta <jem456.vasishta@gmail.com>
+Abdu Ameen <abdu.ameen000@gmail.com>
+Abhijeet Kandalkar <abhijeet.k@samsung.com>
+Abhishek Agarwal <abhishek.a21@samsung.com>
+Abhishek Kanike <abhishek.ka@samsung.com>
+Abhishek Singh <abhi.rathore@samsung.com>
+Adam Bonner <abonner-chromium@solscope.com>
+Adam Bujalski <abujalski@gmail.com>
+Adam Kallai <kadam@inf.u-szeged.hu>
+Adam Roben <adam@github.com>
+Adam Treat <adam.treat@samsung.com>
+Adam Yi <i@adamyi.com>
+Addanki Gandhi Kishor <kishor.ag@samsung.com>
+Adenilson Cavalcanti <a.cavalcanti@samsung.com>
+Aditya Bhargava <heuristicist@gmail.com>
+Adrian Belgun <adrian.belgun@intel.com>
+Ahmet Emir Ercin <ahmetemiremir@gmail.com>
+Ajay Berwal <a.berwal@samsung.com>
+Ajay Berwal <ajay.berwal@samsung.com>
+Ajith Kumar V <ajith.v@samsung.com>
+Aku Kotkavuo <a.kotkavuo@partner.samsung.com>
+Aldo Culquicondor <alculquicondor@gmail.com>
+Aleksandar Stojiljkovic <aleksandar.stojiljkovic@intel.com>
+Alex Gabriel <minilogo@gmail.com>
+Alex Gartrell <agartrell@cmu.edu>
+Alex Henrie <alexhenrie24@gmail.com>
+Alex Scheele <alexscheele@gmail.com>
+Alexander Douglas <agdoug@amazon.com>
+Alexander Guettler <alexander@guettler.io>
+Alexander Shalamov <alexander.shalamov@intel.com>
+Alexander Sulfrian <alexander@sulfrian.net>
+Alexandre Abreu <wiss1976@gmail.com>
+Alexandru Chiculita <achicu@adobe.com>
+Alexey Korepanov <alexkorep@gmail.com>
+Alexey Kuts <kruntuid@gmail.com>
+Alexey Kuzmin <alex.s.kuzmin@gmail.com>
+Alexey Kuznetsov <saturas2000@gmail.com>
+Alexis Brenon <brenon.alexis@gmail.com>
+Alexis La Goutte <alexis.lagoutte@gmail.com>
+Alexis Menard <alexis.menard@intel.com>
+Alfredo Hernandez <ahernandez.miralles@gmail.com>
+Ali Vathi <ali.akbar@gmail.com>
+Allan Sandfeld Jensen <allan.jensen@qt.io>
+Alper Çakan <alpercakan98@gmail.com>
+Ambarish Rapte <ambarish.r@samsung.com>
+Amey Jahagirdar <jahagird@amazon.com>
+Amit Sarkar <amit.srkr@samsung.com>
+Amogh Bihani <amogh.bihani@samsung.com>
+Amos Lim <amoseui@gmail.com>
+Amos Lim <eui-sang.lim@samsung.com>
+Amruth Raj <amruthraj@motorola.com>
+Amruth Raj <ckqr36@motorola.com>
+Anand Ratn <anand.ratn@samsung.com>
+Anastasios Cassiotis <tom.cassiotis@gmail.com>
+anatoly techtonik <techtonik@gmail.com>
+Ancil George <ancilgeorge@samsung.com>
+Andra Paraschiv <andra.paraschiv@intel.com>
+Andrei Borza <andrei.borza@gmail.com>
+Andrei Parvu <andrei.prv@gmail.com>
+Andrei Parvu <parvu@adobe.com>
+Andrew Boyarshin <andrew.boyarshin@gmail.com>
+Andrew Brampton <me@bramp.net>
+Andrew Hung <andrhung@amazon.com>
+Andrew Jorgensen <ajorgens@amazon.com>
+Andrew MacPherson <andrew.macpherson@soundtrap.com>
+Andrew Tulloch <andrew@tullo.ch>
+Anish Patankar <anish.p@samsung.com>
+Ankit Kumar <ankit2.kumar@samsung.com>
+Ankur Verma <ankur1.verma@samsung.com>
+Anna Henningsen <anna@addaleax.net>
+Anne Kao <annekao94@gmail.com>
+Anssi Hannula <anssi.hannula@iki.fi>
+Anthony Halliday <anth.halliday12@gmail.com>
+Anton Obzhirov <a.obzhirov@samsung.com>
+Antonin Hildebrand <antonin.hildebrand@gmail.com>
+Antonio Gomes <a1.gomes@sisa.samsung.com>
+Anuj Kumar Sharma <anujk.sharma@samsung.com>
+Arjun Karthik <arjunkar@amazon.com>
+Arman Ghotb <armanghotb@gmail.com>
+Armin Burgmeier <aburgmeier@bloomberg.net>
+Arnaud Mandy <arnaud.mandy@intel.com>
+Arnaud Renevier <a.renevier@samsung.com>
+Arpita Bahuguna <a.bah@samsung.com>
+Arthur Lussos <developer0420@gmail.com>
+Arun Kulkarni <kulkarni.a@samsung.com>
+Arun Kumar <arun87.kumar@samsung.com>
+Arun Mankuzhi <arun.m@samsung.com>
+Arunoday Sarkar <a.sarkar.arun@gmail.com>
+Arunprasad Rajkumar <ararunprasad@gmail.com>
+Arunprasad Rajkumar <arurajku@cisco.com>
+Asami Doi <d0iasm.pub@gmail.com>
+Ashish Kumar Gupta <guptaag@amazon.com>
+Ashlin Joseph <ashlin.j@samsung.com>
+Asish Singh <asish.singh@samsung.com>
+Attila Dusnoki <dati91@gmail.com>
+Avinaash Doreswamy <avi.nitk@samsung.com>
+Ayush Khandelwal <k.ayush@samsung.com>
+Azhar Shaikh <azhar.shaikh@intel.com>
+Balazs Kelemen <b.kelemen@samsung.com>
+Baul Eun <baul.eun@samsung.com>
+Behara Mani Shyam Patro <behara.ms@samsung.com>
+Bem Jones-Bey <bemajaniman@gmail.com>
+Bem Jones-Bey <bjonesbe@adobe.com>
+Ben Coe <bencoe@gmail.com>
+Ben Fiola <benfiola@gmail.com>
+Ben Karel <eschew@gmail.com>
+Ben Noordhuis <ben@strongloop.com>
+Benedek Heilig <benecene@gmail.com>
+Benjamin Dupont <bedupont@cisco.com>
+Benjamin Jemlich <pcgod99@gmail.com>
+Bernard Cafarelli <voyageur@gentoo.org>
+Bernhard M. Wiedemann <bwiedemann@suse.de>
+Bert Belder <bertbelder@gmail.com>
+Bhagirathi Satpathy <bhagirathi.s@samsung.com>
+Bhanukrushana Rout <b.rout@samsung.com>
+Biljith Jayan <billy.jayan@samsung.com>
+Boaz Sender <boaz@bocoup.com>
+Bobby Powers <bobbypowers@gmail.com>
+Branden Archer <bma4@zips.uakron.edu>
+Brendan Kirby <brendan.kirby@imgtec.com>
+Brendan Long <self@brendanlong.com>
+Brian G. Merrell <bgmerrell@gmail.com>
+Brian Konzman, SJ <b.g.konzman@gmail.com>
+Brian Luft <brian@electroly.com>
+Brian Merrell, Novell Inc. <bgmerrell@gmail.com>
+Brian Yip <itsbriany@gmail.com>
+Bruno Calvignac <bruno@flock.com>
+Bruno de Oliveira Abinader <brunoabinader@gmail.com>
+Bruno Roy <brusi_roy@hotmail.com>
+Bryan Donlan <bdonlan@gmail.com>
+Bryce Thomas <bryct@amazon.com>
+Burton <burton@typewritten.net>
+Byounghoon Yoon <bill.2714@kakaocorp.com>
+Byoungkwon Ko <codeimpl@gmail.com>
+Byungwoo Lee <bw80.lee@samsung.com>
+Caesar Wang <wxt@rock-chips.com>
+Caio Marcelo de Oliveira Filho <caio.de.oliveira.filho@intel.com>
+Caitlin Potter <caitpotter88@gmail.com>
+Calvin Mei <calvimei@amazon.com>
+Cameron Gutman <aicommander@gmail.com>
+Catalin Badea <badea@adobe.com>
+Cathie Chen <cathiechen@tencent.com>
+Cem Kocagil <cem.kocagil@gmail.com>
+Chakshu Ahuja <chakshu.a@samsung.com>
+Chamal De Silva <chamalsl@yahoo.com>
+Chandan Padhi <c.padhi@samsung.com>
+Chandra Shekar Vallala <brk376@motorola.com>
+Chandramouli Sanchi <cm.sanchi@samsung.com>
+Chang Shu <c.shu@samsung.com>
+Changbin Shao <changbin.shao@intel.com>
+Changjun Yang <changjun.yang@intel.com>
+ChangSeok Oh <shivamidow@gmail.com>
+Changwan Hong <changwan.hong@navercorp.com>
+Changyeon Kim <cyzero.kim@samsung.com>
+Chanho Park <parkch98@gmail.com>
+Chansik Yun <chansik.yun@gmail.com>
+Chaobin Zhang <zhchbin@gmail.com>
+Charles Vaughn <cvaughn@gmail.com>
+Choongwoo Han <cwhan.tunz@gmail.com>
+Chris Greene <cwgreene@amazon.com>
+Chris Harrelson <chrishtr@gmail.com>
+Chris Nardi <hichris123@gmail.com>
+Chris Szurgot <szurgotc@amazon.com>
+Chris Tserng <tserng@amazon.com>
+Chris Vasselli <clindsay@gmail.com>
+Christophe Dumez <ch.dumez@samsung.com>
+Christopher Dale <chrelad@gmail.com>
+Claudio DeSouza <claudiomdsjr@gmail.com>
+Clemens Fruhwirth <clemens@endorphin.org>
+Clement Scheelfeldt Skau <clementskau@gmail.com>
+Clinton Staley <clintstaley@gmail.com>
+Connor Pearson <cjp822@gmail.com>
+Craig Schlenter <craig.schlenter@gmail.com>
+Csaba Osztrogonác <ossy.szeged@gmail.com>
+Daegyu Lee <na7jun8gi@gmail.com>
+Dai Chunyang <chunyang.dai@intel.com>
+Daiwei Li <daiweili@suitabletech.com>
+Damien Marié <damien@dam.io>
+Dan McCombs <overridex@gmail.com>
+Daniel Bevenius <daniel.bevenius@gmail.com>
+Daniel Bomar <dbdaniel42@gmail.com>
+Daniel Carvalho Liedke <dliedke@gmail.com>
+Daniel Charles <daniel.charles@intel.com>
+Daniel Imms <daniimms@amazon.com>
+Daniel Johnson <danielj41@gmail.com>
+Daniel Lockyer <thisisdaniellockyer@gmail.com>
+Daniel Nishi <dhnishi@gmail.com>
+Daniel Platz <daplatz@googlemail.com>
+Daniel Shaulov <dshaulov@ptc.com>
+Daniel Trebbien <dtrebbien@gmail.com>
+Daniel Waxweiler <daniel.waxweiler@gmail.com>
+Dániel Bátyai <dbatyai@inf.u-szeged.hu>
+Dániel Vince <vinced@inf.u-szeged.hu>
+Darshini KN <kn.darshini@samsung.com>
+Dave Barker <kzar@kzar.co.uk>
+David Benjamin <davidben@mit.edu>
+David Davidovic <david@davidovic.io>
+David Erceg <erceg.david@gmail.com>
+David Fox <david@davidjfox.com>
+David Futcher <david.mike.futcher@gmail.com>
+David Leen <davileen@amazon.com>
+David McAllister <mcdavid@amazon.com>
+David Michael Barr <david.barr@samsung.com>
+David Spellman <dspell@amazon.com>
+David Valachovic <adenflorian@gmail.com>
+Dax Kelson <dkelson@gurulabs.com>
+Debashish Samantaray <d.samantaray@samsung.com>
+Debug Wang <debugwang@tencent.com>
+Deepak Dilip Borade <deepak.db@samsung.com>
+Deepak Mittal <deepak.m1@samsung.com>
+Deepak Sharma <deepak.sharma@amd.com>
+Deepak Singla <deepak.s@samsung.com>
+Deokjin Kim <deokjin81.kim@samsung.com>
+Derek Halman <d.halman@gmail.com>
+Devlin Cronin <rdevlin.cronin@gmail.com>
+Diana Suvorova <diana.suvorova@gmail.com>
+Diego Ferreiro Val <elfogris@gmail.com>
+Dillon Sellars <dill.sellars@gmail.com>
+Divya Bansal <divya.bansal@samsung.com>
+Dominic Farolino <domfarolino@gmail.com>
+Dominic Jodoin <dominic.jodoin@gmail.com>
+Dominik Röttsches <dominik.rottsches@intel.com>
+Don Woodward <woodward@adobe.com>
+Donghee Na <corona10@gmail.com>
+Dong-hee Na <donghee.na92@gmail.com>
+Dongie Agnir <dongie.agnir@gmail.com>
+Dongjun Kim <djmix.kim@samsung.com>
+Dongseong Hwang <dongseong.hwang@intel.com>
+Dongwoo Joshua Im <dw.im@samsung.com>
+Dongyu Lin <l2d4y3@gmail.com>
+Donna Wu <donna.wu@intel.com>
+Douglas F. Turner <doug.turner@gmail.com>
+Dustin Doloff <doloffd@amazon.com>
+Ebrahim Byagowi <ebrahim@gnu.org>
+Ebrahim Byagowi <ebraminio@gmail.com>
+Eden Wang <nedenwang@tencent.com>
+Eduardo Lima (Etrunko) <eblima@gmail.com>
+Eduardo Lima (Etrunko) <eduardo.lima@intel.com>
+Edward Baker <edward.baker@intel.com>
+Edward Crossman <tedoc2000@gmail.com>
+Eero Häkkinen <e.hakkinen@samsung.com>
+Eero Häkkinen <eero.hakkinen@intel.com>
+Egor Starkov <egor.starkov@samsung.com>
+Ehsan Akhgari <ehsan.akhgari@gmail.com>
+Elan Ruusamäe <elan.ruusamae@gmail.com>
+Ergun Erdogmus <erdogmusergun@gmail.com>
+Eric Ahn <byungwook.ahn@gmail.com>
+Eric Rescorla <ekr@rtfm.com>
+Erik Hill <erikghill@gmail.com>
+Erik Sjölund <erik.sjolund@gmail.com>
+Eriq Augustine <eriq.augustine@gmail.com>
+Ernesto Mudu <ernesto.mudu@gmail.com>
+Etienne Laurin <etienne@atnnn.com>
+Eugene Kim <eugene70kim@gmail.com>
+Eugene Sudin <eugene@sudin.pro>
+Eunseok Oh <fivesilverstone@gmail.com>
+Evan Peterson <evan.peterson.ep@gmail.com>
+Evan Wallace <evan.exe@gmail.com>
+Evangelos Foutras <evangelos@foutrelis.com>
+Evgeniy Dushistov <dushistov@gmail.com>
+Evgeny Agafonchikov <evgeny.agafonchikov@akvelon.com>
+Fabien Tassin <fta@sofaraway.org>
+Felix H. Dahlke <fhd@ubercode.de>
+Fengrong Fang <fr.fang@samsung.com>
+Fernando Jiménez Moreno <ferjmoreno@gmail.com>
+Finbar Crago <finbar.crago@gmail.com>
+François Beaufort <beaufort.francois@gmail.com>
+Francois Kritzinger <francoisk777@gmail.com>
+Francois Rauch <leopardb@gmail.com>
+Frankie Dintino <fdintino@theatlantic.com>
+Franklin Ta <fta2012@gmail.com>
+Frédéric Jacob <frederic.jacob.78@gmail.com>
+Frédéric Wang <fred.wang@free.fr>
+Fu Junwei <junwei.fu@intel.com>
+Gabor Rapcsanyi <g.rapcsanyi@samsung.com>
+Gaetano Mendola <mendola@gmail.com>
+Gajendra N <gajendra.n@samsung.com>
+Gajendra Singh <wxjg68@motorola.com>
+Ganesh Borle <ganesh.borle@samsung.com>
+Gao Chun <chun.gao@intel.com>
+Gao Chun <gaochun.dev@gmail.com>
+Gaurav Dhol <gaurav.dhol@einfochips.com>
+Gautham Banasandra <gautham.bangalore@gmail.com>
+George Adams <geoada@amazon.com>
+George Joseph <kottackal.george@gmail.com>
+George Liaskos <geo.liaskos@gmail.com>
+Georgy Buranov <gburanov@gmail.com>
+Gergely Nagy <ngg@ngg.hu>
+Getulio Sánchez <valentin2507@gmail.com>
+Gideon Pyzer <gjpyzer@gmail.com>
+Giovanni Panaro <tsrwebgl@gmail.com>
+Girish Kumar M <mck.giri@samsung.com>
+Gitanshu Mehndiratta <g.mehndiratt@samsung.com>
+Giuseppe Iuculano <giuseppe@iuculano.it>
+Gnanasekar Somanathan <gnanasekar.s@samsung.com>
+Gordana Cmiljanovic <gordana.cmiljanovic@imgtec.com>
+Goutham Jagannatha <wrm364@motorola.com>
+Graham Yoakum <gyoakum@skobalt.com>
+Greg Visser <gregvis@gmail.com>
+Gregory Davis <gpdavis.chromium@gmail.com>
+Grzegorz Czajkowski <g.czajkowski@samsung.com>
+Guangzhen Li <guangzhen.li@intel.com>
+Gurpreet Kaur <k.gurpreet@samsung.com>
+Gustav Tiger <gustav.tiger@sonymobile.com>
+Gyuyoung Kim <gyuyoung.kim@navercorp.com>
+Gzob Qq <gzobqq@gmail.com>
+Habib Virji <habib.virji@samsung.com>
+Haeun Kim <ggrace.kim93@gmail.com>
+Haeun Kim <haeungun@gmail.com>
+Haitao Feng <haitao.feng@intel.com>
+Halley Zhao <halley.zhao@intel.com>
+Halton Huo <halton.huo@gmail.com>
+Halton Huo <halton.huo@intel.com>
+Hans Hillen <hans.hillen@gmail.com>
+Hao Li <hao.x.li@intel.com>
+Haojian Wu <hokein.wu@gmail.com>
+Hari Singh <hari.singh1@samsung.com>
+Harpreet Singh Khurana <harpreet.sk@samsung.com>
+Harshikesh Kumar <harshikeshnobug@gmail.com>
+Hassan Salehe Matar <hassansalehe@gmail.com>
+Hautio Kari <khautio@gmail.com>
+Heejin R. Chung <heejin.r.chung@samsung.com>
+Heeyoun Lee <heeyoun.lee@samsung.com>
+Henrique Limas <henrique.ramos.limas@gmail.com>
+Himanshu Joshi <h.joshi@samsung.com>
+Holger Kraus <kraush@amazon.com>
+Hong Zheng <hong.zheng@intel.com>
+Hongbo Min <hongbo.min@intel.com>
+Horia Olaru <horia.olaru@gmail.com>
+Horia Olaru <olaru@adobe.com>
+Hosung You <hosung.you@samsung.com>
+Huapeng Li <huapengl@amazon.com>
+Huayong Xu <huayong.xu@samsung.com>
+Hugo Holgersson <hugo.holgersson@sonymobile.com>
+Hui Wang <wanghui07050707@gmail.com>
+Huiwon Jo <jhwon0415@gmail.com>
+Huy Duong <huy.duongdinh@gmail.com>
+Hwanseung Lee <hs1217.lee@gmail.com>
+Hwanseung Lee <hs1217.lee@samsung.com>
+Hyemi Shin <hyemi.sin@samsung.com>
+HyeockJin Kim <kherootz@gmail.com>
+Hyungchan Kim <inlinechan@gmail.com>
+Hyungwook Lee <hyungwook.lee@navercorp.com>
+Hyungwook Lee <withlhw@gmail.com>
+Hyunjun Shin <hyunjun.shin2@navercorp.com>
+Hyunjune Kim <hyunjune.kim@samsung.com>
+Hyunki Baik <hyunki.baik@samsung.com>
+Ian Cullinan <cullinan@amazon.com>
+Ian Scott <ian.scott@arteris.com>
+Ibrar Ahmed <ibrar.ahmad@gmail.com>
+Ilia Demianenko <ilia.demianenko@gmail.com>
+Ilia K <ki.stfu@gmail.com>
+Ilya Konstantinov <ilya.konstantinov@gmail.com>
+Imranur Rahman <ir.shimul@gmail.com>
+Ion Rosca <rosca@adobe.com>
+Irmak Kavasoglu <irmakkavasoglu@gmail.com>
+Isaac Murchie <murchieisaac@gmail.com>
+Isaac Reilly <reillyi@amazon.com>
+Ivan Naydonov <samogot@gmail.com>
+Ivan Sham <ivansham@amazon.com>
+Jack Bates <jack@nottheoilrig.com>
+Jacob Clark <jacob.jh.clark@googlemail.com>
+Jacob Mandelson <jacob@mandelson.org>
+Jaehun Lim <ljaehun.lim@samsung.com>
+Jaehyun Lee <j-hyun.lee@samsung.com>
+Jaekyeom Kim <btapiz@gmail.com>
+Jaemin Seo <jaemin86.seo@samsung.com>
+Jaeseok Yoon <yjaeseok@gmail.com>
+Jaewon Choi <jaewon.james.choi@gmail.com>
+Jaeyong Bae <jdragon.bae@gmail.com>
+Jaime Soriano Pastor <jsorianopastor@gmail.com>
+Jake Helfert <jake@helfert.us>
+Jake Hendy <me@jakehendy.com>
+Jakob Weigert <jakob.j.w@googlemail.com>
+Jakub Machacek <xtreit@gmail.com>
+James Burton <jb@0.me.uk>
+James Choi <jchoi42@pha.jhu.edu>
+James Stanley <james@apphaus.co.uk>
+James Vega <vega.james@gmail.com>
+James Wei <james.wei@intel.com>
+James Willcox <jwillcox@litl.com>
+Jan Rucka <ruckajan10@gmail.com>
+Jan Sauer <jan@jansauer.de>
+Janwar Dinata <j.dinata@gmail.com>
+Jared Shumway <jaredshumway94@gmail.com>
+Jared Sohn <jared.sohn@gmail.com>
+Jared Wein <weinjared@gmail.com>
+Jari Karppanen <jkarp@amazon.com>
+Jay Oster <jay@kodewerx.org>
+Jay Soffian <jaysoffian@gmail.com>
+Jeado Ko <haibane84@gmail.com>
+Jeffrey C <jeffreyca16@gmail.com>
+Jeongeun Kim <je_julie.kim@samsung.com>
+Jeongmin Kim <kimwjdalsl@gmail.com>
+Jeongwoo Park <skeksk91@gmail.com>
+Jeremy Noring <jnoring@hirevue.com>
+Jeremy Spiegel <jeremysspiegel@gmail.com>
+Jeroen Van den Berghe <vandenberghe.jeroen@gmail.com>
+Jerry Lin <wahahab11@gmail.com>
+Jesper Storm Bache <jsbache@gmail.com>
+Jesse Miller <jesse@jmiller.biz>
+Jesus Sanchez-Palencia <jesus.sanchez-palencia.fernandez.fil@intel.com>
+Jiadong Zhu <jiadong.zhu@linaro.org>
+Jiajia Qin <jiajia.qin@intel.com>
+Jiajie Hu <jiajie.hu@intel.com>
+Jianjun Zhu <jianjun.zhu@intel.com>
+Jianneng Zhong <muzuiget@gmail.com>
+Jiawei Shao <jiawei.shao@intel.com>
+Jie Chen <jie.a.chen@intel.com>
+Jihoon Chung <j.c@navercorp.com>
+Jihoon Chung <jihoon@gmail.com>
+Jihun Brent Kim <devgrapher@gmail.com>
+Jihwan Marc Kim <bluewhale.marc@gmail.com>
+Jin Yang <jin.a.yang@intel.com>
+Jincheol Jo <jincheol.jo@navercorp.com>
+Jinfeng Ma <majinfeng1@xiaomi.com>
+Jing Zhao <zhaojing7@xiaomi.com>
+Jinglong Zuo <zuojinglong@xiaomi.com>
+Jingwei Liu <kingweiliu@gmail.com>
+Jingyi Wei <wjywbs@gmail.com>
+Jinho Bang <jinho.bang@samsung.com>
+Jinsong Fan <fanjinsong@sogou-inc.com>
+Jinwoo Song <jinwoo7.song@samsung.com>
+Jinyoung Hur <hurims@gmail.com>
+Jitendra Kumar Sahoo <jitendra.ks@samsung.com>
+Joachim Bauch <jbauch@webrtc.org>
+Joachim Bauch <mail@joachim-bauch.de>
+Joanmarie Diggs <joanmarie.diggs@gmail.com>
+Joe Knoll <joe.knoll@workday.com>
+Joe Thomas <mhx348@motorola.com>
+Joel Stanley <joel@jms.id.au>
+Johannes Rudolph <johannes.rudolph@googlemail.com>
+John Kleinschmidt <kleinschmidtorama@gmail.com>
+John Yani <vanuan@gmail.com>
+John Yoo <nearbyh13@gmail.com>
+Johnson Lin <johnson.lin@intel.com>
+Jonathan Frazer <listedegarde@gmail.com>
+Jonathan Garbee <jonathan@garbee.me>
+Jonathan Hacker <jhacker@arcanefour.com>
+Jongdeok Kim <jongdeok.kim@navercorp.com>
+Jongheon Kim <sapzape@gmail.com>
+JongKwon Lee <jongkwon.lee@navercorp.com>
+Jongsoo Lee <leejongsoo@gmail.com>
+Joone Hur <joone.hur@intel.com>
+Joonghun Park <pjh0718@gmail.com>
+Jorge Villatoro <jorge@tomatocannon.com>
+Joseph Gentle <josephg@gmail.com>
+Joseph Lolak <joseph.lolak@samsung.com>
+Josh Triplett <josh.triplett@intel.com>
+Josh Triplett <josh@joshtriplett.org>
+Joshua Lock <joshua.lock@intel.com>
+Joshua Roesslein <jroesslein@gmail.com>
+Josué Ratelle <jorat1346@gmail.com>
+Josyula Venkat Narasimham <venkat.nj@samsung.com>
+Juan Jose Lopez Jaimez <jj.lopezjaimez@gmail.com>
+Juhui Lee <juhui24.lee@samsung.com>
+Julien Brianceau <jbriance@cisco.com>
+Julien Isorce <j.isorce@samsung.com>
+Julien Racle <jracle@logitech.com>
+Jun Fang <jun_fang@foxitsoftware.com>
+Jun Jiang <jun.a.jiang@intel.com>
+Junchao Han <junchao.han@intel.com>
+Junghoon Lee <sjh836@gmail.com>
+Junghyuk Yoo <wjdgurdl272@gmail.com>
+JungJik Lee <jungjik.lee@samsung.com>
+Jungkee Song <jungkee.song@samsung.com>
+Junmin Zhu <junmin.zhu@intel.com>
+Justin Okamoto <justmoto@amazon.com>
+Justin Ribeiro <justin@justinribeiro.com>
+Jüri Valdmann <juri.valdmann@qt.io>
+Kai Jiang <jiangkai@gmail.com>
+Kai Köhne <kai.koehne@qt.io>
+Kai Uwe Broulik <kde@privat.broulik.de>
+Kal Conley <kcconley@gmail.com>
+Kalyan Kondapally <kalyan.kondapally@intel.com>
+Kamil Jiwa <kamil.jiwa@gmail.com>
+Kamil Rytarowski <krytarowski@gmail.com>
+Kangil Han <kangil.han@samsung.com>
+Kangyuan Shu <kangyuan.shu@intel.com>
+Karan Thakkar <karanjthakkar@gmail.com>
+Kartikey Bhatt <kartikey@amazon.com>
+Kaspar Brand <googlecontrib@velox.ch>
+Kaustubh Atrawalkar <kaustubh.a@samsung.com>
+Kaustubh Atrawalkar <kaustubh.ra@gmail.com>
+Ke He <ke.he@intel.com>
+Keene Pan <keenepan@linpus.com>
+Keita Yoshimoto <y073k3@gmail.com>
+Keith Chen <keitchen@amazon.com>
+Kenneth Rohde Christiansen <kenneth.r.christiansen@intel.com>
+Kenneth Strickland <ken.strickland@gmail.com>
+Kenneth Zhou <knthzh@gmail.com>
+Keonho Kim <keonho07.kim@samsung.com>
+Ketan Goyal <ketan.goyal@samsung.com>
+Kevin Gibbons <bakkot@gmail.com>
+Kevin Lee Helpingstine <sig11@reprehensible.net>
+Kevin M. McCormick <mckev@amazon.com>
+Khasim Syed Mohammed <khasim.mohammed@linaro.org>
+Kihong Kwon <kihong.kwon@samsung.com>
+Kihoon Ko <rhrlgns777@gmail.com>
+Kihwang Kim <pwangkk@gmail.com>
+Kim Christensen <kimworking@gmail.com>
+Kimberly Hunter <kimberhu@amazon.com>
+Kingshuk Jana <kingshuk.j@samsung.com>
+Kirill Bobyrev <kirillbobyrev@gmail.com>
+Kirill Ovchinnikov <kirill.ovchinn@gmail.com>
+Klemen Forstnerič <klemen.forstneric@gmail.com>
+Kodam Nagaraju <k2.nagaraju@samsung.com>
+Konrad Dzwinel <kdzwinel@gmail.com>
+Krishna Chaitanya <krish.botta@samsung.com>
+Kristof Kosztyo <kkosztyo.u-szeged@partner.samsung.com>
+Krzysztof Czech <k.czech@samsung.com>
+Krzysztof Wolanski <k.wolanski@samsung.com>
+Kui Tan <tk1061178@gmail.com>
+Kunal Thakar <kunalt@gmail.com>
+Kushal Pisavadia <kushi.p@gmail.com>
+Kwangho Shin <k_h.shin@samsung.com>
+Kyle Nahrgang <kpn24@drexel.edu>
+Kyle Plumadore <kyle.plumadore@amd.com>
+Kyounga Ra <kyounga.ra@gmail.com>
+Kyoungdeok Kwon <kkd927@gmail.com>
+Kyung Yeol Kim <chitacan@gmail.com>
+Kyungtae Kim <ktf.kim@samsung.com>
+Kyungyoung Heo <bbvch13531@gmail.com>
+Lalit Chandivade <lalit.chandivade@einfochips.com>
+Laszlo Gombos <l.gombos@samsung.com>
+Laszlo Radanyi <bekkra@gmail.com>
+Lauren Yeun Kim <lauren.yeun.kim@gmail.com>
+Lauri Oherd <lauri.oherd@gmail.com>
+Lavar Askew <open.hyperion@gmail.com>
+Legend Lee <guanxian.li@intel.com>
+Leith Bade <leith@leithalweapon.geek.nz>
+Lenny Khazan <lenny.khazan@gmail.com>
+Leo Wolf <jclw@ymail.com>
+Leon Han <leon.han@intel.com>
+Leung Wing Chung <lwchkg@gmail.com>
+Li Yin <li.yin@intel.com>
+Lidwine Genevet <lgenevet@cisco.com>
+Lin Sun <lin.sun@intel.com>
+Lingyun Cai <lingyun.cai@intel.com>
+Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+Lizhi Fan <lizhi.fan@samsung.com>
+Loo Rong Jie <loorongjie@gmail.com>
+Lorenzo Stoakes <lstoakes@gmail.com>
+Lu Guanqun <guanqun.lu@gmail.com>
+Luca Di Domenico <luca94dd@gmail.com>
+Lucie Brozkova <lucinka.brozkova@gmail.com>
+Luiz Von Dentz <luiz.von.dentz@intel.com>
+Luka Dojcilovic <l.dojcilovic@gmail.com>
+Luke Inman-Semerau <luke.semerau@gmail.com>
+Luke Zarko <lukezarko@gmail.com>
+Luoxi Pan <l.panpax@gmail.com>
+Maarten Lankhorst <m.b.lankhorst@gmail.com>
+Magnus Danielsson <fuzzac@gmail.com>
+Mahesh Kulkarni <mahesh.kk@samsung.com>
+Mahesh Machavolu <mahesh.ma@samsung.com>
+Maksim Kolesin <mkolesin@gmail.com>
+Maksim Sisov <maksim.sisov@intel.com>
+Malcolm Wang <malcolm.2.wang@gmail.com>
+Mallikarjuna Rao V <vm.arjun@samsung.com>
+Manish Chhajer <chhajer.m@samsung.com>
+Manish Jethani <m.jethani@eyeo.com>
+Manojkumar Bhosale <manojkumar.bhosale@imgtec.com>
+Manuel Braun <thembrown@gmail.com>
+Mao Yujie <maojie0924@gmail.com>
+Mao Yujie <yujie.mao@intel.com>
+Marc des Garets <marc.desgarets@googlemail.com>
+Marcin Wiacek <marcin@mwiacek.com>
+Marco Rodrigues <gothicx@gmail.com>
+Mario Pistrich <m.pistrich@gmail.com>
+Mario Sanchez Prada <mario.prada@samsung.com>
+Mariusz Mlynski <marius.mlynski@gmail.com>
+Mark Hahnenberg <mhahnenb@andrew.cmu.edu>
+Mark Seaborn <mrs@mythic-beasts.com>
+Martijn Croonen <martijn@martijnc.be>
+Martin Bednorz <m.s.bednorz@gmail.com>
+Martin Rogalla <martin@martinrogalla.com>
+Martina Kollarova <martina.kollarova@intel.com>
+Masahiro Yado <yado.masa@gmail.com>
+Masaru Nishida <msr.i386@gmail.com>
+Matej Knopp <matej.knopp@gmail.com>
+Matheus Bratfisch <matheusbrat@gmail.com>
+Mathias Bynens <mathias@qiwi.be>
+Mathieu Meisser <mmeisser@logitech.com>
+Matt Arpidone <mma.public@gmail.com>
+Matt Strum <mstrum@amazon.com>
+Matt Zeunert <matt@mostlystatic.com>
+Matthew Bauer <mjbauer95@gmail.com>
+Matthew Demarest <demarem@amazon.com>
+Matthew Robertson <matthewrobertson03@gmail.com>
+Matthew Turk <matthewturk@gmail.com>
+Matthew Willis <appamatto@gmail.com>
+Matthias Reitinger <reimarvin@gmail.com>
+Matthieu Rigolot <matthieu.rigolot@gmail.com>
+Max Perepelitsyn <pph34r@gmail.com>
+Max Vujovic <mvujovic@adobe.com>
+Mayank Gupta <mayank.g1@samsung.com>
+Mayur Kankanwadi <mayurk.vk@samsung.com>
+Md Abdullah Al Alamin <a.alamin.cse@gmail.com>
+Md. Hasanur Rashid <hasanur.r@samsung.com>
+Md Jobed Hossain <jrony15@gmail.com>
+Md Sami Uddin <md.sami@samsung.com>
+Michael Cirone <mikecirone@gmail.com>
+Michael Gilbert <floppymaster@gmail.com>
+Michael Lopez <lopes92290@gmail.com>
+Michael Morrison <codebythepound@gmail.com>
+Michael Müller <michael@fds-team.de>
+Michael Schechter <mike.schechter@gmail.com>
+Michaël Zasso <mic.besace@gmail.com>
+Michael Zugelder <michael@zugelder.org>
+Michel Promonet <michel.promonet.1@gmail.com>
+Mihai Maerean <mmaerean@adobe.com>
+Mihai Tica <mihai.o.tica@gmail.com>
+Mihai Tica <mitica@adobe.com>
+Mike Pennisi <mike@mikepennisi.com>
+Mike Tilburg <mtilburg@adobe.com>
+Mikhail Pozdnyakov <mikhail.pozdnyakov@intel.com>
+Milko Leporis <milko.leporis@imgtec.com>
+Milton Chiang <milton.chiang@mediatek.com>
+Minggang Wang <minggang.wang@intel.com>
+Mingmin Xie <melvinxie@gmail.com>
+Minjeong Lee <apenr1234@gmail.com>
+Minseok Koo <kei98301@gmail.com>
+Minsoo Max Koo <msu.koo@samsung.com>
+Miran Karic <miran.karic@imgtec.com>
+Mirela Budaes <mbudaes@adobe.com>
+Mirela Budaes <mbudaes@gmail.com>
+Miyoung Shin <myid.shin@navercorp.com>
+Mohamed I. Hammad <ibraaaa@gmail.com>
+Mohamed Mansour <m0.interactive@gmail.com>
+Mohammad Azam <m.azam@samsung.com>
+Mohammed Wajahat Ali Siddiqui <wajahat.s@samsung.com>
+Mohan Reddy <mohan.reddy@samsung.com>
+Mohit Bhalla <bhallam@amazon.com>
+Momoko Hattori <momohatt10@gmail.com>
+Mostafa Sedaghat joo <mostafa.sedaghat@gmail.com>
+Mrunal Kapade <mrunal.kapade@intel.com>
+Myeongjin Cho <myeongjin.cho@navercorp.com>
+Myles C. Maxfield <mymax@amazon.com>
+Myung-jong Kim <mjkim610@gmail.com>
+Nagarajan Narayanan <nagarajan.n@samsung.com>
+Nagarjuna Atluri <nagarjuna.a@samsung.com>
+Naiem Shaik <naiem.shaik@gmail.com>
+Naoki Takano <takano.naoki@gmail.com>
+Naveen Bobbili <naveenbobbili@motorola.com>
+Naveen Bobbili <qghc36@motorola.com>
+Naveen Kumar Devaraj <devarajn@amazon.com>
+Naveen Kumar S G <naveensg@samsung.com>
+Nayan Kumar K <qtc746@motorola.com>
+Neal Gompa <ngompa13@gmail.com>
+Ned Williamson <nedwilliamson@gmail.com>
+Nedeljko Babic <nedeljko.babic@imgtec.com>
+Nikhil Bansal <n.bansal@samsung.com>
+Nikhil Sahni <nikhil.sahni@samsung.com>
+Nikita Ofitserov <himikof@gmail.com>
+Niklas Hambüchen <mail@nh2.me>
+Niklas Schulze <me@jns.io>
+Nikola Kovacs <nikola.kovacs@gmail.com>
+Nils Schneider <nils.schneider@gmail.com>
+Nils Schneider <nils@nilsschneider.net>
+Ningxin Hu <ningxin.hu@intel.com>
+Nitish Mehrotra <nitish.m@samsung.com>
+Noj Vek <nojvek@gmail.com>
+Nolan Cao <nolan.robin.cao@gmail.com>
+Oleksii Kadurin <ovkadurin@gmail.com>
+Oliver Dunk <oliver@oliverdunk.com>
+Olli Raula (Old name Olli Syrjälä) <olli.raula@intel.com>
+Omar Sandoval <osandov@osandov.com>
+Pan Deng <pan.deng@intel.com>
+Parag Radke <nrqv63@motorola.com>
+Paritosh Kumar <paritosh.in@samsung.com>
+Patrasciuc Sorin Cristian <cristian.patrasciuc@gmail.com>
+Patrick Chan <chanpatorikku@gmail.com>
+Patrick Kettner <patrickkettner@gmail.com>
+Patrick Riordan <patrickriordan177@gmail.com>
+Patrick Stein <patrickwonders@gmail.com>
+Patrik Ackland <patrikackland@gmail.com>
+Paul Adolph <padolph@netflix.com>
+Paul Kehrer <paul.l.kehrer@gmail.com>
+Paul Lind <paul.lind@imgtec.com>
+Paul Nettleship <pnettleship@gmail.com>
+Paul Robinson <paulrobinson85@googlemail.com>
+Paul Roskell <blurrech@gmail.com>
+Paul Sapunaru <paul.sapunaru@intel.com>
+Paul Wicks <pwicks86@gmail.com>
+Pavan Kumar Emani <pavan.e@samsung.com>
+Pavel Golikov <paullo612@ya.ru>
+Pavel Ivanov <paivanof@gmail.com>
+Pawel Forysiuk <p.forysiuk@samsung.com>
+Paweł Hajdan jr <phajdan.jr@gmail.com>
+Payal Pandey <payal.pandey@samsung.com>
+Peng Hu <penghu@tencent.com>
+Peng Jiang <leiyi.jp@gmail.com>
+Peng Xinchao <pxinchao@gmail.com>
+Peter Bright <drpizza@quiscalusmexicanus.org>
+Peter Brophy <pbrophy@adobe.com>
+Peter Collingbourne <peter@pcc.me.uk>
+Peter Gal <pgal.u-szeged@partner.samsung.com>
+Peter Griffin <peter.griffin@linaro.org>
+Peter Molnar <pmolnar.u-szeged@partner.samsung.com>
+Peter Snyder <snyderp@gmail.com>
+Peter Wong <peter.wm.wong@gmail.com>
+Philip Hanson <philip.hanson@intel.com>
+Philipp Hancke <fippo@andyet.net>
+Philipp Hancke <philipp.hancke@googlemail.com>
+Philippe Beauchamp <philippe.beauchamp@gmail.com>
+Philippe Beaudoin <philippe.beaudoin@gmail.com>
+PhistucK <phistuck@gmail.com>
+Pierre Neter <pierreneter@gmail.com>
+Pierre-Antoine LaFayette <pierre.lafayette@gmail.com>
+Po-Chun Chang <pochang0403@gmail.com>
+Pramod Begur Srinath <pramod.bs@samsung.com>
+Pranay Kumar <pranay.kumar@samsung.com>
+Pranjal Jumde <pranjal@brave.com>
+Prashant Hiremath <prashhir@cisco.com>
+Prashant Nevase <prashant.n@samsung.com>
+Prashant Patil <prashant.patil@imgtec.com>
+Praveen Akkiraju <praveen.anp@samsung.com>
+Preeti Nayak <preeti.nayak@samsung.com>
+Pritam Nikam <pritam.nikam@samsung.com>
+Puttaraju R <puttaraju.r@samsung.com>
+Qi Yang <qi1988.yang@samsung.com>
+Qiankun Miao <qiankun.miao@intel.com>
+Qing Zhang <qing.zhang@intel.com>
+Radu Stavila <stavila@adobe.com>
+Radu Velea <radu.velea@intel.com>
+Rafael Antognolli <rafael.antognolli@intel.com>
+Raghavendra Ghatage <r.ghatage@samsung.com>
+Raghu Ram Nagaraj <r.nagaraj@samsung.com>
+Rahul Gupta <rahul.g@samsung.com>
+Rajneesh Rana <rajneesh.r@samsung.com>
+Raman Tenneti <raman.tenneti@gmail.com>
+Ramkumar Gokarnesan <ramkumar.gokarnesan@gmail.com>
+Ramkumar Ramachandra <artagnon@gmail.com>
+Ramya Vadlamudi <ramya.v@samsung.com>
+Randy Posynick <randy.posynick@gmail.com>
+Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
+Raul Tambre <raul@tambre.ee>
+Raveendra Karu <r.karu@samsung.com>
+Ravi Nanjundappa <nravi.n@samsung.com>
+Ravi Phaneendra Kasibhatla <r.kasibhatla@samsung.com>
+Ravi Phaneendra Kasibhatla <ravi.kasibhatla@motorola.com>
+Raviraj Sitaram <raviraj.p.sitaram@intel.com>
+Réda Housni Alaoui <alaoui.rda@gmail.com>
+Refael Ackermann <refack@gmail.com>
+Renata Hodovan <rhodovan.u-szeged@partner.samsung.com>
+Rene Bolldorf <rb@radix.io>
+Rene Ladan <r.c.ladan@gmail.com>
+Richard Baranyi <lordprotector@gmail.com>
+Richard Li <richard.li@intel.com>
+Rijubrata Bhaumik <rijubrata.bhaumik@intel.com>
+Riku Voipio <riku.voipio@linaro.org>
+Rob Buis <rob.buis@samsung.com>
+Rob Wu <rob@robwu.nl>
+Robert Bear Travis <bear.travis@gmail.com>
+Robert Bear Travis <betravis@adobe.com>
+Robert Bradford <robert.bradford@intel.com>
+Robert Goldberg <goldberg@adobe.com>
+Robert Hogan <robhogan@gmail.com>
+Robert Nagy <robert.nagy@gmail.com>
+Robert Sesek <rsesek@bluestatic.org>
+Roland Takacs <rtakacs.u-szeged@partner.samsung.com>
+Romain Pokrzywka <romain.pokrzywka@gmail.com>
+Rosen Dash <nqk836@motorola.com>
+Rosen Dash <rosen.dash@gmail.com>
+Ross Kirsling <rkirsling@gmail.com>
+ruben <chromium@hybridsource.org>
+Ruben Bridgewater <ruben@bridgewater.de>
+Ruben Terrazas <rubentopo@gmail.com>
+Rufus Hamade <rufus.hamade@imgtec.com>
+Ruiyi Luo <luoruiyi2008@gmail.com>
+Ryan Ackley <ryanackley@gmail.com>
+Ryan Norton <rnorton10@gmail.com>
+Ryan Sleevi <ryan-chromium-dev@sleevi.com>
+Ryan Yoakum <ryoakum@skobalt.com>
+Ryuan Choi <ryuan.choi@samsung.com>
+Saikrishna Arcot <saiarcot895@gmail.com>
+Sajal Khandelwal <skhandelwa22@bloomberg.net>
+Salvatore Iovene <salvatore.iovene@intel.com>
+Sam Larison <qufighter@gmail.com>
+Sam McDonald <sam@sammcd.com>
+Samuel Attard <samuel.r.attard@gmail.com>
+Sanggi Hong <sanggi.hong11@gmail.com>
+Sanghee Lee <sanghee.lee1992@gmail.com>
+Sanghyun Park <sh919.park@samsung.com>
+Sanghyup Lee <sh53.lee@samsung.com>
+Sangjoon Je <htamop@gmail.com>
+Sangseok Jang <sangseok.jang@navercorp.com>
+Sangwoo Ko <sangwoo.ko@navercorp.com>
+Sangwoo Ko <sangwoo108@gmail.com>
+Sanjoy Pal <ncj674@motorola.com>
+Sanjoy Pal <sanjoy.pal@samsung.com>
+Sanne Wouda <sanne.wouda@gmail.com>
+Santosh Mahto <samahto@cisco.com>
+Sarath Singapati <s.singapati@gmail.com>
+Sarath Singapati <s.singapati@samsung.com>
+Sarath Singapati <sarath.singapati@huawei.com>
+Saravanan KR <sramajay@cisco.com>
+Sathish Kuppuswamy <sathish.kuppuswamy@intel.com>
+Satoshi Matsuzaki <satoshi.matsuzaki@gmail.com>
+Satyajit Sahu <satyajit.sahu@amd.com>
+Sayan Nayak <sayan.nayak@samsung.com>
+Scott D Phillips <scott.d.phillips@intel.com>
+Sean Bryant <sean@cyberwang.net>
+Sean DuBois <seaduboi@amazon.com>
+Sebastian Amend <sebastian.amend@googlemail.com>
+Sebastian Krzyszkowiak <dos@dosowisko.net>
+Seo Sanghyeon <sanxiyn@gmail.com>
+Seokju Kwon <seokju.kwon@gmail.com>
+SeongTae Jeong <ferendevelop.gl@gmail.com>
+Sergey Kipet <sergey.kipet@gmail.com>
+Sergey Putilin <p.sergey@samsung.com>
+Sergey Shekyan <shekyan@gmail.com>
+Sergio Carlos Morales Angeles <carloschilazo@gmail.com>
+Sergiy Belozorov <rryk.ua@gmail.com>
+Seshadri Mahalingam <seshadri.mahalingam@gmail.com>
+Seungkyu Lee <zx6658@gmail.com>
+Sevan Janiyan <venture37@geeklan.co.uk>
+Shahriar Rostami <shahriar.rostami@gmail.com>
+Shail Singhal <shail.s@samsung.com>
+Shane Hansen <shanemhansen@gmail.com>
+ShankarGanesh K <blr.bmlab@gmail.com>
+Shanmuga Pandi M <shanmuga.m@samsung.com>
+Shaobo Yan <shaobo.yan@intel.com>
+Shashi Kumar <sk.kumar@samsung.com>
+Shawn Anastasio <shawnanastasio@gmail.com>
+Shelley Vohr <shelley.vohr@gmail.com>
+Shen Yu <shenyu.tcv@gmail.com>
+Sherry Mou <wenjinm@amazon.com>
+Shez Baig <sbaig1@bloomberg.net>
+Shigeki Ohtsu <shigeki.ohtsu@gmail.com>
+Shiliu Wang <aofdwsl@gmail.com>
+Shiliu Wang <shiliu.wang@intel.com>
+Shilpa Shri <shilpa.shri@samsung.com>
+Shirish S <shirish.s@amd.com>
+Shiva Kumar <shiva.k1@samsung.com>
+Shivakumar JM <shiva.jm@samsung.com>
+Shouqun Liu <liushouqun@xiaomi.com>
+Shouqun Liu <shouqun.liu@intel.com>
+Shreeram Kushwaha <shreeram.k@samsung.com>
+Shreyas Gopal <shreyas.g@samsung.com>
+Shreyas VA <v.a.shreyas@gmail.com>
+Shubham Agrawal <shubag@amazon.com>
+Siba Samal <siba.samal@samsung.com>
+Siddharth Bagai <b.siddharth@samsung.com>
+Siddharth Shankar <funkysidd@gmail.com>
+Simon Arlott <simon.arlott@gmail.com>
+Simon La Macchia <smacchia@amazon.com>
+Siva Kumar Gunturi <siva.gunturi@samsung.com>
+Sohan Jyoti Ghosh <sohan.jyoti@huawei.com>
+Sohan Jyoti Ghosh <sohan.jyoti@samsung.com>
+Song YeWen <ffmpeg@gmail.com>
+Sooho Park <sooho1000@gmail.com>
+Soojung Choi <crystal2840@gmail.com>
+Soorya R <soorya.r@samsung.com>
+Soren Dreijer <dreijerbit@gmail.com>
+Sreerenj Balachandran <sreerenj.balachandran@intel.com>
+Srirama Chandra Sekhar Mogali <srirama.m@samsung.com>
+Staphany Park <stapark008@gmail.com>
+Stephen Searles <stephen.searles@gmail.com>
+Steve Sanders <steve@zanderz.com>
+Steven Pennington <spenn@engr.uvic.ca>
+Steven Roussey <sroussey@gmail.com>
+Subrahmanya Praveen Munukutla <sataya.m@samsung.com>
+Suchit Agrawal <a.suchit@samsung.com>
+Sudarsana Babu Nagineni <sudarsana.nagineni@intel.com>
+Sudarshan Parthasarathy <sudarshan.p@samsung.com>
+Sujae Jo <sujae33.jo@gmail.com>
+Sujith S S <sujiths.s@samsung.com>
+Sunchang Li <johnstonli@tencent.com>
+Suneel Kota <suneel.kota@samsung.com>
+Sungguk Lim <limasdf@gmail.com>
+Sungmann Cho <sungmann.cho@gmail.com>
+Sungmann Cho <sungmann.cho@navercorp.com>
+Sunil Ratnu <sunil.ratnu@samsung.com>
+Sunitha Srivatsa <srivats@amazon.com>
+Suvanjan Mukherjee <suvanjanmukherjee@gmail.com>
+Suyambulingam R M <suyambu.rm@samsung.com>
+Suyash Sengar <suyash.s@samsung.com>
+Swarali Raut <swarali.sr@samsung.com>
+Swati Jaiswal <swa.jaiswal@samsung.com>
+Sylvain Zimmer <sylvinus@gmail.com>
+Sylvestre Ledru <sylvestre.ledru@gmail.com>
+Synthia Islam <synthia.is@samsung.com>
+Szabolcs David <davidsz@inf.u-szeged.hu>
+Szymon Piechowicz <szymonpiechowicz@o2.pl>
+Taeheon Kim <skyrabbits1@gmail.com>
+Taehoon Lee <taylor.hoon@gmail.com>
+Takashi Fujita <tgfjt.mail@gmail.com>
+Takeshi Kurosawa <taken.spc@gmail.com>
+Tanay Chowdhury <tanay.c@samsung.com>
+Tanvir Rizvi <tanvir.rizvi@samsung.com>
+Tapu Kumar Ghose <ghose.tapu@gmail.com>
+Taylor Price <trprice@gmail.com>
+Ted Kim <neot0000@gmail.com>
+Ted Vessenes <tedvessenes@gmail.com>
+Teodora Novkovic <teodora.petrovic@gmail.com>
+Thiago Farina <thiago.farina@gmail.com>
+Thiago Marcos P. Santos <thiago.santos@intel.com>
+Thomas Butter <tbutter@gmail.com>
+Thomas Conti <tomc@amazon.com>
+Thomas White <im.toms.inbox@gmail.com>
+Tiago Vignatti <tiago.vignatti@intel.com>
+Tibor Dusnoki <tibor.dusnoki.91@gmail.com>
+Tim Ansell <mithro@mithis.com>
+Tim Niederhausen <tim@rnc-ag.de>
+Timo Gurr <timo.gurr@gmail.com>
+Timo Reimann <ttr314@googlemail.com>
+Timo Witte <timo.witte@gmail.com>
+Ting Shao <ting.shao@intel.com>
+Tom Callaway <tcallawa@redhat.com>
+Tom Harwood <tfh@skip.org>
+Tomas Popela <tomas.popela@gmail.com>
+Torsten Kurbad <google@tk-webart.de>
+Trent Willis <trentmwillis@gmail.com>
+Trevor Perrin <unsafe@trevp.net>
+Tripta Gupta <tripta.g@samsung.com>
+U. Artie Eoff <ullysses.a.eoff@intel.com>
+Umar Hansa <umar.hansa@gmail.com>
+Upendra Gowda <upendrag.gowda@gmail.com>
+Uzair Jaleel <uzair.jaleel@samsung.com>
+Vadim Gorbachev <bmsdave@gmail.com>
+Vaibhav Agrawal <vaibhav1.a@samsung.com>
+Valentin Ilie <valentin.ilie@intel.com>
+Vamshikrishna Yellenki <vamshi@motorola.com>
+Vani Hegde <vani.hegde@samsung.com>
+Varun Chowdhary Paturi <v.paturi@samsung.com>
+Vartul Katiyar <vartul.k@samsung.com>
+Vedran Šajatović <vedran.sajatovic@gmail.com>
+Vernon Tang <vt@foilhead.net>
+Viatcheslav Ostapenko <sl.ostapenko@samsung.com>
+Victor Costan <costan@gmail.com>
+Viet-Trung Luu <viettrungluu@gmail.com>
+Vinay Anantharaman <vinaya@adobe.com>
+Vipul Bhasin <vipul.bhasin@gmail.com>
+Visa Putkinen <v.putkinen@partner.samsung.com>
+Vishal Bhatnagar <vishal.b@samsung.com>
+Vitaliy Kharin <kvserr@gmail.com>
+Vivek Galatage <vivek.vg@samsung.com>
+Volker Sorge <volker.sorge@gmail.com>
+Waihung Fu <fufranci@amazon.com>
+Wanming Lin <wanming.lin@intel.com>
+Wei Li <wei.c.li@intel.com>
+WenSheng He <wensheng.he@samsung.com>
+Wesley Lancel <wesleylancel@gmail.com>
+Wesley Wigham <wwigham@gmail.com>
+Will Hirsch <chromium@willhirsch.co.uk>
+Will Shackleton <w.shackleton@gmail.com>
+William Xie <william.xie@intel.com>
+Xiang Long <xiang.long@intel.com>
+Xiangze Zhang <xiangze.zhang@intel.com>
+Xiaofeng Zhang <xiaofeng.zhang@intel.com>
+Xiaolei Yu <dreifachstein@gmail.com>
+Xiaoshu Zhang <xiaoshu@amazon.com>
+Xiaoyin Liu <xiaoyin.l@outlook.com>
+Xinchao He <hexinchao@gmail.com>
+Xing Zhang <xzhang@adobe.com>
+Xinghua Cao <xinghua.cao@intel.com>
+Xu Samuel <samuel.xu@intel.com>
+Xu Xing <xing.xu@intel.com>
+Xuefei Ren <xrenishere@gmail.com>
+Xueqing Huang <huangxueqing@xiaomi.com>
+Xun Sun <xun.sun@intel.com>
+Xunran Ding <xunran.ding@samsung.com>
+Xunran Ding <dingxunran@gmail.com>
+Yael Aharon <yael.aharon@intel.com>
+Yan Wang <yan0422.wang@samsung.com>
+Yang Gu <yang.gu@intel.com>
+Yannic Bonenberger <contact@yannic-bonenberger.com>
+Yarin Kaul <yarin.kaul@gmail.com>
+Yash Vempati <vempatiy@amazon.com>
+Ye Liu <cbakgly@gmail.com>
+Yeol Park <peary2@gmail.com>
+Yeonwoo Jo <yeonwoo.jo.92@gmail.com>
+Yi Shen <yi.shen@samsung.com>
+Yi Sun <ratsunny@gmail.com>
+Yichen Jiang <jiangyichen123@gmail.com>
+Yifei Yu <yuyifei@xiaomi.com>
+Yizhou Jiang <yizhou.jiang@intel.com>
+Yoav Weiss <yoav@yoav.ws>
+Yoav Zilberberg <yoav.zilberberg@gmail.com>
+Yong Shin <sy3620@gmail.com>
+Yong Wang <ccyongwang@tencent.com>
+Yongha Lee <yongha78.lee@samsung.com>
+Yongseok Choi <yongseok.choi@navercorp.com>
+Yongsheng Zhu <yongsheng.zhu@intel.com>
+Yoonjae Cho <yoonjae.cho92@gmail.com>
+Yoshinori Sano <yoshinori.sano@gmail.com>
+Youngho Seo <hazivoo@gmail.com>
+Youngjin Choi <cyjin9.yc@gmail.com>
+YoungKi Hong <simon.hong81@gmail.com>
+Youngmin Yoo <youngmin.yoo@samsung.com>
+Youngsoo Choi <kenshin.choi@samsung.com>
+Youngsun Suh <zard17@gmail.com>
+Yuhong Sha <yuhong.sha@samsung.com>
+Yumikiyo Osanai <yumios.art@gmail.com>
+Yunchao He <yunchao.he@intel.com>
+Yupei Lin <yplam@yplam.com>
+Yupei Wang <perryuwang@tencent.com>
+Yura Yaroshevich <yura.yaroshevich@gmail.com>
+Yuri Gorobets <yuri.gorobets@gmail.com>
+Yuriy Taraday <yorik.sar@gmail.com>
+Yuvanesh Natarajan <yuvanesh.n1@samsung.com>
+Zeno Albisser <zeno.albisser@digia.com>
+Zeqin Chen <talonchen@tencent.com>
+Zhaoze Zhou <zhaoze.zhou@partner.samsung.com>
+Zheda Chen <zheda.chen@intel.com>
+Zheng Chuang <zhengchuangscu@gmail.com>
+Zhengkun Li <zhengkli@amazon.com>
+Zhenyu Liang <zhenyu.liang@intel.com>
+Zhenyu Shan <zhenyu.shan@intel.com>
+Zhifei Fang <facetothefate@gmail.com>
+Zhuoyu Qian <zhuoyu.qian@samsung.com>
+Ziran Sun <ziran.sun@samsung.com>
+Zoltan Herczeg <zherczeg.u-szeged@partner.samsung.com>
+Zoltan Kuscsik <zoltan.kuscsik@linaro.org>
+Zsolt Borbely <zsborbely.u-szeged@partner.samsung.com>
+方觉 (Fang Jue) <fangjue23303@gmail.com>
+Rajesh Mahindra <rmahindra@uber.com>
+Yuan-Pin Yu <yjames@uber.com>
+Vinoth Chandar <vinoth@uber.com>
+Zheng Xu <zxu@kobo.com>
+Junsong Li <ljs.darkfish@gmail.com>
+
+ACCESS CO., LTD. <*@access-company.com>
+Akamai Inc. <*@akamai.com>
+ARM Holdings <*@arm.com>
+BlackBerry Limited <*@blackberry.com>
+Bocoup <*@bocoup.com>
+Canonical Limited <*@canonical.com>
+Cloudflare, Inc. <*@cloudflare.com>
+Code Aurora Forum <*@codeaurora.org>
+Collabora Limited <*@collabora.com>
+Comodo CA Limited
+Cosium <*@cosium.com>
+Duck Duck Go, Inc. <*@duckduckgo.com>
+Endless Mobile, Inc. <*@endlessm.com>
+Estimote, Inc. <*@estimote.com>
+Facebook, Inc. <*@fb.com>
+Facebook, Inc. <*@oculus.com>
+Google Inc. <*@google.com>
+Hewlett-Packard Development Company, L.P. <*@hp.com>
+IBM Inc. <*@*.ibm.com>
+IBM Inc. <*@ibm.com>
+Igalia S.L. <*@igalia.com>
+Imagination Technologies Limited <*@imagination.corp-partner.google.com>
+Impossible Dreams Network <*@impossibledreams.net>
+Intel Corporation <*@intel.com>
+LG Electronics, Inc. <*@lge.com>
+Loongson Technology Corporation Limited. <*@loongson.cn>
+Macadamian <*@macadamian.com>
+Mediatek <*@mediatek.com>
+Microsoft <*@microsoft.com>
+MIPS Technologies, Inc. <*@mips.com>
+Mozilla Corporation <*@mozilla.com>
+Neverware Inc. <*@neverware.com>
+NIKE, Inc. <*@nike.com>
+NVIDIA Corporation <*@nvidia.com>
+Opera Software ASA <*@opera.com>
+Optical Tone Ltd <*@opticaltone.com>
+Pengutronix e.K. <*@pengutronix.de>
+Rakuten Kobo Inc. <*@kobo.com>
+Rakuten Kobo Inc. <*@rakuten.com>
+Seznam.cz, a.s. <*@firma.seznam.cz>
+Slack Technologies Inc. <*@slack-corp.com>
+Spotify AB <*@spotify.com>
+Tableau Software <*@tableau.com>
+TeamSpeak Systems GmbH <*@teamspeak.com>
+The Chromium Authors <*@chromium.org>
+The MathWorks, Inc. <binod.pant@mathworks.com>
+Torchmobile Inc.
+Upwork <*@cloud.upwork.com>
+Venture 3 Systems LLC <*@venture3systems.com>
+Vewd Software AS <*@vewd.com>
+Vivaldi Technologies AS <*@vivaldi.com>
+Yandex LLC <*@yandex-team.ru>
+Make Positive Provar Limited <*@provartesting.com>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a32e00c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..2823b98
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,5 @@
+# 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.
+
+workspace(name = "com_google_googleurl")
diff --git a/base/BUILD b/base/BUILD
new file mode 100644
index 0000000..a9ca0e6
--- /dev/null
+++ b/base/BUILD
@@ -0,0 +1,21 @@
+# 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.
+
+cc_library(
+    name = "base",
+    hdrs = [
+        "compiler_specific.h",
+        "debug/leak_annotations.h",
+        "macros.h",
+        "no_destructor.h",
+        "optional.h",
+        "stl_util.h",
+        "template_util.h",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//build:build_config",
+        "//polyfills",
+    ],
+)
diff --git a/base/compiler_specific.h b/base/compiler_specific.h
new file mode 100644
index 0000000..7e2c510
--- /dev/null
+++ b/base/compiler_specific.h
@@ -0,0 +1,263 @@
+// Copyright (c) 2012 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.
+
+#ifndef BASE_COMPILER_SPECIFIC_H_
+#define BASE_COMPILER_SPECIFIC_H_
+
+#include "build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+
+#if !defined(__clang__)
+#error "Only clang-cl is supported on Windows, see https://crbug.com/988071"
+#endif
+
+// Macros for suppressing and disabling warnings on MSVC.
+//
+// Warning numbers are enumerated at:
+// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
+//
+// The warning pragma:
+// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
+//
+// Using __pragma instead of #pragma inside macros:
+// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
+
+// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
+// The warning remains disabled until popped by MSVC_POP_WARNING.
+#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
+                                     __pragma(warning(disable:n))
+
+// Pop effects of innermost MSVC_PUSH_* macro.
+#define MSVC_POP_WARNING() __pragma(warning(pop))
+
+#else  // Not MSVC
+
+#define MSVC_PUSH_DISABLE_WARNING(n)
+#define MSVC_POP_WARNING()
+#define MSVC_DISABLE_OPTIMIZE()
+#define MSVC_ENABLE_OPTIMIZE()
+
+#endif  // COMPILER_MSVC
+
+// These macros can be helpful when investigating compiler bugs or when
+// investigating issues in local optimized builds, by temporarily disabling
+// optimizations for a single function or file. These macros should never be
+// used to permanently work around compiler bugs or other mysteries, and should
+// not be used in landed changes.
+#if !defined(OFFICIAL_BUILD)
+#if defined(__clang__)
+#define DISABLE_OPTIMIZE() __pragma(clang optimize off)
+#define ENABLE_OPTIMIZE() __pragma(clang optimize on)
+#elif defined(COMPILER_MSVC)
+#define DISABLE_OPTIMIZE() __pragma(optimize("", off))
+#define ENABLE_OPTIMIZE() __pragma(optimize("", on))
+#else
+// These macros are not currently available for other compiler options.
+#endif
+// These macros are not available in official builds.
+#endif  // !defined(OFFICIAL_BUILD)
+
+// Annotate a variable indicating it's ok if the variable is not used.
+// (Typically used to silence a compiler warning when the assignment
+// is important for some other reason.)
+// Use like:
+//   int x = ...;
+//   ALLOW_UNUSED_LOCAL(x);
+#define ALLOW_UNUSED_LOCAL(x) (void)x
+
+// Annotate a typedef or function indicating it's ok if it's not used.
+// Use like:
+//   typedef Foo Bar ALLOW_UNUSED_TYPE;
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define ALLOW_UNUSED_TYPE __attribute__((unused))
+#else
+#define ALLOW_UNUSED_TYPE
+#endif
+
+// Annotate a function indicating it should not be inlined.
+// Use like:
+//   NOINLINE void DoStuff() { ... }
+#if defined(COMPILER_GCC)
+#define NOINLINE __attribute__((noinline))
+#elif defined(COMPILER_MSVC)
+#define NOINLINE __declspec(noinline)
+#else
+#define NOINLINE
+#endif
+
+#if defined(COMPILER_GCC) && defined(NDEBUG)
+#define ALWAYS_INLINE inline __attribute__((__always_inline__))
+#elif defined(COMPILER_MSVC) && defined(NDEBUG)
+#define ALWAYS_INLINE __forceinline
+#else
+#define ALWAYS_INLINE inline
+#endif
+
+// Specify memory alignment for structs, classes, etc.
+// Use like:
+//   class ALIGNAS(16) MyClass { ... }
+//   ALIGNAS(16) int array[4];
+//
+// In most places you can use the C++11 keyword "alignas", which is preferred.
+//
+// But compilers have trouble mixing __attribute__((...)) syntax with
+// alignas(...) syntax.
+//
+// Doesn't work in clang or gcc:
+//   struct alignas(16) __attribute__((packed)) S { char c; };
+// Works in clang but not gcc:
+//   struct __attribute__((packed)) alignas(16) S2 { char c; };
+// Works in clang and gcc:
+//   struct alignas(16) S3 { char c; } __attribute__((packed));
+//
+// There are also some attributes that must be specified *before* a class
+// definition: visibility (used for exporting functions/classes) is one of
+// these attributes. This means that it is not possible to use alignas() with a
+// class that is marked as exported.
+#if defined(COMPILER_MSVC)
+#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#elif defined(COMPILER_GCC)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#endif
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+//   int foo() WARN_UNUSED_RESULT;
+// To explicitly ignore a result, see |ignore_result()| in base/macros.h.
+#undef WARN_UNUSED_RESULT
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+
+// Tell the compiler a function is using a printf-style format string.
+// |format_param| is the one-based index of the format string parameter;
+// |dots_param| is the one-based index of the "..." parameter.
+// For v*printf functions (which take a va_list), pass 0 for dots_param.
+// (This is undocumented but matches what the system C headers do.)
+// For member functions, the implicit this parameter counts as index 1.
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define PRINTF_FORMAT(format_param, dots_param) \
+    __attribute__((format(printf, format_param, dots_param)))
+#else
+#define PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+// WPRINTF_FORMAT is the same, but for wide format strings.
+// This doesn't appear to yet be implemented in any compiler.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
+#define WPRINTF_FORMAT(format_param, dots_param)
+// If available, it would look like:
+//   __attribute__((format(wprintf, format_param, dots_param)))
+
+// Sanitizers annotations.
+#if defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))
+#endif
+#endif
+#if !defined(NO_SANITIZE)
+#define NO_SANITIZE(what)
+#endif
+
+// MemorySanitizer annotations.
+#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
+#include <sanitizer/msan_interface.h>
+
+// Mark a memory region fully initialized.
+// Use this to annotate code that deliberately reads uninitialized data, for
+// example a GC scavenging root set pointers from the stack.
+#define MSAN_UNPOISON(p, size)  __msan_unpoison(p, size)
+
+// Check a memory region for initializedness, as if it was being used here.
+// If any bits are uninitialized, crash with an MSan report.
+// Use this to sanitize data which MSan won't be able to track, e.g. before
+// passing data to another process via shared memory.
+#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \
+    __msan_check_mem_is_initialized(p, size)
+#else  // MEMORY_SANITIZER
+#define MSAN_UNPOISON(p, size)
+#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
+#endif  // MEMORY_SANITIZER
+
+// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons.
+#if !defined(DISABLE_CFI_PERF)
+#if defined(__clang__) && defined(OFFICIAL_BUILD)
+#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi")))
+#else
+#define DISABLE_CFI_PERF
+#endif
+#endif
+
+// Macro useful for writing cross-platform function pointers.
+#if !defined(CDECL)
+#if defined(OS_WIN)
+#define CDECL __cdecl
+#else  // defined(OS_WIN)
+#define CDECL
+#endif  // defined(OS_WIN)
+#endif  // !defined(CDECL)
+
+// Macro for hinting that an expression is likely to be false.
+#if !defined(UNLIKELY)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif  // defined(COMPILER_GCC)
+#endif  // !defined(UNLIKELY)
+
+#if !defined(LIKELY)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define LIKELY(x) (x)
+#endif  // defined(COMPILER_GCC)
+#endif  // !defined(LIKELY)
+
+// Compiler feature-detection.
+// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
+#if defined(__has_feature)
+#define HAS_FEATURE(FEATURE) __has_feature(FEATURE)
+#else
+#define HAS_FEATURE(FEATURE) 0
+#endif
+
+// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
+#if defined(__clang__)
+#define FALLTHROUGH [[clang::fallthrough]]
+#else
+#define FALLTHROUGH
+#endif
+
+#if defined(COMPILER_GCC)
+#define PRETTY_FUNCTION __PRETTY_FUNCTION__
+#elif defined(COMPILER_MSVC)
+#define PRETTY_FUNCTION __FUNCSIG__
+#else
+// See https://en.cppreference.com/w/c/language/function_definition#func
+#define PRETTY_FUNCTION __func__
+#endif
+
+#if !defined(CPU_ARM_NEON)
+#if defined(__arm__)
+#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \
+    !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID)
+#error Chromium does not support middle endian architecture
+#endif
+#if defined(__ARM_NEON__)
+#define CPU_ARM_NEON 1
+#endif
+#endif  // defined(__arm__)
+#endif  // !defined(CPU_ARM_NEON)
+
+#if !defined(HAVE_MIPS_MSA_INTRINSICS)
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+#define HAVE_MIPS_MSA_INTRINSICS 1
+#endif
+#endif
+
+#endif  // BASE_COMPILER_SPECIFIC_H_
diff --git a/base/debug/leak_annotations.h b/base/debug/leak_annotations.h
new file mode 100644
index 0000000..dc50246
--- /dev/null
+++ b/base/debug/leak_annotations.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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.
+
+#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_
+#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+
+// This file defines macros which can be used to annotate intentional memory
+// leaks. Support for annotations is implemented in LeakSanitizer. Annotated
+// objects will be treated as a source of live pointers, i.e. any heap objects
+// reachable by following pointers from an annotated object will not be
+// reported as leaks.
+//
+// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope
+// will be annotated as leaks.
+// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will
+// be annotated as a leak.
+
+#if defined(LEAK_SANITIZER) && !defined(OS_NACL)
+
+#include <sanitizer/lsan_interface.h>
+
+class ScopedLeakSanitizerDisabler {
+ public:
+  ScopedLeakSanitizerDisabler() { __lsan_disable(); }
+  ~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler);
+};
+
+#define ANNOTATE_SCOPED_MEMORY_LEAK \
+    ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
+
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X);
+
+#else
+
+#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0)
+
+#endif
+
+#endif  // BASE_DEBUG_LEAK_ANNOTATIONS_H_
diff --git a/base/macros.h b/base/macros.h
new file mode 100644
index 0000000..cda8e3a
--- /dev/null
+++ b/base/macros.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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.
+
+// This file contains macros and macro-like constructs (e.g., templates) that
+// are commonly used throughout Chromium source. (It may also contain things
+// that are closely related to things that are commonly used that belong in this
+// file.)
+
+#ifndef BASE_MACROS_H_
+#define BASE_MACROS_H_
+
+// Put this in the declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) \
+  TypeName(const TypeName&) = delete
+
+// Put this in the declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete
+
+// Put this in the declarations for a class to be uncopyable and unassignable.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  DISALLOW_COPY(TypeName);                 \
+  DISALLOW_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+// This is especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName() = delete;                           \
+  DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+//   std::unique_ptr<MyType> my_var = ...;
+//   if (TakeOwnership(my_var.get()) == SUCCESS)
+//     ignore_result(my_var.release());
+//
+template<typename T>
+inline void ignore_result(const T&) {
+}
+
+#endif  // BASE_MACROS_H_
diff --git a/base/no_destructor.h b/base/no_destructor.h
new file mode 100644
index 0000000..3d7a85c
--- /dev/null
+++ b/base/no_destructor.h
@@ -0,0 +1,98 @@
+// Copyright 2018 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.
+
+#ifndef BASE_NO_DESTRUCTOR_H_
+#define BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <utility>
+
+namespace gurl_base {
+
+// A wrapper that makes it easy to create an object of type T with static
+// storage duration that:
+// - is only constructed on first access
+// - never invokes the destructor
+// in order to satisfy the styleguide ban on global constructors and
+// destructors.
+//
+// Runtime constant example:
+// const std::string& GetLineSeparator() {
+//  // Forwards to std::string(size_t, char, const Allocator&) constructor.
+//   static const gurl_base::NoDestructor<std::string> s(5, '-');
+//   return *s;
+// }
+//
+// More complex initialization with a lambda:
+// const std::string& GetSessionNonce() {
+//   static const gurl_base::NoDestructor<std::string> nonce([] {
+//     std::string s(16);
+//     crypto::RandString(s.data(), s.size());
+//     return s;
+//   }());
+//   return *nonce;
+// }
+//
+// NoDestructor<T> stores the object inline, so it also avoids a pointer
+// indirection and a malloc. Also note that since C++11 static local variable
+// initialization is thread-safe and so is this pattern. Code should prefer to
+// use NoDestructor<T> over:
+// - A function scoped static T* or T& that is dynamically initialized.
+// - A global gurl_base::LazyInstance<T>.
+//
+// Note that since the destructor is never run, this *will* leak memory if used
+// as a stack or member variable. Furthermore, a NoDestructor<T> should never
+// have global scope as that may require a static initializer.
+template <typename T>
+class NoDestructor {
+ public:
+  // Not constexpr; just write static constexpr T x = ...; if the value should
+  // be a constexpr.
+  template <typename... Args>
+  explicit NoDestructor(Args&&... args) {
+    new (storage_) T(std::forward<Args>(args)...);
+  }
+
+  // Allows copy and move construction of the contained type, to allow
+  // construction from an initializer list, e.g. for std::vector.
+  explicit NoDestructor(const T& x) { new (storage_) T(x); }
+  explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
+
+  NoDestructor(const NoDestructor&) = delete;
+  NoDestructor& operator=(const NoDestructor&) = delete;
+
+  ~NoDestructor() = default;
+
+  const T& operator*() const { return *get(); }
+  T& operator*() { return *get(); }
+
+  const T* operator->() const { return get(); }
+  T* operator->() { return get(); }
+
+  const T* get() const { return reinterpret_cast<const T*>(storage_); }
+  T* get() { return reinterpret_cast<T*>(storage_); }
+
+ private:
+  alignas(T) char storage_[sizeof(T)];
+
+#if defined(LEAK_SANITIZER)
+  // TODO(https://crbug.com/812277): This is a hack to work around the fact
+  // that LSan doesn't seem to treat NoDestructor as a root for reachability
+  // analysis. This means that code like this:
+  //   static gurl_base::NoDestructor<std::vector<int>> v({1, 2, 3});
+  // is considered a leak. Using the standard leak sanitizer annotations to
+  // suppress leaks doesn't work: std::vector is implicitly constructed before
+  // calling the gurl_base::NoDestructor constructor.
+  //
+  // Unfortunately, I haven't been able to demonstrate this issue in simpler
+  // reproductions: until that's resolved, hold an explicit pointer to the
+  // placement-new'd object in leak sanitizer mode to help LSan realize that
+  // objects allocated by the contained type are still reachable.
+  T* storage_ptr_ = reinterpret_cast<T*>(storage_);
+#endif  // defined(LEAK_SANITIZER)
+};
+
+}  // namespace base
+
+#endif  // BASE_NO_DESTRUCTOR_H_
diff --git a/base/optional.h b/base/optional.h
new file mode 100644
index 0000000..345147c
--- /dev/null
+++ b/base/optional.h
@@ -0,0 +1,937 @@
+// Copyright 2016 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.
+
+#ifndef BASE_OPTIONAL_H_
+#define BASE_OPTIONAL_H_
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "polyfills/base/logging.h"
+#include "base/template_util.h"
+
+namespace gurl_base {
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
+struct nullopt_t {
+  constexpr explicit nullopt_t(int) {}
+};
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt
+constexpr nullopt_t nullopt(0);
+
+// Forward declaration, which is refered by following helpers.
+template <typename T>
+class Optional;
+
+namespace internal {
+
+template <typename T, bool = std::is_trivially_destructible<T>::value>
+struct OptionalStorageBase {
+  // Initializing |empty_| here instead of using default member initializing
+  // to avoid errors in g++ 4.8.
+  constexpr OptionalStorageBase() : empty_('\0') {}
+
+  template <class... Args>
+  constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
+      : is_populated_(true), value_(std::forward<Args>(args)...) {}
+
+  // When T is not trivially destructible we must call its
+  // destructor before deallocating its memory.
+  // Note that this hides the (implicitly declared) move constructor, which
+  // would be used for constexpr move constructor in OptionalStorage<T>.
+  // It is needed iff T is trivially move constructible. However, the current
+  // is_trivially_{copy,move}_constructible implementation requires
+  // is_trivially_destructible (which looks a bug, cf:
+  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 and
+  // http://cplusplus.github.io/LWG/lwg-active.html#2116), so it is not
+  // necessary for this case at the moment. Please see also the destructor
+  // comment in "is_trivially_destructible = true" specialization below.
+  ~OptionalStorageBase() {
+    if (is_populated_)
+      value_.~T();
+  }
+
+  template <class... Args>
+  void Init(Args&&... args) {
+    GURL_DCHECK(!is_populated_);
+    ::new (&value_) T(std::forward<Args>(args)...);
+    is_populated_ = true;
+  }
+
+  bool is_populated_ = false;
+  union {
+    // |empty_| exists so that the union will always be initialized, even when
+    // it doesn't contain a value. Union members must be initialized for the
+    // constructor to be 'constexpr'.
+    char empty_;
+    T value_;
+  };
+};
+
+template <typename T>
+struct OptionalStorageBase<T, true /* trivially destructible */> {
+  // Initializing |empty_| here instead of using default member initializing
+  // to avoid errors in g++ 4.8.
+  constexpr OptionalStorageBase() : empty_('\0') {}
+
+  template <class... Args>
+  constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
+      : is_populated_(true), value_(std::forward<Args>(args)...) {}
+
+  // When T is trivially destructible (i.e. its destructor does nothing) there
+  // is no need to call it. Implicitly defined destructor is trivial, because
+  // both members (bool and union containing only variants which are trivially
+  // destructible) are trivially destructible.
+  // Explicitly-defaulted destructor is also trivial, but do not use it here,
+  // because it hides the implicit move constructor. It is needed to implement
+  // constexpr move constructor in OptionalStorage iff T is trivially move
+  // constructible. Note that, if T is trivially move constructible, the move
+  // constructor of OptionalStorageBase<T> is also implicitly defined and it is
+  // trivially move constructor. If T is not trivially move constructible,
+  // "not declaring move constructor without destructor declaration" here means
+  // "delete move constructor", which works because any move constructor of
+  // OptionalStorage will not refer to it in that case.
+
+  template <class... Args>
+  void Init(Args&&... args) {
+    GURL_DCHECK(!is_populated_);
+    ::new (&value_) T(std::forward<Args>(args)...);
+    is_populated_ = true;
+  }
+
+  bool is_populated_ = false;
+  union {
+    // |empty_| exists so that the union will always be initialized, even when
+    // it doesn't contain a value. Union members must be initialized for the
+    // constructor to be 'constexpr'.
+    char empty_;
+    T value_;
+  };
+};
+
+// Implement conditional constexpr copy and move constructors. These are
+// constexpr if is_trivially_{copy,move}_constructible<T>::value is true
+// respectively. If each is true, the corresponding constructor is defined as
+// "= default;", which generates a constexpr constructor (In this case,
+// the condition of constexpr-ness is satisfied because the base class also has
+// compiler generated constexpr {copy,move} constructors). Note that
+// placement-new is prohibited in constexpr.
+template <typename T,
+          bool = is_trivially_copy_constructible<T>::value,
+          bool = std::is_trivially_move_constructible<T>::value>
+struct OptionalStorage : OptionalStorageBase<T> {
+  // This is no trivially {copy,move} constructible case. Other cases are
+  // defined below as specializations.
+
+  // Accessing the members of template base class requires explicit
+  // declaration.
+  using OptionalStorageBase<T>::is_populated_;
+  using OptionalStorageBase<T>::value_;
+  using OptionalStorageBase<T>::Init;
+
+  // Inherit constructors (specifically, the in_place constructor).
+  using OptionalStorageBase<T>::OptionalStorageBase;
+
+  // User defined constructor deletes the default constructor.
+  // Define it explicitly.
+  OptionalStorage() = default;
+
+  OptionalStorage(const OptionalStorage& other) {
+    if (other.is_populated_)
+      Init(other.value_);
+  }
+
+  OptionalStorage(OptionalStorage&& other) noexcept(
+      std::is_nothrow_move_constructible<T>::value) {
+    if (other.is_populated_)
+      Init(std::move(other.value_));
+  }
+};
+
+template <typename T>
+struct OptionalStorage<T,
+                       true /* trivially copy constructible */,
+                       false /* trivially move constructible */>
+    : OptionalStorageBase<T> {
+  using OptionalStorageBase<T>::is_populated_;
+  using OptionalStorageBase<T>::value_;
+  using OptionalStorageBase<T>::Init;
+  using OptionalStorageBase<T>::OptionalStorageBase;
+
+  OptionalStorage() = default;
+  OptionalStorage(const OptionalStorage& other) = default;
+
+  OptionalStorage(OptionalStorage&& other) noexcept(
+      std::is_nothrow_move_constructible<T>::value) {
+    if (other.is_populated_)
+      Init(std::move(other.value_));
+  }
+};
+
+template <typename T>
+struct OptionalStorage<T,
+                       false /* trivially copy constructible */,
+                       true /* trivially move constructible */>
+    : OptionalStorageBase<T> {
+  using OptionalStorageBase<T>::is_populated_;
+  using OptionalStorageBase<T>::value_;
+  using OptionalStorageBase<T>::Init;
+  using OptionalStorageBase<T>::OptionalStorageBase;
+
+  OptionalStorage() = default;
+  OptionalStorage(OptionalStorage&& other) = default;
+
+  OptionalStorage(const OptionalStorage& other) {
+    if (other.is_populated_)
+      Init(other.value_);
+  }
+};
+
+template <typename T>
+struct OptionalStorage<T,
+                       true /* trivially copy constructible */,
+                       true /* trivially move constructible */>
+    : OptionalStorageBase<T> {
+  // If both trivially {copy,move} constructible are true, it is not necessary
+  // to use user-defined constructors. So, just inheriting constructors
+  // from the base class works.
+  using OptionalStorageBase<T>::OptionalStorageBase;
+};
+
+// Base class to support conditionally usable copy-/move- constructors
+// and assign operators.
+template <typename T>
+class OptionalBase {
+  // This class provides implementation rather than public API, so everything
+  // should be hidden. Often we use composition, but we cannot in this case
+  // because of C++ language restriction.
+ protected:
+  constexpr OptionalBase() = default;
+  constexpr OptionalBase(const OptionalBase& other) = default;
+  constexpr OptionalBase(OptionalBase&& other) = default;
+
+  template <class... Args>
+  constexpr explicit OptionalBase(in_place_t, Args&&... args)
+      : storage_(in_place, std::forward<Args>(args)...) {}
+
+  // Implementation of converting constructors.
+  template <typename U>
+  explicit OptionalBase(const OptionalBase<U>& other) {
+    if (other.storage_.is_populated_)
+      storage_.Init(other.storage_.value_);
+  }
+
+  template <typename U>
+  explicit OptionalBase(OptionalBase<U>&& other) {
+    if (other.storage_.is_populated_)
+      storage_.Init(std::move(other.storage_.value_));
+  }
+
+  ~OptionalBase() = default;
+
+  OptionalBase& operator=(const OptionalBase& other) {
+    CopyAssign(other);
+    return *this;
+  }
+
+  OptionalBase& operator=(OptionalBase&& other) noexcept(
+      std::is_nothrow_move_assignable<T>::value&&
+          std::is_nothrow_move_constructible<T>::value) {
+    MoveAssign(std::move(other));
+    return *this;
+  }
+
+  template <typename U>
+  void CopyAssign(const OptionalBase<U>& other) {
+    if (other.storage_.is_populated_)
+      InitOrAssign(other.storage_.value_);
+    else
+      FreeIfNeeded();
+  }
+
+  template <typename U>
+  void MoveAssign(OptionalBase<U>&& other) {
+    if (other.storage_.is_populated_)
+      InitOrAssign(std::move(other.storage_.value_));
+    else
+      FreeIfNeeded();
+  }
+
+  template <typename U>
+  void InitOrAssign(U&& value) {
+    if (storage_.is_populated_)
+      storage_.value_ = std::forward<U>(value);
+    else
+      storage_.Init(std::forward<U>(value));
+  }
+
+  void FreeIfNeeded() {
+    if (!storage_.is_populated_)
+      return;
+    storage_.value_.~T();
+    storage_.is_populated_ = false;
+  }
+
+  // For implementing conversion, allow access to other typed OptionalBase
+  // class.
+  template <typename U>
+  friend class OptionalBase;
+
+  OptionalStorage<T> storage_;
+};
+
+// The following {Copy,Move}{Constructible,Assignable} structs are helpers to
+// implement constructor/assign-operator overloading. Specifically, if T is
+// is not movable but copyable, Optional<T>'s move constructor should not
+// participate in overload resolution. This inheritance trick implements that.
+template <bool is_copy_constructible>
+struct CopyConstructible {};
+
+template <>
+struct CopyConstructible<false> {
+  constexpr CopyConstructible() = default;
+  constexpr CopyConstructible(const CopyConstructible&) = delete;
+  constexpr CopyConstructible(CopyConstructible&&) = default;
+  CopyConstructible& operator=(const CopyConstructible&) = default;
+  CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <bool is_move_constructible>
+struct MoveConstructible {};
+
+template <>
+struct MoveConstructible<false> {
+  constexpr MoveConstructible() = default;
+  constexpr MoveConstructible(const MoveConstructible&) = default;
+  constexpr MoveConstructible(MoveConstructible&&) = delete;
+  MoveConstructible& operator=(const MoveConstructible&) = default;
+  MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <bool is_copy_assignable>
+struct CopyAssignable {};
+
+template <>
+struct CopyAssignable<false> {
+  constexpr CopyAssignable() = default;
+  constexpr CopyAssignable(const CopyAssignable&) = default;
+  constexpr CopyAssignable(CopyAssignable&&) = default;
+  CopyAssignable& operator=(const CopyAssignable&) = delete;
+  CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <bool is_move_assignable>
+struct MoveAssignable {};
+
+template <>
+struct MoveAssignable<false> {
+  constexpr MoveAssignable() = default;
+  constexpr MoveAssignable(const MoveAssignable&) = default;
+  constexpr MoveAssignable(MoveAssignable&&) = default;
+  MoveAssignable& operator=(const MoveAssignable&) = default;
+  MoveAssignable& operator=(MoveAssignable&&) = delete;
+};
+
+// Helper to conditionally enable converting constructors and assign operators.
+template <typename T, typename U>
+struct IsConvertibleFromOptional
+    : std::integral_constant<
+          bool,
+          std::is_constructible<T, Optional<U>&>::value ||
+              std::is_constructible<T, const Optional<U>&>::value ||
+              std::is_constructible<T, Optional<U>&&>::value ||
+              std::is_constructible<T, const Optional<U>&&>::value ||
+              std::is_convertible<Optional<U>&, T>::value ||
+              std::is_convertible<const Optional<U>&, T>::value ||
+              std::is_convertible<Optional<U>&&, T>::value ||
+              std::is_convertible<const Optional<U>&&, T>::value> {};
+
+template <typename T, typename U>
+struct IsAssignableFromOptional
+    : std::integral_constant<
+          bool,
+          IsConvertibleFromOptional<T, U>::value ||
+              std::is_assignable<T&, Optional<U>&>::value ||
+              std::is_assignable<T&, const Optional<U>&>::value ||
+              std::is_assignable<T&, Optional<U>&&>::value ||
+              std::is_assignable<T&, const Optional<U>&&>::value> {};
+
+// Forward compatibility for C++17.
+// Introduce one more deeper nested namespace to avoid leaking using std::swap.
+namespace swappable_impl {
+using std::swap;
+
+struct IsSwappableImpl {
+  // Tests if swap can be called. Check<T&>(0) returns true_type iff swap
+  // is available for T. Otherwise, Check's overload resolution falls back
+  // to Check(...) declared below thanks to SFINAE, so returns false_type.
+  template <typename T>
+  static auto Check(int)
+      -> decltype(swap(std::declval<T>(), std::declval<T>()), std::true_type());
+
+  template <typename T>
+  static std::false_type Check(...);
+};
+}  // namespace swappable_impl
+
+template <typename T>
+struct IsSwappable : decltype(swappable_impl::IsSwappableImpl::Check<T&>(0)) {};
+
+// Forward compatibility for C++20.
+template <typename T>
+using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>;
+
+}  // namespace internal
+
+// On Windows, by default, empty-base class optimization does not work,
+// which means even if the base class is empty struct, it still consumes one
+// byte for its body. __declspec(empty_bases) enables the optimization.
+// cf)
+// https://blogs.msdn.microsoft.com/vcblog/2016/03/30/optimizing-the-layout-of-empty-base-classes-in-vs2015-update-2-3/
+#ifdef OS_WIN
+#define OPTIONAL_DECLSPEC_EMPTY_BASES __declspec(empty_bases)
+#else
+#define OPTIONAL_DECLSPEC_EMPTY_BASES
+#endif
+
+// gurl_base::Optional is a Chromium version of the C++17 optional class:
+// std::optional documentation:
+// http://en.cppreference.com/w/cpp/utility/optional
+// Chromium documentation:
+// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
+//
+// These are the differences between the specification and the implementation:
+// - Constructors do not use 'constexpr' as it is a C++14 extension.
+// - 'constexpr' might be missing in some places for reasons specified locally.
+// - No exceptions are thrown, because they are banned from Chromium.
+//   Marked noexcept for only move constructor and move assign operators.
+// - All the non-members are in the 'base' namespace instead of 'std'.
+//
+// Note that T cannot have a constructor T(Optional<T>) etc. Optional<T> checks
+// T's constructor (specifically via IsConvertibleFromOptional), and in the
+// check whether T can be constructible from Optional<T>, which is recursive
+// so it does not work. As of Feb 2018, std::optional C++17 implementation in
+// both clang and gcc has same limitation. MSVC SFINAE looks to have different
+// behavior, but anyway it reports an error, too.
+template <typename T>
+class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
+    : public internal::OptionalBase<T>,
+      public internal::CopyConstructible<std::is_copy_constructible<T>::value>,
+      public internal::MoveConstructible<std::is_move_constructible<T>::value>,
+      public internal::CopyAssignable<std::is_copy_constructible<T>::value &&
+                                      std::is_copy_assignable<T>::value>,
+      public internal::MoveAssignable<std::is_move_constructible<T>::value &&
+                                      std::is_move_assignable<T>::value> {
+ private:
+  // Disable some versions of T that are ill-formed.
+  // See: https://timsong-cpp.github.io/cppwp/n4659/optional#syn-1
+  static_assert(
+      !std::is_same<internal::RemoveCvRefT<T>, in_place_t>::value,
+      "instantiation of gurl_base::Optional with in_place_t is ill-formed");
+  static_assert(!std::is_same<internal::RemoveCvRefT<T>, nullopt_t>::value,
+                "instantiation of gurl_base::Optional with nullopt_t is ill-formed");
+  static_assert(
+      !std::is_reference<T>::value,
+      "instantiation of gurl_base::Optional with a reference type is ill-formed");
+  // See: https://timsong-cpp.github.io/cppwp/n4659/optional#optional-3
+  static_assert(std::is_destructible<T>::value,
+                "instantiation of gurl_base::Optional with a non-destructible type "
+                "is ill-formed");
+  // Arrays are explicitly disallowed because for arrays of known bound
+  // is_destructible is of undefined value.
+  // See: https://en.cppreference.com/w/cpp/types/is_destructible
+  static_assert(
+      !std::is_array<T>::value,
+      "instantiation of gurl_base::Optional with an array type is ill-formed");
+
+ public:
+#undef OPTIONAL_DECLSPEC_EMPTY_BASES
+  using value_type = T;
+
+  // Defer default/copy/move constructor implementation to OptionalBase.
+  constexpr Optional() = default;
+  constexpr Optional(const Optional& other) = default;
+  constexpr Optional(Optional&& other) noexcept(
+      std::is_nothrow_move_constructible<T>::value) = default;
+
+  constexpr Optional(nullopt_t) {}  // NOLINT(runtime/explicit)
+
+  // Converting copy constructor. "explicit" only if
+  // std::is_convertible<const U&, T>::value is false. It is implemented by
+  // declaring two almost same constructors, but that condition in enable_if_t
+  // is different, so that either one is chosen, thanks to SFINAE.
+  template <
+      typename U,
+      std::enable_if_t<std::is_constructible<T, const U&>::value &&
+                           !internal::IsConvertibleFromOptional<T, U>::value &&
+                           std::is_convertible<const U&, T>::value,
+                       bool> = false>
+  Optional(const Optional<U>& other) : internal::OptionalBase<T>(other) {}
+
+  template <
+      typename U,
+      std::enable_if_t<std::is_constructible<T, const U&>::value &&
+                           !internal::IsConvertibleFromOptional<T, U>::value &&
+                           !std::is_convertible<const U&, T>::value,
+                       bool> = false>
+  explicit Optional(const Optional<U>& other)
+      : internal::OptionalBase<T>(other) {}
+
+  // Converting move constructor. Similar to converting copy constructor,
+  // declaring two (explicit and non-explicit) constructors.
+  template <
+      typename U,
+      std::enable_if_t<std::is_constructible<T, U&&>::value &&
+                           !internal::IsConvertibleFromOptional<T, U>::value &&
+                           std::is_convertible<U&&, T>::value,
+                       bool> = false>
+  Optional(Optional<U>&& other) : internal::OptionalBase<T>(std::move(other)) {}
+
+  template <
+      typename U,
+      std::enable_if_t<std::is_constructible<T, U&&>::value &&
+                           !internal::IsConvertibleFromOptional<T, U>::value &&
+                           !std::is_convertible<U&&, T>::value,
+                       bool> = false>
+  explicit Optional(Optional<U>&& other)
+      : internal::OptionalBase<T>(std::move(other)) {}
+
+  template <class... Args>
+  constexpr explicit Optional(in_place_t, Args&&... args)
+      : internal::OptionalBase<T>(in_place, std::forward<Args>(args)...) {}
+
+  template <
+      class U,
+      class... Args,
+      class = std::enable_if_t<std::is_constructible<value_type,
+                                                     std::initializer_list<U>&,
+                                                     Args...>::value>>
+  constexpr explicit Optional(in_place_t,
+                              std::initializer_list<U> il,
+                              Args&&... args)
+      : internal::OptionalBase<T>(in_place, il, std::forward<Args>(args)...) {}
+
+  // Forward value constructor. Similar to converting constructors,
+  // conditionally explicit.
+  template <
+      typename U = value_type,
+      std::enable_if_t<
+          std::is_constructible<T, U&&>::value &&
+              !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
+              !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+              std::is_convertible<U&&, T>::value,
+          bool> = false>
+  constexpr Optional(U&& value)
+      : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
+
+  template <
+      typename U = value_type,
+      std::enable_if_t<
+          std::is_constructible<T, U&&>::value &&
+              !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
+              !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+              !std::is_convertible<U&&, T>::value,
+          bool> = false>
+  constexpr explicit Optional(U&& value)
+      : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
+
+  ~Optional() = default;
+
+  // Defer copy-/move- assign operator implementation to OptionalBase.
+  Optional& operator=(const Optional& other) = default;
+  Optional& operator=(Optional&& other) noexcept(
+      std::is_nothrow_move_assignable<T>::value&&
+          std::is_nothrow_move_constructible<T>::value) = default;
+
+  Optional& operator=(nullopt_t) {
+    FreeIfNeeded();
+    return *this;
+  }
+
+  // Perfect-forwarded assignment.
+  template <typename U>
+  std::enable_if_t<
+      !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+          std::is_constructible<T, U>::value &&
+          std::is_assignable<T&, U>::value &&
+          (!std::is_scalar<T>::value ||
+           !std::is_same<std::decay_t<U>, T>::value),
+      Optional&>
+  operator=(U&& value) {
+    InitOrAssign(std::forward<U>(value));
+    return *this;
+  }
+
+  // Copy assign the state of other.
+  template <typename U>
+  std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
+                       std::is_constructible<T, const U&>::value &&
+                       std::is_assignable<T&, const U&>::value,
+                   Optional&>
+  operator=(const Optional<U>& other) {
+    CopyAssign(other);
+    return *this;
+  }
+
+  // Move assign the state of other.
+  template <typename U>
+  std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
+                       std::is_constructible<T, U>::value &&
+                       std::is_assignable<T&, U>::value,
+                   Optional&>
+  operator=(Optional<U>&& other) {
+    MoveAssign(std::move(other));
+    return *this;
+  }
+
+  constexpr const T* operator->() const {
+    GURL_CHECK(storage_.is_populated_);
+    return &storage_.value_;
+  }
+
+  constexpr T* operator->() {
+    GURL_CHECK(storage_.is_populated_);
+    return &storage_.value_;
+  }
+
+  constexpr const T& operator*() const & {
+    GURL_CHECK(storage_.is_populated_);
+    return storage_.value_;
+  }
+
+  constexpr T& operator*() & {
+    GURL_CHECK(storage_.is_populated_);
+    return storage_.value_;
+  }
+
+  constexpr const T&& operator*() const && {
+    GURL_CHECK(storage_.is_populated_);
+    return std::move(storage_.value_);
+  }
+
+  constexpr T&& operator*() && {
+    GURL_CHECK(storage_.is_populated_);
+    return std::move(storage_.value_);
+  }
+
+  constexpr explicit operator bool() const { return storage_.is_populated_; }
+
+  constexpr bool has_value() const { return storage_.is_populated_; }
+
+  constexpr T& value() & {
+    GURL_CHECK(storage_.is_populated_);
+    return storage_.value_;
+  }
+
+  constexpr const T& value() const & {
+    GURL_CHECK(storage_.is_populated_);
+    return storage_.value_;
+  }
+
+  constexpr T&& value() && {
+    GURL_CHECK(storage_.is_populated_);
+    return std::move(storage_.value_);
+  }
+
+  constexpr const T&& value() const && {
+    GURL_CHECK(storage_.is_populated_);
+    return std::move(storage_.value_);
+  }
+
+  template <class U>
+  constexpr T value_or(U&& default_value) const& {
+    // TODO(mlamouri): add the following assert when possible:
+    // static_assert(std::is_copy_constructible<T>::value,
+    //               "T must be copy constructible");
+    static_assert(std::is_convertible<U, T>::value,
+                  "U must be convertible to T");
+    return storage_.is_populated_
+               ? storage_.value_
+               : static_cast<T>(std::forward<U>(default_value));
+  }
+
+  template <class U>
+  constexpr T value_or(U&& default_value) && {
+    // TODO(mlamouri): add the following assert when possible:
+    // static_assert(std::is_move_constructible<T>::value,
+    //               "T must be move constructible");
+    static_assert(std::is_convertible<U, T>::value,
+                  "U must be convertible to T");
+    return storage_.is_populated_
+               ? std::move(storage_.value_)
+               : static_cast<T>(std::forward<U>(default_value));
+  }
+
+  void swap(Optional& other) {
+    if (!storage_.is_populated_ && !other.storage_.is_populated_)
+      return;
+
+    if (storage_.is_populated_ != other.storage_.is_populated_) {
+      if (storage_.is_populated_) {
+        other.storage_.Init(std::move(storage_.value_));
+        FreeIfNeeded();
+      } else {
+        storage_.Init(std::move(other.storage_.value_));
+        other.FreeIfNeeded();
+      }
+      return;
+    }
+
+    GURL_DCHECK(storage_.is_populated_ && other.storage_.is_populated_);
+    using std::swap;
+    swap(**this, *other);
+  }
+
+  void reset() { FreeIfNeeded(); }
+
+  template <class... Args>
+  T& emplace(Args&&... args) {
+    FreeIfNeeded();
+    storage_.Init(std::forward<Args>(args)...);
+    return storage_.value_;
+  }
+
+  template <class U, class... Args>
+  std::enable_if_t<
+      std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
+      T&>
+  emplace(std::initializer_list<U> il, Args&&... args) {
+    FreeIfNeeded();
+    storage_.Init(il, std::forward<Args>(args)...);
+    return storage_.value_;
+  }
+
+ private:
+  // Accessing template base class's protected member needs explicit
+  // declaration to do so.
+  using internal::OptionalBase<T>::CopyAssign;
+  using internal::OptionalBase<T>::FreeIfNeeded;
+  using internal::OptionalBase<T>::InitOrAssign;
+  using internal::OptionalBase<T>::MoveAssign;
+  using internal::OptionalBase<T>::storage_;
+};
+
+// Here after defines comparation operators. The definition follows
+// http://en.cppreference.com/w/cpp/utility/optional/operator_cmp
+// while bool() casting is replaced by has_value() to meet the chromium
+// style guide.
+template <class T, class U>
+constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (lhs.has_value() != rhs.has_value())
+    return false;
+  if (!lhs.has_value())
+    return true;
+  return *lhs == *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (lhs.has_value() != rhs.has_value())
+    return true;
+  if (!lhs.has_value())
+    return false;
+  return *lhs != *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator<(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!rhs.has_value())
+    return false;
+  if (!lhs.has_value())
+    return true;
+  return *lhs < *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator<=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!lhs.has_value())
+    return true;
+  if (!rhs.has_value())
+    return false;
+  return *lhs <= *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator>(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!lhs.has_value())
+    return false;
+  if (!rhs.has_value())
+    return true;
+  return *lhs > *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator>=(const Optional<T>& lhs, const Optional<U>& rhs) {
+  if (!rhs.has_value())
+    return true;
+  if (!lhs.has_value())
+    return false;
+  return *lhs >= *rhs;
+}
+
+template <class T>
+constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
+  return !opt;
+}
+
+template <class T>
+constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
+  return !opt;
+}
+
+template <class T>
+constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
+  return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
+  return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
+  return false;
+}
+
+template <class T>
+constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
+  return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
+  return !opt;
+}
+
+template <class T>
+constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
+  return true;
+}
+
+template <class T>
+constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
+  return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
+  return false;
+}
+
+template <class T>
+constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
+  return true;
+}
+
+template <class T>
+constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
+  return !opt;
+}
+
+template <class T, class U>
+constexpr bool operator==(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt == value : false;
+}
+
+template <class T, class U>
+constexpr bool operator==(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value == *opt : false;
+}
+
+template <class T, class U>
+constexpr bool operator!=(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt != value : true;
+}
+
+template <class T, class U>
+constexpr bool operator!=(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value != *opt : true;
+}
+
+template <class T, class U>
+constexpr bool operator<(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt < value : true;
+}
+
+template <class T, class U>
+constexpr bool operator<(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value < *opt : false;
+}
+
+template <class T, class U>
+constexpr bool operator<=(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt <= value : true;
+}
+
+template <class T, class U>
+constexpr bool operator<=(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value <= *opt : false;
+}
+
+template <class T, class U>
+constexpr bool operator>(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt > value : false;
+}
+
+template <class T, class U>
+constexpr bool operator>(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value > *opt : true;
+}
+
+template <class T, class U>
+constexpr bool operator>=(const Optional<T>& opt, const U& value) {
+  return opt.has_value() ? *opt >= value : false;
+}
+
+template <class T, class U>
+constexpr bool operator>=(const U& value, const Optional<T>& opt) {
+  return opt.has_value() ? value >= *opt : true;
+}
+
+template <class T>
+constexpr Optional<std::decay_t<T>> make_optional(T&& value) {
+  return Optional<std::decay_t<T>>(std::forward<T>(value));
+}
+
+template <class T, class... Args>
+constexpr Optional<T> make_optional(Args&&... args) {
+  return Optional<T>(in_place, std::forward<Args>(args)...);
+}
+
+template <class T, class U, class... Args>
+constexpr Optional<T> make_optional(std::initializer_list<U> il,
+                                    Args&&... args) {
+  return Optional<T>(in_place, il, std::forward<Args>(args)...);
+}
+
+// Partial specialization for a function template is not allowed. Also, it is
+// not allowed to add overload function to std namespace, while it is allowed
+// to specialize the template in std. Thus, swap() (kind of) overloading is
+// defined in base namespace, instead.
+template <class T>
+std::enable_if_t<std::is_move_constructible<T>::value &&
+                 internal::IsSwappable<T>::value>
+swap(Optional<T>& lhs, Optional<T>& rhs) {
+  lhs.swap(rhs);
+}
+
+}  // namespace base
+
+namespace std {
+
+template <class T>
+struct hash<gurl_base::Optional<T>> {
+  size_t operator()(const gurl_base::Optional<T>& opt) const {
+    return opt == gurl_base::nullopt ? 0 : std::hash<T>()(*opt);
+  }
+};
+
+}  // namespace std
+
+#endif  // BASE_OPTIONAL_H_
diff --git a/base/stl_util.h b/base/stl_util.h
new file mode 100644
index 0000000..d6ca464
--- /dev/null
+++ b/base/stl_util.h
@@ -0,0 +1,657 @@
+// Copyright (c) 2011 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.
+
+// Derived from google3/util/gtl/stl_util.h
+
+#ifndef BASE_STL_UTIL_H_
+#define BASE_STL_UTIL_H_
+
+#include <algorithm>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "polyfills/base/logging.h"
+#include "base/optional.h"
+#include "base/template_util.h"
+
+namespace gurl_base {
+
+namespace internal {
+
+// Calls erase on iterators of matching elements.
+template <typename Container, typename Predicate>
+void IterateAndEraseIf(Container& container, Predicate pred) {
+  for (auto it = container.begin(); it != container.end();) {
+    if (pred(*it))
+      it = container.erase(it);
+    else
+      ++it;
+  }
+}
+
+template <typename Iter>
+constexpr bool IsRandomAccessIter =
+    std::is_same<typename std::iterator_traits<Iter>::iterator_category,
+                 std::random_access_iterator_tag>::value;
+
+// Utility type traits used for specializing gurl_base::Contains() below.
+template <typename Container, typename Element, typename = void>
+struct HasFindWithNpos : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasFindWithNpos<
+    Container,
+    Element,
+    void_t<decltype(std::declval<const Container&>().find(
+                        std::declval<const Element&>()) != Container::npos)>>
+    : std::true_type {};
+
+template <typename Container, typename Element, typename = void>
+struct HasFindWithEnd : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasFindWithEnd<Container,
+                      Element,
+                      void_t<decltype(std::declval<const Container&>().find(
+                                          std::declval<const Element&>()) !=
+                                      std::declval<const Container&>().end())>>
+    : std::true_type {};
+
+template <typename Container, typename Element, typename = void>
+struct HasContains : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasContains<Container,
+                   Element,
+                   void_t<decltype(std::declval<const Container&>().contains(
+                       std::declval<const Element&>()))>> : std::true_type {};
+
+}  // namespace internal
+
+// C++14 implementation of C++17's std::size():
+// http://en.cppreference.com/w/cpp/iterator/size
+template <typename Container>
+constexpr auto size(const Container& c) -> decltype(c.size()) {
+  return c.size();
+}
+
+template <typename T, size_t N>
+constexpr size_t size(const T (&array)[N]) noexcept {
+  return N;
+}
+
+// C++14 implementation of C++17's std::empty():
+// http://en.cppreference.com/w/cpp/iterator/empty
+template <typename Container>
+constexpr auto empty(const Container& c) -> decltype(c.empty()) {
+  return c.empty();
+}
+
+template <typename T, size_t N>
+constexpr bool empty(const T (&array)[N]) noexcept {
+  return false;
+}
+
+template <typename T>
+constexpr bool empty(std::initializer_list<T> il) noexcept {
+  return il.size() == 0;
+}
+
+// C++14 implementation of C++17's std::data():
+// http://en.cppreference.com/w/cpp/iterator/data
+template <typename Container>
+constexpr auto data(Container& c) -> decltype(c.data()) {
+  return c.data();
+}
+
+// std::basic_string::data() had no mutable overload prior to C++17 [1].
+// Hence this overload is provided.
+// Note: str[0] is safe even for empty strings, as they are guaranteed to be
+// null-terminated [2].
+//
+// [1] http://en.cppreference.com/w/cpp/string/basic_string/data
+// [2] http://en.cppreference.com/w/cpp/string/basic_string/operator_at
+template <typename CharT, typename Traits, typename Allocator>
+CharT* data(std::basic_string<CharT, Traits, Allocator>& str) {
+  return std::addressof(str[0]);
+}
+
+template <typename Container>
+constexpr auto data(const Container& c) -> decltype(c.data()) {
+  return c.data();
+}
+
+template <typename T, size_t N>
+constexpr T* data(T (&array)[N]) noexcept {
+  return array;
+}
+
+template <typename T>
+constexpr const T* data(std::initializer_list<T> il) noexcept {
+  return il.begin();
+}
+
+// Returns a const reference to the underlying container of a container adapter.
+// Works for std::priority_queue, std::queue, and std::stack.
+template <class A>
+const typename A::container_type& GetUnderlyingContainer(const A& adapter) {
+  struct ExposedAdapter : A {
+    using A::c;
+  };
+  return adapter.*&ExposedAdapter::c;
+}
+
+// Clears internal memory of an STL object.
+// STL clear()/reserve(0) does not always free internal memory allocated
+// This function uses swap/destructor to ensure the internal memory is freed.
+template<class T>
+void STLClearObject(T* obj) {
+  T tmp;
+  tmp.swap(*obj);
+  // Sometimes "T tmp" allocates objects with memory (arena implementation?).
+  // Hence using additional reserve(0) even if it doesn't always work.
+  obj->reserve(0);
+}
+
+// Counts the number of instances of val in a container.
+template <typename Container, typename T>
+typename std::iterator_traits<
+    typename Container::const_iterator>::difference_type
+STLCount(const Container& container, const T& val) {
+  return std::count(container.begin(), container.end(), val);
+}
+
+// General purpose implementation to check if |container| contains |value|.
+template <typename Container,
+          typename Value,
+          std::enable_if_t<
+              !internal::HasFindWithNpos<Container, Value>::value &&
+              !internal::HasFindWithEnd<Container, Value>::value &&
+              !internal::HasContains<Container, Value>::value>* = nullptr>
+bool Contains(const Container& container, const Value& value) {
+  using std::begin;
+  using std::end;
+  return std::find(begin(container), end(container), value) != end(container);
+}
+
+// Specialized Contains() implementation for when |container| has a find()
+// member function and a static npos member, but no contains() member function.
+template <typename Container,
+          typename Value,
+          std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
+                           !internal::HasContains<Container, Value>::value>* =
+              nullptr>
+bool Contains(const Container& container, const Value& value) {
+  return container.find(value) != Container::npos;
+}
+
+// Specialized Contains() implementation for when |container| has a find()
+// and end() member function, but no contains() member function.
+template <typename Container,
+          typename Value,
+          std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
+                           !internal::HasContains<Container, Value>::value>* =
+              nullptr>
+bool Contains(const Container& container, const Value& value) {
+  return container.find(value) != container.end();
+}
+
+// Specialized Contains() implementation for when |container| has a contains()
+// member function.
+template <
+    typename Container,
+    typename Value,
+    std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
+bool Contains(const Container& container, const Value& value) {
+  return container.contains(value);
+}
+
+// O(1) implementation of const casting an iterator for any sequence,
+// associative or unordered associative container in the STL.
+//
+// Reference: https://stackoverflow.com/a/10669041
+template <typename Container,
+          typename ConstIter,
+          std::enable_if_t<!internal::IsRandomAccessIter<ConstIter>>* = nullptr>
+constexpr auto ConstCastIterator(Container& c, ConstIter it) {
+  return c.erase(it, it);
+}
+
+// Explicit overload for std::forward_list where erase() is named erase_after().
+template <typename T, typename Allocator>
+constexpr auto ConstCastIterator(
+    std::forward_list<T, Allocator>& c,
+    typename std::forward_list<T, Allocator>::const_iterator it) {
+// The erase_after(it, it) trick used below does not work for libstdc++ [1],
+// thus we need a different way.
+// TODO(crbug.com/972541): Remove this workaround once libstdc++ is fixed on all
+// platforms.
+//
+// [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90857
+#if defined(__GLIBCXX__)
+  return c.insert_after(it, {});
+#else
+  return c.erase_after(it, it);
+#endif
+}
+
+// Specialized O(1) const casting for random access iterators. This is
+// necessary, because erase() is either not available (e.g. array-like
+// containers), or has O(n) complexity (e.g. std::deque or std::vector).
+template <typename Container,
+          typename ConstIter,
+          std::enable_if_t<internal::IsRandomAccessIter<ConstIter>>* = nullptr>
+constexpr auto ConstCastIterator(Container& c, ConstIter it) {
+  using std::begin;
+  using std::cbegin;
+  return begin(c) + (it - cbegin(c));
+}
+
+namespace internal {
+
+template <typename Map, typename Key, typename Value>
+std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
+                                                           Key&& key,
+                                                           Value&& value) {
+  auto lower = map.lower_bound(key);
+  if (lower != map.end() && !map.key_comp()(key, lower->first)) {
+    // key already exists, perform assignment.
+    lower->second = std::forward<Value>(value);
+    return {lower, false};
+  }
+
+  // key did not yet exist, insert it.
+  return {map.emplace_hint(lower, std::forward<Key>(key),
+                           std::forward<Value>(value)),
+          true};
+}
+
+template <typename Map, typename Key, typename Value>
+typename Map::iterator InsertOrAssignImpl(Map& map,
+                                          typename Map::const_iterator hint,
+                                          Key&& key,
+                                          Value&& value) {
+  auto&& key_comp = map.key_comp();
+  if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
+    if (hint == map.end() || key_comp(key, hint->first)) {
+      // *(hint - 1) < key < *hint => key did not exist and hint is correct.
+      return map.emplace_hint(hint, std::forward<Key>(key),
+                              std::forward<Value>(value));
+    }
+
+    if (!key_comp(hint->first, key)) {
+      // key == *hint => key already exists and hint is correct.
+      auto mutable_hint = ConstCastIterator(map, hint);
+      mutable_hint->second = std::forward<Value>(value);
+      return mutable_hint;
+    }
+  }
+
+  // hint was not helpful, dispatch to hintless version.
+  return InsertOrAssignImpl(map, std::forward<Key>(key),
+                            std::forward<Value>(value))
+      .first;
+}
+
+template <typename Map, typename Key, typename... Args>
+std::pair<typename Map::iterator, bool> TryEmplaceImpl(Map& map,
+                                                       Key&& key,
+                                                       Args&&... args) {
+  auto lower = map.lower_bound(key);
+  if (lower != map.end() && !map.key_comp()(key, lower->first)) {
+    // key already exists, do nothing.
+    return {lower, false};
+  }
+
+  // key did not yet exist, insert it.
+  return {map.emplace_hint(lower, std::piecewise_construct,
+                           std::forward_as_tuple(std::forward<Key>(key)),
+                           std::forward_as_tuple(std::forward<Args>(args)...)),
+          true};
+}
+
+template <typename Map, typename Key, typename... Args>
+typename Map::iterator TryEmplaceImpl(Map& map,
+                                      typename Map::const_iterator hint,
+                                      Key&& key,
+                                      Args&&... args) {
+  auto&& key_comp = map.key_comp();
+  if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
+    if (hint == map.end() || key_comp(key, hint->first)) {
+      // *(hint - 1) < key < *hint => key did not exist and hint is correct.
+      return map.emplace_hint(
+          hint, std::piecewise_construct,
+          std::forward_as_tuple(std::forward<Key>(key)),
+          std::forward_as_tuple(std::forward<Args>(args)...));
+    }
+
+    if (!key_comp(hint->first, key)) {
+      // key == *hint => no-op, return correct hint.
+      return ConstCastIterator(map, hint);
+    }
+  }
+
+  // hint was not helpful, dispatch to hintless version.
+  return TryEmplaceImpl(map, std::forward<Key>(key),
+                        std::forward<Args>(args)...)
+      .first;
+}
+
+}  // namespace internal
+
+// Implementation of C++17's std::map::insert_or_assign as a free function.
+template <typename Map, typename Value>
+std::pair<typename Map::iterator, bool>
+InsertOrAssign(Map& map, const typename Map::key_type& key, Value&& value) {
+  return internal::InsertOrAssignImpl(map, key, std::forward<Value>(value));
+}
+
+template <typename Map, typename Value>
+std::pair<typename Map::iterator, bool>
+InsertOrAssign(Map& map, typename Map::key_type&& key, Value&& value) {
+  return internal::InsertOrAssignImpl(map, std::move(key),
+                                      std::forward<Value>(value));
+}
+
+// Implementation of C++17's std::map::insert_or_assign with hint as a free
+// function.
+template <typename Map, typename Value>
+typename Map::iterator InsertOrAssign(Map& map,
+                                      typename Map::const_iterator hint,
+                                      const typename Map::key_type& key,
+                                      Value&& value) {
+  return internal::InsertOrAssignImpl(map, hint, key,
+                                      std::forward<Value>(value));
+}
+
+template <typename Map, typename Value>
+typename Map::iterator InsertOrAssign(Map& map,
+                                      typename Map::const_iterator hint,
+                                      typename Map::key_type&& key,
+                                      Value&& value) {
+  return internal::InsertOrAssignImpl(map, hint, std::move(key),
+                                      std::forward<Value>(value));
+}
+
+// Implementation of C++17's std::map::try_emplace as a free function.
+template <typename Map, typename... Args>
+std::pair<typename Map::iterator, bool>
+TryEmplace(Map& map, const typename Map::key_type& key, Args&&... args) {
+  return internal::TryEmplaceImpl(map, key, std::forward<Args>(args)...);
+}
+
+template <typename Map, typename... Args>
+std::pair<typename Map::iterator, bool> TryEmplace(Map& map,
+                                                   typename Map::key_type&& key,
+                                                   Args&&... args) {
+  return internal::TryEmplaceImpl(map, std::move(key),
+                                  std::forward<Args>(args)...);
+}
+
+// Implementation of C++17's std::map::try_emplace with hint as a free
+// function.
+template <typename Map, typename... Args>
+typename Map::iterator TryEmplace(Map& map,
+                                  typename Map::const_iterator hint,
+                                  const typename Map::key_type& key,
+                                  Args&&... args) {
+  return internal::TryEmplaceImpl(map, hint, key, std::forward<Args>(args)...);
+}
+
+template <typename Map, typename... Args>
+typename Map::iterator TryEmplace(Map& map,
+                                  typename Map::const_iterator hint,
+                                  typename Map::key_type&& key,
+                                  Args&&... args) {
+  return internal::TryEmplaceImpl(map, hint, std::move(key),
+                                  std::forward<Args>(args)...);
+}
+
+// Returns true if the container is sorted.
+template <typename Container>
+bool STLIsSorted(const Container& cont) {
+  return std::is_sorted(std::begin(cont), std::end(cont));
+}
+
+// Returns a new ResultType containing the difference of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
+  GURL_DCHECK(STLIsSorted(a1));
+  GURL_DCHECK(STLIsSorted(a2));
+  ResultType difference;
+  std::set_difference(a1.begin(), a1.end(),
+                      a2.begin(), a2.end(),
+                      std::inserter(difference, difference.end()));
+  return difference;
+}
+
+// Returns a new ResultType containing the union of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
+  GURL_DCHECK(STLIsSorted(a1));
+  GURL_DCHECK(STLIsSorted(a2));
+  ResultType result;
+  std::set_union(a1.begin(), a1.end(),
+                 a2.begin(), a2.end(),
+                 std::inserter(result, result.end()));
+  return result;
+}
+
+// Returns a new ResultType containing the intersection of two sorted
+// containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
+  GURL_DCHECK(STLIsSorted(a1));
+  GURL_DCHECK(STLIsSorted(a2));
+  ResultType result;
+  std::set_intersection(a1.begin(), a1.end(),
+                        a2.begin(), a2.end(),
+                        std::inserter(result, result.end()));
+  return result;
+}
+
+// Returns true if the sorted container |a1| contains all elements of the sorted
+// container |a2|.
+template <typename Arg1, typename Arg2>
+bool STLIncludes(const Arg1& a1, const Arg2& a2) {
+  GURL_DCHECK(STLIsSorted(a1));
+  GURL_DCHECK(STLIsSorted(a2));
+  return std::includes(a1.begin(), a1.end(),
+                       a2.begin(), a2.end());
+}
+
+// Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
+// http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
+// They provide a generic way to erase elements from a container.
+// The functions here implement these for the standard containers until those
+// functions are available in the C++ standard.
+// For Chromium containers overloads should be defined in their own headers
+// (like standard containers).
+// Note: there is no std::erase for standard associative containers so we don't
+// have it either.
+
+template <typename CharT, typename Traits, typename Allocator, typename Value>
+void Erase(std::basic_string<CharT, Traits, Allocator>& container,
+           const Value& value) {
+  container.erase(std::remove(container.begin(), container.end(), value),
+                  container.end());
+}
+
+template <typename CharT, typename Traits, typename Allocator, class Predicate>
+void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
+             Predicate pred) {
+  container.erase(std::remove_if(container.begin(), container.end(), pred),
+                  container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::deque<T, Allocator>& container, const Value& value) {
+  container.erase(std::remove(container.begin(), container.end(), value),
+                  container.end());
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
+  container.erase(std::remove_if(container.begin(), container.end(), pred),
+                  container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::vector<T, Allocator>& container, const Value& value) {
+  container.erase(std::remove(container.begin(), container.end(), value),
+                  container.end());
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
+  container.erase(std::remove_if(container.begin(), container.end(), pred),
+                  container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
+  // Unlike std::forward_list::remove, this function template accepts
+  // heterogeneous types and does not force a conversion to the container's
+  // value type before invoking the == operator.
+  container.remove_if([&](const T& cur) { return cur == value; });
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
+  container.remove_if(pred);
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::list<T, Allocator>& container, const Value& value) {
+  // Unlike std::list::remove, this function template accepts heterogeneous
+  // types and does not force a conversion to the container's value type before
+  // invoking the == operator.
+  container.remove_if([&](const T& cur) { return cur == value; });
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
+  container.remove_if(pred);
+}
+
+template <class Key, class T, class Compare, class Allocator, class Predicate>
+void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class T, class Compare, class Allocator, class Predicate>
+void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
+             Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class Compare, class Allocator, class Predicate>
+void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class Compare, class Allocator, class Predicate>
+void EraseIf(std::multiset<Key, Compare, Allocator>& container,
+             Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+          class T,
+          class Hash,
+          class KeyEqual,
+          class Allocator,
+          class Predicate>
+void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
+             Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+          class T,
+          class Hash,
+          class KeyEqual,
+          class Allocator,
+          class Predicate>
+void EraseIf(
+    std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
+    Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+          class Hash,
+          class KeyEqual,
+          class Allocator,
+          class Predicate>
+void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
+             Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+          class Hash,
+          class KeyEqual,
+          class Allocator,
+          class Predicate>
+void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
+             Predicate pred) {
+  internal::IterateAndEraseIf(container, pred);
+}
+
+// A helper class to be used as the predicate with |EraseIf| to implement
+// in-place set intersection. Helps implement the algorithm of going through
+// each container an element at a time, erasing elements from the first
+// container if they aren't in the second container. Requires each container be
+// sorted. Note that the logic below appears inverted since it is returning
+// whether an element should be erased.
+template <class Collection>
+class IsNotIn {
+ public:
+  explicit IsNotIn(const Collection& collection)
+      : i_(collection.begin()), end_(collection.end()) {}
+
+  bool operator()(const typename Collection::value_type& x) {
+    while (i_ != end_ && *i_ < x)
+      ++i_;
+    if (i_ == end_)
+      return true;
+    if (*i_ == x) {
+      ++i_;
+      return false;
+    }
+    return true;
+  }
+
+ private:
+  typename Collection::const_iterator i_;
+  const typename Collection::const_iterator end_;
+};
+
+// Helper for returning the optional value's address, or nullptr.
+template <class T>
+T* OptionalOrNullptr(gurl_base::Optional<T>& optional) {
+  return optional.has_value() ? &optional.value() : nullptr;
+}
+
+template <class T>
+const T* OptionalOrNullptr(const gurl_base::Optional<T>& optional) {
+  return optional.has_value() ? &optional.value() : nullptr;
+}
+
+}  // namespace base
+
+#endif  // BASE_STL_UTIL_H_
diff --git a/base/strings/BUILD b/base/strings/BUILD
new file mode 100644
index 0000000..60aebcf
--- /dev/null
+++ b/base/strings/BUILD
@@ -0,0 +1,32 @@
+# 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.
+
+cc_library(
+    name = "strings",
+    srcs = [
+        "string16.cc",
+        "string_piece.cc",
+        "string_util.cc",
+        "string_util_constants.cc",
+        "utf_string_conversion_utils.cc",
+        "utf_string_conversions.cc",
+    ],
+    hdrs = [
+        "char_traits.h",
+        "string16.h",
+        "string_piece.h",
+        "string_piece_forward.h",
+        "string_util.h",
+        "string_util_posix.h",
+        "utf_string_conversion_utils.h",
+        "utf_string_conversions.h",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//base",
+        "//base/third_party/icu",
+        "//build:build_config",
+        "//polyfills",
+    ],
+)
diff --git a/base/strings/char_traits.h b/base/strings/char_traits.h
new file mode 100644
index 0000000..0fe9f26
--- /dev/null
+++ b/base/strings/char_traits.h
@@ -0,0 +1,92 @@
+// Copyright 2018 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.
+
+#ifndef BASE_STRINGS_CHAR_TRAITS_H_
+#define BASE_STRINGS_CHAR_TRAITS_H_
+
+#include <stddef.h>
+
+#include "base/compiler_specific.h"
+
+namespace gurl_base {
+
+// constexpr version of http://en.cppreference.com/w/cpp/string/char_traits.
+// This currently just implements the bits needed to support a (mostly)
+// constexpr StringPiece.
+//
+// TODO(dcheng): Once we switch to C++17, most methods will become constexpr and
+// we can switch over to using the one in the standard library.
+template <typename T>
+struct CharTraits {
+  // Performs a lexographical comparison of the first N characters of |s1| and
+  // |s2|. Returns 0 if equal, -1 if |s1| is less than |s2|, and 1 if |s1| is
+  // greater than |s2|.
+  static constexpr int compare(const T* s1, const T* s2, size_t n) noexcept;
+
+  // Returns the length of |s|, assuming null termination (and not including the
+  // terminating null).
+  static constexpr size_t length(const T* s) noexcept;
+};
+
+template <typename T>
+constexpr int CharTraits<T>::compare(const T* s1,
+                                     const T* s2,
+                                     size_t n) noexcept {
+  for (; n; --n, ++s1, ++s2) {
+    if (*s1 < *s2)
+      return -1;
+    if (*s1 > *s2)
+      return 1;
+  }
+  return 0;
+}
+
+template <typename T>
+constexpr size_t CharTraits<T>::length(const T* s) noexcept {
+  size_t i = 0;
+  for (; *s; ++s)
+    ++i;
+  return i;
+}
+
+// char specialization of CharTraits that can use clang's constexpr instrinsics,
+// where available.
+template <>
+struct CharTraits<char> {
+  static constexpr int compare(const char* s1,
+                               const char* s2,
+                               size_t n) noexcept;
+  static constexpr size_t length(const char* s) noexcept;
+};
+
+constexpr int CharTraits<char>::compare(const char* s1,
+                                        const char* s2,
+                                        size_t n) noexcept {
+#if HAS_FEATURE(cxx_constexpr_string_builtins)
+  return __builtin_memcmp(s1, s2, n);
+#else
+  for (; n; --n, ++s1, ++s2) {
+    if (*s1 < *s2)
+      return -1;
+    if (*s1 > *s2)
+      return 1;
+  }
+  return 0;
+#endif
+}
+
+constexpr size_t CharTraits<char>::length(const char* s) noexcept {
+#if defined(__clang__)
+  return __builtin_strlen(s);
+#else
+  size_t i = 0;
+  for (; *s; ++s)
+    ++i;
+  return i;
+#endif
+}
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_CHAR_TRAITS_H_
diff --git a/base/strings/char_traits_unittest.cc b/base/strings/char_traits_unittest.cc
new file mode 100644
index 0000000..d0fdc07
--- /dev/null
+++ b/base/strings/char_traits_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright 2018 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 "base/strings/char_traits.h"
+#include "base/strings/string16.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+TEST(CharTraitsTest, CharCompare) {
+  static_assert(CharTraits<char>::compare("abc", "def", 3) == -1, "");
+  static_assert(CharTraits<char>::compare("def", "def", 3) == 0, "");
+  static_assert(CharTraits<char>::compare("ghi", "def", 3) == 1, "");
+}
+
+TEST(CharTraitsTest, CharLength) {
+  static_assert(CharTraits<char>::length("") == 0, "");
+  static_assert(CharTraits<char>::length("abc") == 3, "");
+}
+
+TEST(CharTraitsTest, Char16TCompare) {
+  static_assert(CharTraits<char16_t>::compare(u"abc", u"def", 3) == -1, "");
+  static_assert(CharTraits<char16_t>::compare(u"def", u"def", 3) == 0, "");
+  static_assert(CharTraits<char16_t>::compare(u"ghi", u"def", 3) == 1, "");
+}
+
+TEST(CharTraitsTest, Char16TLength) {
+  static_assert(CharTraits<char16_t>::length(u"abc") == 3, "");
+}
+
+}  // namespace base
diff --git a/base/strings/latin1_string_conversions.cc b/base/strings/latin1_string_conversions.cc
new file mode 100644
index 0000000..5569015
--- /dev/null
+++ b/base/strings/latin1_string_conversions.cc
@@ -0,0 +1,19 @@
+// Copyright 2013 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 "base/strings/latin1_string_conversions.h"
+
+namespace gurl_base {
+
+string16 Latin1OrUTF16ToUTF16(size_t length,
+                              const Latin1Char* latin1,
+                              const char16* utf16) {
+  if (!length)
+    return string16();
+  if (latin1)
+    return string16(latin1, latin1 + length);
+  return string16(utf16, utf16 + length);
+}
+
+}  // namespace base
diff --git a/base/strings/latin1_string_conversions.h b/base/strings/latin1_string_conversions.h
new file mode 100644
index 0000000..3d60980
--- /dev/null
+++ b/base/strings/latin1_string_conversions.h
@@ -0,0 +1,34 @@
+// Copyright 2013 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.
+
+#ifndef BASE_STRINGS_LATIN1_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_LATIN1_STRING_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "polyfills/base/base_export.h"
+#include "base/strings/string16.h"
+
+namespace gurl_base {
+
+// This definition of Latin1Char matches the definition of LChar in Blink. We
+// use unsigned char rather than char to make less tempting to mix and match
+// Latin-1 and UTF-8 characters..
+typedef unsigned char Latin1Char;
+
+// This somewhat odd function is designed to help us convert from Blink Strings
+// to string16. A Blink string is either backed by an array of Latin-1
+// characters or an array of UTF-16 characters. This function is called by
+// WebString::operator string16() to convert one or the other character array
+// to string16. This function is defined here rather than in WebString.h to
+// avoid binary bloat in all the callers of the conversion operator.
+BASE_EXPORT string16 Latin1OrUTF16ToUTF16(size_t length,
+                                          const Latin1Char* latin1,
+                                          const char16* utf16);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_LATIN1_STRING_CONVERSIONS_H_
diff --git a/base/strings/nullable_string16.cc b/base/strings/nullable_string16.cc
new file mode 100644
index 0000000..618800d
--- /dev/null
+++ b/base/strings/nullable_string16.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 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 "base/strings/nullable_string16.h"
+
+#include <ostream>
+#include <utility>
+
+namespace gurl_base {
+NullableString16::NullableString16() = default;
+NullableString16::NullableString16(const NullableString16& other) = default;
+NullableString16::NullableString16(NullableString16&& other) = default;
+
+NullableString16::NullableString16(const string16& string, bool is_null) {
+  if (!is_null)
+    string_.emplace(string);
+}
+
+NullableString16::NullableString16(Optional<string16> optional_string16)
+    : string_(std::move(optional_string16)) {}
+
+NullableString16::~NullableString16() = default;
+NullableString16& NullableString16::operator=(const NullableString16& other) =
+    default;
+NullableString16& NullableString16::operator=(NullableString16&& other) =
+    default;
+
+std::ostream& operator<<(std::ostream& out, const NullableString16& value) {
+  return value.is_null() ? out << "(null)" : out << value.string();
+}
+
+}  // namespace base
diff --git a/base/strings/nullable_string16.h b/base/strings/nullable_string16.h
new file mode 100644
index 0000000..f2ca7bd
--- /dev/null
+++ b/base/strings/nullable_string16.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2010 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.
+
+#ifndef BASE_STRINGS_NULLABLE_STRING16_H_
+#define BASE_STRINGS_NULLABLE_STRING16_H_
+
+#include <iosfwd>
+
+#include "polyfills/base/base_export.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+
+namespace gurl_base {
+
+// This class is a simple wrapper for string16 which also contains a null
+// state.  This should be used only where the difference between null and
+// empty is meaningful.
+class BASE_EXPORT NullableString16 {
+ public:
+  NullableString16();
+  NullableString16(const NullableString16& other);
+  NullableString16(NullableString16&& other);
+  NullableString16(const string16& string, bool is_null);
+  explicit NullableString16(Optional<string16> optional_string16);
+  ~NullableString16();
+
+  NullableString16& operator=(const NullableString16& other);
+  NullableString16& operator=(NullableString16&& other);
+
+  const string16& string() const {
+    return string_ ? *string_ : EmptyString16();
+  }
+  bool is_null() const { return !string_; }
+  const Optional<string16>& as_optional_string16() const { return string_; }
+
+ private:
+  Optional<string16> string_;
+};
+
+inline bool operator==(const NullableString16& a, const NullableString16& b) {
+  return a.as_optional_string16() == b.as_optional_string16();
+}
+
+inline bool operator!=(const NullableString16& a, const NullableString16& b) {
+  return !(a == b);
+}
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+                                     const NullableString16& value);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_NULLABLE_STRING16_H_
diff --git a/base/strings/nullable_string16_unittest.cc b/base/strings/nullable_string16_unittest.cc
new file mode 100644
index 0000000..e3d063f
--- /dev/null
+++ b/base/strings/nullable_string16_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 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 "base/strings/nullable_string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+TEST(NullableString16Test, DefaultConstructor) {
+  NullableString16 s;
+  EXPECT_TRUE(s.is_null());
+  EXPECT_EQ(string16(), s.string());
+}
+
+TEST(NullableString16Test, Equals) {
+  NullableString16 a(ASCIIToUTF16("hello"), false);
+  NullableString16 b(ASCIIToUTF16("hello"), false);
+  EXPECT_EQ(a, b);
+}
+
+TEST(NullableString16Test, NotEquals) {
+  NullableString16 a(ASCIIToUTF16("hello"), false);
+  NullableString16 b(ASCIIToUTF16("world"), false);
+  EXPECT_NE(a, b);
+}
+
+TEST(NullableString16Test, NotEqualsNull) {
+  NullableString16 a(ASCIIToUTF16("hello"), false);
+  NullableString16 b;
+  EXPECT_NE(a, b);
+}
+
+}  // namespace base
diff --git a/base/strings/pattern.cc b/base/strings/pattern.cc
new file mode 100644
index 0000000..65ec075
--- /dev/null
+++ b/base/strings/pattern.cc
@@ -0,0 +1,155 @@
+// Copyright 2015 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 "base/strings/pattern.h"
+
+#include "base/third_party/icu/icu_utf.h"
+
+namespace gurl_base {
+
+namespace {
+
+constexpr bool IsWildcard(base_icu::UChar32 character) {
+  return character == '*' || character == '?';
+}
+
+// Searches for the next subpattern of |pattern| in |string|, up to the given
+// |maximum_distance|. The subpattern extends from the start of |pattern| up to
+// the first wildcard character (or the end of the string). If the value of
+// |maximum_distance| is negative, the maximum distance is considered infinite.
+template <typename CHAR, typename NEXT>
+constexpr bool SearchForChars(const CHAR** pattern,
+                              const CHAR* pattern_end,
+                              const CHAR** string,
+                              const CHAR* string_end,
+                              int maximum_distance,
+                              NEXT next) {
+  const CHAR* pattern_start = *pattern;
+  const CHAR* string_start = *string;
+  bool escape = false;
+  while (true) {
+    if (*pattern == pattern_end) {
+      // If this is the end of the pattern, only accept the end of the string;
+      // anything else falls through to the mismatch case.
+      if (*string == string_end)
+        return true;
+    } else {
+      // If we have found a wildcard, we're done.
+      if (!escape && IsWildcard(**pattern))
+        return true;
+
+      // Check if the escape character is found. If so, skip it and move to the
+      // next character.
+      if (!escape && **pattern == '\\') {
+        escape = true;
+        next(pattern, pattern_end);
+        continue;
+      }
+
+      escape = false;
+
+      if (*string == string_end)
+        return false;
+
+      // Check if the chars match, if so, increment the ptrs.
+      const CHAR* pattern_next = *pattern;
+      const CHAR* string_next = *string;
+      base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end);
+      if (pattern_char == next(&string_next, string_end) &&
+          pattern_char != CBU_SENTINEL) {
+        *pattern = pattern_next;
+        *string = string_next;
+        continue;
+      }
+    }
+
+    // Mismatch. If we have reached the maximum distance, return false,
+    // otherwise restart at the beginning of the pattern with the next character
+    // in the string.
+    // TODO(bauerb): This is a naive implementation of substring search, which
+    // could be implemented with a more efficient algorithm, e.g.
+    // Knuth-Morris-Pratt (at the expense of requiring preprocessing).
+    if (maximum_distance == 0)
+      return false;
+
+    // Because unlimited distance is represented as -1, this will never reach 0
+    // and therefore fail the match above.
+    maximum_distance--;
+    *pattern = pattern_start;
+    next(&string_start, string_end);
+    *string = string_start;
+  }
+}
+
+// Consumes consecutive wildcard characters (? or *). Returns the maximum number
+// of characters matched by the sequence of wildcards, or -1 if the wildcards
+// match an arbitrary number of characters (which is the case if it contains at
+// least one *).
+template <typename CHAR, typename NEXT>
+constexpr int EatWildcards(const CHAR** pattern, const CHAR* end, NEXT next) {
+  int num_question_marks = 0;
+  bool has_asterisk = false;
+  while (*pattern != end) {
+    if (**pattern == '?') {
+      num_question_marks++;
+    } else if (**pattern == '*') {
+      has_asterisk = true;
+    } else {
+      break;
+    }
+
+    next(pattern, end);
+  }
+  return has_asterisk ? -1 : num_question_marks;
+}
+
+template <typename CHAR, typename NEXT>
+constexpr bool MatchPatternT(const CHAR* eval,
+                             const CHAR* eval_end,
+                             const CHAR* pattern,
+                             const CHAR* pattern_end,
+                             NEXT next) {
+  do {
+    int maximum_wildcard_length = EatWildcards(&pattern, pattern_end, next);
+    if (!SearchForChars(&pattern, pattern_end, &eval, eval_end,
+                        maximum_wildcard_length, next)) {
+      return false;
+    }
+  } while (pattern != pattern_end);
+  return true;
+}
+
+struct NextCharUTF8 {
+  base_icu::UChar32 operator()(const char** p, const char* end) {
+    base_icu::UChar32 c;
+    int offset = 0;
+    CBU8_NEXT(*p, offset, end - *p, c);
+    *p += offset;
+    return c;
+  }
+};
+
+struct NextCharUTF16 {
+  base_icu::UChar32 operator()(const char16** p, const char16* end) {
+    base_icu::UChar32 c;
+    int offset = 0;
+    CBU16_NEXT(*p, offset, end - *p, c);
+    *p += offset;
+    return c;
+  }
+};
+
+}  // namespace
+
+bool MatchPattern(StringPiece eval, StringPiece pattern) {
+  return MatchPatternT(eval.data(), eval.data() + eval.size(), pattern.data(),
+                       pattern.data() + pattern.size(), NextCharUTF8());
+}
+
+bool MatchPattern(StringPiece16 eval, StringPiece16 pattern) {
+  return MatchPatternT(eval.data(), eval.data() + eval.size(), pattern.data(),
+                       pattern.data() + pattern.size(), NextCharUTF16());
+}
+
+}  // namespace base
diff --git a/base/strings/pattern.h b/base/strings/pattern.h
new file mode 100644
index 0000000..3d280d0
--- /dev/null
+++ b/base/strings/pattern.h
@@ -0,0 +1,23 @@
+// Copyright 2015 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.
+
+#ifndef BASE_STRINGS_PATTERN_H_
+#define BASE_STRINGS_PATTERN_H_
+
+#include "polyfills/base/base_export.h"
+#include "base/strings/string_piece.h"
+
+namespace gurl_base {
+
+// Returns true if the |string| passed in matches the |pattern|. The pattern
+// string can contain wildcards like * and ?.
+//
+// The backslash character (\) is an escape character for * and ?.
+// ? matches 0 or 1 character, while * matches 0 or more characters.
+BASE_EXPORT bool MatchPattern(StringPiece string, StringPiece pattern);
+BASE_EXPORT bool MatchPattern(StringPiece16 string, StringPiece16 pattern);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_PATTERN_H_
diff --git a/base/strings/pattern_unittest.cc b/base/strings/pattern_unittest.cc
new file mode 100644
index 0000000..540f784
--- /dev/null
+++ b/base/strings/pattern_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2015 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 "base/strings/pattern.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+TEST(StringUtilTest, MatchPatternTest) {
+  EXPECT_TRUE(MatchPattern("www.google.com", "*.com"));
+  EXPECT_TRUE(MatchPattern("www.google.com", "*"));
+  EXPECT_FALSE(MatchPattern("www.google.com", "www*.g*.org"));
+  EXPECT_TRUE(MatchPattern("Hello", "H?l?o"));
+  EXPECT_FALSE(MatchPattern("www.google.com", "http://*)"));
+  EXPECT_FALSE(MatchPattern("www.msn.com", "*.COM"));
+  EXPECT_TRUE(MatchPattern("Hello*1234", "He??o\\*1*"));
+  EXPECT_FALSE(MatchPattern("", "*.*"));
+  EXPECT_TRUE(MatchPattern("", "*"));
+  EXPECT_TRUE(MatchPattern("", "?"));
+  EXPECT_TRUE(MatchPattern("", ""));
+  EXPECT_FALSE(MatchPattern("Hello", ""));
+  EXPECT_TRUE(MatchPattern("Hello*", "Hello*"));
+  EXPECT_TRUE(MatchPattern("abcd", "*???"));
+  EXPECT_FALSE(MatchPattern("abcd", "???"));
+  EXPECT_TRUE(MatchPattern("abcb", "a*b"));
+  EXPECT_FALSE(MatchPattern("abcb", "a?b"));
+
+  // Test UTF8 matching.
+  EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0", "*\xe2\x99\xa0"));
+  EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0.", "heart: ?."));
+  EXPECT_TRUE(MatchPattern("hearts: \xe2\x99\xa0\xe2\x99\xa0", "*"));
+  // Invalid sequences should be handled as a single invalid character.
+  EXPECT_TRUE(MatchPattern("invalid: \xef\xbf\xbe", "invalid: ?"));
+  // If the pattern has invalid characters, it shouldn't match anything.
+  EXPECT_FALSE(MatchPattern("\xf4\x90\x80\x80", "\xf4\x90\x80\x80"));
+
+  // Test UTF16 character matching.
+  EXPECT_TRUE(MatchPattern(UTF8ToUTF16("www.google.com"),
+                           UTF8ToUTF16("*.com")));
+  EXPECT_TRUE(MatchPattern(UTF8ToUTF16("Hello*1234"),
+                           UTF8ToUTF16("He??o\\*1*")));
+
+  // Some test cases that might cause naive implementations to exhibit
+  // exponential run time or fail.
+  EXPECT_TRUE(MatchPattern("Hello", "He********************************o"));
+  EXPECT_TRUE(MatchPattern("123456789012345678", "?????????????????*"));
+  EXPECT_TRUE(MatchPattern("aaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*a*b"));
+}
+
+}  // namespace base
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc
new file mode 100644
index 0000000..ab6e112
--- /dev/null
+++ b/base/strings/safe_sprintf.cc
@@ -0,0 +1,682 @@
+// Copyright 2013 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 "base/strings/safe_sprintf.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+
+#if !defined(NDEBUG)
+// In debug builds, we use RAW_CHECK() to print useful error messages, if
+// SafeSPrintf() is called with broken arguments.
+// As our contract promises that SafeSPrintf() can be called from any
+// restricted run-time context, it is not actually safe to call logging
+// functions from it; and we only ever do so for debug builds and hope for the
+// best. We should _never_ call any logging function other than RAW_CHECK(),
+// and we should _never_ include any logging code that is active in production
+// builds. Most notably, we should not include these logging functions in
+// unofficial release builds, even though those builds would otherwise have
+// DCHECKS() enabled.
+// In other words; please do not remove the #ifdef around this #include.
+// Instead, in production builds we opt for returning a degraded result,
+// whenever an error is encountered.
+// E.g. The broken function call
+//        SafeSPrintf("errno = %d (%x)", errno, strerror(errno))
+//      will print something like
+//        errno = 13, (%x)
+//      instead of
+//        errno = 13 (Access denied)
+//      In most of the anticipated use cases, that's probably the preferred
+//      behavior.
+#include "polyfills/base/logging.h"
+#define DEBUG_CHECK RAW_CHECK
+#else
+#define DEBUG_CHECK(x) do { if (x) { } } while (0)
+#endif
+
+namespace gurl_base {
+namespace strings {
+
+// The code in this file is extremely careful to be async-signal-safe.
+//
+// Most obviously, we avoid calling any code that could dynamically allocate
+// memory. Doing so would almost certainly result in bugs and dead-locks.
+// We also avoid calling any other STL functions that could have unintended
+// side-effects involving memory allocation or access to other shared
+// resources.
+//
+// But on top of that, we also avoid calling other library functions, as many
+// of them have the side-effect of calling getenv() (in order to deal with
+// localization) or accessing errno. The latter sounds benign, but there are
+// several execution contexts where it isn't even possible to safely read let
+// alone write errno.
+//
+// The stated design goal of the SafeSPrintf() function is that it can be
+// called from any context that can safely call C or C++ code (i.e. anything
+// that doesn't require assembly code).
+//
+// For a brief overview of some but not all of the issues with async-signal-
+// safety, refer to:
+// http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+
+namespace {
+const size_t kSSizeMaxConst = ((size_t)(ssize_t)-1) >> 1;
+
+const char kUpCaseHexDigits[]   = "0123456789ABCDEF";
+const char kDownCaseHexDigits[] = "0123456789abcdef";
+}
+
+#if defined(NDEBUG)
+// We would like to define kSSizeMax as std::numeric_limits<ssize_t>::max(),
+// but C++ doesn't allow us to do that for constants. Instead, we have to
+// use careful casting and shifting. We later use a static_assert to
+// verify that this worked correctly.
+namespace {
+const size_t kSSizeMax = kSSizeMaxConst;
+}
+#else  // defined(NDEBUG)
+// For efficiency, we really need kSSizeMax to be a constant. But for unit
+// tests, it should be adjustable. This allows us to verify edge cases without
+// having to fill the entire available address space. As a compromise, we make
+// kSSizeMax adjustable in debug builds, and then only compile that particular
+// part of the unit test in debug builds.
+namespace {
+static size_t kSSizeMax = kSSizeMaxConst;
+}
+
+namespace internal {
+void SetSafeSPrintfSSizeMaxForTest(size_t max) {
+  kSSizeMax = max;
+}
+
+size_t GetSafeSPrintfSSizeMaxForTest() {
+  return kSSizeMax;
+}
+}
+#endif  // defined(NDEBUG)
+
+namespace {
+class Buffer {
+ public:
+  // |buffer| is caller-allocated storage that SafeSPrintf() writes to. It
+  // has |size| bytes of writable storage. It is the caller's responsibility
+  // to ensure that the buffer is at least one byte in size, so that it fits
+  // the trailing NUL that will be added by the destructor. The buffer also
+  // must be smaller or equal to kSSizeMax in size.
+  Buffer(char* buffer, size_t size)
+      : buffer_(buffer),
+        size_(size - 1),  // Account for trailing NUL byte
+        count_(0) {
+// MSVS2013's standard library doesn't mark max() as constexpr yet. cl.exe
+// supports static_cast but doesn't really implement constexpr yet so it doesn't
+// complain, but clang does.
+#if __cplusplus >= 201103 && !(defined(__clang__) && defined(OS_WIN))
+    static_assert(kSSizeMaxConst ==
+                      static_cast<size_t>(std::numeric_limits<ssize_t>::max()),
+                  "kSSizeMaxConst should be the max value of an ssize_t");
+#endif
+    DEBUG_CHECK(size > 0);
+    DEBUG_CHECK(size <= kSSizeMax);
+  }
+
+  ~Buffer() {
+    // The code calling the constructor guaranteed that there was enough space
+    // to store a trailing NUL -- and in debug builds, we are actually
+    // verifying this with DEBUG_CHECK()s in the constructor. So, we can
+    // always unconditionally write the NUL byte in the destructor.  We do not
+    // need to adjust the count_, as SafeSPrintf() copies snprintf() in not
+    // including the NUL byte in its return code.
+    *GetInsertionPoint() = '\000';
+  }
+
+  // Returns true, iff the buffer is filled all the way to |kSSizeMax-1|. The
+  // caller can now stop adding more data, as GetCount() has reached its
+  // maximum possible value.
+  inline bool OutOfAddressableSpace() const {
+    return count_ == static_cast<size_t>(kSSizeMax - 1);
+  }
+
+  // Returns the number of bytes that would have been emitted to |buffer_|
+  // if it was sized sufficiently large. This number can be larger than
+  // |size_|, if the caller provided an insufficiently large output buffer.
+  // But it will never be bigger than |kSSizeMax-1|.
+  inline ssize_t GetCount() const {
+    DEBUG_CHECK(count_ < kSSizeMax);
+    return static_cast<ssize_t>(count_);
+  }
+
+  // Emits one |ch| character into the |buffer_| and updates the |count_| of
+  // characters that are currently supposed to be in the buffer.
+  // Returns "false", iff the buffer was already full.
+  // N.B. |count_| increases even if no characters have been written. This is
+  // needed so that GetCount() can return the number of bytes that should
+  // have been allocated for the |buffer_|.
+  inline bool Out(char ch) {
+    if (size_ >= 1 && count_ < size_) {
+      buffer_[count_] = ch;
+      return IncrementCountByOne();
+    }
+    // |count_| still needs to be updated, even if the buffer has been
+    // filled completely. This allows SafeSPrintf() to return the number of
+    // bytes that should have been emitted.
+    IncrementCountByOne();
+    return false;
+  }
+
+  // Inserts |padding|-|len| bytes worth of padding into the |buffer_|.
+  // |count_| will also be incremented by the number of bytes that were meant
+  // to be emitted. The |pad| character is typically either a ' ' space
+  // or a '0' zero, but other non-NUL values are legal.
+  // Returns "false", iff the the |buffer_| filled up (i.e. |count_|
+  // overflowed |size_|) at any time during padding.
+  inline bool Pad(char pad, size_t padding, size_t len) {
+    DEBUG_CHECK(pad);
+    DEBUG_CHECK(padding <= kSSizeMax);
+    for (; padding > len; --padding) {
+      if (!Out(pad)) {
+        if (--padding) {
+          IncrementCount(padding-len);
+        }
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // POSIX doesn't define any async-signal-safe function for converting
+  // an integer to ASCII. Define our own version.
+  //
+  // This also gives us the ability to make the function a little more
+  // powerful and have it deal with |padding|, with truncation, and with
+  // predicting the length of the untruncated output.
+  //
+  // IToASCII() converts an integer |i| to ASCII.
+  //
+  // Unlike similar functions in the standard C library, it never appends a
+  // NUL character. This is left for the caller to do.
+  //
+  // While the function signature takes a signed int64_t, the code decides at
+  // run-time whether to treat the argument as signed (int64_t) or as unsigned
+  // (uint64_t) based on the value of |sign|.
+  //
+  // It supports |base|s 2 through 16. Only a |base| of 10 is allowed to have
+  // a |sign|. Otherwise, |i| is treated as unsigned.
+  //
+  // For bases larger than 10, |upcase| decides whether lower-case or upper-
+  // case letters should be used to designate digits greater than 10.
+  //
+  // Padding can be done with either '0' zeros or ' ' spaces. Padding has to
+  // be positive and will always be applied to the left of the output.
+  //
+  // Prepends a |prefix| to the number (e.g. "0x"). This prefix goes to
+  // the left of |padding|, if |pad| is '0'; and to the right of |padding|
+  // if |pad| is ' '.
+  //
+  // Returns "false", if the |buffer_| overflowed at any time.
+  bool IToASCII(bool sign, bool upcase, int64_t i, int base,
+                char pad, size_t padding, const char* prefix);
+
+ private:
+  // Increments |count_| by |inc| unless this would cause |count_| to
+  // overflow |kSSizeMax-1|. Returns "false", iff an overflow was detected;
+  // it then clamps |count_| to |kSSizeMax-1|.
+  inline bool IncrementCount(size_t inc) {
+    // "inc" is either 1 or a "padding" value. Padding is clamped at
+    // run-time to at most kSSizeMax-1. So, we know that "inc" is always in
+    // the range 1..kSSizeMax-1.
+    // This allows us to compute "kSSizeMax - 1 - inc" without incurring any
+    // integer overflows.
+    DEBUG_CHECK(inc <= kSSizeMax - 1);
+    if (count_ > kSSizeMax - 1 - inc) {
+      count_ = kSSizeMax - 1;
+      return false;
+    }
+    count_ += inc;
+    return true;
+  }
+
+  // Convenience method for the common case of incrementing |count_| by one.
+  inline bool IncrementCountByOne() {
+    return IncrementCount(1);
+  }
+
+  // Return the current insertion point into the buffer. This is typically
+  // at |buffer_| + |count_|, but could be before that if truncation
+  // happened. It always points to one byte past the last byte that was
+  // successfully placed into the |buffer_|.
+  inline char* GetInsertionPoint() const {
+    size_t idx = count_;
+    if (idx > size_) {
+      idx = size_;
+    }
+    return buffer_ + idx;
+  }
+
+  // User-provided buffer that will receive the fully formatted output string.
+  char* buffer_;
+
+  // Number of bytes that are available in the buffer excluding the trailing
+  // NUL byte that will be added by the destructor.
+  const size_t size_;
+
+  // Number of bytes that would have been emitted to the buffer, if the buffer
+  // was sufficiently big. This number always excludes the trailing NUL byte
+  // and it is guaranteed to never grow bigger than kSSizeMax-1.
+  size_t count_;
+
+  DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+
+bool Buffer::IToASCII(bool sign, bool upcase, int64_t i, int base,
+                      char pad, size_t padding, const char* prefix) {
+  // Sanity check for parameters. None of these should ever fail, but see
+  // above for the rationale why we can't call GURL_CHECK().
+  DEBUG_CHECK(base >= 2);
+  DEBUG_CHECK(base <= 16);
+  DEBUG_CHECK(!sign || base == 10);
+  DEBUG_CHECK(pad == '0' || pad == ' ');
+  DEBUG_CHECK(padding <= kSSizeMax);
+  DEBUG_CHECK(!(sign && prefix && *prefix));
+
+  // Handle negative numbers, if the caller indicated that |i| should be
+  // treated as a signed number; otherwise treat |i| as unsigned (even if the
+  // MSB is set!)
+  // Details are tricky, because of limited data-types, but equivalent pseudo-
+  // code would look like:
+  //   if (sign && i < 0)
+  //     prefix = "-";
+  //   num = abs(i);
+  int minint = 0;
+  uint64_t num;
+  if (sign && i < 0) {
+    prefix = "-";
+
+    // Turn our number positive.
+    if (i == std::numeric_limits<int64_t>::min()) {
+      // The most negative integer needs special treatment.
+      minint = 1;
+      num = static_cast<uint64_t>(-(i + 1));
+    } else {
+      // "Normal" negative numbers are easy.
+      num = static_cast<uint64_t>(-i);
+    }
+  } else {
+    num = static_cast<uint64_t>(i);
+  }
+
+  // If padding with '0' zero, emit the prefix or '-' character now. Otherwise,
+  // make the prefix accessible in reverse order, so that we can later output
+  // it right between padding and the number.
+  // We cannot choose the easier approach of just reversing the number, as that
+  // fails in situations where we need to truncate numbers that have padding
+  // and/or prefixes.
+  const char* reverse_prefix = nullptr;
+  if (prefix && *prefix) {
+    if (pad == '0') {
+      while (*prefix) {
+        if (padding) {
+          --padding;
+        }
+        Out(*prefix++);
+      }
+      prefix = nullptr;
+    } else {
+      for (reverse_prefix = prefix; *reverse_prefix; ++reverse_prefix) {
+      }
+    }
+  } else
+    prefix = nullptr;
+  const size_t prefix_length = reverse_prefix - prefix;
+
+  // Loop until we have converted the entire number. Output at least one
+  // character (i.e. '0').
+  size_t start = count_;
+  size_t discarded = 0;
+  bool started = false;
+  do {
+    // Make sure there is still enough space left in our output buffer.
+    if (count_ >= size_) {
+      if (start < size_) {
+        // It is rare that we need to output a partial number. But if asked
+        // to do so, we will still make sure we output the correct number of
+        // leading digits.
+        // Since we are generating the digits in reverse order, we actually
+        // have to discard digits in the order that we have already emitted
+        // them. This is essentially equivalent to:
+        //   memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1)
+        for (char* move = buffer_ + start, *end = buffer_ + size_ - 1;
+             move < end;
+             ++move) {
+          *move = move[1];
+        }
+        ++discarded;
+        --count_;
+      } else if (count_ - size_ > 1) {
+        // Need to increment either |count_| or |discarded| to make progress.
+        // The latter is more efficient, as it eventually triggers fast
+        // handling of padding. But we have to ensure we don't accidentally
+        // change the overall state (i.e. switch the state-machine from
+        // discarding to non-discarding). |count_| needs to always stay
+        // bigger than |size_|.
+        --count_;
+        ++discarded;
+      }
+    }
+
+    // Output the next digit and (if necessary) compensate for the most
+    // negative integer needing special treatment. This works because,
+    // no matter the bit width of the integer, the lowest-most decimal
+    // integer always ends in 2, 4, 6, or 8.
+    if (!num && started) {
+      if (reverse_prefix > prefix) {
+        Out(*--reverse_prefix);
+      } else {
+        Out(pad);
+      }
+    } else {
+      started = true;
+      Out((upcase ? kUpCaseHexDigits : kDownCaseHexDigits)[num%base + minint]);
+    }
+
+    minint = 0;
+    num /= base;
+
+    // Add padding, if requested.
+    if (padding > 0) {
+      --padding;
+
+      // Performance optimization for when we are asked to output excessive
+      // padding, but our output buffer is limited in size.  Even if we output
+      // a 64bit number in binary, we would never write more than 64 plus
+      // prefix non-padding characters. So, once this limit has been passed,
+      // any further state change can be computed arithmetically; we know that
+      // by this time, our entire final output consists of padding characters
+      // that have all already been output.
+      if (discarded > 8*sizeof(num) + prefix_length) {
+        IncrementCount(padding);
+        padding = 0;
+      }
+    }
+  } while (num || padding || (reverse_prefix > prefix));
+
+  // Conversion to ASCII actually resulted in the digits being in reverse
+  // order. We can't easily generate them in forward order, as we can't tell
+  // the number of characters needed until we are done converting.
+  // So, now, we reverse the string (except for the possible '-' sign).
+  char* front = buffer_ + start;
+  char* back = GetInsertionPoint();
+  while (--back > front) {
+    char ch = *back;
+    *back = *front;
+    *front++ = ch;
+  }
+
+  IncrementCount(discarded);
+  return !discarded;
+}
+
+}  // anonymous namespace
+
+namespace internal {
+
+ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, const Arg* args,
+                     const size_t max_args) {
+  // Make sure that at least one NUL byte can be written, and that the buffer
+  // never overflows kSSizeMax. Not only does that use up most or all of the
+  // address space, it also would result in a return code that cannot be
+  // represented.
+  if (static_cast<ssize_t>(sz) < 1)
+    return -1;
+  sz = std::min(sz, kSSizeMax);
+
+  // Iterate over format string and interpret '%' arguments as they are
+  // encountered.
+  Buffer buffer(buf, sz);
+  size_t padding;
+  char pad;
+  for (unsigned int cur_arg = 0; *fmt && !buffer.OutOfAddressableSpace(); ) {
+    if (*fmt++ == '%') {
+      padding = 0;
+      pad = ' ';
+      char ch = *fmt++;
+    format_character_found:
+      switch (ch) {
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9':
+        // Found a width parameter. Convert to an integer value and store in
+        // "padding". If the leading digit is a zero, change the padding
+        // character from a space ' ' to a zero '0'.
+        pad = ch == '0' ? '0' : ' ';
+        for (;;) {
+          // The maximum allowed padding fills all the available address
+          // space and leaves just enough space to insert the trailing NUL.
+          const size_t max_padding = kSSizeMax - 1;
+          if (padding > max_padding/10 ||
+              10*padding > max_padding - (ch - '0')) {
+            DEBUG_CHECK(padding <= max_padding/10 &&
+                        10*padding <= max_padding - (ch - '0'));
+            // Integer overflow detected. Skip the rest of the width until
+            // we find the format character, then do the normal error handling.
+          padding_overflow:
+            padding = max_padding;
+            while ((ch = *fmt++) >= '0' && ch <= '9') {
+            }
+            if (cur_arg < max_args) {
+              ++cur_arg;
+            }
+            goto fail_to_expand;
+          }
+          padding = 10*padding + ch - '0';
+          if (padding > max_padding) {
+            // This doesn't happen for "sane" values of kSSizeMax. But once
+            // kSSizeMax gets smaller than about 10, our earlier range checks
+            // are incomplete. Unittests do trigger this artificial corner
+            // case.
+            DEBUG_CHECK(padding <= max_padding);
+            goto padding_overflow;
+          }
+          ch = *fmt++;
+          if (ch < '0' || ch > '9') {
+            // Reached the end of the width parameter. This is where the format
+            // character is found.
+            goto format_character_found;
+          }
+        }
+        break;
+      case 'c': {  // Output an ASCII character.
+        // Check that there are arguments left to be inserted.
+        if (cur_arg >= max_args) {
+          DEBUG_CHECK(cur_arg < max_args);
+          goto fail_to_expand;
+        }
+
+        // Check that the argument has the expected type.
+        const Arg& arg = args[cur_arg++];
+        if (arg.type != Arg::INT && arg.type != Arg::UINT) {
+          DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT);
+          goto fail_to_expand;
+        }
+
+        // Apply padding, if needed.
+        buffer.Pad(' ', padding, 1);
+
+        // Convert the argument to an ASCII character and output it.
+        char as_char = static_cast<char>(arg.integer.i);
+        if (!as_char) {
+          goto end_of_output_buffer;
+        }
+        buffer.Out(as_char);
+        break; }
+      case 'd':    // Output a possibly signed decimal value.
+      case 'o':    // Output an unsigned octal value.
+      case 'x':    // Output an unsigned hexadecimal value.
+      case 'X':
+      case 'p': {  // Output a pointer value.
+        // Check that there are arguments left to be inserted.
+        if (cur_arg >= max_args) {
+          DEBUG_CHECK(cur_arg < max_args);
+          goto fail_to_expand;
+        }
+
+        const Arg& arg = args[cur_arg++];
+        int64_t i;
+        const char* prefix = nullptr;
+        if (ch != 'p') {
+          // Check that the argument has the expected type.
+          if (arg.type != Arg::INT && arg.type != Arg::UINT) {
+            DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT);
+            goto fail_to_expand;
+          }
+          i = arg.integer.i;
+
+          if (ch != 'd') {
+            // The Arg() constructor automatically performed sign expansion on
+            // signed parameters. This is great when outputting a %d decimal
+            // number, but can result in unexpected leading 0xFF bytes when
+            // outputting a %x hexadecimal number. Mask bits, if necessary.
+            // We have to do this here, instead of in the Arg() constructor, as
+            // the Arg() constructor cannot tell whether we will output a %d
+            // or a %x. Only the latter should experience masking.
+            if (arg.integer.width < sizeof(int64_t)) {
+              i &= (1LL << (8*arg.integer.width)) - 1;
+            }
+          }
+        } else {
+          // Pointer values require an actual pointer or a string.
+          if (arg.type == Arg::POINTER) {
+            i = reinterpret_cast<uintptr_t>(arg.ptr);
+          } else if (arg.type == Arg::STRING) {
+            i = reinterpret_cast<uintptr_t>(arg.str);
+          } else if (arg.type == Arg::INT &&
+                     arg.integer.width == sizeof(NULL) &&
+                     arg.integer.i == 0) {  // Allow C++'s version of NULL
+            i = 0;
+          } else {
+            DEBUG_CHECK(arg.type == Arg::POINTER || arg.type == Arg::STRING);
+            goto fail_to_expand;
+          }
+
+          // Pointers always include the "0x" prefix.
+          prefix = "0x";
+        }
+
+        // Use IToASCII() to convert to ASCII representation. For decimal
+        // numbers, optionally print a sign. For hexadecimal numbers,
+        // distinguish between upper and lower case. %p addresses are always
+        // printed as upcase. Supports base 8, 10, and 16. Prints padding
+        // and/or prefixes, if so requested.
+        buffer.IToASCII(ch == 'd' && arg.type == Arg::INT,
+                        ch != 'x', i,
+                        ch == 'o' ? 8 : ch == 'd' ? 10 : 16,
+                        pad, padding, prefix);
+        break; }
+      case 's': {
+        // Check that there are arguments left to be inserted.
+        if (cur_arg >= max_args) {
+          DEBUG_CHECK(cur_arg < max_args);
+          goto fail_to_expand;
+        }
+
+        // Check that the argument has the expected type.
+        const Arg& arg = args[cur_arg++];
+        const char *s;
+        if (arg.type == Arg::STRING) {
+          s = arg.str ? arg.str : "<NULL>";
+        } else if (arg.type == Arg::INT && arg.integer.width == sizeof(NULL) &&
+                   arg.integer.i == 0) {  // Allow C++'s version of NULL
+          s = "<NULL>";
+        } else {
+          DEBUG_CHECK(arg.type == Arg::STRING);
+          goto fail_to_expand;
+        }
+
+        // Apply padding, if needed. This requires us to first check the
+        // length of the string that we are outputting.
+        if (padding) {
+          size_t len = 0;
+          for (const char* src = s; *src++; ) {
+            ++len;
+          }
+          buffer.Pad(' ', padding, len);
+        }
+
+        // Printing a string involves nothing more than copying it into the
+        // output buffer and making sure we don't output more bytes than
+        // available space; Out() takes care of doing that.
+        for (const char* src = s; *src; ) {
+          buffer.Out(*src++);
+        }
+        break; }
+      case '%':
+        // Quoted percent '%' character.
+        goto copy_verbatim;
+      fail_to_expand:
+        // C++ gives us tools to do type checking -- something that snprintf()
+        // could never really do. So, whenever we see arguments that don't
+        // match up with the format string, we refuse to output them. But
+        // since we have to be extremely conservative about being async-
+        // signal-safe, we are limited in the type of error handling that we
+        // can do in production builds (in debug builds we can use
+        // DEBUG_CHECK() and hope for the best). So, all we do is pass the
+        // format string unchanged. That should eventually get the user's
+        // attention; and in the meantime, it hopefully doesn't lose too much
+        // data.
+      default:
+        // Unknown or unsupported format character. Just copy verbatim to
+        // output.
+        buffer.Out('%');
+        DEBUG_CHECK(ch);
+        if (!ch) {
+          goto end_of_format_string;
+        }
+        buffer.Out(ch);
+        break;
+      }
+    } else {
+  copy_verbatim:
+    buffer.Out(fmt[-1]);
+    }
+  }
+ end_of_format_string:
+ end_of_output_buffer:
+  return buffer.GetCount();
+}
+
+}  // namespace internal
+
+ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt) {
+  // Make sure that at least one NUL byte can be written, and that the buffer
+  // never overflows kSSizeMax. Not only does that use up most or all of the
+  // address space, it also would result in a return code that cannot be
+  // represented.
+  if (static_cast<ssize_t>(sz) < 1)
+    return -1;
+  sz = std::min(sz, kSSizeMax);
+
+  Buffer buffer(buf, sz);
+
+  // In the slow-path, we deal with errors by copying the contents of
+  // "fmt" unexpanded. This means, if there are no arguments passed, the
+  // SafeSPrintf() function always degenerates to a version of strncpy() that
+  // de-duplicates '%' characters.
+  const char* src = fmt;
+  for (; *src; ++src) {
+    buffer.Out(*src);
+    DEBUG_CHECK(src[0] != '%' || src[1] == '%');
+    if (src[0] == '%' && src[1] == '%') {
+      ++src;
+    }
+  }
+  return buffer.GetCount();
+}
+
+}  // namespace strings
+}  // namespace base
diff --git a/base/strings/safe_sprintf.h b/base/strings/safe_sprintf.h
new file mode 100644
index 0000000..92f8c59
--- /dev/null
+++ b/base/strings/safe_sprintf.h
@@ -0,0 +1,246 @@
+// Copyright 2013 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.
+
+#ifndef BASE_STRINGS_SAFE_SPRINTF_H_
+#define BASE_STRINGS_SAFE_SPRINTF_H_
+
+#include "build/build_config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// For ssize_t
+#include <unistd.h>
+#endif
+
+#include "polyfills/base/base_export.h"
+
+namespace gurl_base {
+namespace strings {
+
+#if defined(COMPILER_MSVC)
+// Define ssize_t inside of our namespace.
+#if defined(_WIN64)
+typedef __int64 ssize_t;
+#else
+typedef long ssize_t;
+#endif
+#endif
+
+// SafeSPrintf() is a type-safe and completely self-contained version of
+// snprintf().
+//
+// SafeSNPrintf() is an alternative function signature that can be used when
+// not dealing with fixed-sized buffers. When possible, SafeSPrintf() should
+// always be used instead of SafeSNPrintf()
+//
+// These functions allow for formatting complicated messages from contexts that
+// require strict async-signal-safety. In fact, it is safe to call them from
+// any low-level execution context, as they are guaranteed to make no library
+// or system calls. It deliberately never touches "errno", either.
+//
+// The only exception to this rule is that in debug builds the code calls
+// RAW_CHECK() to help diagnose problems when the format string does not
+// match the rest of the arguments. In release builds, no GURL_CHECK()s are used,
+// and SafeSPrintf() instead returns an output string that expands only
+// those arguments that match their format characters. Mismatched arguments
+// are ignored.
+//
+// The code currently only supports a subset of format characters:
+//   %c, %o, %d, %x, %X, %p, and %s.
+//
+// SafeSPrintf() aims to be as liberal as reasonably possible. Integer-like
+// values of arbitrary width can be passed to all of the format characters
+// that expect integers. Thus, it is explicitly legal to pass an "int" to
+// "%c", and output will automatically look at the LSB only. It is also
+// explicitly legal to pass either signed or unsigned values, and the format
+// characters will automatically interpret the arguments accordingly.
+//
+// It is still not legal to mix-and-match integer-like values with pointer
+// values. For instance, you cannot pass a pointer to %x, nor can you pass an
+// integer to %p.
+//
+// The one exception is "0" zero being accepted by "%p". This works-around
+// the problem of C++ defining NULL as an integer-like value.
+//
+// All format characters take an optional width parameter. This must be a
+// positive integer. For %d, %o, %x, %X and %p, if the width starts with
+// a leading '0', padding is done with '0' instead of ' ' characters.
+//
+// There are a few features of snprintf()-style format strings, that
+// SafeSPrintf() does not support at this time.
+//
+// If an actual user showed up, there is no particularly strong reason they
+// couldn't be added. But that assumes that the trade-offs between complexity
+// and utility are favorable.
+//
+// For example, adding support for negative padding widths, and for %n are all
+// likely to be viewed positively. They are all clearly useful, low-risk, easy
+// to test, don't jeopardize the async-signal-safety of the code, and overall
+// have little impact on other parts of SafeSPrintf() function.
+//
+// On the other hands, adding support for alternate forms, positional
+// arguments, grouping, wide characters, localization or floating point numbers
+// are all unlikely to ever be added.
+//
+// SafeSPrintf() and SafeSNPrintf() mimic the behavior of snprintf() and they
+// return the number of bytes needed to store the untruncated output. This
+// does *not* include the terminating NUL byte.
+//
+// They return -1, iff a fatal error happened. This typically can only happen,
+// if the buffer size is a) negative, or b) zero (i.e. not even the NUL byte
+// can be written). The return value can never be larger than SSIZE_MAX-1.
+// This ensures that the caller can always add one to the signed return code
+// in order to determine the amount of storage that needs to be allocated.
+//
+// While the code supports type checking and while it is generally very careful
+// to avoid printing incorrect values, it tends to be conservative in printing
+// as much as possible, even when given incorrect parameters. Typically, in
+// case of an error, the format string will not be expanded. (i.e. something
+// like SafeSPrintf(buf, "%p %d", 1, 2) results in "%p 2"). See above for
+// the use of RAW_CHECK() in debug builds, though.
+//
+// Basic example:
+//   char buf[20];
+//   gurl_base::strings::SafeSPrintf(buf, "The answer: %2d", 42);
+//
+// Example with dynamically sized buffer (async-signal-safe). This code won't
+// work on Visual studio, as it requires dynamically allocating arrays on the
+// stack. Consider picking a smaller value for |kMaxSize| if stack size is
+// limited and known. On the other hand, if the parameters to SafeSNPrintf()
+// are trusted and not controllable by the user, you can consider eliminating
+// the check for |kMaxSize| altogether. The current value of SSIZE_MAX is
+// essentially a no-op that just illustrates how to implement an upper bound:
+//   const size_t kInitialSize = 128;
+//   const size_t kMaxSize = std::numeric_limits<ssize_t>::max();
+//   size_t size = kInitialSize;
+//   for (;;) {
+//     char buf[size];
+//     size = SafeSNPrintf(buf, size, "Error message \"%s\"\n", err) + 1;
+//     if (sizeof(buf) < kMaxSize && size > kMaxSize) {
+//       size = kMaxSize;
+//       continue;
+//     } else if (size > sizeof(buf))
+//       continue;
+//     write(2, buf, size-1);
+//     break;
+//   }
+
+namespace internal {
+// Helpers that use C++ overloading, templates, and specializations to deduce
+// and record type information from function arguments. This allows us to
+// later write a type-safe version of snprintf().
+
+struct Arg {
+  enum Type { INT, UINT, STRING, POINTER };
+
+  // Any integer-like value.
+  Arg(signed char c) : type(INT) {
+    integer.i = c;
+    integer.width = sizeof(char);
+  }
+  Arg(unsigned char c) : type(UINT) {
+    integer.i = c;
+    integer.width = sizeof(char);
+  }
+  Arg(signed short j) : type(INT) {
+    integer.i = j;
+    integer.width = sizeof(short);
+  }
+  Arg(unsigned short j) : type(UINT) {
+    integer.i = j;
+    integer.width = sizeof(short);
+  }
+  Arg(signed int j) : type(INT) {
+    integer.i = j;
+    integer.width = sizeof(int);
+  }
+  Arg(unsigned int j) : type(UINT) {
+    integer.i = j;
+    integer.width = sizeof(int);
+  }
+  Arg(signed long j) : type(INT) {
+    integer.i = j;
+    integer.width = sizeof(long);
+  }
+  Arg(unsigned long j) : type(UINT) {
+    integer.i = j;
+    integer.width = sizeof(long);
+  }
+  Arg(signed long long j) : type(INT) {
+    integer.i = j;
+    integer.width = sizeof(long long);
+  }
+  Arg(unsigned long long j) : type(UINT) {
+    integer.i = j;
+    integer.width = sizeof(long long);
+  }
+
+  // A C-style text string.
+  Arg(const char* s) : str(s), type(STRING) { }
+  Arg(char* s)       : str(s), type(STRING) { }
+
+  // Any pointer value that can be cast to a "void*".
+  template<class T> Arg(T* p) : ptr((void*)p), type(POINTER) { }
+
+  union {
+    // An integer-like value.
+    struct {
+      int64_t       i;
+      unsigned char width;
+    } integer;
+
+    // A C-style text string.
+    const char* str;
+
+    // A pointer to an arbitrary object.
+    const void* ptr;
+  };
+  const enum Type type;
+};
+
+// This is the internal function that performs the actual formatting of
+// an snprintf()-style format string.
+BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt,
+                                 const Arg* args, size_t max_args);
+
+#if !defined(NDEBUG)
+// In debug builds, allow unit tests to artificially lower the kSSizeMax
+// constant that is used as a hard upper-bound for all buffers. In normal
+// use, this constant should always be std::numeric_limits<ssize_t>::max().
+BASE_EXPORT void SetSafeSPrintfSSizeMaxForTest(size_t max);
+BASE_EXPORT size_t GetSafeSPrintfSSizeMaxForTest();
+#endif
+
+}  // namespace internal
+
+template<typename... Args>
+ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args... args) {
+  // Use Arg() object to record type information and then copy arguments to an
+  // array to make it easier to iterate over them.
+  const internal::Arg arg_array[] = { args... };
+  return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args));
+}
+
+template<size_t N, typename... Args>
+ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, Args... args) {
+  // Use Arg() object to record type information and then copy arguments to an
+  // array to make it easier to iterate over them.
+  const internal::Arg arg_array[] = { args... };
+  return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args));
+}
+
+// Fast-path when we don't actually need to substitute any arguments.
+BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt);
+template<size_t N>
+inline ssize_t SafeSPrintf(char (&buf)[N], const char* fmt) {
+  return SafeSNPrintf(buf, N, fmt);
+}
+
+}  // namespace strings
+}  // namespace base
+
+#endif  // BASE_STRINGS_SAFE_SPRINTF_H_
diff --git a/base/strings/safe_sprintf_unittest.cc b/base/strings/safe_sprintf_unittest.cc
new file mode 100644
index 0000000..b7a67fa
--- /dev/null
+++ b/base/strings/safe_sprintf_unittest.cc
@@ -0,0 +1,765 @@
+// Copyright 2013 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 "base/strings/safe_sprintf.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <limits>
+#include <memory>
+
+#include "polyfills/base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Death tests on Android are currently very flaky. No need to add more flaky
+// tests, as they just make it hard to spot real problems.
+// TODO(markus): See if the restrictions on Android can eventually be lifted.
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+#define ALLOW_DEATH_TEST
+#endif
+
+namespace gurl_base {
+namespace strings {
+
+TEST(SafeSPrintfTest, Empty) {
+  char buf[2] = { 'X', 'X' };
+
+  // Negative buffer size should always result in an error.
+  EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast<size_t>(-1), ""));
+  EXPECT_EQ('X', buf[0]);
+  EXPECT_EQ('X', buf[1]);
+
+  // Zero buffer size should always result in an error.
+  EXPECT_EQ(-1, SafeSNPrintf(buf, 0, ""));
+  EXPECT_EQ('X', buf[0]);
+  EXPECT_EQ('X', buf[1]);
+
+  // A one-byte buffer should always print a single NUL byte.
+  EXPECT_EQ(0, SafeSNPrintf(buf, 1, ""));
+  EXPECT_EQ(0, buf[0]);
+  EXPECT_EQ('X', buf[1]);
+  buf[0] = 'X';
+
+  // A larger buffer should leave the trailing bytes unchanged.
+  EXPECT_EQ(0, SafeSNPrintf(buf, 2, ""));
+  EXPECT_EQ(0, buf[0]);
+  EXPECT_EQ('X', buf[1]);
+  buf[0] = 'X';
+
+  // The same test using SafeSPrintf() instead of SafeSNPrintf().
+  EXPECT_EQ(0, SafeSPrintf(buf, ""));
+  EXPECT_EQ(0, buf[0]);
+  EXPECT_EQ('X', buf[1]);
+  buf[0] = 'X';
+}
+
+TEST(SafeSPrintfTest, NoArguments) {
+  // Output a text message that doesn't require any substitutions. This
+  // is roughly equivalent to calling strncpy() (but unlike strncpy(), it does
+  // always add a trailing NUL; it always deduplicates '%' characters).
+  static const char text[] = "hello world";
+  char ref[20], buf[20];
+  memset(ref, 'X', sizeof(ref));
+  memcpy(buf, ref, sizeof(buf));
+
+  // A negative buffer size should always result in an error.
+  EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast<size_t>(-1), text));
+  EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+  // Zero buffer size should always result in an error.
+  EXPECT_EQ(-1, SafeSNPrintf(buf, 0, text));
+  EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+  // A one-byte buffer should always print a single NUL byte.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSNPrintf(buf, 1, text));
+  EXPECT_EQ(0, buf[0]);
+  EXPECT_TRUE(!memcmp(buf+1, ref+1, sizeof(buf)-1));
+  memcpy(buf, ref, sizeof(buf));
+
+  // A larger (but limited) buffer should always leave the trailing bytes
+  // unchanged.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSNPrintf(buf, 2, text));
+  EXPECT_EQ(text[0], buf[0]);
+  EXPECT_EQ(0, buf[1]);
+  EXPECT_TRUE(!memcmp(buf+2, ref+2, sizeof(buf)-2));
+  memcpy(buf, ref, sizeof(buf));
+
+  // A unrestricted buffer length should always leave the trailing bytes
+  // unchanged.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+            SafeSNPrintf(buf, sizeof(buf), text));
+  EXPECT_EQ(std::string(text), std::string(buf));
+  EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+                      sizeof(buf) - sizeof(text)));
+  memcpy(buf, ref, sizeof(buf));
+
+  // The same test using SafeSPrintf() instead of SafeSNPrintf().
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSPrintf(buf, text));
+  EXPECT_EQ(std::string(text), std::string(buf));
+  EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+                      sizeof(buf) - sizeof(text)));
+  memcpy(buf, ref, sizeof(buf));
+
+  // Check for deduplication of '%' percent characters.
+  EXPECT_EQ(1, SafeSPrintf(buf, "%%"));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%%%"));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%X"));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%%%%X"));
+#if defined(NDEBUG)
+  EXPECT_EQ(1, SafeSPrintf(buf, "%"));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%%"));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%X"));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%%%X"));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, "%"), "src.1. == '%'");
+  EXPECT_DEATH(SafeSPrintf(buf, "%%%"), "src.1. == '%'");
+  EXPECT_DEATH(SafeSPrintf(buf, "%X"), "src.1. == '%'");
+  EXPECT_DEATH(SafeSPrintf(buf, "%%%X"), "src.1. == '%'");
+#endif
+}
+
+TEST(SafeSPrintfTest, OneArgument) {
+  // Test basic single-argument single-character substitution.
+  const char text[] = "hello world";
+  const char fmt[]  = "hello%cworld";
+  char ref[20], buf[20];
+  memset(ref, 'X', sizeof(buf));
+  memcpy(buf, ref, sizeof(buf));
+
+  // A negative buffer size should always result in an error.
+  EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast<size_t>(-1), fmt, ' '));
+  EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+  // Zero buffer size should always result in an error.
+  EXPECT_EQ(-1, SafeSNPrintf(buf, 0, fmt, ' '));
+  EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+  // A one-byte buffer should always print a single NUL byte.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+            SafeSNPrintf(buf, 1, fmt, ' '));
+  EXPECT_EQ(0, buf[0]);
+  EXPECT_TRUE(!memcmp(buf+1, ref+1, sizeof(buf)-1));
+  memcpy(buf, ref, sizeof(buf));
+
+  // A larger (but limited) buffer should always leave the trailing bytes
+  // unchanged.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+            SafeSNPrintf(buf, 2, fmt, ' '));
+  EXPECT_EQ(text[0], buf[0]);
+  EXPECT_EQ(0, buf[1]);
+  EXPECT_TRUE(!memcmp(buf+2, ref+2, sizeof(buf)-2));
+  memcpy(buf, ref, sizeof(buf));
+
+  // A unrestricted buffer length should always leave the trailing bytes
+  // unchanged.
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+            SafeSNPrintf(buf, sizeof(buf), fmt, ' '));
+  EXPECT_EQ(std::string(text), std::string(buf));
+  EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+                      sizeof(buf) - sizeof(text)));
+  memcpy(buf, ref, sizeof(buf));
+
+  // The same test using SafeSPrintf() instead of SafeSNPrintf().
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSPrintf(buf, fmt, ' '));
+  EXPECT_EQ(std::string(text), std::string(buf));
+  EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+                      sizeof(buf) - sizeof(text)));
+  memcpy(buf, ref, sizeof(buf));
+
+  // Check for deduplication of '%' percent characters.
+  EXPECT_EQ(1, SafeSPrintf(buf, "%%", 0));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%%%", 0));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%Y", 0));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%Y", 0));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%%%Y", 0));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%%%%Y", 0));
+#if defined(NDEBUG)
+  EXPECT_EQ(1, SafeSPrintf(buf, "%", 0));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%%", 0));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, "%", 0), "ch");
+  EXPECT_DEATH(SafeSPrintf(buf, "%%%", 0), "ch");
+#endif
+}
+
+TEST(SafeSPrintfTest, MissingArg) {
+#if defined(NDEBUG)
+  char buf[20];
+  EXPECT_EQ(3, SafeSPrintf(buf, "%c%c", 'A'));
+  EXPECT_EQ("A%c", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  char buf[20];
+  EXPECT_DEATH(SafeSPrintf(buf, "%c%c", 'A'), "cur_arg < max_args");
+#endif
+}
+
+TEST(SafeSPrintfTest, ASANFriendlyBufferTest) {
+  // Print into a buffer that is sized exactly to size. ASAN can verify that
+  // nobody attempts to write past the end of the buffer.
+  // There is a more complicated test in PrintLongString() that covers a lot
+  // more edge case, but it is also harder to debug in case of a failure.
+  const char kTestString[] = "This is a test";
+  std::unique_ptr<char[]> buf(new char[sizeof(kTestString)]);
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(kTestString) - 1),
+            SafeSNPrintf(buf.get(), sizeof(kTestString), kTestString));
+  EXPECT_EQ(std::string(kTestString), std::string(buf.get()));
+  EXPECT_EQ(static_cast<ssize_t>(sizeof(kTestString) - 1),
+            SafeSNPrintf(buf.get(), sizeof(kTestString), "%s", kTestString));
+  EXPECT_EQ(std::string(kTestString), std::string(buf.get()));
+}
+
+TEST(SafeSPrintfTest, NArgs) {
+  // Pre-C++11 compilers have a different code path, that can only print
+  // up to ten distinct arguments.
+  // We test both SafeSPrintf() and SafeSNPrintf(). This makes sure we don't
+  // have typos in the copy-n-pasted code that is needed to deal with various
+  // numbers of arguments.
+  char buf[12];
+  EXPECT_EQ(1, SafeSPrintf(buf, "%c", 1));
+  EXPECT_EQ("\1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%c%c", 1, 2));
+  EXPECT_EQ("\1\2", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%c%c%c", 1, 2, 3));
+  EXPECT_EQ("\1\2\3", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%c%c%c%c", 1, 2, 3, 4));
+  EXPECT_EQ("\1\2\3\4", std::string(buf));
+  EXPECT_EQ(5, SafeSPrintf(buf, "%c%c%c%c%c", 1, 2, 3, 4, 5));
+  EXPECT_EQ("\1\2\3\4\5", std::string(buf));
+  EXPECT_EQ(6, SafeSPrintf(buf, "%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6));
+  EXPECT_EQ("\1\2\3\4\5\6", std::string(buf));
+  EXPECT_EQ(7, SafeSPrintf(buf, "%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7));
+  EXPECT_EQ("\1\2\3\4\5\6\7", std::string(buf));
+  EXPECT_EQ(8, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7, 8));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10", std::string(buf));
+  EXPECT_EQ(9, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c",
+                           1, 2, 3, 4, 5, 6, 7, 8, 9));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10\11", std::string(buf));
+  EXPECT_EQ(10, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c",
+                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+
+  // Repeat all the tests with SafeSNPrintf() instead of SafeSPrintf().
+  EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf));
+  EXPECT_EQ(1, SafeSNPrintf(buf, 11, "%c", 1));
+  EXPECT_EQ("\1", std::string(buf));
+  EXPECT_EQ(2, SafeSNPrintf(buf, 11, "%c%c", 1, 2));
+  EXPECT_EQ("\1\2", std::string(buf));
+  EXPECT_EQ(3, SafeSNPrintf(buf, 11, "%c%c%c", 1, 2, 3));
+  EXPECT_EQ("\1\2\3", std::string(buf));
+  EXPECT_EQ(4, SafeSNPrintf(buf, 11, "%c%c%c%c", 1, 2, 3, 4));
+  EXPECT_EQ("\1\2\3\4", std::string(buf));
+  EXPECT_EQ(5, SafeSNPrintf(buf, 11, "%c%c%c%c%c", 1, 2, 3, 4, 5));
+  EXPECT_EQ("\1\2\3\4\5", std::string(buf));
+  EXPECT_EQ(6, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6));
+  EXPECT_EQ("\1\2\3\4\5\6", std::string(buf));
+  EXPECT_EQ(7, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7));
+  EXPECT_EQ("\1\2\3\4\5\6\7", std::string(buf));
+  EXPECT_EQ(8, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c",
+                            1, 2, 3, 4, 5, 6, 7, 8));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10", std::string(buf));
+  EXPECT_EQ(9, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c",
+                            1, 2, 3, 4, 5, 6, 7, 8, 9));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10\11", std::string(buf));
+  EXPECT_EQ(10, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c%c",
+                             1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf));
+
+  EXPECT_EQ(11, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c%c",
+                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf));
+  EXPECT_EQ(11, SafeSNPrintf(buf, 12, "%c%c%c%c%c%c%c%c%c%c%c",
+                             1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
+  EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf));
+}
+
+TEST(SafeSPrintfTest, DataTypes) {
+  char buf[40];
+
+  // Bytes
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint8_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%d", (uint8_t)-1));
+  EXPECT_EQ("255", std::string(buf));
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int8_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int8_t)-1));
+  EXPECT_EQ("-1", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%d", (int8_t)-128));
+  EXPECT_EQ("-128", std::string(buf));
+
+  // Half-words
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint16_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(5, SafeSPrintf(buf, "%d", (uint16_t)-1));
+  EXPECT_EQ("65535", std::string(buf));
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int16_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int16_t)-1));
+  EXPECT_EQ("-1", std::string(buf));
+  EXPECT_EQ(6, SafeSPrintf(buf, "%d", (int16_t)-32768));
+  EXPECT_EQ("-32768", std::string(buf));
+
+  // Words
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint32_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(10, SafeSPrintf(buf, "%d", (uint32_t)-1));
+  EXPECT_EQ("4294967295", std::string(buf));
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int32_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int32_t)-1));
+  EXPECT_EQ("-1", std::string(buf));
+  // Work-around for an limitation of C90
+  EXPECT_EQ(11, SafeSPrintf(buf, "%d", (int32_t)-2147483647-1));
+  EXPECT_EQ("-2147483648", std::string(buf));
+
+  // Quads
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint64_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(20, SafeSPrintf(buf, "%d", (uint64_t)-1));
+  EXPECT_EQ("18446744073709551615", std::string(buf));
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int64_t)1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int64_t)-1));
+  EXPECT_EQ("-1", std::string(buf));
+  // Work-around for an limitation of C90
+  EXPECT_EQ(20, SafeSPrintf(buf, "%d", (int64_t)-9223372036854775807LL-1));
+  EXPECT_EQ("-9223372036854775808", std::string(buf));
+
+  // Strings (both const and mutable).
+  EXPECT_EQ(4, SafeSPrintf(buf, "test"));
+  EXPECT_EQ("test", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, buf));
+  EXPECT_EQ("test", std::string(buf));
+
+  // Pointer
+  char addr[20];
+  sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)buf);
+  SafeSPrintf(buf, "%p", buf);
+  EXPECT_EQ(std::string(addr), std::string(buf));
+  SafeSPrintf(buf, "%p", (const char *)buf);
+  EXPECT_EQ(std::string(addr), std::string(buf));
+  sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)sprintf);
+  SafeSPrintf(buf, "%p", sprintf);
+  EXPECT_EQ(std::string(addr), std::string(buf));
+
+  // Padding for pointers is a little more complicated because of the "0x"
+  // prefix. Padding with '0' zeros is relatively straight-forward, but
+  // padding with ' ' spaces requires more effort.
+  sprintf(addr, "0x%017llX", (unsigned long long)(uintptr_t)buf);
+  SafeSPrintf(buf, "%019p", buf);
+  EXPECT_EQ(std::string(addr), std::string(buf));
+  sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)buf);
+  memset(addr, ' ',
+         (char*)memmove(addr + sizeof(addr) - strlen(addr) - 1,
+                        addr, strlen(addr)+1) - addr);
+  SafeSPrintf(buf, "%19p", buf);
+  EXPECT_EQ(std::string(addr), std::string(buf));
+}
+
+namespace {
+void PrintLongString(char* buf, size_t sz) {
+  // Output a reasonably complex expression into a limited-size buffer.
+  // At least one byte is available for writing the NUL character.
+  GURL_CHECK_GT(sz, static_cast<size_t>(0));
+
+  // Allocate slightly more space, so that we can verify that SafeSPrintf()
+  // never writes past the end of the buffer.
+  std::unique_ptr<char[]> tmp(new char[sz + 2]);
+  memset(tmp.get(), 'X', sz+2);
+
+  // Use SafeSPrintf() to output a complex list of arguments:
+  // - test padding and truncating %c single characters.
+  // - test truncating %s simple strings.
+  // - test mismatching arguments and truncating (for %d != %s).
+  // - test zero-padding and truncating %x hexadecimal numbers.
+  // - test outputting and truncating %d MININT.
+  // - test outputting and truncating %p arbitrary pointer values.
+  // - test outputting, padding and truncating NULL-pointer %s strings.
+  char* out = tmp.get();
+  size_t out_sz = sz;
+  size_t len;
+  for (std::unique_ptr<char[]> perfect_buf;;) {
+    size_t needed =
+        SafeSNPrintf(out, out_sz,
+#if defined(NDEBUG)
+                     "A%2cong %s: %d %010X %d %p%7s", 'l', "string", "",
+#else
+                     "A%2cong %s: %%d %010X %d %p%7s", 'l', "string",
+#endif
+                     0xDEADBEEF, std::numeric_limits<intptr_t>::min(),
+                     PrintLongString, static_cast<char*>(nullptr)) +
+        1;
+
+    // Various sanity checks:
+    // The numbered of characters needed to print the full string should always
+    // be bigger or equal to the bytes that have actually been output.
+    len = strlen(tmp.get());
+    GURL_CHECK_GE(needed, len+1);
+
+    // The number of characters output should always fit into the buffer that
+    // was passed into SafeSPrintf().
+    GURL_CHECK_LT(len, out_sz);
+
+    // The output is always terminated with a NUL byte (actually, this test is
+    // always going to pass, as strlen() already verified this)
+    EXPECT_FALSE(tmp[len]);
+
+    // ASAN can check that we are not overwriting buffers, iff we make sure the
+    // buffer is exactly the size that we are expecting to be written. After
+    // running SafeSNPrintf() the first time, it is possible to compute the
+    // correct buffer size for this test. So, allocate a second buffer and run
+    // the exact same SafeSNPrintf() command again.
+    if (!perfect_buf.get()) {
+      out_sz = std::min(needed, sz);
+      out = new char[out_sz];
+      perfect_buf.reset(out);
+    } else {
+      break;
+    }
+  }
+
+  // All trailing bytes are unchanged.
+  for (size_t i = len+1; i < sz+2; ++i)
+    EXPECT_EQ('X', tmp[i]);
+
+  // The text that was generated by SafeSPrintf() should always match the
+  // equivalent text generated by sprintf(). Please note that the format
+  // string for sprintf() is not complicated, as it does not have the
+  // benefit of getting type information from the C++ compiler.
+  //
+  // N.B.: It would be so much cleaner to use snprintf(). But unfortunately,
+  //       Visual Studio doesn't support this function, and the work-arounds
+  //       are all really awkward.
+  char ref[256];
+  GURL_CHECK_LE(sz, sizeof(ref));
+  sprintf(ref, "A long string: %%d 00DEADBEEF %lld 0x%llX <NULL>",
+          static_cast<long long>(std::numeric_limits<intptr_t>::min()),
+          static_cast<unsigned long long>(
+            reinterpret_cast<uintptr_t>(PrintLongString)));
+  ref[sz-1] = '\000';
+
+#if defined(NDEBUG)
+  const size_t kSSizeMax = std::numeric_limits<ssize_t>::max();
+#else
+  const size_t kSSizeMax = internal::GetSafeSPrintfSSizeMaxForTest();
+#endif
+
+  // Compare the output from SafeSPrintf() to the one from sprintf().
+  EXPECT_EQ(std::string(ref).substr(0, kSSizeMax-1), std::string(tmp.get()));
+
+  // We allocated a slightly larger buffer, so that we could perform some
+  // extra sanity checks. Now that the tests have all passed, we copy the
+  // data to the output buffer that the caller provided.
+  memcpy(buf, tmp.get(), len+1);
+}
+
+#if !defined(NDEBUG)
+class ScopedSafeSPrintfSSizeMaxSetter {
+ public:
+  ScopedSafeSPrintfSSizeMaxSetter(size_t sz) {
+    old_ssize_max_ = internal::GetSafeSPrintfSSizeMaxForTest();
+    internal::SetSafeSPrintfSSizeMaxForTest(sz);
+  }
+
+  ~ScopedSafeSPrintfSSizeMaxSetter() {
+    internal::SetSafeSPrintfSSizeMaxForTest(old_ssize_max_);
+  }
+
+ private:
+  size_t old_ssize_max_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSafeSPrintfSSizeMaxSetter);
+};
+#endif
+
+}  // anonymous namespace
+
+TEST(SafeSPrintfTest, Truncation) {
+  // We use PrintLongString() to print a complex long string and then
+  // truncate to all possible lengths. This ends up exercising a lot of
+  // different code paths in SafeSPrintf() and IToASCII(), as truncation can
+  // happen in a lot of different states.
+  char ref[256];
+  PrintLongString(ref, sizeof(ref));
+  for (size_t i = strlen(ref)+1; i; --i) {
+    char buf[sizeof(ref)];
+    PrintLongString(buf, i);
+    EXPECT_EQ(std::string(ref, i - 1), std::string(buf));
+  }
+
+  // When compiling in debug mode, we have the ability to fake a small
+  // upper limit for the maximum value that can be stored in an ssize_t.
+  // SafeSPrintf() uses this upper limit to determine how many bytes it will
+  // write to the buffer, even if the caller claimed a bigger buffer size.
+  // Repeat the truncation test and verify that this other code path in
+  // SafeSPrintf() works correctly, too.
+#if !defined(NDEBUG)
+  for (size_t i = strlen(ref)+1; i > 1; --i) {
+    ScopedSafeSPrintfSSizeMaxSetter ssize_max_setter(i);
+    char buf[sizeof(ref)];
+    PrintLongString(buf, sizeof(buf));
+    EXPECT_EQ(std::string(ref, i - 1), std::string(buf));
+  }
+
+  // kSSizeMax is also used to constrain the maximum amount of padding, before
+  // SafeSPrintf() detects an error in the format string.
+  ScopedSafeSPrintfSSizeMaxSetter ssize_max_setter(100);
+  char buf[256];
+  EXPECT_EQ(99, SafeSPrintf(buf, "%99c", ' '));
+  EXPECT_EQ(std::string(99, ' '), std::string(buf));
+  *buf = '\000';
+#if defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, "%100c", ' '), "padding <= max_padding");
+#endif
+  EXPECT_EQ(0, *buf);
+#endif
+}
+
+TEST(SafeSPrintfTest, Padding) {
+  char buf[40], fmt[40];
+
+  // Chars %c
+  EXPECT_EQ(1, SafeSPrintf(buf, "%c", 'A'));
+  EXPECT_EQ("A", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%2c", 'A'));
+  EXPECT_EQ(" A", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%02c", 'A'));
+  EXPECT_EQ(" A", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%-2c", 'A'));
+  EXPECT_EQ("%-2c", std::string(buf));
+  SafeSPrintf(fmt, "%%%dc", std::numeric_limits<ssize_t>::max() - 1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1, SafeSPrintf(buf, fmt, 'A'));
+  SafeSPrintf(fmt, "%%%dc",
+              static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, fmt, 'A'));
+  EXPECT_EQ("%c", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, fmt, 'A'), "padding <= max_padding");
+#endif
+
+  // Octal %o
+  EXPECT_EQ(1, SafeSPrintf(buf, "%o", 1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%2o", 1));
+  EXPECT_EQ(" 1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%02o", 1));
+  EXPECT_EQ("01", std::string(buf));
+  EXPECT_EQ(12, SafeSPrintf(buf, "%12o", -1));
+  EXPECT_EQ(" 37777777777", std::string(buf));
+  EXPECT_EQ(12, SafeSPrintf(buf, "%012o", -1));
+  EXPECT_EQ("037777777777", std::string(buf));
+  EXPECT_EQ(23, SafeSPrintf(buf, "%23o", -1LL));
+  EXPECT_EQ(" 1777777777777777777777", std::string(buf));
+  EXPECT_EQ(23, SafeSPrintf(buf, "%023o", -1LL));
+  EXPECT_EQ("01777777777777777777777", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%2o", 0111));
+  EXPECT_EQ("111", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%-2o", 1));
+  EXPECT_EQ("%-2o", std::string(buf));
+  SafeSPrintf(fmt, "%%%do", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, 1));
+  EXPECT_EQ("   ", std::string(buf));
+  SafeSPrintf(fmt, "%%0%do", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, 1));
+  EXPECT_EQ("000", std::string(buf));
+  SafeSPrintf(fmt, "%%%do",
+              static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+  EXPECT_EQ("%o", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+  // Decimals %d
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", 1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%2d", 1));
+  EXPECT_EQ(" 1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%02d", 1));
+  EXPECT_EQ("01", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%3d", -1));
+  EXPECT_EQ(" -1", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%03d", -1));
+  EXPECT_EQ("-01", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%2d", 111));
+  EXPECT_EQ("111", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%2d", -111));
+  EXPECT_EQ("-111", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%-2d", 1));
+  EXPECT_EQ("%-2d", std::string(buf));
+  SafeSPrintf(fmt, "%%%dd", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, 1));
+  EXPECT_EQ("   ", std::string(buf));
+  SafeSPrintf(fmt, "%%0%dd", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, 1));
+  EXPECT_EQ("000", std::string(buf));
+  SafeSPrintf(fmt, "%%%dd",
+              static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+  EXPECT_EQ("%d", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+  // Hex %X
+  EXPECT_EQ(1, SafeSPrintf(buf, "%X", 1));
+  EXPECT_EQ("1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%2X", 1));
+  EXPECT_EQ(" 1", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%02X", 1));
+  EXPECT_EQ("01", std::string(buf));
+  EXPECT_EQ(9, SafeSPrintf(buf, "%9X", -1));
+  EXPECT_EQ(" FFFFFFFF", std::string(buf));
+  EXPECT_EQ(9, SafeSPrintf(buf, "%09X", -1));
+  EXPECT_EQ("0FFFFFFFF", std::string(buf));
+  EXPECT_EQ(17, SafeSPrintf(buf, "%17X", -1LL));
+  EXPECT_EQ(" FFFFFFFFFFFFFFFF", std::string(buf));
+  EXPECT_EQ(17, SafeSPrintf(buf, "%017X", -1LL));
+  EXPECT_EQ("0FFFFFFFFFFFFFFFF", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%2X", 0x111));
+  EXPECT_EQ("111", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%-2X", 1));
+  EXPECT_EQ("%-2X", std::string(buf));
+  SafeSPrintf(fmt, "%%%dX", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, 1));
+  EXPECT_EQ("   ", std::string(buf));
+  SafeSPrintf(fmt, "%%0%dX", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, 1));
+  EXPECT_EQ("000", std::string(buf));
+  SafeSPrintf(fmt, "%%%dX",
+              static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+  EXPECT_EQ("%X", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+  // Pointer %p
+  EXPECT_EQ(3, SafeSPrintf(buf, "%p", (void*)1));
+  EXPECT_EQ("0x1", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%4p", (void*)1));
+  EXPECT_EQ(" 0x1", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%04p", (void*)1));
+  EXPECT_EQ("0x01", std::string(buf));
+  EXPECT_EQ(5, SafeSPrintf(buf, "%4p", (void*)0x111));
+  EXPECT_EQ("0x111", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%-2p", (void*)1));
+  EXPECT_EQ("%-2p", std::string(buf));
+  SafeSPrintf(fmt, "%%%dp", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, (void*)1));
+  EXPECT_EQ("   ", std::string(buf));
+  SafeSPrintf(fmt, "%%0%dp", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, (void*)1));
+  EXPECT_EQ("0x0", std::string(buf));
+  SafeSPrintf(fmt, "%%%dp",
+              static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+  EXPECT_EQ("%p", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+  // String
+  EXPECT_EQ(1, SafeSPrintf(buf, "%s", "A"));
+  EXPECT_EQ("A", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%2s", "A"));
+  EXPECT_EQ(" A", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%02s", "A"));
+  EXPECT_EQ(" A", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%2s", "AAA"));
+  EXPECT_EQ("AAA", std::string(buf));
+  EXPECT_EQ(4, SafeSPrintf(buf, "%-2s", "A"));
+  EXPECT_EQ("%-2s", std::string(buf));
+  SafeSPrintf(fmt, "%%%ds", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, "A"));
+  EXPECT_EQ("   ", std::string(buf));
+  SafeSPrintf(fmt, "%%0%ds", std::numeric_limits<ssize_t>::max()-1);
+  EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+            SafeSNPrintf(buf, 4, fmt, "A"));
+  EXPECT_EQ("   ", std::string(buf));
+  SafeSPrintf(fmt, "%%%ds",
+              static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, fmt, "A"));
+  EXPECT_EQ("%s", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, fmt, "A"), "padding <= max_padding");
+#endif
+}
+
+TEST(SafeSPrintfTest, EmbeddedNul) {
+  char buf[] = { 'X', 'X', 'X', 'X' };
+  EXPECT_EQ(2, SafeSPrintf(buf, "%3c", 0));
+  EXPECT_EQ(' ', buf[0]);
+  EXPECT_EQ(' ', buf[1]);
+  EXPECT_EQ(0,   buf[2]);
+  EXPECT_EQ('X', buf[3]);
+
+  // Check handling of a NUL format character. N.B. this takes two different
+  // code paths depending on whether we are actually passing arguments. If
+  // we don't have any arguments, we are running in the fast-path code, that
+  // looks (almost) like a strncpy().
+#if defined(NDEBUG)
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%%"));
+  EXPECT_EQ("%%", std::string(buf));
+  EXPECT_EQ(2, SafeSPrintf(buf, "%%%", 0));
+  EXPECT_EQ("%%", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+  EXPECT_DEATH(SafeSPrintf(buf, "%%%"), "src.1. == '%'");
+  EXPECT_DEATH(SafeSPrintf(buf, "%%%", 0), "ch");
+#endif
+}
+
+TEST(SafeSPrintfTest, EmitNULL) {
+  char buf[40];
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion-null"
+#endif
+  EXPECT_EQ(1, SafeSPrintf(buf, "%d", NULL));
+  EXPECT_EQ("0", std::string(buf));
+  EXPECT_EQ(3, SafeSPrintf(buf, "%p", NULL));
+  EXPECT_EQ("0x0", std::string(buf));
+  EXPECT_EQ(6, SafeSPrintf(buf, "%s", NULL));
+  EXPECT_EQ("<NULL>", std::string(buf));
+#if defined(__GCC__)
+#pragma GCC diagnostic pop
+#endif
+}
+
+TEST(SafeSPrintfTest, PointerSize) {
+  // The internal data representation is a 64bit value, independent of the
+  // native word size. We want to perform sign-extension for signed integers,
+  // but we want to avoid doing so for pointer types. This could be a
+  // problem on systems, where pointers are only 32bit. This tests verifies
+  // that there is no such problem.
+  char *str = reinterpret_cast<char *>(0x80000000u);
+  void *ptr = str;
+  char buf[40];
+  EXPECT_EQ(10, SafeSPrintf(buf, "%p", str));
+  EXPECT_EQ("0x80000000", std::string(buf));
+  EXPECT_EQ(10, SafeSPrintf(buf, "%p", ptr));
+  EXPECT_EQ("0x80000000", std::string(buf));
+}
+
+}  // namespace strings
+}  // namespace base
diff --git a/base/strings/strcat.cc b/base/strings/strcat.cc
new file mode 100644
index 0000000..1774a15
--- /dev/null
+++ b/base/strings/strcat.cc
@@ -0,0 +1,81 @@
+// Copyright 2017 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 "base/strings/strcat.h"
+
+namespace gurl_base {
+
+namespace {
+
+// Reserves an additional amount of size in the given string, growing by at
+// least 2x. Used by StrAppend().
+//
+// The "at least 2x" growing rule duplicates the exponential growth of
+// std::string. The problem is that most implementations of reserve() will grow
+// exactly to the requested amount instead of exponentially growing like would
+// happen when appending normally. If we didn't do this, an append after the
+// call to StrAppend() would definitely cause a reallocation, and loops with
+// StrAppend() calls would have O(n^2) complexity to execute. Instead, we want
+// StrAppend() to have the same semantics as std::string::append().
+//
+// If the string is empty, we assume that exponential growth is not necessary.
+template <typename String>
+void ReserveAdditional(String* str, typename String::size_type additional) {
+  str->reserve(std::max(str->size() + additional, str->size() * 2));
+}
+
+template <typename DestString, typename InputString>
+void StrAppendT(DestString* dest, span<const InputString> pieces) {
+  size_t additional_size = 0;
+  for (const auto& cur : pieces)
+    additional_size += cur.size();
+  ReserveAdditional(dest, additional_size);
+
+  for (const auto& cur : pieces)
+    dest->append(cur.data(), cur.size());
+}
+
+}  // namespace
+
+std::string StrCat(span<const StringPiece> pieces) {
+  std::string result;
+  StrAppendT(&result, pieces);
+  return result;
+}
+
+string16 StrCat(span<const StringPiece16> pieces) {
+  string16 result;
+  StrAppendT(&result, pieces);
+  return result;
+}
+
+std::string StrCat(span<const std::string> pieces) {
+  std::string result;
+  StrAppendT(&result, pieces);
+  return result;
+}
+
+string16 StrCat(span<const string16> pieces) {
+  string16 result;
+  StrAppendT(&result, pieces);
+  return result;
+}
+
+void StrAppend(std::string* dest, span<const StringPiece> pieces) {
+  StrAppendT(dest, pieces);
+}
+
+void StrAppend(string16* dest, span<const StringPiece16> pieces) {
+  StrAppendT(dest, pieces);
+}
+
+void StrAppend(std::string* dest, span<const std::string> pieces) {
+  StrAppendT(dest, pieces);
+}
+
+void StrAppend(string16* dest, span<const string16> pieces) {
+  StrAppendT(dest, pieces);
+}
+
+}  // namespace base
diff --git a/base/strings/strcat.h b/base/strings/strcat.h
new file mode 100644
index 0000000..bcdfe17
--- /dev/null
+++ b/base/strings/strcat.h
@@ -0,0 +1,100 @@
+// Copyright 2017 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.
+
+#ifndef BASE_STRINGS_STRCAT_H_
+#define BASE_STRINGS_STRCAT_H_
+
+#include <initializer_list>
+
+#include "polyfills/base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/containers/span.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+// Guard against conflict with Win32 API StrCat macro:
+// check StrCat wasn't and will not be redefined.
+#define StrCat StrCat
+#endif
+
+namespace gurl_base {
+
+// StrCat ----------------------------------------------------------------------
+//
+// StrCat is a function to perform concatenation on a sequence of strings.
+// It is preferrable to a sequence of "a + b + c" because it is both faster and
+// generates less code.
+//
+//   std::string result = gurl_base::StrCat({"foo ", result, "\nfoo ", bar});
+//
+// To join an array of strings with a separator, see gurl_base::JoinString in
+// base/strings/string_util.h.
+//
+// MORE INFO
+//
+// StrCat can see all arguments at once, so it can allocate one return buffer
+// of exactly the right size and copy once, as opposed to a sequence of
+// operator+ which generates a series of temporary strings, copying as it goes.
+// And by using StringPiece arguments, StrCat can avoid creating temporary
+// string objects for char* constants.
+//
+// ALTERNATIVES
+//
+// Internal Google / Abseil has a similar StrCat function. That version takes
+// an overloaded number of arguments instead of initializer list (overflowing
+// to initializer list for many arguments). We don't have any legacy
+// requirements and using only initializer_list is simpler and generates
+// roughly the same amount of code at the call sites.
+//
+// Abseil's StrCat also allows numbers by using an intermediate class that can
+// be implicitly constructed from either a string or various number types. This
+// class formats the numbers into a static buffer for increased performance,
+// and the call sites look nice.
+//
+// As-written Abseil's helper class for numbers generates slightly more code
+// than the raw StringPiece version. We can de-inline the helper class'
+// constructors which will cause the StringPiece constructors to be de-inlined
+// for this call and generate slightly less code. This is something we can
+// explore more in the future.
+
+BASE_EXPORT std::string StrCat(span<const StringPiece> pieces);
+BASE_EXPORT string16 StrCat(span<const StringPiece16> pieces);
+BASE_EXPORT std::string StrCat(span<const std::string> pieces);
+BASE_EXPORT string16 StrCat(span<const string16> pieces);
+
+// Initializer list forwards to the array version.
+inline std::string StrCat(std::initializer_list<StringPiece> pieces) {
+  return StrCat(make_span(pieces.begin(), pieces.size()));
+}
+inline string16 StrCat(std::initializer_list<StringPiece16> pieces) {
+  return StrCat(make_span(pieces.begin(), pieces.size()));
+}
+
+// StrAppend -------------------------------------------------------------------
+//
+// Appends a sequence of strings to a destination. Prefer:
+//   StrAppend(&foo, ...);
+// over:
+//   foo += StrCat(...);
+// because it avoids a temporary string allocation and copy.
+
+BASE_EXPORT void StrAppend(std::string* dest, span<const StringPiece> pieces);
+BASE_EXPORT void StrAppend(string16* dest, span<const StringPiece16> pieces);
+BASE_EXPORT void StrAppend(std::string* dest, span<const std::string> pieces);
+BASE_EXPORT void StrAppend(string16* dest, span<const string16> pieces);
+
+// Initializer list forwards to the array version.
+inline void StrAppend(std::string* dest,
+                      std::initializer_list<StringPiece> pieces) {
+  return StrAppend(dest, make_span(pieces.begin(), pieces.size()));
+}
+inline void StrAppend(string16* dest,
+                      std::initializer_list<StringPiece16> pieces) {
+  return StrAppend(dest, make_span(pieces.begin(), pieces.size()));
+}
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRCAT_H_
diff --git a/base/strings/strcat_unittest.cc b/base/strings/strcat_unittest.cc
new file mode 100644
index 0000000..d51b840
--- /dev/null
+++ b/base/strings/strcat_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 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 "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+TEST(StrCat, 8Bit) {
+  EXPECT_EQ("", StrCat({""}));
+  EXPECT_EQ("1", StrCat({"1"}));
+  EXPECT_EQ("122", StrCat({"1", "22"}));
+  EXPECT_EQ("122333", StrCat({"1", "22", "333"}));
+  EXPECT_EQ("1223334444", StrCat({"1", "22", "333", "4444"}));
+  EXPECT_EQ("122333444455555", StrCat({"1", "22", "333", "4444", "55555"}));
+}
+
+TEST(StrCat, 16Bit) {
+  string16 arg1 = ASCIIToUTF16("1");
+  string16 arg2 = ASCIIToUTF16("22");
+  string16 arg3 = ASCIIToUTF16("333");
+
+  EXPECT_EQ(ASCIIToUTF16(""), StrCat({string16()}));
+  EXPECT_EQ(ASCIIToUTF16("1"), StrCat({arg1}));
+  EXPECT_EQ(ASCIIToUTF16("122"), StrCat({arg1, arg2}));
+  EXPECT_EQ(ASCIIToUTF16("122333"), StrCat({arg1, arg2, arg3}));
+}
+
+TEST(StrAppend, 8Bit) {
+  std::string result;
+
+  result = "foo";
+  StrAppend(&result, {std::string()});
+  EXPECT_EQ("foo", result);
+
+  result = "foo";
+  StrAppend(&result, {"1"});
+  EXPECT_EQ("foo1", result);
+
+  result = "foo";
+  StrAppend(&result, {"1", "22", "333"});
+  EXPECT_EQ("foo122333", result);
+}
+
+TEST(StrAppend, 16Bit) {
+  string16 arg1 = ASCIIToUTF16("1");
+  string16 arg2 = ASCIIToUTF16("22");
+  string16 arg3 = ASCIIToUTF16("333");
+
+  string16 result;
+
+  result = ASCIIToUTF16("foo");
+  StrAppend(&result, {string16()});
+  EXPECT_EQ(ASCIIToUTF16("foo"), result);
+
+  result = ASCIIToUTF16("foo");
+  StrAppend(&result, {arg1});
+  EXPECT_EQ(ASCIIToUTF16("foo1"), result);
+
+  result = ASCIIToUTF16("foo");
+  StrAppend(&result, {arg1, arg2, arg3});
+  EXPECT_EQ(ASCIIToUTF16("foo122333"), result);
+}
+
+}  // namespace base
diff --git a/base/strings/string16.cc b/base/strings/string16.cc
new file mode 100644
index 0000000..6ac8b8b
--- /dev/null
+++ b/base/strings/string16.cc
@@ -0,0 +1,87 @@
+// Copyright 2013 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 "base/strings/string16.h"
+
+#if defined(WCHAR_T_IS_UTF16) && !defined(_AIX)
+
+#error This file should not be used on 2-byte wchar_t systems
+// If this winds up being needed on 2-byte wchar_t systems, either the
+// definitions below can be used, or the host system's wide character
+// functions like wmemcmp can be wrapped.
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <ostream>
+
+#include "base/strings/string_piece.h"
+
+namespace gurl_base {
+
+int c16memcmp(const char16* s1, const char16* s2, size_t n) {
+  // We cannot call memcmp because that changes the semantics.
+  while (n-- > 0) {
+    if (*s1 != *s2) {
+      // We cannot use (*s1 - *s2) because char16 is unsigned.
+      return ((*s1 < *s2) ? -1 : 1);
+    }
+    ++s1;
+    ++s2;
+  }
+  return 0;
+}
+
+size_t c16len(const char16* s) {
+  const char16 *s_orig = s;
+  while (*s) {
+    ++s;
+  }
+  return s - s_orig;
+}
+
+const char16* c16memchr(const char16* s, char16 c, size_t n) {
+  while (n-- > 0) {
+    if (*s == c) {
+      return s;
+    }
+    ++s;
+  }
+  return nullptr;
+}
+
+char16* c16memmove(char16* s1, const char16* s2, size_t n) {
+  return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memcpy(char16* s1, const char16* s2, size_t n) {
+  return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memset(char16* s, char16 c, size_t n) {
+  char16 *s_orig = s;
+  while (n-- > 0) {
+    *s = c;
+    ++s;
+  }
+  return s_orig;
+}
+
+namespace string16_internals {
+
+std::ostream& operator<<(std::ostream& out, const string16& str) {
+  return out << gurl_base::StringPiece16(str);
+}
+
+void PrintTo(const string16& str, std::ostream* out) {
+  *out << str;
+}
+
+}  // namespace string16_internals
+
+}  // namespace base
+
+template class std::
+    basic_string<gurl_base::char16, gurl_base::string16_internals::string16_char_traits>;
+
+#endif  // WCHAR_T_IS_UTF32
diff --git a/base/strings/string16.h b/base/strings/string16.h
new file mode 100644
index 0000000..f17a57f
--- /dev/null
+++ b/base/strings/string16.h
@@ -0,0 +1,229 @@
+// Copyright 2013 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.
+
+#ifndef BASE_STRINGS_STRING16_H_
+#define BASE_STRINGS_STRING16_H_
+
+// WHAT:
+// A version of std::basic_string that provides 2-byte characters even when
+// wchar_t is not implemented as a 2-byte type. You can access this class as
+// string16. We also define char16, which string16 is based upon.
+//
+// WHY:
+// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2
+// data. Plenty of existing code operates on strings encoded as UTF-16.
+//
+// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make
+// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails
+// at run time, because it calls some functions (like wcslen) that come from
+// the system's native C library -- which was built with a 4-byte wchar_t!
+// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's
+// entirely improper on those systems where the encoding of wchar_t is defined
+// as UTF-32.
+//
+// Here, we define string16, which is similar to std::wstring but replaces all
+// libc functions with custom, 2-byte-char compatible routines. It is capable
+// of carrying UTF-16-encoded data.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <functional>
+#include <string>
+
+#include "polyfills/base/base_export.h"
+#include "build/build_config.h"
+
+#if defined(WCHAR_T_IS_UTF16)
+
+// Define a macro for wrapping construction of char16 arrays and string16s from
+// a literal string. This indirection allows for an easier migration of
+// gurl_base::char16 to char16_t on platforms where WCHAR_T_IS_UTF16, as only a one
+// character change to the macro will be necessary.
+// This macro does not exist when WCHAR_T_IS_UTF32, as it is currently not
+// possible to create a char array form a literal in this case.
+// TODO(https://crbug.com/911896): Remove this macro once gurl_base::char16 is
+// char16_t on all platforms.
+#define STRING16_LITERAL(x) L##x
+
+namespace gurl_base {
+
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+}  // namespace base
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <wchar.h>  // for mbstate_t
+
+namespace gurl_base {
+
+typedef uint16_t char16;
+
+// char16 versions of the functions required by string16_char_traits; these
+// are based on the wide character functions of similar names ("w" or "wcs"
+// instead of "c16").
+BASE_EXPORT int c16memcmp(const char16* s1, const char16* s2, size_t n);
+BASE_EXPORT size_t c16len(const char16* s);
+BASE_EXPORT const char16* c16memchr(const char16* s, char16 c, size_t n);
+BASE_EXPORT char16* c16memmove(char16* s1, const char16* s2, size_t n);
+BASE_EXPORT char16* c16memcpy(char16* s1, const char16* s2, size_t n);
+BASE_EXPORT char16* c16memset(char16* s, char16 c, size_t n);
+
+// This namespace contains the implementation of gurl_base::string16 along with
+// things that need to be found via argument-dependent lookup from a
+// gurl_base::string16.
+namespace string16_internals {
+
+struct string16_char_traits {
+  typedef char16 char_type;
+  typedef int int_type;
+
+  // int_type needs to be able to hold each possible value of char_type, and in
+  // addition, the distinct value of eof().
+  static_assert(sizeof(int_type) > sizeof(char_type),
+                "int must be larger than 16 bits wide");
+
+  typedef std::streamoff off_type;
+  typedef mbstate_t state_type;
+  typedef std::fpos<state_type> pos_type;
+
+  static void assign(char_type& c1, const char_type& c2) {
+    c1 = c2;
+  }
+
+  static bool eq(const char_type& c1, const char_type& c2) {
+    return c1 == c2;
+  }
+  static bool lt(const char_type& c1, const char_type& c2) {
+    return c1 < c2;
+  }
+
+  static int compare(const char_type* s1, const char_type* s2, size_t n) {
+    return c16memcmp(s1, s2, n);
+  }
+
+  static size_t length(const char_type* s) {
+    return c16len(s);
+  }
+
+  static const char_type* find(const char_type* s, size_t n,
+                               const char_type& a) {
+    return c16memchr(s, a, n);
+  }
+
+  static char_type* move(char_type* s1, const char_type* s2, size_t n) {
+    return c16memmove(s1, s2, n);
+  }
+
+  static char_type* copy(char_type* s1, const char_type* s2, size_t n) {
+    return c16memcpy(s1, s2, n);
+  }
+
+  static char_type* assign(char_type* s, size_t n, char_type a) {
+    return c16memset(s, a, n);
+  }
+
+  static int_type not_eof(const int_type& c) {
+    return eq_int_type(c, eof()) ? 0 : c;
+  }
+
+  static char_type to_char_type(const int_type& c) {
+    return char_type(c);
+  }
+
+  static int_type to_int_type(const char_type& c) {
+    return int_type(c);
+  }
+
+  static bool eq_int_type(const int_type& c1, const int_type& c2) {
+    return c1 == c2;
+  }
+
+  static int_type eof() {
+    return static_cast<int_type>(EOF);
+  }
+};
+
+}  // namespace string16_internals
+
+typedef std::basic_string<char16,
+                          gurl_base::string16_internals::string16_char_traits>
+    string16;
+
+namespace string16_internals {
+
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& out,
+                                            const string16& str);
+
+// This is required by googletest to print a readable output on test failures.
+BASE_EXPORT extern void PrintTo(const string16& str, std::ostream* out);
+
+}  // namespace string16_internals
+
+}  // namespace base
+
+// The string class will be explicitly instantiated only once, in string16.cc.
+//
+// std::basic_string<> in GNU libstdc++ contains a static data member,
+// _S_empty_rep_storage, to represent empty strings.  When an operation such
+// as assignment or destruction is performed on a string, causing its existing
+// data member to be invalidated, it must not be freed if this static data
+// member is being used.  Otherwise, it counts as an attempt to free static
+// (and not allocated) data, which is a memory error.
+//
+// Generally, due to C++ template magic, _S_empty_rep_storage will be marked
+// as a coalesced symbol, meaning that the linker will combine multiple
+// instances into a single one when generating output.
+//
+// If a string class is used by multiple shared libraries, a problem occurs.
+// Each library will get its own copy of _S_empty_rep_storage.  When strings
+// are passed across a library boundary for alteration or destruction, memory
+// errors will result.  GNU libstdc++ contains a configuration option,
+// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which
+// disables the static data member optimization, but it's a good optimization
+// and non-STL code is generally at the mercy of the system's STL
+// configuration.  Fully-dynamic strings are not the default for GNU libstdc++
+// libstdc++ itself or for the libstdc++ installations on the systems we care
+// about, such as Mac OS X and relevant flavors of Linux.
+//
+// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 .
+//
+// To avoid problems, string classes need to be explicitly instantiated only
+// once, in exactly one library.  All other string users see it via an "extern"
+// declaration.  This is precisely how GNU libstdc++ handles
+// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring).
+//
+// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2),
+// in which the linker does not fully coalesce symbols when dead code
+// stripping is enabled.  This bug causes the memory errors described above
+// to occur even when a std::basic_string<> does not cross shared library
+// boundaries, such as in statically-linked executables.
+//
+// TODO(mark): File this bug with Apple and update this note with a bug number.
+
+extern template class BASE_EXPORT
+    std::basic_string<gurl_base::char16,
+                      gurl_base::string16_internals::string16_char_traits>;
+
+// Specialize std::hash for gurl_base::string16. Although the style guide forbids
+// this in general, it is necessary for consistency with WCHAR_T_IS_UTF16
+// platforms, where gurl_base::string16 is a type alias for std::wstring.
+namespace std {
+template <>
+struct hash<gurl_base::string16> {
+  std::size_t operator()(const gurl_base::string16& s) const {
+    std::size_t result = 0;
+    for (gurl_base::char16 c : s)
+      result = (result * 131) + c;
+    return result;
+  }
+};
+}  // namespace std
+
+#endif  // WCHAR_T_IS_UTF32
+
+#endif  // BASE_STRINGS_STRING16_H_
diff --git a/base/strings/string16_unittest.cc b/base/strings/string16_unittest.cc
new file mode 100644
index 0000000..a9aecef
--- /dev/null
+++ b/base/strings/string16_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2013 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 <sstream>
+#include <unordered_set>
+
+#include "base/strings/string16.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+#if defined(WCHAR_T_IS_UTF16)
+TEST(String16Test, String16Literal) {
+  static constexpr char16 kHelloWorld[] = STRING16_LITERAL("Hello, World");
+  string16 hello_world = kHelloWorld;
+  EXPECT_EQ(kHelloWorld, hello_world);
+}
+#endif
+
+// We define a custom operator<< for string16 so we can use it with logging.
+// This tests that conversion.
+TEST(String16Test, OutputStream) {
+  // Basic stream test.
+  {
+    std::ostringstream stream;
+    stream << "Empty '" << string16() << "' standard '"
+           << string16(ASCIIToUTF16("Hello, world")) << "'";
+    EXPECT_STREQ("Empty '' standard 'Hello, world'",
+                 stream.str().c_str());
+  }
+
+  // Interesting edge cases.
+  {
+    // These should each get converted to the invalid character: EF BF BD.
+    string16 initial_surrogate;
+    initial_surrogate.push_back(0xd800);
+    string16 final_surrogate;
+    final_surrogate.push_back(0xdc00);
+
+    // Old italic A = U+10300, will get converted to: F0 90 8C 80 'z'.
+    string16 surrogate_pair;
+    surrogate_pair.push_back(0xd800);
+    surrogate_pair.push_back(0xdf00);
+    surrogate_pair.push_back('z');
+
+    // Will get converted to the invalid char + 's': EF BF BD 's'.
+    string16 unterminated_surrogate;
+    unterminated_surrogate.push_back(0xd800);
+    unterminated_surrogate.push_back('s');
+
+    std::ostringstream stream;
+    stream << initial_surrogate << "," << final_surrogate << ","
+           << surrogate_pair << "," << unterminated_surrogate;
+
+    EXPECT_STREQ("\xef\xbf\xbd,\xef\xbf\xbd,\xf0\x90\x8c\x80z,\xef\xbf\xbds",
+                 stream.str().c_str());
+  }
+}
+
+TEST(String16Test, Hash) {
+  string16 str1 = ASCIIToUTF16("hello");
+  string16 str2 = ASCIIToUTF16("world");
+
+  std::unordered_set<string16> set;
+
+  set.insert(str1);
+  EXPECT_EQ(1u, set.count(str1));
+  EXPECT_EQ(0u, set.count(str2));
+}
+
+}  // namespace base
diff --git a/base/strings/string_number_conversions.cc b/base/strings/string_number_conversions.cc
new file mode 100644
index 0000000..2bf6142
--- /dev/null
+++ b/base/strings/string_number_conversions.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2012 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 "base/strings/string_number_conversions.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <wctype.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "polyfills/base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/scoped_clear_last_error.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/dmg_fp/dmg_fp.h"
+
+namespace gurl_base {
+
+namespace {
+
+template <typename STR, typename INT>
+struct IntToStringT {
+  static STR IntToString(INT value) {
+    // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
+    // So round up to allocate 3 output characters per byte, plus 1 for '-'.
+    const size_t kOutputBufSize =
+        3 * sizeof(INT) + std::numeric_limits<INT>::is_signed;
+
+    // Create the string in a temporary buffer, write it back to front, and
+    // then return the substr of what we ended up using.
+    using CHR = typename STR::value_type;
+    CHR outbuf[kOutputBufSize];
+
+    // The ValueOrDie call below can never fail, because UnsignedAbs is valid
+    // for all valid inputs.
+    typename std::make_unsigned<INT>::type res =
+        CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
+
+    CHR* end = outbuf + kOutputBufSize;
+    CHR* i = end;
+    do {
+      --i;
+      GURL_DCHECK(i != outbuf);
+      *i = static_cast<CHR>((res % 10) + '0');
+      res /= 10;
+    } while (res != 0);
+    if (IsValueNegative(value)) {
+      --i;
+      GURL_DCHECK(i != outbuf);
+      *i = static_cast<CHR>('-');
+    }
+    return STR(i, end);
+  }
+};
+
+// Utility to convert a character to a digit in a given base
+template<typename CHAR, int BASE, bool BASE_LTE_10> class BaseCharToDigit {
+};
+
+// Faster specialization for bases <= 10
+template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, true> {
+ public:
+  static bool Convert(CHAR c, uint8_t* digit) {
+    if (c >= '0' && c < '0' + BASE) {
+      *digit = static_cast<uint8_t>(c - '0');
+      return true;
+    }
+    return false;
+  }
+};
+
+// Specialization for bases where 10 < base <= 36
+template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, false> {
+ public:
+  static bool Convert(CHAR c, uint8_t* digit) {
+    if (c >= '0' && c <= '9') {
+      *digit = c - '0';
+    } else if (c >= 'a' && c < 'a' + BASE - 10) {
+      *digit = c - 'a' + 10;
+    } else if (c >= 'A' && c < 'A' + BASE - 10) {
+      *digit = c - 'A' + 10;
+    } else {
+      return false;
+    }
+    return true;
+  }
+};
+
+template <int BASE, typename CHAR>
+bool CharToDigit(CHAR c, uint8_t* digit) {
+  return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
+}
+
+// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it
+// is locale independent, whereas the functions we are replacing were
+// locale-dependent. TBD what is desired, but for the moment let's not
+// introduce a change in behaviour.
+template<typename CHAR> class WhitespaceHelper {
+};
+
+template<> class WhitespaceHelper<char> {
+ public:
+  static bool Invoke(char c) {
+    return 0 != isspace(static_cast<unsigned char>(c));
+  }
+};
+
+template<> class WhitespaceHelper<char16> {
+ public:
+  static bool Invoke(char16 c) {
+    return 0 != iswspace(c);
+  }
+};
+
+template<typename CHAR> bool LocalIsWhitespace(CHAR c) {
+  return WhitespaceHelper<CHAR>::Invoke(c);
+}
+
+// IteratorRangeToNumberTraits should provide:
+//  - a typedef for iterator_type, the iterator type used as input.
+//  - a typedef for value_type, the target numeric type.
+//  - static functions min, max (returning the minimum and maximum permitted
+//    values)
+//  - constant kBase, the base in which to interpret the input
+template<typename IteratorRangeToNumberTraits>
+class IteratorRangeToNumber {
+ public:
+  typedef IteratorRangeToNumberTraits traits;
+  typedef typename traits::iterator_type const_iterator;
+  typedef typename traits::value_type value_type;
+
+  // Generalized iterator-range-to-number conversion.
+  //
+  static bool Invoke(const_iterator begin,
+                     const_iterator end,
+                     value_type* output) {
+    bool valid = true;
+
+    while (begin != end && LocalIsWhitespace(*begin)) {
+      valid = false;
+      ++begin;
+    }
+
+    if (begin != end && *begin == '-') {
+      if (!std::numeric_limits<value_type>::is_signed) {
+        *output = 0;
+        valid = false;
+      } else if (!Negative::Invoke(begin + 1, end, output)) {
+        valid = false;
+      }
+    } else {
+      if (begin != end && *begin == '+') {
+        ++begin;
+      }
+      if (!Positive::Invoke(begin, end, output)) {
+        valid = false;
+      }
+    }
+
+    return valid;
+  }
+
+ private:
+  // Sign provides:
+  //  - a static function, CheckBounds, that determines whether the next digit
+  //    causes an overflow/underflow
+  //  - a static function, Increment, that appends the next digit appropriately
+  //    according to the sign of the number being parsed.
+  template<typename Sign>
+  class Base {
+   public:
+    static bool Invoke(const_iterator begin, const_iterator end,
+                       typename traits::value_type* output) {
+      *output = 0;
+
+      if (begin == end) {
+        return false;
+      }
+
+      // Note: no performance difference was found when using template
+      // specialization to remove this check in bases other than 16
+      if (traits::kBase == 16 && end - begin > 2 && *begin == '0' &&
+          (*(begin + 1) == 'x' || *(begin + 1) == 'X')) {
+        begin += 2;
+      }
+
+      for (const_iterator current = begin; current != end; ++current) {
+        uint8_t new_digit = 0;
+
+        if (!CharToDigit<traits::kBase>(*current, &new_digit)) {
+          return false;
+        }
+
+        if (current != begin) {
+          if (!Sign::CheckBounds(output, new_digit)) {
+            return false;
+          }
+          *output *= traits::kBase;
+        }
+
+        Sign::Increment(new_digit, output);
+      }
+      return true;
+    }
+  };
+
+  class Positive : public Base<Positive> {
+   public:
+    static bool CheckBounds(value_type* output, uint8_t new_digit) {
+      if (*output > static_cast<value_type>(traits::max() / traits::kBase) ||
+          (*output == static_cast<value_type>(traits::max() / traits::kBase) &&
+           new_digit > traits::max() % traits::kBase)) {
+        *output = traits::max();
+        return false;
+      }
+      return true;
+    }
+    static void Increment(uint8_t increment, value_type* output) {
+      *output += increment;
+    }
+  };
+
+  class Negative : public Base<Negative> {
+   public:
+    static bool CheckBounds(value_type* output, uint8_t new_digit) {
+      if (*output < traits::min() / traits::kBase ||
+          (*output == traits::min() / traits::kBase &&
+           new_digit > 0 - traits::min() % traits::kBase)) {
+        *output = traits::min();
+        return false;
+      }
+      return true;
+    }
+    static void Increment(uint8_t increment, value_type* output) {
+      *output -= increment;
+    }
+  };
+};
+
+template<typename ITERATOR, typename VALUE, int BASE>
+class BaseIteratorRangeToNumberTraits {
+ public:
+  typedef ITERATOR iterator_type;
+  typedef VALUE value_type;
+  static value_type min() {
+    return std::numeric_limits<value_type>::min();
+  }
+  static value_type max() {
+    return std::numeric_limits<value_type>::max();
+  }
+  static const int kBase = BASE;
+};
+
+template<typename ITERATOR>
+class BaseHexIteratorRangeToIntTraits
+    : public BaseIteratorRangeToNumberTraits<ITERATOR, int, 16> {
+};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUIntTraits
+    : public BaseIteratorRangeToNumberTraits<ITERATOR, uint32_t, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToInt64Traits
+    : public BaseIteratorRangeToNumberTraits<ITERATOR, int64_t, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUInt64Traits
+    : public BaseIteratorRangeToNumberTraits<ITERATOR, uint64_t, 16> {};
+
+typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
+    HexIteratorRangeToIntTraits;
+
+typedef BaseHexIteratorRangeToUIntTraits<StringPiece::const_iterator>
+    HexIteratorRangeToUIntTraits;
+
+typedef BaseHexIteratorRangeToInt64Traits<StringPiece::const_iterator>
+    HexIteratorRangeToInt64Traits;
+
+typedef BaseHexIteratorRangeToUInt64Traits<StringPiece::const_iterator>
+    HexIteratorRangeToUInt64Traits;
+
+template <typename VALUE, int BASE>
+class StringPieceToNumberTraits
+    : public BaseIteratorRangeToNumberTraits<StringPiece::const_iterator,
+                                             VALUE,
+                                             BASE> {
+};
+
+template <typename VALUE>
+bool StringToIntImpl(StringPiece input, VALUE* output) {
+  return IteratorRangeToNumber<StringPieceToNumberTraits<VALUE, 10> >::Invoke(
+      input.begin(), input.end(), output);
+}
+
+template <typename VALUE, int BASE>
+class StringPiece16ToNumberTraits
+    : public BaseIteratorRangeToNumberTraits<StringPiece16::const_iterator,
+                                             VALUE,
+                                             BASE> {
+};
+
+template <typename VALUE>
+bool String16ToIntImpl(StringPiece16 input, VALUE* output) {
+  return IteratorRangeToNumber<StringPiece16ToNumberTraits<VALUE, 10> >::Invoke(
+      input.begin(), input.end(), output);
+}
+
+}  // namespace
+
+std::string NumberToString(int value) {
+  return IntToStringT<std::string, int>::IntToString(value);
+}
+
+string16 NumberToString16(int value) {
+  return IntToStringT<string16, int>::IntToString(value);
+}
+
+std::string NumberToString(unsigned value) {
+  return IntToStringT<std::string, unsigned>::IntToString(value);
+}
+
+string16 NumberToString16(unsigned value) {
+  return IntToStringT<string16, unsigned>::IntToString(value);
+}
+
+std::string NumberToString(long value) {
+  return IntToStringT<std::string, long>::IntToString(value);
+}
+
+string16 NumberToString16(long value) {
+  return IntToStringT<string16, long>::IntToString(value);
+}
+
+std::string NumberToString(unsigned long value) {
+  return IntToStringT<std::string, unsigned long>::IntToString(value);
+}
+
+string16 NumberToString16(unsigned long value) {
+  return IntToStringT<string16, unsigned long>::IntToString(value);
+}
+
+std::string NumberToString(long long value) {
+  return IntToStringT<std::string, long long>::IntToString(value);
+}
+
+string16 NumberToString16(long long value) {
+  return IntToStringT<string16, long long>::IntToString(value);
+}
+
+std::string NumberToString(unsigned long long value) {
+  return IntToStringT<std::string, unsigned long long>::IntToString(value);
+}
+
+string16 NumberToString16(unsigned long long value) {
+  return IntToStringT<string16, unsigned long long>::IntToString(value);
+}
+
+std::string NumberToString(double value) {
+  // According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
+  char buffer[32];
+  dmg_fp::g_fmt(buffer, value);
+  return std::string(buffer);
+}
+
+gurl_base::string16 NumberToString16(double value) {
+  // According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
+  char buffer[32];
+  dmg_fp::g_fmt(buffer, value);
+
+  // The number will be ASCII. This creates the string using the "input
+  // iterator" variant which promotes from 8-bit to 16-bit via "=".
+  return gurl_base::string16(&buffer[0], &buffer[strlen(buffer)]);
+}
+
+bool StringToInt(StringPiece input, int* output) {
+  return StringToIntImpl(input, output);
+}
+
+bool StringToInt(StringPiece16 input, int* output) {
+  return String16ToIntImpl(input, output);
+}
+
+bool StringToUint(StringPiece input, unsigned* output) {
+  return StringToIntImpl(input, output);
+}
+
+bool StringToUint(StringPiece16 input, unsigned* output) {
+  return String16ToIntImpl(input, output);
+}
+
+bool StringToInt64(StringPiece input, int64_t* output) {
+  return StringToIntImpl(input, output);
+}
+
+bool StringToInt64(StringPiece16 input, int64_t* output) {
+  return String16ToIntImpl(input, output);
+}
+
+bool StringToUint64(StringPiece input, uint64_t* output) {
+  return StringToIntImpl(input, output);
+}
+
+bool StringToUint64(StringPiece16 input, uint64_t* output) {
+  return String16ToIntImpl(input, output);
+}
+
+bool StringToSizeT(StringPiece input, size_t* output) {
+  return StringToIntImpl(input, output);
+}
+
+bool StringToSizeT(StringPiece16 input, size_t* output) {
+  return String16ToIntImpl(input, output);
+}
+
+bool StringToDouble(const std::string& input, double* output) {
+  // Thread-safe?  It is on at least Mac, Linux, and Windows.
+  internal::ScopedClearLastError clear_errno;
+
+  char* endptr = nullptr;
+  *output = dmg_fp::strtod(input.c_str(), &endptr);
+
+  // Cases to return false:
+  //  - If errno is ERANGE, there was an overflow or underflow.
+  //  - If the input string is empty, there was nothing to parse.
+  //  - If endptr does not point to the end of the string, there are either
+  //    characters remaining in the string after a parsed number, or the string
+  //    does not begin with a parseable number.  endptr is compared to the
+  //    expected end given the string's stated length to correctly catch cases
+  //    where the string contains embedded NUL characters.
+  //  - If the first character is a space, there was leading whitespace
+  return errno == 0 &&
+         !input.empty() &&
+         input.c_str() + input.length() == endptr &&
+         !isspace(input[0]);
+}
+
+// Note: if you need to add String16ToDouble, first ask yourself if it's
+// really necessary. If it is, probably the best implementation here is to
+// convert to 8-bit and then use the 8-bit version.
+
+// Note: if you need to add an iterator range version of StringToDouble, first
+// ask yourself if it's really necessary. If it is, probably the best
+// implementation here is to instantiate a string and use the string version.
+
+std::string HexEncode(const void* bytes, size_t size) {
+  static const char kHexChars[] = "0123456789ABCDEF";
+
+  // Each input byte creates two output hex characters.
+  std::string ret(size * 2, '\0');
+
+  for (size_t i = 0; i < size; ++i) {
+    char b = reinterpret_cast<const char*>(bytes)[i];
+    ret[(i * 2)] = kHexChars[(b >> 4) & 0xf];
+    ret[(i * 2) + 1] = kHexChars[b & 0xf];
+  }
+  return ret;
+}
+
+std::string HexEncode(gurl_base::span<const uint8_t> bytes) {
+  return HexEncode(bytes.data(), bytes.size());
+}
+
+bool HexStringToInt(StringPiece input, int* output) {
+  return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
+    input.begin(), input.end(), output);
+}
+
+bool HexStringToUInt(StringPiece input, uint32_t* output) {
+  return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
+      input.begin(), input.end(), output);
+}
+
+bool HexStringToInt64(StringPiece input, int64_t* output) {
+  return IteratorRangeToNumber<HexIteratorRangeToInt64Traits>::Invoke(
+    input.begin(), input.end(), output);
+}
+
+bool HexStringToUInt64(StringPiece input, uint64_t* output) {
+  return IteratorRangeToNumber<HexIteratorRangeToUInt64Traits>::Invoke(
+      input.begin(), input.end(), output);
+}
+
+bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
+  GURL_DCHECK_EQ(output->size(), 0u);
+  size_t count = input.size();
+  if (count == 0 || (count % 2) != 0)
+    return false;
+  for (uintptr_t i = 0; i < count / 2; ++i) {
+    uint8_t msb = 0;  // most significant 4 bits
+    uint8_t lsb = 0;  // least significant 4 bits
+    if (!CharToDigit<16>(input[i * 2], &msb) ||
+        !CharToDigit<16>(input[i * 2 + 1], &lsb)) {
+      return false;
+    }
+    output->push_back((msb << 4) | lsb);
+  }
+  return true;
+}
+
+}  // namespace base
diff --git a/base/strings/string_number_conversions.h b/base/strings/string_number_conversions.h
new file mode 100644
index 0000000..a3acab8
--- /dev/null
+++ b/base/strings/string_number_conversions.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 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.
+
+#ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
+#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "polyfills/base/base_export.h"
+#include "base/containers/span.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+// ----------------------------------------------------------------------------
+// IMPORTANT MESSAGE FROM YOUR SPONSOR
+//
+// This file contains no "wstring" variants. New code should use string16. If
+// you need to make old code work, use the UTF8 version and convert. Please do
+// not add wstring variants.
+//
+// Please do not add "convenience" functions for converting strings to integers
+// that return the value and ignore success/failure. That encourages people to
+// write code that doesn't properly handle the error conditions.
+//
+// DO NOT use these functions in any UI unless it's NOT localized on purpose.
+// Instead, use gurl_base::MessageFormatter for a complex message with numbers
+// (integer, float, double) embedded or gurl_base::Format{Number,Double,Percent} to
+// just format a single number/percent. Note that some languages use native
+// digits instead of ASCII digits while others use a group separator or decimal
+// point different from ',' and '.'. Using these functions in the UI would lead
+// numbers to be formatted in a non-native way.
+// ----------------------------------------------------------------------------
+
+namespace gurl_base {
+
+// Number -> string conversions ------------------------------------------------
+
+// Ignores locale! see warning above.
+BASE_EXPORT std::string NumberToString(int value);
+BASE_EXPORT string16 NumberToString16(int value);
+BASE_EXPORT std::string NumberToString(unsigned int value);
+BASE_EXPORT string16 NumberToString16(unsigned int value);
+BASE_EXPORT std::string NumberToString(long value);
+BASE_EXPORT string16 NumberToString16(long value);
+BASE_EXPORT std::string NumberToString(unsigned long value);
+BASE_EXPORT string16 NumberToString16(unsigned long value);
+BASE_EXPORT std::string NumberToString(long long value);
+BASE_EXPORT string16 NumberToString16(long long value);
+BASE_EXPORT std::string NumberToString(unsigned long long value);
+BASE_EXPORT string16 NumberToString16(unsigned long long value);
+BASE_EXPORT std::string NumberToString(double value);
+BASE_EXPORT string16 NumberToString16(double value);
+
+// String -> number conversions ------------------------------------------------
+
+// Perform a best-effort conversion of the input string to a numeric type,
+// setting |*output| to the result of the conversion.  Returns true for
+// "perfect" conversions; returns false in the following cases:
+//  - Overflow. |*output| will be set to the maximum value supported
+//    by the data type.
+//  - Underflow. |*output| will be set to the minimum value supported
+//    by the data type.
+//  - Trailing characters in the string after parsing the number.  |*output|
+//    will be set to the value of the number that was parsed.
+//  - Leading whitespace in the string before parsing the number. |*output| will
+//    be set to the value of the number that was parsed.
+//  - No characters parseable as a number at the beginning of the string.
+//    |*output| will be set to 0.
+//  - Empty string.  |*output| will be set to 0.
+// WARNING: Will write to |output| even when returning false.
+//          Read the comments above carefully.
+BASE_EXPORT bool StringToInt(StringPiece input, int* output);
+BASE_EXPORT bool StringToInt(StringPiece16 input, int* output);
+
+BASE_EXPORT bool StringToUint(StringPiece input, unsigned* output);
+BASE_EXPORT bool StringToUint(StringPiece16 input, unsigned* output);
+
+BASE_EXPORT bool StringToInt64(StringPiece input, int64_t* output);
+BASE_EXPORT bool StringToInt64(StringPiece16 input, int64_t* output);
+
+BASE_EXPORT bool StringToUint64(StringPiece input, uint64_t* output);
+BASE_EXPORT bool StringToUint64(StringPiece16 input, uint64_t* output);
+
+BASE_EXPORT bool StringToSizeT(StringPiece input, size_t* output);
+BASE_EXPORT bool StringToSizeT(StringPiece16 input, size_t* output);
+
+// For floating-point conversions, only conversions of input strings in decimal
+// form are defined to work.  Behavior with strings representing floating-point
+// numbers in hexadecimal, and strings representing non-finite values (such as
+// NaN and inf) is undefined.  Otherwise, these behave the same as the integral
+// variants.  This expects the input string to NOT be specific to the locale.
+// If your input is locale specific, use ICU to read the number.
+// WARNING: Will write to |output| even when returning false.
+//          Read the comments here and above StringToInt() carefully.
+BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
+
+// Hex encoding ----------------------------------------------------------------
+
+// Returns a hex string representation of a binary buffer. The returned hex
+// string will be in upper case. This function does not check if |size| is
+// within reasonable limits since it's written with trusted data in mind.  If
+// you suspect that the data you want to format might be large, the absolute
+// max size for |size| should be is
+//   std::numeric_limits<size_t>::max() / 2
+BASE_EXPORT std::string HexEncode(const void* bytes, size_t size);
+BASE_EXPORT std::string HexEncode(gurl_base::span<const uint8_t> bytes);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// -0x80000000 < |input| < 0x7FFFFFFF.
+BASE_EXPORT bool HexStringToInt(StringPiece input, int* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// 0x00000000 < |input| < 0xFFFFFFFF.
+// The string is not required to start with 0x.
+BASE_EXPORT bool HexStringToUInt(StringPiece input, uint32_t* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// -0x8000000000000000 < |input| < 0x7FFFFFFFFFFFFFFF.
+BASE_EXPORT bool HexStringToInt64(StringPiece input, int64_t* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// 0x0000000000000000 < |input| < 0xFFFFFFFFFFFFFFFF.
+// The string is not required to start with 0x.
+BASE_EXPORT bool HexStringToUInt64(StringPiece input, uint64_t* output);
+
+// Similar to the previous functions, except that output is a vector of bytes.
+// |*output| will contain as many bytes as were successfully parsed prior to the
+// error.  There is no overflow, but input.size() must be evenly divisible by 2.
+// Leading 0x or +/- are not allowed.
+BASE_EXPORT bool HexStringToBytes(StringPiece input,
+                                  std::vector<uint8_t>* output);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
diff --git a/base/strings/string_number_conversions_fuzzer.cc b/base/strings/string_number_conversions_fuzzer.cc
new file mode 100644
index 0000000..012887a
--- /dev/null
+++ b/base/strings/string_number_conversions_fuzzer.cc
@@ -0,0 +1,118 @@
+// Copyright 2015 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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+
+template <class NumberType, class StringPieceType, class StringType>
+void CheckRoundtripsT(const uint8_t* data,
+                      const size_t size,
+                      StringType (*num_to_string)(NumberType),
+                      bool (*string_to_num)(StringPieceType, NumberType*)) {
+  // Ensure we can read a NumberType from |data|
+  if (size < sizeof(NumberType))
+    return;
+  const NumberType v1 = *reinterpret_cast<const NumberType*>(data);
+
+  // Because we started with an arbitrary NumberType value, not an arbitrary
+  // string, we expect that the function |string_to_num| (e.g. StringToInt) will
+  // return true, indicating a perfect conversion.
+  NumberType v2;
+  GURL_CHECK(string_to_num(num_to_string(v1), &v2));
+
+  // Given that this was a perfect conversion, we expect the original NumberType
+  // value to equal the newly parsed one.
+  GURL_CHECK_EQ(v1, v2);
+}
+
+template <class NumberType>
+void CheckRoundtrips(const uint8_t* data,
+                     const size_t size,
+                     bool (*string_to_num)(gurl_base::StringPiece, NumberType*)) {
+  return CheckRoundtripsT<NumberType, gurl_base::StringPiece, std::string>(
+      data, size, &gurl_base::NumberToString, string_to_num);
+}
+
+template <class NumberType>
+void CheckRoundtrips16(const uint8_t* data,
+                       const size_t size,
+                       bool (*string_to_num)(gurl_base::StringPiece16,
+                                             NumberType*)) {
+  return CheckRoundtripsT<NumberType, gurl_base::StringPiece16, gurl_base::string16>(
+      data, size, &gurl_base::NumberToString16, string_to_num);
+}
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // For each instantiation of NumberToString f and its corresponding StringTo*
+  // function g, check that f(g(x)) = x holds for fuzzer-determined values of x.
+  CheckRoundtrips<int>(data, size, &gurl_base::StringToInt);
+  CheckRoundtrips16<int>(data, size, &gurl_base::StringToInt);
+  CheckRoundtrips<unsigned int>(data, size, &gurl_base::StringToUint);
+  CheckRoundtrips16<unsigned int>(data, size, &gurl_base::StringToUint);
+  CheckRoundtrips<int64_t>(data, size, &gurl_base::StringToInt64);
+  CheckRoundtrips16<int64_t>(data, size, &gurl_base::StringToInt64);
+  CheckRoundtrips<uint64_t>(data, size, &gurl_base::StringToUint64);
+  CheckRoundtrips16<uint64_t>(data, size, &gurl_base::StringToUint64);
+  CheckRoundtrips<size_t>(data, size, &gurl_base::StringToSizeT);
+  CheckRoundtrips16<size_t>(data, size, &gurl_base::StringToSizeT);
+
+  gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data),
+                                       size);
+  std::string string_input(reinterpret_cast<const char*>(data), size);
+
+  int out_int;
+  gurl_base::StringToInt(string_piece_input, &out_int);
+  unsigned out_uint;
+  gurl_base::StringToUint(string_piece_input, &out_uint);
+  int64_t out_int64;
+  gurl_base::StringToInt64(string_piece_input, &out_int64);
+  uint64_t out_uint64;
+  gurl_base::StringToUint64(string_piece_input, &out_uint64);
+  size_t out_size;
+  gurl_base::StringToSizeT(string_piece_input, &out_size);
+
+  // Test for StringPiece16 if size is even.
+  if (size % 2 == 0) {
+    gurl_base::StringPiece16 string_piece_input16(
+        reinterpret_cast<const gurl_base::char16*>(data), size / 2);
+
+    gurl_base::StringToInt(string_piece_input16, &out_int);
+    gurl_base::StringToUint(string_piece_input16, &out_uint);
+    gurl_base::StringToInt64(string_piece_input16, &out_int64);
+    gurl_base::StringToUint64(string_piece_input16, &out_uint64);
+    gurl_base::StringToSizeT(string_piece_input16, &out_size);
+  }
+
+  double out_double;
+  gurl_base::StringToDouble(string_input, &out_double);
+
+  gurl_base::HexStringToInt(string_piece_input, &out_int);
+  gurl_base::HexStringToUInt(string_piece_input, &out_uint);
+  gurl_base::HexStringToInt64(string_piece_input, &out_int64);
+  gurl_base::HexStringToUInt64(string_piece_input, &out_uint64);
+  std::vector<uint8_t> out_bytes;
+  gurl_base::HexStringToBytes(string_piece_input, &out_bytes);
+
+  gurl_base::HexEncode(data, size);
+
+  // Convert the numbers back to strings.
+  gurl_base::NumberToString(out_int);
+  gurl_base::NumberToString16(out_int);
+  gurl_base::NumberToString(out_uint);
+  gurl_base::NumberToString16(out_uint);
+  gurl_base::NumberToString(out_int64);
+  gurl_base::NumberToString16(out_int64);
+  gurl_base::NumberToString(out_uint64);
+  gurl_base::NumberToString16(out_uint64);
+  gurl_base::NumberToString(out_double);
+  gurl_base::NumberToString16(out_double);
+
+  return 0;
+}
diff --git a/base/strings/string_number_conversions_unittest.cc b/base/strings/string_number_conversions_unittest.cc
new file mode 100644
index 0000000..93405e2
--- /dev/null
+++ b/base/strings/string_number_conversions_unittest.cc
@@ -0,0 +1,901 @@
+// Copyright (c) 2012 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 "base/strings/string_number_conversions.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <cmath>
+#include <limits>
+
+#include "base/bit_cast.h"
+#include "base/format_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+namespace {
+
+template <typename INT>
+struct NumberToStringTest {
+  INT num;
+  const char* sexpected;
+  const char* uexpected;
+};
+
+}  // namespace
+
+TEST(StringNumberConversionsTest, NumberToString) {
+  static const NumberToStringTest<int> int_tests[] = {
+      {0, "0", "0"},
+      {-1, "-1", "4294967295"},
+      {std::numeric_limits<int>::max(), "2147483647", "2147483647"},
+      {std::numeric_limits<int>::min(), "-2147483648", "2147483648"},
+  };
+  static const NumberToStringTest<int64_t> int64_tests[] = {
+      {0, "0", "0"},
+      {-1, "-1", "18446744073709551615"},
+      {
+          std::numeric_limits<int64_t>::max(), "9223372036854775807",
+          "9223372036854775807",
+      },
+      {std::numeric_limits<int64_t>::min(), "-9223372036854775808",
+       "9223372036854775808"},
+  };
+
+  for (const auto& test : int_tests) {
+    EXPECT_EQ(NumberToString(test.num), test.sexpected);
+    EXPECT_EQ(NumberToString16(test.num), UTF8ToUTF16(test.sexpected));
+    EXPECT_EQ(NumberToString(static_cast<unsigned>(test.num)), test.uexpected);
+    EXPECT_EQ(NumberToString16(static_cast<unsigned>(test.num)),
+              UTF8ToUTF16(test.uexpected));
+  }
+  for (const auto& test : int64_tests) {
+    EXPECT_EQ(NumberToString(test.num), test.sexpected);
+    EXPECT_EQ(NumberToString16(test.num), UTF8ToUTF16(test.sexpected));
+    EXPECT_EQ(NumberToString(static_cast<uint64_t>(test.num)), test.uexpected);
+    EXPECT_EQ(NumberToString16(static_cast<uint64_t>(test.num)),
+              UTF8ToUTF16(test.uexpected));
+  }
+}
+
+TEST(StringNumberConversionsTest, Uint64ToString) {
+  static const struct {
+    uint64_t input;
+    std::string output;
+  } cases[] = {
+      {0, "0"},
+      {42, "42"},
+      {INT_MAX, "2147483647"},
+      {std::numeric_limits<uint64_t>::max(), "18446744073709551615"},
+  };
+
+  for (const auto& i : cases)
+    EXPECT_EQ(i.output, NumberToString(i.input));
+}
+
+TEST(StringNumberConversionsTest, SizeTToString) {
+  size_t size_t_max = std::numeric_limits<size_t>::max();
+  std::string size_t_max_string = StringPrintf("%" PRIuS, size_t_max);
+
+  static const struct {
+    size_t input;
+    std::string output;
+  } cases[] = {
+    {0, "0"},
+    {9, "9"},
+    {42, "42"},
+    {INT_MAX, "2147483647"},
+    {2147483648U, "2147483648"},
+#if SIZE_MAX > 4294967295U
+    {99999999999U, "99999999999"},
+#endif
+    {size_t_max, size_t_max_string},
+  };
+
+  for (const auto& i : cases)
+    EXPECT_EQ(i.output, NumberToString(i.input));
+}
+
+TEST(StringNumberConversionsTest, StringToInt) {
+  static const struct {
+    std::string input;
+    int output;
+    bool success;
+  } cases[] = {
+    {"0", 0, true},
+    {"42", 42, true},
+    {"42\x99", 42, false},
+    {"\x99" "42\x99", 0, false},
+    {"-2147483648", INT_MIN, true},
+    {"2147483647", INT_MAX, true},
+    {"", 0, false},
+    {" 42", 42, false},
+    {"42 ", 42, false},
+    {"\t\n\v\f\r 42", 42, false},
+    {"blah42", 0, false},
+    {"42blah", 42, false},
+    {"blah42blah", 0, false},
+    {"-273.15", -273, false},
+    {"+98.6", 98, false},
+    {"--123", 0, false},
+    {"++123", 0, false},
+    {"-+123", 0, false},
+    {"+-123", 0, false},
+    {"-", 0, false},
+    {"-2147483649", INT_MIN, false},
+    {"-99999999999", INT_MIN, false},
+    {"2147483648", INT_MAX, false},
+    {"99999999999", INT_MAX, false},
+  };
+
+  for (const auto& i : cases) {
+    int output = i.output ^ 1;  // Ensure StringToInt wrote something.
+    EXPECT_EQ(i.success, StringToInt(i.input, &output));
+    EXPECT_EQ(i.output, output);
+
+    string16 utf16_input = UTF8ToUTF16(i.input);
+    output = i.output ^ 1;  // Ensure StringToInt wrote something.
+    EXPECT_EQ(i.success, StringToInt(utf16_input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "6\06";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  int output;
+  EXPECT_FALSE(StringToInt(input_string, &output));
+  EXPECT_EQ(6, output);
+
+  string16 utf16_input = UTF8ToUTF16(input_string);
+  output = 0;
+  EXPECT_FALSE(StringToInt(utf16_input, &output));
+  EXPECT_EQ(6, output);
+
+  output = 0;
+  const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0};
+  EXPECT_FALSE(StringToInt(string16(negative_wide_input), &output));
+  EXPECT_EQ(0, output);
+}
+
+TEST(StringNumberConversionsTest, StringToUint) {
+  static const struct {
+    std::string input;
+    unsigned output;
+    bool success;
+  } cases[] = {
+    {"0", 0, true},
+    {"42", 42, true},
+    {"42\x99", 42, false},
+    {"\x99" "42\x99", 0, false},
+    {"-2147483648", 0, false},
+    {"2147483647", INT_MAX, true},
+    {"", 0, false},
+    {" 42", 42, false},
+    {"42 ", 42, false},
+    {"\t\n\v\f\r 42", 42, false},
+    {"blah42", 0, false},
+    {"42blah", 42, false},
+    {"blah42blah", 0, false},
+    {"-273.15", 0, false},
+    {"+98.6", 98, false},
+    {"--123", 0, false},
+    {"++123", 0, false},
+    {"-+123", 0, false},
+    {"+-123", 0, false},
+    {"-", 0, false},
+    {"-2147483649", 0, false},
+    {"-99999999999", 0, false},
+    {"4294967295", UINT_MAX, true},
+    {"4294967296", UINT_MAX, false},
+    {"99999999999", UINT_MAX, false},
+  };
+
+  for (const auto& i : cases) {
+    unsigned output = i.output ^ 1;  // Ensure StringToUint wrote something.
+    EXPECT_EQ(i.success, StringToUint(i.input, &output));
+    EXPECT_EQ(i.output, output);
+
+    string16 utf16_input = UTF8ToUTF16(i.input);
+    output = i.output ^ 1;  // Ensure StringToUint wrote something.
+    EXPECT_EQ(i.success, StringToUint(utf16_input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "6\06";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  unsigned output;
+  EXPECT_FALSE(StringToUint(input_string, &output));
+  EXPECT_EQ(6U, output);
+
+  string16 utf16_input = UTF8ToUTF16(input_string);
+  output = 0;
+  EXPECT_FALSE(StringToUint(utf16_input, &output));
+  EXPECT_EQ(6U, output);
+
+  output = 0;
+  const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0};
+  EXPECT_FALSE(StringToUint(string16(negative_wide_input), &output));
+  EXPECT_EQ(0U, output);
+}
+
+TEST(StringNumberConversionsTest, StringToInt64) {
+  static const struct {
+    std::string input;
+    int64_t output;
+    bool success;
+  } cases[] = {
+      {"0", 0, true},
+      {"42", 42, true},
+      {"-2147483648", INT_MIN, true},
+      {"2147483647", INT_MAX, true},
+      {"-2147483649", INT64_C(-2147483649), true},
+      {"-99999999999", INT64_C(-99999999999), true},
+      {"2147483648", INT64_C(2147483648), true},
+      {"99999999999", INT64_C(99999999999), true},
+      {"9223372036854775807", std::numeric_limits<int64_t>::max(), true},
+      {"-9223372036854775808", std::numeric_limits<int64_t>::min(), true},
+      {"09", 9, true},
+      {"-09", -9, true},
+      {"", 0, false},
+      {" 42", 42, false},
+      {"42 ", 42, false},
+      {"0x42", 0, false},
+      {"\t\n\v\f\r 42", 42, false},
+      {"blah42", 0, false},
+      {"42blah", 42, false},
+      {"blah42blah", 0, false},
+      {"-273.15", -273, false},
+      {"+98.6", 98, false},
+      {"--123", 0, false},
+      {"++123", 0, false},
+      {"-+123", 0, false},
+      {"+-123", 0, false},
+      {"-", 0, false},
+      {"-9223372036854775809", std::numeric_limits<int64_t>::min(), false},
+      {"-99999999999999999999", std::numeric_limits<int64_t>::min(), false},
+      {"9223372036854775808", std::numeric_limits<int64_t>::max(), false},
+      {"99999999999999999999", std::numeric_limits<int64_t>::max(), false},
+  };
+
+  for (const auto& i : cases) {
+    int64_t output = 0;
+    EXPECT_EQ(i.success, StringToInt64(i.input, &output));
+    EXPECT_EQ(i.output, output);
+
+    string16 utf16_input = UTF8ToUTF16(i.input);
+    output = 0;
+    EXPECT_EQ(i.success, StringToInt64(utf16_input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "6\06";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  int64_t output;
+  EXPECT_FALSE(StringToInt64(input_string, &output));
+  EXPECT_EQ(6, output);
+
+  string16 utf16_input = UTF8ToUTF16(input_string);
+  output = 0;
+  EXPECT_FALSE(StringToInt64(utf16_input, &output));
+  EXPECT_EQ(6, output);
+}
+
+TEST(StringNumberConversionsTest, StringToUint64) {
+  static const struct {
+    std::string input;
+    uint64_t output;
+    bool success;
+  } cases[] = {
+      {"0", 0, true},
+      {"42", 42, true},
+      {"-2147483648", 0, false},
+      {"2147483647", INT_MAX, true},
+      {"-2147483649", 0, false},
+      {"-99999999999", 0, false},
+      {"2147483648", UINT64_C(2147483648), true},
+      {"99999999999", UINT64_C(99999999999), true},
+      {"9223372036854775807", std::numeric_limits<int64_t>::max(), true},
+      {"-9223372036854775808", 0, false},
+      {"09", 9, true},
+      {"-09", 0, false},
+      {"", 0, false},
+      {" 42", 42, false},
+      {"42 ", 42, false},
+      {"0x42", 0, false},
+      {"\t\n\v\f\r 42", 42, false},
+      {"blah42", 0, false},
+      {"42blah", 42, false},
+      {"blah42blah", 0, false},
+      {"-273.15", 0, false},
+      {"+98.6", 98, false},
+      {"--123", 0, false},
+      {"++123", 0, false},
+      {"-+123", 0, false},
+      {"+-123", 0, false},
+      {"-", 0, false},
+      {"-9223372036854775809", 0, false},
+      {"-99999999999999999999", 0, false},
+      {"9223372036854775808", UINT64_C(9223372036854775808), true},
+      {"99999999999999999999", std::numeric_limits<uint64_t>::max(), false},
+      {"18446744073709551615", std::numeric_limits<uint64_t>::max(), true},
+      {"18446744073709551616", std::numeric_limits<uint64_t>::max(), false},
+  };
+
+  for (const auto& i : cases) {
+    uint64_t output = 0;
+    EXPECT_EQ(i.success, StringToUint64(i.input, &output));
+    EXPECT_EQ(i.output, output);
+
+    string16 utf16_input = UTF8ToUTF16(i.input);
+    output = 0;
+    EXPECT_EQ(i.success, StringToUint64(utf16_input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "6\06";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  uint64_t output;
+  EXPECT_FALSE(StringToUint64(input_string, &output));
+  EXPECT_EQ(6U, output);
+
+  string16 utf16_input = UTF8ToUTF16(input_string);
+  output = 0;
+  EXPECT_FALSE(StringToUint64(utf16_input, &output));
+  EXPECT_EQ(6U, output);
+}
+
+TEST(StringNumberConversionsTest, StringToSizeT) {
+  size_t size_t_max = std::numeric_limits<size_t>::max();
+  std::string size_t_max_string = StringPrintf("%" PRIuS, size_t_max);
+
+  static const struct {
+    std::string input;
+    size_t output;
+    bool success;
+  } cases[] = {
+    {"0", 0, true},
+    {"42", 42, true},
+    {"-2147483648", 0, false},
+    {"2147483647", INT_MAX, true},
+    {"-2147483649", 0, false},
+    {"-99999999999", 0, false},
+    {"2147483648", 2147483648U, true},
+#if SIZE_MAX > 4294967295U
+    {"99999999999", 99999999999U, true},
+#endif
+    {"-9223372036854775808", 0, false},
+    {"09", 9, true},
+    {"-09", 0, false},
+    {"", 0, false},
+    {" 42", 42, false},
+    {"42 ", 42, false},
+    {"0x42", 0, false},
+    {"\t\n\v\f\r 42", 42, false},
+    {"blah42", 0, false},
+    {"42blah", 42, false},
+    {"blah42blah", 0, false},
+    {"-273.15", 0, false},
+    {"+98.6", 98, false},
+    {"--123", 0, false},
+    {"++123", 0, false},
+    {"-+123", 0, false},
+    {"+-123", 0, false},
+    {"-", 0, false},
+    {"-9223372036854775809", 0, false},
+    {"-99999999999999999999", 0, false},
+    {"999999999999999999999999", size_t_max, false},
+    {size_t_max_string, size_t_max, true},
+  };
+
+  for (const auto& i : cases) {
+    size_t output = 0;
+    EXPECT_EQ(i.success, StringToSizeT(i.input, &output));
+    EXPECT_EQ(i.output, output);
+
+    string16 utf16_input = UTF8ToUTF16(i.input);
+    output = 0;
+    EXPECT_EQ(i.success, StringToSizeT(utf16_input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "6\06";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  size_t output;
+  EXPECT_FALSE(StringToSizeT(input_string, &output));
+  EXPECT_EQ(6U, output);
+
+  string16 utf16_input = UTF8ToUTF16(input_string);
+  output = 0;
+  EXPECT_FALSE(StringToSizeT(utf16_input, &output));
+  EXPECT_EQ(6U, output);
+}
+
+TEST(StringNumberConversionsTest, HexStringToInt) {
+  static const struct {
+    std::string input;
+    int64_t output;
+    bool success;
+  } cases[] = {
+    {"0", 0, true},
+    {"42", 66, true},
+    {"-42", -66, true},
+    {"+42", 66, true},
+    {"7fffffff", INT_MAX, true},
+    {"-80000000", INT_MIN, true},
+    {"80000000", INT_MAX, false},  // Overflow test.
+    {"-80000001", INT_MIN, false},  // Underflow test.
+    {"0x42", 66, true},
+    {"-0x42", -66, true},
+    {"+0x42", 66, true},
+    {"0x7fffffff", INT_MAX, true},
+    {"-0x80000000", INT_MIN, true},
+    {"-80000000", INT_MIN, true},
+    {"80000000", INT_MAX, false},  // Overflow test.
+    {"-80000001", INT_MIN, false},  // Underflow test.
+    {"0x0f", 15, true},
+    {"0f", 15, true},
+    {" 45", 0x45, false},
+    {"\t\n\v\f\r 0x45", 0x45, false},
+    {" 45", 0x45, false},
+    {"45 ", 0x45, false},
+    {"45:", 0x45, false},
+    {"efgh", 0xef, false},
+    {"0xefgh", 0xef, false},
+    {"hgfe", 0, false},
+    {"-", 0, false},
+    {"", 0, false},
+    {"0x", 0, false},
+  };
+
+  for (const auto& i : cases) {
+    int output = 0;
+    EXPECT_EQ(i.success, HexStringToInt(i.input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "0xc0ffee\0" "9";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  int output;
+  EXPECT_FALSE(HexStringToInt(input_string, &output));
+  EXPECT_EQ(0xc0ffee, output);
+}
+
+TEST(StringNumberConversionsTest, HexStringToUInt) {
+  static const struct {
+    std::string input;
+    uint32_t output;
+    bool success;
+  } cases[] = {
+      {"0", 0, true},
+      {"42", 0x42, true},
+      {"-42", 0, false},
+      {"+42", 0x42, true},
+      {"7fffffff", INT_MAX, true},
+      {"-80000000", 0, false},
+      {"ffffffff", 0xffffffff, true},
+      {"DeadBeef", 0xdeadbeef, true},
+      {"0x42", 0x42, true},
+      {"-0x42", 0, false},
+      {"+0x42", 0x42, true},
+      {"0x7fffffff", INT_MAX, true},
+      {"-0x80000000", 0, false},
+      {"0xffffffff", std::numeric_limits<uint32_t>::max(), true},
+      {"0XDeadBeef", 0xdeadbeef, true},
+      {"0x7fffffffffffffff", std::numeric_limits<uint32_t>::max(),
+       false},  // Overflow test.
+      {"-0x8000000000000000", 0, false},
+      {"0x8000000000000000", std::numeric_limits<uint32_t>::max(),
+       false},  // Overflow test.
+      {"-0x8000000000000001", 0, false},
+      {"0xFFFFFFFFFFFFFFFF", std::numeric_limits<uint32_t>::max(),
+       false},  // Overflow test.
+      {"FFFFFFFFFFFFFFFF", std::numeric_limits<uint32_t>::max(),
+       false},  // Overflow test.
+      {"0x0000000000000000", 0, true},
+      {"0000000000000000", 0, true},
+      {"1FFFFFFFFFFFFFFFF", std::numeric_limits<uint32_t>::max(),
+       false},  // Overflow test.
+      {"0x0f", 0x0f, true},
+      {"0f", 0x0f, true},
+      {" 45", 0x45, false},
+      {"\t\n\v\f\r 0x45", 0x45, false},
+      {" 45", 0x45, false},
+      {"45 ", 0x45, false},
+      {"45:", 0x45, false},
+      {"efgh", 0xef, false},
+      {"0xefgh", 0xef, false},
+      {"hgfe", 0, false},
+      {"-", 0, false},
+      {"", 0, false},
+      {"0x", 0, false},
+  };
+
+  for (const auto& i : cases) {
+    uint32_t output = 0;
+    EXPECT_EQ(i.success, HexStringToUInt(i.input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "0xc0ffee\0" "9";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  uint32_t output;
+  EXPECT_FALSE(HexStringToUInt(input_string, &output));
+  EXPECT_EQ(0xc0ffeeU, output);
+}
+
+TEST(StringNumberConversionsTest, HexStringToInt64) {
+  static const struct {
+    std::string input;
+    int64_t output;
+    bool success;
+  } cases[] = {
+      {"0", 0, true},
+      {"42", 66, true},
+      {"-42", -66, true},
+      {"+42", 66, true},
+      {"40acd88557b", INT64_C(4444444448123), true},
+      {"7fffffff", INT_MAX, true},
+      {"-80000000", INT_MIN, true},
+      {"ffffffff", 0xffffffff, true},
+      {"DeadBeef", 0xdeadbeef, true},
+      {"0x42", 66, true},
+      {"-0x42", -66, true},
+      {"+0x42", 66, true},
+      {"0x40acd88557b", INT64_C(4444444448123), true},
+      {"0x7fffffff", INT_MAX, true},
+      {"-0x80000000", INT_MIN, true},
+      {"0xffffffff", 0xffffffff, true},
+      {"0XDeadBeef", 0xdeadbeef, true},
+      {"0x7fffffffffffffff", std::numeric_limits<int64_t>::max(), true},
+      {"-0x8000000000000000", std::numeric_limits<int64_t>::min(), true},
+      {"0x8000000000000000", std::numeric_limits<int64_t>::max(),
+       false},  // Overflow test.
+      {"-0x8000000000000001", std::numeric_limits<int64_t>::min(),
+       false},  // Underflow test.
+      {"0x0f", 15, true},
+      {"0f", 15, true},
+      {" 45", 0x45, false},
+      {"\t\n\v\f\r 0x45", 0x45, false},
+      {" 45", 0x45, false},
+      {"45 ", 0x45, false},
+      {"45:", 0x45, false},
+      {"efgh", 0xef, false},
+      {"0xefgh", 0xef, false},
+      {"hgfe", 0, false},
+      {"-", 0, false},
+      {"", 0, false},
+      {"0x", 0, false},
+  };
+
+  for (const auto& i : cases) {
+    int64_t output = 0;
+    EXPECT_EQ(i.success, HexStringToInt64(i.input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "0xc0ffee\0" "9";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  int64_t output;
+  EXPECT_FALSE(HexStringToInt64(input_string, &output));
+  EXPECT_EQ(0xc0ffee, output);
+}
+
+TEST(StringNumberConversionsTest, HexStringToUInt64) {
+  static const struct {
+    std::string input;
+    uint64_t output;
+    bool success;
+  } cases[] = {
+      {"0", 0, true},
+      {"42", 66, true},
+      {"-42", 0, false},
+      {"+42", 66, true},
+      {"40acd88557b", INT64_C(4444444448123), true},
+      {"7fffffff", INT_MAX, true},
+      {"-80000000", 0, false},
+      {"ffffffff", 0xffffffff, true},
+      {"DeadBeef", 0xdeadbeef, true},
+      {"0x42", 66, true},
+      {"-0x42", 0, false},
+      {"+0x42", 66, true},
+      {"0x40acd88557b", INT64_C(4444444448123), true},
+      {"0x7fffffff", INT_MAX, true},
+      {"-0x80000000", 0, false},
+      {"0xffffffff", 0xffffffff, true},
+      {"0XDeadBeef", 0xdeadbeef, true},
+      {"0x7fffffffffffffff", std::numeric_limits<int64_t>::max(), true},
+      {"-0x8000000000000000", 0, false},
+      {"0x8000000000000000", UINT64_C(0x8000000000000000), true},
+      {"-0x8000000000000001", 0, false},
+      {"0xFFFFFFFFFFFFFFFF", std::numeric_limits<uint64_t>::max(), true},
+      {"FFFFFFFFFFFFFFFF", std::numeric_limits<uint64_t>::max(), true},
+      {"0x0000000000000000", 0, true},
+      {"0000000000000000", 0, true},
+      {"1FFFFFFFFFFFFFFFF", std::numeric_limits<uint64_t>::max(),
+       false},  // Overflow test.
+      {"0x0f", 15, true},
+      {"0f", 15, true},
+      {" 45", 0x45, false},
+      {"\t\n\v\f\r 0x45", 0x45, false},
+      {" 45", 0x45, false},
+      {"45 ", 0x45, false},
+      {"45:", 0x45, false},
+      {"efgh", 0xef, false},
+      {"0xefgh", 0xef, false},
+      {"hgfe", 0, false},
+      {"-", 0, false},
+      {"", 0, false},
+      {"0x", 0, false},
+  };
+
+  for (const auto& i : cases) {
+    uint64_t output = 0;
+    EXPECT_EQ(i.success, HexStringToUInt64(i.input, &output));
+    EXPECT_EQ(i.output, output);
+  }
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "0xc0ffee\0" "9";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  uint64_t output;
+  EXPECT_FALSE(HexStringToUInt64(input_string, &output));
+  EXPECT_EQ(0xc0ffeeU, output);
+}
+
+TEST(StringNumberConversionsTest, HexStringToBytes) {
+  static const struct {
+    const std::string input;
+    const char* output;
+    size_t output_len;
+    bool success;
+  } cases[] = {
+    {"0", "", 0, false},  // odd number of characters fails
+    {"00", "\0", 1, true},
+    {"42", "\x42", 1, true},
+    {"-42", "", 0, false},  // any non-hex value fails
+    {"+42", "", 0, false},
+    {"7fffffff", "\x7f\xff\xff\xff", 4, true},
+    {"80000000", "\x80\0\0\0", 4, true},
+    {"deadbeef", "\xde\xad\xbe\xef", 4, true},
+    {"DeadBeef", "\xde\xad\xbe\xef", 4, true},
+    {"0x42", "", 0, false},  // leading 0x fails (x is not hex)
+    {"0f", "\xf", 1, true},
+    {"45  ", "\x45", 1, false},
+    {"efgh", "\xef", 1, false},
+    {"", "", 0, false},
+    {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true},
+    {"0123456789ABCDEF012345",
+     "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true},
+  };
+
+  for (size_t i = 0; i < gurl_base::size(cases); ++i) {
+    std::vector<uint8_t> output;
+    std::vector<uint8_t> compare;
+    EXPECT_EQ(cases[i].success, HexStringToBytes(cases[i].input, &output)) <<
+        i << ": " << cases[i].input;
+    for (size_t j = 0; j < cases[i].output_len; ++j)
+      compare.push_back(static_cast<uint8_t>(cases[i].output[j]));
+    ASSERT_EQ(output.size(), compare.size()) << i << ": " << cases[i].input;
+    EXPECT_TRUE(std::equal(output.begin(), output.end(), compare.begin())) <<
+        i << ": " << cases[i].input;
+  }
+}
+
+TEST(StringNumberConversionsTest, StringToDouble) {
+  static const struct {
+    std::string input;
+    double output;
+    bool success;
+  } cases[] = {
+    // Test different forms of zero.
+    {"0", 0.0, true},
+    {"+0", 0.0, true},
+    {"-0", 0.0, true},
+    {"0.0", 0.0, true},
+    {"000000000000000000000000000000.0", 0.0, true},
+    {"0.000000000000000000000000000", 0.0, true},
+
+    // Test the answer.
+    {"42", 42.0, true},
+    {"-42", -42.0, true},
+
+    // Test variances of an ordinary number.
+    {"123.45", 123.45, true},
+    {"-123.45", -123.45, true},
+    {"+123.45", 123.45, true},
+
+    // Test different forms of representation.
+    {"2.99792458e8", 299792458.0, true},
+    {"149597870.691E+3", 149597870691.0, true},
+    {"6.", 6.0, true},
+
+    // Test around the largest/smallest value that a double can represent.
+    {"9e307", 9e307, true},
+    {"1.7976e308", 1.7976e308, true},
+    {"1.7977e308", HUGE_VAL, false},
+    {"1.797693134862315807e+308", HUGE_VAL, true},
+    {"1.797693134862315808e+308", HUGE_VAL, false},
+    {"9e308", HUGE_VAL, false},
+    {"9e309", HUGE_VAL, false},
+    {"9e999", HUGE_VAL, false},
+    {"9e1999", HUGE_VAL, false},
+    {"9e19999", HUGE_VAL, false},
+    {"9e99999999999999999999", HUGE_VAL, false},
+    {"-9e307", -9e307, true},
+    {"-1.7976e308", -1.7976e308, true},
+    {"-1.7977e308", -HUGE_VAL, false},
+    {"-1.797693134862315807e+308", -HUGE_VAL, true},
+    {"-1.797693134862315808e+308", -HUGE_VAL, false},
+    {"-9e308", -HUGE_VAL, false},
+    {"-9e309", -HUGE_VAL, false},
+    {"-9e999", -HUGE_VAL, false},
+    {"-9e1999", -HUGE_VAL, false},
+    {"-9e19999", -HUGE_VAL, false},
+    {"-9e99999999999999999999", -HUGE_VAL, false},
+
+    // Test more exponents.
+    {"1e-2", 0.01, true},
+    {"42 ", 42.0, false},
+    {" 1e-2", 0.01, false},
+    {"1e-2 ", 0.01, false},
+    {"-1E-7", -0.0000001, true},
+    {"01e02", 100, true},
+    {"2.3e15", 2.3e15, true},
+    {"100e-309", 100e-309, true},
+
+    // Test some invalid cases.
+    {"\t\n\v\f\r -123.45e2", -12345.0, false},
+    {"+123 e4", 123.0, false},
+    {"123e ", 123.0, false},
+    {"123e", 123.0, false},
+    {" 2.99", 2.99, false},
+    {"1e3.4", 1000.0, false},
+    {"nothing", 0.0, false},
+    {"-", 0.0, false},
+    {"+", 0.0, false},
+    {"", 0.0, false},
+
+    // crbug.org/588726
+    {"-0.0010000000000000000000000000000000000000001e-256",
+     -1.0000000000000001e-259, true},
+  };
+
+  for (size_t i = 0; i < gurl_base::size(cases); ++i) {
+    double output;
+    errno = 1;
+    EXPECT_EQ(cases[i].success, StringToDouble(cases[i].input, &output));
+    if (cases[i].success)
+      EXPECT_EQ(1, errno) << i;  // confirm that errno is unchanged.
+    EXPECT_DOUBLE_EQ(cases[i].output, output);
+  }
+
+  // One additional test to verify that conversion of numbers in strings with
+  // embedded NUL characters.  The NUL and extra data after it should be
+  // interpreted as junk after the number.
+  const char input[] = "3.14\0" "159";
+  std::string input_string(input, gurl_base::size(input) - 1);
+  double output;
+  EXPECT_FALSE(StringToDouble(input_string, &output));
+  EXPECT_DOUBLE_EQ(3.14, output);
+}
+
+TEST(StringNumberConversionsTest, DoubleToString) {
+  static const struct {
+    double input;
+    const char* expected;
+  } cases[] = {
+    {0.0, "0"},
+    {1.25, "1.25"},
+    {1.33518e+012, "1.33518e+12"},
+    {1.33489e+012, "1.33489e+12"},
+    {1.33505e+012, "1.33505e+12"},
+    {1.33545e+009, "1335450000"},
+    {1.33503e+009, "1335030000"},
+  };
+
+  for (const auto& i : cases) {
+    EXPECT_EQ(i.expected, NumberToString(i.input));
+    EXPECT_EQ(i.expected, UTF16ToUTF8(NumberToString16(i.input)));
+  }
+
+  // The following two values were seen in crashes in the wild.
+  const char input_bytes[8] = {0, 0, 0, 0, '\xee', '\x6d', '\x73', '\x42'};
+  double input = 0;
+  memcpy(&input, input_bytes, gurl_base::size(input_bytes));
+  EXPECT_EQ("1335179083776", NumberToString(input));
+  const char input_bytes2[8] =
+      {0, 0, 0, '\xa0', '\xda', '\x6c', '\x73', '\x42'};
+  input = 0;
+  memcpy(&input, input_bytes2, gurl_base::size(input_bytes2));
+  EXPECT_EQ("1334890332160", NumberToString(input));
+}
+
+TEST(StringNumberConversionsTest, HexEncode) {
+  std::string hex(HexEncode(nullptr, 0));
+  EXPECT_EQ(hex.length(), 0U);
+  unsigned char bytes[] = {0x01, 0xff, 0x02, 0xfe, 0x03, 0x80, 0x81};
+  hex = HexEncode(bytes, sizeof(bytes));
+  EXPECT_EQ(hex.compare("01FF02FE038081"), 0);
+}
+
+// Test cases of known-bad strtod conversions that motivated the use of dmg_fp.
+// See https://bugs.chromium.org/p/chromium/issues/detail?id=593512.
+TEST(StringNumberConversionsTest, StrtodFailures) {
+  static const struct {
+    const char* input;
+    uint64_t expected;
+  } cases[] = {
+      // http://www.exploringbinary.com/incorrectly-rounded-conversions-in-visual-c-plus-plus/
+      {"9214843084008499", 0x43405e6cec57761aULL},
+      {"0.500000000000000166533453693773481063544750213623046875",
+       0x3fe0000000000002ULL},
+      {"30078505129381147446200", 0x44997a3c7271b021ULL},
+      {"1777820000000000000001", 0x4458180d5bad2e3eULL},
+      {"0.500000000000000166547006220929549868969843373633921146392822265625",
+       0x3fe0000000000002ULL},
+      {"0.50000000000000016656055874808561867439493653364479541778564453125",
+       0x3fe0000000000002ULL},
+      {"0.3932922657273", 0x3fd92bb352c4623aULL},
+
+      // http://www.exploringbinary.com/incorrectly-rounded-conversions-in-gcc-and-glibc/
+      {"0.500000000000000166533453693773481063544750213623046875",
+       0x3fe0000000000002ULL},
+      {"3.518437208883201171875e13", 0x42c0000000000002ULL},
+      {"62.5364939768271845828", 0x404f44abd5aa7ca4ULL},
+      {"8.10109172351e-10", 0x3e0bd5cbaef0fd0cULL},
+      {"1.50000000000000011102230246251565404236316680908203125",
+       0x3ff8000000000000ULL},
+      {"9007199254740991.4999999999999999999999999999999995",
+       0x433fffffffffffffULL},
+
+      // http://www.exploringbinary.com/incorrect-decimal-to-floating-point-conversion-in-sqlite/
+      {"1e-23", 0x3b282db34012b251ULL},
+      {"8.533e+68", 0x4e3fa69165a8eea2ULL},
+      {"4.1006e-184", 0x19dbe0d1c7ea60c9ULL},
+      {"9.998e+307", 0x7fe1cc0a350ca87bULL},
+      {"9.9538452227e-280", 0x0602117ae45cde43ULL},
+      {"6.47660115e-260", 0x0a1fdd9e333badadULL},
+      {"7.4e+47", 0x49e033d7eca0adefULL},
+      {"5.92e+48", 0x4a1033d7eca0adefULL},
+      {"7.35e+66", 0x4dd172b70eababa9ULL},
+      {"8.32116e+55", 0x4b8b2628393e02cdULL},
+  };
+
+  for (const auto& test : cases) {
+    double output;
+    EXPECT_TRUE(StringToDouble(test.input, &output));
+    EXPECT_EQ(bit_cast<uint64_t>(output), test.expected);
+  }
+}
+
+}  // namespace base
diff --git a/base/strings/string_piece.cc b/base/strings/string_piece.cc
new file mode 100644
index 0000000..68f3efc
--- /dev/null
+++ b/base/strings/string_piece.cc
@@ -0,0 +1,450 @@
+// Copyright (c) 2012 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.
+// Copied from strings/stringpiece.cc with modifications
+
+#include "base/strings/string_piece.h"
+
+#include <limits.h>
+
+#include <algorithm>
+#include <ostream>
+
+#include "polyfills/base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace gurl_base {
+namespace {
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table.  This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char.  Thus it should be be declared
+// as follows:
+//   bool table[UCHAR_MAX + 1]
+inline void BuildLookupTable(const StringPiece& characters_wanted,
+                             bool* table) {
+  const size_t length = characters_wanted.length();
+  const char* const data = characters_wanted.data();
+  for (size_t i = 0; i < length; ++i) {
+    table[static_cast<unsigned char>(data[i])] = true;
+  }
+}
+
+}  // namespace
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+template class BasicStringPiece<std::string>;
+template class BasicStringPiece<string16>;
+#endif
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
+  o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
+  return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const StringPiece16& piece) {
+  return o << UTF16ToUTF8(piece);
+}
+
+namespace internal {
+
+template<typename STR>
+void CopyToStringT(const BasicStringPiece<STR>& self, STR* target) {
+  if (self.empty())
+    target->clear();
+  else
+    target->assign(self.data(), self.size());
+}
+
+void CopyToString(const StringPiece& self, std::string* target) {
+  CopyToStringT(self, target);
+}
+
+void CopyToString(const StringPiece16& self, string16* target) {
+  CopyToStringT(self, target);
+}
+
+template<typename STR>
+void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
+  if (!self.empty())
+    target->append(self.data(), self.size());
+}
+
+void AppendToString(const StringPiece& self, std::string* target) {
+  AppendToStringT(self, target);
+}
+
+void AppendToString(const StringPiece16& self, string16* target) {
+  AppendToStringT(self, target);
+}
+
+template<typename STR>
+size_t copyT(const BasicStringPiece<STR>& self,
+             typename STR::value_type* buf,
+             size_t n,
+             size_t pos) {
+  size_t ret = std::min(self.size() - pos, n);
+  memcpy(buf, self.data() + pos, ret * sizeof(typename STR::value_type));
+  return ret;
+}
+
+size_t copy(const StringPiece& self, char* buf, size_t n, size_t pos) {
+  return copyT(self, buf, n, pos);
+}
+
+size_t copy(const StringPiece16& self, char16* buf, size_t n, size_t pos) {
+  return copyT(self, buf, n, pos);
+}
+
+template<typename STR>
+size_t findT(const BasicStringPiece<STR>& self,
+             const BasicStringPiece<STR>& s,
+             size_t pos) {
+  if (pos > self.size())
+    return BasicStringPiece<STR>::npos;
+
+  typename BasicStringPiece<STR>::const_iterator result =
+      std::search(self.begin() + pos, self.end(), s.begin(), s.end());
+  const size_t xpos =
+    static_cast<size_t>(result - self.begin());
+  return xpos + s.size() <= self.size() ? xpos : BasicStringPiece<STR>::npos;
+}
+
+size_t find(const StringPiece& self, const StringPiece& s, size_t pos) {
+  return findT(self, s, pos);
+}
+
+size_t find(const StringPiece16& self, const StringPiece16& s, size_t pos) {
+  return findT(self, s, pos);
+}
+
+template<typename STR>
+size_t findT(const BasicStringPiece<STR>& self,
+             typename STR::value_type c,
+             size_t pos) {
+  if (pos >= self.size())
+    return BasicStringPiece<STR>::npos;
+
+  typename BasicStringPiece<STR>::const_iterator result =
+      std::find(self.begin() + pos, self.end(), c);
+  return result != self.end() ?
+      static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
+}
+
+size_t find(const StringPiece& self, char c, size_t pos) {
+  return findT(self, c, pos);
+}
+
+size_t find(const StringPiece16& self, char16 c, size_t pos) {
+  return findT(self, c, pos);
+}
+
+template<typename STR>
+size_t rfindT(const BasicStringPiece<STR>& self,
+              const BasicStringPiece<STR>& s,
+              size_t pos) {
+  if (self.size() < s.size())
+    return BasicStringPiece<STR>::npos;
+
+  if (s.empty())
+    return std::min(self.size(), pos);
+
+  typename BasicStringPiece<STR>::const_iterator last =
+      self.begin() + std::min(self.size() - s.size(), pos) + s.size();
+  typename BasicStringPiece<STR>::const_iterator result =
+      std::find_end(self.begin(), last, s.begin(), s.end());
+  return result != last ?
+      static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
+}
+
+size_t rfind(const StringPiece& self, const StringPiece& s, size_t pos) {
+  return rfindT(self, s, pos);
+}
+
+size_t rfind(const StringPiece16& self, const StringPiece16& s, size_t pos) {
+  return rfindT(self, s, pos);
+}
+
+template<typename STR>
+size_t rfindT(const BasicStringPiece<STR>& self,
+              typename STR::value_type c,
+              size_t pos) {
+  if (self.size() == 0)
+    return BasicStringPiece<STR>::npos;
+
+  for (size_t i = std::min(pos, self.size() - 1); ;
+       --i) {
+    if (self.data()[i] == c)
+      return i;
+    if (i == 0)
+      break;
+  }
+  return BasicStringPiece<STR>::npos;
+}
+
+size_t rfind(const StringPiece& self, char c, size_t pos) {
+  return rfindT(self, c, pos);
+}
+
+size_t rfind(const StringPiece16& self, char16 c, size_t pos) {
+  return rfindT(self, c, pos);
+}
+
+// 8-bit version using lookup table.
+size_t find_first_of(const StringPiece& self,
+                     const StringPiece& s,
+                     size_t pos) {
+  if (self.size() == 0 || s.size() == 0)
+    return StringPiece::npos;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.size() == 1)
+    return find(self, s.data()[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_t i = pos; i < self.size(); ++i) {
+    if (lookup[static_cast<unsigned char>(self.data()[i])]) {
+      return i;
+    }
+  }
+  return StringPiece::npos;
+}
+
+// 16-bit brute force version.
+size_t find_first_of(const StringPiece16& self,
+                     const StringPiece16& s,
+                     size_t pos) {
+  StringPiece16::const_iterator found =
+      std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end());
+  if (found == self.end())
+    return StringPiece16::npos;
+  return found - self.begin();
+}
+
+// 8-bit version using lookup table.
+size_t find_first_not_of(const StringPiece& self,
+                         const StringPiece& s,
+                         size_t pos) {
+  if (self.size() == 0)
+    return StringPiece::npos;
+
+  if (s.size() == 0)
+    return 0;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.size() == 1)
+    return find_first_not_of(self, s.data()[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_t i = pos; i < self.size(); ++i) {
+    if (!lookup[static_cast<unsigned char>(self.data()[i])]) {
+      return i;
+    }
+  }
+  return StringPiece::npos;
+}
+
+// 16-bit brute-force version.
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+                                     const StringPiece16& s,
+                                     size_t pos) {
+  if (self.size() == 0)
+    return StringPiece16::npos;
+
+  for (size_t self_i = pos; self_i < self.size(); ++self_i) {
+    bool found = false;
+    for (auto c : s) {
+      if (self[self_i] == c) {
+        found = true;
+        break;
+      }
+    }
+    if (!found)
+      return self_i;
+  }
+  return StringPiece16::npos;
+}
+
+template<typename STR>
+size_t find_first_not_ofT(const BasicStringPiece<STR>& self,
+                          typename STR::value_type c,
+                          size_t pos) {
+  if (self.size() == 0)
+    return BasicStringPiece<STR>::npos;
+
+  for (; pos < self.size(); ++pos) {
+    if (self.data()[pos] != c) {
+      return pos;
+    }
+  }
+  return BasicStringPiece<STR>::npos;
+}
+
+size_t find_first_not_of(const StringPiece& self,
+                         char c,
+                         size_t pos) {
+  return find_first_not_ofT(self, c, pos);
+}
+
+size_t find_first_not_of(const StringPiece16& self,
+                         char16 c,
+                         size_t pos) {
+  return find_first_not_ofT(self, c, pos);
+}
+
+// 8-bit version using lookup table.
+size_t find_last_of(const StringPiece& self, const StringPiece& s, size_t pos) {
+  if (self.size() == 0 || s.size() == 0)
+    return StringPiece::npos;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.size() == 1)
+    return rfind(self, s.data()[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (size_t i = std::min(pos, self.size() - 1); ; --i) {
+    if (lookup[static_cast<unsigned char>(self.data()[i])])
+      return i;
+    if (i == 0)
+      break;
+  }
+  return StringPiece::npos;
+}
+
+// 16-bit brute-force version.
+size_t find_last_of(const StringPiece16& self,
+                    const StringPiece16& s,
+                    size_t pos) {
+  if (self.size() == 0)
+    return StringPiece16::npos;
+
+  for (size_t self_i = std::min(pos, self.size() - 1); ;
+       --self_i) {
+    for (auto c : s) {
+      if (self.data()[self_i] == c)
+        return self_i;
+    }
+    if (self_i == 0)
+      break;
+  }
+  return StringPiece16::npos;
+}
+
+// 8-bit version using lookup table.
+size_t find_last_not_of(const StringPiece& self,
+                        const StringPiece& s,
+                        size_t pos) {
+  if (self.size() == 0)
+    return StringPiece::npos;
+
+  size_t i = std::min(pos, self.size() - 1);
+  if (s.size() == 0)
+    return i;
+
+  // Avoid the cost of BuildLookupTable() for a single-character search.
+  if (s.size() == 1)
+    return find_last_not_of(self, s.data()[0], pos);
+
+  bool lookup[UCHAR_MAX + 1] = { false };
+  BuildLookupTable(s, lookup);
+  for (; ; --i) {
+    if (!lookup[static_cast<unsigned char>(self.data()[i])])
+      return i;
+    if (i == 0)
+      break;
+  }
+  return StringPiece::npos;
+}
+
+// 16-bit brute-force version.
+size_t find_last_not_of(const StringPiece16& self,
+                        const StringPiece16& s,
+                        size_t pos) {
+  if (self.size() == 0)
+    return StringPiece::npos;
+
+  for (size_t self_i = std::min(pos, self.size() - 1); ; --self_i) {
+    bool found = false;
+    for (auto c : s) {
+      if (self.data()[self_i] == c) {
+        found = true;
+        break;
+      }
+    }
+    if (!found)
+      return self_i;
+    if (self_i == 0)
+      break;
+  }
+  return StringPiece16::npos;
+}
+
+template<typename STR>
+size_t find_last_not_ofT(const BasicStringPiece<STR>& self,
+                         typename STR::value_type c,
+                         size_t pos) {
+  if (self.size() == 0)
+    return BasicStringPiece<STR>::npos;
+
+  for (size_t i = std::min(pos, self.size() - 1); ; --i) {
+    if (self.data()[i] != c)
+      return i;
+    if (i == 0)
+      break;
+  }
+  return BasicStringPiece<STR>::npos;
+}
+
+size_t find_last_not_of(const StringPiece& self,
+                        char c,
+                        size_t pos) {
+  return find_last_not_ofT(self, c, pos);
+}
+
+size_t find_last_not_of(const StringPiece16& self,
+                        char16 c,
+                        size_t pos) {
+  return find_last_not_ofT(self, c, pos);
+}
+
+template<typename STR>
+BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self,
+                              size_t pos,
+                              size_t n) {
+  if (pos > self.size()) pos = self.size();
+  if (n > self.size() - pos) n = self.size() - pos;
+  return BasicStringPiece<STR>(self.data() + pos, n);
+}
+
+StringPiece substr(const StringPiece& self,
+                   size_t pos,
+                   size_t n) {
+  return substrT(self, pos, n);
+}
+
+StringPiece16 substr(const StringPiece16& self,
+                     size_t pos,
+                     size_t n) {
+  return substrT(self, pos, n);
+}
+
+#if GURL_DCHECK_IS_ON()
+void AssertIteratorsInOrder(std::string::const_iterator begin,
+                            std::string::const_iterator end) {
+  GURL_DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
+}
+void AssertIteratorsInOrder(string16::const_iterator begin,
+                            string16::const_iterator end) {
+  GURL_DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
+}
+#endif
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
new file mode 100644
index 0000000..5359af6
--- /dev/null
+++ b/base/strings/string_piece.h
@@ -0,0 +1,548 @@
+// Copyright (c) 2012 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.
+// Copied from strings/stringpiece.h with modifications
+//
+// A string-like object that points to a sized piece of memory.
+//
+// You can use StringPiece as a function or method parameter.  A StringPiece
+// parameter can receive a double-quoted string literal argument, a "const
+// char*" argument, a string argument, or a StringPiece argument with no data
+// copying.  Systematic use of StringPiece for arguments reduces data
+// copies and strlen() calls.
+//
+// Prefer passing StringPieces by value:
+//   void MyFunction(StringPiece arg);
+// If circumstances require, you may also pass by const reference:
+//   void MyFunction(const StringPiece& arg);  // not preferred
+// Both of these have the same lifetime semantics.  Passing by value
+// generates slightly smaller code.  For more discussion, Googlers can see
+// the thread go/stringpiecebyvalue on c-users.
+
+#ifndef BASE_STRINGS_STRING_PIECE_H_
+#define BASE_STRINGS_STRING_PIECE_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+#include <type_traits>
+
+#include "polyfills/base/base_export.h"
+#include "polyfills/base/logging.h"
+#include "base/strings/char_traits.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece_forward.h"
+
+namespace gurl_base {
+
+// internal --------------------------------------------------------------------
+
+// Many of the StringPiece functions use different implementations for the
+// 8-bit and 16-bit versions, and we don't want lots of template expansions in
+// this (very common) header that will slow down compilation.
+//
+// So here we define overloaded functions called by the StringPiece template.
+// For those that share an implementation, the two versions will expand to a
+// template internal to the .cc file.
+namespace internal {
+
+BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target);
+BASE_EXPORT void CopyToString(const StringPiece16& self, string16* target);
+
+BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
+BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target);
+
+BASE_EXPORT size_t copy(const StringPiece& self,
+                        char* buf,
+                        size_t n,
+                        size_t pos);
+BASE_EXPORT size_t copy(const StringPiece16& self,
+                        char16* buf,
+                        size_t n,
+                        size_t pos);
+
+BASE_EXPORT size_t find(const StringPiece& self,
+                        const StringPiece& s,
+                        size_t pos);
+BASE_EXPORT size_t find(const StringPiece16& self,
+                        const StringPiece16& s,
+                        size_t pos);
+BASE_EXPORT size_t find(const StringPiece& self,
+                        char c,
+                        size_t pos);
+BASE_EXPORT size_t find(const StringPiece16& self,
+                        char16 c,
+                        size_t pos);
+
+BASE_EXPORT size_t rfind(const StringPiece& self,
+                         const StringPiece& s,
+                         size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece16& self,
+                         const StringPiece16& s,
+                         size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece& self,
+                         char c,
+                         size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece16& self,
+                         char16 c,
+                         size_t pos);
+
+BASE_EXPORT size_t find_first_of(const StringPiece& self,
+                                 const StringPiece& s,
+                                 size_t pos);
+BASE_EXPORT size_t find_first_of(const StringPiece16& self,
+                                 const StringPiece16& s,
+                                 size_t pos);
+
+BASE_EXPORT size_t find_first_not_of(const StringPiece& self,
+                                     const StringPiece& s,
+                                     size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+                                     const StringPiece16& s,
+                                     size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece& self,
+                                     char c,
+                                     size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+                                     char16 c,
+                                     size_t pos);
+
+BASE_EXPORT size_t find_last_of(const StringPiece& self,
+                                const StringPiece& s,
+                                size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece16& self,
+                                const StringPiece16& s,
+                                size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece& self,
+                                char c,
+                                size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece16& self,
+                                char16 c,
+                                size_t pos);
+
+BASE_EXPORT size_t find_last_not_of(const StringPiece& self,
+                                    const StringPiece& s,
+                                    size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece16& self,
+                                    const StringPiece16& s,
+                                    size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece16& self,
+                                    char16 c,
+                                    size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece& self,
+                                    char c,
+                                    size_t pos);
+
+BASE_EXPORT StringPiece substr(const StringPiece& self,
+                               size_t pos,
+                               size_t n);
+BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
+                                 size_t pos,
+                                 size_t n);
+
+#if GURL_DCHECK_IS_ON()
+// Asserts that begin <= end to catch some errors with iterator usage.
+BASE_EXPORT void AssertIteratorsInOrder(std::string::const_iterator begin,
+                                        std::string::const_iterator end);
+BASE_EXPORT void AssertIteratorsInOrder(string16::const_iterator begin,
+                                        string16::const_iterator end);
+#endif
+
+}  // namespace internal
+
+// BasicStringPiece ------------------------------------------------------------
+
+// Defines the types, methods, operators, and data members common to both
+// StringPiece and StringPiece16. Do not refer to this class directly, but
+// rather to BasicStringPiece, StringPiece, or StringPiece16.
+//
+// This is templatized by string class type rather than character type, so
+// BasicStringPiece<std::string> or BasicStringPiece<gurl_base::string16>.
+template <typename STRING_TYPE> class BasicStringPiece {
+ public:
+  // Standard STL container boilerplate.
+  typedef size_t size_type;
+  typedef typename STRING_TYPE::value_type value_type;
+  typedef const value_type* pointer;
+  typedef const value_type& reference;
+  typedef const value_type& const_reference;
+  typedef ptrdiff_t difference_type;
+  typedef const value_type* const_iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+  static const size_type npos;
+
+ public:
+  // We provide non-explicit singleton constructors so users can pass
+  // in a "const char*" or a "string" wherever a "StringPiece" is
+  // expected (likewise for char16, string16, StringPiece16).
+  constexpr BasicStringPiece() : ptr_(NULL), length_(0) {}
+  // TODO(dcheng): Construction from nullptr is not allowed for
+  // std::basic_string_view, so remove the special handling for it.
+  // Note: This doesn't just use STRING_TYPE::traits_type::length(), since that
+  // isn't constexpr until C++17.
+  constexpr BasicStringPiece(const value_type* str)
+      : ptr_(str), length_(!str ? 0 : CharTraits<value_type>::length(str)) {}
+  BasicStringPiece(const STRING_TYPE& str)
+      : ptr_(str.data()), length_(str.size()) {}
+  constexpr BasicStringPiece(const value_type* offset, size_type len)
+      : ptr_(offset), length_(len) {}
+  BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
+                   const typename STRING_TYPE::const_iterator& end) {
+#if GURL_DCHECK_IS_ON()
+    // This assertion is done out-of-line to avoid bringing in logging.h and
+    // instantiating logging macros for every instantiation.
+    internal::AssertIteratorsInOrder(begin, end);
+#endif
+    length_ = static_cast<size_t>(std::distance(begin, end));
+
+    // The length test before assignment is to avoid dereferencing an iterator
+    // that may point to the end() of a string.
+    ptr_ = length_ > 0 ? &*begin : nullptr;
+  }
+
+  // data() may return a pointer to a buffer with embedded NULs, and the
+  // returned buffer may or may not be null terminated.  Therefore it is
+  // typically a mistake to pass data() to a routine that expects a NUL
+  // terminated string.
+  constexpr const value_type* data() const { return ptr_; }
+  constexpr size_type size() const noexcept { return length_; }
+  constexpr size_type length() const noexcept { return length_; }
+  bool empty() const { return length_ == 0; }
+
+  void clear() {
+    ptr_ = NULL;
+    length_ = 0;
+  }
+  void set(const value_type* data, size_type len) {
+    ptr_ = data;
+    length_ = len;
+  }
+  void set(const value_type* str) {
+    ptr_ = str;
+    length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
+  }
+
+  constexpr value_type operator[](size_type i) const {
+    GURL_CHECK(i < length_);
+    return ptr_[i];
+  }
+
+  value_type front() const {
+    GURL_CHECK_NE(0UL, length_);
+    return ptr_[0];
+  }
+
+  value_type back() const {
+    GURL_CHECK_NE(0UL, length_);
+    return ptr_[length_ - 1];
+  }
+
+  constexpr void remove_prefix(size_type n) {
+    GURL_CHECK(n <= length_);
+    ptr_ += n;
+    length_ -= n;
+  }
+
+  constexpr void remove_suffix(size_type n) {
+    GURL_CHECK(n <= length_);
+    length_ -= n;
+  }
+
+  constexpr int compare(BasicStringPiece x) const noexcept {
+    int r = CharTraits<value_type>::compare(
+        ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_));
+    if (r == 0) {
+      if (length_ < x.length_) r = -1;
+      else if (length_ > x.length_) r = +1;
+    }
+    return r;
+  }
+
+  // This is the style of conversion preferred by std::string_view in C++17.
+  explicit operator STRING_TYPE() const { return as_string(); }
+
+  STRING_TYPE as_string() const {
+    // std::string doesn't like to take a NULL pointer even with a 0 size.
+    return empty() ? STRING_TYPE() : STRING_TYPE(data(), size());
+  }
+
+  const_iterator begin() const { return ptr_; }
+  const_iterator end() const { return ptr_ + length_; }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(ptr_ + length_);
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(ptr_);
+  }
+
+  size_type max_size() const { return length_; }
+  size_type capacity() const { return length_; }
+
+  // Sets the value of the given string target type to be the current string.
+  // This saves a temporary over doing |a = b.as_string()|
+  void CopyToString(STRING_TYPE* target) const {
+    internal::CopyToString(*this, target);
+  }
+
+  void AppendToString(STRING_TYPE* target) const {
+    internal::AppendToString(*this, target);
+  }
+
+  size_type copy(value_type* buf, size_type n, size_type pos = 0) const {
+    return internal::copy(*this, buf, n, pos);
+  }
+
+  // Does "this" start with "x"
+  constexpr bool starts_with(BasicStringPiece x) const noexcept {
+    return (
+        (this->length_ >= x.length_) &&
+        (CharTraits<value_type>::compare(this->ptr_, x.ptr_, x.length_) == 0));
+  }
+
+  // Does "this" end with "x"
+  constexpr bool ends_with(BasicStringPiece x) const noexcept {
+    return ((this->length_ >= x.length_) &&
+            (CharTraits<value_type>::compare(
+                 this->ptr_ + (this->length_ - x.length_), x.ptr_, x.length_) ==
+             0));
+  }
+
+  // find: Search for a character or substring at a given offset.
+  size_type find(const BasicStringPiece<STRING_TYPE>& s,
+                 size_type pos = 0) const {
+    return internal::find(*this, s, pos);
+  }
+  size_type find(value_type c, size_type pos = 0) const {
+    return internal::find(*this, c, pos);
+  }
+
+  // rfind: Reverse find.
+  size_type rfind(const BasicStringPiece& s,
+                  size_type pos = BasicStringPiece::npos) const {
+    return internal::rfind(*this, s, pos);
+  }
+  size_type rfind(value_type c, size_type pos = BasicStringPiece::npos) const {
+    return internal::rfind(*this, c, pos);
+  }
+
+  // find_first_of: Find the first occurence of one of a set of characters.
+  size_type find_first_of(const BasicStringPiece& s,
+                          size_type pos = 0) const {
+    return internal::find_first_of(*this, s, pos);
+  }
+  size_type find_first_of(value_type c, size_type pos = 0) const {
+    return find(c, pos);
+  }
+
+  // find_first_not_of: Find the first occurence not of a set of characters.
+  size_type find_first_not_of(const BasicStringPiece& s,
+                              size_type pos = 0) const {
+    return internal::find_first_not_of(*this, s, pos);
+  }
+  size_type find_first_not_of(value_type c, size_type pos = 0) const {
+    return internal::find_first_not_of(*this, c, pos);
+  }
+
+  // find_last_of: Find the last occurence of one of a set of characters.
+  size_type find_last_of(const BasicStringPiece& s,
+                         size_type pos = BasicStringPiece::npos) const {
+    return internal::find_last_of(*this, s, pos);
+  }
+  size_type find_last_of(value_type c,
+                         size_type pos = BasicStringPiece::npos) const {
+    return rfind(c, pos);
+  }
+
+  // find_last_not_of: Find the last occurence not of a set of characters.
+  size_type find_last_not_of(const BasicStringPiece& s,
+                             size_type pos = BasicStringPiece::npos) const {
+    return internal::find_last_not_of(*this, s, pos);
+  }
+  size_type find_last_not_of(value_type c,
+                             size_type pos = BasicStringPiece::npos) const {
+    return internal::find_last_not_of(*this, c, pos);
+  }
+
+  // substr.
+  BasicStringPiece substr(size_type pos,
+                          size_type n = BasicStringPiece::npos) const {
+    return internal::substr(*this, pos, n);
+  }
+
+ protected:
+  const value_type* ptr_;
+  size_type length_;
+};
+
+template <typename STRING_TYPE>
+const typename BasicStringPiece<STRING_TYPE>::size_type
+BasicStringPiece<STRING_TYPE>::npos =
+    typename BasicStringPiece<STRING_TYPE>::size_type(-1);
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+extern template class BASE_EXPORT BasicStringPiece<std::string>;
+extern template class BASE_EXPORT BasicStringPiece<string16>;
+#endif
+
+// Comparison operators --------------------------------------------------------
+// operator ==
+template <typename StringT>
+constexpr bool operator==(BasicStringPiece<StringT> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+// Here and below we make use of std::common_type_t to emulate an identity type
+// transformation. This creates a non-deduced context, so that we can compare
+// StringPieces with types that implicitly convert to StringPieces. See
+// https://wg21.link/n3766 for details.
+// Furthermore, we require dummy template parameters for these overloads to work
+// around a name mangling issue on Windows.
+template <typename StringT, int = 1>
+constexpr bool operator==(
+    BasicStringPiece<StringT> lhs,
+    std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+  return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator==(std::common_type_t<BasicStringPiece<StringT>> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+// operator !=
+template <typename StringT>
+constexpr bool operator!=(BasicStringPiece<StringT> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return !(lhs == rhs);
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator!=(
+    BasicStringPiece<StringT> lhs,
+    std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+  return !(lhs == rhs);
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator!=(std::common_type_t<BasicStringPiece<StringT>> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return !(lhs == rhs);
+}
+
+// operator <
+template <typename StringT>
+constexpr bool operator<(BasicStringPiece<StringT> lhs,
+                         BasicStringPiece<StringT> rhs) noexcept {
+  return lhs.compare(rhs) < 0;
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator<(
+    BasicStringPiece<StringT> lhs,
+    std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+  return lhs.compare(rhs) < 0;
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator<(std::common_type_t<BasicStringPiece<StringT>> lhs,
+                         BasicStringPiece<StringT> rhs) noexcept {
+  return lhs.compare(rhs) < 0;
+}
+
+// operator >
+template <typename StringT>
+constexpr bool operator>(BasicStringPiece<StringT> lhs,
+                         BasicStringPiece<StringT> rhs) noexcept {
+  return rhs < lhs;
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator>(
+    BasicStringPiece<StringT> lhs,
+    std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+  return rhs < lhs;
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator>(std::common_type_t<BasicStringPiece<StringT>> lhs,
+                         BasicStringPiece<StringT> rhs) noexcept {
+  return rhs < lhs;
+}
+
+// operator <=
+template <typename StringT>
+constexpr bool operator<=(BasicStringPiece<StringT> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return !(rhs < lhs);
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator<=(
+    BasicStringPiece<StringT> lhs,
+    std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+  return !(rhs < lhs);
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator<=(std::common_type_t<BasicStringPiece<StringT>> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return !(rhs < lhs);
+}
+
+// operator >=
+template <typename StringT>
+constexpr bool operator>=(BasicStringPiece<StringT> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return !(lhs < rhs);
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator>=(
+    BasicStringPiece<StringT> lhs,
+    std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+  return !(lhs < rhs);
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator>=(std::common_type_t<BasicStringPiece<StringT>> lhs,
+                          BasicStringPiece<StringT> rhs) noexcept {
+  return !(lhs < rhs);
+}
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& o,
+                                     const StringPiece& piece);
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& o,
+                                     const StringPiece16& piece);
+
+// Hashing ---------------------------------------------------------------------
+
+// We provide appropriate hash functions so StringPiece and StringPiece16 can
+// be used as keys in hash sets and maps.
+
+// This hash function is copied from base/strings/string16.h. We don't use the
+// ones already defined for string and string16 directly because it would
+// require the string constructors to be called, which we don't want.
+
+template <typename StringPieceType>
+struct StringPieceHashImpl {
+  std::size_t operator()(StringPieceType sp) const {
+    std::size_t result = 0;
+    for (auto c : sp)
+      result = (result * 131) + c;
+    return result;
+  }
+};
+
+using StringPieceHash = StringPieceHashImpl<StringPiece>;
+using StringPiece16Hash = StringPieceHashImpl<StringPiece16>;
+using WStringPieceHash = StringPieceHashImpl<WStringPiece>;
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRING_PIECE_H_
diff --git a/base/strings/string_piece_forward.h b/base/strings/string_piece_forward.h
new file mode 100644
index 0000000..aa79117
--- /dev/null
+++ b/base/strings/string_piece_forward.h
@@ -0,0 +1,24 @@
+// Copyright 2017 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.
+
+// Forward declaration of StringPiece types from base/strings/string_piece.h
+
+#ifndef BASE_STRINGS_STRING_PIECE_FORWARD_H_
+#define BASE_STRINGS_STRING_PIECE_FORWARD_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+namespace gurl_base {
+
+template <typename STRING_TYPE>
+class BasicStringPiece;
+typedef BasicStringPiece<std::string> StringPiece;
+typedef BasicStringPiece<string16> StringPiece16;
+typedef BasicStringPiece<std::wstring> WStringPiece;
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRING_PIECE_FORWARD_H_
diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc
new file mode 100644
index 0000000..8e245e6
--- /dev/null
+++ b/base/strings/string_piece_unittest.cc
@@ -0,0 +1,838 @@
+// Copyright (c) 2012 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 <stddef.h>
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+template <typename T>
+class CommonStringPieceTest : public ::testing::Test {
+ public:
+  static const T as_string(const char* input) {
+    return T(input);
+  }
+  static const T& as_string(const T& input) {
+    return input;
+  }
+};
+
+template <>
+class CommonStringPieceTest<string16> : public ::testing::Test {
+ public:
+  static const string16 as_string(const char* input) {
+    return ASCIIToUTF16(input);
+  }
+  static const string16 as_string(const std::string& input) {
+    return ASCIIToUTF16(input);
+  }
+};
+
+typedef ::testing::Types<std::string, string16> SupportedStringTypes;
+
+TYPED_TEST_SUITE(CommonStringPieceTest, SupportedStringTypes);
+
+TYPED_TEST(CommonStringPieceTest, CheckComparisonOperators) {
+#define CMP_Y(op, x, y)                                                   \
+  {                                                                       \
+    TypeParam lhs(TestFixture::as_string(x));                             \
+    TypeParam rhs(TestFixture::as_string(y));                             \
+    ASSERT_TRUE((BasicStringPiece<TypeParam>((lhs.c_str()))               \
+                     op BasicStringPiece<TypeParam>((rhs.c_str()))));     \
+    ASSERT_TRUE(BasicStringPiece<TypeParam>(lhs) op rhs);                 \
+    ASSERT_TRUE(lhs op BasicStringPiece<TypeParam>(rhs));                 \
+    ASSERT_TRUE((BasicStringPiece<TypeParam>((lhs.c_str()))               \
+                     .compare(BasicStringPiece<TypeParam>((rhs.c_str()))) \
+                         op 0));                                          \
+  }
+
+#define CMP_N(op, x, y)                                                    \
+  {                                                                        \
+    TypeParam lhs(TestFixture::as_string(x));                              \
+    TypeParam rhs(TestFixture::as_string(y));                              \
+    ASSERT_FALSE((BasicStringPiece<TypeParam>((lhs.c_str()))               \
+                      op BasicStringPiece<TypeParam>((rhs.c_str()))));     \
+    ASSERT_FALSE(BasicStringPiece<TypeParam>(lhs) op rhs);                 \
+    ASSERT_FALSE(lhs op BasicStringPiece<TypeParam>(rhs));                 \
+    ASSERT_FALSE((BasicStringPiece<TypeParam>((lhs.c_str()))               \
+                      .compare(BasicStringPiece<TypeParam>((rhs.c_str()))) \
+                          op 0));                                          \
+  }
+
+  CMP_Y(==, "", "")
+  CMP_Y(==, "a", "a")
+  CMP_Y(==, "aa", "aa")
+  CMP_N(==, "a", "")
+  CMP_N(==, "", "a")
+  CMP_N(==, "a", "b")
+  CMP_N(==, "a", "aa")
+  CMP_N(==, "aa", "a")
+
+  CMP_N(!=, "", "")
+  CMP_N(!=, "a", "a")
+  CMP_N(!=, "aa", "aa")
+  CMP_Y(!=, "a", "")
+  CMP_Y(!=, "", "a")
+  CMP_Y(!=, "a", "b")
+  CMP_Y(!=, "a", "aa")
+  CMP_Y(!=, "aa", "a")
+
+  CMP_Y(<, "a", "b")
+  CMP_Y(<, "a", "aa")
+  CMP_Y(<, "aa", "b")
+  CMP_Y(<, "aa", "bb")
+  CMP_N(<, "a", "a")
+  CMP_N(<, "b", "a")
+  CMP_N(<, "aa", "a")
+  CMP_N(<, "b", "aa")
+  CMP_N(<, "bb", "aa")
+
+  CMP_Y(<=, "a", "a")
+  CMP_Y(<=, "a", "b")
+  CMP_Y(<=, "a", "aa")
+  CMP_Y(<=, "aa", "b")
+  CMP_Y(<=, "aa", "bb")
+  CMP_N(<=, "b", "a")
+  CMP_N(<=, "aa", "a")
+  CMP_N(<=, "b", "aa")
+  CMP_N(<=, "bb", "aa")
+
+  CMP_N(>=, "a", "b")
+  CMP_N(>=, "a", "aa")
+  CMP_N(>=, "aa", "b")
+  CMP_N(>=, "aa", "bb")
+  CMP_Y(>=, "a", "a")
+  CMP_Y(>=, "b", "a")
+  CMP_Y(>=, "aa", "a")
+  CMP_Y(>=, "b", "aa")
+  CMP_Y(>=, "bb", "aa")
+
+  CMP_N(>, "a", "a")
+  CMP_N(>, "a", "b")
+  CMP_N(>, "a", "aa")
+  CMP_N(>, "aa", "b")
+  CMP_N(>, "aa", "bb")
+  CMP_Y(>, "b", "a")
+  CMP_Y(>, "aa", "a")
+  CMP_Y(>, "b", "aa")
+  CMP_Y(>, "bb", "aa")
+
+  std::string x;
+  for (int i = 0; i < 256; i++) {
+    x += 'a';
+    std::string y = x;
+    CMP_Y(==, x, y);
+    for (int j = 0; j < i; j++) {
+      std::string z = x;
+      z[j] = 'b';       // Differs in position 'j'
+      CMP_N(==, x, z);
+    }
+  }
+
+#undef CMP_Y
+#undef CMP_N
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckSTL) {
+  TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz"));
+  TypeParam abc(TestFixture::as_string("abc"));
+  TypeParam xyz(TestFixture::as_string("xyz"));
+  TypeParam foobar(TestFixture::as_string("foobar"));
+
+  BasicStringPiece<TypeParam> a(alphabet);
+  BasicStringPiece<TypeParam> b(abc);
+  BasicStringPiece<TypeParam> c(xyz);
+  BasicStringPiece<TypeParam> d(foobar);
+  BasicStringPiece<TypeParam> e;
+  TypeParam temp(TestFixture::as_string("123"));
+  temp += static_cast<typename TypeParam::value_type>(0);
+  temp += TestFixture::as_string("456");
+  BasicStringPiece<TypeParam> f(temp);
+
+  ASSERT_EQ(a[6], static_cast<typename TypeParam::value_type>('g'));
+  ASSERT_EQ(b[0], static_cast<typename TypeParam::value_type>('a'));
+  ASSERT_EQ(c[2], static_cast<typename TypeParam::value_type>('z'));
+  ASSERT_EQ(f[3], static_cast<typename TypeParam::value_type>('\0'));
+  ASSERT_EQ(f[5], static_cast<typename TypeParam::value_type>('5'));
+
+  ASSERT_EQ(*d.data(), static_cast<typename TypeParam::value_type>('f'));
+  ASSERT_EQ(d.data()[5], static_cast<typename TypeParam::value_type>('r'));
+  ASSERT_EQ(e.data(), nullptr);
+
+  ASSERT_EQ(*a.begin(), static_cast<typename TypeParam::value_type>('a'));
+  ASSERT_EQ(*(b.begin() + 2), static_cast<typename TypeParam::value_type>('c'));
+  ASSERT_EQ(*(c.end() - 1), static_cast<typename TypeParam::value_type>('z'));
+
+  ASSERT_EQ(*a.rbegin(), static_cast<typename TypeParam::value_type>('z'));
+  ASSERT_EQ(*(b.rbegin() + 2),
+            static_cast<typename TypeParam::value_type>('a'));
+  ASSERT_EQ(*(c.rend() - 1), static_cast<typename TypeParam::value_type>('x'));
+  ASSERT_EQ(a.rbegin() + 26, a.rend());
+
+  ASSERT_EQ(a.size(), 26U);
+  ASSERT_EQ(b.size(), 3U);
+  ASSERT_EQ(c.size(), 3U);
+  ASSERT_EQ(d.size(), 6U);
+  ASSERT_EQ(e.size(), 0U);
+  ASSERT_EQ(f.size(), 7U);
+
+  ASSERT_TRUE(!d.empty());
+  ASSERT_TRUE(d.begin() != d.end());
+  ASSERT_EQ(d.begin() + 6, d.end());
+
+  ASSERT_TRUE(e.empty());
+  ASSERT_EQ(e.begin(), e.end());
+
+  d.clear();
+  ASSERT_EQ(d.size(), 0U);
+  ASSERT_TRUE(d.empty());
+  ASSERT_EQ(d.data(), nullptr);
+  ASSERT_EQ(d.begin(), d.end());
+
+  ASSERT_GE(a.max_size(), a.capacity());
+  ASSERT_GE(a.capacity(), a.size());
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckFind) {
+  typedef BasicStringPiece<TypeParam> Piece;
+
+  TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz"));
+  TypeParam abc(TestFixture::as_string("abc"));
+  TypeParam xyz(TestFixture::as_string("xyz"));
+  TypeParam foobar(TestFixture::as_string("foobar"));
+
+  BasicStringPiece<TypeParam> a(alphabet);
+  BasicStringPiece<TypeParam> b(abc);
+  BasicStringPiece<TypeParam> c(xyz);
+  BasicStringPiece<TypeParam> d(foobar);
+
+  d.clear();
+  Piece e;
+  TypeParam temp(TestFixture::as_string("123"));
+  temp.push_back('\0');
+  temp += TestFixture::as_string("456");
+  Piece f(temp);
+
+  typename TypeParam::value_type buf[4] = { '%', '%', '%', '%' };
+  ASSERT_EQ(a.copy(buf, 4), 4U);
+  ASSERT_EQ(buf[0], a[0]);
+  ASSERT_EQ(buf[1], a[1]);
+  ASSERT_EQ(buf[2], a[2]);
+  ASSERT_EQ(buf[3], a[3]);
+  ASSERT_EQ(a.copy(buf, 3, 7), 3U);
+  ASSERT_EQ(buf[0], a[7]);
+  ASSERT_EQ(buf[1], a[8]);
+  ASSERT_EQ(buf[2], a[9]);
+  ASSERT_EQ(buf[3], a[3]);
+  ASSERT_EQ(c.copy(buf, 99), 3U);
+  ASSERT_EQ(buf[0], c[0]);
+  ASSERT_EQ(buf[1], c[1]);
+  ASSERT_EQ(buf[2], c[2]);
+  ASSERT_EQ(buf[3], a[3]);
+
+  ASSERT_EQ(Piece::npos, TypeParam::npos);
+
+  ASSERT_EQ(a.find(b), 0U);
+  ASSERT_EQ(a.find(b, 1), Piece::npos);
+  ASSERT_EQ(a.find(c), 23U);
+  ASSERT_EQ(a.find(c, 9), 23U);
+  ASSERT_EQ(a.find(c, Piece::npos), Piece::npos);
+  ASSERT_EQ(b.find(c), Piece::npos);
+  ASSERT_EQ(b.find(c, Piece::npos), Piece::npos);
+  ASSERT_EQ(a.find(d), 0U);
+  ASSERT_EQ(a.find(e), 0U);
+  ASSERT_EQ(a.find(d, 12), 12U);
+  ASSERT_EQ(a.find(e, 17), 17U);
+  TypeParam not_found(TestFixture::as_string("xx not found bb"));
+  Piece g(not_found);
+  ASSERT_EQ(a.find(g), Piece::npos);
+  // empty string nonsense
+  ASSERT_EQ(d.find(b), Piece::npos);
+  ASSERT_EQ(e.find(b), Piece::npos);
+  ASSERT_EQ(d.find(b, 4), Piece::npos);
+  ASSERT_EQ(e.find(b, 7), Piece::npos);
+
+  size_t empty_search_pos = TypeParam().find(TypeParam());
+  ASSERT_EQ(d.find(d), empty_search_pos);
+  ASSERT_EQ(d.find(e), empty_search_pos);
+  ASSERT_EQ(e.find(d), empty_search_pos);
+  ASSERT_EQ(e.find(e), empty_search_pos);
+  ASSERT_EQ(d.find(d, 4), std::string().find(std::string(), 4));
+  ASSERT_EQ(d.find(e, 4), std::string().find(std::string(), 4));
+  ASSERT_EQ(e.find(d, 4), std::string().find(std::string(), 4));
+  ASSERT_EQ(e.find(e, 4), std::string().find(std::string(), 4));
+
+  ASSERT_EQ(a.find('a'), 0U);
+  ASSERT_EQ(a.find('c'), 2U);
+  ASSERT_EQ(a.find('z'), 25U);
+  ASSERT_EQ(a.find('$'), Piece::npos);
+  ASSERT_EQ(a.find('\0'), Piece::npos);
+  ASSERT_EQ(f.find('\0'), 3U);
+  ASSERT_EQ(f.find('3'), 2U);
+  ASSERT_EQ(f.find('5'), 5U);
+  ASSERT_EQ(g.find('o'), 4U);
+  ASSERT_EQ(g.find('o', 4), 4U);
+  ASSERT_EQ(g.find('o', 5), 8U);
+  ASSERT_EQ(a.find('b', 5), Piece::npos);
+  // empty string nonsense
+  ASSERT_EQ(d.find('\0'), Piece::npos);
+  ASSERT_EQ(e.find('\0'), Piece::npos);
+  ASSERT_EQ(d.find('\0', 4), Piece::npos);
+  ASSERT_EQ(e.find('\0', 7), Piece::npos);
+  ASSERT_EQ(d.find('x'), Piece::npos);
+  ASSERT_EQ(e.find('x'), Piece::npos);
+  ASSERT_EQ(d.find('x', 4), Piece::npos);
+  ASSERT_EQ(e.find('x', 7), Piece::npos);
+
+  ASSERT_EQ(a.rfind(b), 0U);
+  ASSERT_EQ(a.rfind(b, 1), 0U);
+  ASSERT_EQ(a.rfind(c), 23U);
+  ASSERT_EQ(a.rfind(c, 22U), Piece::npos);
+  ASSERT_EQ(a.rfind(c, 1U), Piece::npos);
+  ASSERT_EQ(a.rfind(c, 0U), Piece::npos);
+  ASSERT_EQ(b.rfind(c), Piece::npos);
+  ASSERT_EQ(b.rfind(c, 0U), Piece::npos);
+  ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.as_string().rfind(TypeParam())));
+  ASSERT_EQ(a.rfind(e), a.as_string().rfind(TypeParam()));
+  ASSERT_EQ(a.rfind(d), static_cast<size_t>(TypeParam(a).rfind(TypeParam())));
+  ASSERT_EQ(a.rfind(e), TypeParam(a).rfind(TypeParam()));
+  ASSERT_EQ(a.rfind(d, 12), 12U);
+  ASSERT_EQ(a.rfind(e, 17), 17U);
+  ASSERT_EQ(a.rfind(g), Piece::npos);
+  ASSERT_EQ(d.rfind(b), Piece::npos);
+  ASSERT_EQ(e.rfind(b), Piece::npos);
+  ASSERT_EQ(d.rfind(b, 4), Piece::npos);
+  ASSERT_EQ(e.rfind(b, 7), Piece::npos);
+  // empty string nonsense
+  ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
+  ASSERT_EQ(d.rfind(e, 4), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(e, 7), std::string().rfind(std::string()));
+  ASSERT_EQ(d.rfind(d), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(d), std::string().rfind(std::string()));
+  ASSERT_EQ(d.rfind(e), std::string().rfind(std::string()));
+  ASSERT_EQ(e.rfind(e), std::string().rfind(std::string()));
+
+  ASSERT_EQ(g.rfind('o'), 8U);
+  ASSERT_EQ(g.rfind('q'), Piece::npos);
+  ASSERT_EQ(g.rfind('o', 8), 8U);
+  ASSERT_EQ(g.rfind('o', 7), 4U);
+  ASSERT_EQ(g.rfind('o', 3), Piece::npos);
+  ASSERT_EQ(f.rfind('\0'), 3U);
+  ASSERT_EQ(f.rfind('\0', 12), 3U);
+  ASSERT_EQ(f.rfind('3'), 2U);
+  ASSERT_EQ(f.rfind('5'), 5U);
+  // empty string nonsense
+  ASSERT_EQ(d.rfind('o'), Piece::npos);
+  ASSERT_EQ(e.rfind('o'), Piece::npos);
+  ASSERT_EQ(d.rfind('o', 4), Piece::npos);
+  ASSERT_EQ(e.rfind('o', 7), Piece::npos);
+
+  TypeParam one_two_three_four(TestFixture::as_string("one,two:three;four"));
+  TypeParam comma_colon(TestFixture::as_string(",:"));
+  ASSERT_EQ(3U, Piece(one_two_three_four).find_first_of(comma_colon));
+  ASSERT_EQ(a.find_first_of(b), 0U);
+  ASSERT_EQ(a.find_first_of(b, 0), 0U);
+  ASSERT_EQ(a.find_first_of(b, 1), 1U);
+  ASSERT_EQ(a.find_first_of(b, 2), 2U);
+  ASSERT_EQ(a.find_first_of(b, 3), Piece::npos);
+  ASSERT_EQ(a.find_first_of(c), 23U);
+  ASSERT_EQ(a.find_first_of(c, 23), 23U);
+  ASSERT_EQ(a.find_first_of(c, 24), 24U);
+  ASSERT_EQ(a.find_first_of(c, 25), 25U);
+  ASSERT_EQ(a.find_first_of(c, 26), Piece::npos);
+  ASSERT_EQ(g.find_first_of(b), 13U);
+  ASSERT_EQ(g.find_first_of(c), 0U);
+  ASSERT_EQ(a.find_first_of(f), Piece::npos);
+  ASSERT_EQ(f.find_first_of(a), Piece::npos);
+  // empty string nonsense
+  ASSERT_EQ(a.find_first_of(d), Piece::npos);
+  ASSERT_EQ(a.find_first_of(e), Piece::npos);
+  ASSERT_EQ(d.find_first_of(b), Piece::npos);
+  ASSERT_EQ(e.find_first_of(b), Piece::npos);
+  ASSERT_EQ(d.find_first_of(d), Piece::npos);
+  ASSERT_EQ(e.find_first_of(d), Piece::npos);
+  ASSERT_EQ(d.find_first_of(e), Piece::npos);
+  ASSERT_EQ(e.find_first_of(e), Piece::npos);
+
+  ASSERT_EQ(a.find_first_not_of(b), 3U);
+  ASSERT_EQ(a.find_first_not_of(c), 0U);
+  ASSERT_EQ(b.find_first_not_of(a), Piece::npos);
+  ASSERT_EQ(c.find_first_not_of(a), Piece::npos);
+  ASSERT_EQ(f.find_first_not_of(a), 0U);
+  ASSERT_EQ(a.find_first_not_of(f), 0U);
+  ASSERT_EQ(a.find_first_not_of(d), 0U);
+  ASSERT_EQ(a.find_first_not_of(e), 0U);
+  // empty string nonsense
+  ASSERT_EQ(d.find_first_not_of(a), Piece::npos);
+  ASSERT_EQ(e.find_first_not_of(a), Piece::npos);
+  ASSERT_EQ(d.find_first_not_of(d), Piece::npos);
+  ASSERT_EQ(e.find_first_not_of(d), Piece::npos);
+  ASSERT_EQ(d.find_first_not_of(e), Piece::npos);
+  ASSERT_EQ(e.find_first_not_of(e), Piece::npos);
+
+  TypeParam equals(TestFixture::as_string("===="));
+  Piece h(equals);
+  ASSERT_EQ(h.find_first_not_of('='), Piece::npos);
+  ASSERT_EQ(h.find_first_not_of('=', 3), Piece::npos);
+  ASSERT_EQ(h.find_first_not_of('\0'), 0U);
+  ASSERT_EQ(g.find_first_not_of('x'), 2U);
+  ASSERT_EQ(f.find_first_not_of('\0'), 0U);
+  ASSERT_EQ(f.find_first_not_of('\0', 3), 4U);
+  ASSERT_EQ(f.find_first_not_of('\0', 2), 2U);
+  // empty string nonsense
+  ASSERT_EQ(d.find_first_not_of('x'), Piece::npos);
+  ASSERT_EQ(e.find_first_not_of('x'), Piece::npos);
+  ASSERT_EQ(d.find_first_not_of('\0'), Piece::npos);
+  ASSERT_EQ(e.find_first_not_of('\0'), Piece::npos);
+
+  //  Piece g("xx not found bb");
+  TypeParam fifty_six(TestFixture::as_string("56"));
+  Piece i(fifty_six);
+  ASSERT_EQ(h.find_last_of(a), Piece::npos);
+  ASSERT_EQ(g.find_last_of(a), g.size()-1);
+  ASSERT_EQ(a.find_last_of(b), 2U);
+  ASSERT_EQ(a.find_last_of(c), a.size()-1);
+  ASSERT_EQ(f.find_last_of(i), 6U);
+  ASSERT_EQ(a.find_last_of('a'), 0U);
+  ASSERT_EQ(a.find_last_of('b'), 1U);
+  ASSERT_EQ(a.find_last_of('z'), 25U);
+  ASSERT_EQ(a.find_last_of('a', 5), 0U);
+  ASSERT_EQ(a.find_last_of('b', 5), 1U);
+  ASSERT_EQ(a.find_last_of('b', 0), Piece::npos);
+  ASSERT_EQ(a.find_last_of('z', 25), 25U);
+  ASSERT_EQ(a.find_last_of('z', 24), Piece::npos);
+  ASSERT_EQ(f.find_last_of(i, 5), 5U);
+  ASSERT_EQ(f.find_last_of(i, 6), 6U);
+  ASSERT_EQ(f.find_last_of(a, 4), Piece::npos);
+  // empty string nonsense
+  ASSERT_EQ(f.find_last_of(d), Piece::npos);
+  ASSERT_EQ(f.find_last_of(e), Piece::npos);
+  ASSERT_EQ(f.find_last_of(d, 4), Piece::npos);
+  ASSERT_EQ(f.find_last_of(e, 4), Piece::npos);
+  ASSERT_EQ(d.find_last_of(d), Piece::npos);
+  ASSERT_EQ(d.find_last_of(e), Piece::npos);
+  ASSERT_EQ(e.find_last_of(d), Piece::npos);
+  ASSERT_EQ(e.find_last_of(e), Piece::npos);
+  ASSERT_EQ(d.find_last_of(f), Piece::npos);
+  ASSERT_EQ(e.find_last_of(f), Piece::npos);
+  ASSERT_EQ(d.find_last_of(d, 4), Piece::npos);
+  ASSERT_EQ(d.find_last_of(e, 4), Piece::npos);
+  ASSERT_EQ(e.find_last_of(d, 4), Piece::npos);
+  ASSERT_EQ(e.find_last_of(e, 4), Piece::npos);
+  ASSERT_EQ(d.find_last_of(f, 4), Piece::npos);
+  ASSERT_EQ(e.find_last_of(f, 4), Piece::npos);
+
+  ASSERT_EQ(a.find_last_not_of(b), a.size()-1);
+  ASSERT_EQ(a.find_last_not_of(c), 22U);
+  ASSERT_EQ(b.find_last_not_of(a), Piece::npos);
+  ASSERT_EQ(b.find_last_not_of(b), Piece::npos);
+  ASSERT_EQ(f.find_last_not_of(i), 4U);
+  ASSERT_EQ(a.find_last_not_of(c, 24), 22U);
+  ASSERT_EQ(a.find_last_not_of(b, 3), 3U);
+  ASSERT_EQ(a.find_last_not_of(b, 2), Piece::npos);
+  // empty string nonsense
+  ASSERT_EQ(f.find_last_not_of(d), f.size()-1);
+  ASSERT_EQ(f.find_last_not_of(e), f.size()-1);
+  ASSERT_EQ(f.find_last_not_of(d, 4), 4U);
+  ASSERT_EQ(f.find_last_not_of(e, 4), 4U);
+  ASSERT_EQ(d.find_last_not_of(d), Piece::npos);
+  ASSERT_EQ(d.find_last_not_of(e), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of(d), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of(e), Piece::npos);
+  ASSERT_EQ(d.find_last_not_of(f), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of(f), Piece::npos);
+  ASSERT_EQ(d.find_last_not_of(d, 4), Piece::npos);
+  ASSERT_EQ(d.find_last_not_of(e, 4), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of(d, 4), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of(e, 4), Piece::npos);
+  ASSERT_EQ(d.find_last_not_of(f, 4), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of(f, 4), Piece::npos);
+
+  ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1);
+  ASSERT_EQ(h.find_last_not_of('='), Piece::npos);
+  ASSERT_EQ(b.find_last_not_of('c'), 1U);
+  ASSERT_EQ(h.find_last_not_of('x', 2), 2U);
+  ASSERT_EQ(h.find_last_not_of('=', 2), Piece::npos);
+  ASSERT_EQ(b.find_last_not_of('b', 1), 0U);
+  // empty string nonsense
+  ASSERT_EQ(d.find_last_not_of('x'), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of('x'), Piece::npos);
+  ASSERT_EQ(d.find_last_not_of('\0'), Piece::npos);
+  ASSERT_EQ(e.find_last_not_of('\0'), Piece::npos);
+
+  ASSERT_EQ(a.substr(0, 3), b);
+  ASSERT_EQ(a.substr(23), c);
+  ASSERT_EQ(a.substr(23, 3), c);
+  ASSERT_EQ(a.substr(23, 99), c);
+  ASSERT_EQ(a.substr(0), a);
+  ASSERT_EQ(a.substr(3, 2), TestFixture::as_string("de"));
+  // empty string nonsense
+  ASSERT_EQ(a.substr(99, 2), e);
+  ASSERT_EQ(d.substr(99), e);
+  ASSERT_EQ(d.substr(0, 99), e);
+  ASSERT_EQ(d.substr(99, 99), e);
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckCustom) {
+  TypeParam foobar(TestFixture::as_string("foobar"));
+  BasicStringPiece<TypeParam> a(foobar);
+  TypeParam s1(TestFixture::as_string("123"));
+  s1 += static_cast<typename TypeParam::value_type>('\0');
+  s1 += TestFixture::as_string("456");
+  BasicStringPiece<TypeParam> b(s1);
+  BasicStringPiece<TypeParam> e;
+  TypeParam s2;
+
+  // remove_prefix
+  BasicStringPiece<TypeParam> c(a);
+  c.remove_prefix(3);
+  ASSERT_EQ(c, TestFixture::as_string("bar"));
+  c = a;
+  c.remove_prefix(0);
+  ASSERT_EQ(c, a);
+  c.remove_prefix(c.size());
+  ASSERT_EQ(c, e);
+
+  // remove_suffix
+  c = a;
+  c.remove_suffix(3);
+  ASSERT_EQ(c, TestFixture::as_string("foo"));
+  c = a;
+  c.remove_suffix(0);
+  ASSERT_EQ(c, a);
+  c.remove_suffix(c.size());
+  ASSERT_EQ(c, e);
+
+  // set
+  c.set(foobar.c_str());
+  ASSERT_EQ(c, a);
+  c.set(foobar.c_str(), 6);
+  ASSERT_EQ(c, a);
+  c.set(foobar.c_str(), 0);
+  ASSERT_EQ(c, e);
+  c.set(foobar.c_str(), 7);  // Note, has an embedded NULL
+  ASSERT_NE(c, a);
+
+  // as_string
+  TypeParam s3(a.as_string().c_str(), 7);  // Note, has an embedded NULL
+  ASSERT_EQ(c, s3);
+  TypeParam s4(e.as_string());
+  ASSERT_TRUE(s4.empty());
+
+  // operator STRING_TYPE()
+  TypeParam s5(TypeParam(a).c_str(), 7);  // Note, has an embedded NULL
+  ASSERT_EQ(c, s5);
+  TypeParam s6(e);
+  ASSERT_TRUE(s6.empty());
+}
+
+TEST(StringPieceTest, CheckCustom) {
+  StringPiece a("foobar");
+  std::string s1("123");
+  s1 += '\0';
+  s1 += "456";
+  StringPiece b(s1);
+  StringPiece e;
+  std::string s2;
+
+  // CopyToString
+  a.CopyToString(&s2);
+  ASSERT_EQ(s2.size(), 6U);
+  ASSERT_EQ(s2, "foobar");
+  b.CopyToString(&s2);
+  ASSERT_EQ(s2.size(), 7U);
+  ASSERT_EQ(s1, s2);
+  e.CopyToString(&s2);
+  ASSERT_TRUE(s2.empty());
+
+  // AppendToString
+  s2.erase();
+  a.AppendToString(&s2);
+  ASSERT_EQ(s2.size(), 6U);
+  ASSERT_EQ(s2, "foobar");
+  a.AppendToString(&s2);
+  ASSERT_EQ(s2.size(), 12U);
+  ASSERT_EQ(s2, "foobarfoobar");
+
+  // starts_with
+  ASSERT_TRUE(a.starts_with(a));
+  ASSERT_TRUE(a.starts_with("foo"));
+  ASSERT_TRUE(a.starts_with(e));
+  ASSERT_TRUE(b.starts_with(s1));
+  ASSERT_TRUE(b.starts_with(b));
+  ASSERT_TRUE(b.starts_with(e));
+  ASSERT_TRUE(e.starts_with(""));
+  ASSERT_TRUE(!a.starts_with(b));
+  ASSERT_TRUE(!b.starts_with(a));
+  ASSERT_TRUE(!e.starts_with(a));
+
+  // ends with
+  ASSERT_TRUE(a.ends_with(a));
+  ASSERT_TRUE(a.ends_with("bar"));
+  ASSERT_TRUE(a.ends_with(e));
+  ASSERT_TRUE(b.ends_with(s1));
+  ASSERT_TRUE(b.ends_with(b));
+  ASSERT_TRUE(b.ends_with(e));
+  ASSERT_TRUE(e.ends_with(""));
+  ASSERT_TRUE(!a.ends_with(b));
+  ASSERT_TRUE(!b.ends_with(a));
+  ASSERT_TRUE(!e.ends_with(a));
+
+  StringPiece c;
+  c.set("foobar", 6);
+  ASSERT_EQ(c, a);
+  c.set("foobar", 0);
+  ASSERT_EQ(c, e);
+  c.set("foobar", 7);
+  ASSERT_NE(c, a);
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckNULL) {
+  // we used to crash here, but now we don't.
+  BasicStringPiece<TypeParam> s(nullptr);
+  ASSERT_EQ(s.data(), nullptr);
+  ASSERT_EQ(s.size(), 0U);
+
+  s.set(nullptr);
+  ASSERT_EQ(s.data(), nullptr);
+  ASSERT_EQ(s.size(), 0U);
+
+  TypeParam str(s);
+  ASSERT_EQ(str.length(), 0U);
+  ASSERT_EQ(str, TypeParam());
+
+  str = s.as_string();
+  ASSERT_EQ(str.length(), 0U);
+  ASSERT_EQ(str, TypeParam());
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckComparisons2) {
+  TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz"));
+  TypeParam alphabet_z(TestFixture::as_string("abcdefghijklmnopqrstuvwxyzz"));
+  TypeParam alphabet_y(TestFixture::as_string("abcdefghijklmnopqrstuvwxyy"));
+  BasicStringPiece<TypeParam> abc(alphabet);
+
+  // check comparison operations on strings longer than 4 bytes.
+  ASSERT_EQ(abc, BasicStringPiece<TypeParam>(alphabet));
+  ASSERT_EQ(abc.compare(BasicStringPiece<TypeParam>(alphabet)), 0);
+
+  ASSERT_TRUE(abc < BasicStringPiece<TypeParam>(alphabet_z));
+  ASSERT_LT(abc.compare(BasicStringPiece<TypeParam>(alphabet_z)), 0);
+
+  ASSERT_TRUE(abc > BasicStringPiece<TypeParam>(alphabet_y));
+  ASSERT_GT(abc.compare(BasicStringPiece<TypeParam>(alphabet_y)), 0);
+}
+
+// Test operations only supported by std::string version.
+TEST(StringPieceTest, CheckComparisons2) {
+  StringPiece abc("abcdefghijklmnopqrstuvwxyz");
+
+  // starts_with
+  ASSERT_TRUE(abc.starts_with(abc));
+  ASSERT_TRUE(abc.starts_with("abcdefghijklm"));
+  ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz"));
+
+  // ends_with
+  ASSERT_TRUE(abc.ends_with(abc));
+  ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz"));
+  ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz"));
+}
+
+TYPED_TEST(CommonStringPieceTest, StringCompareNotAmbiguous) {
+  ASSERT_TRUE(TestFixture::as_string("hello").c_str() ==
+              TestFixture::as_string("hello"));
+  ASSERT_TRUE(TestFixture::as_string("hello").c_str() <
+              TestFixture::as_string("world"));
+}
+
+TYPED_TEST(CommonStringPieceTest, HeterogenousStringPieceEquals) {
+  TypeParam hello(TestFixture::as_string("hello"));
+
+  ASSERT_EQ(BasicStringPiece<TypeParam>(hello), hello);
+  ASSERT_EQ(hello.c_str(), BasicStringPiece<TypeParam>(hello));
+}
+
+// string16-specific stuff
+TEST(StringPiece16Test, CheckSTL) {
+  // Check some non-ascii characters.
+  string16 fifth(ASCIIToUTF16("123"));
+  fifth.push_back(0x0000);
+  fifth.push_back(0xd8c5);
+  fifth.push_back(0xdffe);
+  StringPiece16 f(fifth);
+
+  ASSERT_EQ(f[3], '\0');
+  ASSERT_EQ(f[5], static_cast<char16>(0xdffe));
+
+  ASSERT_EQ(f.size(), 6U);
+}
+
+
+
+TEST(StringPiece16Test, CheckConversion) {
+  // Make sure that we can convert from UTF8 to UTF16 and back. We use a two
+  // byte character (G clef) to test this.
+  ASSERT_EQ(
+      UTF16ToUTF8(
+          StringPiece16(UTF8ToUTF16("\xf0\x9d\x84\x9e")).as_string()),
+      "\xf0\x9d\x84\x9e");
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckConstructors) {
+  TypeParam str(TestFixture::as_string("hello world"));
+  TypeParam empty;
+
+  ASSERT_EQ(str, BasicStringPiece<TypeParam>(str));
+  ASSERT_EQ(str, BasicStringPiece<TypeParam>(str.c_str()));
+  ASSERT_TRUE(TestFixture::as_string("hello") ==
+              BasicStringPiece<TypeParam>(str.c_str(), 5));
+  ASSERT_EQ(
+      empty,
+      BasicStringPiece<TypeParam>(
+          str.c_str(),
+          static_cast<typename BasicStringPiece<TypeParam>::size_type>(0)));
+  ASSERT_EQ(empty, BasicStringPiece<TypeParam>(nullptr));
+  ASSERT_TRUE(
+      empty ==
+      BasicStringPiece<TypeParam>(
+          nullptr,
+          static_cast<typename BasicStringPiece<TypeParam>::size_type>(0)));
+  ASSERT_EQ(empty, BasicStringPiece<TypeParam>());
+  ASSERT_EQ(str, BasicStringPiece<TypeParam>(str.begin(), str.end()));
+  ASSERT_EQ(empty, BasicStringPiece<TypeParam>(str.begin(), str.begin()));
+  ASSERT_EQ(empty, BasicStringPiece<TypeParam>(empty));
+  ASSERT_EQ(empty, BasicStringPiece<TypeParam>(empty.begin(), empty.end()));
+}
+
+TEST(StringPieceTest, ConstexprCtor) {
+  {
+    constexpr StringPiece piece;
+    std::ignore = piece;
+  }
+
+  {
+    constexpr StringPiece piece("abc");
+    std::ignore = piece;
+  }
+
+  {
+    constexpr StringPiece piece("abc", 2);
+    std::ignore = piece;
+  }
+}
+
+TEST(StringPieceTest, OutOfBoundsDeath) {
+  {
+    constexpr StringPiece piece;
+    ASSERT_DEATH_IF_SUPPORTED(piece[0], "");
+  }
+
+  {
+    constexpr StringPiece piece;
+    ASSERT_DEATH_IF_SUPPORTED(piece.front(), "");
+  }
+
+  {
+    constexpr StringPiece piece;
+    ASSERT_DEATH_IF_SUPPORTED(piece.back(), "");
+  }
+
+  {
+    StringPiece piece;
+    ASSERT_DEATH_IF_SUPPORTED(piece.remove_suffix(1), "");
+  }
+
+  {
+    StringPiece piece;
+    ASSERT_DEATH_IF_SUPPORTED(piece.remove_prefix(1), "");
+  }
+}
+
+TEST(StringPieceTest, ConstexprData) {
+  {
+    constexpr StringPiece piece;
+    static_assert(piece.data() == nullptr, "");
+  }
+
+  {
+    constexpr StringPiece piece("abc");
+    static_assert(piece.data()[0] == 'a', "");
+    static_assert(piece.data()[1] == 'b', "");
+    static_assert(piece.data()[2] == 'c', "");
+  }
+
+  {
+    constexpr StringPiece piece("def", 2);
+    static_assert(piece.data()[0] == 'd', "");
+    static_assert(piece.data()[1] == 'e', "");
+  }
+}
+
+TEST(StringPieceTest, ConstexprSize) {
+  {
+    constexpr StringPiece piece;
+    static_assert(piece.size() == 0, "");
+  }
+
+  {
+    constexpr StringPiece piece("abc");
+    static_assert(piece.size() == 3, "");
+  }
+
+  {
+    constexpr StringPiece piece("def", 2);
+    static_assert(piece.size() == 2, "");
+  }
+}
+
+TEST(StringPieceTest, Compare) {
+  constexpr StringPiece piece = "def";
+
+  static_assert(piece.compare("ab") == 1, "");
+  static_assert(piece.compare("abc") == 1, "");
+  static_assert(piece.compare("abcd") == 1, "");
+  static_assert(piece.compare("de") == 1, "");
+  static_assert(piece.compare("def") == 0, "");
+  static_assert(piece.compare("defg") == -1, "");
+  static_assert(piece.compare("gh") == -1, "");
+  static_assert(piece.compare("ghi") == -1, "");
+  static_assert(piece.compare("ghij") == -1, "");
+}
+
+TEST(StringPieceTest, StartsWith) {
+  constexpr StringPiece piece("abc");
+
+  static_assert(piece.starts_with(""), "");
+  static_assert(piece.starts_with("a"), "");
+  static_assert(piece.starts_with("ab"), "");
+  static_assert(piece.starts_with("abc"), "");
+
+  static_assert(!piece.starts_with("b"), "");
+  static_assert(!piece.starts_with("bc"), "");
+
+  static_assert(!piece.starts_with("abcd"), "");
+}
+
+TEST(StringPieceTest, EndsWith) {
+  constexpr StringPiece piece("abc");
+
+  static_assert(piece.ends_with(""), "");
+  static_assert(piece.ends_with("c"), "");
+  static_assert(piece.ends_with("bc"), "");
+  static_assert(piece.ends_with("abc"), "");
+
+  static_assert(!piece.ends_with("a"), "");
+  static_assert(!piece.ends_with("ab"), "");
+
+  static_assert(!piece.ends_with("abcd"), "");
+}
+
+}  // namespace base
diff --git a/base/strings/string_split.cc b/base/strings/string_split.cc
new file mode 100644
index 0000000..ef9c74d
--- /dev/null
+++ b/base/strings/string_split.cc
@@ -0,0 +1,277 @@
+// Copyright (c) 2012 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 "base/strings/string_split.h"
+
+#include <stddef.h>
+
+#include "polyfills/base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/third_party/icu/icu_utf.h"
+
+namespace gurl_base {
+
+namespace {
+
+// PieceToOutputType converts a StringPiece as needed to a given output type,
+// which is either the same type of StringPiece (a NOP) or the corresponding
+// non-piece string type.
+//
+// The default converter is a NOP, it works when the OutputType is the
+// correct StringPiece.
+template<typename Str, typename OutputType>
+OutputType PieceToOutputType(BasicStringPiece<Str> piece) {
+  return piece;
+}
+template<>  // Convert StringPiece to std::string
+std::string PieceToOutputType<std::string, std::string>(StringPiece piece) {
+  return piece.as_string();
+}
+template<>  // Convert StringPiece16 to string16.
+string16 PieceToOutputType<string16, string16>(StringPiece16 piece) {
+  return piece.as_string();
+}
+
+// Returns either the ASCII or UTF-16 whitespace.
+template<typename Str> BasicStringPiece<Str> WhitespaceForType();
+template<> StringPiece16 WhitespaceForType<string16>() {
+  return kWhitespaceUTF16;
+}
+template<> StringPiece WhitespaceForType<std::string>() {
+  return kWhitespaceASCII;
+}
+
+// Optimize the single-character case to call find() on the string instead,
+// since this is the common case and can be made faster. This could have been
+// done with template specialization too, but would have been less clear.
+//
+// There is no corresponding FindFirstNotOf because StringPiece already
+// implements these different versions that do the optimized searching.
+size_t FindFirstOf(StringPiece piece, char c, size_t pos) {
+  return piece.find(c, pos);
+}
+size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) {
+  return piece.find(c, pos);
+}
+size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) {
+  return piece.find_first_of(one_of, pos);
+}
+size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) {
+  return piece.find_first_of(one_of, pos);
+}
+
+// General string splitter template. Can take 8- or 16-bit input, can produce
+// the corresponding string or StringPiece output, and can take single- or
+// multiple-character delimiters.
+//
+// DelimiterType is either a character (Str::value_type) or a string piece of
+// multiple characters (BasicStringPiece<Str>). StringPiece has a version of
+// find for both of these cases, and the single-character version is the most
+// common and can be implemented faster, which is why this is a template.
+template<typename Str, typename OutputStringType, typename DelimiterType>
+static std::vector<OutputStringType> SplitStringT(
+    BasicStringPiece<Str> str,
+    DelimiterType delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type) {
+  std::vector<OutputStringType> result;
+  if (str.empty())
+    return result;
+
+  size_t start = 0;
+  while (start != Str::npos) {
+    size_t end = FindFirstOf(str, delimiter, start);
+
+    BasicStringPiece<Str> piece;
+    if (end == Str::npos) {
+      piece = str.substr(start);
+      start = Str::npos;
+    } else {
+      piece = str.substr(start, end - start);
+      start = end + 1;
+    }
+
+    if (whitespace == TRIM_WHITESPACE)
+      piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
+
+    if (result_type == SPLIT_WANT_ALL || !piece.empty())
+      result.push_back(PieceToOutputType<Str, OutputStringType>(piece));
+  }
+  return result;
+}
+
+bool AppendStringKeyValue(StringPiece input,
+                          char delimiter,
+                          StringPairs* result) {
+  // Always append a new item regardless of success (it might be empty). The
+  // below code will copy the strings directly into the result pair.
+  result->resize(result->size() + 1);
+  auto& result_pair = result->back();
+
+  // Find the delimiter.
+  size_t end_key_pos = input.find_first_of(delimiter);
+  if (end_key_pos == std::string::npos) {
+    DVLOG(1) << "cannot find delimiter in: " << input;
+    return false;    // No delimiter.
+  }
+  input.substr(0, end_key_pos).CopyToString(&result_pair.first);
+
+  // Find the value string.
+  StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos);
+  size_t begin_value_pos = remains.find_first_not_of(delimiter);
+  if (begin_value_pos == StringPiece::npos) {
+    DVLOG(1) << "cannot parse value from input: " << input;
+    return false;   // No value.
+  }
+  remains.substr(begin_value_pos, remains.size() - begin_value_pos)
+      .CopyToString(&result_pair.second);
+
+  return true;
+}
+
+template <typename Str, typename OutputStringType>
+void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
+                             BasicStringPiece<Str> delimiter,
+                             WhitespaceHandling whitespace,
+                             SplitResult result_type,
+                             std::vector<OutputStringType>* result) {
+  using Piece = BasicStringPiece<Str>;
+  using size_type = typename Piece::size_type;
+
+  result->clear();
+  for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
+       begin_index = end_index + delimiter.size()) {
+    end_index = input.find(delimiter, begin_index);
+    Piece term = end_index == Piece::npos
+                     ? input.substr(begin_index)
+                     : input.substr(begin_index, end_index - begin_index);
+
+    if (whitespace == TRIM_WHITESPACE)
+      term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
+
+    if (result_type == SPLIT_WANT_ALL || !term.empty())
+      result->push_back(PieceToOutputType<Str, OutputStringType>(term));
+  }
+}
+
+}  // namespace
+
+std::vector<std::string> SplitString(StringPiece input,
+                                     StringPiece separators,
+                                     WhitespaceHandling whitespace,
+                                     SplitResult result_type) {
+  if (separators.size() == 1) {
+    return SplitStringT<std::string, std::string, char>(
+        input, separators[0], whitespace, result_type);
+  }
+  return SplitStringT<std::string, std::string, StringPiece>(
+      input, separators, whitespace, result_type);
+}
+
+std::vector<string16> SplitString(StringPiece16 input,
+                                  StringPiece16 separators,
+                                  WhitespaceHandling whitespace,
+                                  SplitResult result_type) {
+  if (separators.size() == 1) {
+    return SplitStringT<string16, string16, char16>(
+        input, separators[0], whitespace, result_type);
+  }
+  return SplitStringT<string16, string16, StringPiece16>(
+      input, separators, whitespace, result_type);
+}
+
+std::vector<StringPiece> SplitStringPiece(StringPiece input,
+                                          StringPiece separators,
+                                          WhitespaceHandling whitespace,
+                                          SplitResult result_type) {
+  if (separators.size() == 1) {
+    return SplitStringT<std::string, StringPiece, char>(
+        input, separators[0], whitespace, result_type);
+  }
+  return SplitStringT<std::string, StringPiece, StringPiece>(
+      input, separators, whitespace, result_type);
+}
+
+std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
+                                            StringPiece16 separators,
+                                            WhitespaceHandling whitespace,
+                                            SplitResult result_type) {
+  if (separators.size() == 1) {
+    return SplitStringT<string16, StringPiece16, char16>(
+        input, separators[0], whitespace, result_type);
+  }
+  return SplitStringT<string16, StringPiece16, StringPiece16>(
+      input, separators, whitespace, result_type);
+}
+
+bool SplitStringIntoKeyValuePairs(StringPiece input,
+                                  char key_value_delimiter,
+                                  char key_value_pair_delimiter,
+                                  StringPairs* key_value_pairs) {
+  return SplitStringIntoKeyValuePairsUsingSubstr(
+      input, key_value_delimiter, StringPiece(&key_value_pair_delimiter, 1),
+      key_value_pairs);
+}
+
+bool SplitStringIntoKeyValuePairsUsingSubstr(
+    StringPiece input,
+    char key_value_delimiter,
+    StringPiece key_value_pair_delimiter,
+    StringPairs* key_value_pairs) {
+  key_value_pairs->clear();
+
+  std::vector<StringPiece> pairs = SplitStringPieceUsingSubstr(
+      input, key_value_pair_delimiter, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+  key_value_pairs->reserve(pairs.size());
+
+  bool success = true;
+  for (const StringPiece& pair : pairs) {
+    if (!AppendStringKeyValue(pair, key_value_delimiter, key_value_pairs)) {
+      // Don't return here, to allow for pairs without associated
+      // value or key; just record that the split failed.
+      success = false;
+    }
+  }
+  return success;
+}
+
+std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
+                                             StringPiece16 delimiter,
+                                             WhitespaceHandling whitespace,
+                                             SplitResult result_type) {
+  std::vector<string16> result;
+  SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+  return result;
+}
+
+std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
+                                                StringPiece delimiter,
+                                                WhitespaceHandling whitespace,
+                                                SplitResult result_type) {
+  std::vector<std::string> result;
+  SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+  return result;
+}
+
+std::vector<StringPiece16> SplitStringPieceUsingSubstr(
+    StringPiece16 input,
+    StringPiece16 delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type) {
+  std::vector<StringPiece16> result;
+  SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+  return result;
+}
+
+std::vector<StringPiece> SplitStringPieceUsingSubstr(
+    StringPiece input,
+    StringPiece delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type) {
+  std::vector<StringPiece> result;
+  SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
+  return result;
+}
+
+}  // namespace base
diff --git a/base/strings/string_split.h b/base/strings/string_split.h
new file mode 100644
index 0000000..1894d05
--- /dev/null
+++ b/base/strings/string_split.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 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.
+
+#ifndef BASE_STRINGS_STRING_SPLIT_H_
+#define BASE_STRINGS_STRING_SPLIT_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "polyfills/base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+
+namespace gurl_base {
+
+enum WhitespaceHandling {
+  KEEP_WHITESPACE,
+  TRIM_WHITESPACE,
+};
+
+enum SplitResult {
+  // Strictly return all results.
+  //
+  // If the input is ",," and the separator is ',' this will return a
+  // vector of three empty strings.
+  SPLIT_WANT_ALL,
+
+  // Only nonempty results will be added to the results. Multiple separators
+  // will be coalesced. Separators at the beginning and end of the input will
+  // be ignored. With TRIM_WHITESPACE, whitespace-only results will be dropped.
+  //
+  // If the input is ",," and the separator is ',', this will return an empty
+  // vector.
+  SPLIT_WANT_NONEMPTY,
+};
+
+// Split the given string on ANY of the given separators, returning copies of
+// the result.
+//
+// To split on either commas or semicolons, keeping all whitespace:
+//
+//   std::vector<std::string> tokens = gurl_base::SplitString(
+//       input, ",;", gurl_base::KEEP_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+BASE_EXPORT std::vector<std::string> SplitString(
+    StringPiece input,
+    StringPiece separators,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+BASE_EXPORT std::vector<string16> SplitString(
+    StringPiece16 input,
+    StringPiece16 separators,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+
+// Like SplitString above except it returns a vector of StringPieces which
+// reference the original buffer without copying. Although you have to be
+// careful to keep the original string unmodified, this provides an efficient
+// way to iterate through tokens in a string.
+//
+// To iterate through all whitespace-separated tokens in an input string:
+//
+//   for (const auto& cur :
+//        gurl_base::SplitStringPiece(input, gurl_base::kWhitespaceASCII,
+//                               gurl_base::KEEP_WHITESPACE,
+//                               gurl_base::SPLIT_WANT_NONEMPTY)) {
+//     ...
+BASE_EXPORT std::vector<StringPiece> SplitStringPiece(
+    StringPiece input,
+    StringPiece separators,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
+    StringPiece16 input,
+    StringPiece16 separators,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+
+using StringPairs = std::vector<std::pair<std::string, std::string>>;
+
+// Splits |line| into key value pairs according to the given delimiters and
+// removes whitespace leading each key and trailing each value. Returns true
+// only if each pair has a non-empty key and value. |key_value_pairs| will
+// include ("","") pairs for entries without |key_value_delimiter|.
+BASE_EXPORT bool SplitStringIntoKeyValuePairs(StringPiece input,
+                                              char key_value_delimiter,
+                                              char key_value_pair_delimiter,
+                                              StringPairs* key_value_pairs);
+
+// Similar to SplitStringIntoKeyValuePairs, but use a substring
+// |key_value_pair_delimiter| instead of a single char.
+BASE_EXPORT bool SplitStringIntoKeyValuePairsUsingSubstr(
+    StringPiece input,
+    char key_value_delimiter,
+    StringPiece key_value_pair_delimiter,
+    StringPairs* key_value_pairs);
+
+// Similar to SplitString, but use a substring delimiter instead of a list of
+// characters that are all possible delimiters.
+BASE_EXPORT std::vector<string16> SplitStringUsingSubstr(
+    StringPiece16 input,
+    StringPiece16 delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
+    StringPiece input,
+    StringPiece delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+
+// Like SplitStringUsingSubstr above except it returns a vector of StringPieces
+// which reference the original buffer without copying. Although you have to be
+// careful to keep the original string unmodified, this provides an efficient
+// way to iterate through tokens in a string.
+//
+// To iterate through all newline-separated tokens in an input string:
+//
+//   for (const auto& cur :
+//        gurl_base::SplitStringUsingSubstr(input, "\r\n",
+//                                     gurl_base::KEEP_WHITESPACE,
+//                                     gurl_base::SPLIT_WANT_NONEMPTY)) {
+//     ...
+BASE_EXPORT std::vector<StringPiece16> SplitStringPieceUsingSubstr(
+    StringPiece16 input,
+    StringPiece16 delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
+    StringPiece input,
+    StringPiece delimiter,
+    WhitespaceHandling whitespace,
+    SplitResult result_type);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRING_SPLIT_H_
diff --git a/base/strings/string_split_unittest.cc b/base/strings/string_split_unittest.cc
new file mode 100644
index 0000000..993450a
--- /dev/null
+++ b/base/strings/string_split_unittest.cc
@@ -0,0 +1,448 @@
+// Copyright (c) 2012 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 "base/strings/string_split.h"
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+
+namespace gurl_base {
+
+class SplitStringIntoKeyValuePairsTest : public testing::Test {
+ protected:
+  gurl_base::StringPairs kv_pairs;
+};
+
+using SplitStringIntoKeyValuePairsUsingSubstrTest =
+    SplitStringIntoKeyValuePairsTest;
+
+TEST_F(SplitStringIntoKeyValuePairsUsingSubstrTest, EmptyString) {
+  EXPECT_TRUE(
+      SplitStringIntoKeyValuePairsUsingSubstr(std::string(),
+                                              ':',  // Key-value delimiter
+                                              ",",  // Key-value pair delimiter
+                                              &kv_pairs));
+  EXPECT_TRUE(kv_pairs.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuePairsUsingSubstrTest, MissingKeyValueDelimiter) {
+  EXPECT_FALSE(
+      SplitStringIntoKeyValuePairsUsingSubstr("key1,,key2:value2",
+                                              ':',   // Key-value delimiter
+                                              ",,",  // Key-value pair delimiter
+                                              &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_TRUE(kv_pairs[0].first.empty());
+  EXPECT_TRUE(kv_pairs[0].second.empty());
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsUsingSubstrTest,
+       MissingKeyValuePairDelimeter) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairsUsingSubstr(
+      "key1:value1,,key3:value3",
+      ':',    // Key-value delimiter
+      ",,,",  // Key-value pair delimiter
+      &kv_pairs));
+  ASSERT_EQ(1U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("value1,,key3:value3", kv_pairs[0].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsUsingSubstrTest, UntrimmedWhitespace) {
+  EXPECT_TRUE(
+      SplitStringIntoKeyValuePairsUsingSubstr("key1 : value1",
+                                              ':',  // Key-value delimiter
+                                              ",",  // Key-value pair delimiter
+                                              &kv_pairs));
+  ASSERT_EQ(1U, kv_pairs.size());
+  EXPECT_EQ("key1 ", kv_pairs[0].first);
+  EXPECT_EQ(" value1", kv_pairs[0].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsUsingSubstrTest, OnlySplitAtGivenSeparator) {
+  std::string a("a ?!@#$%^&*()_+:/{}\\\t\nb");
+  EXPECT_TRUE(
+      SplitStringIntoKeyValuePairsUsingSubstr(a + "X" + a + "XY" + a + "YX" + a,
+                                              'X',   // Key-value delimiter
+                                              "XY",  // Key-value pair delimiter
+                                              &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ(a, kv_pairs[0].first);
+  EXPECT_EQ(a, kv_pairs[0].second);
+  EXPECT_EQ(a + 'Y', kv_pairs[1].first);
+  EXPECT_EQ(a, kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyString) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs(std::string(),
+                                           ':',  // Key-value delimiter
+                                           ',',  // Key-value pair delimiter
+                                           &kv_pairs));
+  EXPECT_TRUE(kv_pairs.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, MissingKeyValueDelimiter) {
+  EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1,key2:value2",
+                                            ':',  // Key-value delimiter
+                                            ',',  // Key-value pair delimiter
+                                            &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_TRUE(kv_pairs[0].first.empty());
+  EXPECT_TRUE(kv_pairs[0].second.empty());
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyKeyWithKeyValueDelimiter) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs(":value1,key2:value2",
+                                           ':',  // Key-value delimiter
+                                           ',',  // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_TRUE(kv_pairs[0].first.empty());
+  EXPECT_EQ("value1", kv_pairs[0].second);
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, TrailingAndLeadingPairDelimiter) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs(",key1:value1,key2:value2,",
+                                           ':',   // Key-value delimiter
+                                           ',',   // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("value1", kv_pairs[0].second);
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyPair) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1,,key3:value3",
+                                           ':',   // Key-value delimiter
+                                           ',',   // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("value1", kv_pairs[0].second);
+  EXPECT_EQ("key3", kv_pairs[1].first);
+  EXPECT_EQ("value3", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyValue) {
+  EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1:,key2:value2",
+                                            ':',   // Key-value delimiter
+                                            ',',   // Key-value pair delimiter
+                                            &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("", kv_pairs[0].second);
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, UntrimmedWhitespace) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1 : value1",
+                                           ':',  // Key-value delimiter
+                                           ',',  // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(1U, kv_pairs.size());
+  EXPECT_EQ("key1 ", kv_pairs[0].first);
+  EXPECT_EQ(" value1", kv_pairs[0].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, TrimmedWhitespace) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1 , key2:value2",
+                                           ':',   // Key-value delimiter
+                                           ',',   // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("value1", kv_pairs[0].second);
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, MultipleKeyValueDelimiters) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:::value1,key2:value2",
+                                           ':',   // Key-value delimiter
+                                           ',',   // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("value1", kv_pairs[0].second);
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, OnlySplitAtGivenSeparator) {
+  std::string a("a ?!@#$%^&*()_+:/{}\\\t\nb");
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs(a + "X" + a + "Y" + a + "X" + a,
+                                           'X',  // Key-value delimiter
+                                           'Y',  // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ(a, kv_pairs[0].first);
+  EXPECT_EQ(a, kv_pairs[0].second);
+  EXPECT_EQ(a, kv_pairs[1].first);
+  EXPECT_EQ(a, kv_pairs[1].second);
+}
+
+
+TEST_F(SplitStringIntoKeyValuePairsTest, DelimiterInValue) {
+  EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:va:ue1,key2:value2",
+                                           ':',   // Key-value delimiter
+                                           ',',   // Key-value pair delimiter
+                                           &kv_pairs));
+  ASSERT_EQ(2U, kv_pairs.size());
+  EXPECT_EQ("key1", kv_pairs[0].first);
+  EXPECT_EQ("va:ue1", kv_pairs[0].second);
+  EXPECT_EQ("key2", kv_pairs[1].first);
+  EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST(SplitStringUsingSubstrTest, EmptyString) {
+  std::vector<std::string> results = SplitStringUsingSubstr(
+      std::string(), "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(1u, results.size());
+  EXPECT_THAT(results, ElementsAre(""));
+}
+
+TEST(StringUtilTest, SplitString_Basics) {
+  std::vector<std::string> r;
+
+  r = SplitString(std::string(), ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+  EXPECT_TRUE(r.empty());
+
+  // Empty separator list
+  r = SplitString("hello, world", "", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(1u, r.size());
+  EXPECT_EQ("hello, world", r[0]);
+
+  // Should split on any of the separators.
+  r = SplitString("::,,;;", ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(7u, r.size());
+  for (auto str : r)
+    ASSERT_TRUE(str.empty());
+
+  r = SplitString("red, green; blue:", ",:;", TRIM_WHITESPACE,
+                  SPLIT_WANT_NONEMPTY);
+  ASSERT_EQ(3u, r.size());
+  EXPECT_EQ("red", r[0]);
+  EXPECT_EQ("green", r[1]);
+  EXPECT_EQ("blue", r[2]);
+
+  // Want to split a string along whitespace sequences.
+  r = SplitString("  red green   \tblue\n", " \t\n", TRIM_WHITESPACE,
+                  SPLIT_WANT_NONEMPTY);
+  ASSERT_EQ(3u, r.size());
+  EXPECT_EQ("red", r[0]);
+  EXPECT_EQ("green", r[1]);
+  EXPECT_EQ("blue", r[2]);
+
+  // Weird case of splitting on spaces but not trimming.
+  r = SplitString(" red ", " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(3u, r.size());
+  EXPECT_EQ("", r[0]);  // Before the first space.
+  EXPECT_EQ("red", r[1]);
+  EXPECT_EQ("", r[2]);  // After the last space.
+}
+
+TEST(StringUtilTest, SplitString_WhitespaceAndResultType) {
+  std::vector<std::string> r;
+
+  // Empty input handling.
+  r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+  EXPECT_TRUE(r.empty());
+  r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+  EXPECT_TRUE(r.empty());
+
+  // Input string is space and we're trimming.
+  r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(1u, r.size());
+  EXPECT_EQ("", r[0]);
+  r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+  EXPECT_TRUE(r.empty());
+
+  // Test all 4 combinations of flags on ", ,".
+  r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(3u, r.size());
+  EXPECT_EQ("", r[0]);
+  EXPECT_EQ(" ", r[1]);
+  EXPECT_EQ("", r[2]);
+  r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+  ASSERT_EQ(1u, r.size());
+  ASSERT_EQ(" ", r[0]);
+  r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(3u, r.size());
+  EXPECT_EQ("", r[0]);
+  EXPECT_EQ("", r[1]);
+  EXPECT_EQ("", r[2]);
+  r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+  ASSERT_TRUE(r.empty());
+}
+
+TEST(SplitStringUsingSubstrTest, StringWithNoDelimiter) {
+  std::vector<std::string> results = SplitStringUsingSubstr(
+      "alongwordwithnodelimiter", "DELIMITER", TRIM_WHITESPACE,
+      SPLIT_WANT_ALL);
+  ASSERT_EQ(1u, results.size());
+  EXPECT_THAT(results, ElementsAre("alongwordwithnodelimiter"));
+}
+
+TEST(SplitStringUsingSubstrTest, LeadingDelimitersSkipped) {
+  std::vector<std::string> results = SplitStringUsingSubstr(
+      "DELIMITERDELIMITERDELIMITERoneDELIMITERtwoDELIMITERthree",
+      "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(6u, results.size());
+  EXPECT_THAT(results, ElementsAre("", "", "", "one", "two", "three"));
+}
+
+TEST(SplitStringUsingSubstrTest, ConsecutiveDelimitersSkipped) {
+  std::vector<std::string> results = SplitStringUsingSubstr(
+      "unoDELIMITERDELIMITERDELIMITERdosDELIMITERtresDELIMITERDELIMITERcuatro",
+      "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(7u, results.size());
+  EXPECT_THAT(results, ElementsAre("uno", "", "", "dos", "tres", "", "cuatro"));
+}
+
+TEST(SplitStringUsingSubstrTest, TrailingDelimitersSkipped) {
+  std::vector<std::string> results = SplitStringUsingSubstr(
+      "unDELIMITERdeuxDELIMITERtroisDELIMITERquatreDELIMITERDELIMITERDELIMITER",
+      "DELIMITER", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  ASSERT_EQ(7u, results.size());
+  EXPECT_THAT(
+      results, ElementsAre("un", "deux", "trois", "quatre", "", "", ""));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, StringWithNoDelimiter) {
+  std::vector<gurl_base::StringPiece> results =
+      SplitStringPieceUsingSubstr("alongwordwithnodelimiter", "DELIMITER",
+                                  gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(1u, results.size());
+  EXPECT_THAT(results, ElementsAre("alongwordwithnodelimiter"));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, LeadingDelimitersSkipped) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "DELIMITERDELIMITERDELIMITERoneDELIMITERtwoDELIMITERthree", "DELIMITER",
+      gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(6u, results.size());
+  EXPECT_THAT(results, ElementsAre("", "", "", "one", "two", "three"));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, ConsecutiveDelimitersSkipped) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "unoDELIMITERDELIMITERDELIMITERdosDELIMITERtresDELIMITERDELIMITERcuatro",
+      "DELIMITER", gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(7u, results.size());
+  EXPECT_THAT(results, ElementsAre("uno", "", "", "dos", "tres", "", "cuatro"));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, TrailingDelimitersSkipped) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "unDELIMITERdeuxDELIMITERtroisDELIMITERquatreDELIMITERDELIMITERDELIMITER",
+      "DELIMITER", gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(7u, results.size());
+  EXPECT_THAT(results,
+              ElementsAre("un", "deux", "trois", "quatre", "", "", ""));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, KeepWhitespace) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "un DELIMITERdeux\tDELIMITERtrois\nDELIMITERquatre", "DELIMITER",
+      gurl_base::KEEP_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(4u, results.size());
+  EXPECT_THAT(results, ElementsAre("un ", "deux\t", "trois\n", "quatre"));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, TrimWhitespace) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "un DELIMITERdeux\tDELIMITERtrois\nDELIMITERquatre", "DELIMITER",
+      gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(4u, results.size());
+  EXPECT_THAT(results, ElementsAre("un", "deux", "trois", "quatre"));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, SplitWantAll) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "unDELIMITERdeuxDELIMITERtroisDELIMITERDELIMITER", "DELIMITER",
+      gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(5u, results.size());
+  EXPECT_THAT(results, ElementsAre("un", "deux", "trois", "", ""));
+}
+
+TEST(SplitStringPieceUsingSubstrTest, SplitWantNonEmpty) {
+  std::vector<gurl_base::StringPiece> results = SplitStringPieceUsingSubstr(
+      "unDELIMITERdeuxDELIMITERtroisDELIMITERDELIMITER", "DELIMITER",
+      gurl_base::TRIM_WHITESPACE, gurl_base::SPLIT_WANT_NONEMPTY);
+  ASSERT_EQ(3u, results.size());
+  EXPECT_THAT(results, ElementsAre("un", "deux", "trois"));
+}
+
+TEST(StringSplitTest, StringSplitKeepWhitespace) {
+  std::vector<std::string> r;
+
+  r = SplitString("   ", "*", gurl_base::KEEP_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(1U, r.size());
+  EXPECT_EQ(r[0], "   ");
+
+  r = SplitString("\t  \ta\t ", "\t", gurl_base::KEEP_WHITESPACE,
+                  gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(4U, r.size());
+  EXPECT_EQ(r[0], "");
+  EXPECT_EQ(r[1], "  ");
+  EXPECT_EQ(r[2], "a");
+  EXPECT_EQ(r[3], " ");
+
+  r = SplitString("\ta\t\nb\tcc", "\n", gurl_base::KEEP_WHITESPACE,
+                  gurl_base::SPLIT_WANT_ALL);
+  ASSERT_EQ(2U, r.size());
+  EXPECT_EQ(r[0], "\ta\t");
+  EXPECT_EQ(r[1], "b\tcc");
+}
+
+TEST(StringSplitTest, SplitStringAlongWhitespace) {
+  struct TestData {
+    const char* input;
+    const size_t expected_result_count;
+    const char* output1;
+    const char* output2;
+  } data[] = {
+    { "a",       1, "a",  ""   },
+    { " ",       0, "",   ""   },
+    { " a",      1, "a",  ""   },
+    { " ab ",    1, "ab", ""   },
+    { " ab c",   2, "ab", "c"  },
+    { " ab c ",  2, "ab", "c"  },
+    { " ab cd",  2, "ab", "cd" },
+    { " ab cd ", 2, "ab", "cd" },
+    { " \ta\t",  1, "a",  ""   },
+    { " b\ta\t", 2, "b",  "a"  },
+    { " b\tat",  2, "b",  "at" },
+    { "b\tat",   2, "b",  "at" },
+    { "b\t at",  2, "b",  "at" },
+  };
+  for (const auto& i : data) {
+    std::vector<std::string> results =
+        gurl_base::SplitString(i.input, kWhitespaceASCII, gurl_base::KEEP_WHITESPACE,
+                          gurl_base::SPLIT_WANT_NONEMPTY);
+    ASSERT_EQ(i.expected_result_count, results.size());
+    if (i.expected_result_count > 0)
+      ASSERT_EQ(i.output1, results[0]);
+    if (i.expected_result_count > 1)
+      ASSERT_EQ(i.output2, results[1]);
+  }
+}
+
+}  // namespace base
diff --git a/base/strings/string_tokenizer.h b/base/strings/string_tokenizer.h
new file mode 100644
index 0000000..7ee0178
--- /dev/null
+++ b/base/strings/string_tokenizer.h
@@ -0,0 +1,303 @@
+// Copyright (c) 2011 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.
+
+#ifndef BASE_STRINGS_STRING_TOKENIZER_H_
+#define BASE_STRINGS_STRING_TOKENIZER_H_
+
+#include <algorithm>
+#include <string>
+
+#include "base/strings/string_piece.h"
+
+namespace gurl_base {
+
+// StringTokenizerT is a simple string tokenizer class.  It works like an
+// iterator that with each step (see the Advance method) updates members that
+// refer to the next token in the input string.  The user may optionally
+// configure the tokenizer to return delimiters.
+//
+// EXAMPLE 1:
+//
+//   char input[] = "this is a test";
+//   CStringTokenizer t(input, input + strlen(input), " ");
+//   while (t.GetNext()) {
+//     printf("%s\n", t.token().c_str());
+//   }
+//
+// Output:
+//
+//   this
+//   is
+//   a
+//   test
+//
+//
+// EXAMPLE 2:
+//
+//   std::string input = "no-cache=\"foo, bar\", private";
+//   StringTokenizer t(input, ", ");
+//   t.set_quote_chars("\"");
+//   while (t.GetNext()) {
+//     printf("%s\n", t.token().c_str());
+//   }
+//
+// Output:
+//
+//   no-cache="foo, bar"
+//   private
+//
+//
+// EXAMPLE 3:
+//
+//   bool next_is_option = false, next_is_value = false;
+//   std::string input = "text/html; charset=UTF-8; foo=bar";
+//   StringTokenizer t(input, "; =");
+//   t.set_options(StringTokenizer::RETURN_DELIMS);
+//   while (t.GetNext()) {
+//     if (t.token_is_delim()) {
+//       switch (*t.token_begin()) {
+//         case ';':
+//           next_is_option = true;
+//           break;
+//         case '=':
+//           next_is_value = true;
+//           break;
+//       }
+//     } else {
+//       const char* label;
+//       if (next_is_option) {
+//         label = "option-name";
+//         next_is_option = false;
+//       } else if (next_is_value) {
+//         label = "option-value";
+//         next_is_value = false;
+//       } else {
+//         label = "mime-type";
+//       }
+//       printf("%s: %s\n", label, t.token().c_str());
+//     }
+//   }
+//
+//
+template <class str, class const_iterator>
+class StringTokenizerT {
+ public:
+  typedef typename str::value_type char_type;
+
+  // Options that may be pass to set_options()
+  enum {
+    // Specifies the delimiters should be returned as tokens
+    RETURN_DELIMS = 1 << 0,
+
+    // Specifies that empty tokens should be returned. Treats the beginning and
+    // ending of the string as implicit delimiters, though doesn't return them
+    // as tokens if RETURN_DELIMS is also used.
+    RETURN_EMPTY_TOKENS = 1 << 1,
+  };
+
+  // The string object must live longer than the tokenizer. In particular, this
+  // should not be constructed with a temporary. The deleted rvalue constructor
+  // blocks the most obvious instances of this (e.g. passing a string literal to
+  // the constructor), but caution must still be exercised.
+  StringTokenizerT(const str& string,
+                   const str& delims) {
+    Init(string.begin(), string.end(), delims);
+  }
+
+  // Don't allow temporary strings to be used with string tokenizer, since
+  // Init() would otherwise save iterators to a temporary string.
+  StringTokenizerT(str&&, const str& delims) = delete;
+
+  StringTokenizerT(const_iterator string_begin,
+                   const_iterator string_end,
+                   const str& delims) {
+    Init(string_begin, string_end, delims);
+  }
+
+  // Set the options for this tokenizer.  By default, this is 0.
+  void set_options(int options) { options_ = options; }
+
+  // Set the characters to regard as quotes.  By default, this is empty.  When
+  // a quote char is encountered, the tokenizer will switch into a mode where
+  // it ignores delimiters that it finds.  It switches out of this mode once it
+  // finds another instance of the quote char.  If a backslash is encountered
+  // within a quoted string, then the next character is skipped.
+  void set_quote_chars(const str& quotes) { quotes_ = quotes; }
+
+  // Call this method to advance the tokenizer to the next delimiter.  This
+  // returns false if the tokenizer is complete.  This method must be called
+  // before calling any of the token* methods.
+  bool GetNext() {
+    if (quotes_.empty() && options_ == 0)
+      return QuickGetNext();
+    else
+      return FullGetNext();
+  }
+
+  // Start iterating through tokens from the beginning of the string.
+  void Reset() {
+    token_end_ = start_pos_;
+  }
+
+  // Returns true if token is a delimiter.  When the tokenizer is constructed
+  // with the RETURN_DELIMS option, this method can be used to check if the
+  // returned token is actually a delimiter. Returns true before the first
+  // time GetNext() has been called, and after GetNext() returns false.
+  bool token_is_delim() const { return token_is_delim_; }
+
+  // If GetNext() returned true, then these methods may be used to read the
+  // value of the token.
+  const_iterator token_begin() const { return token_begin_; }
+  const_iterator token_end() const { return token_end_; }
+  str token() const { return str(token_begin_, token_end_); }
+  BasicStringPiece<str> token_piece() const {
+    return BasicStringPiece<str>(&*token_begin_,
+                                 std::distance(token_begin_, token_end_));
+  }
+
+ private:
+  void Init(const_iterator string_begin,
+            const_iterator string_end,
+            const str& delims) {
+    start_pos_ = string_begin;
+    token_begin_ = string_begin;
+    token_end_ = string_begin;
+    end_ = string_end;
+    delims_ = delims;
+    options_ = 0;
+    token_is_delim_ = true;
+  }
+
+  // Implementation of GetNext() for when we have no quote characters. We have
+  // two separate implementations because AdvanceOne() is a hot spot in large
+  // text files with large tokens.
+  bool QuickGetNext() {
+    token_is_delim_ = false;
+    for (;;) {
+      token_begin_ = token_end_;
+      if (token_end_ == end_) {
+        token_is_delim_ = true;
+        return false;
+      }
+      ++token_end_;
+      if (delims_.find(*token_begin_) == str::npos)
+        break;
+      // else skip over delimiter.
+    }
+    while (token_end_ != end_ && delims_.find(*token_end_) == str::npos)
+      ++token_end_;
+    return true;
+  }
+
+  // Implementation of GetNext() for when we have to take quotes into account.
+  bool FullGetNext() {
+    AdvanceState state;
+
+    for (;;) {
+      if (token_is_delim_) {
+        // Last token was a delimiter. Note: This is also the case at the start.
+        //
+        //    ... D T T T T D ...
+        //        ^ ^
+        //        | |
+        //        | |token_end_| : The next character to look at or |end_|.
+        //        |
+        //        |token_begin_| : Points to delimiter or |token_end_|.
+        //
+        // The next token is always a non-delimiting token. It could be empty,
+        // however.
+        token_is_delim_ = false;
+        token_begin_ = token_end_;
+
+        // Slurp all non-delimiter characters into the token.
+        while (token_end_ != end_ && AdvanceOne(&state, *token_end_)) {
+          ++token_end_;
+        }
+
+        // If it's non-empty, or empty tokens were requested, return the token.
+        if (token_begin_ != token_end_ || (options_ & RETURN_EMPTY_TOKENS))
+          return true;
+      }
+
+      GURL_DCHECK(!token_is_delim_);
+      // Last token was a regular token.
+      //
+      //    ... T T T D T T ...
+      //        ^     ^
+      //        |     |
+      //        |     token_end_ : The next character to look at. Always one
+      //        |                  char beyond the token boundary.
+      //        |
+      //        token_begin_ : Points to beginning of token. Note: token could
+      //                       be empty, in which case
+      //                       token_begin_ == token_end_.
+      //
+      // The next token is always a delimiter. It could be |end_| however, but
+      // |end_| is also an implicit delimiter.
+      token_is_delim_ = true;
+      token_begin_ = token_end_;
+
+      if (token_end_ == end_)
+        return false;
+
+      // Look at the delimiter.
+      ++token_end_;
+      if (options_ & RETURN_DELIMS)
+        return true;
+    }
+
+    return false;
+  }
+
+  bool IsDelim(char_type c) const {
+    return delims_.find(c) != str::npos;
+  }
+
+  bool IsQuote(char_type c) const {
+    return quotes_.find(c) != str::npos;
+  }
+
+  struct AdvanceState {
+    bool in_quote;
+    bool in_escape;
+    char_type quote_char;
+    AdvanceState() : in_quote(false), in_escape(false), quote_char('\0') {}
+  };
+
+  // Returns true if a delimiter was not hit.
+  bool AdvanceOne(AdvanceState* state, char_type c) {
+    if (state->in_quote) {
+      if (state->in_escape) {
+        state->in_escape = false;
+      } else if (c == '\\') {
+        state->in_escape = true;
+      } else if (c == state->quote_char) {
+        state->in_quote = false;
+      }
+    } else {
+      if (IsDelim(c))
+        return false;
+      state->in_quote = IsQuote(state->quote_char = c);
+    }
+    return true;
+  }
+
+  const_iterator start_pos_;
+  const_iterator token_begin_;
+  const_iterator token_end_;
+  const_iterator end_;
+  str delims_;
+  str quotes_;
+  int options_;
+  bool token_is_delim_;
+};
+
+typedef StringTokenizerT<std::string, std::string::const_iterator>
+    StringTokenizer;
+typedef StringTokenizerT<string16, string16::const_iterator> String16Tokenizer;
+typedef StringTokenizerT<std::string, const char*> CStringTokenizer;
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRING_TOKENIZER_H_
diff --git a/base/strings/string_tokenizer_fuzzer.cc b/base/strings/string_tokenizer_fuzzer.cc
new file mode 100644
index 0000000..3aaee7b
--- /dev/null
+++ b/base/strings/string_tokenizer_fuzzer.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/string_tokenizer.h"
+
+void GetAllTokens(gurl_base::StringTokenizer& t) {
+  while (t.GetNext()) {
+    (void)t.token();
+  }
+}
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  uint8_t size_t_bytes = sizeof(size_t);
+  if (size < size_t_bytes + 1) {
+    return 0;
+  }
+
+  // Calculate pattern size based on remaining bytes, otherwise fuzzing is
+  // inefficient with bailouts in most cases.
+  size_t pattern_size =
+      *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes);
+
+  std::string pattern(reinterpret_cast<const char*>(data + size_t_bytes),
+                      pattern_size);
+  std::string input(
+      reinterpret_cast<const char*>(data + size_t_bytes + pattern_size),
+      size - pattern_size - size_t_bytes);
+
+  // Allow quote_chars and options to be set. Otherwise full coverage
+  // won't be possible since IsQuote, FullGetNext and other functions
+  // won't be called.
+  for (bool return_delims : {false, true}) {
+    for (bool return_empty_strings : {false, true}) {
+      int options = 0;
+      if (return_delims)
+        options |= gurl_base::StringTokenizer::RETURN_DELIMS;
+      if (return_empty_strings)
+        options |= gurl_base::StringTokenizer::RETURN_EMPTY_TOKENS;
+
+      gurl_base::StringTokenizer t(input, pattern);
+      t.set_options(options);
+      GetAllTokens(t);
+
+      gurl_base::StringTokenizer t_quote(input, pattern);
+      t_quote.set_quote_chars("\"");
+      t_quote.set_options(options);
+      GetAllTokens(t_quote);
+    }
+  }
+
+  return 0;
+}
diff --git a/base/strings/string_tokenizer_unittest.cc b/base/strings/string_tokenizer_unittest.cc
new file mode 100644
index 0000000..1665d5d
--- /dev/null
+++ b/base/strings/string_tokenizer_unittest.cc
@@ -0,0 +1,387 @@
+// Copyright (c) 2006-2008 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 "base/strings/string_tokenizer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace gurl_base {
+
+namespace {
+
+TEST(StringTokenizerTest, Simple) {
+  string input = "this is a test";
+  StringTokenizer t(input, " ");
+  // The start of string, before returning any tokens, is considered a
+  // delimiter.
+  EXPECT_TRUE(t.token_is_delim());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("this", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("is", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("a", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("test", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+  // The end of string, after the last token tokens, is considered a delimiter.
+  EXPECT_TRUE(t.token_is_delim());
+}
+
+TEST(StringTokenizerTest, Reset) {
+  string input = "this is a test";
+  StringTokenizer t(input, " ");
+
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_TRUE(t.token_is_delim());
+
+    EXPECT_TRUE(t.GetNext());
+    EXPECT_FALSE(t.token_is_delim());
+    EXPECT_EQ("this", t.token());
+
+    EXPECT_TRUE(t.GetNext());
+    EXPECT_FALSE(t.token_is_delim());
+    EXPECT_EQ("is", t.token());
+
+    EXPECT_TRUE(t.GetNext());
+    EXPECT_FALSE(t.token_is_delim());
+    EXPECT_EQ("a", t.token());
+
+    EXPECT_TRUE(t.GetNext());
+    EXPECT_FALSE(t.token_is_delim());
+    EXPECT_EQ("test", t.token());
+
+    EXPECT_FALSE(t.GetNext());
+    EXPECT_TRUE(t.token_is_delim());
+
+    t.Reset();
+  }
+}
+
+TEST(StringTokenizerTest, RetDelims) {
+  string input = "this is a test";
+  StringTokenizer t(input, " ");
+  t.set_options(StringTokenizer::RETURN_DELIMS);
+  EXPECT_TRUE(t.token_is_delim());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("this", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(" ", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("is", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(" ", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("a", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(" ", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("test", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+}
+
+TEST(StringTokenizerTest, RetEmptyTokens) {
+  string input = "foo='a, b',,bar,,baz,quux";
+  StringTokenizer t(input, ",");
+  t.set_options(StringTokenizer::RETURN_EMPTY_TOKENS);
+  t.set_quote_chars("'");
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("foo='a, b'", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("baz", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("quux", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetEmptyTokens_AtStart) {
+  string input = ",bar";
+  StringTokenizer t(input, ",");
+  t.set_options(StringTokenizer::RETURN_EMPTY_TOKENS);
+  t.set_quote_chars("'");
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetEmptyTokens_AtEnd) {
+  string input = "bar,";
+  StringTokenizer t(input, ",");
+  t.set_options(StringTokenizer::RETURN_EMPTY_TOKENS);
+  t.set_quote_chars("'");
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetEmptyTokens_Both) {
+  string input = ",";
+  StringTokenizer t(input, ",");
+  t.set_options(StringTokenizer::RETURN_EMPTY_TOKENS);
+  t.set_quote_chars("'");
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetEmptyTokens_Empty) {
+  string input = "";
+  StringTokenizer t(input, ",");
+  t.set_options(StringTokenizer::RETURN_EMPTY_TOKENS);
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetDelimsAndEmptyTokens) {
+  string input = "foo='a, b',,bar,,baz,quux";
+  StringTokenizer t(input, ",");
+  t.set_options(StringTokenizer::RETURN_DELIMS |
+                StringTokenizer::RETURN_EMPTY_TOKENS);
+  t.set_quote_chars("'");
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("foo='a, b'", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ(",", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ(",", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ(",", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ(",", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("baz", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ(",", t.token());
+
+  ASSERT_TRUE(t.GetNext());
+  EXPECT_EQ("quux", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ManyDelims) {
+  string input = "this: is, a-test";
+  StringTokenizer t(input, ": ,-");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("this", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("is", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("a", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("test", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseHeader) {
+  string input = "Content-Type: text/html ; charset=UTF-8";
+  StringTokenizer t(input, ": ;=");
+  t.set_options(StringTokenizer::RETURN_DELIMS);
+  EXPECT_TRUE(t.token_is_delim());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("Content-Type", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(":", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(" ", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("text/html", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(" ", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(";", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ(" ", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("charset", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+  EXPECT_EQ("=", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_FALSE(t.token_is_delim());
+  EXPECT_EQ("UTF-8", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+  EXPECT_TRUE(t.token_is_delim());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString) {
+  string input = "foo bar 'hello world' baz";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("foo", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("'hello world'", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("baz", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Malformed) {
+  string input = "bar 'hello wo";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("'hello wo", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Multiple) {
+  string input = "bar 'hel\"lo\" wo' baz\"";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'\"");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("'hel\"lo\" wo'", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("baz\"", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes) {
+  string input = "foo 'don\\'t do that'";
+  StringTokenizer t(input, " ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("foo", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("'don\\'t do that'", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes2) {
+  string input = "foo='a, b', bar";
+  StringTokenizer t(input, ", ");
+  t.set_quote_chars("'");
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("foo='a, b'", t.token());
+
+  EXPECT_TRUE(t.GetNext());
+  EXPECT_EQ("bar", t.token());
+
+  EXPECT_FALSE(t.GetNext());
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
new file mode 100644
index 0000000..2b2591d
--- /dev/null
+++ b/base/strings/string_util.cc
@@ -0,0 +1,1119 @@
+// Copyright 2013 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 "base/strings/string_util.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "polyfills/base/logging.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "build/build_config.h"
+
+namespace gurl_base {
+
+namespace {
+
+// Used by ReplaceStringPlaceholders to track the position in the string of
+// replaced parameters.
+struct ReplacementOffset {
+  ReplacementOffset(uintptr_t parameter, size_t offset)
+      : parameter(parameter),
+        offset(offset) {}
+
+  // Index of the parameter.
+  uintptr_t parameter;
+
+  // Starting position in the string.
+  size_t offset;
+};
+
+static bool CompareParameter(const ReplacementOffset& elem1,
+                             const ReplacementOffset& elem2) {
+  return elem1.parameter < elem2.parameter;
+}
+
+// Overloaded function to append one string onto the end of another. Having a
+// separate overload for |source| as both string and StringPiece allows for more
+// efficient usage from functions templated to work with either type (avoiding a
+// redundant call to the BasicStringPiece constructor in both cases).
+template <typename string_type>
+inline void AppendToString(string_type* target, const string_type& source) {
+  target->append(source);
+}
+
+template <typename string_type>
+inline void AppendToString(string_type* target,
+                           const BasicStringPiece<string_type>& source) {
+  source.AppendToString(target);
+}
+
+// Assuming that a pointer is the size of a "machine word", then
+// uintptr_t is an integer type that is also a machine word.
+using MachineWord = uintptr_t;
+
+inline bool IsMachineWordAligned(const void* pointer) {
+  return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1));
+}
+
+template <typename CharacterType>
+struct NonASCIIMask;
+template <>
+struct NonASCIIMask<char> {
+  static constexpr MachineWord value() {
+    return static_cast<MachineWord>(0x8080808080808080ULL);
+  }
+};
+template <>
+struct NonASCIIMask<char16> {
+  static constexpr MachineWord value() {
+    return static_cast<MachineWord>(0xFF80FF80FF80FF80ULL);
+  }
+};
+#if defined(WCHAR_T_IS_UTF32)
+template <>
+struct NonASCIIMask<wchar_t> {
+  static constexpr MachineWord value() {
+    return static_cast<MachineWord>(0xFFFFFF80FFFFFF80ULL);
+  }
+};
+#endif  // WCHAR_T_IS_UTF32
+
+}  // namespace
+
+bool IsWprintfFormatPortable(const wchar_t* format) {
+  for (const wchar_t* position = format; *position != '\0'; ++position) {
+    if (*position == '%') {
+      bool in_specification = true;
+      bool modifier_l = false;
+      while (in_specification) {
+        // Eat up characters until reaching a known specifier.
+        if (*++position == '\0') {
+          // The format string ended in the middle of a specification.  Call
+          // it portable because no unportable specifications were found.  The
+          // string is equally broken on all platforms.
+          return true;
+        }
+
+        if (*position == 'l') {
+          // 'l' is the only thing that can save the 's' and 'c' specifiers.
+          modifier_l = true;
+        } else if (((*position == 's' || *position == 'c') && !modifier_l) ||
+                   *position == 'S' || *position == 'C' || *position == 'F' ||
+                   *position == 'D' || *position == 'O' || *position == 'U') {
+          // Not portable.
+          return false;
+        }
+
+        if (wcschr(L"diouxXeEfgGaAcspn%", *position)) {
+          // Portable, keep scanning the rest of the format string.
+          in_specification = false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+namespace {
+
+template<typename StringType>
+StringType ToLowerASCIIImpl(BasicStringPiece<StringType> str) {
+  StringType ret;
+  ret.reserve(str.size());
+  for (size_t i = 0; i < str.size(); i++)
+    ret.push_back(ToLowerASCII(str[i]));
+  return ret;
+}
+
+template<typename StringType>
+StringType ToUpperASCIIImpl(BasicStringPiece<StringType> str) {
+  StringType ret;
+  ret.reserve(str.size());
+  for (size_t i = 0; i < str.size(); i++)
+    ret.push_back(ToUpperASCII(str[i]));
+  return ret;
+}
+
+}  // namespace
+
+std::string ToLowerASCII(StringPiece str) {
+  return ToLowerASCIIImpl<std::string>(str);
+}
+
+string16 ToLowerASCII(StringPiece16 str) {
+  return ToLowerASCIIImpl<string16>(str);
+}
+
+std::string ToUpperASCII(StringPiece str) {
+  return ToUpperASCIIImpl<std::string>(str);
+}
+
+string16 ToUpperASCII(StringPiece16 str) {
+  return ToUpperASCIIImpl<string16>(str);
+}
+
+template<class StringType>
+int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
+                                 BasicStringPiece<StringType> b) {
+  // Find the first characters that aren't equal and compare them.  If the end
+  // of one of the strings is found before a nonequal character, the lengths
+  // of the strings are compared.
+  size_t i = 0;
+  while (i < a.length() && i < b.length()) {
+    typename StringType::value_type lower_a = ToLowerASCII(a[i]);
+    typename StringType::value_type lower_b = ToLowerASCII(b[i]);
+    if (lower_a < lower_b)
+      return -1;
+    if (lower_a > lower_b)
+      return 1;
+    i++;
+  }
+
+  // End of one string hit before finding a different character. Expect the
+  // common case to be "strings equal" at this point so check that first.
+  if (a.length() == b.length())
+    return 0;
+
+  if (a.length() < b.length())
+    return -1;
+  return 1;
+}
+
+int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
+  return CompareCaseInsensitiveASCIIT<std::string>(a, b);
+}
+
+int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
+  return CompareCaseInsensitiveASCIIT<string16>(a, b);
+}
+
+bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
+  if (a.length() != b.length())
+    return false;
+  return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
+}
+
+bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
+  if (a.length() != b.length())
+    return false;
+  return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
+}
+
+const std::string& EmptyString() {
+  static const gurl_base::NoDestructor<std::string> s;
+  return *s;
+}
+
+const string16& EmptyString16() {
+  static const gurl_base::NoDestructor<string16> s16;
+  return *s16;
+}
+
+template <class StringType>
+bool ReplaceCharsT(const StringType& input,
+                   BasicStringPiece<StringType> find_any_of_these,
+                   BasicStringPiece<StringType> replace_with,
+                   StringType* output);
+
+bool ReplaceChars(const string16& input,
+                  StringPiece16 replace_chars,
+                  const string16& replace_with,
+                  string16* output) {
+  return ReplaceCharsT(input, replace_chars, StringPiece16(replace_with),
+                       output);
+}
+
+bool ReplaceChars(const std::string& input,
+                  StringPiece replace_chars,
+                  const std::string& replace_with,
+                  std::string* output) {
+  return ReplaceCharsT(input, replace_chars, StringPiece(replace_with), output);
+}
+
+bool RemoveChars(const string16& input,
+                 StringPiece16 remove_chars,
+                 string16* output) {
+  return ReplaceCharsT(input, remove_chars, StringPiece16(), output);
+}
+
+bool RemoveChars(const std::string& input,
+                 StringPiece remove_chars,
+                 std::string* output) {
+  return ReplaceCharsT(input, remove_chars, StringPiece(), output);
+}
+
+template<typename Str>
+TrimPositions TrimStringT(const Str& input,
+                          BasicStringPiece<Str> trim_chars,
+                          TrimPositions positions,
+                          Str* output) {
+  // Find the edges of leading/trailing whitespace as desired. Need to use
+  // a StringPiece version of input to be able to call find* on it with the
+  // StringPiece version of trim_chars (normally the trim_chars will be a
+  // constant so avoid making a copy).
+  BasicStringPiece<Str> input_piece(input);
+  const size_t last_char = input.length() - 1;
+  const size_t first_good_char = (positions & TRIM_LEADING) ?
+      input_piece.find_first_not_of(trim_chars) : 0;
+  const size_t last_good_char = (positions & TRIM_TRAILING) ?
+      input_piece.find_last_not_of(trim_chars) : last_char;
+
+  // When the string was all trimmed, report that we stripped off characters
+  // from whichever position the caller was interested in. For empty input, we
+  // stripped no characters, but we still need to clear |output|.
+  if (input.empty() ||
+      (first_good_char == Str::npos) || (last_good_char == Str::npos)) {
+    bool input_was_empty = input.empty();  // in case output == &input
+    output->clear();
+    return input_was_empty ? TRIM_NONE : positions;
+  }
+
+  // Trim.
+  *output =
+      input.substr(first_good_char, last_good_char - first_good_char + 1);
+
+  // Return where we trimmed from.
+  return static_cast<TrimPositions>(
+      ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
+      ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
+}
+
+bool TrimString(const string16& input,
+                StringPiece16 trim_chars,
+                string16* output) {
+  return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+bool TrimString(const std::string& input,
+                StringPiece trim_chars,
+                std::string* output) {
+  return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+template<typename Str>
+BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
+                                       BasicStringPiece<Str> trim_chars,
+                                       TrimPositions positions) {
+  size_t begin = (positions & TRIM_LEADING) ?
+      input.find_first_not_of(trim_chars) : 0;
+  size_t end = (positions & TRIM_TRAILING) ?
+      input.find_last_not_of(trim_chars) + 1 : input.size();
+  return input.substr(begin, end - begin);
+}
+
+StringPiece16 TrimString(StringPiece16 input,
+                         StringPiece16 trim_chars,
+                         TrimPositions positions) {
+  return TrimStringPieceT(input, trim_chars, positions);
+}
+
+StringPiece TrimString(StringPiece input,
+                       StringPiece trim_chars,
+                       TrimPositions positions) {
+  return TrimStringPieceT(input, trim_chars, positions);
+}
+
+void TruncateUTF8ToByteSize(const std::string& input,
+                            const size_t byte_size,
+                            std::string* output) {
+  GURL_DCHECK(output);
+  if (byte_size > input.length()) {
+    *output = input;
+    return;
+  }
+  GURL_DCHECK_LE(byte_size,
+            static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
+  // Note: This cast is necessary because CBU8_NEXT uses int32_ts.
+  int32_t truncation_length = static_cast<int32_t>(byte_size);
+  int32_t char_index = truncation_length - 1;
+  const char* data = input.data();
+
+  // Using CBU8, we will move backwards from the truncation point
+  // to the beginning of the string looking for a valid UTF8
+  // character.  Once a full UTF8 character is found, we will
+  // truncate the string to the end of that character.
+  while (char_index >= 0) {
+    int32_t prev = char_index;
+    base_icu::UChar32 code_point = 0;
+    CBU8_NEXT(data, char_index, truncation_length, code_point);
+    if (!IsValidCharacter(code_point) ||
+        !IsValidCodepoint(code_point)) {
+      char_index = prev - 1;
+    } else {
+      break;
+    }
+  }
+
+  if (char_index >= 0 )
+    *output = input.substr(0, char_index);
+  else
+    output->clear();
+}
+
+TrimPositions TrimWhitespace(const string16& input,
+                             TrimPositions positions,
+                             string16* output) {
+  return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
+}
+
+StringPiece16 TrimWhitespace(StringPiece16 input,
+                             TrimPositions positions) {
+  return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
+}
+
+TrimPositions TrimWhitespaceASCII(const std::string& input,
+                                  TrimPositions positions,
+                                  std::string* output) {
+  return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
+}
+
+StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) {
+  return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
+}
+
+template<typename STR>
+STR CollapseWhitespaceT(const STR& text,
+                        bool trim_sequences_with_line_breaks) {
+  STR result;
+  result.resize(text.size());
+
+  // Set flags to pretend we're already in a trimmed whitespace sequence, so we
+  // will trim any leading whitespace.
+  bool in_whitespace = true;
+  bool already_trimmed = true;
+
+  int chars_written = 0;
+  for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
+    if (IsUnicodeWhitespace(*i)) {
+      if (!in_whitespace) {
+        // Reduce all whitespace sequences to a single space.
+        in_whitespace = true;
+        result[chars_written++] = L' ';
+      }
+      if (trim_sequences_with_line_breaks && !already_trimmed &&
+          ((*i == '\n') || (*i == '\r'))) {
+        // Whitespace sequences containing CR or LF are eliminated entirely.
+        already_trimmed = true;
+        --chars_written;
+      }
+    } else {
+      // Non-whitespace chracters are copied straight across.
+      in_whitespace = false;
+      already_trimmed = false;
+      result[chars_written++] = *i;
+    }
+  }
+
+  if (in_whitespace && !already_trimmed) {
+    // Any trailing whitespace is eliminated.
+    --chars_written;
+  }
+
+  result.resize(chars_written);
+  return result;
+}
+
+string16 CollapseWhitespace(const string16& text,
+                            bool trim_sequences_with_line_breaks) {
+  return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+std::string CollapseWhitespaceASCII(const std::string& text,
+                                    bool trim_sequences_with_line_breaks) {
+  return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+bool ContainsOnlyChars(StringPiece input, StringPiece characters) {
+  return input.find_first_not_of(characters) == StringPiece::npos;
+}
+
+bool ContainsOnlyChars(StringPiece16 input, StringPiece16 characters) {
+  return input.find_first_not_of(characters) == StringPiece16::npos;
+}
+
+template <class Char>
+inline bool DoIsStringASCII(const Char* characters, size_t length) {
+  if (!length)
+    return true;
+  constexpr MachineWord non_ascii_bit_mask = NonASCIIMask<Char>::value();
+  MachineWord all_char_bits = 0;
+  const Char* end = characters + length;
+
+  // Prologue: align the input.
+  while (!IsMachineWordAligned(characters) && characters < end)
+    all_char_bits |= *characters++;
+  if (all_char_bits & non_ascii_bit_mask)
+    return false;
+
+  // Compare the values of CPU word size.
+  constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
+  constexpr int batch_count = 16;
+  while (characters <= end - batch_count * chars_per_word) {
+    all_char_bits = 0;
+    for (int i = 0; i < batch_count; ++i) {
+      all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
+      characters += chars_per_word;
+    }
+    if (all_char_bits & non_ascii_bit_mask)
+      return false;
+  }
+
+  // Process the remaining words.
+  all_char_bits = 0;
+  while (characters <= end - chars_per_word) {
+    all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
+    characters += chars_per_word;
+  }
+
+  // Process the remaining bytes.
+  while (characters < end)
+    all_char_bits |= *characters++;
+
+  return !(all_char_bits & non_ascii_bit_mask);
+}
+
+bool IsStringASCII(StringPiece str) {
+  return DoIsStringASCII(str.data(), str.length());
+}
+
+bool IsStringASCII(StringPiece16 str) {
+  return DoIsStringASCII(str.data(), str.length());
+}
+
+#if defined(WCHAR_T_IS_UTF32)
+bool IsStringASCII(WStringPiece str) {
+  return DoIsStringASCII(str.data(), str.length());
+}
+#endif
+
+bool IsStringUTF8(StringPiece str) {
+  const char *src = str.data();
+  int32_t src_len = static_cast<int32_t>(str.length());
+  int32_t char_index = 0;
+
+  while (char_index < src_len) {
+    int32_t code_point;
+    CBU8_NEXT(src, char_index, src_len, code_point);
+    if (!IsValidCharacter(code_point))
+      return false;
+  }
+  return true;
+}
+
+// Implementation note: Normally this function will be called with a hardcoded
+// constant for the lowercase_ascii parameter. Constructing a StringPiece from
+// a C constant requires running strlen, so the result will be two passes
+// through the buffers, one to file the length of lowercase_ascii, and one to
+// compare each letter.
+//
+// This function could have taken a const char* to avoid this and only do one
+// pass through the string. But the strlen is faster than the case-insensitive
+// compares and lets us early-exit in the case that the strings are different
+// lengths (will often be the case for non-matches). So whether one approach or
+// the other will be faster depends on the case.
+//
+// The hardcoded strings are typically very short so it doesn't matter, and the
+// string piece gives additional flexibility for the caller (doesn't have to be
+// null terminated) so we choose the StringPiece route.
+template<typename Str>
+static inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,
+                                          StringPiece lowercase_ascii) {
+  if (str.size() != lowercase_ascii.size())
+    return false;
+  for (size_t i = 0; i < str.size(); i++) {
+    if (ToLowerASCII(str[i]) != lowercase_ascii[i])
+      return false;
+  }
+  return true;
+}
+
+bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) {
+  return DoLowerCaseEqualsASCII<std::string>(str, lowercase_ascii);
+}
+
+bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowercase_ascii) {
+  return DoLowerCaseEqualsASCII<string16>(str, lowercase_ascii);
+}
+
+bool EqualsASCII(StringPiece16 str, StringPiece ascii) {
+  if (str.length() != ascii.length())
+    return false;
+  return std::equal(ascii.begin(), ascii.end(), str.begin());
+}
+
+template<typename Str>
+bool StartsWithT(BasicStringPiece<Str> str,
+                 BasicStringPiece<Str> search_for,
+                 CompareCase case_sensitivity) {
+  if (search_for.size() > str.size())
+    return false;
+
+  BasicStringPiece<Str> source = str.substr(0, search_for.size());
+
+  switch (case_sensitivity) {
+    case CompareCase::SENSITIVE:
+      return source == search_for;
+
+    case CompareCase::INSENSITIVE_ASCII:
+      return std::equal(
+          search_for.begin(), search_for.end(),
+          source.begin(),
+          CaseInsensitiveCompareASCII<typename Str::value_type>());
+
+    default:
+      GURL_NOTREACHED();
+      return false;
+  }
+}
+
+bool StartsWith(StringPiece str,
+                StringPiece search_for,
+                CompareCase case_sensitivity) {
+  return StartsWithT<std::string>(str, search_for, case_sensitivity);
+}
+
+bool StartsWith(StringPiece16 str,
+                StringPiece16 search_for,
+                CompareCase case_sensitivity) {
+  return StartsWithT<string16>(str, search_for, case_sensitivity);
+}
+
+template <typename Str>
+bool EndsWithT(BasicStringPiece<Str> str,
+               BasicStringPiece<Str> search_for,
+               CompareCase case_sensitivity) {
+  if (search_for.size() > str.size())
+    return false;
+
+  BasicStringPiece<Str> source = str.substr(str.size() - search_for.size(),
+                                            search_for.size());
+
+  switch (case_sensitivity) {
+    case CompareCase::SENSITIVE:
+      return source == search_for;
+
+    case CompareCase::INSENSITIVE_ASCII:
+      return std::equal(
+          source.begin(), source.end(),
+          search_for.begin(),
+          CaseInsensitiveCompareASCII<typename Str::value_type>());
+
+    default:
+      GURL_NOTREACHED();
+      return false;
+  }
+}
+
+bool EndsWith(StringPiece str,
+              StringPiece search_for,
+              CompareCase case_sensitivity) {
+  return EndsWithT<std::string>(str, search_for, case_sensitivity);
+}
+
+bool EndsWith(StringPiece16 str,
+              StringPiece16 search_for,
+              CompareCase case_sensitivity) {
+  return EndsWithT<string16>(str, search_for, case_sensitivity);
+}
+
+char HexDigitToInt(wchar_t c) {
+  GURL_DCHECK(IsHexDigit(c));
+  if (c >= '0' && c <= '9')
+    return static_cast<char>(c - '0');
+  if (c >= 'A' && c <= 'F')
+    return static_cast<char>(c - 'A' + 10);
+  if (c >= 'a' && c <= 'f')
+    return static_cast<char>(c - 'a' + 10);
+  return 0;
+}
+
+bool IsUnicodeWhitespace(wchar_t c) {
+  // kWhitespaceWide is a NULL-terminated string
+  for (const wchar_t* cur = kWhitespaceWide; *cur; ++cur) {
+    if (*cur == c)
+      return true;
+  }
+  return false;
+}
+
+static const char* const kByteStringsUnlocalized[] = {
+  " B",
+  " kB",
+  " MB",
+  " GB",
+  " TB",
+  " PB"
+};
+
+string16 FormatBytesUnlocalized(int64_t bytes) {
+  double unit_amount = static_cast<double>(bytes);
+  size_t dimension = 0;
+  const int kKilo = 1024;
+  while (unit_amount >= kKilo &&
+         dimension < gurl_base::size(kByteStringsUnlocalized) - 1) {
+    unit_amount /= kKilo;
+    dimension++;
+  }
+
+  char buf[64];
+  if (bytes != 0 && dimension > 0 && unit_amount < 100) {
+    gurl_base::snprintf(buf, gurl_base::size(buf), "%.1lf%s", unit_amount,
+                   kByteStringsUnlocalized[dimension]);
+  } else {
+    gurl_base::snprintf(buf, gurl_base::size(buf), "%.0lf%s", unit_amount,
+                   kByteStringsUnlocalized[dimension]);
+  }
+
+  return ASCIIToUTF16(buf);
+}
+
+// A Matcher for DoReplaceMatchesAfterOffset() that matches substrings.
+template <class StringType>
+struct SubstringMatcher {
+  BasicStringPiece<StringType> find_this;
+
+  size_t Find(const StringType& input, size_t pos) {
+    return input.find(find_this.data(), pos, find_this.length());
+  }
+  size_t MatchSize() { return find_this.length(); }
+};
+
+// A Matcher for DoReplaceMatchesAfterOffset() that matches single characters.
+template <class StringType>
+struct CharacterMatcher {
+  BasicStringPiece<StringType> find_any_of_these;
+
+  size_t Find(const StringType& input, size_t pos) {
+    return input.find_first_of(find_any_of_these.data(), pos,
+                               find_any_of_these.length());
+  }
+  constexpr size_t MatchSize() { return 1; }
+};
+
+enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST };
+
+// Runs in O(n) time in the length of |str|, and transforms the string without
+// reallocating when possible. Returns |true| if any matches were found.
+//
+// This is parameterized on a |Matcher| traits type, so that it can be the
+// implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset().
+template <class StringType, class Matcher>
+bool DoReplaceMatchesAfterOffset(StringType* str,
+                                 size_t initial_offset,
+                                 Matcher matcher,
+                                 BasicStringPiece<StringType> replace_with,
+                                 ReplaceType replace_type) {
+  using CharTraits = typename StringType::traits_type;
+
+  const size_t find_length = matcher.MatchSize();
+  if (!find_length)
+    return false;
+
+  // If the find string doesn't appear, there's nothing to do.
+  size_t first_match = matcher.Find(*str, initial_offset);
+  if (first_match == StringType::npos)
+    return false;
+
+  // If we're only replacing one instance, there's no need to do anything
+  // complicated.
+  const size_t replace_length = replace_with.length();
+  if (replace_type == ReplaceType::REPLACE_FIRST) {
+    str->replace(first_match, find_length, replace_with.data(), replace_length);
+    return true;
+  }
+
+  // If the find and replace strings are the same length, we can simply use
+  // replace() on each instance, and finish the entire operation in O(n) time.
+  if (find_length == replace_length) {
+    auto* buffer = &((*str)[0]);
+    for (size_t offset = first_match; offset != StringType::npos;
+         offset = matcher.Find(*str, offset + replace_length)) {
+      CharTraits::copy(buffer + offset, replace_with.data(), replace_length);
+    }
+    return true;
+  }
+
+  // Since the find and replace strings aren't the same length, a loop like the
+  // one above would be O(n^2) in the worst case, as replace() will shift the
+  // entire remaining string each time. We need to be more clever to keep things
+  // O(n).
+  //
+  // When the string is being shortened, it's possible to just shift the matches
+  // down in one pass while finding, and truncate the length at the end of the
+  // search.
+  //
+  // If the string is being lengthened, more work is required. The strategy used
+  // here is to make two find() passes through the string. The first pass counts
+  // the number of matches to determine the new size. The second pass will
+  // either construct the new string into a new buffer (if the existing buffer
+  // lacked capacity), or else -- if there is room -- create a region of scratch
+  // space after |first_match| by shifting the tail of the string to a higher
+  // index, and doing in-place moves from the tail to lower indices thereafter.
+  size_t str_length = str->length();
+  size_t expansion = 0;
+  if (replace_length > find_length) {
+    // This operation lengthens the string; determine the new length by counting
+    // matches.
+    const size_t expansion_per_match = (replace_length - find_length);
+    size_t num_matches = 0;
+    for (size_t match = first_match; match != StringType::npos;
+         match = matcher.Find(*str, match + find_length)) {
+      expansion += expansion_per_match;
+      ++num_matches;
+    }
+    const size_t final_length = str_length + expansion;
+
+    if (str->capacity() < final_length) {
+      // If we'd have to allocate a new buffer to grow the string, build the
+      // result directly into the new allocation via append().
+      StringType src(str->get_allocator());
+      str->swap(src);
+      str->reserve(final_length);
+
+      size_t pos = 0;
+      for (size_t match = first_match;; match = matcher.Find(src, pos)) {
+        str->append(src, pos, match - pos);
+        str->append(replace_with.data(), replace_length);
+        pos = match + find_length;
+
+        // A mid-loop test/break enables skipping the final Find() call; the
+        // number of matches is known, so don't search past the last one.
+        if (!--num_matches)
+          break;
+      }
+
+      // Handle substring after the final match.
+      str->append(src, pos, str_length - pos);
+      return true;
+    }
+
+    // Prepare for the copy/move loop below -- expand the string to its final
+    // size by shifting the data after the first match to the end of the resized
+    // string.
+    size_t shift_src = first_match + find_length;
+    size_t shift_dst = shift_src + expansion;
+
+    // Big |expansion| factors (relative to |str_length|) require padding up to
+    // |shift_dst|.
+    if (shift_dst > str_length)
+      str->resize(shift_dst);
+
+    str->replace(shift_dst, str_length - shift_src, *str, shift_src,
+                 str_length - shift_src);
+    str_length = final_length;
+  }
+
+  // We can alternate replacement and move operations. This won't overwrite the
+  // unsearched region of the string so long as |write_offset| <= |read_offset|;
+  // that condition is always satisfied because:
+  //
+  //   (a) If the string is being shortened, |expansion| is zero and
+  //       |write_offset| grows slower than |read_offset|.
+  //
+  //   (b) If the string is being lengthened, |write_offset| grows faster than
+  //       |read_offset|, but |expansion| is big enough so that |write_offset|
+  //       will only catch up to |read_offset| at the point of the last match.
+  auto* buffer = &((*str)[0]);
+  size_t write_offset = first_match;
+  size_t read_offset = first_match + expansion;
+  do {
+    if (replace_length) {
+      CharTraits::copy(buffer + write_offset, replace_with.data(),
+                       replace_length);
+      write_offset += replace_length;
+    }
+    read_offset += find_length;
+
+    // min() clamps StringType::npos (the largest unsigned value) to str_length.
+    size_t match = std::min(matcher.Find(*str, read_offset), str_length);
+
+    size_t length = match - read_offset;
+    if (length) {
+      CharTraits::move(buffer + write_offset, buffer + read_offset, length);
+      write_offset += length;
+      read_offset += length;
+    }
+  } while (read_offset < str_length);
+
+  // If we're shortening the string, truncate it now.
+  str->resize(write_offset);
+  return true;
+}
+
+template <class StringType>
+bool ReplaceCharsT(const StringType& input,
+                   BasicStringPiece<StringType> find_any_of_these,
+                   BasicStringPiece<StringType> replace_with,
+                   StringType* output) {
+  // Commonly, this is called with output and input being the same string; in
+  // that case, this assignment is inexpensive.
+  *output = input;
+
+  return DoReplaceMatchesAfterOffset(
+      output, 0, CharacterMatcher<StringType>{find_any_of_these}, replace_with,
+      ReplaceType::REPLACE_ALL);
+}
+
+void ReplaceFirstSubstringAfterOffset(string16* str,
+                                      size_t start_offset,
+                                      StringPiece16 find_this,
+                                      StringPiece16 replace_with) {
+  DoReplaceMatchesAfterOffset(str, start_offset,
+                              SubstringMatcher<string16>{find_this},
+                              replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceFirstSubstringAfterOffset(std::string* str,
+                                      size_t start_offset,
+                                      StringPiece find_this,
+                                      StringPiece replace_with) {
+  DoReplaceMatchesAfterOffset(str, start_offset,
+                              SubstringMatcher<std::string>{find_this},
+                              replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceSubstringsAfterOffset(string16* str,
+                                  size_t start_offset,
+                                  StringPiece16 find_this,
+                                  StringPiece16 replace_with) {
+  DoReplaceMatchesAfterOffset(str, start_offset,
+                              SubstringMatcher<string16>{find_this},
+                              replace_with, ReplaceType::REPLACE_ALL);
+}
+
+void ReplaceSubstringsAfterOffset(std::string* str,
+                                  size_t start_offset,
+                                  StringPiece find_this,
+                                  StringPiece replace_with) {
+  DoReplaceMatchesAfterOffset(str, start_offset,
+                              SubstringMatcher<std::string>{find_this},
+                              replace_with, ReplaceType::REPLACE_ALL);
+}
+
+template <class string_type>
+inline typename string_type::value_type* WriteIntoT(string_type* str,
+                                                    size_t length_with_null) {
+  GURL_DCHECK_GT(length_with_null, 1u);
+  str->reserve(length_with_null);
+  str->resize(length_with_null - 1);
+  return &((*str)[0]);
+}
+
+char* WriteInto(std::string* str, size_t length_with_null) {
+  return WriteIntoT(str, length_with_null);
+}
+
+char16* WriteInto(string16* str, size_t length_with_null) {
+  return WriteIntoT(str, length_with_null);
+}
+
+#if defined(_MSC_VER) && !defined(__clang__)
+// Work around VC++ code-gen bug. https://crbug.com/804884
+#pragma optimize("", off)
+#endif
+
+// Generic version for all JoinString overloads. |list_type| must be a sequence
+// (std::vector or std::initializer_list) of strings/StringPieces (std::string,
+// string16, StringPiece or StringPiece16). |string_type| is either std::string
+// or string16.
+template <typename list_type, typename string_type>
+static string_type JoinStringT(const list_type& parts,
+                               BasicStringPiece<string_type> sep) {
+  if (parts.size() == 0)
+    return string_type();
+
+  // Pre-allocate the eventual size of the string. Start with the size of all of
+  // the separators (note that this *assumes* parts.size() > 0).
+  size_t total_size = (parts.size() - 1) * sep.size();
+  for (const auto& part : parts)
+    total_size += part.size();
+  string_type result;
+  result.reserve(total_size);
+
+  auto iter = parts.begin();
+  GURL_DCHECK(iter != parts.end());
+  AppendToString(&result, *iter);
+  ++iter;
+
+  for (; iter != parts.end(); ++iter) {
+    sep.AppendToString(&result);
+    // Using the overloaded AppendToString allows this template function to work
+    // on both strings and StringPieces without creating an intermediate
+    // StringPiece object.
+    AppendToString(&result, *iter);
+  }
+
+  // Sanity-check that we pre-allocated correctly.
+  GURL_DCHECK_EQ(total_size, result.size());
+
+  return result;
+}
+
+std::string JoinString(const std::vector<std::string>& parts,
+                       StringPiece separator) {
+  return JoinStringT(parts, separator);
+}
+
+string16 JoinString(const std::vector<string16>& parts,
+                    StringPiece16 separator) {
+  return JoinStringT(parts, separator);
+}
+
+#if defined(_MSC_VER) && !defined(__clang__)
+// Work around VC++ code-gen bug. https://crbug.com/804884
+#pragma optimize("", on)
+#endif
+
+std::string JoinString(const std::vector<StringPiece>& parts,
+                       StringPiece separator) {
+  return JoinStringT(parts, separator);
+}
+
+string16 JoinString(const std::vector<StringPiece16>& parts,
+                    StringPiece16 separator) {
+  return JoinStringT(parts, separator);
+}
+
+std::string JoinString(std::initializer_list<StringPiece> parts,
+                       StringPiece separator) {
+  return JoinStringT(parts, separator);
+}
+
+string16 JoinString(std::initializer_list<StringPiece16> parts,
+                    StringPiece16 separator) {
+  return JoinStringT(parts, separator);
+}
+
+template<class FormatStringType, class OutStringType>
+OutStringType DoReplaceStringPlaceholders(
+    const FormatStringType& format_string,
+    const std::vector<OutStringType>& subst,
+    std::vector<size_t>* offsets) {
+  size_t substitutions = subst.size();
+  GURL_DCHECK_LT(substitutions, 10U);
+
+  size_t sub_length = 0;
+  for (const auto& cur : subst)
+    sub_length += cur.length();
+
+  OutStringType formatted;
+  formatted.reserve(format_string.length() + sub_length);
+
+  std::vector<ReplacementOffset> r_offsets;
+  for (auto i = format_string.begin(); i != format_string.end(); ++i) {
+    if ('$' == *i) {
+      if (i + 1 != format_string.end()) {
+        ++i;
+        if ('$' == *i) {
+          while (i != format_string.end() && '$' == *i) {
+            formatted.push_back('$');
+            ++i;
+          }
+          --i;
+        } else {
+          if (*i < '1' || *i > '9') {
+            GURL_DLOG(ERROR) << "Invalid placeholder: $" << *i;
+            continue;
+          }
+          uintptr_t index = *i - '1';
+          if (offsets) {
+            ReplacementOffset r_offset(index,
+                                       static_cast<int>(formatted.size()));
+            r_offsets.insert(
+                std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
+                                 &CompareParameter),
+                r_offset);
+          }
+          if (index < substitutions)
+            formatted.append(subst.at(index));
+        }
+      }
+    } else {
+      formatted.push_back(*i);
+    }
+  }
+  if (offsets) {
+    for (const auto& cur : r_offsets)
+      offsets->push_back(cur.offset);
+  }
+  return formatted;
+}
+
+string16 ReplaceStringPlaceholders(const string16& format_string,
+                                   const std::vector<string16>& subst,
+                                   std::vector<size_t>* offsets) {
+  return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+std::string ReplaceStringPlaceholders(StringPiece format_string,
+                                      const std::vector<std::string>& subst,
+                                      std::vector<size_t>* offsets) {
+  return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+string16 ReplaceStringPlaceholders(const string16& format_string,
+                                   const string16& a,
+                                   size_t* offset) {
+  std::vector<size_t> offsets;
+  std::vector<string16> subst;
+  subst.push_back(a);
+  string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
+
+  GURL_DCHECK_EQ(1U, offsets.size());
+  if (offset)
+    *offset = offsets[0];
+  return result;
+}
+
+// The following code is compatible with the OpenBSD lcpy interface.  See:
+//   http://www.gratisoft.us/todd/papers/strlcpy.html
+//   ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
+
+namespace {
+
+template <typename CHAR>
+size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
+  for (size_t i = 0; i < dst_size; ++i) {
+    if ((dst[i] = src[i]) == 0)  // We hit and copied the terminating NULL.
+      return i;
+  }
+
+  // We were left off at dst_size.  We over copied 1 byte.  Null terminate.
+  if (dst_size != 0)
+    dst[dst_size - 1] = 0;
+
+  // Count the rest of the |src|, and return it's length in characters.
+  while (src[dst_size]) ++dst_size;
+  return dst_size;
+}
+
+}  // namespace
+
+size_t strlcpy(char* dst, const char* src, size_t dst_size) {
+  return lcpyT<char>(dst, src, dst_size);
+}
+size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
+  return lcpyT<wchar_t>(dst, src, dst_size);
+}
+
+}  // namespace base
diff --git a/base/strings/string_util.h b/base/strings/string_util.h
new file mode 100644
index 0000000..5a8cb02
--- /dev/null
+++ b/base/strings/string_util.h
@@ -0,0 +1,530 @@
+// Copyright 2013 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.
+//
+// This file defines utility functions for working with strings.
+
+#ifndef BASE_STRINGS_STRING_UTIL_H_
+#define BASE_STRINGS_STRING_UTIL_H_
+
+#include <ctype.h>
+#include <stdarg.h>   // va_list
+#include <stddef.h>
+#include <stdint.h>
+
+#include <initializer_list>
+#include <string>
+#include <vector>
+
+#include "polyfills/base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"  // For implicit conversions.
+#include "build/build_config.h"
+
+namespace gurl_base {
+
+// C standard-library functions that aren't cross-platform are provided as
+// "gurl_base::...", and their prototypes are listed below. These functions are
+// then implemented as inline calls to the platform-specific equivalents in the
+// platform-specific headers.
+
+// Wrapper for vsnprintf that always null-terminates and always returns the
+// number of characters that would be in an untruncated formatted
+// string, even when truncation occurs.
+int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments)
+    PRINTF_FORMAT(3, 0);
+
+// Some of these implementations need to be inlined.
+
+// We separate the declaration from the implementation of this inline
+// function just so the PRINTF_FORMAT works.
+inline int snprintf(char* buffer, size_t size, const char* format, ...)
+    PRINTF_FORMAT(3, 4);
+inline int snprintf(char* buffer, size_t size, const char* format, ...) {
+  va_list arguments;
+  va_start(arguments, format);
+  int result = vsnprintf(buffer, size, format, arguments);
+  va_end(arguments);
+  return result;
+}
+
+// BSD-style safe and consistent string copy functions.
+// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
+// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
+// long as |dst_size| is not 0.  Returns the length of |src| in characters.
+// If the return value is >= dst_size, then the output was truncated.
+// NOTE: All sizes are in number of characters, NOT in bytes.
+BASE_EXPORT size_t strlcpy(char* dst, const char* src, size_t dst_size);
+BASE_EXPORT size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size);
+
+// Scan a wprintf format string to determine whether it's portable across a
+// variety of systems.  This function only checks that the conversion
+// specifiers used by the format string are supported and have the same meaning
+// on a variety of systems.  It doesn't check for other errors that might occur
+// within a format string.
+//
+// Nonportable conversion specifiers for wprintf are:
+//  - 's' and 'c' without an 'l' length modifier.  %s and %c operate on char
+//     data on all systems except Windows, which treat them as wchar_t data.
+//     Use %ls and %lc for wchar_t data instead.
+//  - 'S' and 'C', which operate on wchar_t data on all systems except Windows,
+//     which treat them as char data.  Use %ls and %lc for wchar_t data
+//     instead.
+//  - 'F', which is not identified by Windows wprintf documentation.
+//  - 'D', 'O', and 'U', which are deprecated and not available on all systems.
+//     Use %ld, %lo, and %lu instead.
+//
+// Note that there is no portable conversion specifier for char data when
+// working with wprintf.
+//
+// This function is intended to be called from gurl_base::vswprintf.
+BASE_EXPORT bool IsWprintfFormatPortable(const wchar_t* format);
+
+// ASCII-specific tolower.  The standard library's tolower is locale sensitive,
+// so we don't want to use it here.
+inline char ToLowerASCII(char c) {
+  return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+inline char16 ToLowerASCII(char16 c) {
+  return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+
+// ASCII-specific toupper.  The standard library's toupper is locale sensitive,
+// so we don't want to use it here.
+inline char ToUpperASCII(char c) {
+  return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+}
+inline char16 ToUpperASCII(char16 c) {
+  return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+}
+
+// Converts the given string to it's ASCII-lowercase equivalent.
+BASE_EXPORT std::string ToLowerASCII(StringPiece str);
+BASE_EXPORT string16 ToLowerASCII(StringPiece16 str);
+
+// Converts the given string to it's ASCII-uppercase equivalent.
+BASE_EXPORT std::string ToUpperASCII(StringPiece str);
+BASE_EXPORT string16 ToUpperASCII(StringPiece16 str);
+
+// Functor for case-insensitive ASCII comparisons for STL algorithms like
+// std::search.
+//
+// Note that a full Unicode version of this functor is not possible to write
+// because case mappings might change the number of characters, depend on
+// context (combining accents), and require handling UTF-16. If you need
+// proper Unicode support, use gurl_base::i18n::ToLower/FoldCase and then just
+// use a normal operator== on the result.
+template<typename Char> struct CaseInsensitiveCompareASCII {
+ public:
+  bool operator()(Char x, Char y) const {
+    return ToLowerASCII(x) == ToLowerASCII(y);
+  }
+};
+
+// Like strcasecmp for case-insensitive ASCII characters only. Returns:
+//   -1  (a < b)
+//    0  (a == b)
+//    1  (a > b)
+// (unlike strcasecmp which can return values greater or less than 1/-1). For
+// full Unicode support, use gurl_base::i18n::ToLower or gurl_base::i18h::FoldCase
+// and then just call the normal string operators on the result.
+BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b);
+BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b);
+
+// Equality for ASCII case-insensitive comparisons. For full Unicode support,
+// use gurl_base::i18n::ToLower or gurl_base::i18h::FoldCase and then compare with either
+// == or !=.
+BASE_EXPORT bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b);
+BASE_EXPORT bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b);
+
+// These threadsafe functions return references to globally unique empty
+// strings.
+//
+// It is likely faster to construct a new empty string object (just a few
+// instructions to set the length to 0) than to get the empty string instance
+// returned by these functions (which requires threadsafe static access).
+//
+// Therefore, DO NOT USE THESE AS A GENERAL-PURPOSE SUBSTITUTE FOR DEFAULT
+// CONSTRUCTORS. There is only one case where you should use these: functions
+// which need to return a string by reference (e.g. as a class member
+// accessor), and don't have an empty string to use (e.g. in an error case).
+// These should not be used as initializers, function arguments, or return
+// values for functions which return by value or outparam.
+BASE_EXPORT const std::string& EmptyString();
+BASE_EXPORT const string16& EmptyString16();
+
+// Contains the set of characters representing whitespace in the corresponding
+// encoding. Null-terminated. The ASCII versions are the whitespaces as defined
+// by HTML5, and don't include control characters.
+BASE_EXPORT extern const wchar_t kWhitespaceWide[];  // Includes Unicode.
+BASE_EXPORT extern const char16 kWhitespaceUTF16[];  // Includes Unicode.
+BASE_EXPORT extern const char kWhitespaceASCII[];
+BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[];  // No unicode.
+
+// Null-terminated string representing the UTF-8 byte order mark.
+BASE_EXPORT extern const char kUtf8ByteOrderMark[];
+
+// Removes characters in |remove_chars| from anywhere in |input|.  Returns true
+// if any characters were removed.  |remove_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+BASE_EXPORT bool RemoveChars(const string16& input,
+                             StringPiece16 remove_chars,
+                             string16* output);
+BASE_EXPORT bool RemoveChars(const std::string& input,
+                             StringPiece remove_chars,
+                             std::string* output);
+
+// Replaces characters in |replace_chars| from anywhere in |input| with
+// |replace_with|.  Each character in |replace_chars| will be replaced with
+// the |replace_with| string.  Returns true if any characters were replaced.
+// |replace_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+BASE_EXPORT bool ReplaceChars(const string16& input,
+                              StringPiece16 replace_chars,
+                              const string16& replace_with,
+                              string16* output);
+BASE_EXPORT bool ReplaceChars(const std::string& input,
+                              StringPiece replace_chars,
+                              const std::string& replace_with,
+                              std::string* output);
+
+enum TrimPositions {
+  TRIM_NONE     = 0,
+  TRIM_LEADING  = 1 << 0,
+  TRIM_TRAILING = 1 << 1,
+  TRIM_ALL      = TRIM_LEADING | TRIM_TRAILING,
+};
+
+// Removes characters in |trim_chars| from the beginning and end of |input|.
+// The 8-bit version only works on 8-bit characters, not UTF-8. Returns true if
+// any characters were removed.
+//
+// It is safe to use the same variable for both |input| and |output| (this is
+// the normal usage to trim in-place).
+BASE_EXPORT bool TrimString(const string16& input,
+                            StringPiece16 trim_chars,
+                            string16* output);
+BASE_EXPORT bool TrimString(const std::string& input,
+                            StringPiece trim_chars,
+                            std::string* output);
+
+// StringPiece versions of the above. The returned pieces refer to the original
+// buffer.
+BASE_EXPORT StringPiece16 TrimString(StringPiece16 input,
+                                     StringPiece16 trim_chars,
+                                     TrimPositions positions);
+BASE_EXPORT StringPiece TrimString(StringPiece input,
+                                   StringPiece trim_chars,
+                                   TrimPositions positions);
+
+// Truncates a string to the nearest UTF-8 character that will leave
+// the string less than or equal to the specified byte size.
+BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
+                                        const size_t byte_size,
+                                        std::string* output);
+
+#if defined(WCHAR_T_IS_UTF16)
+// Utility functions to access the underlying string buffer as a wide char
+// pointer.
+//
+// Note: These functions violate strict aliasing when char16 and wchar_t are
+// unrelated types. We thus pass -fno-strict-aliasing to the compiler on
+// non-Windows platforms [1], and rely on it being off in Clang's CL mode [2].
+//
+// [1] https://crrev.com/b9a0976622/build/config/compiler/BUILD.gn#244
+// [2]
+// https://github.com/llvm/llvm-project/blob/1e28a66/clang/lib/Driver/ToolChains/Clang.cpp#L3949
+inline wchar_t* as_writable_wcstr(char16* str) {
+  return reinterpret_cast<wchar_t*>(str);
+}
+
+inline wchar_t* as_writable_wcstr(string16& str) {
+  return reinterpret_cast<wchar_t*>(data(str));
+}
+
+inline const wchar_t* as_wcstr(const char16* str) {
+  return reinterpret_cast<const wchar_t*>(str);
+}
+
+inline const wchar_t* as_wcstr(StringPiece16 str) {
+  return reinterpret_cast<const wchar_t*>(str.data());
+}
+
+// Utility functions to access the underlying string buffer as a char16 pointer.
+inline char16* as_writable_u16cstr(wchar_t* str) {
+  return reinterpret_cast<char16*>(str);
+}
+
+inline char16* as_writable_u16cstr(std::wstring& str) {
+  return reinterpret_cast<char16*>(data(str));
+}
+
+inline const char16* as_u16cstr(const wchar_t* str) {
+  return reinterpret_cast<const char16*>(str);
+}
+
+inline const char16* as_u16cstr(WStringPiece str) {
+  return reinterpret_cast<const char16*>(str.data());
+}
+#endif  // defined(WCHAR_T_IS_UTF16)
+
+// Trims any whitespace from either end of the input string.
+//
+// The StringPiece versions return a substring referencing the input buffer.
+// The ASCII versions look only for ASCII whitespace.
+//
+// The std::string versions return where whitespace was found.
+// NOTE: Safe to use the same variable for both input and output.
+BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
+                                         TrimPositions positions,
+                                         string16* output);
+BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
+                                         TrimPositions positions);
+BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
+                                              TrimPositions positions,
+                                              std::string* output);
+BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
+                                            TrimPositions positions);
+
+// Searches for CR or LF characters.  Removes all contiguous whitespace
+// strings that contain them.  This is useful when trying to deal with text
+// copied from terminals.
+// Returns |text|, with the following three transformations:
+// (1) Leading and trailing whitespace is trimmed.
+// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
+//     sequences containing a CR or LF are trimmed.
+// (3) All other whitespace sequences are converted to single spaces.
+BASE_EXPORT string16 CollapseWhitespace(
+    const string16& text,
+    bool trim_sequences_with_line_breaks);
+BASE_EXPORT std::string CollapseWhitespaceASCII(
+    const std::string& text,
+    bool trim_sequences_with_line_breaks);
+
+// Returns true if |input| is empty or contains only characters found in
+// |characters|.
+BASE_EXPORT bool ContainsOnlyChars(StringPiece input, StringPiece characters);
+BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input,
+                                   StringPiece16 characters);
+
+// Returns true if the specified string matches the criteria. How can a wide
+// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
+// first case) or characters that use only 8-bits and whose 8-bit
+// representation looks like a UTF-8 string (the second case).
+//
+// Note that IsStringUTF8 checks not only if the input is structurally
+// valid but also if it doesn't contain any non-character codepoint
+// (e.g. U+FFFE). It's done on purpose because all the existing callers want
+// to have the maximum 'discriminating' power from other encodings. If
+// there's a use case for just checking the structural validity, we have to
+// add a new function for that.
+//
+// IsStringASCII assumes the input is likely all ASCII, and does not leave early
+// if it is not the case.
+BASE_EXPORT bool IsStringUTF8(StringPiece str);
+BASE_EXPORT bool IsStringASCII(StringPiece str);
+BASE_EXPORT bool IsStringASCII(StringPiece16 str);
+#if defined(WCHAR_T_IS_UTF32)
+BASE_EXPORT bool IsStringASCII(WStringPiece str);
+#endif
+
+// Compare the lower-case form of the given string against the given
+// previously-lower-cased ASCII string (typically a constant).
+BASE_EXPORT bool LowerCaseEqualsASCII(StringPiece str,
+                                      StringPiece lowecase_ascii);
+BASE_EXPORT bool LowerCaseEqualsASCII(StringPiece16 str,
+                                      StringPiece lowecase_ascii);
+
+// Performs a case-sensitive string compare of the given 16-bit string against
+// the given 8-bit ASCII string (typically a constant). The behavior is
+// undefined if the |ascii| string is not ASCII.
+BASE_EXPORT bool EqualsASCII(StringPiece16 str, StringPiece ascii);
+
+// Indicates case sensitivity of comparisons. Only ASCII case insensitivity
+// is supported. Full Unicode case-insensitive conversions would need to go in
+// base/i18n so it can use ICU.
+//
+// If you need to do Unicode-aware case-insensitive StartsWith/EndsWith, it's
+// best to call gurl_base::i18n::ToLower() or gurl_base::i18n::FoldCase() (see
+// base/i18n/case_conversion.h for usage advice) on the arguments, and then use
+// the results to a case-sensitive comparison.
+enum class CompareCase {
+  SENSITIVE,
+  INSENSITIVE_ASCII,
+};
+
+BASE_EXPORT bool StartsWith(StringPiece str,
+                            StringPiece search_for,
+                            CompareCase case_sensitivity);
+BASE_EXPORT bool StartsWith(StringPiece16 str,
+                            StringPiece16 search_for,
+                            CompareCase case_sensitivity);
+BASE_EXPORT bool EndsWith(StringPiece str,
+                          StringPiece search_for,
+                          CompareCase case_sensitivity);
+BASE_EXPORT bool EndsWith(StringPiece16 str,
+                          StringPiece16 search_for,
+                          CompareCase case_sensitivity);
+
+// Determines the type of ASCII character, independent of locale (the C
+// library versions will change based on locale).
+template <typename Char>
+inline bool IsAsciiWhitespace(Char c) {
+  return c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\f';
+}
+template <typename Char>
+inline bool IsAsciiAlpha(Char c) {
+  return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+template <typename Char>
+inline bool IsAsciiUpper(Char c) {
+  return c >= 'A' && c <= 'Z';
+}
+template <typename Char>
+inline bool IsAsciiLower(Char c) {
+  return c >= 'a' && c <= 'z';
+}
+template <typename Char>
+inline bool IsAsciiDigit(Char c) {
+  return c >= '0' && c <= '9';
+}
+template <typename Char>
+inline bool IsAsciiPrintable(Char c) {
+  return c >= ' ' && c <= '~';
+}
+
+template <typename Char>
+inline bool IsHexDigit(Char c) {
+  return (c >= '0' && c <= '9') ||
+         (c >= 'A' && c <= 'F') ||
+         (c >= 'a' && c <= 'f');
+}
+
+// Returns the integer corresponding to the given hex character. For example:
+//    '4' -> 4
+//    'a' -> 10
+//    'B' -> 11
+// Assumes the input is a valid hex character. DCHECKs in debug builds if not.
+BASE_EXPORT char HexDigitToInt(wchar_t c);
+
+// Returns true if it's a Unicode whitespace character.
+BASE_EXPORT bool IsUnicodeWhitespace(wchar_t c);
+
+// Return a byte string in human-readable format with a unit suffix. Not
+// appropriate for use in any UI; use of FormatBytes and friends in ui/base is
+// highly recommended instead. TODO(avi): Figure out how to get callers to use
+// FormatBytes instead; remove this.
+BASE_EXPORT string16 FormatBytesUnlocalized(int64_t bytes);
+
+// Starting at |start_offset| (usually 0), replace the first instance of
+// |find_this| with |replace_with|.
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
+    gurl_base::string16* str,
+    size_t start_offset,
+    StringPiece16 find_this,
+    StringPiece16 replace_with);
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
+    std::string* str,
+    size_t start_offset,
+    StringPiece find_this,
+    StringPiece replace_with);
+
+// Starting at |start_offset| (usually 0), look through |str| and replace all
+// instances of |find_this| with |replace_with|.
+//
+// This does entire substrings; use std::replace in <algorithm> for single
+// characters, for example:
+//   std::replace(str.begin(), str.end(), 'a', 'b');
+BASE_EXPORT void ReplaceSubstringsAfterOffset(
+    string16* str,
+    size_t start_offset,
+    StringPiece16 find_this,
+    StringPiece16 replace_with);
+BASE_EXPORT void ReplaceSubstringsAfterOffset(
+    std::string* str,
+    size_t start_offset,
+    StringPiece find_this,
+    StringPiece replace_with);
+
+// Reserves enough memory in |str| to accommodate |length_with_null| characters,
+// sets the size of |str| to |length_with_null - 1| characters, and returns a
+// pointer to the underlying contiguous array of characters.  This is typically
+// used when calling a function that writes results into a character array, but
+// the caller wants the data to be managed by a string-like object.  It is
+// convenient in that is can be used inline in the call, and fast in that it
+// avoids copying the results of the call from a char* into a string.
+//
+// |length_with_null| must be at least 2, since otherwise the underlying string
+// would have size 0, and trying to access &((*str)[0]) in that case can result
+// in a number of problems.
+//
+// Internally, this takes linear time because the resize() call 0-fills the
+// underlying array for potentially all
+// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes.  Ideally we
+// could avoid this aspect of the resize() call, as we expect the caller to
+// immediately write over this memory, but there is no other way to set the size
+// of the string, and not doing that will mean people who access |str| rather
+// than str.c_str() will get back a string of whatever size |str| had on entry
+// to this function (probably 0).
+BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
+BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
+
+// Does the opposite of SplitString()/SplitStringPiece(). Joins a vector or list
+// of strings into a single string, inserting |separator| (which may be empty)
+// in between all elements.
+//
+// If possible, callers should build a vector of StringPieces and use the
+// StringPiece variant, so that they do not create unnecessary copies of
+// strings. For example, instead of using SplitString, modifying the vector,
+// then using JoinString, use SplitStringPiece followed by JoinString so that no
+// copies of those strings are created until the final join operation.
+//
+// Use StrCat (in base/strings/strcat.h) if you don't need a separator.
+BASE_EXPORT std::string JoinString(const std::vector<std::string>& parts,
+                                   StringPiece separator);
+BASE_EXPORT string16 JoinString(const std::vector<string16>& parts,
+                                StringPiece16 separator);
+BASE_EXPORT std::string JoinString(const std::vector<StringPiece>& parts,
+                                   StringPiece separator);
+BASE_EXPORT string16 JoinString(const std::vector<StringPiece16>& parts,
+                                StringPiece16 separator);
+// Explicit initializer_list overloads are required to break ambiguity when used
+// with a literal initializer list (otherwise the compiler would not be able to
+// decide between the string and StringPiece overloads).
+BASE_EXPORT std::string JoinString(std::initializer_list<StringPiece> parts,
+                                   StringPiece separator);
+BASE_EXPORT string16 JoinString(std::initializer_list<StringPiece16> parts,
+                                StringPiece16 separator);
+
+// Replace $1-$2-$3..$9 in the format string with values from |subst|.
+// Additionally, any number of consecutive '$' characters is replaced by that
+// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
+// NULL. This only allows you to use up to nine replacements.
+BASE_EXPORT string16 ReplaceStringPlaceholders(
+    const string16& format_string,
+    const std::vector<string16>& subst,
+    std::vector<size_t>* offsets);
+
+BASE_EXPORT std::string ReplaceStringPlaceholders(
+    StringPiece format_string,
+    const std::vector<std::string>& subst,
+    std::vector<size_t>* offsets);
+
+// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
+BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
+                                               const string16& a,
+                                               size_t* offset);
+
+}  // namespace base
+
+#if defined(OS_WIN)
+#include "base/strings/string_util_win.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "base/strings/string_util_posix.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
+
+#endif  // BASE_STRINGS_STRING_UTIL_H_
diff --git a/base/strings/string_util_constants.cc b/base/strings/string_util_constants.cc
new file mode 100644
index 0000000..3ca29b7
--- /dev/null
+++ b/base/strings/string_util_constants.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 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 "base/strings/string_util.h"
+
+namespace gurl_base {
+
+#define WHITESPACE_UNICODE \
+  0x0009, /* CHARACTER TABULATION */      \
+  0x000A, /* LINE FEED (LF) */            \
+  0x000B, /* LINE TABULATION */           \
+  0x000C, /* FORM FEED (FF) */            \
+  0x000D, /* CARRIAGE RETURN (CR) */      \
+  0x0020, /* SPACE */                     \
+  0x0085, /* NEXT LINE (NEL) */           \
+  0x00A0, /* NO-BREAK SPACE */            \
+  0x1680, /* OGHAM SPACE MARK */          \
+  0x2000, /* EN QUAD */                   \
+  0x2001, /* EM QUAD */                   \
+  0x2002, /* EN SPACE */                  \
+  0x2003, /* EM SPACE */                  \
+  0x2004, /* THREE-PER-EM SPACE */        \
+  0x2005, /* FOUR-PER-EM SPACE */         \
+  0x2006, /* SIX-PER-EM SPACE */          \
+  0x2007, /* FIGURE SPACE */              \
+  0x2008, /* PUNCTUATION SPACE */         \
+  0x2009, /* THIN SPACE */                \
+  0x200A, /* HAIR SPACE */                \
+  0x2028, /* LINE SEPARATOR */            \
+  0x2029, /* PARAGRAPH SEPARATOR */       \
+  0x202F, /* NARROW NO-BREAK SPACE */     \
+  0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
+  0x3000, /* IDEOGRAPHIC SPACE */         \
+  0
+
+const wchar_t kWhitespaceWide[] = {
+  WHITESPACE_UNICODE
+};
+
+const char16 kWhitespaceUTF16[] = {
+  WHITESPACE_UNICODE
+};
+
+const char kWhitespaceASCII[] = {
+  0x09,    // CHARACTER TABULATION
+  0x0A,    // LINE FEED (LF)
+  0x0B,    // LINE TABULATION
+  0x0C,    // FORM FEED (FF)
+  0x0D,    // CARRIAGE RETURN (CR)
+  0x20,    // SPACE
+  0
+};
+
+const char16 kWhitespaceASCIIAs16[] = {
+  0x09,    // CHARACTER TABULATION
+  0x0A,    // LINE FEED (LF)
+  0x0B,    // LINE TABULATION
+  0x0C,    // FORM FEED (FF)
+  0x0D,    // CARRIAGE RETURN (CR)
+  0x20,    // SPACE
+  0
+};
+
+const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
+
+}  // namespace base
diff --git a/base/strings/string_util_perftest.cc b/base/strings/string_util_perftest.cc
new file mode 100644
index 0000000..033df0e
--- /dev/null
+++ b/base/strings/string_util_perftest.cc
@@ -0,0 +1,46 @@
+// Copyright 2018 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 "base/strings/string_util.h"
+
+#include <cinttypes>
+
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+template <typename String>
+void MeasureIsStringASCII(size_t str_length, size_t non_ascii_pos) {
+  String str(str_length, 'A');
+  if (non_ascii_pos < str_length)
+    str[non_ascii_pos] = '\xAF';
+
+  TimeTicks t0 = TimeTicks::Now();
+  for (size_t i = 0; i < 10000000; ++i)
+    IsStringASCII(str);
+  TimeDelta time = TimeTicks::Now() - t0;
+  printf(
+      "char-size:\t%zu\tlength:\t%zu\tnon-ascii-pos:\t%zu\ttime-ms:\t%" PRIu64
+      "\n",
+      sizeof(typename String::value_type), str_length, non_ascii_pos,
+      time.InMilliseconds());
+}
+
+TEST(StringUtilTest, DISABLED_IsStringASCIIPerf) {
+  for (size_t str_length = 4; str_length <= 1024; str_length *= 2) {
+    for (size_t non_ascii_loc = 0; non_ascii_loc < 3; ++non_ascii_loc) {
+      size_t non_ascii_pos = str_length * non_ascii_loc / 2 + 2;
+      MeasureIsStringASCII<std::string>(str_length, non_ascii_pos);
+      MeasureIsStringASCII<string16>(str_length, non_ascii_pos);
+#if defined(WCHAR_T_IS_UTF32)
+      MeasureIsStringASCII<std::basic_string<wchar_t>>(str_length,
+                                                       non_ascii_pos);
+#endif
+    }
+  }
+}
+
+}  // namespace base
diff --git a/base/strings/string_util_posix.h b/base/strings/string_util_posix.h
new file mode 100644
index 0000000..e1ba7c3
--- /dev/null
+++ b/base/strings/string_util_posix.h
@@ -0,0 +1,37 @@
+// Copyright 2013 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.
+
+#ifndef BASE_STRINGS_STRING_UTIL_POSIX_H_
+#define BASE_STRINGS_STRING_UTIL_POSIX_H_
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "polyfills/base/logging.h"
+
+namespace gurl_base {
+
+// Chromium code style is to not use malloc'd strings; this is only for use
+// for interaction with APIs that require it.
+inline char* strdup(const char* str) {
+  return ::strdup(str);
+}
+
+inline int vsnprintf(char* buffer, size_t size,
+                     const char* format, va_list arguments) {
+  return ::vsnprintf(buffer, size, format, arguments);
+}
+
+inline int vswprintf(wchar_t* buffer, size_t size,
+                     const wchar_t* format, va_list arguments) {
+  GURL_DCHECK(IsWprintfFormatPortable(format));
+  return ::vswprintf(buffer, size, format, arguments);
+}
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_STRING_UTIL_POSIX_H_
diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc
new file mode 100644
index 0000000..51b4ee1
--- /dev/null
+++ b/base/strings/string_util_unittest.cc
@@ -0,0 +1,1430 @@
+// Copyright 2013 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 "base/strings/string_util.h"
+
+#include <math.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+
+namespace gurl_base {
+
+static const struct trim_case {
+  const wchar_t* input;
+  const TrimPositions positions;
+  const wchar_t* output;
+  const TrimPositions return_value;
+} trim_cases[] = {
+  {L" Google Video ", TRIM_LEADING, L"Google Video ", TRIM_LEADING},
+  {L" Google Video ", TRIM_TRAILING, L" Google Video", TRIM_TRAILING},
+  {L" Google Video ", TRIM_ALL, L"Google Video", TRIM_ALL},
+  {L"Google Video", TRIM_ALL, L"Google Video", TRIM_NONE},
+  {L"", TRIM_ALL, L"", TRIM_NONE},
+  {L"  ", TRIM_LEADING, L"", TRIM_LEADING},
+  {L"  ", TRIM_TRAILING, L"", TRIM_TRAILING},
+  {L"  ", TRIM_ALL, L"", TRIM_ALL},
+  {L"\t\rTest String\n", TRIM_ALL, L"Test String", TRIM_ALL},
+  {L"\x2002Test String\x00A0\x3000", TRIM_ALL, L"Test String", TRIM_ALL},
+};
+
+static const struct trim_case_ascii {
+  const char* input;
+  const TrimPositions positions;
+  const char* output;
+  const TrimPositions return_value;
+} trim_cases_ascii[] = {
+  {" Google Video ", TRIM_LEADING, "Google Video ", TRIM_LEADING},
+  {" Google Video ", TRIM_TRAILING, " Google Video", TRIM_TRAILING},
+  {" Google Video ", TRIM_ALL, "Google Video", TRIM_ALL},
+  {"Google Video", TRIM_ALL, "Google Video", TRIM_NONE},
+  {"", TRIM_ALL, "", TRIM_NONE},
+  {"  ", TRIM_LEADING, "", TRIM_LEADING},
+  {"  ", TRIM_TRAILING, "", TRIM_TRAILING},
+  {"  ", TRIM_ALL, "", TRIM_ALL},
+  {"\t\rTest String\n", TRIM_ALL, "Test String", TRIM_ALL},
+};
+
+namespace {
+
+// Helper used to test TruncateUTF8ToByteSize.
+bool Truncated(const std::string& input,
+               const size_t byte_size,
+               std::string* output) {
+    size_t prev = input.length();
+    TruncateUTF8ToByteSize(input, byte_size, output);
+    return prev != output->length();
+}
+
+}  // namespace
+
+TEST(StringUtilTest, TruncateUTF8ToByteSize) {
+  std::string output;
+
+  // Empty strings and invalid byte_size arguments
+  EXPECT_FALSE(Truncated(std::string(), 0, &output));
+  EXPECT_EQ(output, "");
+  EXPECT_TRUE(Truncated("\xe1\x80\xbf", 0, &output));
+  EXPECT_EQ(output, "");
+  EXPECT_FALSE(Truncated("\xe1\x80\xbf", static_cast<size_t>(-1), &output));
+  EXPECT_FALSE(Truncated("\xe1\x80\xbf", 4, &output));
+
+  // Testing the truncation of valid UTF8 correctly
+  EXPECT_TRUE(Truncated("abc", 2, &output));
+  EXPECT_EQ(output, "ab");
+  EXPECT_TRUE(Truncated("\xc2\x81\xc2\x81", 2, &output));
+  EXPECT_EQ(output.compare("\xc2\x81"), 0);
+  EXPECT_TRUE(Truncated("\xc2\x81\xc2\x81", 3, &output));
+  EXPECT_EQ(output.compare("\xc2\x81"), 0);
+  EXPECT_FALSE(Truncated("\xc2\x81\xc2\x81", 4, &output));
+  EXPECT_EQ(output.compare("\xc2\x81\xc2\x81"), 0);
+
+  {
+    const char array[] = "\x00\x00\xc2\x81\xc2\x81";
+    const std::string array_string(array, gurl_base::size(array));
+    EXPECT_TRUE(Truncated(array_string, 4, &output));
+    EXPECT_EQ(output.compare(std::string("\x00\x00\xc2\x81", 4)), 0);
+  }
+
+  {
+    const char array[] = "\x00\xc2\x81\xc2\x81";
+    const std::string array_string(array, gurl_base::size(array));
+    EXPECT_TRUE(Truncated(array_string, 4, &output));
+    EXPECT_EQ(output.compare(std::string("\x00\xc2\x81", 3)), 0);
+  }
+
+  // Testing invalid UTF8
+  EXPECT_TRUE(Truncated("\xed\xa0\x80\xed\xbf\xbf", 6, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xed\xa0\x8f", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xed\xbf\xbf", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+
+  // Testing invalid UTF8 mixed with valid UTF8
+  EXPECT_FALSE(Truncated("\xe1\x80\xbf", 3, &output));
+  EXPECT_EQ(output.compare("\xe1\x80\xbf"), 0);
+  EXPECT_FALSE(Truncated("\xf1\x80\xa0\xbf", 4, &output));
+  EXPECT_EQ(output.compare("\xf1\x80\xa0\xbf"), 0);
+  EXPECT_FALSE(Truncated("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf",
+              10, &output));
+  EXPECT_EQ(output.compare("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf"), 0);
+  EXPECT_TRUE(Truncated("a\xc2\x81\xe1\x80\xbf\xf1""a""\x80\xa0",
+              10, &output));
+  EXPECT_EQ(output.compare("a\xc2\x81\xe1\x80\xbf\xf1""a"), 0);
+  EXPECT_FALSE(Truncated("\xef\xbb\xbf" "abc", 6, &output));
+  EXPECT_EQ(output.compare("\xef\xbb\xbf" "abc"), 0);
+
+  // Overlong sequences
+  EXPECT_TRUE(Truncated("\xc0\x80", 2, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xc1\x80\xc1\x81", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xe0\x80\x80", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xe0\x82\x80", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xe0\x9f\xbf", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf0\x80\x80\x8D", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf0\x80\x82\x91", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf0\x80\xa0\x80", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf0\x8f\xbb\xbf", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf8\x80\x80\x80\xbf", 5, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xfc\x80\x80\x80\xa0\xa5", 6, &output));
+  EXPECT_EQ(output.compare(""), 0);
+
+  // Beyond U+10FFFF (the upper limit of Unicode codespace)
+  EXPECT_TRUE(Truncated("\xf4\x90\x80\x80", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf8\xa0\xbf\x80\xbf", 5, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xfc\x9c\xbf\x80\xbf\x80", 6, &output));
+  EXPECT_EQ(output.compare(""), 0);
+
+  // BOMs in UTF-16(BE|LE) and UTF-32(BE|LE)
+  EXPECT_TRUE(Truncated("\xfe\xff", 2, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xff\xfe", 2, &output));
+  EXPECT_EQ(output.compare(""), 0);
+
+  {
+    const char array[] = "\x00\x00\xfe\xff";
+    const std::string array_string(array, gurl_base::size(array));
+    EXPECT_TRUE(Truncated(array_string, 4, &output));
+    EXPECT_EQ(output.compare(std::string("\x00\x00", 2)), 0);
+  }
+
+  // Variants on the previous test
+  {
+    const char array[] = "\xff\xfe\x00\x00";
+    const std::string array_string(array, 4);
+    EXPECT_FALSE(Truncated(array_string, 4, &output));
+    EXPECT_EQ(output.compare(std::string("\xff\xfe\x00\x00", 4)), 0);
+  }
+  {
+    const char array[] = "\xff\x00\x00\xfe";
+    const std::string array_string(array, gurl_base::size(array));
+    EXPECT_TRUE(Truncated(array_string, 4, &output));
+    EXPECT_EQ(output.compare(std::string("\xff\x00\x00", 3)), 0);
+  }
+
+  // Non-characters : U+xxFFF[EF] where xx is 0x00 through 0x10 and <FDD0,FDEF>
+  EXPECT_TRUE(Truncated("\xef\xbf\xbe", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf0\x8f\xbf\xbe", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xf3\xbf\xbf\xbf", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xef\xb7\x90", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_TRUE(Truncated("\xef\xb7\xaf", 3, &output));
+  EXPECT_EQ(output.compare(""), 0);
+
+  // Strings in legacy encodings that are valid in UTF-8, but
+  // are invalid as UTF-8 in real data.
+  EXPECT_TRUE(Truncated("caf\xe9", 4, &output));
+  EXPECT_EQ(output.compare("caf"), 0);
+  EXPECT_TRUE(Truncated("\xb0\xa1\xb0\xa2", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+  EXPECT_FALSE(Truncated("\xa7\x41\xa6\x6e", 4, &output));
+  EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
+  EXPECT_TRUE(Truncated("\xa7\x41\xa6\x6e\xd9\xee\xe4\xee", 7,
+              &output));
+  EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
+
+  // Testing using the same string as input and output.
+  EXPECT_FALSE(Truncated(output, 4, &output));
+  EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
+  EXPECT_TRUE(Truncated(output, 3, &output));
+  EXPECT_EQ(output.compare("\xa7\x41"), 0);
+
+  // "abc" with U+201[CD] in windows-125[0-8]
+  EXPECT_TRUE(Truncated("\x93" "abc\x94", 5, &output));
+  EXPECT_EQ(output.compare("\x93" "abc"), 0);
+
+  // U+0639 U+064E U+0644 U+064E in ISO-8859-6
+  EXPECT_TRUE(Truncated("\xd9\xee\xe4\xee", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+
+  // U+03B3 U+03B5 U+03B9 U+03AC in ISO-8859-7
+  EXPECT_TRUE(Truncated("\xe3\xe5\xe9\xdC", 4, &output));
+  EXPECT_EQ(output.compare(""), 0);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+TEST(StringUtilTest, as_wcstr) {
+  char16 rw_buffer[10] = {};
+  static_assert(
+      std::is_same<wchar_t*, decltype(as_writable_wcstr(rw_buffer))>::value,
+      "");
+  EXPECT_EQ(static_cast<void*>(rw_buffer), as_writable_wcstr(rw_buffer));
+
+  string16 rw_str(10, '\0');
+  static_assert(
+      std::is_same<wchar_t*, decltype(as_writable_wcstr(rw_str))>::value, "");
+  EXPECT_EQ(static_cast<const void*>(rw_str.data()), as_writable_wcstr(rw_str));
+
+  const char16 ro_buffer[10] = {};
+  static_assert(
+      std::is_same<const wchar_t*, decltype(as_wcstr(ro_buffer))>::value, "");
+  EXPECT_EQ(static_cast<const void*>(ro_buffer), as_wcstr(ro_buffer));
+
+  const string16 ro_str(10, '\0');
+  static_assert(std::is_same<const wchar_t*, decltype(as_wcstr(ro_str))>::value,
+                "");
+  EXPECT_EQ(static_cast<const void*>(ro_str.data()), as_wcstr(ro_str));
+
+  StringPiece16 piece = ro_buffer;
+  static_assert(std::is_same<const wchar_t*, decltype(as_wcstr(piece))>::value,
+                "");
+  EXPECT_EQ(static_cast<const void*>(piece.data()), as_wcstr(piece));
+}
+
+TEST(StringUtilTest, as_u16cstr) {
+  wchar_t rw_buffer[10] = {};
+  static_assert(
+      std::is_same<char16*, decltype(as_writable_u16cstr(rw_buffer))>::value,
+      "");
+  EXPECT_EQ(static_cast<void*>(rw_buffer), as_writable_u16cstr(rw_buffer));
+
+  std::wstring rw_str(10, '\0');
+  static_assert(
+      std::is_same<char16*, decltype(as_writable_u16cstr(rw_str))>::value, "");
+  EXPECT_EQ(static_cast<const void*>(rw_str.data()),
+            as_writable_u16cstr(rw_str));
+
+  const wchar_t ro_buffer[10] = {};
+  static_assert(
+      std::is_same<const char16*, decltype(as_u16cstr(ro_buffer))>::value, "");
+  EXPECT_EQ(static_cast<const void*>(ro_buffer), as_u16cstr(ro_buffer));
+
+  const std::wstring ro_str(10, '\0');
+  static_assert(
+      std::is_same<const char16*, decltype(as_u16cstr(ro_str))>::value, "");
+  EXPECT_EQ(static_cast<const void*>(ro_str.data()), as_u16cstr(ro_str));
+
+  WStringPiece piece = ro_buffer;
+  static_assert(std::is_same<const char16*, decltype(as_u16cstr(piece))>::value,
+                "");
+  EXPECT_EQ(static_cast<const void*>(piece.data()), as_u16cstr(piece));
+}
+#endif  // defined(WCHAR_T_IS_UTF16)
+
+TEST(StringUtilTest, TrimWhitespace) {
+  string16 output;  // Allow contents to carry over to next testcase
+  for (const auto& value : trim_cases) {
+    EXPECT_EQ(value.return_value,
+              TrimWhitespace(WideToUTF16(value.input), value.positions,
+                             &output));
+    EXPECT_EQ(WideToUTF16(value.output), output);
+  }
+
+  // Test that TrimWhitespace() can take the same string for input and output
+  output = ASCIIToUTF16("  This is a test \r\n");
+  EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+  EXPECT_EQ(ASCIIToUTF16("This is a test"), output);
+
+  // Once more, but with a string of whitespace
+  output = ASCIIToUTF16("  \r\n");
+  EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+  EXPECT_EQ(string16(), output);
+
+  std::string output_ascii;
+  for (const auto& value : trim_cases_ascii) {
+    EXPECT_EQ(value.return_value,
+              TrimWhitespaceASCII(value.input, value.positions, &output_ascii));
+    EXPECT_EQ(value.output, output_ascii);
+  }
+}
+
+static const struct collapse_case {
+  const wchar_t* input;
+  const bool trim;
+  const wchar_t* output;
+} collapse_cases[] = {
+  {L" Google Video ", false, L"Google Video"},
+  {L"Google Video", false, L"Google Video"},
+  {L"", false, L""},
+  {L"  ", false, L""},
+  {L"\t\rTest String\n", false, L"Test String"},
+  {L"\x2002Test String\x00A0\x3000", false, L"Test String"},
+  {L"    Test     \n  \t String    ", false, L"Test String"},
+  {L"\x2002Test\x1680 \x2028 \tString\x00A0\x3000", false, L"Test String"},
+  {L"   Test String", false, L"Test String"},
+  {L"Test String    ", false, L"Test String"},
+  {L"Test String", false, L"Test String"},
+  {L"", true, L""},
+  {L"\n", true, L""},
+  {L"  \r  ", true, L""},
+  {L"\nFoo", true, L"Foo"},
+  {L"\r  Foo  ", true, L"Foo"},
+  {L" Foo bar ", true, L"Foo bar"},
+  {L"  \tFoo  bar  \n", true, L"Foo bar"},
+  {L" a \r b\n c \r\n d \t\re \t f \n ", true, L"abcde f"},
+};
+
+TEST(StringUtilTest, CollapseWhitespace) {
+  for (const auto& value : collapse_cases) {
+    EXPECT_EQ(WideToUTF16(value.output),
+              CollapseWhitespace(WideToUTF16(value.input), value.trim));
+  }
+}
+
+static const struct collapse_case_ascii {
+  const char* input;
+  const bool trim;
+  const char* output;
+} collapse_cases_ascii[] = {
+  {" Google Video ", false, "Google Video"},
+  {"Google Video", false, "Google Video"},
+  {"", false, ""},
+  {"  ", false, ""},
+  {"\t\rTest String\n", false, "Test String"},
+  {"    Test     \n  \t String    ", false, "Test String"},
+  {"   Test String", false, "Test String"},
+  {"Test String    ", false, "Test String"},
+  {"Test String", false, "Test String"},
+  {"", true, ""},
+  {"\n", true, ""},
+  {"  \r  ", true, ""},
+  {"\nFoo", true, "Foo"},
+  {"\r  Foo  ", true, "Foo"},
+  {" Foo bar ", true, "Foo bar"},
+  {"  \tFoo  bar  \n", true, "Foo bar"},
+  {" a \r b\n c \r\n d \t\re \t f \n ", true, "abcde f"},
+};
+
+TEST(StringUtilTest, CollapseWhitespaceASCII) {
+  for (const auto& value : collapse_cases_ascii) {
+    EXPECT_EQ(value.output, CollapseWhitespaceASCII(value.input, value.trim));
+  }
+}
+
+TEST(StringUtilTest, IsStringUTF8) {
+  EXPECT_TRUE(IsStringUTF8("abc"));
+  EXPECT_TRUE(IsStringUTF8("\xc2\x81"));
+  EXPECT_TRUE(IsStringUTF8("\xe1\x80\xbf"));
+  EXPECT_TRUE(IsStringUTF8("\xf1\x80\xa0\xbf"));
+  EXPECT_TRUE(IsStringUTF8("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf"));
+  EXPECT_TRUE(IsStringUTF8("\xef\xbb\xbf" "abc"));  // UTF-8 BOM
+
+  // surrogate code points
+  EXPECT_FALSE(IsStringUTF8("\xed\xa0\x80\xed\xbf\xbf"));
+  EXPECT_FALSE(IsStringUTF8("\xed\xa0\x8f"));
+  EXPECT_FALSE(IsStringUTF8("\xed\xbf\xbf"));
+
+  // overlong sequences
+  EXPECT_FALSE(IsStringUTF8("\xc0\x80"));  // U+0000
+  EXPECT_FALSE(IsStringUTF8("\xc1\x80\xc1\x81"));  // "AB"
+  EXPECT_FALSE(IsStringUTF8("\xe0\x80\x80"));  // U+0000
+  EXPECT_FALSE(IsStringUTF8("\xe0\x82\x80"));  // U+0080
+  EXPECT_FALSE(IsStringUTF8("\xe0\x9f\xbf"));  // U+07ff
+  EXPECT_FALSE(IsStringUTF8("\xf0\x80\x80\x8D"));  // U+000D
+  EXPECT_FALSE(IsStringUTF8("\xf0\x80\x82\x91"));  // U+0091
+  EXPECT_FALSE(IsStringUTF8("\xf0\x80\xa0\x80"));  // U+0800
+  EXPECT_FALSE(IsStringUTF8("\xf0\x8f\xbb\xbf"));  // U+FEFF (BOM)
+  EXPECT_FALSE(IsStringUTF8("\xf8\x80\x80\x80\xbf"));  // U+003F
+  EXPECT_FALSE(IsStringUTF8("\xfc\x80\x80\x80\xa0\xa5"));  // U+00A5
+
+  // Beyond U+10FFFF (the upper limit of Unicode codespace)
+  EXPECT_FALSE(IsStringUTF8("\xf4\x90\x80\x80"));  // U+110000
+  EXPECT_FALSE(IsStringUTF8("\xf8\xa0\xbf\x80\xbf"));  // 5 bytes
+  EXPECT_FALSE(IsStringUTF8("\xfc\x9c\xbf\x80\xbf\x80"));  // 6 bytes
+
+  // BOMs in UTF-16(BE|LE) and UTF-32(BE|LE)
+  EXPECT_FALSE(IsStringUTF8("\xfe\xff"));
+  EXPECT_FALSE(IsStringUTF8("\xff\xfe"));
+  EXPECT_FALSE(IsStringUTF8(std::string("\x00\x00\xfe\xff", 4)));
+  EXPECT_FALSE(IsStringUTF8("\xff\xfe\x00\x00"));
+
+  // Non-characters : U+xxFFF[EF] where xx is 0x00 through 0x10 and <FDD0,FDEF>
+  EXPECT_FALSE(IsStringUTF8("\xef\xbf\xbe"));  // U+FFFE)
+  EXPECT_FALSE(IsStringUTF8("\xf0\x8f\xbf\xbe"));  // U+1FFFE
+  EXPECT_FALSE(IsStringUTF8("\xf3\xbf\xbf\xbf"));  // U+10FFFF
+  EXPECT_FALSE(IsStringUTF8("\xef\xb7\x90"));  // U+FDD0
+  EXPECT_FALSE(IsStringUTF8("\xef\xb7\xaf"));  // U+FDEF
+  // Strings in legacy encodings. We can certainly make up strings
+  // in a legacy encoding that are valid in UTF-8, but in real data,
+  // most of them are invalid as UTF-8.
+  EXPECT_FALSE(IsStringUTF8("caf\xe9"));  // cafe with U+00E9 in ISO-8859-1
+  EXPECT_FALSE(IsStringUTF8("\xb0\xa1\xb0\xa2"));  // U+AC00, U+AC001 in EUC-KR
+  EXPECT_FALSE(IsStringUTF8("\xa7\x41\xa6\x6e"));  // U+4F60 U+597D in Big5
+  // "abc" with U+201[CD] in windows-125[0-8]
+  EXPECT_FALSE(IsStringUTF8("\x93" "abc\x94"));
+  // U+0639 U+064E U+0644 U+064E in ISO-8859-6
+  EXPECT_FALSE(IsStringUTF8("\xd9\xee\xe4\xee"));
+  // U+03B3 U+03B5 U+03B9 U+03AC in ISO-8859-7
+  EXPECT_FALSE(IsStringUTF8("\xe3\xe5\xe9\xdC"));
+
+  // Check that we support Embedded Nulls. The first uses the canonical UTF-8
+  // representation, and the second uses a 2-byte sequence. The second version
+  // is invalid UTF-8 since UTF-8 states that the shortest encoding for a
+  // given codepoint must be used.
+  static const char kEmbeddedNull[] = "embedded\0null";
+  EXPECT_TRUE(IsStringUTF8(
+      std::string(kEmbeddedNull, sizeof(kEmbeddedNull))));
+  EXPECT_FALSE(IsStringUTF8("embedded\xc0\x80U+0000"));
+}
+
+TEST(StringUtilTest, IsStringASCII) {
+  static char char_ascii[] =
+      "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
+  static char16 char16_ascii[] = {
+      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A',
+      'B', 'C', 'D', 'E', 'F', '0', '1', '2', '3', '4', '5', '6',
+      '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', 0 };
+  static std::wstring wchar_ascii(
+      L"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF");
+
+  // Test a variety of the fragment start positions and lengths in order to make
+  // sure that bit masking in IsStringASCII works correctly.
+  // Also, test that a non-ASCII character will be detected regardless of its
+  // position inside the string.
+  {
+    const size_t string_length = gurl_base::size(char_ascii) - 1;
+    for (size_t offset = 0; offset < 8; ++offset) {
+      for (size_t len = 0, max_len = string_length - offset; len < max_len;
+           ++len) {
+        EXPECT_TRUE(IsStringASCII(StringPiece(char_ascii + offset, len)));
+        for (size_t char_pos = offset; char_pos < len; ++char_pos) {
+          char_ascii[char_pos] |= '\x80';
+          EXPECT_FALSE(IsStringASCII(StringPiece(char_ascii + offset, len)));
+          char_ascii[char_pos] &= ~'\x80';
+        }
+      }
+    }
+  }
+
+  {
+    const size_t string_length = gurl_base::size(char16_ascii) - 1;
+    for (size_t offset = 0; offset < 4; ++offset) {
+      for (size_t len = 0, max_len = string_length - offset; len < max_len;
+           ++len) {
+        EXPECT_TRUE(IsStringASCII(StringPiece16(char16_ascii + offset, len)));
+        for (size_t char_pos = offset; char_pos < len; ++char_pos) {
+          char16_ascii[char_pos] |= 0x80;
+          EXPECT_FALSE(
+              IsStringASCII(StringPiece16(char16_ascii + offset, len)));
+          char16_ascii[char_pos] &= ~0x80;
+          // Also test when the upper half is non-zero.
+          char16_ascii[char_pos] |= 0x100;
+          EXPECT_FALSE(
+              IsStringASCII(StringPiece16(char16_ascii + offset, len)));
+          char16_ascii[char_pos] &= ~0x100;
+        }
+      }
+    }
+  }
+
+#if defined(WCHAR_T_IS_UTF32)
+  {
+    const size_t string_length = wchar_ascii.length();
+    for (size_t len = 0; len < string_length; ++len) {
+      EXPECT_TRUE(IsStringASCII(wchar_ascii.substr(0, len)));
+      for (size_t char_pos = 0; char_pos < len; ++char_pos) {
+        wchar_ascii[char_pos] |= 0x80;
+        EXPECT_FALSE(IsStringASCII(wchar_ascii.substr(0, len)));
+        wchar_ascii[char_pos] &= ~0x80;
+        wchar_ascii[char_pos] |= 0x100;
+        EXPECT_FALSE(IsStringASCII(wchar_ascii.substr(0, len)));
+        wchar_ascii[char_pos] &= ~0x100;
+        wchar_ascii[char_pos] |= 0x10000;
+        EXPECT_FALSE(IsStringASCII(wchar_ascii.substr(0, len)));
+        wchar_ascii[char_pos] &= ~0x10000;
+      }
+    }
+  }
+#endif  // WCHAR_T_IS_UTF32
+}
+
+TEST(StringUtilTest, ConvertASCII) {
+  static const char* const char_cases[] = {
+    "Google Video",
+    "Hello, world\n",
+    "0123ABCDwxyz \a\b\t\r\n!+,.~"
+  };
+
+  static const wchar_t* const wchar_cases[] = {
+    L"Google Video",
+    L"Hello, world\n",
+    L"0123ABCDwxyz \a\b\t\r\n!+,.~"
+  };
+
+  for (size_t i = 0; i < gurl_base::size(char_cases); ++i) {
+    EXPECT_TRUE(IsStringASCII(char_cases[i]));
+    string16 utf16 = ASCIIToUTF16(char_cases[i]);
+    EXPECT_EQ(WideToUTF16(wchar_cases[i]), utf16);
+
+    std::string ascii = UTF16ToASCII(WideToUTF16(wchar_cases[i]));
+    EXPECT_EQ(char_cases[i], ascii);
+  }
+
+  EXPECT_FALSE(IsStringASCII("Google \x80Video"));
+
+  // Convert empty strings.
+  string16 empty16;
+  std::string empty;
+  EXPECT_EQ(empty, UTF16ToASCII(empty16));
+  EXPECT_EQ(empty16, ASCIIToUTF16(empty));
+
+  // Convert strings with an embedded NUL character.
+  const char chars_with_nul[] = "test\0string";
+  const int length_with_nul = gurl_base::size(chars_with_nul) - 1;
+  std::string string_with_nul(chars_with_nul, length_with_nul);
+  string16 string16_with_nul = ASCIIToUTF16(string_with_nul);
+  EXPECT_EQ(static_cast<string16::size_type>(length_with_nul),
+            string16_with_nul.length());
+  std::string narrow_with_nul = UTF16ToASCII(string16_with_nul);
+  EXPECT_EQ(static_cast<std::string::size_type>(length_with_nul),
+            narrow_with_nul.length());
+  EXPECT_EQ(0, string_with_nul.compare(narrow_with_nul));
+}
+
+TEST(StringUtilTest, ToLowerASCII) {
+  EXPECT_EQ('c', ToLowerASCII('C'));
+  EXPECT_EQ('c', ToLowerASCII('c'));
+  EXPECT_EQ('2', ToLowerASCII('2'));
+
+  EXPECT_EQ(static_cast<char16>('c'), ToLowerASCII(static_cast<char16>('C')));
+  EXPECT_EQ(static_cast<char16>('c'), ToLowerASCII(static_cast<char16>('c')));
+  EXPECT_EQ(static_cast<char16>('2'), ToLowerASCII(static_cast<char16>('2')));
+
+  EXPECT_EQ("cc2", ToLowerASCII("Cc2"));
+  EXPECT_EQ(ASCIIToUTF16("cc2"), ToLowerASCII(ASCIIToUTF16("Cc2")));
+}
+
+TEST(StringUtilTest, ToUpperASCII) {
+  EXPECT_EQ('C', ToUpperASCII('C'));
+  EXPECT_EQ('C', ToUpperASCII('c'));
+  EXPECT_EQ('2', ToUpperASCII('2'));
+
+  EXPECT_EQ(static_cast<char16>('C'), ToUpperASCII(static_cast<char16>('C')));
+  EXPECT_EQ(static_cast<char16>('C'), ToUpperASCII(static_cast<char16>('c')));
+  EXPECT_EQ(static_cast<char16>('2'), ToUpperASCII(static_cast<char16>('2')));
+
+  EXPECT_EQ("CC2", ToUpperASCII("Cc2"));
+  EXPECT_EQ(ASCIIToUTF16("CC2"), ToUpperASCII(ASCIIToUTF16("Cc2")));
+}
+
+TEST(StringUtilTest, LowerCaseEqualsASCII) {
+  static const struct {
+    const char*    src_a;
+    const char*    dst;
+  } lowercase_cases[] = {
+    { "FoO", "foo" },
+    { "foo", "foo" },
+    { "FOO", "foo" },
+  };
+
+  for (const auto& i : lowercase_cases) {
+    EXPECT_TRUE(LowerCaseEqualsASCII(ASCIIToUTF16(i.src_a), i.dst));
+    EXPECT_TRUE(LowerCaseEqualsASCII(i.src_a, i.dst));
+  }
+}
+
+TEST(StringUtilTest, FormatBytesUnlocalized) {
+  static const struct {
+    int64_t bytes;
+    const char* expected;
+  } cases[] = {
+    // Expected behavior: we show one post-decimal digit when we have
+    // under two pre-decimal digits, except in cases where it makes no
+    // sense (zero or bytes).
+    // Since we switch units once we cross the 1000 mark, this keeps
+    // the display of file sizes or bytes consistently around three
+    // digits.
+    {0, "0 B"},
+    {512, "512 B"},
+    {1024*1024, "1.0 MB"},
+    {1024*1024*1024, "1.0 GB"},
+    {10LL*1024*1024*1024, "10.0 GB"},
+    {99LL*1024*1024*1024, "99.0 GB"},
+    {105LL*1024*1024*1024, "105 GB"},
+    {105LL*1024*1024*1024 + 500LL*1024*1024, "105 GB"},
+    {~(1LL << 63), "8192 PB"},
+
+    {99*1024 + 103, "99.1 kB"},
+    {1024*1024 + 103, "1.0 MB"},
+    {1024*1024 + 205 * 1024, "1.2 MB"},
+    {1024*1024*1024 + (927 * 1024*1024), "1.9 GB"},
+    {10LL*1024*1024*1024, "10.0 GB"},
+    {100LL*1024*1024*1024, "100 GB"},
+  };
+
+  for (const auto& i : cases) {
+    EXPECT_EQ(ASCIIToUTF16(i.expected), FormatBytesUnlocalized(i.bytes));
+  }
+}
+TEST(StringUtilTest, ReplaceSubstringsAfterOffset) {
+  static const struct {
+    StringPiece str;
+    size_t start_offset;
+    StringPiece find_this;
+    StringPiece replace_with;
+    StringPiece expected;
+  } cases[] = {
+      {"aaa", 0, "", "b", "aaa"},
+      {"aaa", 1, "", "b", "aaa"},
+      {"aaa", 0, "a", "b", "bbb"},
+      {"aaa", 0, "aa", "b", "ba"},
+      {"aaa", 0, "aa", "bbb", "bbba"},
+      {"aaaaa", 0, "aa", "b", "bba"},
+      {"ababaaababa", 0, "aba", "", "baaba"},
+      {"ababaaababa", 0, "aba", "_", "_baa_ba"},
+      {"ababaaababa", 0, "aba", "__", "__baa__ba"},
+      {"ababaaababa", 0, "aba", "___", "___baa___ba"},
+      {"ababaaababa", 0, "aba", "____", "____baa____ba"},
+      {"ababaaababa", 0, "aba", "_____", "_____baa_____ba"},
+      {"abb", 0, "ab", "a", "ab"},
+      {"Removing some substrings inging", 0, "ing", "", "Remov some substrs "},
+      {"Not found", 0, "x", "0", "Not found"},
+      {"Not found again", 5, "x", "0", "Not found again"},
+      {" Making it much longer ", 0, " ", "Four score and seven years ago",
+       "Four score and seven years agoMakingFour score and seven years agoit"
+       "Four score and seven years agomuchFour score and seven years agolonger"
+       "Four score and seven years ago"},
+      {" Making it much much much much shorter ", 0,
+       "Making it much much much much shorter", "", "  "},
+      {"so much much much much much very much much much shorter", 0, "much ",
+       "", "so very shorter"},
+      {"Invalid offset", 9999, "t", "foobar", "Invalid offset"},
+      {"Replace me only me once", 9, "me ", "", "Replace me only once"},
+      {"abababab", 2, "ab", "c", "abccc"},
+      {"abababab", 1, "ab", "c", "abccc"},
+      {"abababab", 1, "aba", "c", "abcbab"},
+  };
+
+  // gurl_base::string16 variant
+  for (const auto& scenario : cases) {
+    string16 str = ASCIIToUTF16(scenario.str);
+    ReplaceSubstringsAfterOffset(&str, scenario.start_offset,
+                                 ASCIIToUTF16(scenario.find_this),
+                                 ASCIIToUTF16(scenario.replace_with));
+    EXPECT_EQ(ASCIIToUTF16(scenario.expected), str);
+  }
+
+  // std::string with insufficient capacity: expansion must realloc the buffer.
+  for (const auto& scenario : cases) {
+    std::string str = scenario.str.as_string();
+    str.shrink_to_fit();  // This is nonbinding, but it's the best we've got.
+    ReplaceSubstringsAfterOffset(&str, scenario.start_offset,
+                                 scenario.find_this, scenario.replace_with);
+    EXPECT_EQ(scenario.expected, str);
+  }
+
+  // std::string with ample capacity: should be possible to grow in-place.
+  for (const auto& scenario : cases) {
+    std::string str = scenario.str.as_string();
+    str.reserve(std::max(scenario.str.length(), scenario.expected.length()) *
+                2);
+
+    ReplaceSubstringsAfterOffset(&str, scenario.start_offset,
+                                 scenario.find_this, scenario.replace_with);
+    EXPECT_EQ(scenario.expected, str);
+  }
+}
+
+TEST(StringUtilTest, ReplaceFirstSubstringAfterOffset) {
+  static const struct {
+    const char* str;
+    string16::size_type start_offset;
+    const char* find_this;
+    const char* replace_with;
+    const char* expected;
+  } cases[] = {
+    {"aaa", 0, "a", "b", "baa"},
+    {"abb", 0, "ab", "a", "ab"},
+    {"Removing some substrings inging", 0, "ing", "",
+      "Remov some substrings inging"},
+    {"Not found", 0, "x", "0", "Not found"},
+    {"Not found again", 5, "x", "0", "Not found again"},
+    {" Making it much longer ", 0, " ", "Four score and seven years ago",
+     "Four score and seven years agoMaking it much longer "},
+    {"Invalid offset", 9999, "t", "foobar", "Invalid offset"},
+    {"Replace me only me once", 4, "me ", "", "Replace only me once"},
+    {"abababab", 2, "ab", "c", "abcabab"},
+  };
+
+  for (const auto& i : cases) {
+    string16 str = ASCIIToUTF16(i.str);
+    ReplaceFirstSubstringAfterOffset(&str, i.start_offset,
+                                     ASCIIToUTF16(i.find_this),
+                                     ASCIIToUTF16(i.replace_with));
+    EXPECT_EQ(ASCIIToUTF16(i.expected), str);
+  }
+}
+
+TEST(StringUtilTest, HexDigitToInt) {
+  EXPECT_EQ(0, HexDigitToInt('0'));
+  EXPECT_EQ(1, HexDigitToInt('1'));
+  EXPECT_EQ(2, HexDigitToInt('2'));
+  EXPECT_EQ(3, HexDigitToInt('3'));
+  EXPECT_EQ(4, HexDigitToInt('4'));
+  EXPECT_EQ(5, HexDigitToInt('5'));
+  EXPECT_EQ(6, HexDigitToInt('6'));
+  EXPECT_EQ(7, HexDigitToInt('7'));
+  EXPECT_EQ(8, HexDigitToInt('8'));
+  EXPECT_EQ(9, HexDigitToInt('9'));
+  EXPECT_EQ(10, HexDigitToInt('A'));
+  EXPECT_EQ(11, HexDigitToInt('B'));
+  EXPECT_EQ(12, HexDigitToInt('C'));
+  EXPECT_EQ(13, HexDigitToInt('D'));
+  EXPECT_EQ(14, HexDigitToInt('E'));
+  EXPECT_EQ(15, HexDigitToInt('F'));
+
+  // Verify the lower case as well.
+  EXPECT_EQ(10, HexDigitToInt('a'));
+  EXPECT_EQ(11, HexDigitToInt('b'));
+  EXPECT_EQ(12, HexDigitToInt('c'));
+  EXPECT_EQ(13, HexDigitToInt('d'));
+  EXPECT_EQ(14, HexDigitToInt('e'));
+  EXPECT_EQ(15, HexDigitToInt('f'));
+}
+
+TEST(StringUtilTest, JoinString) {
+  std::string separator(", ");
+  std::vector<std::string> parts;
+  EXPECT_EQ(std::string(), JoinString(parts, separator));
+
+  parts.push_back(std::string());
+  EXPECT_EQ(std::string(), JoinString(parts, separator));
+  parts.clear();
+
+  parts.push_back("a");
+  EXPECT_EQ("a", JoinString(parts, separator));
+
+  parts.push_back("b");
+  parts.push_back("c");
+  EXPECT_EQ("a, b, c", JoinString(parts, separator));
+
+  parts.push_back(std::string());
+  EXPECT_EQ("a, b, c, ", JoinString(parts, separator));
+  parts.push_back(" ");
+  EXPECT_EQ("a|b|c|| ", JoinString(parts, "|"));
+}
+
+TEST(StringUtilTest, JoinString16) {
+  string16 separator = ASCIIToUTF16(", ");
+  std::vector<string16> parts;
+  EXPECT_EQ(string16(), JoinString(parts, separator));
+
+  parts.push_back(string16());
+  EXPECT_EQ(string16(), JoinString(parts, separator));
+  parts.clear();
+
+  parts.push_back(ASCIIToUTF16("a"));
+  EXPECT_EQ(ASCIIToUTF16("a"), JoinString(parts, separator));
+
+  parts.push_back(ASCIIToUTF16("b"));
+  parts.push_back(ASCIIToUTF16("c"));
+  EXPECT_EQ(ASCIIToUTF16("a, b, c"), JoinString(parts, separator));
+
+  parts.push_back(ASCIIToUTF16(""));
+  EXPECT_EQ(ASCIIToUTF16("a, b, c, "), JoinString(parts, separator));
+  parts.push_back(ASCIIToUTF16(" "));
+  EXPECT_EQ(ASCIIToUTF16("a|b|c|| "), JoinString(parts, ASCIIToUTF16("|")));
+}
+
+TEST(StringUtilTest, JoinStringPiece) {
+  std::string separator(", ");
+  std::vector<StringPiece> parts;
+  EXPECT_EQ(std::string(), JoinString(parts, separator));
+
+  // Test empty first part (https://crbug.com/698073).
+  parts.push_back(StringPiece());
+  EXPECT_EQ(std::string(), JoinString(parts, separator));
+  parts.clear();
+
+  parts.push_back("a");
+  EXPECT_EQ("a", JoinString(parts, separator));
+
+  parts.push_back("b");
+  parts.push_back("c");
+  EXPECT_EQ("a, b, c", JoinString(parts, separator));
+
+  parts.push_back(StringPiece());
+  EXPECT_EQ("a, b, c, ", JoinString(parts, separator));
+  parts.push_back(" ");
+  EXPECT_EQ("a|b|c|| ", JoinString(parts, "|"));
+}
+
+TEST(StringUtilTest, JoinStringPiece16) {
+  string16 separator = ASCIIToUTF16(", ");
+  std::vector<StringPiece16> parts;
+  EXPECT_EQ(string16(), JoinString(parts, separator));
+
+  // Test empty first part (https://crbug.com/698073).
+  parts.push_back(StringPiece16());
+  EXPECT_EQ(string16(), JoinString(parts, separator));
+  parts.clear();
+
+  const string16 kA = ASCIIToUTF16("a");
+  parts.push_back(kA);
+  EXPECT_EQ(ASCIIToUTF16("a"), JoinString(parts, separator));
+
+  const string16 kB = ASCIIToUTF16("b");
+  parts.push_back(kB);
+  const string16 kC = ASCIIToUTF16("c");
+  parts.push_back(kC);
+  EXPECT_EQ(ASCIIToUTF16("a, b, c"), JoinString(parts, separator));
+
+  parts.push_back(StringPiece16());
+  EXPECT_EQ(ASCIIToUTF16("a, b, c, "), JoinString(parts, separator));
+  const string16 kSpace = ASCIIToUTF16(" ");
+  parts.push_back(kSpace);
+  EXPECT_EQ(ASCIIToUTF16("a|b|c|| "), JoinString(parts, ASCIIToUTF16("|")));
+}
+
+TEST(StringUtilTest, JoinStringInitializerList) {
+  std::string separator(", ");
+  EXPECT_EQ(std::string(), JoinString({}, separator));
+
+  // Test empty first part (https://crbug.com/698073).
+  EXPECT_EQ(std::string(), JoinString({StringPiece()}, separator));
+
+  // With const char*s.
+  EXPECT_EQ("a", JoinString({"a"}, separator));
+  EXPECT_EQ("a, b, c", JoinString({"a", "b", "c"}, separator));
+  EXPECT_EQ("a, b, c, ", JoinString({"a", "b", "c", StringPiece()}, separator));
+  EXPECT_EQ("a|b|c|| ", JoinString({"a", "b", "c", StringPiece(), " "}, "|"));
+
+  // With std::strings.
+  const std::string kA = "a";
+  const std::string kB = "b";
+  EXPECT_EQ("a, b", JoinString({kA, kB}, separator));
+
+  // With StringPieces.
+  const StringPiece kPieceA = kA;
+  const StringPiece kPieceB = kB;
+  EXPECT_EQ("a, b", JoinString({kPieceA, kPieceB}, separator));
+}
+
+TEST(StringUtilTest, JoinStringInitializerList16) {
+  string16 separator = ASCIIToUTF16(", ");
+  EXPECT_EQ(string16(), JoinString({}, separator));
+
+  // Test empty first part (https://crbug.com/698073).
+  EXPECT_EQ(string16(), JoinString({StringPiece16()}, separator));
+
+  // With string16s.
+  const string16 kA = ASCIIToUTF16("a");
+  EXPECT_EQ(ASCIIToUTF16("a"), JoinString({kA}, separator));
+
+  const string16 kB = ASCIIToUTF16("b");
+  const string16 kC = ASCIIToUTF16("c");
+  EXPECT_EQ(ASCIIToUTF16("a, b, c"), JoinString({kA, kB, kC}, separator));
+
+  EXPECT_EQ(ASCIIToUTF16("a, b, c, "),
+            JoinString({kA, kB, kC, StringPiece16()}, separator));
+  const string16 kSpace = ASCIIToUTF16(" ");
+  EXPECT_EQ(
+      ASCIIToUTF16("a|b|c|| "),
+      JoinString({kA, kB, kC, StringPiece16(), kSpace}, ASCIIToUTF16("|")));
+
+  // With StringPiece16s.
+  const StringPiece16 kPieceA = kA;
+  const StringPiece16 kPieceB = kB;
+  EXPECT_EQ(ASCIIToUTF16("a, b"), JoinString({kPieceA, kPieceB}, separator));
+}
+
+TEST(StringUtilTest, StartsWith) {
+  EXPECT_TRUE(StartsWith("javascript:url", "javascript",
+                         gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(StartsWith("JavaScript:url", "javascript",
+                          gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(StartsWith("javascript:url", "javascript",
+                         gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(StartsWith("JavaScript:url", "javascript",
+                         gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(StartsWith("java", "javascript", gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(StartsWith("java", "javascript",
+                          gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(StartsWith(std::string(), "javascript",
+                          gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(StartsWith(std::string(), "javascript",
+                          gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(StartsWith("java", std::string(),
+                         gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(StartsWith("java", std::string(), gurl_base::CompareCase::SENSITIVE));
+
+  EXPECT_TRUE(StartsWith(ASCIIToUTF16("javascript:url"),
+                         ASCIIToUTF16("javascript"),
+                         gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(StartsWith(ASCIIToUTF16("JavaScript:url"),
+                          ASCIIToUTF16("javascript"),
+                          gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(StartsWith(ASCIIToUTF16("javascript:url"),
+                         ASCIIToUTF16("javascript"),
+                         gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(StartsWith(ASCIIToUTF16("JavaScript:url"),
+                         ASCIIToUTF16("javascript"),
+                         gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"), ASCIIToUTF16("javascript"),
+                          gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"), ASCIIToUTF16("javascript"),
+                          gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"),
+                          gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"),
+                          gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(),
+                         gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(),
+                         gurl_base::CompareCase::SENSITIVE));
+}
+
+TEST(StringUtilTest, EndsWith) {
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), ASCIIToUTF16(".plugin"),
+                       gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.Plugin"), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), ASCIIToUTF16(".plugin"),
+                       gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.Plugin"), ASCIIToUTF16(".plugin"),
+                       gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::SENSITIVE));
+  EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"),
+                        gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(),
+                       gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(),
+                       gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"), ASCIIToUTF16(".plugin"),
+                       gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"), ASCIIToUTF16(".plugin"),
+                       gurl_base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(
+      EndsWith(string16(), string16(), gurl_base::CompareCase::INSENSITIVE_ASCII));
+  EXPECT_TRUE(EndsWith(string16(), string16(), gurl_base::CompareCase::SENSITIVE));
+}
+
+TEST(StringUtilTest, GetStringFWithOffsets) {
+  std::vector<string16> subst;
+  subst.push_back(ASCIIToUTF16("1"));
+  subst.push_back(ASCIIToUTF16("2"));
+  std::vector<size_t> offsets;
+
+  ReplaceStringPlaceholders(ASCIIToUTF16("Hello, $1. Your number is $2."),
+                            subst,
+                            &offsets);
+  EXPECT_EQ(2U, offsets.size());
+  EXPECT_EQ(7U, offsets[0]);
+  EXPECT_EQ(25U, offsets[1]);
+  offsets.clear();
+
+  ReplaceStringPlaceholders(ASCIIToUTF16("Hello, $2. Your number is $1."),
+                            subst,
+                            &offsets);
+  EXPECT_EQ(2U, offsets.size());
+  EXPECT_EQ(25U, offsets[0]);
+  EXPECT_EQ(7U, offsets[1]);
+  offsets.clear();
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholdersTooFew) {
+  // Test whether replacestringplaceholders works as expected when there
+  // are fewer inputs than outputs.
+  std::vector<string16> subst;
+  subst.push_back(ASCIIToUTF16("9a"));
+  subst.push_back(ASCIIToUTF16("8b"));
+  subst.push_back(ASCIIToUTF16("7c"));
+
+  string16 formatted =
+      ReplaceStringPlaceholders(
+          ASCIIToUTF16("$1a,$2b,$3c,$4d,$5e,$6f,$1g,$2h,$3i"), subst, nullptr);
+
+  EXPECT_EQ(ASCIIToUTF16("9aa,8bb,7cc,d,e,f,9ag,8bh,7ci"), formatted);
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholders) {
+  std::vector<string16> subst;
+  subst.push_back(ASCIIToUTF16("9a"));
+  subst.push_back(ASCIIToUTF16("8b"));
+  subst.push_back(ASCIIToUTF16("7c"));
+  subst.push_back(ASCIIToUTF16("6d"));
+  subst.push_back(ASCIIToUTF16("5e"));
+  subst.push_back(ASCIIToUTF16("4f"));
+  subst.push_back(ASCIIToUTF16("3g"));
+  subst.push_back(ASCIIToUTF16("2h"));
+  subst.push_back(ASCIIToUTF16("1i"));
+
+  string16 formatted =
+      ReplaceStringPlaceholders(
+          ASCIIToUTF16("$1a,$2b,$3c,$4d,$5e,$6f,$7g,$8h,$9i"), subst, nullptr);
+
+  EXPECT_EQ(ASCIIToUTF16("9aa,8bb,7cc,6dd,5ee,4ff,3gg,2hh,1ii"), formatted);
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholdersNetExpansionWithContraction) {
+  // In this test, some of the substitutions are shorter than the placeholders,
+  // but overall the string gets longer.
+  std::vector<string16> subst;
+  subst.push_back(ASCIIToUTF16("9a____"));
+  subst.push_back(ASCIIToUTF16("B"));
+  subst.push_back(ASCIIToUTF16("7c___"));
+  subst.push_back(ASCIIToUTF16("d"));
+  subst.push_back(ASCIIToUTF16("5e____"));
+  subst.push_back(ASCIIToUTF16("F"));
+  subst.push_back(ASCIIToUTF16("3g___"));
+  subst.push_back(ASCIIToUTF16("h"));
+  subst.push_back(ASCIIToUTF16("1i_____"));
+
+  string16 original = ASCIIToUTF16("$1a,$2b,$3c,$4d,$5e,$6f,$7g,$8h,$9i");
+  string16 expected =
+      ASCIIToUTF16("9a____a,Bb,7c___c,dd,5e____e,Ff,3g___g,hh,1i_____i");
+
+  EXPECT_EQ(expected, ReplaceStringPlaceholders(original, subst, nullptr));
+
+  std::vector<size_t> offsets;
+  EXPECT_EQ(expected, ReplaceStringPlaceholders(original, subst, &offsets));
+  std::vector<size_t> expected_offsets = {0, 8, 11, 18, 21, 29, 32, 39, 42};
+  EXPECT_EQ(offsets.size(), subst.size());
+  EXPECT_EQ(expected_offsets, offsets);
+  for (size_t i = 0; i < offsets.size(); i++) {
+    EXPECT_EQ(expected.substr(expected_offsets[i], subst[i].length()),
+              subst[i]);
+  }
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholdersNetContrac