From ab035127bf44c79e5053e747033e564a30ae5c53 Mon Sep 17 00:00:00 2001 From: Ken Rockot Date: Wed, 6 Feb 2019 00:35:24 +0000 Subject: [PATCH] Update Mojo and services documentation This is a large update to all Mojo and services documentation in the tree. Here are the changes in a nutshell: - Images for Mojo docs are upstreamed instead of being pulled from docs.google.com - Various minor updates, corrections, clarifications in core Mojo docs - Consolidation of documents in the tree. - Refresh of all service and Service Manager related documentation, removing lots of outdated information, adding clarifying definitions and examples of core concepts As of this CL, the relevant documentation in the tree is pared down to: - The mojo/ subtree itself - services/README.md - service guidelines - services/service_manager/README.md - general service manager and service API documentation, examples, etc - docs/mojo_and_services.md - intro to mojo & services for chromium developers - docs/mojo_ipc_conversion.md - advice for converting legacy IPCs to mojo interfaces - docs/servicification.md - advice for servicifying chromium features specifically Specifically this wipes out content/public/common/services.md, ipc/README.md, services/service_manager/service_manifests.md, and services/api_standards.md. Any still-relevant content that was in these docs has been merged into one of the docs listed above. Finally, the presence in docs/README.md has been cleaned up a bit. A new section for "Mojo & Services" is added with links to the above list of documents. Change-Id: I294a32025afdca62441d3605da51d714f3aebd00 TBR: jam@chromium.org Reviewed-on: https://chromium-review.googlesource.com/c/1441640 Commit-Queue: Ken Rockot Reviewed-by: Oksana Zhuravlova Cr-Commit-Position: refs/heads/master@{#629384} --- content/public/common/services.md | 65 - docs/README.md | 15 +- docs/images/mojo_binding_and_dispatch.png | Bin 0 -> 21655 bytes docs/images/mojo_message.png | Bin 0 -> 5045 bytes docs/images/mojo_pipe.png | Bin 0 -> 4828 bytes docs/images/mojo_stack.png | Bin 0 -> 18788 bytes docs/images/mojo_sync_call_deadlock.png | Bin 0 -> 23367 bytes docs/images/mojo_sync_call_flow.png | Bin 0 -> 28135 bytes docs/mojo_and_services.md | 855 ++++++++++++ docs/mojo_guide.md | 157 --- docs/mojo_ipc_conversion.md | 374 +++++ docs/servicification.md | 401 ++++-- ipc/README.md | 860 ------------ mojo/README.md | 144 +- mojo/public/c/system/README.md | 4 +- mojo/public/cpp/bindings/README.md | 14 +- mojo/public/tools/bindings/README.md | 46 +- services/README.md | 280 +++- services/api_standards.md | 116 -- services/service_manager/README.md | 1241 +++++++++++------ services/service_manager/service_manifests.md | 315 ----- 21 files changed, 2635 insertions(+), 2252 deletions(-) delete mode 100644 content/public/common/services.md create mode 100644 docs/images/mojo_binding_and_dispatch.png create mode 100644 docs/images/mojo_message.png create mode 100644 docs/images/mojo_pipe.png create mode 100644 docs/images/mojo_stack.png create mode 100644 docs/images/mojo_sync_call_deadlock.png create mode 100644 docs/images/mojo_sync_call_flow.png create mode 100644 docs/mojo_and_services.md delete mode 100644 docs/mojo_guide.md create mode 100644 docs/mojo_ipc_conversion.md delete mode 100644 ipc/README.md delete mode 100644 services/api_standards.md delete mode 100644 services/service_manager/service_manifests.md diff --git a/content/public/common/services.md b/content/public/common/services.md deleted file mode 100644 index 315726d1056832..00000000000000 --- a/content/public/common/services.md +++ /dev/null @@ -1,65 +0,0 @@ -# Services in Content - -The //content layer implements the core process model for Chrome, including -major process types: `browser`, `renderer`, `gpu`, `utility`, `plugin`. From the -perspective of the service manager, each process type is a service, and each -process instance is an instantiation of that service. For a renderer process, -its `service_manager::Identity` is constructed as follows: - -``` -name: content_renderer -userid: -instance: -``` - -These services express the set of capabilities they expose to one another using -service manifests (see [Service Manager README](https://chromium.googlesource.com/chromium/src/+/master/services/service_manager/README.md)). For //content, the service manifests live in -`//content/public/app/mojo`. - -Every `content::BrowserContext` has an instance group ID generated for it upon -construction, and the services run with that BrowserContext use that instance -group as part of their instance identity. Where there are multiple instances of -the same service within the same instance group, the Identity's instance ID -field is used for disambiguation. - -Launching code for each process type is currently ad-hoc & specific per type, -and lives in `//content/browser`. In the medium-long term, we'll work to -generalize this and move it all into the service manager. -Each content process type is launched by host code in `//content/browser`, -though eventually all process launching will be moved to the service manager. - -The canonical service for each process type is represented by an implementation -of the `service_manager::Service` interface which lives on the IO thread. This -implementation is shared, and is a detail of `content::ServiceManagerConnection` -which you will find in `//content/public/common`. This implementation receives -the `OnStart()` and `OnBindInterface()` calls from the service manager. - -The rest of this document talks about things you might like to do and how to -accomplish them. - -### Expose Mojo interfaces from one of the existing content-provided services. - -To expose interfaces at the service-level from one of the existing content- -provided services, you will need to add a `content::ConnectionFilter` to the -`content::ServiceManagerConnection` in the relevant process. See -`//content/public/common/connection_filter.h`. You implement this interface to -handle `OnBindInterface()` requests on the IO thread. You can construct a -`service_manager::BinderRegistry` on any other thread and move it to the IO -thread using `//content/public/common/connection_filter_impl.h`. When you add -bind callbacks to the binder registry you can specify what task runner you -would like incoming interface requests to be bound on. - -### Expose Mojo interfaces at the frame level between browser & renderer. - -You can add bind callbacks to the `service_manager::InterfaceRegistry` owned by -the `RenderFrame` and the `RenderFrameHost`. See the various content client -interfaces also for signals to embedders allowing them to add additional -interfaces. - -### Expose a named service from an existing process. - -If you want to expose a named service (i.e. a service other than the ones -provided by content) from a process provided by content, you can "embed" a -service in one of the content-provided services. You do this by calling -`AddEmbeddedService()` on `ServiceManagerConnection`. - diff --git a/docs/README.md b/docs/README.md index 933f296f6b5691..9939e5c632179f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -340,7 +340,20 @@ used when committed. install Chromium OS on VMWare. * [User Data Directory](user_data_dir.md) - How the user data and cache directories are determined on all platforms. -* [Mojo](../mojo/README.md) - IPC mechanism used by services. + +### Mojo & Services +* [Intro to Mojo & Services](mojo_and_services.md) - Quick introduction + to Mojo and services in Chromium, with examples +* [Mojo API Reference](/mojo/README.md) - Detailed reference documentation for + all things Mojo +* [The Service Manager & Services](/services/service_manager/README.md) - + Services system overview, API references, example services +* [Service Development Guidelines](/services/README.md) - Guidelines for + service development in the Chromium tree +* [Servicifying Chromium Features](servicification.md) - General advice for + integrating new and existing subsystems into Chromium as services +* [Converting Legacy IPC to Mojo](mojo_ipc_conversion.md) - Tips and common + patterns for practical IPC conversion work ### Probably Obsolete * [TPM Quick Reference](tpm_quick_ref.md) - Trusted Platform Module notes. diff --git a/docs/images/mojo_binding_and_dispatch.png b/docs/images/mojo_binding_and_dispatch.png new file mode 100644 index 0000000000000000000000000000000000000000..06a0da75efcf981c8fc55842959832643d69d8a4 GIT binary patch literal 21655 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 n>AuU_FViw*_Rd9 literal 0 HcmV?d00001 diff --git a/docs/images/mojo_message.png b/docs/images/mojo_message.png new file mode 100644 index 0000000000000000000000000000000000000000..be819052f50f28990ec2535fd67ba4d407d78213 GIT binary patch literal 5045 zcmbt2c|4SD*YzazRz!-iCq+~khOw*c`lsZR{Gmc}MU2e&1i;@Av(_@A~7qulqjhb?$S{@0^ohXJg8DOzhZ!0|)rb z&5Rrl9QXsyJqI1-5#g=>a98cUPTuG=6g zE42hVRR;F2CsJ*9K0KcGfE6Qr`@MTlkL|G6QCWd^JX{~s&XCZz2p+F>BaShTPepE` zxN2lszO~8P+sSOyxS!Zyc2fTra{{JJ+==^#klbD)!j>zfKB^8B*~=jOGUv0y9%}=q zj0f*Oo51?yM2iL>&9=l z>{RQ(e8W$_)cDzBu$KF8Osrbq8LxDuF8uI$?)@|4OicMQiRLf2_vmsm=g$2_bL3Np z>ltjjd2lWIg{45v;WG1Or$M-y8G+yy1bME!Oua%HS0_PgP9H0Q3y0YHvHxeVnFx+} zrZli(?REeMac4*v%&2KrRgvcWfQD~3&QwB6C_B_K7!As)tJ__m28hab{>0EXN9@yg zmo#Thq8|rthLwWgT(S6|ETWqJTB=0_sRn3`l-g^UU8KTxtbOkGwRqm&$ZAorFzZBBbv`Kn; z#JX(HeN!HMqPd3MTt>>m=tGv&(F{N6Yd2zz54~r0vjd%~0H^EN6iZRY!l_b=BuzZg zPYoj65<0Rx*o;(zH?=r1Vz!@Imdv+LX>K=NaLTrI*%|$)3g~ZEa7=7rtQ`9N6F|Kz z&zz3XWh`fCq`FeoW~gFsZkE&L!C56~NxR+70G<#unHy%>ib<3rf&P?%rYYx`=nqmd zRyT3PvD=ZsK8lO9t$V*X#<_b@)yiyoCktqk5@5t=*-L_#a`G3@R8#~1g%jhG&K|$* z!;M)zyxXQfPNzWw_$0LJ1s`f??|X@XB*+fCGrr{YS7-|8tY8~ z@_f1uxC#W9)?DAQh+xRBcGS>knu|^>_U}828M9jo!pD5o`(iRe6}GvdNp6n&=r14g zH4v=iqY|ZyokfB7d@Kph&g)e+1M1+|c`^5Y-My8ytP7EkPF5Q1VA(^-)g*sEjiB=x z#PM$TZ@ZbvBIiqoJ6YL#A4A+zQ&A0+ZThW8MGFAmeXqQis|rVFST>po%JIhKsm8}5GX)musLckRu+$s^kM=+I)8OJeslP-wrkx!}Ge0W7`>nIGPjM%Tc=wLEx z2P(6H@K70lw$Z%`)u6qRm<(VDS*bAdUF=l%6Bj})j5_*f<*>D9l~NNcQIUX1Xw@xr z%LnK}zN&^}3N5FF`3Oj0TMA#bjawrG6X0olXx^usjug=9CX0qw4`>z)mTQG*i4{i2Tqlz9NB~+D{7k&BGmISY&8#ej9 zG$a@#;+K3F<6+I9pJ8slp=99d0@K3fO~q^WbPEkzasO)m?BQ9|1haG!gM{ zvH@Rec~dEGQYvHw4%0E9718=x3WYRB3&M~Y!E{}Y@pmZl#lh07rK`>l^y|Gm1cdBd zdV9ynga5&*-Vc0kQt)G^^|81c#Fp@uyK$4oJ^&ru34j{fY4Gf)iQgz(=3EK;Q10fC zsrSDG6o+?@?rq!dNdG1(1i`5jg;(g|%>^`j|3&)kZd~+I2sb7DRwR>jXsBW#<|-jS z-J`YyrxWzi-8?BE&TNac41yHU#^dqW*8?)lv0@;pHuy)H1Q0g+Xp|7Jj*1 zwDYb>SnfZH`%8(~;y!TNVmKRwUf-w&+w}+0c${{?*6L)~Lx?Smxt^y=rU|6+<$ZbI zzEnnE>oD;Bv%z#%ym|FyY>^H9Ru0D!-o9w)gZ95|IJ`3)O;W$qVi(06-6QAv>O-@e zSANZvqW(s_p`lJlyEPogY>~n)-DEV+e0-Y&OAkC$%}pj=le2r!WSzZ&Mau>#ISE}#zB>t0nxC$Obrvsf$<@VF;~Mo*=_t0Oi`!*yM!yfnA z%R}BsVm%EQTL>pmuzM|hup`=cH-yD3jkUE688&zL+hBh}f>;v*x0k1eZu<0iY-Mp> zZ**8)6@OQrI4AiEc>V3}w_=hctfJU@@WqAv?q11Ym$31@!L14>k%&mjN(cK$X>^O{ zc&gK_n&>G6y>$J_EJlAbF(4{;zK+|OhkEoYMEnTg44`TYY@d2vX;1es}rL?&e6SC$XonGCV<|3~6 zP!(|#rlq*cSX#5!7J9}hV{Cg(zT{Exi?%nZ*39Rt5+cANG@=b|A6e;rF(Pn5CZwdn zeJ{@ERfhN2%wchwrjmtH_^bsjTugK^q1zqwwXQ8O+D=}7=$hln_hP_pGXuC`_h`8w z4>I=znqzMfma`Z191;}pHjB8CJhcs;5D0#{xG><70y+y3fFV$ZIBfWY00aRHdw`r5 zyz}nJwoTAhesY>@xd2ihHh(67l-ChuytzX#+Cgc*os4&5VenB9_k0No5-&)ZXl!11 z>^N2RL|nT0?Kfrmd0{*iz#Q@e8Z^ONXXe@Hsz; znahew{3ir%Y3DGL52>|hn?(wm6kQe*h4Jp#=9{mR7|u_ivBeY5d}92D{8^V4BQG#< z>HdQqb@|M4c`=u*=7zwLi&*y`Gt0j5+>9Icwg!f$$rcG9H%5Dz0?P*-PX)){J5YI$ z@B(Ye6c7dP^~xKkNOF(cOkz7w)oo_o0Ff9?;%SMOoB=F>_$&E33(hUrL*1e9Wi0{T z1V9$WKoyGXeXQ>Vy&vhQ)+i6bbo(*@t;5c`w!Eng1ygZ#-CE{qapBDHI$B%ml!$%2 zj*NR-VgJ#`-3dWXN@AiSg0SN=#*zxms?=cT{*q+Y%c1g?Ny|oce8U6!Lyu5URTYrW zhYhfze+R;fvZ(dGH0JyrhxekV9t=tY3d}%HVe1VnKUr3;}HF>Rd{HRV8N!|GS zGfBfSb{X(kXs}a&L4WRJZ(+ZpKVnBFp zwXd_uiR4$9dO#0LJ?fbW=jgMxNThK>=;d8m5FE_4IBIbWL`YATKWQjLw`|nxUw+~z zaZXM}sWC979h=&or}E6udZ7!BQ@L^P4A5yn)>^Z`gwHT@5sXg@9=RPRNP%seQKg9P zX|f707j+_M&Azp3FzW#pao4DnHGj7D2#fR&u})TS-pR0ap^Cz!2j?M}w8w!1*DI;1 zAbdCl5t>K4qxA&9sj`759_q8q2}9!$<-VyfwIJ{AL$I@kVN5a)rcTA2 zub+|&WA>%=uSI}*8Xm|jN|w(#5o%5OJbkj5a5+(1W9`gW0b;~biXypM9El6Ps)b-z zB`PnY%*q;qSIfWOWYldw{0pFZqxSoC*}2cV;-r>R!b$x&(}^Fg$RDN{@h{h z3QF_6*Myx1O38o`K%2AVX+O3}naqdLuXbG;AHuQztb|bgwrZcRT}}9@Nslkn!$hgV&nrcsplc6;6kz)P_o7zr0c#%By@ zWeZ&E&YuJHr$Iq&J1?v)<*k31X8;gqAegOHgUW^l-GX1T{bt}meST%K0-2}FMgNL{ zMrFaR*CNzWK$f1^G5I_j&xwiisnyKCAZQ*DOIwY64Y$ZR-nPS{{U70Yg~jD3R_FPi z(>~ZMWhaR%1d6G^Tpq|OvDR88tO+8p!~uncpA8+X!_f4to4s9Kl}nD6mm=NW13Zk_ zd@D8G=Q?ZoLmA0_Xn%SB$)5)g8A>FouRm5wr*&d4C1^hPykE@&;Jq{<;8uhy*X4#z z$_fh`VALw3Zc`!0U&yHUeBGL|BBAHY!uA?w_djaCPhJ*;N7&wO8DJSZ6hZI$0h48O zlX|=1{ad6LiV`}fKs&v`$%UU=+l`5zY$o_FQZyoh8aj;!kgFncTn7UcBVo2W7Xilc_h5FCK9=0pu&(h2n z!|RUrYfpI#_7yL+B_Lw!S)zb6ahOdRSu~uEF+!Qi&U9#|L6%j5xDiJl5JiUC`*hw- zSs6D9Ln*FG$!!jjWUOvjp{GT0O@9W9QZ-M9*@!6pTs2;F5#y|UQjEe7&RdG*w-pps zW}=}0{pbIHNDo}8kftc6$6W&&g29BfzQU=(h2e*;%#f-~Kt65mY_Q-I0nw*Z(m->l zT(fu|+Vj+QXrhZ^F<`BZ8HxyU{94OxQ_R3wMCaA)K}CMgvvU)6q0tCIS>D02Q2_{M zC5#Sj@fJjWONzNSGND%V(s{PCdi&)YdktxuK?RBlp%!~4)~H`7i69TBFbs92e=2*h z@4c&D*>9gg@U{e&IHNT}BJuFnYS{Wpa3By;5m?p~9aeWUo2EwgW3yd=P7)!e9Nk!F zCUmeb*Yx8qrT2r%&1d8@ev?%gkNqy206s-JdGPMWIet=aNnrv{^Lo4?+8L#J+TfMe z3r_=XW#jIgh(aN*=U-c+PZ`vnLMV%gc)kCFd7AatAh!vFEDj!Z#8UDACTS-vy@u_i zkeQDprb{4E9t_M8022i7M4a7k@PAj+Tyg&{==Wy;efxF45Vz~+mhN2m{;g1Bpm0fyd=Xc=Z@nJm>b&|m0!R6?4LyxG8q5> literal 0 HcmV?d00001 diff --git a/docs/images/mojo_pipe.png b/docs/images/mojo_pipe.png new file mode 100644 index 0000000000000000000000000000000000000000..06b247767d937ba1e197117ab10f40d773248907 GIT binary patch literal 4828 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 literal 0 HcmV?d00001 diff --git a/docs/images/mojo_sync_call_flow.png b/docs/images/mojo_sync_call_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..ad6fd15ad66e82c52476254a91d320d26d5c6418 GIT binary patch literal 28135 zcmeFZX*`te8$YZ?lB7bCEw_+tMV68!D%m5EJ=wDFJ44y-vK1Lkwq(mTm@$JHOvpO6 zh-r*5_H`KR494&u?(X03_xU`}=Xv+M`_GHwn(MmG^E}q?ah%`N+b8;(tY@yAVPIfj z)q4EEh=Jh*ih<$y&C{poSM3HX(fFL@dI6h8#Gc=Wyp#0rc$1t{FXR{-w5G$FIwvRRQfEL-MFmbWgMLaj zUE%vZ1~suF@h5n1_$_U($dd^#ohfAdwXy!d82%kB2}N6Q*(0qLur|#&jG6mwTxO!r zkfrc>SO0^ZH+Smx;bC(dMXhm=?sc;L!q$t%+oGZ)V-mkicoR$~b9y3F%tC}PsY`@j zF4F!!E~ny_P>en=0sp`$T)ajUU6BHiR#iYHAC#|?|N1)4$-wZvGgFZ@912+2@YGiD zoQvS!zh}cMJ7MbhPE?1*`-yep0g}&#JoTdf!a<-G@YP~x$XM*+V0GHH#J<*Hgp-w-t$gz_zckvxoN|Hn=y2~Ks3rAzEAEN z*W}H5DV@8^5d95*!Ea$ST{b)n}>tGwD zYTtctZLrma0WKKyOyzRQGBeW~NZ2b58_}|p%A5E{5n&m8JCyv_A?A+pGlVR__uGQd zjaoGc?>GBGw15_)w)I?7;?{(B4A1p^88HND7Zsws1evHD%EN_Hg6AU_@3ZP z6?~ttrp%22yzobVaVW#Yykrj$R!nDFrWCxaj38JIr;r!XaoR>+Ix6;e5%WnS|9%>T1Tp?wWF%c zT@SYm(74eUM<&ZV+s+`^6yn}fs+s?AAox;jk4e-!(0yPz^V-AGdeiBauOsd(%7}-# z;MNq5TBRPdm!{h~>h+W>(**DA3v}C$%u_UVmzjLh?@AUz8@b|>d6~w!9Q(Z8RoAmm z#eBslOO)r>tNH|6IyT#jB+@z(NU1MWYC#h29f~@hh z$SfH*-Qw*fV=2z+KKFVX=3b15bw!_KF6^p*zTKMpPLgV# zB(!h0-kO*E>2m}WOQUF)((_g1B|{0}JJ%>UP|+e_TI?wd0Z@gw)9gzvrHv^aSvpJ-i^kF_)~ zk-tV1xU_Ej-qI)v$x`aC1DVMr$I726k z+q?FaMwN&OxBkR5Ug2N0w2lDmW)R&S?(9e}4h%NDP+ey!?pPuV;R=R%8hCr$l0B_f zkaOmISilJ6_iF-c)b+E6$u6k#8%AK&NQg* zoc|cfn=@@{p^E*kWP99;_jEPadMxup`RiPzbmQVKThRpe|8*=X4~w6|Nj(LZu3HwH zgaXXNva2J8!d|^>Z)+TFTnR)N-{s|*XdGE|bM2QeZe0~m`!;8Bd5-p0GQ;gYLVY<@ z)~ZpN972Cx=nLOs(oy!2VGf= z&$2NEzYzjv=Bl)?#7i>xRqKdFC}(#BKm1|FfcOXG_@ZfefaTWg&D#RMO>fKlWzQ+Pl&2j+%*WWjd)b=2yhQ=zqALg?w$Du-l!e%ukl9H!KBH~|QzHrR3`XEu z-?!s*^MPE-oK*Wu=fM(1DC2zf7wXj-SLz2Hor_X?w2s29!NGM-vQQvX79MZ#88&1# zU0(g2NFFkYcTkor-@b2=Zdr6G!)GWzwVCuf`$s^)NRK_qBz+g>(AU?nid^cuhGYx) z9Ji#NY~oJvsc3>lrQGU$e3;-+uTs6UPe3+9)51UPtbf5LTZa5~ET6u9cWQO%YjvOv zN>0{)&42l#nApBQegXgUUNSkwsw44gM)pv z8gsRJXIUxE*frIoo+n-+{CcSTQhyahdg@yF@3de(geX?BRNtkCLn>aMW&29AUe(Qw z`$^mp5Vuj7tv2{;&ynvoOZSI7&c1GQlloz!6)C^{v8a^#p=aMtHq`Xi!4R!umO^*g zwOoAi-r!LFo5u;HsA`(Q&oG9cFFB{#&rLlY(jFR$&oUd9*SBztHP0#C)1`2ecA9=g zKdX>?Dyf*BEf*G6e%ieaSW}H!8JJc%TD00W;|Nzu55Qh*+PDkLtx+R@HQu%w?1$Lk zlie&-wOYr*{8iVdlhb)PBVUH);F+!#uGex@p8+bM*F!K=bAlZ*a}0d-8(HL>g=}Yp z-sPl-?A= z(rWAqQ4=_9&^kAZ@MfU)9ZYoO5CPNO%NxW=ihrJ|dR6!0=YrL>P$9+|-G}RnytqnN z7Vo;@2SYc0?c~G_u9%uf$=&EGpdiCO1!x@&#?Y^2TVm7v6c4a3Hg~54Nd=eZ!2QLD z>NLP~PMibx3O7;nQ7UC9{8esva?#)}n`x;rx^eDQ45noHOLfJq!^~$dtxH=Qd}wCk zN)bX18x$U_)vsudmB0Wuq|qu4NC7Mykm3M!A4^UMXsCg|+zB!lm^9ypp^H-AdC(2} zD)m+Bv9`eT`J(`PW@DqPow#JnU)*gCE)nKPft?r$Bn<6~2TUiq(fk@UAjVO>>DIfF zACL6n1$ZLCm_QPW{x$-zXAcskJK&WB%I)i1hYs1-eYRks*plBb)cn|f>_3RpZ+RQo z@Qv=IH8V;GfnaTlP+#YOi>K;3knT>0L1Yke+u|^ZE+=rlIv&yT53uZ=+6H=wKdjMR zD_;cIo4Ht-_y1)z0hneJd9VbqhyL1i-gy`8KIU@-W>XH)C&}Y<2YBmg zC|xmr%p>-ZYl)lh_j-1PkpgueQ$Z65T8HE8@9wLlrG)hqY_f!GPijFJUD*cVyn2Vz z`EK_05MC=g%4lhwrpTaLMg2felK7~4@pQVN=>iCo!oX^(%5$b06~7*Wa(YT- zXba?uqrXOm#qDr)A2GuQ0`|k(+bgQ_}bcX`uo3 zIJ!iSfL2d;l{;;kEX0*R2|L0&`4sYsF7mRm#DZ}pelARToGE805=HHuJryOSm5B&ui7Da8xl@UI+RNPhc*AN7fo6es-lenDM*6fntR+(Ggoxls1W6Ksqj+5KdDG zOwU7apE@)Px?^SO*V6Xc!ly`$9a?JPdjNa$cV;`~fAgbw!i8-qHr9JJiW_5jN z>4$CLbYYk@Eo9mMk$@aC%9k8cN1c$Uy2q7em?RK5{jVZ31FJg~|6(rnuC*jPT2mY@ zVR{y|uMc_cnjeSk={g2FNQ3t*k3*S}^vTwpca2)=aA-iyv9?r5CF^Gh?l0oGYN|gC z^ZaW$XPNVS*3z#jFgMD?BSmQba<6)~bDzC4>|CFUkeB^s!DUWJy!U=dYhEsyx`HkZ zK^BkrPf`In)c%`$i|g~%+%LnE=9Kpiahw_xqX9dEW_E`}@ycsHrE+;?1ThGg(S-MD zx=oXJ)LQe{<`rf@k6CZ{?KUQ@`3AyL)T2u@A48 zAqb@YS`8mdSEffQhDh&1cNss=#kenCsYqhSJ|+)Ova$NeZOQXZ@7Op0g#vM%ceEv~ z9Lt*9W{#c?%vGoQXzhe#Yn_G&3riO;cH{j_@9t%bS?{qq`BLSesrMv20eG+PswL?* zATc1gI@)rh-bqscM1{Be;%MMBklP;tbs%IVfkxt89C7l}pB7b~7eB9`D!HmqpA%Ro z#thnpalA3VWo!CQPwTQ@ve3j+mZzO%ZUgHtgZZVN$OqjE?v+ie$-BqJ7SyF4W-Ju z8nL_Ql|y>iK;ZMnK`3zLFH>kUw1w0HJklTZ9w3{$|-1)7-&^68RR2uE%$S z7mi7Aewmtkg%aQf7K~^HdtAMI)HI5W>MhfY#gbepy&hVX?%u&0kh( zxkpvc@mn0P59TW}o{s<1vGV$S?00Mj4M8h`7?8s~nxb3j!ELj`CQEXd)<9NyG&a1P z?%pqI>iE=((*eJcQA-?=R0@!-5|oW{r7W0CuQ^;YOP3-cz1(xq=~3qX!T9<@#qmN$ z7rOZ|r~yw@Mh{Vh2CrPSdi~A(SdpUTsbeN*%$Ga6%eY?6y;=Gd)4$jkb)yRr7EC&H z^8%^gZO6>s*~tOMBIZ+tf9+;cx)WBYKbha@X080b1tmg$S&>6MbVi<3F){Lp?ai$Lx-}XBFHBazM3oohYjOpmZWt0dm2}_mA8QXgQrd%0o2H!KU!*^S!B|LEMDOpMYWAz5ur-`vx-y>2R@qwZCk zEMIN_CUVOs@6hJu#&!pVsHHsscdBix?{ZW9*UjXBnbyH&iKMvhQn;3 zRue~1FqaFyx0lsTtRskSO~TM0dm6`QPEcGe81?EJ>lDq_tvlVycmso_9e^ejuZm}? z`IU^#j(d$iCgRvno;Wf*FQeXSy;v#Nv4Qi*Z|IcvdbnCze)F^DWy+Sg>eTe7VPkrP zNDYS-O0T)Cz=HdT)N0ZVb6j{`9{c+17$Qb_YH_&w^|2$^+Ao%?U80neO z@Fv5lyI`<5)N;l#PLo>7Ba%$!I1)CEPz*k|Ml6G&Hl_2KCtrn|00Az!E!+UOtLFm zetG2L$O&ik)j|HpC0ZAN9k7&(?v-1X?6}cxU*f;`FzLcaE+1hyS=(49B)&w&oTUu-#rdq zeofZ>ASNa+{IDp0n`8O);Y|#p^J*Dq&3x|dOHl}|uQ%HtDflhF*2M|X9~n8eL!YSC zd-~Aw>JXcBvYI0LPsr>oN_2TI$C;ve;LjU&Aa$-R;w|a`hajub6SIF|fEn(@GW{3t zj_`>Zrpd~$sef{!;@o~y#GU|6AOKs%3`tYm+z-uyrJ)H+{G$#S{`pdh5SugG$v4N2 zu<3jkyC`UamVy0nFuO+V$Y~?Y>{Aqp`^a{Z?}2n2(;r`b&sgj(GoKhQk^JvY!_m49 z|4~!O)jRb^T30u&1CRfi#`q8BKYJKQ<AXDczdh zv<(3t+jeP7=0qs$b)j~ax7Y8t-w>6fFS4U7SlLYfGd|61La*UGz=93c8&0F0oZ#ZS z0>EERTg-xV*@@m25Lc$(>vgVYq0@7OIBi9Guv>DfVIg`6KS1cz+7zal>zC!Ur(1=e z1=)Vqt}7?Xj$xME;g>JZT(fd4sQEe~GZoXZV!L@_uUbW*ruu1ia<>a5%WIo&n2;=2 zmn<405Oqa`yj5E_C(zhdH!dgdtCt+F)M&A*vU#mOLP1E0WgggoCT^V|h3Je?#V`>U zM%z;;itAJ_fdyrf_o%B6y#)=OZ7}d@2xF-ry$lOpQm#-AZ}4CyMUG)MqH$xt2z8kG zWE`L8s0v$BI&!)BLR&dcYKC50c|G~%O%JEdEx7oe)I5zGToWGD9N|mv`$*V@MbS5| z_}qRgHANI2D=cc-!ffU%*Z1H{xURW5DKwT#^g&-Sv!YyoEd z!)n{pNxmVWlqOxMx-MgQ;tHTmuGZrb%&eO;T)NqtYLr8X8K;f*V^j-kSaaI-8t}2! zPQQ6fdyXSRtvu_!k>%|pi53+8hG9E^0YQ2P>z2_>5^h*Y01%4Wy?3GyW-D!Y&DxHN znY*KT-{jFVj-M{w*4<>nWc4qNt^tNiYrI8gr1m@>Zb3`A8Y0<)bL~Y>|E+xGqk_fh zs;FRZ z&wkRaHQ!NPn}}xj*-dYr1$dooVj1_ft&~k&=}LY3#pZ7UDJ^Vg zeVAV>ihYWkf=l03^c`EQ4bQD%O)p0;HHQyr~w_4`IqJ`!jF3?gbjHv>c2##Sr2&Pop$g`JH4>KXq=0^@SSX zna=p`j>)k!Q@-w|vBAT&U&AJOF&j z8};T^PClN#E&)Wv!Dee(g_CC7zGHx2q$-sj|K?J~gJcQgcatwRSD;J>BuyK9qF0xI z%A%50-3xt#!1s+V=WjS|?jUF=tsEn8rqQ;Q%A;+6#*ZmUuO&XXo2+5^2>CO04Oh-e zR}1c0$GbeNOHnWGUZp790jGbBqUuTqwUdmZ`a>lrE-U|iZY3|E9C@k1JAC={L=CFR z+kZ-anFin%mJSVQAL3-u8Q~%(7fnUm)WikXH4$RMS(~hu*NF3Z^yUk0tzgm$j{3|O zPTr=fZXS|D6ir9Fryq1_t-s4YZKJFBmN1But~viHMLn7&7k58s;U{(U-9UOhBJyV1 z`o`KflT7hZn-D#l!F}d98?MG=1QE+v>$Ou`&m|*d={7;L`biNR4ZL?@sBeW&bno5P z^*YGb#&}C=_0*#I%BH}iM$+|?a{eIT#XTF*dpY5(-%G_vbGj^3!<-k9pH@l5h83et zA1S~14(bCF_=Us9s|c~HN|)qvu>RuW9$DWBnNOWl*Mj5&ZCX}FOrdpa6=TWwJ|46Y zq!;_r53t?ml@oua9YMzLKP{77&<`+j5*^H2ZpJuGkY5VXk(#pmn_pz_+@mc?AD^+e zflxpmxV_|LKJp5$<#pngx`*MxbG8M4_?FRH^OkpW1@-)QRPBBlQu5^A)vHU=xa^;H z8;=P^wD}jW%dRPG-EngsZ&+gdE+?JOC$fjYqJur%MiX70RkUwuoaVA*8S~ax0teQ! zo#9`gDGyRJJ=d>Tc}{Tc*0R;Jxi{NIwDQy#BwYaDz5OK-(+xZ4VZ)eU`4Uf?tq1LP zhAjsUq~{24r3*tNf;-Quk}m@ARdxN-(<0^1N4&GNt3E1@f`6oE<#SuhZtyU@U~buy zX22^R!A@R0BkOHI+FUp*XX5c;7vrVnB_ziXIZK2wZ8bWipXH`hwCbygV2aE58YQSJ zoC@{6)1XR0jJk$=>H8Uz$JHdT8QMhRp&4AQFLQkoazW1KGcVowky<*T)Hj&x=`-IN zhm6IK1>H|h7T4VpIjve#UpYi<;O(EPEu!!Q9~+4f2=ueuf(2QU9>Q*5!Ou*BsMTxK zHfm*CB3?St@6h@bj*V9L_4vzHPn1;QDy~{Fq6Ff-ol4YjnJhYYQSk=qqH!!EdI690F8*(dC51YbI^!f7x98 zyx@@8@E=~$yW3$>zu(`XH^)P*Qh4JCKbch}KKI|t2+g{^RNCZc;b_OsZ_vDw9oS%H zHpy#k&|Fn6lhwG1-4scRst8^27^akBEv-+GzTP{iAk3{GxSg0=hj2PPqMpGu`+>L4 zj@le7#EVOnMIBiiz0bb-qthawUL1vKrrc`g*!DIJWjHIO4kzIG$-aJ_`{zGbw7^XbjNzjT@xW<%2(Kv zqF$(Pmp`_>v19J^x3F4-qSt}1WrH+gF7(mmh(sYS(W65ZK8-^(8 zop7t_O)zZ+*_)klcS`iFV7nI(X2t$yW*hsyURtNZwf%eaT)6L9heu|P{ZqKxAq}Eh zEeKq1V-mp+wObX#SZ`%I?lnT@zLTUo`tGR%6IsPUDne%7A5D!OyAkErsJ zy={PBHM4bVr4RmmcOQM98EnoRLOk42*hN0XI6dcYuQo1;ndUS*z8q)S5dLsKPZf0u4od-Yv1yqpk3-HF&MN*CbZOW zb_)N)#Ty(lY&GgmaO3@6Ql6Ofo&3 zN+I)PI1EDTFo17EW+Na(!OwuU+LVh|9A*P`PLy*DmRyVZv8ZC-V8B|;$yFJ}#!>&? z4Jd%Y7vV%DQ5(k(g37a=@-{CUxgdWb@59^<_EUh_)b_fcOc%_!|HBB>NS7obXX zoevEv?PTR!tz|dX6+@Jha7fr=xLkPiWA`qoqy<_>MMU^L zioye9Yve2n0;dZkUZ<%)mDvbXe=RKQW-$KK^h%Jp$EhKLLFt6AMH2L+-W|9Hwx*km zu#*sYeC=87FxJQHP5mssNWvmp?{p_)O~S|rssafajuQ8Hgo`njJy9W9Y>$PGwI?ud zB1G@FwR40$UcHukmz-_7?E=Fy!kgDv?P*6n*AX?xD-LbF5*Rt?2a#^c`Tf3d)?4uq zHe=wK43G+zD>POIh(8{r~^!IKSw?G9$OMp_LtR6i} zN8-q>PE6tWFY1T5eFHGGrdw5Se$0u-*CIjM9f$eO#Fta= zNlWsrxu9%~*7Aqy^Fs+4%wU$@`~3j|MxR`I*9BvRaSo~2fk1RbG0ouIzTC*7RRfiJ&>xFl+&({ z^}}U&fA}F zF(M`zL}a_QWOK80j~9cAm8TrWW#V0NJe?_1`2n(=)j4I+_MAU?zkl|oi961Ix6cG8&)K75$Mc>`_$CsrcQHQ| zNIcvXhw5cjABe$jgrpZ^Kyin})Sz5%Xh zaU}_3i4zeU@E;qI|I@%#6U}?B&9m*EkuRBwfje8V+{ME6)_vV^*D$KNXWCyJ^@5K0 zis>8!DcmnW%mMBh)ioaBI1#1Pd&074{BQW%{{R*B8(3N5h&-s_2Mn%<8-Y8+M&Jup z9tm*0KU?r#L3AG93jj`}SjD~MiB7VmMOp{}<}UNB6y~043^d&XtLKbty=-s;Gx zDtFxG5Irnd;|d+{cJ&r?9cnO-t5I*A4GTv4V0UeIyFv$36k&!?fkFLz|NgHlh!8oztb4mH zTdmth%+e^@s@a5eOoZQu|K5SwE%%zz=JlzWM6XEervHsmo zKR#mbS9MUd%0?ZR>q_w_KA*@q=?#m`9?S5UIhmH@79c$ zqkROpyB}eOvjJ=qv%{nh+=Gw;D#$!^hE0K0keniDt&P2W_TJchRfPCx?bTy-X~)g~%koKj z(Coo$6xhsP?*W1`f1}Um{DB*Rvnb`g8#vXd)yz0u=fEo*Y`aaHFbAh8|M-^*#7d{K zp6X7SNyfBKdEN0GVW)Y~$~nq>P=QdWw_5mGdb29xm3rxN6Y_!$yc823RczUZH+lEG zlVWtX)Vs2=Q6#tSXBpUnUven-5Ab79?~2V|76jcz;>=dT3pA4b6Q4I_D^F zqEVWW<-eS`4_>vg@}WQM`>l5oP~S+ToB{9e_bHsxQ?y1{=g(X?RLFNCI<29~=R-K8 z2V6n*q0nf^wPo-phI+p#5K9i^9+qa^NZvk50hyn-v2 z(#7u^19@Bpi-l#`d5j&vV@3EHoSzxw6ya;{_eLMe+z8K3L)g?D{IAg04Ra2TeiopZ z0E%<^9l~XK+1?me8h#tl>ElzJAW1EV*qMMT_*m}mq7U3MKAb&D!Squ$JddeXP1ixp za#R=hTP5LHoc^+TIvxk5-S0+n@Iid1Ds6yZ1Fg&YtT1xpn0$ynDtwRlLDF&yo0q>T zp_A5fex@4_s$9I%4xOE((OEY zP85pZx!4HnF@(KO3+0Ea0hwp$r9^33F6=q*WH2;rz zqq8S(ww00)$Q?L`DSrlcxmrvM%8FRS&IN|c}3=VoX z3MqQbMn&zW4pP+`Md*rtYKcs3;c8yAk1MVmwJlG%e(K~uP_?C{0#00G z(Rh~5!Ngc>&*qH3_|j9%`|<;S#{f-@4UgZx3Uqb}j!mMki7iHC^Rsdt#?+n+wQ%x& zm~lhm-_D4re))iKCuc-m8S(AV&43pM<<9Y&Yr7|SqCM+};c5`+Km$kTQVLI;QdU-Y z`LUh*_$im~@}~*hZE7z`0Y`dUVXPz{C+jI(Y(gk3`i6? z={l$`mRUSBGt2{tzRTjddZ(gNm*?{8k-=r5Ig)&6;qEM7*yQW6?`Iuj3O-k|)?oxX z_6JYgH%Y%qjj4#1zz!*whME=2>&7@7DsNr}ar@4>Mulu)nsw>fj8&K2#gUnQWFbSU z#1t$vlB;J7zfJwo{@&rIKt+Wgu4>`U8T8?E{oZ#WdatW-bTj*g=jm@I8H z-4&H?lyM_lZpGwN^aF=c20HX3d+{8f=_P|2US#23a34YnG6qt9^m{1+>ax%fcgy|LbO zpDU5#IjWK>_J{Q7^XK$AIf3_Ye%HFvou2)8oJOlrwc*)ac}kc|El51~e82mUd)u-2 zmi_21vsz`d5Xp%^`uRxfjfUQnZ2FhDj)uyru*2CuvE?@7Cn#EI5IE1#nx6CerB9*@ z51{mel(HfWbLgof*7xlPbln$m22Fjr>i`tb;KmK?AD(55DKkHd!mT+pg&$BoBM^5L z%`*F`Bko&=LFK)-@$*w&z-#l`;|*ET4fR05?vtmQs_+B zjNwCR+%DnxA+`34ubg|TAZIF@Z=CV-M+%9a_AT@^F(D*^`=m6a%FoTBqhN$|QpRHU z&g66aa-$gjwrY+4>)#ZxmlJFR>hP zur%PLT4)t_20cH#mbfCj*>1o!<8$*9un2arQWkAd-c|2#<5on?&x(oda+|WQQ5#-M zJ>1vMfx>9VgFvQc*l2~b@8*Wrq%TxC(m_6m6lj1pJwo1m{O7s{?6EdrNqG@;jUIXUtt|0@J84xR<cKyK1oebVQT-fd$%iLTFB{tk`WB6*U^f8#fw=3afA2$FV7p?N5DbiIzz4 z^gG9#1@SZ?&$?{Qyg8{!qBLA_p|pfodw6=n2w*Z{OFAS+2ZF(jK5}xvQyOsuAnDb0 zz`)f#kFK*v2j8x?yplfoB@3XiGvxp!D%y`4L_Ck$=|@U zm9rWm3!b5RD~1=ywlJE7Sc(Il@qcs}*rkp12j(oAl-FYIgRNPcNgr+04%;sbUcFdl zMB(mQii84CxcmNliN~v3>0+*IV~qH8CHoc4=CWwQmk4n56RX|5{GS`&tUV$3%9ArN zpb0q;^ZuUmf+0-Xm-l<{9Q|F8qoc@EzjaFm&6?eg7Z86+_H{^r$=~tmG?>UV01D8G z*KIgM(rB)f(#yU@5ep4tzafF{F2^ysH72)Iv39|Zn~L1S&2)1H>krLwN4SS>eLi1b zag28CkiC7CdPEp9|4Y!Ag1Mx7tQet!DD3lisNw8R0$X`Vq@~pmRjilOOQBUv{4J*j zzlK?fhdc|pWNq*Uj*D{TZy^)#DZmF8tCwXQHlVOak_ksD$-@S>MNE^l?>PQwDgqr*pgrKcX0OJ z3JEBLd5!Ojc5&^^bkBzD47g}-r)l>h{cRN+_g}AONGD;>yQe>;_LUl*As;XW_WN6N43;hVe#n0PMcB;LFdHuSki#sT_rntsAN+r)J2 zC+PPbP;1(mPQa}jgEfJWY61Ag^=8r=X7AM2V+|GV%sz!L(!zC}1FM%zU+?c_4CpDb zH+X~1!JvZK*{HYcwZVk-f@heL&XpQqfI(~SMt~*Vu&#WdHDVOce&2Bj8AGgcFd3W4 zxP?qDT9102GHG#XI%#I5E+N=G8pm3mHZJ3!;VV{z&J*3EreL)XLWZ7*OU5mdioC`g zef9bildH;gtuia2q}kezP}s|o=Q<2Y$);^AfpkaCJSwsA?zUp*?%#d-Io%BJ2#r8I zfd7^DIqJzhcgLWq7Y-|Z-J_oyYj9HUMHua8h z&#WQi>H-uoxWv?a(*ehl1{@_3v!Aoe#}`wImt*XcJ*?gjH`GI_MSeRX~>%CiKCQ4L7@Sv-oa0@ZHAf5tOReE6_#<9mI{dI0t=_3qS!y(&G`oJL+O{&NQb@30N>r*38R`4cOGxmigcT6Feat7#RCuQ=lN+ zh&RUMW(NeG>QT@x1~p91(SHqu%^#`b-ZEb_P=e(8+>eT(VSXNX1SdqB< z1;-cj?Wi2;l@oii5!u2}edlazu5tQhPv_iXm-!EB^EWP;M!n?COd(GqZlI;EBUg!i z>z|jLUH?)TEY(S_N#X#w^L&j4)Wz_l7>%|rGS!>B*?VnwO*XGk&nURCbPV@P-MFJA7gImQEBdr!;dIkZb;nZb)Dy`E1p=>(Sqxj- zOITyR(Ff|cJvwq&n%f&YtX_ZeB0br+fIyv+kDd|pI?%}D-RmA_ilfFV>aiYS|awqVVBR8 z#gLPxZag4Ta^j5P1pqxV=>JVLz$KZNu6Dm=KaFa?DhnF{zPca(MACU^PT6z|%l6&+ zR#`o9N$>N_P(f`eQ**_ol6A_&=Vpu48D}$n$vz=>HXo5UVDSgS?YZ{_Zyd(*?>mC- z50fD2$#lLb5a~qGG*Cx14Hlr~ z+FH0E12kUuL=-PlU$2n(eVH^O}r za8D%|&4$TqnPb~PZhdI9siT~0c@jK7r;O;}&T+XXWj@TiEaTi)$p_Cu z&U1Vn*<>;7bhWGfM^W#pwLx<~m=%$0k~&!qHm=ahdxw8wBG=oL%osS?lrqx+k*G8O zyNi6De8!Md^!kH`+w|2dviKYi8HEc}cd7yXySIlMzU$P`&roMw@T1#7;i|E=U`bmO z{}Hffm_N;N=#pv*c38p4sQc;a#&k({oL^u>@L}JWw$so3*_#!%2f7HBggGav8~>l` zzWc4IrE42Snp8nWq{*XLKm-fDDWDvs9i%7%K}A8NcL*eOP$|KJQ9(euw4ex~B!r>@ zBE1um5JC?jp@f8lz_;<7=Y7BLPx$zSi;LNNX4b4(bFVdP-TMzQ%lY)p8;FS@m%Tmk z_+ccVV6(}tAca!>!GO>Ao|T3HFZrik_OcV{yFdq?sR4-xWP8h(ENGp9|NGg&KcV^g zg!AFq#P0$NJFCvhCU(C@kK}zgui6Nxs^&W*+drZH=G8w%wl=iJPI*+PhVp+7L3KkSSfyC3>9Ij7`nWwbj_YK&W*5;x)e_K7oJW!ZkC3k1%+dGWU2aVRD=})^P&mA4^oL{1tSa)5$8xsGOnq3t6jSD zlT`!9aL5ilabpXqy#0%bo%=2@&ficgma*KXUkK>5uNYuGRWehTmq|SO0&s+3K~^rp zq(;ANW$mY+^BVhOyq^*SE$IRxGZH#_F7+N@)JVuW?LYq@NVyL0HWc~UFQ=*YMlaGd zXAtW5QJ}F2hF;bHeil3dY2RLnz;$>;kkxIH(i9tbLKgx9+;~tq8JhaFnxI^ zU-hqLiWBPV5r9SqJ?OJgGN~a;=CQUjb9_Gf0hU`6P}b@Ij*axHNht zU6LPUO0RzF%7Prro9y)sCjzOG#9ZD_Y*uw%XrnUg-CA`ftJ?8+Vy4QG?A86QIUvyW z8V4WA6UxWuSZH>rSq2*>1#KG*v?Y?9>9Hu?&!~K&e<8pFGKwbScE8GbYGzK9?Y7~D z#js`jD5;WnarBUlO*o2CQNox{gADwprc0h)XH`R0XIK&U4NswRE;Os2LN!^)+Hki- zfIHb!uEP-!eU`#^a(p{fLT^)%qiN&e%GAI4t+RPGZbjbEdE=mU_wbRtUvN8b8f$WM^oaF0ti9{k`YyIoucIxA2U@9ZZ$zb5OuLnN*_Sa*T_CL^uno4U z0OQLv4>o2hq$ihmB3U@?y>4Zo0CliPat5(LcN5P}mOdHRLY6j@BDdkc+yZVRTs_g9 z?^cHA?-G0YzZTg)D`ldZ#Oyp;j^wd$0Le(&Vc;M+EqjehKn7RWaI-j1gTC!|V&uhDhjwo~?RbUlK`Lx--l#d)OA*0v zTYGn|Nke)njoh=pF72oGEof3X33;uOZihUxr-MitPySSvQ%I||riGdf`DZQ9T7 zox^<>E3fl0tP&+LC}tSBJ>r7%UBAnTy?Jq6C5qcV-^8GrdHLO0212c# z9}I-e$kv?fUb;p+;V+n#aze@nN|G5^PaqabxUc;)Sp_w>$Q#og<#JHClA)$0cW2{E zoBkJuqY#ino;@GUp#L4`x~@kIv2Jy(pVuR_GUYcw5n?;uCJ@c*jE)Z65yq1R!%Yfe$fbiYQBa9uAteL=D#z|0`G_}-vje;+BxnwZR< zEYQcOVh=-;Bn)K!66~lqjwdK^XbO54V8;*CsBI?>f5qWPkQSAaO;&Jv1F>n@(YoU7 zoobqHhWK|)x+n6(3BrkIhFXu9AJYb~VEw*kob>Qp3g(uS8HFEn2JQP!qWC-?u1Ce> zx*FG>=|8kmBZha1IdSLl-$pC;wuD!Os->#tGj+{Xa*9ZUr@-X%OnPIRRbu=NpX#;M zSN1Y)i@ji|r`OXOk4>&Twau*2^I-<>!zVQFOpT!}!$rDV5rXwZ16xhksNw{gBR?KK z+Tz(%-u_g()a9*yi6M~tZswzzDZX7CJ4(BxknY1vT7uHR5kV*~KI+zW&Q@Bem!mLd zg78!!_py!RwNS6ma14ej}RIml_U#Lex@wG|Nf>zX^lmL*nz>= z#3Iq-0h7J*HNeKaUc;%1VClTgo4_v?mW0DbX{+G;iQn6Ka_}E-#6BE_A~(2WrKnAy z(~(@$`1SWCwco%e!(dLtVs&(OR5=^XMK-Wa$GWVik>BpDRA_3zWBm+D+JB>Ss;w(D zX&>W9N6&93BB&J%RA?US)-;^16Tzwr1UC*pvp3b+=aEn{P1QVW^5ah(j-Qr|N__iY zajoAZPo1hl4OG>YM5Pb9NlhAbJDy`?4!t^443q4M#|`#b_xY(0O_Se9w7IjK6m4hY z*em@xDhm+I!01?6@5%Xu-o=vIP^1e+Ug!Ho`4Q3m2f;6^f(>usZ8A5mnJ~jDdnsA? zoC8QcmB}2zq|uGB!y98o6xb$VJsk48020fftJjlfB`QO0U@hiiQ2E6$r#@_2F)A$? z&#o_VaWUtwzv+55QuH;5VDt-egB9~UVp*Hh>|XLi^lCKkHHMALJq4vk7o?}4^o(l)WV4v{EkF=c) za$tQ}4?*HwtVYLkoB6V%dEk4IcUATPXoac?eO)b~1fU7s3m;D9zTP>7t|fx`US+dZu=rEKmR_ZnUJdcexDQF---(mqXs99N8M~t$j$?jB zxVYm|5obh1l@a;B>jmvn7COF&OxZ zw_^AoS*0)CeuFx-na68iOknRAJ%SS^N3lr?cVb7rLUBN4NQ6z>{R?%l?0B|Cgg1O6 zJh^Oj_F81UbRiPHI4t%bm~L3dRG0&-8y!y~$rB!+qr&AN-XfSdFvQ`sevyl){Da6k zSHmot42Ln9npEIm7Pag&K%R|dHj)$3I?#9Ipa<=t((M9tywf`xb2%CV8%^hJ{<9__Ji-m2KcGO8x(%x_G<#aWp9qn5esHpGDw@5810xQLc4oWK%L zOO_n+hQ_`o-^-IB2>Lsq`dv83z)5Kd3jGm*f;ukiva|kTn!hu(|ATB3JfuX83Ul& zW>%-HrFUkA`JPwW|7Kf6or#e~aQ$H4EhYmjFh7gFAtS4(jCq{6}H+E#yoAIZY3HXF1oUXV*fyr3uc* zg3kSkm3YMyXa$jH70%Wu1E@n&;L8?o%vHDX5VT*=WrY&oCw^uJFYS=ukjrI=S_aQV zFbMl>#BZTrx2Fp=5(%?YVf^n^aOMIpJ=)RMT2j6=Fb+zJR12v*mGZ?;bnvRtf{=Sy z{?swyVia8WcvX`41+0L4+F|jP2pwYWO58!@fk?bGfn>ctyV~8sch<^siU2nAz9b@X z>zqk~h>`TMy^?irQ4DwSF(@wEEDE+-lqed7nCFy0i`EPAMVN$kL2esGoqNNC#S`Tk zU<=hemVRCYt6}006k)>0C`p8~EmmAFJ_V`Q_mgG$vppxqw9T0oU>tGG46G{ctZ_UA zws_QsDtE`N(ml?1eBOQwtdl-cMvWw2~$TTO)yrz9F`GgVR$rgVQLsWh2C;l}}n*Hq%i! zJM^-omG1VhWeTNpVD|{$IOe;;rf<|P#p({=?7v9fL)^Up;QmwP5$(fqZOEdnPfH~c z41`bAPtEItv9zfD;+vZrtRuo*rUIC*$2_>h0 zNiuB&{vGh0zP^Pd1!NSxgczz#gl$;6K^4DOn9?tMNvijq=NS!VM{3ds0>9IA--<1P zuS8O=FX|`YvL{lfbXJ(CjaF*XR4q^k{%ZV_YrM;yq0)Y@d}TL!X-n`a^IO-7x2Cij zE~Y0hDI19%p!O#6Vs-$%^?SQ@%cxF>V5TW8e{}2DnFH92sd^@&cNe(&bq%-^hbSvY z%M4Qsn+m9lUVI!!L~Un!VW)ppx7m-!l3;X~>b8T4WS+1P0B56%CZXf9`qrU8Ud_P{ zL7jE;R8&pmfNT3vvD!k$mOTcay2Spb<7o}PPO@+FwX${GbnGKEvSmHjBKhd`-sHgG z;lyN+SWs8CB>i2_^_r(G7w*adAb7+$)h*chF829x$)fI4^Pv)Z~Eqo^u_rT#ts+v#ZS1!Ya-z8(-@SATZ9vAK9vRdG6r$ z9rQ#jY=I{?=~^9ltTpg;I{Q`*HV^MR0cu<1C8|)#qi2WM)Nu9z2#=f%+>?- z)0{}>l1?)9AQ)E$cA5>Zo1NHWEtt|0RS)xe-5p16r4cWCM6K{Xn5R z3oJ<|Ckv32J(l%SMC{c=+7%?@2~zqRfq0VBlT|c|n(AB62@CRje0AVmYTi9h6$TXj z;#FCiXn6Zmg;^~b4Zum_V;ljpB)FpA*MR2K9$8KpES5W_Oz5)BfRLONNg9Y=U7hP( zp15l3Sj3JtHez=3OAY!?ghlPeGl4{8;Lv@P?HNkiTo`bDL(4qE{Zi%cpZ~*jR&8qQ z*74{k65jUmrg4A2plOW6nSV2Go3)MpH;dNvmC_JG`%ivwbU8NV1G4Z>GI?&<%^&lT z(kn^-!>ho><-B4U>F~6T18^MvTeQrT+jWy&>G`Ml`VVDC^Yg)|CsgPIa>JTlwB)0L zSRSd*CoAlEcz7~G)FlXT=Oc%052~F#d+hJ@mj`c@?c2x0!`*QE+O?MG*y!l}+;Pzk z9@nl$t9x#t%l9ALfAFBtd5f6BR5!}l6eL61O z4dR;0&Gq}eDz;1W4*dtEJPp5g6hQE_QoOfnCS`Wwh)@$%Us{vxKkkg)QpN89ThMiL zNE1DlQBQNdRNLMiMM*I=tR*t;M|`D4_)5S#E~@SI8+bOBnrv^c8g%#9$nIp_p--Xi zUupE}o9k^oVGo9}-xV(j@q5Z}MJMl04e=8AD37U#vRxL85(R%qUf-?USvO>UIe9D^ z7_ge8d{*sgO~aH@--w%Wbs-}+azb|HAXkFe3w&uV1!YsL^1VpPl2;=H&$jao-X>4$7{D zBVFc`7w$mH(i;!KSq+rc)`}qPO^J4Ow0{+gm|1@dg}E%pS$*Q?(9{eP~uW3XJD#%&o#>zkH##Lk5> zr|UZxOMeZogJ8D;BF2~Z2k4|NN@q;hPaiv0?#~si&6tz&4P9xw9iSM3@X}7ZX8)Ph z2n3jeW~?PG(D!3v29d!SycDtcf=bue(w zqhGsjWs{rfVjr~?GFfZ;jE@^Cg2n~nm|CU3R>7h6roI`Vd8Y6C5Ls*~S#ExjF-ljA zlG?XhP=V*?y$@_GJpyTS4W&{)A7Xw(r#MuPJP^(n`h2JUOZ6I~1eMAg$bp zmf`PdEL@Vtroj?OHFK-0%{bu+r3aey1F+1_uE?A%`pTwq!z#MHb`k7%=infYB8qft z|6+k#Hw}&$0A$ zxuDeQ2niCLwu=lzm*$;fLEkSz?uR1KS@Jc()qc5lp+;oF$Lo9CX9IOypTA z9eHT}enZsl-T=9d;AQQ#@SlA30T4r9cYFyfRO!K$BIEhoh8&mnFT{I;L=c7i8IiMT zCojCl2zS%?T#-;-%4fE3IE+=)sZwM~mN z=8V#w$G0~6zL*}Wa@7rx{YD~$D~AZQlY@5y1WGyCb21?|RTfBc>`IG(Bibfiwb#*N zI<1ruVEcaEcFEQKL~)L*bX75T<3x1igjf|k0~94CKGOJIBsy@xUqgr=0}$}OOBJ;| zhx&P7?!tWo*@?<+J?o1U(UM;+{)H14P#MT$Ia&&hGA|DwnK}uxD=p>b)Eyc-X+A>y zDhJKzmBg2}1y=URZNx><$zXh8(DGknB)bc;{>_htkKnoF1qK%W1!*lc~+?dmR?M z6l`#OC9npfotRRX^O$Y$k^ZFE>ADsA_o|HIg1zGu2{In`BR}r|_qGTh7gON*Vq0?c zcFX4jL;MelW%D;K{pU?~FxFS#g)u^bubCsRVMgm7E%g3nM-TSb(hGGz2H$tr7@^F! ziPPa{^r~0NS8wpae}(MUz(;NO2Z@mQR!x%@U1t}ubd6Ay0;7n zf?;vty=>zapaiwe!~KTfpM5*@@Y%pkn;iG)cPac2tafiu=qeTM} zz|~$J7v{I;KzRN(82N>p0Qm8gos`$GXrgxfN`}}y-xxW57&4H_IYrQ5MIzH6R^eSC zTTMj>#af*^^Ae^R$jPFtHknUNBKX;3#XDD)U3(o$2S{#PL(y4LX$aa|ZQjIicff+! z<{e}d%D2sGT|#|E;qaKXn4Av^cM>U2{LT0|$XNHmw zUIC8w%co?!hIl&oUxeoCUWJpIg5K|K(pY3)9%Vk?GH$wd`yyJuye$HL9l z+DO-~@KDe@bnD=r!4jCacJHWs0T%)NGvn|Rn<;86+r#um#=)(=5N`k=CQrRfQ7#eM zyoJqbBw8tlC$E3OQwH)g_dk|42+U2}nXC}My6(`ts8tYi;&{e9ow@5n-dw2Gshd;j z2_-)t-#R~6yeLFkp{vZ2N7ieXTOK$MGm!oRA>L%^J!>Dxkl)?0+J!5>5VfhhWs`xu zI+$2qA~z8L-dd|A@qaLL7wyW21*nJQM;MwimL53Ni^~jSRe56<+}`JYj0k}VzgHsG zZ>-JHzee2_4LLApQ|uNxJl(~QN!)jly4OQB^G9#g=DbYG(MBausmzUp)dwO!9P3{p zvy~&vJ`dE5Og@M9j)g>sxPQ6L*QT{p{9!v%?msC%@0ab^&wR>tj`g+;B5}IOuAikl z)LB=2Y>KQIy$U@rq>~j&?sn~s9F2P2)R1{YfG@|8&ly@Pug^FbF4W$K8R)8ShZtil(ar{^pOV9`-BW9(0=2F^ z(@trfXBD2#_b=`7X57Mi3~ z~0VqA|(JlSNwtMk$xWp3q`eiep%Q&BOA=D=uO~) z7I&g1k|r(A%Rvf6$60$2vzf2B{%=;gje3~(h6gSwtL&#$;F0>#B$?qkA7x)ime`2q z9%_bnU6h(HSTRm(ng&M6N^NW+4JBpmM2saxYT{O&$ggEWKm613_qU)cefe`7+d~G+ z#-N@fq=6*h^C$|%2LYM}ZKZzbD_}eS{fW+4r%dKm%P^7bD$)A-*Qf2|DaQEZTWgyI zTL!0{Mj;&(jHIjh<`If?f!bsxBlVw6;?`qs~3b6 z3Z!EtJUI6|fN9Zwg_4rFYYu4xv;+Nl@Sl-P#Yt3CgxZxI{zww=!tN6Gkm2?ft<&6p z>=w}dpP#yEnb$FYT(#!O%jbFi`Ja4N^S|nIrmCq00MGy1>c7u9`KNXM)9C(+)Z5tq r*VzMl|EItIZS~*h{=fV^*FxCWA2k-{I#RZ (int random); +}; +``` + +This should have a corresponding build rule to generate C++ bindings for the +definition here: + +``` python +# src/example/public/mojom/BUILD.gn +import "mojo/public/tools/bindings/mojom.gni" +mojom("mojom") { + sources = [ "ping_responder.mojom" ] +} +``` + +### Creating the Pipe + +Now let's create a message pipe to use this interface. + +*** aside +As a general rule and as a matter of convenience when +using Mojo, the *client* of an interface (*i.e.* the InterfacePtr side) is +typically the party who creates a new pipe. This is convenient because the +InterfacePtr may be used to start sending messages immediately without waiting +for the InterfaceRequest endpoint to be transferred or bound anywhere. +*** + +This code would be placed somewhere in the renderer: + +```cpp +example::mojom::PingResponderPtr ping_responder; +example::mojom::PingResponderRequest request = + mojo::MakeRequest(&ping_responder); +``` + +In this example, ```ping_responder``` is the InterfacePtr, and ```request``` +is an InterfaceRequest, which is a Binding precursor that will eventually +be turned into a Binding. `mojo::MakeRequest` is the most common way to create +a message pipe: it yields both endpoints as strongly-typed objects, with the +`InterfacePtr` as an output argument and the `InterfaceRequest` as the return +value. + +*** aside +NOTE: Every mojom interface `T` generates corresponding C++ type aliases +`TPtr = InterfacePtr` and `TRequest = InterfaceRequest`. Chromium code +almost exclusively uses these aliases instead of writing out the more verbose +templated name. + +Also note that an InterfaceRequest doesn't actually **do** anything. It is an +inert holder of a single message pipe endpoint. It exists only to make its +endpoint more strongly-typed at compile-time, indicating that the endpoint +expects to be bound by a Binding of the same interface type. +*** + +### Sending a Message + +Finally, we can call the `Ping()` method on our InterfacePtr to send a message: + +```cpp +ping_responder->Ping(base::BindOnce(&OnPong)); +``` + +*** aside +**IMPORTANT:** If we want to receive the the response, we must keep the +`ping_responder` object alive until `OnPong` is invoked. After all, +`ping_responder` *owns* its message pipe endpoint. If it's destroyed then so is +the endpoint, and there will be nothing to receive the response message. +*** + +We're almost done! Of course, if everything were this easy, this document +wouldn't need to exist. We've taken the hard problem of sending a message from +a renderer process to the browser process, and transformed it into a problem +where we just need to take the `request` object from above and pass it to the +browser process somehow where it can be turned into a Binding that dispatches +its received messages. + +### Sending an InterfaceRequest to the Browser + +It's worth noting that InterfaceRequests (and message pipe endpoints in general) +are just another type of object that can be freely sent over mojom messages. +The most common way to get an InterfaceRequest somewhere is to pass it as a +method argument on some other already-connected interface. + +One such interface which we always have connected between a renderer's +`RenderFrameImpl` and its corresponding `RenderFrameHostImpl` in the browser +is +[`DocumentInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/frame/document_interface_broker.mojom). +We can update this definition to add support for our new PingResponder +interface: + +``` cpp +interface DocumentInterfaceBroker { + ... + + GetPingResponder(PingResponder& responder); +} +``` + +The `&` syntax is not a reference! In mojom it denotes an InterfaceRequest. +Specifically in this case, the `GetPingResponder` takes a single +`PingResponderRequest` argument. If the `&` were omitted, this would instead +take a `PingResponderPtr`. + +Now the renderer can call this method with the `request` object it created +earlier via `mojo::MakeRequest`: + +``` cpp +RenderFrame* my_frame = GetMyFrame(); +my_frame->GetDocumentInterfaceBroker()->GetPingResponder(std::move(request)); +``` + +This will transfer the PingResponderRequest endpoint to the browser process +where it will be received by the corresponding `DocumentInterfaceBroker` +implementation. More on that below. + +### Implementing the Interface + +Finally, we need a browser-side implementation of our `PingResponder` interface +as well as an implementation of the new +`DocumentInterfaceBroker.GetPingResponder` message. Let's implement +`PingResponder` first: + +```cpp +#include "example/public/mojom/ping_responder.mojom.h" + +class PingResponderImpl : example::mojom::PingResponder { + public: + explicit PingResponderImpl(example::mojom::PingResponderRequest request) + : binding_(this, std::move(request)) {} + + // example::mojom::PingResponder: + void Ping(PingCallback callback) override { + // Respond with a random 4, chosen by fair dice roll. + std::move(callback).Run(4); + } + + private: + mojo::Binding binding_; + + DISALLOW_COPY_AND_ASSIGN(PingResponderImpl); +}; +``` + +And conveniently `RenderFrameHostImpl` implements `DocumentInterfaceBroker`, and +any calls made on the object returned by +`RenderFrameImpl::GetDocumentInterfaceBroker()' will be routed directly to the +`RenderFrameHostImpl`. So the only thing left to do is update +`RenderFrameHostImpl` to implement `GetPingResponder`. If you forget to do this +the compiler will complain anyway, because generated mojom interface methods are +pure virtual methods in C++. + +``` cpp +// render_frame_host_impl.h +class RenderFrameHostImpl + ... + void GetPingResponder(example::mojom::PingResponderRequest request) override; + ... + private: + ... + std::unique_ptr ping_responder_; + ... +}; + +// render_frame_host_impl.cc +void RenderFrameHostImpl::GetPingResponder( + example::mojom::PingResponderRequest request) { + ping_responder_ = std::make_unique(std::move(request)); +} +``` + +And we're done. This setup is sufficient to plumb a new interface connection +between a renderer frame and its browser-side host object! + +Assuming we kept our `ping_responder` object alive in the renderer long enough, +we would eventually see its `OnPong` callback invoked with the totally random +value of `4`, as defined by the browser-side implementation above. + +## Services Overview & Terminology +The previous section only scratches the surface of how Mojo IPC is used in +Chromium. While renderer-to-browser messaging is simple and possibly the most +prevalent usage by sheer code volume, we are incrementally decomposing the +codebase into a set of services with a bit more granularity than the traditional +Content browser/renderer/gpu/utility process split. + +A **service** is a self-contained library of code which implements one or more +related features or behaviors and whose interaction with outside code is done +*exclusively* through Mojo interface connections facilitated by the **Service +Manager.** + +The **Service Manager** is a component which can run in a dedicated process +or embedded within another process. Only one Service Manager exists globally +across the system, and in Chromium the browser process runs an embedded Service +Manager instance immediately on startup. The Service Manager spawns +**service instances** on-demand, and it routes each interface request from a +service instance to some destination instance of the Service Manager's choosing. + +Each service instance implements the +[**`Service`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h) +interface to receive incoming interface requests brokered by the Service +Manager, and each service instance has a +[**`Connector`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h) +it can use to issue interface requests to other services via the +Service Manager. + +Every service has a **manifest** which declares some static metadata about the +service. This metadata is used by the Service Manager for various purposes, +including as a declaration of what interfaces are exposed to other services in +the system. This eases the security review process. + +Inside its manifest every service declares its **service name**, used to +identify instances of the service in the most general sense. Names are free-form +and usually short strings which must be globally unique. Some services defined +in Chromium today include `"device"`, `"identity"`, and `"network"` services. + +For more complete and in-depth coverage of the concepts covered here and other +related APIs, see the +[Service Manager documentation](/services/service_manager/README.md). + +## Example: Building a Simple Out-of-Process Service + +There are multiple steps required to get a new service up and running in +Chromium. You must: + +- Define the `Service` implementation +- Define the service's manifest +- Tell Chromium's Service Manager about the manifest +- Tell Chromium how to instantiate the `Service` implementation when it's needed + +This section walks through these steps with some brief explanations. For more +thorough documentation of the concepts and APIs used herein, see the +[Service Manager](/services/service_manager/README.md) and +[Mojo](/mojo/README.md) documentation. + +### Defining the Service + +Typically service definitions are placed in a `services` directory, either at +the top level of the tree or within some subdirectory. In this example, we'll +define a new service for use by Chrome specifically, so we'll define it within +`//chrome/services`. + +We can create the following files. First some mojoms: + +``` cpp +// src/chrome/services/math/public/mojom/constants.mojom +module math.mojom; + +// These are not used by the implementation directly, but will be used in +// following sections. +const string kServiceName = "math"; +const string kArithmeticCapability = "arithmetic"; +``` + +``` cpp +// src/chrome/services/math/public/mojom/divider.mojom +module math.mojom; + +interface Divider { + Divide(int32 dividend, int32 divisor) => (int32 quotient); +}; +``` + +``` python +# src/chrome/services/math/public/mojom/BUILD.gn +import "mojo/public/tools/bindings/mojom.gni" + +mojom("mojom") { + sources = [ + "constants.mojom", + "divider.mojom", + ] +} +``` + +Then the actual `Service` implementation: + +``` cpp +// src/chrome/services/math/math_service.h +#include "services/service_manager/public/cpp/service.h" + +#include "base/macros.h" +#include "chrome/services/math/public/mojom/divider.mojom.h" + +namespace math { + +class MathService : public service_manager::Service, + public mojom::Divider { + public: + explicit MathService(service_manager::mojom::ServiceRequest request); + ~MathService() override; + + private: + // service_manager::Service: + void OnBindInterface(const service_manager::BindSourceInfo& source, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) override; + + // mojom::Divider: + void Divide(int32_t dividend, + int32_t divisor, + DivideCallback callback) override; + + service_manager::ServiceBinding service_binding_; + + // You could also use a Binding. We use BindingSet to conveniently allow + // multiple clients to bind to the same instance of this class. See Mojo + // C++ Bindings documentation for more information. + mojo::BindingSet divider_bindings_; + + DISALLOW_COPY_AND_ASSIGN(MathService); +}; + +} // namespace math +``` + +``` cpp +// src/chrome/services/math/math_service.cc +#include "chrome/services/math/math_service.h" + +namespace math { + +MathService::MathService(service_manager::ServiceRequest request) + : service_binding_(this, std::move(request)) {} + +MathService::~MathService() = default; + +void MathService::OnBindInterface( + const service_manager::BindSourceInfo& source, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) { + // Note that services typically use a service_manager::BinderRegistry if they + // plan on handling many different interface request types. + if (interface_name == mojom::Divider::Name_) { + divider_bindings_.AddBinding( + this, mojom::DividerRequest(std::move(interface_pipe))); + } +} + +void MathService::Divide(int32_t dividend, + int32_t divisor, + DivideCallback callback) { + // Respond with the quotient! + callback.Run(dividend / divisor); +} + +} // namespace math +``` + +``` python +# src/chrome/services/math/BUILD.gn + +source_set("math") { + sources = [ + "math.cc", + "math.h", + ] + + deps = [ + "//base", + "//chrome/services/math/public/mojom", + "//services/service_manager/public/cpp", + ] +} +``` + +Now we have a fully defined `math` service implementation, including a nice +little `Divider` interface for clients to play with. Next we need to define the +service's manifest to declare how the service can be used. + +### Defining the Manifest +Manifests are defined as +[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h) +objects, typically built using a +[`ManifestBuilder`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h). As a general rule, services should define their manifest +in a dedicated `source_set` or `component` target under their `public/cpp` +subdirectory (typically referred to as the service's **C++ client library**). + +We can create the following files for this purpose: + +``` cpp +// src/chrome/services/math/public/cpp/manifest.h +#include "services/service_manager/public/cpp/manifest.h" + +namespace math { + +const service_manager::Manifest& GetManifest(); + +} // namespace math +``` + +``` cpp +// src/chrome/services/math/public/cpp/manifest.cc +#include "chrome/services/math/public/cpp/manifest.h" + +#include "base/no_destructor.h" +#include "chrome/services/math/public/mojom/constants.mojom.h" +#include "chrome/services/math/public/mojom/divider.mojom.h" +#include "services/service_manager/public/cpp/manifest_builder.h" + +namespace math { + +const service_manager::Manifest& GetManifest() { + static base::NoDestructor manifest{ + service_manager::ManifestBuilder() + .WithServiceName(mojom::kServiceName) + .ExposeCapability( + mojom::kArithmeticCapability, + service_manager::Manifest::InterfaceList()) + .Build()}; + return *manifest +} + +} // namespace math +``` + +We also need to define a build target for our manifest sources: + +``` python +# src/chrome/services/math/public/cpp/BUILD.gn + +source_set("manifest") { + sources = [ + "manifest.cc", + "manifest.h", + ] + + deps = [ + "//base", + "//chrome/services/math/public/mojom", + "//services/service_manager/public/cpp", + ] +} +``` + +The above `Manifest` definition declares that the service is named `math` and +that it **exposes** a single **capability** named `arithmetic` which allows +access to the `Divider` interface. + +Another service may **require** this capability from its own manifest in order +for the Service Manager to grant it access to a `Divider`. We'll see this a +few sections below. First, let's get the manifest and service implementation +registered with Chromium's Service Manager. + +### Registering the Manifest + +For the most common out-of-process service cases, we register service manifests +by **packaging** them in Chrome. This can be done by augmenting the value +returned by +[`GetChromePackagedServiceManifests`](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=af43cabf3c01e28be437becb972a7eae44fd54e8&l=133). + +We can add our manifest there: + +``` cpp +// Deep within src/chrome/app/chrome_packaged_service_manifests.cc... +const std::vector +GetChromePackagedServiceManifests() { + ... + math::GetManifest(), + ... +``` + +And don't forget to add a GN dependency from +`//chrome/app:packaged_service_manifests` onto +`//chrome/services/math/public/cpp:manifest`! + +We're almost done with service setup. The last step is to teach Chromium (and +thus the Service Manager) how to launch an instance of our beautiful `math` +service. + +### Hooking Up the Service Implementation + +There are two parts to this for an out-of-process Chrome service. + +First, we need +to inform the embedded Service Manager that this service is an out-of-process +service. The goofiness of this part is a product of some legacy issues and it +should be eliminated soon, but for now it just means teaching the Service +Manager how to *label* the process it creates for this service (e.g. how the process will +appear in the system task manager). We modify +[`ChromeContentBrowserClient::RegisterOutOfProcessServices`](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=960886a7febcc2acccea7f797d3d5e03a344a12c&l=3766) +for this: + +``` cpp +void ChromeContentBrowserClient::RegisterOutOfProcessServices( + OutOfProcessServicesMap* services) { + ... + + (*services)[math::mojom::kServiceName] = + base::BindRepeating([]() -> base::string16 { + return "Math Service"; + }); + + ... +} +``` + +And finally, since nearly all out-of-process services run in a "utility" process +today, we need to add a dependency on our actual `Service` implementation to +Chrome's service spawning code within the utility process. + +For this step we just modify +[`ChromeContentUtilityClient::MaybeCreateMainThreadService`](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=7226adebd6e8d077d673a82acf1aab0790627178&l=261) +by adding a block of code as follows: + +``` cpp +void ChromeContentUtilityClient::MaybeCreateMainThreadService( + const std::string& service_name, + service_manager::mojom::ServiceRequest request) { + ... + + if (service_name == math::mojom::kServiceName) + return std::make_unique(std::move(request)); + + ... +} +``` + +And we're done! + +As one nice follow-up step, let's use our math service from the browser. + +### Using the Service + +We can grant the browser process access to our `Divider` interface by +**requiring** the `math` service's `arithmetic` capability within the +`content_browser` service manifest. + +*** aside +NOTE: See the following section for an elaboration on what `content_browser` is. +For the sake of this example, it's magic. +*** + +For Chrome-specific features such as our glorious new `math` service, we can +amend the `content_browser` manifest by modifying +[GetChromeContentBrowserOverlayManifest](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=38db90321e8e3627b2f3165cdb051fa8d668af48&l=100) +as follows: + +``` cpp +// src/chrome/app/chrome_content_browser_overlay_manifest.cc + +... +const service_manager::Manifest& GetChromeContentBrowserOverlayManifest() { + ... + .RequireCapability(math::mojom::kServiceName, + math::mojom::kArithmeticCapability) + ... +} +``` + +Finally, we can use the global `content_browser` instance's `Connector` to send +an interface request to our service. This is accessible from the main thread of +the browser process. Somewhere in `src/chrome/browser`, we can write: + +``` cpp +// This gives us the global content_browser's Connector +service_manager::Connector* connector = + content::ServiceManagerConnection::GetForProcess()->GetConnector(); + +// Recall from the earlier Mojo section that mojo::MakeRequest creates a new +// message pipe for our interface. Connector passes the request endpoint to +// the Service Manager along with the name of our target service, "math". +math::mojom::DividerPtr divider; +connector->BindInterface(math::mojom::kServiceName, + mojo::MakeRequest(÷r)); + +// As a client, we do not have to wait for any acknowledgement or confirmation +// of a connection. We can start queueing messages immediately and they will be +// delivered as soon as the service is up and running. +divider->Divide( + 42, 6, base::BindOnce([](int32_t quotient) { LOG(INFO) << quotient; })); +``` + +This should successfully spawn a new process to run the `math` service if it's +not already running, then ask it to do a division, and ultimately log the result +after it's sent back to the browser process. + +Finally it's worth reiterating that every service instance in the system has +its own `Connector` and there's no reason we have to limit ourselves to +`content_browser` as the client, as long as the appropriate manifest declares +that it requires our `arithmetic` capability. + +If we did not update the `content_browser` manifest overlay as we did in this +example, the `Divide` call would never reach the `math` service (in fact the +service wouldn't even be started) and instead we'd get an error message (or in +developer builds, an assertion failure) informing us that the Service Manager +blocked the `BindInterface` call. + +## Content-Layer Services Overview + +Apart from very early initialization steps in the browser process, every bit of +logic in Chromium today is effectively running as part of one service instance +or another. + +Although we continue to migrate parts of the browser's privileged +functionality to more granular services defined below the Content layer, the +main services defined in Chromium today continue to model the Content layer's +classical multiprocess architecture which defines a handful of +**process types**: browser, renderer, gpu, utility, and plugin processes. For +each of these process types, we now define corresponding services. + +Manifest definitions for all of the following services can be found in +`//content/public/app`. + +### The Browser Service + +`content_browser` is defined to encapsulate general-purpose browser process +code. There are multiple instances of this service, all running within the +singular browser process. There is one shared global instance as well an +additional instance for each `BrowserContext` (*i.e.* per Chrome profile). + +The global instance exists primarily so that arbitrary browser process code can +reach various system services conveniently via a global `Connector` instance +on the main thread. + +Each instance associated with a `BrowserContext` is placed in an isolated +instance group specific to that `BrowserContext`. This limits the service +instances with which its `Connector` can make contact. These instances are +used primarily to facilitate the spawning of other isolated per-profile service +instances, such as renderers and plugins. + +### The Renderer Service + +A `content_renderer` instance is spawned in its own sandboxed process for every +site-isolated instance of Blink we require. Instances are placed in the same +instance group as the renderer's corresponding `BrowserContext`, *i.e.* the +profile which navigated to the site being rendered. + +Most interfaces used by `content_renderer` are not brokered through the Service +Manager but instead are brokered through dedicated interfaces implemented by +`content_browser`, with which each renderer maintains persistent connections. + +### The GPU Service + +Only a single instance of `content_gpu` exists at a time and it always runs in +its own isolated, sandboxed process. This service hosts the code in content/gpu +and whatever else Content's embedder adds to that for GPU support. + +### The Plugin Service + +`content_plugin` hosts a plugin in an isolated process. Similarly to +`content_renderer` instances, each instance of `content_plugin` belongs to +an instance group associated with a specific `BrowserContext`, and in general +plugins get most of their functionality by talking directly to `content_browser` +rather than brokering interface requests through the Service Manager. + +### The Utility Service + +`content_utility` exists only nominally today, as there is no remaining API +surface within Content which would allow a caller to explicitly create an +instance of it. Instead, this service is used exclusively to bootstrap new +isolated processes in which other services will run. + +## Exposing Interfaces Between Content Processes + +Apart from the standard Service Manager APIs, the Content layer defines a number +of additional concepts for Content and its embedder to expose interfaces +specifically between Content processes in various contexts. + +### Exposing Browser Interfaces to Renderer Documents and Workers + +Documents and workers are somewhat of a special case since interface access +decisions often require browser-centric state that the Service Manager cannot +know about, such as details of the current `BrowserContext`, the origin of the +renderered content, installed extensions in the renderer, *etc.* For this +reason, interface brokering decisions are increasingly being made by the +browser. + +There are two ways this is done: the Deprecated way and the New way. + +#### The Deprecated Way: InterfaceProvider + +This is built on the concept of **interface filters** and the +**`InterfaceProvider`** interface. It is **deprecated** and new features should +use [The New Way](#The-New-Way_Interface-Brokers) instead. This section only +briefly covers practical usage in Chromium. + +The `content_browser` manifest exposes capabilities on a few named interface +filters, the main one being `"navigation:frame"`. There are others scoped to +different worker contexts, *e.g.* `"navigation:service_worker"`. +`RenderProcessHostImpl` or `RenderFrameHostImpl` sets up an `InterfaceProvider` +for each known execution context in the corresponding renderer, filtered through +the Service Manager according to one of the named filters. + +The practical result of all this means the interface must be listed in the +`content_browser` manifest under the +`ExposeInterfaceFilterCapability_Deprecated("navigation:frame", "renderer", ...)` +entry, and a corresponding interface request handler must be registered with the +host's `registry_` in +[`RenderFrameHostImpl::RegisterMojoInterfaces`](https://cs.chromium.org/chromium/src/content/browser/frame_host/render_frame_host_impl.cc?rcl=0a23c78c57ecb2405837155aa0a0def7b5ba9c22&l=3971) + +Similarly for worker contexts, an interface must be exposed by the `"renderer"` +capability on the corresponding interface filter +(*e.g.*, `"navigation:shared_worker"`) and a request handler must be registered +within +[`RendererInterfaceBinders::InitializeParameterizedBinderRegistry`](https://cs.chromium.org/chromium/src/content/browser/renderer_interface_binders.cc?rcl=0a23c78c57ecb2405837155aa0a0def7b5ba9c22&l=116). + +The best way to understand all of this after reading this section is to look at +the linked code above and examine a few examples. They are fairly repetitive. +For additional convenience, here is also a link to the `content_browser` +[manifest](https://cs.chromium.org/chromium/src/content/public/app/content_browser_manifest.cc). + +#### The New Way: Interface Brokers + +*** aside +In classic Google tradition, the New Way is not entirely ready yet. As of this +writing, worker-scoped interfaces must still use the Old Way described above. +*** + +Rather than the confusing spaghetti of interface filter logic, we now define an +explicit mojom interface with a persistent connection between a renderer's +frame object and the corresponding `RenderFrameHostImpl` in the browser process. +This interface is called +[`DocumentInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/frame/document_interface_broker.mojom?rcl=ea6921f717f21e9a72d321a15c4bf50d47d10310&l=11) +and is fairly easy to work with: you simply add a new factory method to the +interface definition: + +``` cpp +interface DocumentInterfaceBroker { + ... + + GetGoatTeleporter(magic.mojom.GoatTeleporter& request); +}; +``` + +and implement this new method on `RenderFrameHostImpl`, which is an +implementation (**the** production implementation) of +`DocumentInterfaceBroker`: + +``` cpp +void RenderFrameHostImpl::GetGoatTeleporter( + magic::mojom::GoatTeleporterRequest request) { + goat_teleporter_binding_.Bind(std::move(request)); +} +``` + +### Exposing Browser Interfaces to Render Processes + +Sometimes (albeit rarely) it's useful to expose a browser interface directly to +a renderer process. This can be done as for any other interface exposed between +two services. In this specific instance, the `content_browser` manifest exposes +a capability named `"renderer"` which `content_renderer` requires. Any interface +listed as part of that capability can be accessed by a `content_renderer` +instance by using its own `Connector`. See below. + +### Exposing Browser Interfaces to Content Child Processes + +All Content child process types (renderer, GPU, and plugin) share a common API +to interface with the Service Manager. Their Service Manager connection is +initialized and maintained by `ChildThreadImpl` on process startup, and from +the main thread, you can access the process's `Connector` as follows: + +``` cpp +auto* connector = content::ChildThread::Get()->GetConnector(); + +// For example... +connector->BindInterface(content::mojom::kBrowserServiceName, + std::move(some_request)); +``` + +### Exposing Content Child Process Interfaces to the Browser + +Content child processes may also expose interfaces to the browser, though this +is much less common and requires a fair bit of caution since the browser must be +careful to only call `Connector.BindInterface` in these cases with an exact +`service_manager::Identity` to avoid unexpected behavior. + +Every child process provides a subclass of ChildThreadImpl, and this can be used +to install a new `ConnectionFilter` on the process's Service Manager connection +before starting to accept requests. + +This behavior should really be considered deprecated, but for posterity, here is +how the GPU process does it: + +1. [Disable Service Manager connection auto-start](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=62) +2. [Register a new ConnectionFilter impl to handle certain interface requests](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=255) +3. [Start the Service Manager connection manually](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=257) + +It's much more common instead for there to be some primordial interface +connection established by the child process which can then be used to facilitate +push communications from the browser, so please consider not duplicating this +behavior. + +## Additional Support + +If this document was not helpful in some way, please post a message to your +friendly +[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) +or +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) +mailing list. diff --git a/docs/mojo_guide.md b/docs/mojo_guide.md deleted file mode 100644 index c5ec846ce5ab2f..00000000000000 --- a/docs/mojo_guide.md +++ /dev/null @@ -1,157 +0,0 @@ -# Mojo For Chromium Developers - -## Overview - -This document contains the minimum amount of information needed for a developer -to start using Mojo in Chromium. For more detailed documentation on the C++ -bindings, see [this link](/mojo/public/cpp/bindings/README.md). - -## Terminology - -A **message pipe** is a pair of **endpoints**. Each endpoint has a queue of -incoming messages, and writing a message to one endpoint effectively enqueues -that message on the other endpoint. Message pipes are thus bidirectional. - -A **mojom** file describes **interfaces** which describe strongly typed message -structures, similar to proto files. - -Given a **mojom interface** and a **message pipe**, the two **endpoints** -can be given the labels **InterfacePtr** and **Binding**. This now describes a -strongly typed **message pipe** which transports messages described by the -**mojom interface**. The **InterfacePtr** is the **endpoint** which "sends" -messages, and the **Binding** "receives" messages. Note that the **message -pipe** itself is still bidirectional, and it's possible for a message to have a -response callback, which the **InterfacePtr** would receive. - -Another way to think of this is that an **InterfacePtr** is capable of making -remote calls on an implementation of the mojom interface associated with the -**Binding**. - -The **Binding** itself is just glue that wires the endpoint's message queue up -to some implementation of the interface provided by the developer. - -## Example - -Let's apply this to Chrome. Let's say we want to send a "Ping" message from a -Browser to a Renderer. First we need to define the mojom interface. - -``` -module example.mojom; -interface PingResponder { - // Receives a "Ping" and responds with a random integer. - Ping() => (int random); -}; -``` - -Now let's make a MessagePipe. -```cpp -example::mojom::PingResponderPtr ping_responder; -example::mojom::PingResponderRequest request = mojo::MakeRequest(&ping_responder); -``` - -In this example, ```ping_responder``` is the **InterfacePtr**, and ```request``` -is an **InterfaceRequest**, which is a **Binding** precursor that will shortly -be turned into a **Binding**. Now we can send our Ping message. - -```cpp -auto callback = base::Bind(&OnPong); -ping_responder->Ping(callback); -``` - -Important aside: If we want to receive the the response, we must keep the object -```ping_responder``` alive. After all, it's just a wrapper around a **Message -Pipe endpoint**, if it were to go away, there'd be nothing left to receive the -response. - -We're done! Of course, if everything were this easy, this document wouldn't need -to exist. We've taken the hard problem of sending a message from the Browser to -a Renderer, and transformed it into a problem where we just need to take the -```request``` object, pass it to the Renderer, turn it into a **Binding**, and -implement the interface. - -In Chrome, processes host services, and the services themselves are connected to -a Service Manager via **message pipes**. It's easy to pass ```request``` to the -appropriate Renderer using the Service Manager, but this requires explicitly -declaring our intentions via manifest files. For this example, we'll use the -content_browser service [manifest -file](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json) -and the content_renderer service [manifest -file](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_renderer_manifest.json). - -```js -content_renderer_manifest.json: -... - "interface_provider_specs": { - "service_manager:connector": { - "provides": { - "cool_ping_feature": [ - "example::mojom::PingResponder" - ] - }, - }, -... -``` - -```js -content_browser_manifest.json: -... - "interface_provider_specs": { - "service_manager:connector": { - "requires": { - "content_renderer": [ "cool_ping_feature" ], - }, - }, - }, -... -``` - -These changes indicate that the content_renderer service provides the interface -PingResponder, under the **capability** named "cool_ping_feature". And the -content_browser services intends to use this feature. -```content::BindInterface``` is a helper function that takes ```request``` and -sends it to the renderer process via the Service Manager. - -```cpp -content::RenderProcessHost* host = GetRenderProcessHost(); -content::BindInterface(host, std::move(request)); -``` - -Putting this all together for the browser process: -```cpp -example::mojom::PingResponderPtr ping_responder; // Make sure to keep this alive! Otherwise the response will never be received. -example::mojom::PingResponderRequest request = mojo::MakeRequest(&ping_responder); -ping_responder->Ping(base::BindOnce(&OnPong)); -content::RenderProcessHost* host = GetRenderProcessHost(); -content::BindInterface(host, std::move(request)); -``` - -In the Renderer process, we need to write an implementation for PingResponder, -and ensure that a **Binding** is created using the transported ```request```. In a -standalone Mojo service, this would require us to implement -```service_manager::Service::OnBindInterface()```. In Chrome, this is abstracted -behind ```content::ConnectionFilters``` and -```service_manager::BinderRegistry```. This is typically done in -```RenderThreadImpl::Init```. - -```cpp -class PingResponderImpl : mojom::PingResponder { - void BindToInterface(example::mojom::PingResponderRequest request) { - binding_.reset( - new mojo::Binding(this, std::move(request))); - } - void Ping(PingCallback callback) { std::move(callback).Run(4); } - std::unique_ptr> binding_; -}; - -RenderThreadImpl::Init() { -... - this->ping_responder = std::make_unique(); - auto registry = base::MakeUnique(); - - // This makes the assumption that |this->ping_responder| will outlive |registry|. - registry->AddInterface(base::Bind(&PingResponderImpl::BindToInterface), base::Unretained(this->ping_responder.get())); - - GetServiceManagerConnection()->AddConnectionFilter( - base::MakeUnique(std::move(registry))); -... -``` diff --git a/docs/mojo_ipc_conversion.md b/docs/mojo_ipc_conversion.md new file mode 100644 index 00000000000000..2a4c81dca70c13 --- /dev/null +++ b/docs/mojo_ipc_conversion.md @@ -0,0 +1,374 @@ +# Converting Legacy IPC to Mojo + +[TOC] + +## Overview + +A number of IPC messages sent (primarily between the browser and renderer +processes) are still defined using Chrome's old IPC system in `//ipc`. This +system uses +[`base::Pickle`](https://cs.chromium.org/chromium/src/base/pickle.h?rcl=8b7842262ee1239b1f3ae20b9c851748ef0b9a8b&l=128) +as the basis for message serialization and is supported by a number if `IPC_*` +preprocessor macros defined in `//ipc` and used around the source tree. + +There is an ongoing, distributed effort to get these messages converted to Mojo +interface messages. Messages that still need to be converted are tracked in two +spreadsheets: + +- [Chrome IPC to Mojo migration](https://docs.google.com/spreadsheets/d/1pGWX_wxGdjAVtQOmlDDfhuIc3Pbwg9FtvFXAXYu7b7c/edit#gid=0) for non-web platform messages +- [Mojoifying Platform Features](https://docs.google.com/spreadsheets/d/1VIINt17Dg2cJjPpoJ_HY3HI0uLpidql-1u8pBJtpbGk/edit#gid=1603373208) for web platform messages + +This document is concerned primarily with rote conversion of legacy IPC messages +to Mojo interface messages. If you are considering more holistic refactoring and +better isolation of an entire subsystem of the browser, you may consider +[servicifying](servicification.md) the feature instead of merely converting its +IPCs. + +See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation +for introductory guides, API references, and more. + +## Legacy IPC Concepts + +Each Content child process has a single **`IPC::Channel`** implementation going +between it and the browser process, and this is used as the sole two-way FIFO +to send legacy IPC messages between the processes. + +There are two fundamental types of legacy IPC messages: **control** messages, +defined via `IPC_MESSAGE_CONTROLn` macros (where `n` is some small integer) and +**routed** messages defined via `IPC_MESSAGE_ROUTEDn` macros. + +Control messages generally go between a browser-side process host (*e.g.*, +`RenderProcessHost` or `GpuProcessHost`) and the child-side `ChildThreadImpl` +subclass. All of these classes implement `IPC::Sender` and thus have a `Send` +method for sending a control message to their remote counterpart, and they +implement `IPC::Listener` to receive incoming control messages via +`OnMessageReceived`. + +Routed messages are relegated to **routes** which have arbitrary meaning +determined by their use within a given process. For example, renderers use +routes to isolate messages scoped to individual render frames, and so such +routed messages will travel between a `RenderFrameHostImpl` and its +corresponding `RenderFrameImpl`, both of which also implement `IPC::Sender` and +`IPC::Listener`. + +## Mojo Interfaces as Routes + +Routed messages in the old IPC system always carry a **routing ID** to identify +to the receiving endpoint which routed object (*e.g.* which `RenderFrameImpl` +or `RenderViewImpl` or whatever) the message is targeting. Each endpoint is thus +required to do some additional book-keeping to track what each routing ID means. + +Mojo interfaces obviate the need for routing IDs, as new "routes" can be +established by simply creating a new interface pipe and passing one endpoint to +something which knows how to bind it. + +When thinking about an IPC message conversion to Mojo, it's important to +consider whether the message is a control message or a routed message, as this +determines where you might find an existing Mojo interface to carry your +message, or where you will want to add a new end-to-end Mojo interface for that +purpose. This can mean the difference between a single per-process interface +going between each `RenderProcessHostImpl` and its corresponding +`RenderThreadImpl`, vs a per-frame interface going between each +`RenderFrameHostImpl` and its corresponding `RenderFrameImpl`. + +## Ordering Considerations + +One **very important** consideration when doing IPC conversions is the relative +ordering of IPC-driven operations. With the old IPC system, because every +message between two processes is globally ordered, it is quite easy for parts +of the system to (intentionally or often unintentionally) rely on strict +ordering guarantees. + +For example, imagine a `WebContentsObserver` in the browser processes observes +a frame navigation and immediately sends an IPC message to the frame to +configure some new behavior. The implementation may be inadvertently relying on +this message arriving *before* some other tangentially related message sent to +the same frame shortly after the same navigation event. + +Mojo does not (and in fact cannot) make any strict ordering guarantees between +separate message pipes, as message pipes may be freely moved across process +boundaries and thus cannot necessarily share a common FIFO at all times. + +If the two messages described above were moved to separate Mojo interfaces on +separate message pipes, renderer behavior could break as the first message may +arrive after the second message. + +The best solution to this problem is to rethink the IPC surface and/or +implementation on either side to eliminate ordering dependencies between two +interfaces that otherwise seem to be logically distinct. Failing that, Mojo's +solution to this problem is to support +[**associated interfaces**](/mojo/public/tools/bindings/README.md#Associated-Interfaces). +In a nutshell, these allow multiple distinct interfaces to be multiplexed over +a shared message pipe. + +## Channel-Associated Interfaces + +The previous section mentions **associated interfaces** as a general-purpose +solution for establishing a mutual FIFO between multiple logical Mojo interfaces +by having them share a single message pipe. + +In Chrome, the `IPC::Channel` which carries all legacy IPC messages between +two processes is itself a Mojo message pipe. We provide a mechanism for +associating arbitrary Mojo interfaces with this pipe, which means messages can +be converted to Mojo while preserving strict FIFO with respect to other legacy +IPC messages. Such interfaces are designated in Chrome parlance as +**Channel-associated interfaces**. + +*** aside +**NOTE:** Channel-associated interface acquisition is not constrained by the +Service Manager in any way, so security reviewers need to be careful to inspect +new additions and uses of such interfaces. +*** + +Usage of Channel-associated interfaces should be rare but is considered a +reasonable intermediate solution for incremental IPC conversions where it would +be too risky or noisy to convert a large IPC surface all at once, but it would +also be impossible to split the IPC surface between legacy IPC and a dedicated +Mojo interface pipe without introducing timing bugs. + +At this point in Chrome's development, practical usage of Channel-associated +interfaces is restricted to the `IPC::Channel` between the browser process and +a renderer process, as this is the most complex IPC surface with the most +implicit ordering dependencies. A few simple APIs exist to support this. + +`RenderProcessHostImpl` owns an `IPC::Channel` to its corresponding +`RenderThreadImpl` in the render process. This object has a +`GetRemoteAssociatedInterfaces` method which can be used to pass arbitrary +associated interface requests: + +``` cpp +magic::mojom::GoatTeleporterAssociatedPtr teleporter; +channel_->GetRemoteAssociatedInterfaces()->GetInterface(&teleporter); + +// These messages are all guaranteed to arrive in the same order they were sent. +channel_->Send(new FooMsg_SomeLegacyIPC); +teleporter->TeleportAllGoats(); +channel_->Send(new FooMsg_AnotherLegacyIPC); +``` + +Likewise, `ChildThreadImpl` has an `IPC::Channel` that can be used in the same +way to send such messages back to the browser. + +To receive and bind incoming Channel-associated interface requests, the above +objects also implement `IPC::Listener::OnAssociatedInterfaceRequest`. + +For supplementation of routed messages, both `RenderFrameHostImpl` and +`RenderFrameImpl` define a `GetRemoteAssociatedInterfaces` method which works +like the one on `IPC::Channel`, and both objects also implement +`IPC::Listener::OnAssociatedInterfaceRequest` for processing incoming associated +interface requests specific to their own frame. + +There are some example conversion CLs which use Channel-associated interfaces +[here](https://codereview.chromium.org/2381493003) and +[here](https://codereview.chromium.org/2400313002). + +## Deciding How to Approach a Conversion + +There are a few questions you should ask before embarking upon any IPC message +conversion journey, and there are many potential approaches to consider. The +right one depends on context. + +Note that this section assumes the message is traveling between the browser +process and a renderer process. Other cases are rare and developers may wish to +consult +[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) +before proceeding with them. Otherwise, apply the following basic algorithm to +decide how to proceed: + +- General note: If the message is a reply to some other message (typically these + take a "request ID" argument), see the note about message replies at the + bottom of this section. +- Consider whether or not the message makes sense as part of the IPC surface of + a new or existing service somewhere in `//services` or `//chrome/services`, + *etc.* This is less and less likely to be the case as time goes on, as many + remaining IPC conversions are quite narrowly dealing with specific + browser/renderer details rather than the browser's supporting subsystems. If + defining a new service, you may wish to consult some of the other + [Mojo & Services documentation](/docs/README.md#Mojo-Services) first. +- If the message is an `IPC_MESSAGE_CONTROL` message: + - If there are likely to be strict ordering requirements between this + message and other legacy IPC or Channel-associated interface messages, + consider using a new or existing + [Channel-associated interface](#Channel-Associated-Interfaces) between + `RenderProcessHostImpl` and `RenderThreadImpl`. + - If the message is sent from a renderer to the browser: + - If an existing interface is bound by `RenderProcessHostImpl` and + requested through `RenderThread`'s Connector and seems to be a good + fit for the message, add the equivalent Mojo message to that + interface. + - If no such interface exists, consider adding one for this message and + any related messages. + - If the message is sent from the browser to a renderer: + - If an existing interface is bound by `RenderThreadImpl` and requested + through a `BrowserContext` Connector referencing a specific + `RenderProcessHost` [identity](https://cs.chromium.org/chromium/src/content/public/browser/render_process_host.h?rcl=1497b88b7d6400a2a5cced258df03d53800d7848&l=327), + and the interface seems to be a good fit for the message, add the + equivalent Mojo message to that interface. + - If no such interface exists, consider adding one for this message and + any related messages. +- If the message is an `IPC_MESSAGE_ROUTED` message: + - Determine what the routing endpoints are. If they are + `RenderFrameHostImpl` and `RenderFrameImpl`: + - If there are likely to be strict ordering requirements between this + message and other legacy IPC or Channel-associated interface messages, + consider using a new or existing + [Channel-associated interface](#Channel-Associated-Interfaces) between + `RenderFrameHostImpl` and `RenderFrameImpl`. + - If the message is sent from a renderer to the browser: + - If an existing interface is bound by `RenderFrameHostImpl` and + acquired either via `RenderFrame::GetRemoteInterfaces` or + `RenderFrame::GetDocumentInterfaceBroker` and the interface seems + to be a good fit for this message, add the equivalent Mojo message + to that interface. + - If no such interface exists, consider adding one and exposing it + via a new getter method on `DocumentInterfaceBroker`. See the + [simple example](/docs/mojo_and_services.md#Example_Defining-a-New-Frame-Interface) + earlier in this document. + - If the message is sent from the browser to a renderer, consider + adding a Mojo equivalent to the `content.mojom.Frame` interface + defined + [here](https://cs.chromium.org/chromium/src/content/common/frame.mojom?rcl=138b66744ee9ee853cbb0ae8437b71eaa1fafaa9&l=42). + - If the routing endpoints are **not** frame objects (for example, they may + be `RenderView`/`RenderViewHost` objects), this is a special case which + does not yet have an easy conversion approach readily available. Contact + [chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/chromium-mojo) + to propose or discuss options. + +### Dealing With Replies + +If the message is a **reply**, meaning it has a "request ID" which correlates it +to a prior message in the opposite direction, consider converting the +**request** message following the algorithm above. Unlike with legacy IPC, Mojo +messages support replies as a first-class concept. So for example if you have: + +``` cpp +IPC_CONTROL_MESSAGE2(FooHostMsg_DoTheThing, + int /* request_id */, + std::string /* name */); +IPC_CONTROL_MESSAGE2(FooMsg_DidTheThing, + int /* request_id */, + bool /* success */); +``` + +You should consider defining an interface `Foo` which is bound in +`RenderProcessHostImpl` and acquired from `RenderThreadImpl`, with the following +mojom definition: + +``` cpp +interface Foo { + DoTheThing(string name) => (bool success); +}; +``` + +## Repurposing `IPC::ParamTraits` and `IPC_STRUCT*` Invocations + +Occasionally it is useful to do partial IPC conversions, where you want to +convert a message to a Mojo interface method but you don't want to necessarily +convert every structure passed by the message. In this case, you can leverage +Mojo's +[type-mapping](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/cpp/bindings/README.md#Type-Mapping) +system to repurpose existing `IPC::ParamTraits`. + +*** aside +**NOTE**: Although in some cases `IPC::ParamTraits` specializations are +defined manually in library code, the `IPC_STRUCT*` macro helpers also define +`IPC::ParamTraits` specializations under the hood. All advice in this section +pertains to both kinds of definitions. +*** + +If a mojom struct is declared without a struct body and is tagged with +`[Native]`, and a corresponding typemap is provided for the struct, the emitted +C++ bindings will -- as if by magic -- replace the mojom type with the +typemapped C++ type and will internally use the existing `IPC::ParamTraits` +specialization for that type in order to serialize and deserialize the struct. + +For example, given the +[`resource_messages.h`](https://cs.chromium.org/chromium/src/content/common/resource_messages.h?rcl=2e7a430d8d88222c04ab3ffb0a143fa85b3cec5b&l=215) header +which defines an IPC mapping for `content::ResourceRequest`: + +``` cpp +IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest) + IPC_STRUCT_TRAITS_MEMBER(method) + IPC_STRUCT_TRAITS_MEMBER(url) + // ... +IPC_STRUCT_TRAITS_END() +``` + +and the +[`resource_request.h`](https://cs.chromium.org/chromium/src/content/common/resource_request.h?rcl=dce9e476a525e4ff0304787935dc1a8c38392ac8&l=32) header +which actually defines the `content::ResourceRequest` type: + +``` cpp +namespace content { + +struct CONTENT_EXPORT ResourceRequest { + // ... +}; + +} // namespace content +``` + +we can declare a corresponding "native" mojom struct: + +``` cpp +module content.mojom; + +[Native] +struct URLRequest; +``` + +and add a typemap like +[`url_request.typemap`](https://cs.chromium.org/chromium/src/content/common/url_request.typemap?rcl=4b5963fa744a706398f8f06a4cbbf70d7fa3213d) +to define how to map between them: + +``` python +mojom = "//content/public/common/url_loader.mojom" +public_headers = [ "//content/common/resource_request.h" ] +traits_headers = [ "//content/common/resource_messages.h" ] +... +type_mappings = [ "content.mojom.URLRequest=content::ResourceRequest" ] +``` + +Note specifically that public_headers includes the definition of the native C++ +type, and traits_headers includes the definition of the legacy IPC traits. + +As a result of all this, other mojom files can now reference +`content.mojom.URLRequest` as a type for method parameters and other struct +fields, and the generated C++ bindings will represent those values exclusively +as `content::ResourceRequest` objects. + +This same basic approach can be used to leverage existing `IPC_ENUM_TRAITS` for +invocations for `[Native]` mojom enum aliases. + +*** aside +**NOTE:** Use of `[Native]` mojom definitions is strictly limited to C++ +bindings. If a mojom message depends on such definitions, it cannot be sent or +received by other language bindings. This feature also depends on continued +support for legacy IPC serialization and all uses of it should therefore be +treated as technical debt. +*** + +## Typemaps For Content and Blink Types + +Using typemapping for messages that go between Blink and content browser code +can sometimes be tricky due to things like dependency cycles or confusion over +the correct place for some definition +to live. There are some example CLs provided here, but feel free to also contact +[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) +with specific details if you encounter trouble. + +[This CL](https://codereview.chromium.org/2363533002) introduces a Mojom +definition and typemap for `ui::WindowOpenDisposition` as a precursor to the +IPC conversion below. + +The [follow-up CL](https://codereview.chromium.org/2363573002) uses that +definition along with several other new typemaps (including native typemaps as +described above) to convert the relatively large `ViewHostMsg_CreateWindow` +message to Mojo. + +## Additional Support + +If this document was not helpful in some way, please post a message to your +friendly +[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) +mailing list. diff --git a/docs/servicification.md b/docs/servicification.md index 2a0c67b36490b5..d24dcd3686230e 100644 --- a/docs/servicification.md +++ b/docs/servicification.md @@ -1,102 +1,197 @@ -# Servicification Strategies - -This document captures strategies, hints, and best practices for solving typical -challenges enountered when converting existing Chromium -code to services. It is assumed that you have already read the high-level -documentation on [what a service is](/services). - -If you're looking for Mojo documentation, please see the [general -Mojo documentation](/mojo) and/or the [documentation on converting Chrome IPC to -Mojo](/ipc). - -Note that throughout the below document we link to CLs to illustrate the -strategies being made. Over the course of time code tends to shift, so it is -likely that the code on trunk does not exactly match what it was at the time of -the CLs. When necessary, use the CLs as a starting point for examining the -current state of the codebase with respect to these issues (e.g., exactly where -a service is embedded within the content layer). +# Servicifying Chromium Features [TOC] -## Questions to Answer When Getting Started - -For the basic nuts and bolts of how to create a new service, see [the -documentation on adding a new service](/services#Adding-a-new-service). This -section gives questions that you should answer in order to shape the design of -your service, as well as hints as to which answers make sense given your -situation. - -### Is your service global or per-BrowserContext? -The Service Manager can either: - -- create one service instance per instance group or -- field all connection requests for a given service via the same instance - -Which of these policies the Service Manager employs is determined by the -contents of your service manifest: the former is the default, while the latter -is selected by informing the Service Manager that your service has the -"instance_sharing" option value set to "shared_across_instance_groups" -([example](https://cs.chromium.org/chromium/src/services/device/manifest.json)). - -Service manifests are described in more detail in this -[document](https://chromium.googlesource.com/chromium/src/+/master/services/service_manager/service_manifests.md). - -In practice, there is one instance group per-BrowserContext, so the question -becomes: Is your Service a global or keyed by BrowserContext? In considering -this question, there is one obvious hint: If you are converting per-Profile -classes (e.g., KeyedServices), then your service is almost certainly going to -want an instance per BrowserContext. More generally, if you envision needing to -use *any* state related to the profile (e.g., you need to store files in the -user's home directory), then your service should have an instance -per-BrowserContext. - -Conversely, your service could be a good fit for being global if it is a utility -that is unconcerned with the identity of the requesting client (e.g., the [data -decoder service](/services/data_decoder), which simply decodes untrusted data in -a separate process. - -### Will you embed your service in //content, //chrome, or neither? - -At the start (and potentially even long-term), your service will likely not -actually run in its own process but will rather be embedded in the browser -process. This is especially true in the common case where you are converting -existing browser-process code. - -You then have a question: Where should it be embedded? The answer to this -question hinges on the nature and location of the code that you are converting: - -- //content is the obvious choice if you are converting existing //content code - (e.g., the Device Service). Global services - are embedded by [content::ServiceManagerContext](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?type=cs&q=CreateDeviceService), - while per-BrowserContext services are naturally embedded by [content::BrowserContext](https://cs.chromium.org/chromium/src/content/browser/browser_context.cc?type=cs&q=CreateFileService). - -- If your service is converting existing //chrome code, then you will need - to embed your service in //chrome rather than //content. Global services - are embedded by [ChromeContentBrowserClient](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?type=cs&q=CreateMediaService), - while per-Profile services are embedded by [ProfileImpl](https://cs.chromium.org/chromium/src/chrome/browser/profiles/profile_impl.cc?type=cs&q=CreateIdentityService). - -- If you are looking to convert all or part of a component (i.e., a feature in - //components) into a service, the question arises of whether your new service - is worthy of being in //services (i.e., is it a foundational service?). If - not, then it can be placed in //components/services. See this - [document](https://docs.google.com/document/d/1Zati5ZohwjUM0vz5qj6sWg5r-_I0iisUoSoAMNdd7C8/edit#) for discussion of this point. - -### If your service is embedded in the browser process, what is its threading model? - -If your service is embedded in the browser process, it will run on the IO thread -by default. You can change that by specifying a task runner as part of the -information for constructing your service. In particular, if the code that you -are converting is UI-thread code, then you likely want your service running on -the UI thread. Look at the changes to profile_impl.cc in [this -CL](https://codereview.chromium.org/2753753007) to see an example of setting the -task runner that a service should be run on as part of the factory for creating -the service. - -### What is your approach for incremental conversion? +## Overview + +Much to the dismay of Chromium developers, practicing linguists, and keyboard +operators everywhere, the term **servicificificification** [sic] has been +egregiously smuggled into the Chromium parlance. + +Lots of Chromium code is contained in reasonably well-isolated component +libraries with some occasionally fuzzy boundaries and often a surprising number +of gnarly runtime interdependencies among a complex graph of components. Y +implements one of Z's delegate interfaces, while X implements one of Y's +delegate interfaces, and now it's possible for some ridiculous bug to creep in +where W calls into Z at the wrong time and causes a crash in X. Yikes. + +Servicification embodies the ongoing process of **servicifying** Chromium +features and subsystems, or refactoring these collections of library code into +services with well-defined public API boundaries and very strong runtime +isolation via Mojo interfaces. + +The primary goals are to improve maintainability and extensibility of the system +over time, while also allowing for more flexible runtime configuration. For +example, with the Network Service in place we can now run the entire network +stack either inside or outside of the browser process with the flip of a +command-line switch. Client code using the Network Service stays the same, +independent of that switch. + +This document focuses on helpful guidelines and patterns for servicifying parts +of Chromium, taking into account some nuances in how Chromium models its core +services as well as how it embeds and configures the Service Manager. Readers +are strongly encouraged to first read some basic +[Service Manager documentation](/services/service_manager/README.md), as it will +likely make the contents of this document easier to digest. + +Also see general [Mojo & Services](/docs/README.md#Mojo-Services) +documentation for other introductory guides, API references, *etc.* + +## Setting Up The Service + +There are three big things you must decide when building and hooking up a shiny +new service: + +- Where should the service live in the tree? +- Do you need an instance of your service per BrowserContext? +- Can Content depend on your service, or must Content embedders like Chrome do + so independently? + +This section aims to help you understand and answer those questions. + +### Where in the Tree? + +Based on the +[service development guidelines](/services/README.md), any service which could +be reasonably justified as a core system service in a hypothetical, +well-designed operating system may belong in the top-level `//services` +directory. If that sounds super hand-wavy and unclear, that's because it is! +There isn't really a great universal policy here, so when in doubt, contact your +favorite local +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev) +mailing list and start a friendly discussion. + +Other common places where developers place services, and why: + +- `//components/services` for services which haven't yet made the cut for + `//services` but which are either used by Content directly or by multiple + Content embedders. +- `//chrome/services` for services which are used exclusively within Chrome and + not shared with other Content embedders. +- `//chromeos/services` for services which are used on Chrome OS by more than + just Chrome itself (for example, if the `ash` service must also connect to + them for use in system UI). + +### Inside Content or Not? + +The next decision you need to make is whether or not Content will wire in your +service directly -- that is, whether or not your service is necessary to support +some subsystem Content makes available to either the web platform or to Content +embedders like Chrome, Android WebView, Cast Shell, and various third-party +applications. + +For example, Content cannot function at all without the Network Service being +available, because Content depends heavily on the Network Service to issue and +process all of its network requests (imagine that, right?). As such, the +Network Service is wired up to the Service Manager from within Content directly. +In general, services which will be wired up in Content must live either in +`//services` or `//components/services` but ideally the former. + +Conversely there are a large number of services used only by Chrome today, +such as the `unzip` service which safely performs sandboxed unpacking of +compressed archive files on behalf of clients in the browser process. These +can always be placed in `//chrome/services`. + +### Per-BrowserContext or Not? + +Now that you've decided on a source location for your service and you know +whether it will be wired into Content or hooked up by Content embedder code, all +that's left left is to decide whether or not you want an instance of your service +per BrowserContext (*i.e.* per user profile in Chrome). + +The alternative is for you to manage your own instance arity, either as a +singleton service (quite common) or as a service which supports multiple +instances that are *not* each intrinsically tied to a BrowserContext. Most +services choose this path because BrowserContext coupling is typically +unnecessary. + +As a general rule, if you're porting a subsystem which today relies heavily +on `BrowserContextKeyedService`, it's likely that you want your service +instances to have a 1:1 correspondence with BrowserContext instances. + +### Putting It All Together + +Let's get down to brass tacks. You're a developer of action. You've made all the +important choices you need to make and you've even built a small and extremely +well-tested prototype service with the help of +[this glorious guide](/services/service_manager/README.md#Services). Now you +want to get it working in Chromium while suffering as little pain as possible. + +You're not going to believe it, but this section was written *just for YOU*. + +For services which are **are not** isolated per BrowserContext and which **can** +be wired directly into Content: + + - Include your service's manifest in the `content_packaged_services` manifest + directly, similar to + [these ones](https://cs.chromium.org/chromium/src/content/public/app/content_packaged_services_manifest.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=63). + - If you want to run your service embedded in the browser process, follow the + examples using `RegisterInProcessService` + [here](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=589). + - If you want to run your service out-of-process, update + `out_of_process_services` + [like so](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=642) + and hook up your actual private `Service` implementation exactly like the + many examples + [here](https://cs.chromium.org/chromium/src/content/utility/utility_service_factory.cc?rcl=2bdcc80a55c72a26ffe9778681f98dc4b6a565c0&l=114). + +For services which are **are** isolated per BrowserContext and which **can** +be wired directly into Content: + +- Include your service's manifest in the `content_browser` manifest directly, + similar to + [these ones](https://cs.chromium.org/chromium/src/content/public/app/content_browser_manifest.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=277). +- If you want to run your service embedded in the browser process, follow the + example + [here](https://cs.chromium.org/chromium/src/content/browser/browser_context.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=250) +- If you want to run your service out-of-process, you are doing something that + hasn't been done yet and you will need to build a new thing. + +For services which **are not** isolated per BrowserContext but which **can not** +be wired directly into Content: + +- Include your service's manifest in Chrome's `content_packaged_services` + manifest overlay similar to + [these ones](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=135) +- If you want to run your service embedded in the browser process, follow the + examples in `ChromeContentBrowserClient::HandleServiceRequest` + [here](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=3891) +- If you want to run your service out-of-process, modify + `ChromeContentBrowserClient::RegisterOutOfProcessServices` like the examples + [here](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=3785) + and hook up your `Service` implementation in + `ChromeContentUtilityClient::HandleServiceRequest` like the ones + [here](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=237). + +For services which **are** isolated per BrowserContext but which **can not** be +wired directly into Content: + +- Include your service's manifest in Chrome's `content_browser` manifest overlay + similar to + [these ones](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=247) +- If you want to run your service embedded in the browser process, follow the + examples in `ProfileImpl::HandleServiceRequest` + [here](https://cs.chromium.org/chromium/src/chrome/browser/profiles/profile_impl.cc?rcl=350aea1a0242c2ea8610c9f755acee085c74ea7d&l=1263) +- If you want to run your service out-of-process, you are doing something that + hasn't been done yet and you will need to build a new thing. + +*** aside +The non-Content examples above are obviously specific to Chrome as the embedder, +but Chrome's additions to supported services are all facilitated through the +common `ContentBrowserClient` and `ContentUtilityClient` APIs that all embedders +can implement. Mimicking what Chrome does should be sufficient for any embedder. +*** + +## Incremental Servicification + +For large Chromium features it is not feasible to convert an entire subsystem +to a service all at once. As a result, it may be necessary for the subsystem +to spend a considerable amount of time (weeks or months) split between the old +implementation and your beautiful, sparkling new service implementation. In creating your service, you likely have two goals: -- Making the service available to other services +- Making the service available to its consumers - Making the service self-contained Those two goals are not the same, and to some extent are at tension: @@ -111,7 +206,7 @@ Those two goals are not the same, and to some extent are at tension: Whatever your goals, you will need to proceed incrementally if your project is at all non-trivial (as they basically all are given the nature of the effort). You should explicitly decide what your approach to incremental bringup and -conversion will be. Here some approaches that have been taken for various +conversion will be. Here are some approaches that have been taken for various services: - Build out your service depending directly on existing code, @@ -139,11 +234,11 @@ service available for new use cases sooner at the cost of leaving legacy code in place longer, while the last is most suitable when you want to be very exacting about doing the servicification cleanly as you go. -## Platform-Specific Issues +## Platform-Specific Issues: Android -### Android As you servicify code running on Android, you might find that you need to port -interfaces that are served in Java. Here is an [example CL](https://codereview.chromium.org/2643713002) that gives a basic +interfaces that are served in Java. Here is an +[example CL](https://codereview.chromium.org/2643713002) that gives a basic pattern to follow in doing this. You also might need to register JNI in your service. That is simple to set @@ -156,50 +251,60 @@ Finally, it is possible that your feature will have coupling to UI process state (e.g., the Activity) via Android system APIs. To handle this challenging issue, see the section on [Coupling to UI](#Coupling-to-UI). -### iOS +## Platform-Specific Issues: iOS + +*** aside +**WARNING:** Some of this content is obsolete and needs to be updated. When in +doubt, look approximately near the recommended bits of code and try to find +relevant prior art. +*** Services are supported on iOS, with the usage model in //ios/web being very close to the usage model in //content. More specifically: * To embed a global service in the browser service, override - [WebClient::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::Register&sq=package:chromium&l=136). For an - example usage, see + [WebClient::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::Register&sq=package:chromium&l=136). For an example usage, see [ShellWebClient](https://cs.chromium.org/chromium/src/ios/web/shell/shell_web_client.mm?q=ShellWebClient::RegisterS&sq=package:chromium&l=91) - and the related integration test that [connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=89). + and the related integration test that + [connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=89). * To embed a per-BrowserState service, override [BrowserState::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/browser_state.h?q=BrowserState::RegisterServices&sq=package:chromium&l=89). For an example usage, see [ShellBrowserState](https://cs.chromium.org/chromium/src/ios/web/shell/shell_browser_state.mm?q=ShellBrowserState::RegisterServices&sq=package:chromium&l=48) - and the related integration test that [connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=110). + and the related integration test that + [connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=110). * To register a per-frame Mojo interface, override - [WebClient::BindInterfaceRequestFromMainFrame](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=148). For an - example usage, see + [WebClient::BindInterfaceRequestFromMainFrame](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=148). + For an example usage, see [ShellWebClient](https://cs.chromium.org/chromium/src/ios/web/shell/shell_web_client.mm?type=cs&q=ShellWebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=115) - and the related integration test that [connects to the interface](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=130). Note that this is the - equivalent of [ContentBrowserClient::BindInterfaceRequestFromFrame()](https://cs.chromium.org/chromium/src/content/public/browser/content_browser_client.h?type=cs&q=ContentBrowserClient::BindInterfaceRequestFromFrame&sq=package:chromium&l=667), as on iOS all operation "in the content area" is implicitly - operating in the context of the page's main frame. + and the related integration test that + [connects to the interface](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=130). + Note that this is the equivalent of + [ContentBrowserClient::BindInterfaceRequestFromFrame()](https://cs.chromium.org/chromium/src/content/public/browser/content_browser_client.h?type=cs&q=ContentBrowserClient::BindInterfaceRequestFromFrame&sq=package:chromium&l=667), + as on iOS all operation "in the content area" is implicitly operating in the + context of the page's main frame. If you have a use case or need for services on iOS, contact -blundell@chromium.org. For general information on the motivations and vision for supporting services on iOS, see the high-level [servicification design doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU/edit) (in particular, search for the mentions -of iOS within the doc). +blundell@chromium.org. For general information on the motivations and vision for +supporting services on iOS, see the high-level +[servicification design doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU/edit). +In particular, search for the mentions of iOS within the doc. ## Client-Specific Issues -### Services and Blink -Connecting to services directly from Blink is fully supported. [This -CL](https://codereview.chromium.org/2698083007) gives a basic example of -connecting to an arbitrary service by name from Blink (look at the change to -SensorProviderProxy.cpp as a starting point). - -Below, we go through strategies for some common challenges encountered when -servicifying features that have Blink as a client. - #### Mocking Interface Impls in JS It is a common pattern in Blink's web tests to mock a remote Mojo interface -in JS. [This CL](https://codereview.chromium.org/2643713002) illustrates the -basic pattern for porting such mocking of an interface hosted by -//content/browser to an interface hosted by an arbitrary service (see the -changes to mock-battery-monitor.js). +in JS so that native Blink code requests interfaces from the test JS rather +than whatever would normally service them in the browser process. + +The current way to set up that sort of thing looks like +[this](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/battery-status/resources/mock-battery-monitor.js?rcl=be6e0001855f7f1cfc26205d0ff5a2b5b324fcbd&l=19). + +*** aside +**NOTE:** The above approach to mocking in JS no longer applies when using +the new recommended `DocumentInterfaceBroker` approach to exposing interfaces +to documents. New JS mocking support is in development for this. +*** #### Feature Impls That Depend on Blink Headers In the course of servicifying a feature that has Blink as a client, you might @@ -215,7 +320,9 @@ To meet this challenge, you have two options: 1. Move the code in question from C++ to mojom (e.g., if it is simple structs). 2. Move the code into the service's C++ client library, being very explicit - about its usage by Blink. See [this CL](https://codereview.chromium.org/2415083002) for a basic pattern to follow. + about its usage by Blink. See + [this CL](https://codereview.chromium.org/2415083002) for a basic pattern to + follow. #### Frame-Scoped Connections You must think carefully about the scoping of the connection being made @@ -226,8 +333,8 @@ Blink has no frame-scoped connection to arbitrary services (by design, as arbitrary services have no knowledge of frames or even a notion of what a frame is). -After a [long -discussion](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/CSnDUjthAuw), +After a +[long discussion](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/CSnDUjthAuw), the policy that we have adopted for this challenge is the following: CURRENT @@ -246,9 +353,6 @@ AFTER SERVICIFYING THE FEATURE IN QUESTION Notably, from the renderer's POV essentially nothing changes here. -In the longer term, this will still be the basic model, only with "the browser" -replaced by "the Navigation Service" or "the web permissions broker". - ## Strategies for Challenges to Decoupling from //content ### Coupling to UI @@ -268,16 +372,17 @@ what requires the coupling and to avoid the coupling creeping in scope. The basic strategy to support this coupling while still servicifying the feature in question is to inject a mechanism of mapping from an opaque "context ID" to the required context. The embedder (e.g., //content) maintains this map, and the -service makes use of it. The embedder also serves as an intermediary: It +service makes use of it. The embedder also serves as an intermediary: it provides a connection that is appropriately context-scoped to clients. When clients request the feature in question, the embedder forwards the request on along with the appropriate context ID. The service impl can then map that context ID back to the needed context on-demand using the mapping functionality injected into the service impl. -To make this more concrete, see [this CL](https://codereview.chromium.org/2734943003). +To make this more concrete, see +[this CL](https://codereview.chromium.org/2734943003). -### Shutdown of singletons +### Shutdown of Singletons You might find that your feature includes singletons that are shut down as part of //content's shutdown process. As part of decoupling the feature @@ -293,29 +398,15 @@ either ported into your service or eliminated: to when the previous code was executing, and ensure that any differences introduced do not impact correctness. -See [this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/Y9FKZf9n1ls) for more discussion of this issue. - -### Tests that muck with service internals -It is often the case that browsertests reach directly into what will become part -of the internal service implementation to either inject mock/fake state or to -monitor private state. - -This poses a challenge: As part of servicification, *no* code outside the -service impl should depend on the service impl. Thus, these dependencies need to -be removed. The question is how to do so while preserving testing coverage. - -To answer this question, there are several different strategies. These -strategies are not mutually-exclusive; they can and should be combined to -preserve the full breadth of coverage. +See +[this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/Y9FKZf9n1ls) +for more discussion of this issue. -- Blink client-side behavior can be tested via [web tests](https://codereview.chromium.org/2731953003) -- To test service impl behavior, create [service tests](https://codereview.chromium.org/2774783003). -- To preserve tests of end-to-end behavior (e.g., that when Blink makes a - request via a Web API in JS, the relevant feature impl receives a connection - request), we are planning on introducing the ability to register mock - implementations with the Service Manager. +## Additional Support -To emphasize one very important point: it is in general necessary to leave -*some* test of end-to-end functionality, as otherwise it is too easy for bustage -to slip in via e.g. changes to how services are registered. See [this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/lJCKAElWz-E) -for further discussion of this point. +If this document was not helpful in some way, please post a message to your +friendly local +[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) +or +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) +mailing list. diff --git a/ipc/README.md b/ipc/README.md deleted file mode 100644 index ad1882a4b4ec10..00000000000000 --- a/ipc/README.md +++ /dev/null @@ -1,860 +0,0 @@ -# Converting Legacy Chrome IPC To Mojo - -Looking for [Mojo Documentation](/mojo)? - -[TOC] - -## Overview - -The `//ipc` directory contains interfaces and implementation for Chrome's -legacy IPC system, including `IPC::Channel` and various macros for defining -messages and type serialization. For details on using this system please see the -original -[documentation](https://www.chromium.org/developers/design-documents/inter-process-communication). - -Legacy IPC is **deprecated**, and Chrome developers are strongly discouraged -from introducing new messages using this system. [Mojo](/mojo) is the correct -IPC system to use moving forward. This document introduces developers to the -various tools available to help with conversion of legacy IPC messages to Mojo. -It assumes familiarity with [Mojom](/mojo/public/tools/bindings) syntax and -general use of Mojo [C++ bindings](/mojo/public/cpp/bindings). - -The existing messages that still need to be converted are tracked in two -spreadsheets: - -[Chrome IPC to Mojo migration]( -https://docs.google.com/spreadsheets/d/1pGWX_wxGdjAVtQOmlDDfhuIc3Pbwg9FtvFXAXYu7b7c/edit#gid=0) - -for non-web platform messages - -[Mojoifying Platform Features]( -https://docs.google.com/spreadsheets/d/1VIINt17Dg2cJjPpoJ_HY3HI0uLpidql-1u8pBJtpbGk/edit#gid=1603373208) - -for web platform messages - -Ownership of items is claimed by specifying a username in the 'Owner' field to -prevent duplication of effort. If you are not very familiar with the messages -you plan to convert, it might be worth asking the owner first. - -In traditional Chrome IPC, we have One Big Pipe (the `IPC::Channel`) between -each connected process. Sending an IPC from one process to another means knowing -how to get a handle to the Channel interface (*e.g.,* -[RenderProcessHost::GetChannel]( -https://cs.chromium.org/chromium/src/content/public/browser/render_process_host.h?rcl=722be98f7e4d7551710fb9cf30674750cdd0d857&l=196) -when sending from the browser to a renderer process), and then having either an -`IPC::MessageFilter` or some other appropriate `IPC::Listener` implementation -installed in the right place on the other side of the channel. - -Because of this arrangement, any message sent on a channel is sent in FIFO order -with respect to all other messages on the channel. While this may be easier to -reason about in general, it carries with it the unfortunate consequence that -many unrelated messages in the system have an implicit, often unintended -ordering dependency. - -It's primarily for this reason that conversion to Mojo IPC can be more -challenging than would otherwise be necessary, and that is why we have a number -of different tools available to facilitate such conversions. - -## Deciding What to Do - -There are few questions you should ask yourself before embarking upon any IPC -message conversion journey. Should this be part of a service? Does message -ordering matter with respect to other parts of the system? What is the meaning -of life? - -### Moving Messages to Services - -We have a small but growing number of services defined in -[`//services`](https://cs.chromium.org/chromium/src/services/), each of which has -some set of public interfaces defined in their `public/interfaces` subdirectory. -In the limit, this is the preferred destination for any message conversions -pertaining to foundational system services (more info at -[https://www.chromium.org/servicification](https://www.chromium.org/servicification).) -For other code it may make sense to introduce services elsewhere (*e.g.*, in -`//chrome/services` or `//components/services`), or to simply -avoid using services altogether for now and instead define some one-off Mojom -interface alongside the old messages file. - -If you need help deciding where a message should live, or if you feel it would -be appropriate to introduce a new service to implement some feature or large set -of messages, please post to -[`services-dev@chromium.org`](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) -with questions, concerns, and/or a brief proposal or design doc describing -the augmentation of an existing service or introduction of a new service. - -See the [Using Services](#Using-Services) section below for details. - -When converting messages that still require tight coupling to content or Chrome -code or which require unchanged ordering with respect to one or more remaining -legacy IPC messages, it is often not immediately feasible to move a message -definition or handler implementation into a service. - -### Moving Messages to Not-Services - -While this isn't strictly possible because everything is a service now, we model -all existing content processes as service instances and provide helpers to make -interface exposure and consumption between them relatively easy. - -See [Using Content's Connectors](#Using-Content_s-Connectors) for details on -the recommended way to accomplish this. - -See -[Using Content's Interface Registries](#Using-Content_s-Interface-Registries) -for details on the **deprecated** way to accomplish this. - -Note that when converting messages to standalone Mojo interfaces, every -interface connection operates 100% independently of each other. This means that -ordering is only guaranteed over a single interface (ignoring -[associated interfaces](/mojo/public/tools/bindings#Associated-Interfaces).) -Consider this example: - -``` cpp -mojom::FrobinatorPtr frob1; -RenderThread::Get()->GetConnector()->BindInterface( - foo_service::mojom::kServiceName, &frob1); - -mojom::FrobinatorPtr frob2; -RenderThread::Get()->GetConnector()->BindInterface( - foo_service::mojom::kServiceName, &frob2); - -// These are ordered on |frob1|. -frob1->Frobinate(1); -frob1->Frobinate(2); - -// These are ordered on |frob2|. -frob2->Frobinate(1); -frob2->Frobinate(2); - -// It is entirely possible, however, that the renderer receives: -// -// [frob1]Frobinate(1) -// [frob2]Frobinate(1) -// [frob1]Frobinate(2) -// [frob2]Frobinate(2) -// -// Because |frob1| and |frob2| guarantee no mutual ordering. -``` - -Also note that neither interface is ordered with respect to legacy -`IPC::Channel` messages. This can present significant problems when converting a -single message or group of messages which must retain ordering with respect to -others still on the Channel. - -### When Ordering Matters - -If ordering really matters with respect to other legacy messages in the system, -as is often the case for *e.g.* frame and navigation-related messages, you -almost certainly want to take advantage of -[Channel-associated interfaces](#Using-Channel_associated-Interfaces) to -eliminate any risk of introducing subtle behavioral changes. - -Even if ordering only matters among a small set of messages which you intend to -move entirely to Mojom, you may wish to move them one-by-one in separate CLs. -In that case, it may make sense to use a Channel-associated interface during the -transitional period. Once all relevant messages are fully relocated into a -single Mojom interface, it's trivial to lift the interface away from Channel -association and into a proper independent service connection. - -## Using Services - -Suppose you have some IPC messages for safely decoding a PNG image: - -``` cpp -IPC_MESSAGE_CONTROL2(UtilityMsg_DecodePNG, - int32_t request_id, - std::string /* png_data */); -IPC_MESSAGE_CONTROL2(UtilityHostMsg_PNGDecoded, - int32_t request_id, - int32_t width, int32_t height, - std::string /* rgba_data */); -``` - -This seems like a perfect fit for an addition to the sandboxed `data_decoder` -service. Your first order of business is to translate this into a suitable -public interface definition within that service: - -``` cpp -// src/services/data_decoder/public/mojom/png_decoder.mojom -module data_decoder.mojom; - -interface PngDecoder { - Decode(array png_data) - => (int32 width, int32 height, array rbga_data); -}; -``` - -You will need to update the relevant BUILD.gn target with the newly added mojom -file. See [this section]( -https://chromium.googlesource.com/chromium/src/+/master/mojo/public/cpp/bindings/README.md#getting-started) -for details. - -You'll also want to define the implementation within -`//services/data_decoder`, plugging in some appropriate binder so the service -knows how to bind incoming interface requests to your implementation: - -``` cpp -// src/services/data_decoder/png_decoder_impl.h -class PngDecoderImpl : public mojom::PngDecoder { - public: - static void BindRequest(mojom::PngDecoderRequest request) { /* ... */ } - - // mojom::PngDecoder: - void Decode(const std::vector& png_data, - const DecodeCallback& callback) override { /* ... */ } - // ... -}; - -// src/services/data_decoder/data_decoder_service.cc -// Not quite legitimate pseudocode... -DataDecoderService::DataDecoderService() { - // ... - registry_.AddInterface(base::Bind(&PngDecoderImpl::BindRequest)); -} -``` - -and finally you need to update the usage of the old IPC by probably deleting -lots of ugly code which sets up a `UtilityProcessHost` and replacing it with -something like: - -``` cpp -#include "services/data_decoder/public/mojom/png_decoder.mojom.h" -... -void OnDecodedPng(const std::vector& rgba_data) { /* ... */ } - -data_decoder::mojom::PngDecoderPtr png_decoder; -connector->BindInterface(data_decoder::mojom::kServiceName, - mojo::MakeRequest(&png_decoder)); -png_decoder->Decode(untrusted_png_data, base::Bind(&OnDecodedPng)); -``` - -Where to get a [`Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h) -is an interesting question, and the answer ultimately depends on where your code -is written. All service instances get a primordial `Connector` which can be -cloned arbitrarily many times and passed around to different threads. - -If you're writing service code the answer is trivial since each `Service` -instance has direct access to a `Connector`. If you're writing code at or above -the content layer, the answer is slightly more interesting and is explained in -the [Using Content's Connectors](#Using-Content_s-Connectors) section below. - -## Using Content's Connectors - -As explained earlier in this document, all content processes are modeled as -service instances today. This means that all content processes have at least -one [`Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h) -instance which can be used to bind interfaces exposed by other services. - -We define [`content::ServiceManagerConnection`](https://cs.chromium.org/chromium/src/content/public/common/service_manager_connection.h?rcl=dd92156efac57169b45aeb0094111b8d94302b12&l=38) -as a helper which fully encapsulates the service instance state within a given -Content process. The main thread of the browser process can access the global -instance by calling -[`content::ServiceManager::GetForProcess()`](https://cs.chromium.org/chromium/src/content/public/common/service_manager_connection.h?rcl=dd92156efac57169b45aeb0094111b8d94302b12&l=56), -and this object has a `GetConnector()` method which exposes the `Connector` for -that process. - -The main thread of any Content child process can use -`content::ChildThread::GetServiceManagerConnection` or -`content::ChildThread::GetConnector` directly. - -For example, any interfaces registered in -[`RenderProcessHostImpl::RegisterMojoInterfaces()`](https://cs.chromium.org/chromium/src/content/browser/renderer_host/render_process_host_impl.cc?rcl=dd92156efac57169b45aeb0094111b8d94302b12&l=1203) -can be acquired by a renderer as follows: - -``` cpp -mojom::LoggerPtr logger; -content::RenderThread::Get()->GetConnector()->BindInterface( - content::mojom::kBrowserServiceName, &logger); -logger->Log("Message to log here"); -``` - -Usually `logger` will be saved in a field at construction time, so the -connection is only created once. There may be situations where you want to -create one connection per request, e.g. a new instance of the Mojo -implementation is created with some information about the request, and any -responses for this request go straight to that instance. - -### On Other Threads - -`Connector` instances can be created and asynchronously associated with each -other to maximize flexibility in when and how outgoing interface requests are -initiated. - -For example if a background (*e.g.,* worker) thread in a renderer process wants -to make an outgoing service request, it can construct its own `Connector` -- -which may be used immediately and retained on that thread -- and asynchronously -associate it with the main-thread `Connector` like so: - -``` cpp -class Logger { - public: - explicit Logger(scoped_refptr main_thread_task_runner) { - service_manager::mojom::ConnectorRequest request; - - // Of course we could also retain |connector| if we intend to use it again. - auto connector = service_manager::Connector::Create(&request); - // Replace service_name with the name of the service to bind on, e.g. - // content::mojom::kBrowserServiceName. - connector->BindInterface("service_name", &logger_); - logger_->Log("Test Message."); - - // Doesn't matter when this happens, as long as it happens eventually. - main_thread_task_runner->PostTask( - FROM_HERE, base::BindOnce(&Logger::BindConnectorOnMainThread, - std::move(request))); - } - - private: - static void BindConnectorOnMainThread( - service_manager::mojom::ConnectorRequest request) { - DCHECK(RenderThreadImpl::Get()); - RenderThreadImpl::Get()->GetConnector()->BindConnectorRequest( - std::move(request)); - } - - mojom::LoggerPtr logger_; - - DISALLOW_COPY_AND_ASSIGN(Logger); -}; -``` - -## Using Content's Interface Registries - -**NOTE:** This section is here mainly for posterity and documentation of -existing usage. Please use `Connector` instead of using an `InterfaceProvider` -directly. - -For convenience the Service Manager's -[client library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/) -exposes two useful types: `BinderRegistry` and `InterfaceProvider`. These -objects generally exist as an intertwined pair with a `BinderRegistry` in -one process and a corresponding `InterfaceProvider` in another process. - -The `BinderRegistry` is essentially just a mapping from interface name -to binder function: - -``` cpp -void BindFrobinator(mojom::FrobinatorRequest request) { - mojo::MakeStrongBinding(std::make_unique, std::move(request)); -} - -// |registry| will hereby handle all incoming requests for "mojom::Frobinator" -// using the above function, which binds the request pipe handle to a new -// instance of |FrobinatorImpl|. -registry->AddInterface(base::Bind(&BindFrobinator)); -``` - -while an `InterfaceProvider` exposes a means of requesting interfaces from a -remote `BinderRegistry`: - -``` cpp -mojom::FrobinatorPtr frob; - -// MakeRequest creates a new pipe, and GetInterface sends one end of it to -// the remote InterfaceRegistry along with the "mojom::Frobinator" name. The -// other end of the pipe is bound to |frob| which may immediately begin sending -// messages. -provider->GetInterface(mojo::MakeRequest(&frob)); -frob->DoTheFrobinator(); -``` - -`RenderFrameHostImpl`'s binder registry corresponds to the `InterfaceProvider` -returned by `RenderFrameImpl::GetRemoteInterfaces()`, and `RenderFrameImpl`'s -binder registry corresponds to the `InterfaceProvider` returned by -`RenderFrameHostImpl::GetRemoteInterfaces()`. - -**NOTE:** this mechanism is being replaced with explicit interface factory -approach described in [this document]( -https://docs.google.com/document/d/1e0qqv3ZGQYskE4XhtuGrYJeThjYXu8xozl7zIlwjSD0). - -As noted above, use of these registries is generally discouraged. - -### Deciding Which Interface Registry to Use - -Once you have an implementation of a Mojo interface, the next thing to decide is -which registry and service to register it on. - -For browser/renderer communication, you can register your Mojo interface -implementation in either the Browser or Renderer process (whichever side the -interface was implemented on). Usually, this involves calling `AddInterface()` -on the correct registry, passing a method that takes the Mojo Request object -(e.g. `sample::mojom::LoggerRequest`) and binding it (e.g. -`mojo::MakeStrongBinding()`, `bindings_.AddBinding()`, etc). Then the class that -needs this API can call `BindInterface()` on the connector for that process, -e.g. -`ChildThreadImpl::current()->GetConnector()->BindInterface(mojom::kBrowserServiceName, std::move(&mojo_interface_))`. - -**NOTE:** `content::ServiceManagerConnection::GetForProcess()` must be called in -the browser process on the main thread, and its connector can only be used on -the main thread; but you can clone connectors and move the clones around to -other threads. A `Connector` is only bound to the thread which first calls into -it. - -Depending on what resources you need access to, the main classes are: - -| Renderer Class | Corresponding Browser Class | Explanation | -|-----------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------| -| `RenderFrame` | `RenderFrameHost` | A single frame. Use this for frame-to-frame messages. | -| `RenderView` | `RenderViewHost` | A view (conceptually a 'tab'). You cannot send Mojo messages to a `RenderView` directly, since frames in a tab can be in multiple processes (and the classes are deprecated). Migrate these to `RenderFrame` instead, or see section [Migrating IPC calls to `RenderView` or `RenderViewHost`](#other-routed-messages-to-the-browser). | -| `RenderProcess` | `RenderProcessHost` | A process, containing multiple frames (probably from the same origin, but not always). | - -**NOTE:** Previously, classes that ended with `Host` were implemented on the -browser side; the equivalent classes on the renderer side had the same name -without the `Host` suffix. We have since deviated from this convention since -Mojo interfaces are not intended to prescribe where their endpoints live, so -future classes should omit such suffixes and just describe the interface they -are providing. - -Of course, any combination of the above is possible, e.g. `RenderProcessHost` -can register a Mojo interface that can be called by a `RenderFrame` (this would -be a way of the browser communicating with multiple frames at once). - -Once you know which class you want the implementation to be registered in, find -the corresponding `Impl` class (e.g. `RenderProcessImpl`). There should be a -`RegisterMojoInterfaces()` method where you can add calls to `AddInterface`, -e.g. For a strong binding: - -```cpp - registry->AddInterface(base::Bind(&Logger::Create, GetID())); -``` - -Then in `Logger` we add a static `Create()` method that takes the -`LoggerRequest` object: - -```cpp -// static -void Logger::Create(int render_process_id, - mojom::LoggerRequest request) { - mojo::MakeStrongBinding(std::make_unique(render_process_id), - std::move(request)); -} -``` - -For a `BindingSet`, we can store a `std::unique_ptr` on the -`RenderProcessHost` instead, e.g.: - -```cpp -// render_process_host_impl.h: -std::unique_ptr logger_; - -// render_process_host_impl.cc: -logger_ = std::make_unique(GetID()); -registry->AddInterface(base::Bind(&Logger::BindRequest, - base::Unretained(logger_.get()))); -``` - -Then in `Logger` we define the `BindRequest` method: - -```h -class Logger : public sample::mojom::Logger { - public: - explicit Logger(int render_process_id); - ~Logger() override; - - void BindRequest(mojom::LoggerRequest request); - - // sample::mojom::Logger: - void Log(const std::string& message) override; - void GetTail(GetTailCallback callback) override; - - private: - mojo::BindingSet bindings_; - - DISALLOW_COPY_AND_ASSIGN(Logger); -}; -``` - -```cpp -void Logger::BindRequest(mojom::LoggerRequest request) { - bindings_.AddBinding(this, std::move(request)); -} -``` - -As you can see above, `MakeStrongBinding()` is used to lazily create an -interface implementation when a connection is created. The lifetime of this -implementation spans the lifetime of the connection. This is done to not -unnecessarily create instances that might not be used. - -Because strong binding lifetime is not tracked or easily observable, any -implementation bound with a strong binding must be very careful to avoid -dependencies on things which it might outlive. As a general rule, if your -implementation depends on any state outside of itself, please consider avoiding -strong bindings in favor of explicit object ownership. - -Alternatively, the relevant registry owner class can own the implementation -instance and dispatch the connection requests that the implementation will -bind. - -#### Setting up Capabilities - -Once you've registered your interface, you need to add capabilities (resolved at -runtime) to the corresponding capabilities manifest json file. - -The service manifest files (which contain the capability spec) are located in -[/content/public/app/mojo/](/content/public/app/mojo/). As a general rule, the -file you want to edit is the service which *provides* the interface (the side -which instantiates the implementation), and the part of the file you want to add -the name of the interface to is the service which *calls* the interface (i.e. -the side containing `LoggerPtr`). - -You can usually just run your Mojo code and look at the error messages. The -errors look like: - -```sh -[ERROR:service_manager.cc(158)] Connection InterfaceProviderSpec prevented -service: content_renderer from binding interface: content.mojom.Logger -exposed by: content_browser -``` - -This means something in the renderer process (called "content_renderer") was -trying to bind to `content.mojom.Logger` in the browser process (called -"content_browser"). To add a capability for this, we need to find the json file -with the capabilities for "content_browser", and add our new interface with name -`content.mojom.Logger` to the "renderer" section. - -In this example, the capabilities for "content_browser" are implemented in -[content_browser_manifest.json](/content/public/app/mojo/content_browser_manifest.json). -It should look like: - -```json -{ - "name": "content_browser", - "display_name": "Content (browser process)", - "interface_provider_specs": { - "service_manager:connector": { - "provides": { - // ... - "renderer": [ - //... -``` - -To add permission for `content.mojom.Logger`, add the string -`"content.mojom.Logger"` to the "renderer" list. - -Similarly, if the error was: - -```sh -[ERROR:service_manager.cc(158)] Connection InterfaceProviderSpec prevented -service: content_browser from binding interface: content.mojom.Logger exposed -by: content_renderer -``` - -We would want the -`interface_provider_specs.service_manager:connector.provides.browser` section in -[content_renderer_manifest.json](/content/public/app/mojo/content_renderer_manifest.json) -(which defines the capabilities for `content_renderer`). - -TODO: Add more details on permission manifests here - -## Using Channel-associated Interfaces - -**NOTE**: Channel-associated interfaces are an interim solution to make the -transition to Mojo IPC easier in Chrome. You should not design new features -which rely on this system. The ballpark date of deletion for `IPC::Channel` is -projected to be somewhere around mid-2019, and obviously Channel-associated -interfaces can't live beyond that point. - -Mojo has support for the concept of -[associated interfaces](/mojo/public/tools/bindings#Associated-Interfaces). -One interface is "associated" with another when it's a logically separate -interface but it shares an underlying message pipe, causing both interfaces to -maintain mutual FIFO message ordering. For example: - -``` cpp -// db.mojom -module db.mojom; - -interface Table { - void AddRow(string data); -}; - -interface Database { - QuerySize() => (uint64 size); - AddTable(associated Table& table) -}; - -// db_client.cc -db::mojom::DatabasePtr db = /* ... get it from somewhere... */ -db::mojom::TableAssociatedPtr new_table; -db->AddTable(mojo::MakeRequest(&new_table)); -new_table->AddRow("my hovercraft is full of eels"); -db->QuerySize(base::Bind([](uint64_t size) { /* ... */ })); -``` - -In the above code, the `AddTable` message will always arrive before the `AddRow` -message, which itself will always arrive before the `QuerySize` message. If the -`Table` interface were not associated with the `Database` pipe, it would be -possible for the `QuerySize` message to be received before `AddRow`, -potentially leading to unintended behavior. - -The legacy `IPC::Channel` used everywhere today is in fact just another Mojo -interface, and developers have the ability to associate other arbitrary Mojo -interfaces with any given Channel. This means that you can define a set of Mojo -messages to convert old IPC messages, and implement them in a way which -perfectly preserves current message ordering. - -There are many different facilities in place for taking advantage of -Channel-associated interfaces, and the right one for your use case depends on -how the legacy IPC message is used today. The subsections below cover various -interesting scenarios. - -### Basic Usage - -The most primitive way to use Channel-associated interfaces is by working -directly with `IPC::Channel` (IO thread) or more commonly `IPC::ChannelProxy` -(main thread). There are a handful of interesting interface methods here. - -**On the IO thread** (*e.g.,* typically when working with process hosts that -aren't for render processes), the interesting methods are as follows: - -[`IPC::Channel::GetAssociatedInterfaceSupport`](https://cs.chromium.org/chromium/src/ipc/ipc_channel.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=235) -returns an object for working with interfaces associated with the `Channel`. -This is never null. - -[`IPC::Channel::AssociatedInterfaceSupport::AddAssociatedInterface`]( -https://cs.chromium.org/chromium/src/ipc/ipc_channel.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=115) -allows you to add a binding function to handle all incoming requests for a -specific type of associated interface. Callbacks added here are called on the IO -thread any time a corresponding interface request arrives on the `Channel.` If -no callback is registered for an incoming interface request, the request falls -through to the Channel's `Listener` via -[`IPC::Listener::OnAssociatedInterfaceRequest`](https://cs.chromium.org/chromium/src/ipc/ipc_listener.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=40). - -[`IPC::Channel::AssociatedInterfaceSupport::GetRemoteAssociatedInterface`]( -https://cs.chromium.org/chromium/src/ipc/ipc_channel.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=124) -requests a Channel-associated interface from the remote endpoint of the channel. - -**On the main thread**, typically when working with `RenderProcessHost`, basic -usage involves calls to -[`IPC::ChannelProxy::GetRemoteAssociatedInterface`]( -https://cs.chromium.org/chromium/src/ipc/ipc_channel_proxy.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=196) -when making outgoing interface requests, or some implementation of -[`IPC::Listener::OnAssociatedInterfaceRequest`](https://cs.chromium.org/chromium/src/ipc/ipc_listener.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=40) -when handling incoming ones. - -TODO - Add docs for using AssociatedInterfaceRegistry where possible. - -### BrowserMessageFilter - -[BrowserMessageFilter](https://cs.chromium.org/chromium/src/content/public/browser/browser_message_filter.h?rcl=805f2ca5aa0902f56885ea3c8c0a42cb80d84522&l=37) -is a popular helper for listening to incoming legacy IPC messages on the browser -process IO thread and (typically) handling them there. - -A common and totally reasonable tactic for converting a group of messages on an -existing `BrowserMessageFilter` is to define a similiarly named Mojom interface -in an inner `mojom` namespace (*e.g.,* a `content::FooMessageFilter` would have -a corresponding `content::mojom::FooMessageFilter` interface), and have the -`BrowserMessageFilter` implementation also implement -`BrowserAssociatedInterface` (optionally overriding methods such as -`BrowserMessageFilter::OnDestruct()` if it needs to be deleted on a certain -thread). - -Real code is probably the most useful explanation, so here are some example -conversion CLs which demonstrate practical `BrowserAssociatedInterface` usage. - -[FrameHostMsg_SetCookie](https://codereview.chromium.org/2167513003) - This -CL introduces a `content::mojom::RenderFrameMessageFilter` interface -corresponding to the existing `content::RenderFrameMessageFilter` implementation -of `BrowserMessageFilter`. Of particular interest is the fact that it only -converts **one** of the messages on that filter. This is fine because ordering -among the messages -- Mojom or otherwise -- is unchanged. - -[FrameHostMsg_GetCookie](https://codereview.chromium.org/2202723005) - A small -follow-up to the above CL, this converts another message on the same filter. It -is common to convert a large group of messages one-by-one in separate CLs. Also -note that this message, unlike the one above on the same interface, is -synchronous. - -[ViewHostMsg_GenerateRoutingID](https://codereview.chromium.org/2351333002) - -Another small CL to introduce a new `BrowserAssociatedInterface`. - -### Routed Per-Frame Messages To the Browser - -Many legacy IPC messages are "routed" -- they carry a routing ID parameter which -is interpreted by the channel endpoint and used to pass a received message on to -some other more specific handler. - -Messages received by the browser with a frame routing ID for example are routed -to the RenderFrameHost's owning [`WebContents`](https://cs.chromium.org/chromium/src/content/browser/web_contents/web_contents_impl.h?rcl=a12e52d81346dd23d8284763d44c4ee657f11cea&l=447) -with the corresponding `RenderFrameHostImpl` as additional context. - -[This CL](https://codereview.chromium.org/2310583002) introduces usage of -[`WebContentsFrameBindingSet`](https://cs.chromium.org/chromium/src/content/public/browser/web_contents_binding_set.h?rcl=d364139fee76154a1d9fa8875ad0cbb5ccb523c3&l=112), which helps establish -per-frame bindings for Channel-associated interfaces. Some hidden magic is done -to make it so that interface requests from a remote -[`RenderFrame AssociatedInterfaceProvider`](https://cs.chromium.org/chromium/src/content/public/renderer/render_frame.h?rcl=d364139fee76154a1d9fa8875ad0cbb5ccb523c3&l=170) -are routed to the appropriate `WebContentsFrameBindingSet`, typically installed -(as in this CL) by a `WebContentsObserver`. - -When a message is received by an interface implementation using a -`WebContentsFrameBindingSet`, that object's `dispatch_context()` can be used -to retrieve the `RenderFrameHostImpl` targeted by the message. See the above CL -for additional clarity. - -**NOTE:** When the ordering of messages doesn't matter, the `InterfaceProvider` -of the relevant `RenderFrameHost` should be used to replace the routing ID. It -can be obtained by calling `RenderFrame::GetRemoteInterfaces()`. - -### Other Routed Messages To the Browser - -Other routing IDs are used when targeting either specific `RenderViewHost` or -`RenderWidgetHost` instances. We don't currently have any facilities in place -to assist with these conversions. Because render views are essentially a -deprecated concept, messages targeting "view" routes should not be converted -as-is, but should instead be moved to target either widgets or frames -accordingly. - -Facilities to assist in conversion of widget-routed messages may be added in the -future. Who knows, maybe you'll be the brave developer to add them (and to then -update this documentation, of course!) If you decide this is exactly what you -need but are nervous about the prospect of writing it yourself, please send a -friendly message to [chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) -explaining the use case so we can help you get things done. - -### Messages to a Renderer - -[This CL](https://codereview.chromium.org/2381493003) converts `ViewMsg_New` -to a Mojo interface, by virtue of the fact that -`IPC::ChannelProxy::GetRemoteAssociatedInterface` from the browser process -results in an associated interface request arriving at -`ChildThreadImpl::OnAssociatedInterfaceRequest` in the corresponding child -process. - -Similar message conversions are done by [this CL](https://codereview.chromium.org/2400313002). - -Note that we do not currently have any helpers for converting routed messages -from browser to renderer. Please confer with -[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) -if such a use case is blocking your work. - -## Miscellany - -### Using Legacy IPC Traits - -In some circumstances there may be a C++ enum, struct, or class that you want -to use in a Mojom via [type mapping](/mojo/public/cpp/bindings#Type-Mapping), -and that type may already have `IPC::ParamTraits` defined (possibly via -`IPC_STRUCT_TRAITS*` macros) for legacy IPC. - -If this is the case and the Mojom which uses the type will definitely only be -called from and implemented in C++ code, *and* you have sufficient reason to -avoid moving or duplicating the type definition in Mojom, you can take advantage -of the existing `ParamTraits`. - -#### The [Native] Attribute -In order to do this you must declare a placeholder type in Mojom somewhere, like -so: - -``` -module foo.mojom; - -[Native] -enum WindowType; - -[Native] -struct MyGiganticStructure; -``` - -The rest of your Mojom will use this typename when referring to the type, but -the wire format used is defined entirely by `IPC::ParamTraits` for whatever -`T` to which you typemap the Mojom type. For example if you typemap -`foo::mojom::MyGiganticStructure` to `foo::MyGiganticStructure`, your typemap -must point to some header which defines -`IPC::ParamTraits`. - -There are several examples of this traits implementation in common IPC traits -defined [here](https://code.google.com/p/chromium/codesearch#chromium/src/ipc/ipc_message_utils.h). - -#### A Practical Example - -Given the [`resource_messages.h`](https://cs.chromium.org/chromium/src/content/common/resource_messages.h?rcl=2e7a430d8d88222c04ab3ffb0a143fa85b3cec5b&l=215) -header with the following definition: - -``` cpp -IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest) - IPC_STRUCT_TRAITS_MEMBER(method) - IPC_STRUCT_TRAITS_MEMBER(url) - // ... -IPC_STRUCT_TRAITS_END() -``` - -and the [`resource_request.h`](https://cs.chromium.org/chromium/src/content/common/resource_request.h?rcl=dce9e476a525e4ff0304787935dc1a8c38392ac8&l=32) -header with the definition for `content::ResourceRequest`: - -``` cpp -namespace content { - -struct CONTENT_EXPORT ResourceRequest { - // ... -}; - -} // namespace content -``` - -we can declare a corresponding Mojom type: - -``` -module content.mojom; - -[Native] -struct URLRequest; -``` - -and add a typemap like [url_request.typemap](https://cs.chromium.org/chromium/src/content/common/url_request.typemap) -to define the mapping: - -``` -mojom = "//content/public/common/url_loader.mojom" -public_headers = [ "//content/common/resource_request.h" ] -traits_headers = [ "//content/common/resource_messages.h" ] -... -type_mappings = [ "content.mojom.URLRequest=content::ResourceRequest" ] -``` - -Note specifically that `public_headers` includes the definition of the native -C++ type, and `traits_headers` includes the definition of the legacy IPC traits. - -Finally, note that this same approach can be used to leverage existing -`IPC_ENUM_TRAITS` for `[Native]` Mojom enum aliases. - -### Typemaps With Content and Blink Types - -Using typemapping for messages that go between Blink and content browser code -can sometimes be tricky due to things like dependency cycles or confusion over -the correct place for some definition to live. There are some example -CLs provided here, but feel free to also contact -[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) -with specific details if you encounter trouble. - -[This CL](https://codereview.chromium.org/2363533002) introduces a Mojom -definition and typemap for -`ui::WindowOpenDisposition` as a precursor to the IPC conversion below. - -The [follow-up CL](https://codereview.chromium.org/2363573002) uses that -definition along with several other new typemaps (including native typemaps as -described above in [Using Legacy IPC Traits](#Using-Legacy-IPC-Traits)) to -convert the relatively large `ViewHostMsg_CreateWindow` message to Mojo. - -### Utility Process Messages - -Given that there are no interesting ordering dependencies among disparate IPC -messages to and from utility processes, and because the utility process is -already sort of a mixed bag of unrelated IPCs, the correct way to convert -utility process IPCs to Mojo is to move them into services. - -We already have support for running services out-of-process (with or without a -sandbox), and many utility process operations already have a suitable service -home they could be moved to. For example, the `data_decoder` service in -[`//services/data_decoder`](https://cs.chromium.org/chromium/src/services/data_decoder/) -is a good place to stick utility process IPCs that do decoding of relatively -complex and untrusted data, of which (at the time of this writing) there are -quite a few. - -When in doubt, contact -[`services-dev@chromium.org`](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) -with ideas, questions, suggestions, etc. - -### Additional Documentation - -[Chrome IPC to Mojo IPC Cheat Sheet](https://www.chromium.org/developers/design-documents/mojo/chrome-ipc-to-mojo-ipc-cheat-sheet) -: A slightly dated but still valuable document covering some details - regarding the conceptual mapping between legacy IPC and Mojo. - -[Additional example CLs](https://docs.google.com/document/d/1GCi08AVMV96cD-tI8kW3xfRir3aNMb5U2yJFU3iAFSU) diff --git a/mojo/README.md b/mojo/README.md index b7ce83fbae9815..1dfe46eb48a97d 100644 --- a/mojo/README.md +++ b/mojo/README.md @@ -4,18 +4,21 @@ ## Getting Started With Mojo -To get started using Mojo in applications which already support it (such as -Chrome), the fastest path forward will be to look at the bindings documentation -for your language of choice ([**C++**](#C_Bindings), -[**JavaScript**](#JavaScript-Bindings), or [**Java**](#Java-Bindings)) as well -as the documentation for the -[**Mojom IDL and bindings generator**](/mojo/public/tools/bindings/README.md). - -If you're looking for information on creating and/or connecting to services, see -the top-level [Services documentation](/services/README.md). - -For specific details regarding the conversion of old things to new things, check -out [Converting Legacy Chrome IPC To Mojo](/ipc/README.md). +To get started using Mojo in Chromium, the fastest path forward will likely be +to read the Mojo sections of the +[Intro to Mojo & Services](/docs/mojo_and_services.md) guide. + +For more detailed reference material on the most commonly used features of Mojo, +head directly to the [bindings](#Bindings-APIs) documentation for your language +of choice or the more general +[mojom Interface Definition Language (IDL)](/mojo/public/tools/bindings/README.md) +documentation. + +If you're looking for information on creating and/or connecting to services, +you're in the wrong place! Mojo does not deal with services, it only facilitates +interface definition, message passing, and other lower-level IPC primitives. +Instead, you should take a look at some of the other available +[Mojo & Services](/docs/README.md#Mojo-Services) documentation. ## System Overview @@ -25,9 +28,10 @@ library with code generation for multiple target languages to facilitate convenient message passing across arbitrary inter- and intra-process boundaries. The documentation here is segmented according to the different libraries -comprising Mojo. The basic hierarchy of features is as follows: +comprising Mojo. Mojo is divided into cleanly-separated layers with the basic +hierarchy of subcomponents as follows: -![Mojo Library Layering: Core on bottom, language bindings on top, public system support APIs in the middle](https://docs.google.com/drawings/d/1RwhzKblXUZw-zhy_KDVobAYprYSqxZzopXTUsbwzDPw/pub?w=570&h=324) +![Mojo Library Layering: Core on bottom, language bindings on top, public system support APIs in the middle](/docs/images/mojo_stack.png) ## Mojo Core In order to use any of the more interesting high-level support libraries like @@ -44,6 +48,9 @@ Mojo support within each process by calling `mojo::core::Init()`. See This is a reasonable option when you can guarantee that all interconnected process binaries are linking against precisely the same revision of Mojo Core. +This includes Chromium itself as well as any developer tools and test +executables built within the tree. + To support other scenarios, use dynamic linking. ### Dynamic Linking @@ -51,7 +58,7 @@ On some platforms, it's also possible for applications to rely on a dynamically-linked Mojo Core library (`libmojo_core.so` or `mojo_core.dll`) instead of statically linking against Mojo Core. -In order to take advantage of this mechanism, the corresponding library must be +In order to take advantage of this mechanism, the library's binary must be present in either: - The working directory of the application @@ -60,84 +67,65 @@ present in either: Instead of calling `mojo::core::Init()` as embedders do, an application using dynamic Mojo Core instead calls `MojoInitialize()` from the C System API. This -call will attempt to locate (see above) and load a Mojo Core library to support -subsequent Mojo API usage within the process. +call will attempt to locate (see above) and load the Mojo Core library to +support subsequent Mojo API usage within the process. -Note that the Mojo Core shared library presents a stable, forward-compatible C -ABI which can support all current and future versions of the higher-level, -public (and not binary-stable) System and Bindings APIs. +Note that the Mojo Core shared library presents a **stable C ABI** designed with +both forward- and backward-compatibility in mind. Thus old applications will +work with new versions of the shared library, and new applications can work +with old versions of the shared library (modulo any dependency on newer +features, whose absence can be gracefully detected at runtime). ## C System API Once Mojo is initialized within a process, the public [**C System API**](/mojo/public/c/system/README.md) is usable on any thread for -the remainder of the process's lifetime. This is a lightweight API with a -relatively small, stable, forward-compatible ABI, comprising the total public -API surface of the Mojo Core library. +the remainder of the process's lifetime. This encapsulates Mojo Core's stable +ABI and comprises the total public API surface of the Mojo Core library. + +The C System library's only dependency (apart from the system libc and e.g. +pthreads) is Mojo Core itself. As such, it's possible build a fully-featured +multiprocess system using only Mojo Core and its exposed C API. It exposes the +fundamental cross-platform capabilities to create and manipulate Mojo primitives +like **message pipes**, **data pipes**, and **shared buffers**, as well as APIs +to help bootstrap connections among processes. -This API is rarely used directly, but it is the foundation upon which all -higher-level Mojo APIs are built. It exposes the fundamental capabilities to -create and interact Mojo primitives like **message pipes**, **data pipes**, and -**shared buffers**, as well as APIs to help bootstrap connections among -processes. +Despite this, it's rare for applications to use the C API directly. Instead this +API acts as a stable foundation upon which several higher-level and more +ergonomic Mojo libraries are built. ## Platform Support API Mojo provides a small collection of abstractions around platform-specific IPC primitives to facilitate bootstrapping Mojo IPC between two processes. See the [Platform API](/mojo/public/cpp/platform/README.md) documentation for details. -## High-Level System APIs +## Higher-Level System APIs There is a relatively small, higher-level system API for each supported language, built upon the low-level C API. Like the C API, direct usage of these system APIs is rare compared to the bindings APIs, but it is sometimes desirable or necessary. -### C++ -The [**C++ System API**](/mojo/public/cpp/system/README.md) provides a layer of -C++ helper classes and functions to make safe System API usage easier: -strongly-typed handle scopers, synchronous waiting operations, system handle -wrapping and unwrapping helpers, common handle operations, and utilities for -more easily watching handle state changes. - -### JavaScript -The [**JavaScript System API**](/third_party/blink/renderer/core/mojo/README.md) -exposes the Mojo primitives to JavaScript, covering all basic functionality of the -low-level C API. - -### Java -The [**Java System API**](/mojo/public/java/system/README.md) provides helper -classes for working with Mojo primitives, covering all basic functionality of -the low-level C API. - -## High-Level Bindings APIs -Typically developers do not use raw message pipe I/O directly, but instead -define some set of interfaces which are used to generate code that resembles -an idiomatic method-calling interface in the target language of choice. This is -the bindings layer. - -### Mojom IDL and Bindings Generator -Interfaces are defined using the -[**Mojom IDL**](/mojo/public/tools/bindings/README.md), which can be fed to the -[**bindings generator**](/mojo/public/tools/bindings/README.md) to generate code -in various supported languages. Generated code manages serialization and -deserialization of messages between interface clients and implementations, -simplifying the code -- and ultimately hiding the message pipe -- on either side -of an interface connection. - -### C++ Bindings -By far the most commonly used API defined by Mojo, the -[**C++ Bindings API**](/mojo/public/cpp/bindings/README.md) exposes a robust set -of features for interacting with message pipes via generated C++ bindings code, -including support for sets of related bindings endpoints, associated interfaces, -nested sync IPC, versioning, bad-message reporting, arbitrary message filter -injection, and convenient test facilities. - -### JavaScript Bindings -The [**JavaScript Bindings API**](/mojo/public/js/README.md) provides helper -classes for working with JavaScript code emitted by the bindings generator. - -### Java Bindings -The [**Java Bindings API**](/mojo/public/java/bindings/README.md) provides -helper classes for working with Java code emitted by the bindings generator. +These APIs provide wrappers around low-level [system API](#C-System-API) +concepts, presenting interfaces that are more idiomatic for the target language: + +- [**C++ System API**](/mojo/public/cpp/system/README.md) +- [**JavaScript System API**](/third_party/blink/renderer/core/mojo/README.md) +- [**Java System API**](/mojo/public/java/system/README.md) + +## Bindings APIs +The [**mojom Interface Definition Language (IDL)**](/mojo/public/tools/bindings/README.md) +is used to generate interface bindings for various languages to send and receive +mojom interface messages using Mojo message pipes. The generated code is +supported by a language-specific bindings API: + +- [**C++ Bindings API**](/mojo/public/cpp/bindings/README.md) +- [**JavaScript Bindings API**](/mojo/public/js/README.md) +- [**Java Bindings API**](/mojo/public/java/bindings/README.md) + +Note that the C++ bindings see the broadest usage in Chromium and are thus +naturally the most feature-rich, including support for things like +[associated interfaces](/mojo/public/cpp/bindings/README.md#Associated-Interfaces), +[synchronous calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls), and +[type-mapping](/mojo/public/cpp/bindings/README.md#Type-Mapping). ## FAQ @@ -162,7 +150,9 @@ Compared to the old IPC in Chrome, making a Mojo call is about 1/3 faster and us ### Can I use in-process message pipes? Yes, and message pipe usage is identical regardless of whether the pipe actually -crosses a process boundary -- in fact this detail is intentionally obscured. +crosses a process boundary -- in fact the location of the other end of a pipe is +intentionally obscured, in part for the sake of efficiency, and in part to +discourage tight coupling of application logic to such details. Message pipes which don't cross a process boundary are efficient: sent messages are never copied, and a write on one end will synchronously modify the message diff --git a/mojo/public/c/system/README.md b/mojo/public/c/system/README.md index ec88e3e667404b..1d8b64b5ae7273 100644 --- a/mojo/public/c/system/README.md +++ b/mojo/public/c/system/README.md @@ -43,8 +43,8 @@ user-provided notification handlers may be invoked at any time on arbitrary threads in the process. It is entirely up to the API user to take appropriate measures to synchronize operations against other application state. -The higher level [system](/mojo/README.md#High-Level-System-APIs) and -[bindings](/mojo/README.md#High-Level-Bindings-APIs) APIs provide helpers to +The higher-level [system](/mojo/README.md#Higher_Level-System-APIs) and +[bindings](/mojo/README.md#Bindings-APIs) APIs provide helpers to simplify Mojo usage in this regard, at the expense of some flexibility. ## Result Codes diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md index 07d4e3503517bf..2739747b9421eb 100644 --- a/mojo/public/cpp/bindings/README.md +++ b/mojo/public/cpp/bindings/README.md @@ -17,7 +17,7 @@ snippets. For a detailed API references please consult the headers in [//mojo/public/cpp/bindings](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/README.md). For a simplified guide targeted at Chromium developers, see [this -link](/docs/mojo_guide.md). +link](/docs/mojo_and_services.md). ## Getting Started @@ -153,7 +153,7 @@ routed to some implementation which will **bind** it. The `InterfaceRequest` doesn't actually *do* anything other than hold onto a pipe endpoint and carry useful compile-time type information. -![Diagram illustrating InterfacePtr and InterfaceRequest on either end of a message pipe](https://docs.google.com/drawings/d/1_Ocprq7EGgTKcSE_WlOn_RBfXcr5C3FJyIbWhwzwNX8/pub?w=608&h=100) +![Diagram illustrating InterfacePtr and InterfaceRequest on either end of a message pipe](/docs/images/mojo_pipe.png) So how do we create a strongly-typed message pipe? @@ -211,7 +211,7 @@ logger->Log("Hello!"); This actually writes a `Log` message to the pipe. -![Diagram illustrating a message traveling on a pipe from LoggerPtr to LoggerRequest](https://docs.google.com/drawings/d/11vnOpNP3UBLlWg4KplQuIU3r_e1XqwDFETD-O_bV-2w/pub?w=635&h=112) +![Diagram illustrating a message traveling on a pipe from LoggerPtr to LoggerRequest](/docs/images/mojo_message.png) But as mentioned above, `InterfaceRequest` *doesn't actually do anything*, so that message will just sit on the pipe forever. We need a way to read messages @@ -278,7 +278,7 @@ motion by the above line of code: 3. The `Log` message is read and deserialized, causing the `Binding` to invoke the `Logger::Log` implementation on its bound `LoggerImpl`. -![Diagram illustrating the progression of binding a request, reading a pending message, and dispatching it](https://docs.google.com/drawings/d/1F2VvfoOINGuNibomqeEU8KekYCtxYVFC00146CFGGQY/pub?w=550&h=500) +![Diagram illustrating the progression of binding a request, reading a pending message, and dispatching it](/docs/images/mojo_binding_and_dispatch.png) As a result, our implementation will eventually log the client's `"Hello!"` message via `LOG(ERROR)`. @@ -1261,8 +1261,7 @@ sync method call? It continues to process incoming sync request messages messages and sync response messages that don’t match the ongoing sync call. -![Diagram illustrating sync call flow]( -https://docs.google.com/a/google.com/drawings/d/e/2PACX-1vRvsmrmZBszFl_OX9AhCn2Cqwx63K0GC7cYrDNPoRYRuHzxS30OZ4ygMBpeU_cThuQY2lYZkYpvSCdM/pub?w=960&h=560) +![Diagram illustrating sync call flow](/docs/images/mojo_sync_call_flow.png) Please note that sync response messages that don’t match the ongoing sync call cannot re-enter. That is because they correspond to sync calls @@ -1274,8 +1273,7 @@ while the stack unwinds. Please note that the re-entrancy behavior doesn’t prevent deadlocks involving async calls. You need to avoid call sequences such as: -![Diagram illustrating a sync call deadlock]( -https://docs.google.com/a/google.com/drawings/d/e/2PACX-1vTBl5XPA8K-kVPt0oByMNSSoxpCKh1p2_atIDR9Me4xGfa6nf0fNAKkJ-Hg5utllY5ghXtoS1haHL6d/pub?w=960&h=480) +![Diagram illustrating a sync call deadlock](/docs/images/mojo_sync_call_deadlock.png) ### Read more diff --git a/mojo/public/tools/bindings/README.md b/mojo/public/tools/bindings/README.md index 76c6f321ee678a..070d055cd9103b 100644 --- a/mojo/public/tools/bindings/README.md +++ b/mojo/public/tools/bindings/README.md @@ -1,18 +1,18 @@ -# Mojom IDL and Bindings Generator +# Mojom Interface Definition Language (IDL) This document is a subset of the [Mojo documentation](/mojo/README.md). [TOC] ## Overview -Mojom is the IDL for Mojo bindings interfaces. Given a `.mojom` file, the +Mojom is the IDL for Mojo interfaces. Given a `.mojom` file, the [bindings -generator](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/) -outputs bindings for all supported languages: **C++**, **JavaScript**, and +generator](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/) can +output bindings for any supported language: **C++**, **JavaScript**, or **Java**. For a trivial example consider the following hypothetical Mojom file we write to -`//services/widget/public/interfaces/frobinator.mojom`: +`//services/widget/public/mojom/frobinator.mojom`: ``` module widget.mojom; @@ -25,15 +25,15 @@ interface Frobinator { This defines a single [interface](#Interfaces) named `Frobinator` in a [module](#Modules) named `widget.mojom` (and thus fully qualified in Mojom as `widget.mojom.Frobinator`.) Note that many interfaces and/or other types of -definitions may be included in a single Mojom file. +definitions (structs, enums, *etc.*) may be included in a single Mojom file. If we add a corresponding GN target to -`//services/widget/public/interfaces/BUILD.gn`: +`//services/widget/public/mojom/BUILD.gn`: ``` import("mojo/public/tools/bindings/mojom.gni") -mojom("interfaces") { +mojom("mojom") { sources = [ "frobinator.mojom", ] @@ -43,22 +43,32 @@ mojom("interfaces") { and then build this target: ``` -ninja -C out/r services/widget/public/interfaces +ninja -C out/r services/widget/public/mojom ``` we'll find several generated sources in our output directory: ``` -out/r/gen/services/widget/public/interfaces/frobinator.mojom.cc -out/r/gen/services/widget/public/interfaces/frobinator.mojom.h -out/r/gen/services/widget/public/interfaces/frobinator.mojom.js -out/r/gen/services/widget/public/interfaces/frobinator.mojom.srcjar -... +out/r/gen/services/widget/public/mojom/frobinator.mojom.cc +out/r/gen/services/widget/public/mojom/frobinator.mojom.h +out/r/gen/services/widget/public/mojom/frobinator.mojom-shared.h +etc... ``` Each of these generated source modules includes a set of definitions -representing the Mojom contents within the target language. For more details -regarding the generated outputs please see +representing the Mojom contents in C++. You can also build or depend on suffixed +target names to get bindings for other languages. For example, + +``` +ninja -C out/r services/widget/public/mojom:mojom_js +ninja -C out/r services/widget/public/mojom:mojom_java +``` + +would generate JavaScript and Java bindings respectively, in the same generated +output directory. + +For more details regarding the generated +outputs please see [documentation for individual target languages](#Generated-Code-For-Target-Languages). ## Mojom Syntax @@ -136,7 +146,7 @@ If your Mojom references definitions from other Mojom files, you must **import** those files. Import syntax is as follows: ``` -import "services/widget/public/interfaces/frobinator.mojom"; +import "services/widget/public/mojom/frobinator.mojom"; ``` Import paths are always relative to the top-level directory. @@ -493,7 +503,7 @@ relative ordering guarantees among them. Associated interfaces are useful when one interface needs to guarantee strict FIFO ordering with respect to one or more other interfaces, as they allow interfaces to share a single pipe. -Currenly associated interfaces are only supported in generated C++ bindings. +Currently associated interfaces are only supported in generated C++ bindings. See the documentation for [C++ Associated Interfaces](/mojo/public/cpp/bindings/README.md#Associated-Interfaces). diff --git a/services/README.md b/services/README.md index fb2c309c4d60b6..0d9cdcf3e44cb9 100644 --- a/services/README.md +++ b/services/README.md @@ -1,84 +1,256 @@ -# Chrome Foundation Services +# Service Development Guidelines [TOC] ## Overview -This directory contains Chrome Foundation Services. If you think of Chrome as a -"portable OS," Chrome Foundation Services can be thought of as that OS' -foundational "system services" layer. +If you're looking for general documentation on the Service Manager, what a +"service" is, and how to build one, see the +[Service Manager & Services](/services/service_manager/README.md) +documentation instead of this document. -Roughly each subdirectory here corresponds to a service that: +The top-level `//services` directory contains the sources, public Mojo interface +definitions, and public client libraries for a number of essential services, +designated as **Chrome Foundation Services**. If you think of Chrome as a +"portable OS," Chrome Foundation Services can be thought of as the core system +services of that OS. - * is a client of `//services/service_manager` with its own unique Identity. - * could logically run a standalone process for security/performance isolation - benefits depending on the constraints of the host OS. +Each subdirectory here corresponds to a service that: -## API Standards +- implements + [`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h?rcl=ebec02bcca6e327b2e01855ce43ab1bec0aeef27&l=21) + and is thus a client of the Service Manager +- generally focuses on a subset of functionality or features which are + thematically or functionally related in a way that makes sense given the name + of the service +- could logically run in an isolated process for security or performance + isolation, depending on the constraints of the host OS -As illustrated above, the individual services in //services are intended for -graceful reusability across a broad variety of use cases. To enable this goal, -we have rigorous [standards](/services/api_standards.md) on services' -public APIs. Before doing significant work in //services (and especially before -becoming an owner of a service), please internalize these standards -- you are -responsible for upholding them. +*** aside +Note that there are other parts of the tree which aggregate +slightly-less-than-foundational service definitions, such as services specific +to the Chrome browser, defined in `//chrome/services`. The motivations, advice, +and standards discussed in this document apply to all service definitions in the +Chromium tree. +*** -## Service Directory Structure +The `//services/service_manager` directory contains the implementation and +public APIs of the Service Manager itself, including an embedder API with which +software applications (such as Chrome) can embed the Service Manager to manage +their own multiprocess service architecture. -Individual services are structured like so: +One of the main motivations for expressing Chromium as a collection of services +is long-term maintainability and code health. Because service API boundaries are +strictly limited to Mojo interfaces, state owned and managed by each service is +strongly isolated from other components in the system. + +Another key motivation is general modularity and reusability: in the past there +have been a number of missed opportunities for potential new features or +Chromium-based products due to the browser's generally monolothic and inflexible +system design. With the Service Manager & services providing scaffolding for +system components, it becomes progressively easier to build out newer use cases +with *e.g.* a smaller resource footprint, or a different process model, or even +a more granular binary distribution. + +## Service Standards + +As outlined above, individual services are intended for graceful reusability +across a broad variety of use cases. To enable this goal, we have rigorous +standards on services' structure and public API design. Before doing significant +work in `//services` (or other places where services are defined), please +internalize these standards. All Chromium developers are responsible for +upholding them! + +### Public Service APIs + +In creating and maintaining a service's public API, please respect the following +principles: + +- The purpose of a service should be readily apparent. +- The supported client use cases of the service should be easy for a new + consumer to understand. +- The service should use idioms and design patterns consistent with other + services. +- From the service's public API documentation and tests, it should be feasible + to develop a new implementation of the service which satisfies existing + clients and doesn't require mimicking internal implementation details of the + existing service. +- Perhaps most important of all, a service's public API should be designed with + multiple hypothetical clients in mind, *not* focused on supporting only a + single narrow use known at development time. **Always be thinking about the + future!** + +If you're working on a new service and have concerns or doubts about API design, +please post to +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev) +and ask for help. The list is generally quite responsive, and it's loaded with +people who have done a lot of work on services. + +### Service API Design Tips + +#### Using Interface Factories to Establish Context + +One common pitfall when designing service APIs is to write something like: + +``` cpp +interface GoatTeleporter { + // Sets the client interface pipe for this teleporter. Must be called before + // other interface methods. + SetClient(GoatTeleporterClient client); + + TeleportGoat(string name); +}; + +interface GoatTeleporterClient { + TeleporterReady(); +}; +``` + +The problem with this approach is that a client may easily fail to call +`SetClient` before calling `TeleportGoat`. When such ordering requirements are +necessary, the service can benefit clients by designing an API that is harder +to fail at. For example: + +``` cpp +interface GoatTeleporterFactory { + GetGoatTeleporter(GoatTeleporter& request, GoatTeleporterClient client); +}; + +interface GoatTeleporter { + TeleportGoat(string name); +}; +``` + +Instead of exposing `GoatTeleporter` directly to other services, the service can +expose `GoatTeleporterFactory` instead. Now it's impossible for a client to +acquire a functioning `GoatTeleporter` pipe without also providing a +corresponding client pipe to complement it. + +### Service & Interface Naming + +Just some basic tips for service and interface naming: + +- Strive to give your service a name that makes it immediately obvious what the + service is for (*e.g.*, `"network"`, `"metrics"`) rather than a meaningless + codename like `"cromulator_3000"`. +- Avoid the usage of `"Service"` in interface names. While the term "service" + is overloaded in Chromium and certainly has plenty of valid interpretations, + in the context of Service Manager services it has a very specific meaning + and should not be overloaded further if possible. An interface which exposes a + control API for a goat teleporter can just be called `GoatTeleporter`, not + `GoatTeleporterService`. + +- Strive to avoid conceptual layering violations in naming and documentation -- + *e.g.*, avoid referencing Blink or Content concepts like "renderers" or + "frame hosts". + +- Use the names `FooClient` and `FooObserver` consistently in interfaces. If + there is an expected 1:1 correspondence between a Foo and its client interface + counterpart, that counterpart should most likely be called `FooClient`. If + there is expected to be 1-to-many correspondence between a Foo and its + counterpart clients, the client interface may be better named `FooObserver`. + +### Service Directory & Dependency Structure + +Services typically follow a canonical directory structure: ``` -//services/foo/ <-- Implementation code, may have subdirs. - /public/ - /cpp/ <-- C++ client libraries (optional) - /mojom/ <-- Mojom interfaces +//services/service_name/ # Private implementation + public/ + mojom/ # Mojom interfaces + cpp/ # C++ client libraries (optional) + java/ # Java client libararies (optional, rare) + js/ # JS client libraries (optional, rare) ``` -## Dependencies +As a general rule, **nothing below `/public` can depend on the private service +implementation** (*i.e.* things above `/public`). Enforcing this principle makes +it much easier to keep the service's state well-isolated from the rest of the +system. + +Generally the language-specific client libraries are built against only the +public mojom API of the service (and usually few other common dependencies like +`//base` and `//mojo`), and they bootstrap connections to those interfaces by +using public Service Manager APIs like +[`Connector`](/services/service_manager/README.md#Connectors). + +Even in the private service implementation, services should not depend on very +large components like Content, Chrome, or Blink. + +*** aside +NOTE: Exceptions to the above rule are made in rare cases where Blink or V8 is +actually required as part of the service implementation. For example +`"data_decoder"` uses Blink implementation to decode common image formats, and +`"proxy_resolver"` uses V8 to execute proxy autoconfig scripts. +*** + +### Service Documentation + +- Every service should have a top-level `README.md` that explains the purpose and + supported usage models of the service. + +- Every public interface should be documented within its Mojom file at both the + interface level and indivudal message level. + +- Interface documentation should be complete enough to serve as test + specifications. If the method returns information of a user's accounts, what + should happen if the user is not signed in? If the method makes a request for + an access token, what happens if a client makes a second method call before + the first one has completed? If the method returns a nullable object, under + which conditions will it be null? + +- Avoid writing interface documentation which is unnecessarily prescriptive + about implementation details. Keep in mind that these are **interface** + definitions, not implementations thereof. -Code within `//services` may only depend on each other via each other's -`/public/` directories, *i.e.* implementation code may not be shared directly. +- Avoid writing documentation which is tailored to a specific client. -Service code should also take care to tightly limit the dependencies on static -libraries from outside of `//services`. Dependencies to large platform -layers like `//content`, `//chrome` or `//third_party/WebKit` must be avoided. +### Service Testing -## Physical Packaging +- Try to cover service implementation details with unit tests tied as closely + as possible to the private implementation object or method being tested, + rather than exercising implementation details through public API surface. -Note that while it may be possible to build a discrete physical package (DSO) -for each service, products consuming these services may package them -differently, e.g. by combining them into a single package. +- For integration tests, try to have tests cover as much of the public API + surface as possible while mocking out as little of the underlying service as + possible. -## Additional Documentation +- Treat the public API tests as "conformance tests" which clearly demonstrate + what expectations and guarantees are supposed to be upheld by *any* + implementation of the service's APIs. -[High-level Design Doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU) +- Take advantage of the + [test support library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/) + provided by the Service Manager. In particular, `TestConnectorFactory` is + useful for driving public API tests with the service running inside the test + process, and `TestServiceManager` makes it possible to easily cover + out-of-process testing scenarios while faking out as little of the system as + possible. -[Servicification Homepage](https://sites.google.com/a/chromium.org/dev/servicification) +## Adding a New Service -[Servicification Strategies](/docs/servicification.md) +See the [Service Manager documentation](/services/service_manager/README.md) for +more details regarding how to define a service and expose or consume interfaces +to and from it, as well as how to make the service available to an application's +runtime environment. -## Relationship To Other Top-Level Directories +Please start a thread on +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) +if you want to propose the introduction of a new service. -Services can be thought of as integrators of library code from across the -Chromium repository, most commonly `//base` and `//mojo` (obviously) but for -each service also `//components`, `//ui`, *etc.* in accordance with the -functionality they provide. +If you are servicifying an existing Chromium feature, please check out +[Servicifying Chromium Features](/docs/servicification.md). -Not everything in `//components` is automatically a service in its own right. -Think of `//components` as sort of like a `//lib`. Individual `//components` can -define, implement and use Mojom interfaces, but only `//services` have unique -identities with the Service Manager and so only `//services` make it possible -for Mojom interfaces to be acquired. +## Other Docs -## Adding a new service +Here are some other external documents that aren't quite fully captured by any +documents in the Chromium tree. Beware of obsolete information: -See the [Service Manager documentation](/services/service_manager) for more -details regarding how to define a service and expose or consume interfaces to -and from other services. +- [High-level Design Doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU) +- [Servicification Homepage](https://sites.google.com/a/chromium.org/dev/servicification) -Please start a thread on [services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) -if you want to introduce a new service. +## Additional Support -If you are servicifying existing Chromium code: Please first read the -[servicification strategies documentation](/docs/servicification.md), which -contains information that will hopefully make your task easier. +You can always post to +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev) +with questions or concerns about anything related to service development. diff --git a/services/api_standards.md b/services/api_standards.md deleted file mode 100644 index 55b4dd1587b27c..00000000000000 --- a/services/api_standards.md +++ /dev/null @@ -1,116 +0,0 @@ -# API Standards for Foundation Services - -In creating and maintaining the public-facing structure of a foundation service, -you should hold yourself to several first-order goals: - -* The purpose of the service should be readily apparent. -* The supported usage models of the service should be easy for a new - consumer to understand. -* The service should be consistent with other foundation services. -* From the API documentation and tests, it should be feasible - to develop a distinct implementation of the service (i.e., without having to - delve into the internals of the current implementation). - -Below we outline concrete standards that aid in achieving the above goals. - -## Naming - -* Strive to give your service a name that makes it immediately obvious what the - service is for ("network", "metrics"). - -* Avoid the usage of "Service" in interface names. While the term "Service" is - overloaded in Chromium, in the context of //services it has a very specific - meaning and should not be overloaded. - -* Strive to avoid conceptual layering violations in naming -- e.g., references - to Blink or //content. - -* Use the names "FooClient" and "FooObserver" consistently in interfaces. If - there is an expected 1:1 correspondence between Foo and its counterpart, that - counterpart should be called FooClient. If there is an expected 1:many - correspondence between Foo and its counterparts, those counterparts should be - called FooObservers. - -## Documentation - -* Every service should have a top-level README.md that explains the purpose and - supported usage models of the service. - -* Every public interface should be documented at the interface (class) level and - at the method level. - -* Interface documentation should be complete enough to serve as test - specifications. If the method returns information of a user's accounts, what - happens if the user is not signed in? If the method makes a request for an - access token, what happens if a client makes a second method call before a - first one has completed? If the method returns a nullable object, under which - conditions will it be null? - -* Strive to avoid your documentation being too specific to a given client. - -## API Shape - -* Strive to avoid molding your API shape too specifically to the needs of a - given client. Most foundational services should make sense even in a - Chrome-less system environment. A good test is: Would your service's APIs seem - sensible to users who don't have knowledge of Chrome's specific needs? - -* If a given interface Foo requires "construction parameters" (e.g., the client - must give it a FooClient before calling any methods), provide a FooProvider - interface with a GetFoo() method that takes in the relevant construction - parameters. This approach eliminates the possibility of a badly-written (or - malevolent) client calling methods on a partially-constructed Foo. To be - concrete: - - ```` - // NO: Client will have access to partially-constructed Foo. - interface Foo { - SetClient(FooClient client); - ... - }; - - // YES: Foo will be completely constructed before client has access. - interface FooProvider { - GetFoo(Foo& request, FooClient client); - }; - interface Foo { ... }; - ```` - -* In the absence of specific guidance, strive for consistency with surrounding - interfaces and with interfaces in other services. - -## Testing - -* Use service tests to test the public interfaces exposed by your service. - -* Every public method should be covered by at least one service test. Strive - to have your tests enforce your documentation (corollary: if you can enforce - your documentation without any tests, improve your documentation :). - -* Think of these tests as a form of "compliance tests": They should be written - in such a way that engineers with a distinct implementation of your - APIs should trivially be able to run your tests against their implementation. - Notably, try to avoid relying on implementation details of the service in its - tests. - -* Related to the above, aim for a high degree of coverage with these tests. If a - reimplementation passes your tests, you should have a high degree of - confidence that it will be usable by your consumers. - -## Appendix: Responsibility for Upholding These Standards - -The responsibility for holding these standards is shared across -//services/OWNERS, individual service OWNERS, and services developers: - -* //services/OWNERS own the standards themselves and are responsible for - ensuring that quality and consistency are maintained across //services. -* Individual service OWNERS are responsible for ensuring that their service - adheres to these standards. -* Service developers are responsible for ensuring that their CLs adhere to - these standards (and thus making life easier for the OWNERS that must review - these CLs :). - -We expect that these standards will evolve over time. If you encounter a tricky -situation not covered here, please send an email to services-dev@. Similarly, if -you see inconsistency or violations of the standards, please file a bug and CC -relevant OWNERS (i.e., of the service in question and/or //services/OWNERS). diff --git a/services/service_manager/README.md b/services/service_manager/README.md index fd43b59ca6b000..2af79b6a5ed2e5 100644 --- a/services/service_manager/README.md +++ b/services/service_manager/README.md @@ -1,535 +1,928 @@ -# Service Manager User Guide +# The Service Manager & Services [TOC] -## What is the Service Manager? +## Overview -The **Service Manager** is a tool that brokers connections and capabilities -between -- and manages instances of -- system components referred to henceforth -as **services**. +The Service Manager is a component which large applications like Chromium can +use support a cross-platform, multi-process, service-oriented, +hyphenated-adjective-laden architecture. -The Service Manager performs the following functions: +This document covers how to embed +the Service Manager into an application as well as how to define and register +services for it to manage. If you just want to read about defining services and +using common service APIs, skip to the main [Services](#Services) section. -* Brokers interface requests between service instances, enforcing static - capability policies declared by the services involved. -* Launches and manages the lifecycle of services and processes. -* Isolates service instances and interface requests among them. -* Tracks running service instances and exposes privileged APIs for querying - system state. +## Embedding the Service Manager -The Service Manager presents a series of Mojo -[interfaces](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/) -to services, though in practice most interaction with the Service Manager is -made simpler by using its corresponding -[C++ client library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/). +To embed the Service Manager, an application should link against the code in +`//services/service_manager/embedder`. This defines a main entry point for +most platforms, with a relatively small +[`service_manager::MainDelegate`](https://cs.chromium.org/chromium/src/services/service_manager/embedder/main_delegate.h) +interface for the application to implement. In particular, the application +should at least implement +[`GetServiceManifests`](https://cs.chromium.org/chromium/src/services/service_manager/embedder/main_delegate.h?rcl=734122d6a01196706dfc1c252fa09ed933778f8f&l=80) to provide +metadata about the full set of services comprising the application. -## Mojo Recap +*** aside +Note that Chromium does not currently implement `GetServiceManifests` for +production use of the Service Manager. This is because a bunch of process +launching and management logic still lives at the Content layer. As more of this +code moves into Service Manager internals, Chromium will start to look more like +any other Service Manager embedder. +*** -The Mojo system provides two key components of interest here - a lightweight -message pipe concept allowing two endpoints to communicate, and a bindings layer -that allows interfaces to be described to bind to those endpoints, with -ergonomic bindings for languages used in Chrome. +*TODO: Improve embedder documentation here, and include support for in-process +service launching once MainDelegate supports it.* -Mojo message pipes are designed to be lightweight and may be read from/written -to and passed around from one process to another. In most situations a developer -won't interact with the pipes directly, but rather with bindings types generated -to encapsulate a bound interface. To use the bindings, a developer defines their -interface in the [Mojom IDL format](/mojo/public/tools/bindings). With some -build magic, the generated definitions can then be referenced from C++, -JavaScript and Java code. +## Services -See the [Mojo documentation](/mojo) for a complete overview, detailed -explanations, and API references. +A **service** in this context can be defined as any self-contained body of +application logic which satisfies *all* of the following constraints: + +- It defines a single [implementation](#Implementation) of + [`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h) + to receive interface requests brokered by the + Service Manager, and it maintains a connection between this object and the + Service Manager using a + [`ServiceBinding`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h). +- Its API surface in from or out to other services is restricted exclusively to + [Mojo](/mojo/README.md) interfaces and self-contained client libraries built + on those Mojo interfaces. This means no link-time or run-time exposure of + the service implementation's internal heap or global state. +- It defines a [service manifest](#Manifests) to declare how the Service Manager + should identify and manage instances of the service, as well as what + interfaces are exposed to or required from other services in the system. + +The Service Manager is responsible for managing the creation and interconnection +of individual service instances, whether they are embedded within an existing +process or each isolated within dedicated processes. Managed service processes +may be sandboxed with any of various supported +[sandbox configurations](#Sandbox-Configurations). + +This section walks through important concepts and APIs throughout service +development, and builds up a small working example service in the process. + +### A Brief Note About Service Granularity + +Many developers fret over what the right "size" or granularity is for a service +or set of services. This makes sense, and there is always going to be some +design tension between choosing a simpler and potentially more efficient, +monolithic implementation, versus choosing a more modular but often more complex +one. + +One classic example of this tension is in the origins of Chromium's +`device` service. The service hosts a number of independent device interfacing +subsystems for things like USB, Bluetooth, HID, battery status, etc. You could +easily imagine justifying separate services for each of these features, but it +was ultimately decided keep them merged together as one service thematically +related to hardware device capabilities. Some factors which played into this +decision: + +- There was no clear **security** benefit to keeping the features isolated from + each other. +- There was no clear **code size** benefit to keeping the features isolated from + each other -- environments supporting any one of the device capabilities are + fairly likely to support several others and would thus likely include all or + most of the smaller services anyway. +- There isn't really any coupling between the different features in the service, + so there would be few **code health** benefits to building separate services. + +Given all of the above conditions, opting for a smaller overall number of +services seems likely to have been the right decision. + +When making these kinds of decisions yourself, use your best judgment. When in +doubt, start a bike-shedding centithread on +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev). + +### Implementation + +The central fixture in any service implementation is, well, its +[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h) +implementation. This is a small interface with really only three virtual methods +of practical interest, all optional to implement: -## Services +``` cpp +class Service { + public: + virtual void OnStart(); + virtual void OnBindInterface(const BindSourceInfo& source, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe); + virtual void OnDisconnected(); +}; +``` -A **service** is a collection of one or more private implementations of public -Mojo interfaces which are reachable via the Service Manager. Every service is -comprised of the following pieces: - -* A set of public Mojo interface definitions -* A **service manifest** declarating arbitrarily named capabilities which are - each comprised of one or more exposed Mojo interfaces. -* Private implementation code which responds to lifecycle events and incoming - interface requests, all driven by the Service Manager. - -The Service Manager is responsible for starting new service instances on-demand, -and a given service may have any number of concurrently running instances. The -Service Manager disambiguates service instances by their unique **identity**. A -service's identity is represented by the 3-tuple of its **service name**, **user -ID**, and **instance qualifier**: - -* The service name is a free-form -- typically short -- string identifying the - the specific service being run in the instance. -* The user ID is a GUID string representing the identity of a user in the system. - Every running service instance is associated with a specific user ID. -* Finally, the instance qualifier is an arbitrary free-form string used to - disambiguate multiple instances of a service for the same user. - -As long as a service instance is running it must maintain an implementation of -the -[`service_manager.mojom.Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/service.mojom) -interface. Typically this is done in C++ code by implementing the C++ client -library's -[`service_manager::Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h) -interface. This interface is driven by messages from the Service Manager and is -used to receive incoming interface requests the Service Manager brokers from -other services. - -Every service instance also has an outgoing link back to the Service Manager -which it can use to make interface requests to other services in the system. -This is the -[`service_manager.mojom.Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/connector.mojom) -interface, and it's commonly used via the C++ client library's -[`service_manager::Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h) -class. +Services implement a subclass of this to work in conjunction with a +[`ServiceBinding`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h) +so the Service Manager can call into the service with lifecycle events and +interface requests from other services. + +*** aside +NOTE: As discussed in [Instance Sharing](#Instance-Sharing) below, your service +configuration may allow for the Service Manager to manage many concurrent +instances of your service. Whether these instances run in the same shared +process or in separate processes, each instance is comprised of exactly one +dedicated instance of your actual `Service` subclass. +*** + +Through the rest of this document we'll build out a basic working service +implementation, complete with a manifest and simple tests. We'll call it the +`storage` service, and it will provide the basis for all persistent storage +capabilities in our crappy operating system hobby project that is doomed to +languish forever in an unfinished state. + +*** aside +NOTE: Sheerly for the sake of brevity, example code written here is inlined in +headers where it would typically be moved out-of-line. +*** + +The first step is usually to imagine and define some mojom API surface to start +with. We'll limit this example to two mojom files. It's conventional to keep +important constants defined in a separate `constants.mojom` file: -## A Simple Service Example +``` cpp +// src/services/storage/public/mojom/constants.mojom +module storage.mojom; -This section walks through the creation of a simple skeleton service. +// This string will identify our service to the Service Manager. It will be used +// in our manifest when registering the service, and clients can use it when +// sending interface requests to the Service Manager if they want to reach our +// service. +const string kServiceName = "storage"; -### Private Implementation +// We'll use this later, in service manifest definitions. +const string kAllocationCapability = "allocation"; +``` -Consider this implementation of the `service_manager::Service` interface: +And some useful interface definitions: -**`//services/my_service/my_service.h`** ``` cpp +// src/services/storage/public/mojom/block.mojom +module storage.mojom; + +interface BlockAllocator { + // Allocates a new block of persistent storage for the client. If allocation + // fails, |request| is discarded. + Allocate(uint64 num_bytes, Block& request); +}; + +interface Block { + // Reads and returns a small range of bytes from the block. + Read(uint64 byte_offset, uint16 num_bytes) => (array bytes); + + // Writes a small range of bytes to the block. + Write(uint64 byte_offset, array bytes); +}; +``` + +And finally we'll define our basic `Service` subclass: + +``` cpp +// src/services/storage/storage_service.h + #include "base/macros.h" #include "services/service_manager/public/cpp/service.h" +#include "services/service_manager/public/cpp/service_binding.h" +#include "services/storage/public/mojom/block.mojom.h" -namespace my_service { +namespace storage { -class MyService : public service_manager::Service { +class StorageService : public service_manager::Service, + public mojom::BlockAllocator { public: - MyService(); - ~MyService() override; + explicit StorageService(service_manager::mojom::ServiceRequest request) + : service_binding_(this, std::move(request)) {} + ~StorageService() override = default; + private: // service_manager::Service: - void OnStart() override; - void OnBindInterface(const service_manager::BindSourceInfo& source_info, + void OnBindInterface(const service_mangaer::BindSourceInfo& source, const std::string& interface_name, - mojo::ScopedMessagePipeHandle handle) override; - private: - DISALLOW_COPY_AND_ASSIGN(MyService); + mojo::ScopedMessagePipeHandle interface_pipe) override { + if (interface_name == mojom::BlockAllocator::Name_) { + // If the Service Manager sends us a request with BlockAllocator's + // interface name, we should treat |interface_pipe| as a + // BlockAllocatorRequest that we can bind. + allocator_bindings_.AddBinding( + this, mojom::BlockAllocatorRequest(std::move(interface_pipe))); + } + } + + // mojom::BlockAllocator: + void Allocate(uint64_t num_bytes, mojom::BlockRequest request) override { + // This space intentionally left blank. + } + + service_manager::ServiceBinding service_binding_; + mojo::BindingSet allocator_bindings_; + + DISALLOW_COPY_AND_ASSIGN(StorageService); }; -} // namespace my_service +} // namespace storage ``` -**`//services/my_service/my_service.cc`** -``` cpp -#include "services/my_service/my_service.h" +Great. This is a basic service implementation. It does nothing useful, but we +can come back and fix that some other time. + +First, notice that the `StorageService` constructor takes a +`service_manager::mojom::ServiceRequest` and immediately passes it to the +`service_binding_` constructor. This is a nearly universal convention among +service implementations, and your service will probably do it too. The +`ServiceRequest` is an interface pipe that the Service Manager uses to drive +your service, and the `ServiceBinding` is a helper class which translates +messages from the Service Manager into the simpler interface methods of the +`Service` class you've implemented. + +`StorageService` also implements `OnBindInterface`, which is what the Service +Manager invokes (via your `ServiceBinding`) when it has decided to route another +service's interface request to your service instance. Note that because this is +a generic API intended to support arbitrary interfaces, the request comes in the +form of an interface name and a raw message pipe handle. It is the service's +responsibility to inspect the name and decide how (or even if) to bind the pipe. +Here we recognize only incoming `BlockAllocator` requests and drop anything +else. + +*** aside +NOTE: Because interface requests are just strongly-type message pipe endpoint +wrappers, you can freely construct any kind of interface request over a raw +message pipe handle. If you're planning to pass the endpoint around, it's good +to do this as early as possible (i.e. as soon as you know the intended interface +type) to benefit from your compiler's type-checking and avoid having to pass +around both a name and a pipe. +*** + +The last piece of our service that we need to lay down is its manifest. + +### Manifests + +A service's manifest is a simple static data structure provided to the Service +Manager early during its initialization process. The Service Manager combines +all of the manifest data it has in order to form a complete picture of the +system it's coordinating. It uses all of this information to make decisions +like: + +- When service X requests interface Q from service Y, should it be allowed? +- Were all of the constraints specified in X's request valid, and is X allowed + to specify them as such? +- Do I need to spawn a new instance of Y to satisfy this request or can I re-use + an existing one (assuming there are any)? +- If I have to spawn a new process for a new Y instance, how should I configure + its sandbox, if at all? + +All of this metadata is contained within different instances of the +[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h) +class. -namespace my_service { +#### A Basic Manifest -MyService::MyService() = default; +The most common way to define a service's manifest is to place it in its own +source target within the service's C++ client library. To combine the +convenience of inline one-time initialization with the avoidance of static +initializers, typically this means using a function-local static with +`base::NoDestructor` and `service_manager::ManifestBuilder` as below. First the +header: -MyService::~MyService() = default; +``` cpp +// src/services/storage/public/cpp/manifest.h -void MyService::OnStart() { -} +#include "services/service_manager/public/cpp/manifest.h" -void MyService::OnBindInterface(const service_manager::ServiceInfo& remote_info, - const std::string& interface_name, - mojo::ScopedMessagePipeHandle handle) { -} +namespace storage { -} // namespace my_service -``` +const service_manager::Manifest& GetManifest(); -### Main Entry Point +} // namespace storage +``` -While services do not need to define a main entry point -- *e.g.* they may only -intend to be embedded in other running processes -- for the sake of completeness -we also define a `ServiceMain` definition so that the service can be run in its -own process: +And for the actual implementation: -**`//services/my_service/my_service_main.cc`** ``` cpp -#include "services/my_service/my_service.h" -#include "services/service_manager/public/c/main.h" -#include "services/service_manager/public/cpp/service_runner.h" +// src/services/storage/public/cpp/manifest.cc -MojoResult ServiceMain(MojoHandle service_request_handle) { - return service_manager::ServiceRunner(new MyService).Run( - service_request_handle); -} +#include "services/storage/public/cpp/manifest.h" + +#include "base/no_destructor.h" +#include "services/storage/public/mojom/constants.mojom.h" +#include "services/service_manager/public/cpp/manifest_builder.h" + +namespace storage { + +const service_manager::Manifest& GetManifest() { + static base::NoDestructor manifest{ + service_manager::ManifestBuilder() + .WithServiceName(mojom::kServiceName) + .Build()}; + return *manifest; +}; + +} // namespace storage ``` -### Manifest +Here we've specified only the **service name**, matching the constant defined +in `constants.mojom` so that other services can easily locate us without a +hard-coded string. -A static manifest is provided to the Service Manager by each service to declare -the capabilities exposed and required by the service: +With this manifest definition there is no way for our service to reach other +services, and there's no way for other services to reach us; this is because +we neither **expose** nor **require** any capabilities, thus the Service Manager +will always block any interface request from us or targeting us. -**`//services/my_service/manifest.json`** -``` json -{ - "name": "my_service", - "display_name": "My Service", - "interface_provider_specs": { - "service_manager:connector": {} - } -} +#### Exposing Interfaces + +Let's expose an "allocator" capability that grants permission to bind a +`BlockAllocator` pipe. We can augment the above manifest definition as follows: + +``` cpp +... +#include "services/storage/public/mojom/block.mojom.h" +... + +... + .WithServiceName(mojom::kServiceName) + .ExposeCapability( + mojom::kAllocatorCapability, + service_manager::Manifest::InterfaceList()) + .Build() +... ``` -See [Service Manifests](#Service-Manifests) for more information. +This declares the existence of an `"allocator"` capability exposed by our +service, and specifies that granting a client this capability means granting it +the privilege to send our service `storage.mojom.BlockAllocator` interface +requests. -### Build Targets +You can list as many interfaces as you like for each exposed capability, and +multiple capabilities may list the same interface. -Finally some build targets corresponding to the above things: +**NOTE**: You only need to expose an interface through a capability if you want +other services to be able to be able to request it *through the Service +Manager* (see [Connectors](#Connectors)) -- that is, if you handle requests for +it in your `Service::OnBindInterface` implementation. -**`//services/my_service/BUILD.gn`** -``` python -import("//services/service_manager/public/cpp/service_executable.gni") -import("//services/service_manager/public/service_manifest.gni") +Contrast this with interfaces acquired transitively, like `Block` above. The +Service Manager does not mediate the behavior of existing interface connections, +so once a client has a `BlockAllocator` they can use `BlockAllocator.Allocate` +to send as many `Block` requests as they like. Such requests go directly to +the service-side implementation of `BlockAllocator` to which the pipe is bound, +and so manifest contents are irrelevant to their behavior. -source_set("lib") { - public = [ "my_service.h" ] - sources = [ "my_service.cc" ] +#### Getting Access to Interfaces - public_deps = [ - "//base", - "//services/service_manager/public/cpp", - ] -} +We don't need to add anything else to our `storage` manifest, but if another +service wanted to enjoy access to our amazing storage block allocation +facilities, they would need to declare in their manifest that they **require** +our `"allocation"` capability. For ease of maintenance they would utilitize our +publicly defined constants to do this. It's pretty straightforward: -service("my_service") { - sources = [ - "my_service_main.cc", - ] - deps = [ - ":lib", - "//services/service_manager/public/c", - ] -} +``` cpp +// src/services/some_other_pretty_cool_service/public/cpp/manifest.cc -service_manifest("manifest") { - name = "my_service" - source = "manifest.json" -} +... // Somewhere along the chain of ManifestBuilder calls... + .RequireCapability(storage::mojom::kServiceName, + storage::mojom::kAllocationCapability) +... ``` -Building the `my_service` target produces a `my_service.service` (or on Windows, -`my_service.service.exe`) binary in the output directory. This can be run as -a standalone executable, but it will exit immediately without doing anything -interesting, because it won't have a `Service` pipe to drive it. The Service -Manager knows how to provide such a pipe when launching a service executable. - -This service doesn't do much of anything. It will simply run forever (or at -least until the Service Manager itself shuts down), ignoring all incoming -messages. Before we expand on the definition of this service, let's look at some -of the details of the `service_manager::Service` interface. - -### OnStart - -The `Service` implementation is guaranteed to receive a single `OnStart()` -invocation from the Service Manager before anything else hapens. Once this -method is called, the implementation can access its -`service_manager::ServiceContext` via `context()`. This object itself exposes a -few values: - -* `service_info()` is a `service_manager::ServiceInfo` structure describing the - running service from the Service Manager's perspective. This includes the - `service_manager::Identity` which uniquely identifies the running instance, - as well as the `service_manager::InterfaceProviderSpec` describing the - capability specifications outlined in the service's manifest. -* `identity()` is a shortcut to the `Identity` stored in the - `ServiceInfo`. -* `connector()` is a `service_manager::Connector` which can be used to make - outgoing interface requests to other services. - -For example, we could modify `MyService` to connect out to logger service on -startup: +Now `some_other_pretty_cool_service` can use its [Connector](#Connectors) to ask +the Service Manager for a `BlockAllocator` from us, like so: ``` cpp -void MyService::OnStart() { - logger::mojom::LoggerPtr logger; - context()->connector()->BindInterface("logger", &logger); - logger->Log("Started MyService!"); -} +storage::mojom::BlockAllocatorPtr allocator; +connector->BindInterface(storage::mojom::kServiceName, + mojo::MakeRequest(&allocator)); + +storage::mojom::BlockPtr block; +allocator->Allocate(42, mojo::MakeRequest(&block)); + +// etc.. ``` -### OnBindInterface - -The `OnBindInterface` method on `service_manager::Service` is invoked by the -Service Manager any time another service instance uses its own `Connector` to -request an interface from this `my_service` instance. The Service Manager only -invokes this method once it has already validated that the request meets the -mutual constraints specified in each involved service's manifest. - -The arguments to `OnBindInterface` are as follows: - -* `remote_info` is the `service_manager::ServiceInfo` corresponding to the - remote service which is requesting this interface. The information in this - structure is provided authoritatively by the Service Manager and can be - trusted in any context. -* `interface_name` is the (`std::string`) name of the interface being requested - by the remote service. The Service Manager has already validated that the - remote service requires at least one capability which exposes this interface - from the local service. -* `handle` is the `mojo::ScopedMessagePipeHandle` of an interface pipe which - the remote service expects us to bind to a concrete implementation of - the requested interface. - -The Service Manager client library provides a `service_manager::BinderRegistry` -class definition which can make it easier for services to bind incoming -interface requests. Typesafe binding callbacks are added to an `BinderRegistry` -ahead of time, and the incoming arguments to `OnBindInterface` can be forwarded -to the registry, which will bind the message pipe if it knows how. For example, -we could modify our `MyService` implementation as follows: +#### Other Manifest Elements + +There are a handful of other optional elements in a `Manifest` structure which +can affect how your service behaves at runtime. See the current +[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h) +definition and comments as well as +[`ManifestBuilder`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h) +for the most complete and current information, but some of the more common +properties specified by manifests are: + +- **Display Name** - This is the string the Service Manager will use to name + any new process created to run your service. This string would appear in the + Windows Task Manager to identify the service process, for example. +- **Options** - A few miscellaneous options are stuffed into a `ManifestOptions` + field. These include sandbox type (see + [Sandbox Configurations](#Sandbox-Configurations)), + [instance sharing policy](#Instance-Sharing), and various behavioral flags to + control a few [special capabilities](#Additional-Capabilities). +- **Preloaded Files** - On Android and Linux platforms, the Service Manager can + open specified files on the service's behalf and pass the corresponding open + file descriptor(s) to each new service process on launch. +- **Packaged Services** - A service may declare that it **packages** another + service by including a copy of that service's own manifest. See + [Packaging](#Packaging) for details. + +### Running the Service + +Hooking the service up so that it can be run in a production environment is +actually outside the scope of this document at the moment, only because it still +depends heavily on the environment in which the Service Manager is embedded. For +now, if you want to get your great little service hooked up in Chromium for +example, you should check out the sections on this in the very Chromium-centric +[Intro to Mojo & Services](/docs/mojo_and_services.md#Hooking-Up-the-Service-Implementation) +and/or +[Servicifying Chromium Features](/docs/servicification.md#Putting-It-All-Together) +documents. + +For the sake of this document, we'll focus on running the service in test +environments with the service both in-process and out-of-process. + +### Testing + +There are three primary approaches used when testing services, applied in +varying combinations: + +#### Standard Unit-testing +This is ideal for covering details of your service's internal components and +making sure they operate as expected. There is nothing special here regarding +services. Code is code, you can unit-test it. + +#### Out-of-process End-to-end Tests +These are good for emulating a production environment as closely as possible, +with your service implementation isolated in a separate process from the test +(client) code. + +The main drawback to this approach is that it limits your test's ability to poke +at or observe internal service state, which can sometimes be useful in test +environments (for *e.g.* faking out some behavior in a predictable manner). In +general, supporting such controls means adding test-only interfaces to your +service. + +The +[`TestServiceManager`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/test_service_manager.h) +helper and +[`service_executable`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_executable.gni) +GN target type make this fairly easy to accomplish. You simply define a new +entry point for your service: ``` cpp -namespace { +// src/services/storage/service_main.cc -void BindDatabase(my_service::mojom::DatabaseRequest request) { - mojo::MakeStrongBinding(std::make_unique(), - std::move(request)); +#include "base/message_loop.h" +#include "services/service_manager/public/cpp/service_executable/main.h" +#include "services/storage/storage_service.h" + +void ServiceMain(service_manager::ServiceRequest request) { + base::MessageLoop message_loop; + storage::StorageService(std::move(request)).RunUntilTermination(); } +``` -} // namespace +and a GN target for this: -MyService::MyService() { - // Imagine |registry_| is added as a member of MyService, with type - // service_manager::BinderRegistry. +``` python +import "services/service_manager/public/cpp/service_executable.gni" - // The |my_service::mojom::Database| interface type is inferred by the - // compiler in the AddInterface call, and this effectively adds the bound - // function to an internal map keyed on the interface name, i.e. - // "my_service::mojom::Database" in this case. - registry_.AddInterface(base::Bind(&BindDatabase)); -} +service_executable("storage") { + sources = [ + "service_main.cc", + ] -void MyService::OnBindInterface(const service_manager::ServiceInfo& remote_info, - const std::string& interface_name, - mojo::ScopedMessagePipeHandle handle) { - registry_.BindInterface(interface_name, std::move(handle)); + deps = [ + # The ":impl" target would be the target that defines our StorageService + # implementation. + ":impl", + "//base", + "//services/service_manager/public/cpp", + ] } -``` -For more details regarding the definition of Mojom interfaces, implementing them -in C++, and working with C++ types like `InterfaceRequest`, see the -[Mojom IDL and Bindings Generator](/mojo/public/tools/bindings) and -[Mojo C++ Bindings API](/mojo/public/cpp/bindings) documentation. - -## Service Manifests - -If some service were to come along and attempt to connect to `my_service` and -bind the `my_service::mojom::Database` interface, we might see the Service -Manager spit out an error log complaining that `InterfaceProviderSpec` prevented -a connection to `my_service`. - -In order for the interface to be reachable by other services, we must first fix -its manifest's **interface provider spec**. The interface provider spec is -a dictionary keyed by **interface provider name**, with each value representing -the **capability spec** for that provider. - -Each capability spec defines an optional `"provides"` key and an optional -`"requires"` key. - -The `provides` key value is a dictionary which is itself keyed by arbitrary -free-form strings (capability names, implicitly scoped to the manifest's own -service) whose values are lists of Mojom interface names exposed as part of that -capability. - -The `requires` key value is also a dictionary, but it's one which is keyed by -remote service name. Each value is a list of capabilities required from the -corresponding remote service. - -Finally, every interface provider spec (often exclusively) contains one standard -capability spec named "service_manager:connector". This is the capability spec -enforced when inter-service connections are made from a service's `Connector` -interface. - -Let's update the `my_service` manifest as follows: - -**`//services/my_service/manifest.json`** -``` json -{ - "name": "my_service", - "display_name": "My Service", - "interface_provider_specs": { - "service_manager:connector": { - "provides": { - "database": [ - "my_service::mojom::Database" - ] - } - } - } -} -``` +test("whatever_unittests") { + ... -This means that `my_service` has defined a `database` capability comprised -solely of the `my_service::mojom::Database` interface. Any service which -requires this capability can bind that interface from `my_service`. - -For the sake of this example, let's define another service manifest: - -**`//services/other_service/manifest.json`** -``` json -{ - "name": "other_service", - "display_name": "Other Service", - "interface_provider_specs": { - "service_manager:connector": { - "requires": { - "my_service": [ "database" ] - } - } - } + # Include the executable target as data_deps for your test target + data_deps = [ ":storage" ] } ``` -Now if `other_service` attempts to bind the database interface: +And finally in your test code, use `TestServiceManager` to create a real +Service Manager instance within your test environment, configured to know about +your `storage` service. + +`TestServiceManager` allows you to inject an artificial service instance to +treat your test suite as an actual service instance. You can provide a manifest +for your test to simulate requiring (or failing to require) various capabilities +and get a `Connector` with which to reach your service-under-test. This looks +something like: ``` cpp -void OtherService::OnStart() { - my_service::mojom::DatabasePtr database; - context()->connector()->BindInterface("my_service", &database); - database->AddTable(...); +#include "services/service_manager/public/cpp/manifest_builder.h" +#include "services/service_manager/public/cpp/test/test_service.h" +#include "services/service_manager/public/cpp/test/test_service_manager.h" +#include "services/storage/public/cpp/manifest.h" +#include "services/storage/public/mojom/constants.mojom.h" +#include "services/storage/public/mojom/block.mojom.h" +... + +TEST(StorageServiceTest, AllocateBlock) { + const char kTestServiceName[] = "my_inconsequentially_named_test_service"; + service_manager::TestServiceManager service_manager( + // Make sure the Service Manager knows about the storage service. + {storage::GetManifest, + + // Also make sure it has a manifest for our test service, which this + // test will effectively act as an instance of. + service_manager::ManifestBuilder() + .WithServiceName(kTestServiceName) + .RequireCapability(storage::mojom::kServiceName, + storage::mojom::kAllocationCapability) + .Build()}); + service_manager::TestService test_service( + service_manager.RegisterTestInstance(kTestServiceName)); + + storage::mojom::BlockAllocatorPtr allocator; + + // This Connector belongs to the test service instance and can reach the + // storage service through the Service Manager by virtue of the required + // capability above. + test_service.connector()->BindInterface(storage::mojom::kServiceName, + mojo::MakeRequest(&allocator)); + + // Verify that we can request a small block of storage. + storage::mojom::BlockPtr block; + allocator->Allocate(64, mojo::MakeRequest(&block)); + + // Do some stuff with the block, etc... } ``` -The Service Manager will approve of the request and forward it on to the -`my_service` instance's `OnBindInterface` method. - -## Testing +#### In-Process Service API Tests -Now that we've built a simple service it's time to write a test for it. -The Service Manager client library provides a test fixture base class in -[`service_manager::test::ServiceTest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_test.h) that makes writing service integration tests straightforward. This test fixture -runs an in-process Service Manager on a background thread which allows test -service instances to be injected at runtime. +Sometimes you want to poke at your service primarily through its client API, +but you also want to be able to -- either for convenience or out of necessity -- +observe or manipulate its internal state within the test code. Running the +service in-process is ideal in this case, and in that case there's not much use +in involving the Service Manager or dealing with manifests. -Let's look at a simple test of our service: +Instead you can use a +[`TestConnectorFactory`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/test_connector_factory.h) +to give yourself a working `Connector` object which routes interface requests +directly to specific service instances which you wire up directly. For a quick +example, suppose we had some client library helper function for allocating a +block of storage when given a `Connector`: -**`//services/my_service/my_service_unittest.cc`** ``` cpp -#include "base/bind.h" -#include "base/run_loop.h" -#include "services/service_manager/public/cpp/service_test.h" -#include "path/to/some_interface.mojom.h" +// src/services/storage/public/cpp/allocate_block.h -class MyServiceTest : public service_manager::test::ServiceTest { - public: - // Our tests run as service instances themselves. In this case each instance - // identifies as the service named "my_service_unittests". - MyServiceTest() : service_manager::test::ServiceTest("my_service_unittests") { - } +namespace storage { - ~MyServiceTest() override {} -} +// This helper function can be used by any service which is granted the +// |kAllocationCapability| capability. +mojom::BlockPtr AllocateBlock(service_manager::Connector* connector, + uint64_t size) { + mojom::BlockAllocatorPtr allocator; + connector->BindInterface(mojom::kServiceName, mojo::MakeRequest(&allocator)); -TEST_F(MyServiceTest, Basic) { - my_service::mojom::DatabasePtr database; - connector()->BindInterface("my_service", &database); + mojom::BlockPtr block; + allocator->Allocate(size, mojo::MakeRequest(block)); + return block; +} - base::RunLoop loop; +} // namespace storage +``` - // This assumes DropTable expects a response with no arguments. When the - // response is received, the RunLoop is quit. - database->DropTable("foo", loop.QuitClosure()); +Our test could look something like: - loop.Run(); +``` cpp +TEST(StorageTest, AllocateBlock) { + service_manager::TestConnectorFactory test_connector_factory; + storage::StorageService service( + test_connector_factory.RegisterInstance(storage::mojom::kServiceName)); + + constexpr uint64_t kTestBlockSize = 64; + storage::mojom::BlockPtr block = storage::AllocateBlock( + test_connector_factory.GetDefaultConnector(), kTestBlockSize); + block.FlushForTesting(); + + // Verify that we have the expected number of bytes allocated within the + // service implementation. + EXPECT_EQ(kTestBlockSize, service.GetTotalAllocationSizeForTesting()); } ``` -If adding a new test binary for these tests, we can augment our `BUILD.gn` to -use the `service_test` GN template like so: +### Connectors -**`//services/my_service/BUILD.gn`** -``` cpp -import("//services/catalog/public/tools/catalog.gni") -import("//services/service_manager/public/tools/test/service_test.gni") +While the +[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h) +interface is what the Service Manager uses to drive a service instance's +behavior, a +[`Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h) +is what the service instance uses to send requests to the Service Manager. This +interface is connected when your instance is launched, and `ServiceBinding` +maintains and +[exposes](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h?rcl=887b934e0d979f3da81c41cadc396b4ef587257a&l=66) +it on your behalf. -service_test("my_service_unittests") { - sources = [ - "my_service_unittest.cc", - ] - deps = [ - "//services/my_service/public/interfaces", - ] - catalog = ":my_service_unittests_catalog" -} +#### Sending Interface Requests -service_manifest("my_service_unittests_manifest") { - name = "my_service_unittests" - manifest = "my_service_unittests_manifest.json" -} +By far the most common and useful method on `Connector` is `BindInterface`, +which allows your service to send an interface request to another service in the +system, configuration permitting. -catalog("my_service_unittests_catalog") { - testonly = true - embedded_services = [ ":my_service_unittests_manifest" ] - standalone_services = [ ":manifest" ] -} -``` +Supposing the `storage` service actually depended on an even lower-level storage +service to get at its disk, you could imagine its block allocation code doing +something like: -Alright, there's a lot going on here. First we also have to create a service -manifest for the test service itself, as the Service Manager needs to be able -to reason about the test's own required capabilities with respect to the -service-under-test. - -We can do something like: - -**`//services/my_service/my_service_unittests_manifest.json`** -``` json -{ - "name": "my_service_unittests", - "display_name": "my_service tests", - "interface_provider_specs": { - "service_manager:connector": { - "requires": { - "my_service": [ "database" ] - } - } - } -} +``` cpp + real_storage::mojom::ReallyRealStoragePtr storage; + service_binding_.GetConnector()->BindInterface( + real_storage::mojom::kServiceName, mojo::MakeRequest(&storage)); + storage->AllocateBytes(...); ``` -You may also notice that we have suddenly introduced a **catalog** in the -`service_test` target incantation. Any runtime environment which hosts a -Service Manager must provide the Service Manager implementation with a catalog -of service manifests. This catalog defines the complete set of services -recognized by the Service Manager instance and can be used in all kinds of -interesting ways to control how various services are started in the system. See -[Service Manager Catalogs](#Service-Manager-Catalogs) for more information. - -For now let's just accept that we have to create a `catalog` rule for our test -suite and plug it into the `service_test` target. +Note that the first argument to this particular overload of `BindInterface` is +a string, but the more generalized form of `BindInterface` takes a +`ServiceFilter`. See more about these in the section on +[Service Filters](#Service-Filters). -In practice, we typically try to avoid introducing new unittest binaries for -individual services. Instead we have an aggregate `service_unittests` target -defined in [`//services/BUILD.gn`](https://cs.chromium.org/chromium/src/services/BUILD.gn). -There are several examples of other services adding their service tests to this -suite. +#### Registering Service Instances -## Service Manager Catalogs +One of the superpowers services can be granted is the ability to forcibly inject +new service instances into the Service Manager's universe. This is done via +[`Connector::ServiceInstance`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h?rcl=ec509adfa3ac85fab3cd51422b8aaf9cbb6b43cb&l=108) and is still +used pretty heavily by Chromium's browser process. Most services don't need to +touch this API. -A **catalog** is an aggregation of service manifests which comprises a complete -runtime configuration of the Service Manager. +#### Usage in Multithreaded Environments -The GN `catalog` target template defined in -[`//services/catalog/public/tools/catalog.gni`](https://cs.chromium.org/chromium/src/services/catalog/public/tools/catalog.gni). -provides a simple means of aggregating service manifests into a single build -artifact. See the comments on the template for detailed documentation. +Connectors are **not** thread-safe, but they do support **cloning**. There are +two useful ways you can associate a new Connector with an existing one on a +different thread. -This GNI also defines a `catalog_cpp_source` target which can generate a static -C++ representation of an aggregated catalog manifest so that it can be passed -the Service Manager at runtime. +You can `Clone` the `Connector` on its own thread and then pass the clone to +another thread: -In general, service developers should never be concerned with creating new -catalogs or instantiating the Service Manager, but it's important to be aware -of these concepts. When introducing a new service into any runtime environment --- including Chrome, Content, or various unit test suites such as -`service_unittests` discussed in the previous section -- your service manifest -must be added to the catalog used in that environment. +``` cpp +std::unique_ptr new_connector = connector->Clone(); +base::PostTaskWithTraits(...[elsewhere]..., + base::BindOnce(..., std::move(new_connector))); +``` -TODO - expand on this +Or you can fabricate a brand new `Connector` right from where you're standing, +and asynchronously associate it with one on another thread: -## Packaging Services +``` cpp +service_manager::mojom::ConnectorRequest request; +std::unique_ptr new_connector = + service_manager::Connector::Create(&request); + +// |new_connector| can be used to start issuing calls immediately, despite not +// yet being associated with the establshed Connector. The calls will queue as +// long as necessary. + +base::PostTaskWithTraits( + ...[over to the correct thread]..., + base::BindOnce([](service_manager::ConnectorRequest request) { + service_manager::Connector* connector = GetMyConnectorForThisThread(); + connector->BindConnectorRequest(std::move(request)); + })); +``` -TODO +### Identity + +Every service instance started by the Service Manager is assigned a globally +unique (across space *and* time) identity, encapsulated by the +[`Identity`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/identity.h) +type. This value is communicated to the service and retained and +[exposed](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h?rcl=b8bc0ab281f2cb5cd567dc994692c6022845fb89&l=62) +by `ServiceBinding` immediately before `Service::OnStart` is invoked. + +There are *four* components to an `Identity`: + +- Service name +- Instance ID +- Instance group ID +- Globally unique ID + +You're already quite familiar with the **service name**: this is whatever the +service declared in its manifest, *e.g.*, `"storage"`. + +#### Instance ID + +**Instance ID** is a `base::Token` qualifier which is simply used to +differentiate multiple instances of the service if multiple instances are +desired for whatever arbitrary reason. By default instances get an instance ID +of zero when started unless a connecting client *explicitly* requests a specific +instance ID. Doing so requires a special manifest-declared capability covered by +[Additional Capabilities](#Additional-Capabilities). + +*** aside +A good example of how instance ID can be useful: the `"unzip"` service in +Chrome is used to safely unpack untrusted Chrome extensions (CRX) archives, but +we don't want multiple extensions being unpacked by the same process. To support +this, Chrome generates a random `base::Token` for the instance ID it uses when +connecting to the `"unzip"` service, and this elicits the creation of a new +service instance in a new isolated process for each such connection. See +[Service Filters](#Service-Filters) for how this can be done. +*** + +#### Instance Group ID + +All created service instances implicitly belong to an **instance group**, which +is also identified by a `base::Token`. Unless either specially privileged by +[Additional Capabilities](#Additional-Capabilities), or the target service is +a [singleton or shared across groups](#Instance-Sharing), the service sending out +an interface request can only reach other service instances in the same instance +group. See [Instance Groups](#Instance-Groups) for more information. + +#### Globally Unique ID + +Finally, the **globally unique ID** is a cryptographically secure, unguessably +random `base::Token` value which can be considered unique across all time and +space. This can never be controlled by an instance or even by a highly +privileged service, and its sole purpose is to ensure that `Identity` itself +can be treated as unique across time and space. See +[Service Filters](#Service-Filters) and +[Observing Service Instances](#Observing-Service-Instances) for why this +property of uniqueness is useful and sometimes necessary. + +### Instance Sharing + +Assuming the Service Manager has decided to allow an interface request due to +sufficient capability requirements, it must consider a number of factors to +decide where exactly to route the request. The first factor is the **instance +sharing policy** of the target service, declared in its manifest. There are +three supported policies: + +- **No sharing** - This means the precise identity of the target instance + depends on both the instance ID provided by the request's `ServiceFilter`, + as well as the instance group either provided by the `ServiceFilter` or + inherited from the source instance's group. +- **Shared across groups** - This means the precise identity of the target + instance still depends on the instance ID provided by the request's + `ServiceFilter`, but the instance group of both the `ServiceFilter` and the + source instance are completely ignored. +- **Singleton** - This means there can be only one instance of the service at + a time, no matter what. Instance ID and group are always ignored when + connecting to the service. + +Based on one of the policies above, the Service Manager determines whether or +not an existing service instance matches the parameters specified by the given +`ServiceFilter` in conjunction with the source instance's own identity. If so, +that Service Manager will forward the interface request to that instance via +`Service::OnBindInterface`. Otherwise, it will spawn a new instance which +sufficiently matches the constraints, and it will forward the request to that +new instance. + +### Instance Groups + +Service instances are organized into **instance groups**. These are arbitrary +partitions of instances which can be used by the host application to impose +various kinds of security boundaries. + +Most services in the system do not have the privilege of specifying the +instance group they want to connect into when passing a `ServiceFilter` to +`Connector::BindInterface` (see +[Additional Capabilities](#Additional-Capabilities)). As such, most +`BindInterface` calls implicitly inherit the group ID of the caller and only +cross outside of the caller's instance group when targeting a service which +adopts either a singleton or shared-across-groups +[sharing policy](#Instance-Sharing) in its manifest. + +Singleton and shared-across-groups services are themselves always run in their +own isolated groups. + +### Service Filters + +The most common form of `BindInterface` calls passes a simple string as the +first argument. This is essentially telling the Service Manager that the caller +doesn't care about any details regarding the target instance's identity -- it +only cares about talking to *some* instance of the named service. + +When a client *does* care about other details, they can explicitly construct and +pass a `ServiceFilter` object, which essentially provides some subset of the +desired target instance's total `Identity`. + +Specifying an instance group or instance ID in a `ServiceFilter` requires a +service to declare [additional capabilities](#Additional-Capabilities) in its +manifest options. + +A `ServiceFilter` can also wrap a complete `Identity` value, including the +globally unique ID. This filter always *only* matches a specific instance unique +in space and time. So if the identified instance has died and been replaced by +a new instance with the same service name, same instance ID, and same instance +group, the request will still *fail*, because the globally unique ID component +will *never* match this or any future instance. + +One useful property of targeting a specific `Identity` is that the client can +connect without any risk of eliciting new target instance creation: either +the target exists and the request can be routed, or the target doesn't exist +and the request will be dropped. + +### Additional Capabilities + +Service manifests can use `ManifestOptionsBuilder` to set a few additional +boolean options controlling their Service Manager privileges: + +- `CanRegisterOtherServiceInstances` - If this is `true` the service can call + `RegisterServiceInstance` on its `Connector` to forcibly introduce new service + instances into the environment. +- `CanConnectToInstancesWithAnyId` - If this is `true` the service can specify + an instance ID in any `ServiceFilter` it passes to `BindInterface`. +- `CanConnectToInstancesInAnyGroup` - If this is `true` the service can specify + an instance group ID in any `ServiceFilter` it passes to `BindInterface`. + +### Packaging + +A service can declare that it **packages** another service by +[nesting](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h?rcl=7839843db1ccdf13c3f1b8cb90a763989dde83a8&l=87) that +service's manifest within its own. + +This signals to the Service Manager that it should defer to the packaging +service when it needs a new instance of the packaged service. For example, if +we offered up the manifest: -## Chrome and Chrome OS Service Manager Integration +``` cpp + service_manager::ManifestBuilder() + .WithServiceName("fruit_vendor") + ... + .PackageService(service_manager::ManifestBuilder() + .WithServiceName("banana_stand") + .Build()) + .Build() +``` -TODO +And someone wanted to connect to a new instance of the `"banana_stand"` service +(there's always money in the banana stand), the Service Manager would ask an +appropriate `"fruit_vendor"` instance to do this on its behalf. + +*** aside +NOTE: If an appropriate instance of `"fruit_vendor"` wasn't already running -- +as determined by the rules described in [Instance Sharing](#Instance-Sharing) +above -- one would first be spawned by the Service Manager. +*** + +In order to support this operation, the `fruit_vendor` must expose a capability +named exactly `"service_manager:service_factory"` which includes the +`"service_manager.mojom.ServiceFactory"` interface. Then it must handle requests +for the `service_manager.mojom.ServiceFactory` interface in its implementation +of `Service::OnBindInterface`. The implementation of `ServiceFactory` provided +by the service must then handle the `CreateService` that will be sent by +the Service Manager. This call will include the name of the service and the +`ServiceRequest` the new service instance will need to bind. + +*** aside +NOTE: It is this complicated for historical reasons. Expect it to be less +complicated soon. +*** + +Services can use this for example if, in certain runtime environments, they want +to share their process with another service. + +*** aside +FUN FACT: This is actually how Chromium manages *all* services today, because +the Content layer still owns much of the production-ready process launching +logic. We have a singleton `content_packaged_services` service which packages +nearly all other registered services in the system, and so the Service Manager +defers (via `ServiceFactory`) nearly all service instance creation operations +to Content. +*** + +### Sandbox Configurations + +Service manifests support specifying a fixed sandbox configuration for the +service to be launched with when run out-of-process. Currently these values +are strings which must match one of the defined constants +[here](https://cs.chromium.org/chromium/src/services/service_manager/sandbox/switches.cc?rcl=2e6a3bddac0aff89c5ff415e9c1cd4da804280ef&l=23). + +The most common and default value is `"utility"`, which is a restrictive sandbox +configuration and generally a safe choice. For services which must run +unsandboxed, use the value `"none"`. Use of other sandbox configurations should +be done under the advisory of Chrome's security reviewers. + +### Observing Service Instances + +Services which require the `"service_manager:service_manager`" capability from +the `"service_manager"` service can connect to the `"service_manager"` service +to request a +[`ServiceManager`](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/service_manager.mojom?rcl=765c18ee7c317535594ba37520a23c11f0cef008&l=82) +interface. This can in turn be used to register a new +[`ServiceManagerListener`](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/service_manager.mojom?rcl=765c18ee7c317535594ba37520a23c11f0cef008&l=44) to +observe lifecycle events pertaining to all service instances hosted by the +Service Manager. + +There are several +[examples](https://cs.chromium.org/search/?q=mojo::Binding%3Cservice_manager::mojom::ServiceManagerListener%3E&type=cs) +of this throughout the tree. + +## Additional Support + +If this document was not helpful in some way, please post a message to your +friendly +[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) +mailing list. + +Also don't forget to take a look at other +[Mojo & Services](/docs/README.md#Mojo-Services) documentation in the tree. diff --git a/services/service_manager/service_manifests.md b/services/service_manager/service_manifests.md deleted file mode 100644 index 6a8fd9f2fcb72f..00000000000000 --- a/services/service_manager/service_manifests.md +++ /dev/null @@ -1,315 +0,0 @@ -# Service Manifests - -[TOC] - -## Overview - -Manifest files are used to configure security properties and -permissions for services, such as listing allowed sets of interfaces or -specifying a sandbox type. The files use JSON format and are usually -placed in the same directory as the service source files, but the path -is configurable in the BUILD.gn file for the service -(see [README.md](README.md#build-targets) for an example). - -## Terminology - -The Service Manager is responsible for starting new service instances on-demand, -and a given service may have any number of concurrently running instances. -The Service Manager disambiguates service instances by their unique identity. -A service's **identity** is represented by the 4-tuple of its service name, -instance group, instance ID, and a globally unique ID: - -### Service name - -A free-form -- typically short -- string identifying the the specific service -being run in the instance. - -### Instance Group - -Service instances started by the Service Manager are organized into -*instance groups*. Typically a service instance can only connect to other -service instances in its own group. Exceptions are made for targeted services -which are designed to be accessible cross-group (such as singleton services), as -well as for connecting services which are sufficiently privileged to connect to -any service instance in the system. - -Instance group is represented by a `base::Token` in `service_manager::Identity`. -When instance group is omitted from an Identity passed with an interface request -to the Service Manager, the Service Manager assumes that the request should be -routed to a service instance in the requestor's same instance group. - -In Chrome, every `BrowserContext` has a randomly generated instance group ID -associated with it, and this is used to isolate the service instances which run -on behalf of specific profiles, including renderers. - -### Instance ID - -Another `base::Token` in `service_manager::Identity`, used to disambiguate -multiple instances of a service for the same user. Every instance has an -instance ID, but it typically takes on the default value of a zero-token. - -### Globally Unique ID - -Every service instance started by the Service Manager is assigned a randomly -generated `base::Token` value designated as the instance's -**globally unique ID**. This value is always unique to a single instance across -time and space. - -### Connections - -Every service instance has a Connector API it can use to issue requests to the -Service Manager. One such request is `BindInterface`, which allows the service -to bind an interface within another service instance. - -When binding an interface, the **source identity** refers to the service -initiating the bind request, and the **target identity** refers to the -destination service instance. Based on both the source and target identities, -the Service Manager may choose to start a new service instance, reuse an -existing instance as the destination for the bind request or deny the request. - -### Interface provider - -InterfaceProvider is a Mojo -[interface](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/interface_provider.mojom) -for providing an implementation of an interface by name. It is implemented by -different hosts (for frames and workers) in the browser, and the manifest -allows to specify different sets of capabilities exposed by these hosts. - -## File structure - -### name (string) - -A unique identifier that is used to refer to the service. - -### display\_name (string) - -A human-readable name which can have any descriptive value. Not user-visible. - -### sandbox\_type (string) - -An optional field that provides access to several types of sandboxes. -Inside a sandbox, by default the service is essentially restricted to CPU and -memory access only. Common values are: - -* `utility` (default) - also allows full access to one configurable directory; -* `none` - no sandboxing is applied; -* `none_and_elevated` - under Windows, no sandboxing is applied and privileges -are elevated. - -If the service cannot run with a sandbox type of utility, elevated, or none, -please reach out to the security team. - -### options (dictionary) - -This field can be used to specify values for any of the following options: - -#### instance\_sharing (string) - -Determines which parameters result in the creation of a new service -instance on an incoming service start/interface bind request. - -Possible values: - -##### none (default) - -When one service is connecting to another, checks are performed to either find -an existing instance that matches the target identity or create a new one if -no match is found. - -By default, all four identity components (service name, instance group, instance -ID, and globally unique ID) are used to find a match. - -See -[advice](https://chromium.googlesource.com/chromium/src/+/master/docs/servicification.md#is-your-service-global-or-per_browsercontext) -on using this option. - -Example: -[identity](https://cs.chromium.org/chromium/src/services/identity/manifest.json) - -##### shared\_across\_instance\_groups - -In this case, the instance group parameter is ignored when looking for a -matching target instance, so an existing instance can be reused regardless of -which instance group the connecting service belongs to. - -Example: -[data_decoder](https://cs.chromium.org/chromium/src/services/data_decoder/manifest.json) - -##### singleton - -In this case, both instance group and instance ID parameters are ignored when -an interface request is targeting the service. Only one service instance is -created, and all interface requests targeting the service will be routed to that -instance. - -Example: -[download_manager](https://cs.chromium.org/chromium/src/chrome/browser/android/download/manifest.json) - -#### can\_connect\_to\_instances\_in\_any\_group (bool) - -This option allows a service to make outgoing requests with a target instance -group other than its own. - -**Note:** this privilege must only be granted to services that are trusted -at the same level as the browser process itself. - -Example: -[content_browser](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json) - -The browser process manages all `BrowserContexts`, so it needs this privilege -to act on behalf of different users. - -#### can\_connect\_to\_other\_services\_with\_any\_instance\_name (bool) - -This option allows a service to specify an instance ID that is -different from the service name when connecting. - -**Note:** this privilege must only be granted to services that are trusted -at the same level as the browser process itself. - -Example: -[chrome_browser](https://cs.chromium.org/chromium/src/chrome/app/chrome_manifest.json) - -Code in chrome_browser calls an XML parsing library function, which generates a -random instance ID to -[isolate unrelated decode operations](https://cs.chromium.org/chromium/src/services/data_decoder/public/cpp/safe_xml_parser.cc?l=50). - -#### can\_create\_other\_service\_instances (bool) - -This option allows a service to register arbitrary new service instances it -creates on its own. - -**Note:** this privilege must only be granted to services that are trusted -at least at the same level as the Service Manager itself. - -Example: -[content_browser](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json) - -The browser manages render processes, and thus needs this privilege to manage -the content_renderer instances on behalf of the service manager. - -### interface\_provider\_specs (dictionary) - -The interface provider spec is a dictionary keyed by interface provider -name, with each value representing the capability spec for that -provider. -Each capability spec defines an optional "provides" key and an optional -"requires" key. - -Every interface provider spec (often exclusively) contains one standard -capability spec named “service_manager:connector”. This is the -capability spec enforced when inter-service connections are made from a -service's `Connector` interface. - -Some other examples of capability specs are things like "navigation:frame", -which enforces capability specs for interfaces retrieved through a -frame's `InterfaceProvider`. - -See [README.md](README.md#service-manifests) for some examples. - -**Note:** Since multiple interface provider support makes the manifest files -harder to understand, there is a plan to simplify this section -(see https://crbug.com/718652 for more details). - -#### provides (dictionary) - -This optional section specifies a set of capabilities provided by the service. -A capability is a set of accessible interfaces. - -For example, suppose we have the following capabilities: - -* useful_capability - * useful\_interface\_1 - * useful\_interface\_2 -* better\_capability - * better\_interface - -The `provides` section would be: -``` json - "provides": { - "useful_capability": [ - "useful_interface_1", - "useful_interface_2" ], - "better_capability": [ - "better_interface" ], - } -``` - -#### requires (dictionary) - -This optional section is also a dictionary, keyed by remote service -names (the service name must match the "name" value in the remote service's -manifest). Each value is a list of capabilities required by this service -from the listed remote service. This section does not name interfaces, -only capabilities. - -For example, if our capability requires service "some_capability" from -service "another_service", the "requires" section will look like this: - -``` json -"requires": { - "another_service": [ "some_capability" ] -``` - -An asterisk is a wildcard which means that any listed capabilities are -required of any service that provides them. For example: - -``` json -"requires": { - "*": [ "some_capability" ] -``` - -In the above example, this service can access any interface provided as part -of a capability named "some_capability" in any service on the system. - -While generally discouraged, there are use cases for wildcards. -Consider building something like a UI shell with a launcher that wants to -tell any service "please launch with some default UI". The services providing -a "launch" capability would be expected to include access to an -"`app_launcher.mojom.Launcher`" interface as part of that capability, with an -implementation that does something useful like open some default UI for the -service: - -``` json -"provides": { - "launch": [ "app_launcher.mojom.Launcher" ] -} -``` - -Then our app launcher service would expect to be able to bind -`app_launcher.mojom.Launcher` in any service that provides that capability: - - -``` json -"requires": { - "*" : [ "launch" ] -} -``` - -### required\_files (dictionary) - -Allows the (sandboxed) service to specify a list of platform-specific files it -needs to access from disk while running. Each file is keyed by an arbitrary -name chosen by the service, and references a file path relative to the Service -Manager embedder (e.g. the Chrome binary) at runtime. - -Files specified here will be opened by the Service Manager before launching a -new instance of this service, and their opened file descriptors will be passed -to the new sandboxed process. The file descriptors may be accessed via -`base::FileDescriptorStore` using the corresponding key string from the -manifest. - -**Note:** This is currently only supported on Android and Linux-based desktop -builds. - -#### path (string) - -Path to the file relative to the executable location at runtime. - -#### platform (string) - -The platform this file is required on. -Possible values: - -* `linux` -* `android`