api_binding_unittest.cc 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/bindings/api_binding.h"
  5. #include <tuple>
  6. #include "base/auto_reset.h"
  7. #include "base/bind.h"
  8. #include "base/callback_helpers.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/test/bind.h"
  11. #include "base/values.h"
  12. #include "build/build_config.h"
  13. #include "build/chromeos_buildflags.h"
  14. #include "extensions/renderer/bindings/api_binding_hooks.h"
  15. #include "extensions/renderer/bindings/api_binding_hooks_test_delegate.h"
  16. #include "extensions/renderer/bindings/api_binding_test.h"
  17. #include "extensions/renderer/bindings/api_binding_test_util.h"
  18. #include "extensions/renderer/bindings/api_binding_types.h"
  19. #include "extensions/renderer/bindings/api_binding_util.h"
  20. #include "extensions/renderer/bindings/api_event_handler.h"
  21. #include "extensions/renderer/bindings/api_invocation_errors.h"
  22. #include "extensions/renderer/bindings/api_request_handler.h"
  23. #include "extensions/renderer/bindings/api_signature.h"
  24. #include "extensions/renderer/bindings/api_type_reference_map.h"
  25. #include "extensions/renderer/bindings/binding_access_checker.h"
  26. #include "extensions/renderer/bindings/exception_handler.h"
  27. #include "extensions/renderer/bindings/test_interaction_provider.h"
  28. #include "extensions/renderer/bindings/test_js_runner.h"
  29. #include "gin/arguments.h"
  30. #include "gin/converter.h"
  31. #include "gin/public/context_holder.h"
  32. #include "testing/gmock/include/gmock/gmock.h"
  33. #include "testing/gtest/include/gtest/gtest.h"
  34. #include "v8/include/v8.h"
  35. namespace extensions {
  36. namespace {
  37. const char kBindingName[] = "test";
  38. // Function spec; we use single quotes for readability and then replace them.
  39. const char kFunctions[] =
  40. "[{"
  41. " 'name': 'oneString',"
  42. " 'parameters': [{"
  43. " 'type': 'string',"
  44. " 'name': 'str'"
  45. " }]"
  46. "}, {"
  47. " 'name': 'stringAndInt',"
  48. " 'parameters': [{"
  49. " 'type': 'string',"
  50. " 'name': 'str'"
  51. " }, {"
  52. " 'type': 'integer',"
  53. " 'name': 'int'"
  54. " }]"
  55. "}, {"
  56. " 'name': 'oneObject',"
  57. " 'parameters': [{"
  58. " 'type': 'object',"
  59. " 'name': 'foo',"
  60. " 'properties': {"
  61. " 'prop1': {'type': 'string'},"
  62. " 'prop2': {'type': 'string', 'optional': true}"
  63. " }"
  64. " }]"
  65. "}, {"
  66. " 'name': 'intAndCallback',"
  67. " 'parameters': [{"
  68. " 'name': 'int',"
  69. " 'type': 'integer'"
  70. " }, {"
  71. " 'name': 'callback',"
  72. " 'type': 'function'"
  73. " }]"
  74. "}]";
  75. constexpr char kFunctionsWithCallbackSignatures[] = R"(
  76. [{
  77. "name": "noCallback",
  78. "parameters": [{
  79. "name": "int",
  80. "type": "integer"
  81. }]
  82. }, {
  83. "name": "intCallback",
  84. "parameters": [{
  85. "name": "callback",
  86. "type": "function",
  87. "parameters": [{
  88. "name": "int",
  89. "type": "integer"
  90. }]
  91. }]
  92. }, {
  93. "name": "noParamCallback",
  94. "parameters": [{
  95. "name": "callback",
  96. "type": "function",
  97. "parameters": []
  98. }]
  99. }])";
  100. constexpr char kFunctionsWithPromiseSignatures[] =
  101. R"([{
  102. "name": "supportsPromises",
  103. "parameters": [{
  104. "name": "int",
  105. "type": "integer"
  106. }],
  107. "returns_async": {
  108. "name": "callback",
  109. "parameters": [{
  110. "name": "strResult",
  111. "type": "string"
  112. }]
  113. }
  114. },
  115. {
  116. "name": "callbackOptional",
  117. "parameters": [{
  118. "name": "int",
  119. "type": "integer"
  120. }],
  121. "returns_async": {
  122. "name": "callback",
  123. "optional": true,
  124. "parameters": [{
  125. "name": "strResult",
  126. "type": "string"
  127. }]
  128. }
  129. }])";
  130. bool AllowAllFeatures(v8::Local<v8::Context> context, const std::string& name) {
  131. return true;
  132. }
  133. bool DisallowPromises(v8::Local<v8::Context> context) {
  134. return false;
  135. }
  136. void OnEventListenersChanged(const std::string& event_name,
  137. binding::EventListenersChanged change,
  138. const base::DictionaryValue* filter,
  139. bool was_manual,
  140. v8::Local<v8::Context> context) {}
  141. } // namespace
  142. class APIBindingUnittest : public APIBindingTest {
  143. public:
  144. APIBindingUnittest(const APIBindingUnittest&) = delete;
  145. APIBindingUnittest& operator=(const APIBindingUnittest&) = delete;
  146. void OnFunctionCall(std::unique_ptr<APIRequestHandler::Request> request,
  147. v8::Local<v8::Context> context) {
  148. last_request_ = std::move(request);
  149. }
  150. using GetParentCallback = base::RepeatingCallback<v8::Local<v8::Object>()>;
  151. v8::Local<v8::Object> GetParent(v8::Local<v8::Context> context,
  152. v8::Local<v8::Object>* secondary_parent) {
  153. DCHECK(!get_last_error_parent_.is_null())
  154. << "You must have get_last_error_parent_ set if a test is dealing with"
  155. "lastError being set";
  156. return get_last_error_parent_.Run();
  157. }
  158. void AddConsoleError(v8::Local<v8::Context> context,
  159. const std::string& error) {
  160. console_errors_.push_back(error);
  161. }
  162. protected:
  163. APIBindingUnittest()
  164. : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {}
  165. void SetUp() override {
  166. APIBindingTest::SetUp();
  167. interaction_provider_ = std::make_unique<TestInteractionProvider>();
  168. binding::AddConsoleError add_console_error(base::BindRepeating(
  169. &APIBindingUnittest::AddConsoleError, base::Unretained(this)));
  170. exception_handler_ = std::make_unique<ExceptionHandler>(add_console_error);
  171. request_handler_ = std::make_unique<APIRequestHandler>(
  172. base::BindRepeating(&APIBindingUnittest::OnFunctionCall,
  173. base::Unretained(this)),
  174. APILastError(base::BindRepeating(&APIBindingUnittest::GetParent,
  175. base::Unretained(this)),
  176. add_console_error),
  177. exception_handler_.get(), interaction_provider_.get());
  178. }
  179. void TearDown() override {
  180. DisposeAllContexts();
  181. access_checker_.reset();
  182. interaction_provider_.reset();
  183. request_handler_.reset();
  184. event_handler_.reset();
  185. binding_.reset();
  186. APIBindingTest::TearDown();
  187. }
  188. void OnWillDisposeContext(v8::Local<v8::Context> context) override {
  189. event_handler_->InvalidateContext(context);
  190. request_handler_->InvalidateContext(context);
  191. }
  192. void SetFunctions(const char* functions) {
  193. binding_functions_ = DeprecatedListValueFromString(functions);
  194. ASSERT_TRUE(binding_functions_);
  195. }
  196. void SetEvents(const char* events) {
  197. binding_events_ = DeprecatedListValueFromString(events);
  198. ASSERT_TRUE(binding_events_);
  199. }
  200. void SetTypes(const char* types) {
  201. binding_types_ = DeprecatedListValueFromString(types);
  202. ASSERT_TRUE(binding_types_);
  203. }
  204. void SetProperties(const char* properties) {
  205. binding_properties_ = DeprecatedDictionaryValueFromString(properties);
  206. ASSERT_TRUE(binding_properties_);
  207. }
  208. void SetHooks(std::unique_ptr<APIBindingHooks> hooks) {
  209. binding_hooks_ = std::move(hooks);
  210. ASSERT_TRUE(binding_hooks_);
  211. }
  212. void SetHooksDelegate(
  213. std::unique_ptr<APIBindingHooksDelegate> hooks_delegate) {
  214. binding_hooks_delegate_ = std::move(hooks_delegate);
  215. ASSERT_TRUE(binding_hooks_delegate_);
  216. }
  217. void SetCreateCustomType(const APIBinding::CreateCustomType& callback) {
  218. create_custom_type_ = callback;
  219. }
  220. void SetOnSilentRequest(const APIBinding::OnSilentRequest& callback) {
  221. on_silent_request_ = callback;
  222. }
  223. void SetAPIAvailabilityCallback(
  224. const BindingAccessChecker::APIAvailabilityCallback& callback) {
  225. api_availability_callback_ = callback;
  226. }
  227. void SetPromiseAvailabilityFlag(bool* availability_flag) {
  228. promise_availability_callback_ = base::BindRepeating(
  229. [](bool* flag, v8::Local<v8::Context> context) { return *flag; },
  230. availability_flag);
  231. }
  232. void SetLastErrorParentCallback(GetParentCallback get_parent) {
  233. get_last_error_parent_ = std::move(get_parent);
  234. }
  235. void ClearConsoleErrors() { console_errors_.clear(); }
  236. void InitializeJSHooks(
  237. const char* register_hook,
  238. v8::Local<v8::Value> additional_arg = v8::Local<v8::Value>()) {
  239. auto hooks =
  240. std::make_unique<APIBindingHooks>(kBindingName, request_handler());
  241. v8::HandleScope handle_scope(isolate());
  242. v8::Local<v8::Context> context = MainContext();
  243. {
  244. v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
  245. v8::Local<v8::Function> function =
  246. FunctionFromString(context, register_hook);
  247. if (!additional_arg.IsEmpty()) {
  248. v8::Local<v8::Value> args[] = {js_hooks, additional_arg};
  249. RunFunctionOnGlobal(function, context, std::size(args), args);
  250. } else {
  251. v8::Local<v8::Value> args[] = {js_hooks};
  252. RunFunctionOnGlobal(function, context, std::size(args), args);
  253. }
  254. }
  255. SetHooks(std::move(hooks));
  256. }
  257. void InitializeBinding() {
  258. if (!binding_hooks_)
  259. binding_hooks_ =
  260. std::make_unique<APIBindingHooks>(kBindingName, request_handler());
  261. if (binding_hooks_delegate_)
  262. binding_hooks_->SetDelegate(std::move(binding_hooks_delegate_));
  263. if (!on_silent_request_)
  264. on_silent_request_ = base::DoNothing();
  265. if (!api_availability_callback_)
  266. api_availability_callback_ = base::BindRepeating(&AllowAllFeatures);
  267. if (!promise_availability_callback_)
  268. promise_availability_callback_ = base::BindRepeating(&DisallowPromises);
  269. auto get_context_owner = [](v8::Local<v8::Context>) {
  270. return std::string("context");
  271. };
  272. event_handler_ = std::make_unique<APIEventHandler>(
  273. base::BindRepeating(&OnEventListenersChanged),
  274. base::BindRepeating(get_context_owner), nullptr);
  275. access_checker_ = std::make_unique<BindingAccessChecker>(
  276. api_availability_callback_, promise_availability_callback_);
  277. binding_ = std::make_unique<APIBinding>(
  278. kBindingName, binding_functions_.get(), binding_types_.get(),
  279. binding_events_.get(), binding_properties_.get(), create_custom_type_,
  280. on_silent_request_, std::move(binding_hooks_), &type_refs_,
  281. request_handler_.get(), event_handler_.get(), access_checker_.get());
  282. EXPECT_EQ(!binding_types_.get(), type_refs_.empty());
  283. }
  284. v8::Local<v8::Value> ExpectPass(
  285. v8::Local<v8::Object> object,
  286. const std::string& script_source,
  287. const std::string& expected_json_arguments_single_quotes,
  288. bool expect_async_handler) {
  289. return ExpectPass(MainContext(), object, script_source,
  290. expected_json_arguments_single_quotes,
  291. expect_async_handler);
  292. }
  293. v8::Local<v8::Value> ExpectPass(
  294. v8::Local<v8::Context> context,
  295. v8::Local<v8::Object> object,
  296. const std::string& script_source,
  297. const std::string& expected_json_arguments_single_quotes,
  298. bool expect_async_handler) {
  299. return RunTest(context, object, script_source, true,
  300. ReplaceSingleQuotes(expected_json_arguments_single_quotes),
  301. expect_async_handler, std::string());
  302. }
  303. void ExpectFailure(v8::Local<v8::Object> object,
  304. const std::string& script_source,
  305. const std::string& expected_error) {
  306. RunTest(MainContext(), object, script_source, false, std::string(), false,
  307. "Uncaught TypeError: " + expected_error);
  308. }
  309. void ExpectThrow(v8::Local<v8::Object> object,
  310. const std::string& script_source,
  311. const std::string& expected_error) {
  312. RunTest(MainContext(), object, script_source, false, std::string(), false,
  313. "Uncaught Error: " + expected_error);
  314. }
  315. bool HandlerWasInvoked() const { return last_request_ != nullptr; }
  316. const APIRequestHandler::Request* last_request() const {
  317. return last_request_.get();
  318. }
  319. void reset_last_request() { last_request_.reset(); }
  320. const std::vector<std::string>& console_errors() const {
  321. return console_errors_;
  322. }
  323. APIBinding* binding() { return binding_.get(); }
  324. APIEventHandler* event_handler() { return event_handler_.get(); }
  325. APIRequestHandler* request_handler() { return request_handler_.get(); }
  326. const APITypeReferenceMap& type_refs() const { return type_refs_; }
  327. private:
  328. v8::Local<v8::Value> RunTest(v8::Local<v8::Context> context,
  329. v8::Local<v8::Object> object,
  330. const std::string& script_source,
  331. bool should_pass,
  332. const std::string& expected_json_arguments,
  333. bool expect_async_handler,
  334. const std::string& expected_error);
  335. std::unique_ptr<APIRequestHandler::Request> last_request_;
  336. std::vector<std::string> console_errors_;
  337. GetParentCallback get_last_error_parent_;
  338. std::unique_ptr<APIBinding> binding_;
  339. std::unique_ptr<APIEventHandler> event_handler_;
  340. std::unique_ptr<TestInteractionProvider> interaction_provider_;
  341. std::unique_ptr<ExceptionHandler> exception_handler_;
  342. std::unique_ptr<APIRequestHandler> request_handler_;
  343. std::unique_ptr<BindingAccessChecker> access_checker_;
  344. APITypeReferenceMap type_refs_;
  345. std::unique_ptr<base::ListValue> binding_functions_;
  346. std::unique_ptr<base::ListValue> binding_events_;
  347. std::unique_ptr<base::ListValue> binding_types_;
  348. std::unique_ptr<base::DictionaryValue> binding_properties_;
  349. std::unique_ptr<APIBindingHooks> binding_hooks_;
  350. std::unique_ptr<APIBindingHooksDelegate> binding_hooks_delegate_;
  351. APIBinding::CreateCustomType create_custom_type_;
  352. APIBinding::OnSilentRequest on_silent_request_;
  353. BindingAccessChecker::APIAvailabilityCallback api_availability_callback_;
  354. BindingAccessChecker::PromiseAvailabilityCallback
  355. promise_availability_callback_;
  356. };
  357. using APIBindingDeathTest = APIBindingUnittest;
  358. v8::Local<v8::Value> APIBindingUnittest::RunTest(
  359. v8::Local<v8::Context> context,
  360. v8::Local<v8::Object> object,
  361. const std::string& script_source,
  362. bool should_pass,
  363. const std::string& expected_json_arguments,
  364. bool expect_async_handler,
  365. const std::string& expected_error) {
  366. EXPECT_FALSE(last_request_);
  367. std::string wrapped_script_source =
  368. base::StringPrintf("(function(obj) { %s })", script_source.c_str());
  369. v8::Local<v8::Function> func =
  370. FunctionFromString(context, wrapped_script_source);
  371. if (func.IsEmpty()) {
  372. ADD_FAILURE() << "Script source couldn't be converted to a function: "
  373. << script_source;
  374. return v8::Local<v8::Value>();
  375. }
  376. v8::Local<v8::Value> argv[] = {object};
  377. v8::Local<v8::Value> result;
  378. if (should_pass) {
  379. result = RunFunction(func, context, 1, argv);
  380. if (!last_request_) {
  381. ADD_FAILURE() << "No request was made. Script source: " << script_source;
  382. return v8::Local<v8::Value>();
  383. }
  384. EXPECT_EQ(expected_json_arguments,
  385. ValueToString(*last_request_->arguments_list));
  386. EXPECT_EQ(expect_async_handler, last_request_->has_async_response_handler)
  387. << script_source;
  388. } else {
  389. RunFunctionAndExpectError(func, context, 1, argv, expected_error);
  390. EXPECT_FALSE(last_request_);
  391. }
  392. last_request_.reset();
  393. return result;
  394. }
  395. TEST_F(APIBindingUnittest, TestEmptyAPI) {
  396. InitializeBinding();
  397. v8::HandleScope handle_scope(isolate());
  398. v8::Local<v8::Context> context = MainContext();
  399. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  400. EXPECT_EQ(
  401. 0u,
  402. binding_object->GetOwnPropertyNames(context).ToLocalChecked()->Length());
  403. }
  404. // Tests the basic call -> request flow of the API binding (ensuring that
  405. // functions are set up correctly and correctly enforced).
  406. TEST_F(APIBindingUnittest, TestBasicAPICalls) {
  407. SetFunctions(kFunctions);
  408. InitializeBinding();
  409. v8::HandleScope handle_scope(isolate());
  410. v8::Local<v8::Context> context = MainContext();
  411. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  412. // Argument parsing is tested primarily in APISignature and ArgumentSpec
  413. // tests, so do a few quick sanity checks...
  414. ExpectPass(binding_object, "obj.oneString('foo');", "['foo']", false);
  415. ExpectFailure(binding_object, "obj.oneString(1);",
  416. api_errors::InvocationError("test.oneString", "string str",
  417. api_errors::NoMatchingSignature()));
  418. ExpectPass(binding_object, "obj.stringAndInt('foo', 1)", "['foo',1]", false);
  419. ExpectFailure(binding_object, "obj.stringAndInt(1)",
  420. api_errors::InvocationError("test.stringAndInt",
  421. "string str, integer int",
  422. api_errors::NoMatchingSignature()));
  423. ExpectPass(binding_object, "obj.intAndCallback(1, function() {})", "[1]",
  424. true);
  425. ExpectFailure(binding_object, "obj.intAndCallback(function() {})",
  426. api_errors::InvocationError("test.intAndCallback",
  427. "integer int, function callback",
  428. api_errors::NoMatchingSignature()));
  429. // ...And an interesting case (throwing an error during parsing).
  430. ExpectThrow(binding_object,
  431. "obj.oneObject({ get prop1() { throw new Error('Badness'); } });",
  432. "Badness");
  433. }
  434. // Test that enum values are properly exposed on the binding object.
  435. TEST_F(APIBindingUnittest, EnumValues) {
  436. const char kTypes[] =
  437. "[{"
  438. " 'id': 'first',"
  439. " 'type': 'string',"
  440. " 'enum': ['alpha', 'camelCase', 'Hyphen-ated',"
  441. " 'SCREAMING', 'nums123', '42nums']"
  442. "}, {"
  443. " 'id': 'last',"
  444. " 'type': 'string',"
  445. " 'enum': [{'name': 'omega'}]"
  446. "}]";
  447. SetTypes(kTypes);
  448. InitializeBinding();
  449. v8::HandleScope handle_scope(isolate());
  450. v8::Local<v8::Context> context = MainContext();
  451. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  452. const char kExpected[] =
  453. "{'ALPHA':'alpha','CAMEL_CASE':'camelCase','HYPHEN_ATED':'Hyphen-ated',"
  454. "'NUMS123':'nums123','SCREAMING':'SCREAMING','_42NUMS':'42nums'}";
  455. EXPECT_EQ(ReplaceSingleQuotes(kExpected),
  456. GetStringPropertyFromObject(binding_object, context, "first"));
  457. EXPECT_EQ(ReplaceSingleQuotes("{'OMEGA':'omega'}"),
  458. GetStringPropertyFromObject(binding_object, context, "last"));
  459. }
  460. // Test that empty enum entries are (unfortunately) allowed.
  461. TEST_F(APIBindingUnittest, EnumWithEmptyEntry) {
  462. const char kTypes[] =
  463. "[{"
  464. " 'id': 'enumWithEmpty',"
  465. " 'type': 'string',"
  466. " 'enum': [{'name': ''}, {'name': 'other'}]"
  467. "}]";
  468. SetTypes(kTypes);
  469. InitializeBinding();
  470. v8::HandleScope handle_scope(isolate());
  471. v8::Local<v8::Context> context = MainContext();
  472. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  473. EXPECT_EQ(
  474. "{\"\":\"\",\"OTHER\":\"other\"}",
  475. GetStringPropertyFromObject(binding_object, context, "enumWithEmpty"));
  476. }
  477. // Test that type references are correctly set up in the API.
  478. TEST_F(APIBindingUnittest, TypeRefsTest) {
  479. const char kTypes[] =
  480. "[{"
  481. " 'id': 'refObj',"
  482. " 'type': 'object',"
  483. " 'properties': {"
  484. " 'prop1': {'type': 'string'},"
  485. " 'prop2': {'type': 'integer', 'optional': true}"
  486. " }"
  487. "}, {"
  488. " 'id': 'refEnum',"
  489. " 'type': 'string',"
  490. " 'enum': ['alpha', 'beta']"
  491. "}]";
  492. const char kRefFunctions[] =
  493. "[{"
  494. " 'name': 'takesRefObj',"
  495. " 'parameters': [{"
  496. " 'name': 'o',"
  497. " '$ref': 'refObj'"
  498. " }]"
  499. "}, {"
  500. " 'name': 'takesRefEnum',"
  501. " 'parameters': [{"
  502. " 'name': 'e',"
  503. " '$ref': 'refEnum'"
  504. " }]"
  505. "}]";
  506. SetFunctions(kRefFunctions);
  507. SetTypes(kTypes);
  508. InitializeBinding();
  509. EXPECT_EQ(2u, type_refs().size());
  510. EXPECT_TRUE(type_refs().GetSpec("refObj"));
  511. EXPECT_TRUE(type_refs().GetSpec("refEnum"));
  512. v8::HandleScope handle_scope(isolate());
  513. v8::Local<v8::Context> context = MainContext();
  514. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  515. // Parsing in general is tested in APISignature and ArgumentSpec tests, but
  516. // we test that the binding a) correctly finds the definitions, and b) accepts
  517. // properties from the API object.
  518. ExpectPass(binding_object, "obj.takesRefObj({prop1: 'foo'})",
  519. "[{'prop1':'foo'}]", false);
  520. ExpectFailure(binding_object, "obj.takesRefObj({prop1: 'foo', prop2: 'a'})",
  521. api_errors::InvocationError(
  522. "test.takesRefObj", "refObj o",
  523. api_errors::ArgumentError(
  524. "o", api_errors::PropertyError(
  525. "prop2", api_errors::InvalidType(
  526. api_errors::kTypeInteger,
  527. api_errors::kTypeString)))));
  528. ExpectPass(binding_object, "obj.takesRefEnum('alpha')", "['alpha']", false);
  529. ExpectPass(binding_object, "obj.takesRefEnum(obj.refEnum.BETA)", "['beta']",
  530. false);
  531. ExpectFailure(binding_object, "obj.takesRefEnum('gamma')",
  532. api_errors::InvocationError(
  533. "test.takesRefEnum", "refEnum e",
  534. api_errors::ArgumentError(
  535. "e", api_errors::InvalidEnumValue({"alpha", "beta"}))));
  536. }
  537. TEST_F(APIBindingUnittest, RestrictedAPIs) {
  538. const char kLocalFunctions[] =
  539. "[{"
  540. " 'name': 'allowedOne',"
  541. " 'parameters': []"
  542. "}, {"
  543. " 'name': 'allowedTwo',"
  544. " 'parameters': []"
  545. "}, {"
  546. " 'name': 'restrictedOne',"
  547. " 'parameters': []"
  548. "}, {"
  549. " 'name': 'restrictedTwo',"
  550. " 'parameters': []"
  551. "}]";
  552. SetFunctions(kLocalFunctions);
  553. const char kEvents[] =
  554. "[{'name': 'allowedEvent'}, {'name': 'restrictedEvent'}]";
  555. SetEvents(kEvents);
  556. const char kProperties[] =
  557. R"({
  558. "allowedProperty": { "type": "integer", "value": 3 },
  559. "restrictedProperty": { "type": "string", "value": "restricted" }
  560. })";
  561. SetProperties(kProperties);
  562. auto is_available = [](v8::Local<v8::Context> context,
  563. const std::string& name) {
  564. std::set<std::string> allowed = {"test.allowedOne", "test.allowedTwo",
  565. "test.allowedEvent",
  566. "test.allowedProperty"};
  567. std::set<std::string> restricted = {
  568. "test.restrictedOne", "test.restrictedTwo", "test.restrictedEvent",
  569. "test.restrictedProperty"};
  570. EXPECT_TRUE(allowed.count(name) || restricted.count(name)) << name;
  571. return allowed.count(name) != 0;
  572. };
  573. SetAPIAvailabilityCallback(base::BindRepeating(is_available));
  574. InitializeBinding();
  575. v8::HandleScope handle_scope(isolate());
  576. v8::Local<v8::Context> context = MainContext();
  577. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  578. auto is_defined = [&binding_object, context](const std::string& name) {
  579. v8::Local<v8::Value> val =
  580. GetPropertyFromObject(binding_object, context, name);
  581. EXPECT_FALSE(val.IsEmpty());
  582. return !val->IsUndefined() && !val->IsNull();
  583. };
  584. EXPECT_TRUE(is_defined("allowedOne"));
  585. EXPECT_TRUE(is_defined("allowedTwo"));
  586. EXPECT_TRUE(is_defined("allowedEvent"));
  587. EXPECT_TRUE(is_defined("allowedProperty"));
  588. EXPECT_FALSE(is_defined("restrictedOne"));
  589. EXPECT_FALSE(is_defined("restrictedTwo"));
  590. EXPECT_FALSE(is_defined("restrictedEvent"));
  591. EXPECT_FALSE(is_defined("restrictedProperty"));
  592. }
  593. // Tests that events specified in the API are created as properties of the API
  594. // object.
  595. TEST_F(APIBindingUnittest, TestEventCreation) {
  596. SetEvents(
  597. R"([
  598. {'name': 'onFoo'},
  599. {'name': 'onBar'},
  600. {'name': 'onBaz', 'options': {'maxListeners': 1}}
  601. ])");
  602. SetFunctions(kFunctions);
  603. InitializeBinding();
  604. v8::HandleScope handle_scope(isolate());
  605. v8::Local<v8::Context> context = MainContext();
  606. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  607. // Event behavior is tested in the APIEventHandler unittests as well as the
  608. // APIBindingsSystem tests, so we really only need to check that the events
  609. // are being initialized on the object.
  610. v8::Maybe<bool> has_on_foo =
  611. binding_object->Has(context, gin::StringToV8(isolate(), "onFoo"));
  612. EXPECT_TRUE(has_on_foo.IsJust());
  613. EXPECT_TRUE(has_on_foo.FromJust());
  614. v8::Maybe<bool> has_on_bar =
  615. binding_object->Has(context, gin::StringToV8(isolate(), "onBar"));
  616. EXPECT_TRUE(has_on_bar.IsJust());
  617. EXPECT_TRUE(has_on_bar.FromJust());
  618. v8::Maybe<bool> has_on_baz =
  619. binding_object->Has(context, gin::StringToV8(isolate(), "onBaz"));
  620. EXPECT_TRUE(has_on_baz.IsJust());
  621. EXPECT_TRUE(has_on_baz.FromJust());
  622. // Test that the maxListeners property is correctly used.
  623. v8::Local<v8::Function> add_listener = FunctionFromString(
  624. context, "(function(e) { e.addListener(function() {}); })");
  625. v8::Local<v8::Value> args[] = {
  626. GetPropertyFromObject(binding_object, context, "onBaz")};
  627. RunFunction(add_listener, context, std::size(args), args);
  628. EXPECT_EQ(1u, event_handler()->GetNumEventListenersForTesting("test.onBaz",
  629. context));
  630. RunFunctionAndExpectError(add_listener, context, std::size(args), args,
  631. "Uncaught TypeError: Too many listeners.");
  632. EXPECT_EQ(1u, event_handler()->GetNumEventListenersForTesting("test.onBaz",
  633. context));
  634. v8::Maybe<bool> has_nonexistent_event = binding_object->Has(
  635. context, gin::StringToV8(isolate(), "onNonexistentEvent"));
  636. EXPECT_TRUE(has_nonexistent_event.IsJust());
  637. EXPECT_FALSE(has_nonexistent_event.FromJust());
  638. }
  639. TEST_F(APIBindingUnittest, TestProperties) {
  640. SetProperties(
  641. "{"
  642. " 'prop1': { 'value': 17, 'type': 'integer' },"
  643. " 'prop2': {"
  644. " 'type': 'object',"
  645. " 'properties': {"
  646. " 'subprop1': { 'value': 'some value', 'type': 'string' },"
  647. " 'subprop2': { 'value': true, 'type': 'boolean' }"
  648. " }"
  649. " },"
  650. " 'linuxOnly': {"
  651. " 'value': 'linux',"
  652. " 'type': 'string',"
  653. " 'platforms': ['linux']"
  654. " },"
  655. " 'lacrosOnly': {"
  656. " 'value': 'lacros',"
  657. " 'type': 'string',"
  658. " 'platforms': ['lacros']"
  659. " },"
  660. " 'notLinuxOrLacros': {"
  661. " 'value': 'nonlinux',"
  662. " 'type': 'string',"
  663. " 'platforms': ['win', 'mac', 'chromeos', 'fuchsia']"
  664. " }"
  665. "}");
  666. InitializeBinding();
  667. v8::HandleScope handle_scope(isolate());
  668. v8::Local<v8::Context> context = MainContext();
  669. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  670. EXPECT_EQ("17",
  671. GetStringPropertyFromObject(binding_object, context, "prop1"));
  672. EXPECT_EQ(R"({"subprop1":"some value","subprop2":true})",
  673. GetStringPropertyFromObject(binding_object, context, "prop2"));
  674. // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
  675. // of lacros-chrome is complete.
  676. #if BUILDFLAG(IS_CHROMEOS_LACROS)
  677. EXPECT_EQ("\"lacros\"",
  678. GetStringPropertyFromObject(binding_object, context, "lacrosOnly"));
  679. EXPECT_EQ("undefined",
  680. GetStringPropertyFromObject(binding_object, context, "linuxOnly"));
  681. EXPECT_EQ("undefined", GetStringPropertyFromObject(binding_object, context,
  682. "notLinuxOrLacros"));
  683. #elif BUILDFLAG(IS_LINUX)
  684. EXPECT_EQ("\"linux\"",
  685. GetStringPropertyFromObject(binding_object, context, "linuxOnly"));
  686. EXPECT_EQ("undefined", GetStringPropertyFromObject(binding_object, context,
  687. "notLinuxOrLacros"));
  688. EXPECT_EQ("undefined",
  689. GetStringPropertyFromObject(binding_object, context, "lacrosOnly"));
  690. #else
  691. EXPECT_EQ("undefined",
  692. GetStringPropertyFromObject(binding_object, context, "linuxOnly"));
  693. EXPECT_EQ("undefined",
  694. GetStringPropertyFromObject(binding_object, context, "lacrosOnly"));
  695. EXPECT_EQ("\"nonlinux\"", GetStringPropertyFromObject(binding_object, context,
  696. "notLinuxOrLacros"));
  697. #endif
  698. }
  699. TEST_F(APIBindingUnittest, TestRefProperties) {
  700. SetProperties(
  701. "{"
  702. " 'alpha': {"
  703. " '$ref': 'AlphaRef',"
  704. " 'value': ['a']"
  705. " },"
  706. " 'beta': {"
  707. " '$ref': 'BetaRef',"
  708. " 'value': ['b']"
  709. " }"
  710. "}");
  711. auto create_custom_type = [](v8::Isolate* isolate,
  712. const std::string& type_name,
  713. const std::string& property_name,
  714. const base::ListValue* property_values) {
  715. v8::Local<v8::Context> context = isolate->GetCurrentContext();
  716. v8::Local<v8::Object> result = v8::Object::New(isolate);
  717. if (type_name == "AlphaRef") {
  718. EXPECT_EQ("alpha", property_name);
  719. EXPECT_EQ("[\"a\"]", ValueToString(*property_values));
  720. result
  721. ->Set(context, gin::StringToSymbol(isolate, "alphaProp"),
  722. gin::StringToV8(isolate, "alphaVal"))
  723. .ToChecked();
  724. } else if (type_name == "BetaRef") {
  725. EXPECT_EQ("beta", property_name);
  726. EXPECT_EQ("[\"b\"]", ValueToString(*property_values));
  727. result
  728. ->Set(context, gin::StringToSymbol(isolate, "betaProp"),
  729. gin::StringToV8(isolate, "betaVal"))
  730. .ToChecked();
  731. } else {
  732. EXPECT_TRUE(false) << type_name;
  733. }
  734. return result;
  735. };
  736. SetCreateCustomType(base::BindRepeating(create_custom_type));
  737. InitializeBinding();
  738. v8::HandleScope handle_scope(isolate());
  739. v8::Local<v8::Context> context = MainContext();
  740. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  741. EXPECT_EQ(R"({"alphaProp":"alphaVal"})",
  742. GetStringPropertyFromObject(binding_object, context, "alpha"));
  743. EXPECT_EQ(
  744. R"({"betaProp":"betaVal"})",
  745. GetStringPropertyFromObject(binding_object, context, "beta"));
  746. }
  747. TEST_F(APIBindingUnittest, TestDisposedContext) {
  748. SetFunctions(kFunctions);
  749. InitializeBinding();
  750. v8::HandleScope handle_scope(isolate());
  751. v8::Local<v8::Context> context = MainContext();
  752. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  753. v8::Local<v8::Function> func =
  754. FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
  755. v8::Local<v8::Value> argv[] = {binding_object};
  756. DisposeContext(context);
  757. RunFunctionAndExpectError(func, context, std::size(argv), argv,
  758. "Uncaught Error: Extension context invalidated.");
  759. EXPECT_FALSE(HandlerWasInvoked());
  760. // This test passes if this does not crash, even under AddressSanitizer
  761. // builds.
  762. }
  763. TEST_F(APIBindingUnittest, TestInvalidatedContext) {
  764. SetFunctions(kFunctions);
  765. InitializeBinding();
  766. v8::HandleScope handle_scope(isolate());
  767. v8::Local<v8::Context> context = MainContext();
  768. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  769. v8::Local<v8::Function> func =
  770. FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
  771. v8::Local<v8::Value> argv[] = {binding_object};
  772. binding::InvalidateContext(context);
  773. RunFunctionAndExpectError(func, context, std::size(argv), argv,
  774. "Uncaught Error: Extension context invalidated.");
  775. EXPECT_FALSE(HandlerWasInvoked());
  776. // This test passes if this does not crash, even under AddressSanitizer
  777. // builds.
  778. }
  779. TEST_F(APIBindingUnittest, MultipleContexts) {
  780. v8::HandleScope handle_scope(isolate());
  781. v8::Local<v8::Context> context_a = MainContext();
  782. v8::Local<v8::Context> context_b = AddContext();
  783. SetFunctions(kFunctions);
  784. InitializeBinding();
  785. v8::Local<v8::Object> binding_object_a = binding()->CreateInstance(context_a);
  786. v8::Local<v8::Object> binding_object_b = binding()->CreateInstance(context_b);
  787. ExpectPass(context_a, binding_object_a, "obj.oneString('foo');", "['foo']",
  788. false);
  789. ExpectPass(context_b, binding_object_b, "obj.oneString('foo');", "['foo']",
  790. false);
  791. DisposeContext(context_b);
  792. ExpectPass(context_a, binding_object_a, "obj.oneString('foo');", "['foo']",
  793. false);
  794. }
  795. // Tests adding custom hooks for an API method.
  796. TEST_F(APIBindingUnittest, TestCustomHooks) {
  797. SetFunctions(kFunctions);
  798. // Register a hook for the test.oneString method.
  799. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  800. bool did_call = false;
  801. auto hook = [](bool* did_call, const APISignature* signature,
  802. v8::Local<v8::Context> context,
  803. std::vector<v8::Local<v8::Value>>* arguments,
  804. const APITypeReferenceMap& ref_map) {
  805. *did_call = true;
  806. APIBindingHooks::RequestResult result(
  807. APIBindingHooks::RequestResult::HANDLED);
  808. if (arguments->size() != 1u) { // ASSERT* messes with the return type.
  809. EXPECT_EQ(1u, arguments->size());
  810. return result;
  811. }
  812. EXPECT_EQ("foo", gin::V8ToString(context->GetIsolate(), arguments->at(0)));
  813. return result;
  814. };
  815. hooks->AddHandler("test.oneString", base::BindRepeating(hook, &did_call));
  816. SetHooksDelegate(std::move(hooks));
  817. InitializeBinding();
  818. v8::HandleScope handle_scope(isolate());
  819. v8::Local<v8::Context> context = MainContext();
  820. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  821. // First try calling the oneString() method, which has a custom hook
  822. // installed.
  823. v8::Local<v8::Function> func =
  824. FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
  825. v8::Local<v8::Value> args[] = {binding_object};
  826. RunFunction(func, context, 1, args);
  827. EXPECT_TRUE(did_call);
  828. // Other methods, like stringAndInt(), should behave normally.
  829. ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
  830. false);
  831. }
  832. TEST_F(APIBindingUnittest, TestJSCustomHook) {
  833. // Register a hook for the test.oneString method.
  834. const char kRegisterHook[] = R"(
  835. (function(hooks) {
  836. hooks.setHandleRequest('oneString', function() {
  837. this.requestArguments = Array.from(arguments);
  838. });
  839. }))";
  840. InitializeJSHooks(kRegisterHook);
  841. SetFunctions(kFunctions);
  842. InitializeBinding();
  843. v8::HandleScope handle_scope(isolate());
  844. v8::Local<v8::Context> context = MainContext();
  845. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  846. // First try calling with an invalid invocation. An error should be raised and
  847. // the hook should never have been called, since the arguments didn't match.
  848. ExpectFailure(binding_object, "obj.oneString(1);",
  849. api_errors::InvocationError("test.oneString", "string str",
  850. api_errors::NoMatchingSignature()));
  851. v8::Local<v8::Value> property =
  852. GetPropertyFromObject(context->Global(), context, "requestArguments");
  853. ASSERT_FALSE(property.IsEmpty());
  854. EXPECT_TRUE(property->IsUndefined());
  855. // Try calling the oneString() method with valid arguments. The hook should
  856. // be called.
  857. v8::Local<v8::Function> func =
  858. FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
  859. v8::Local<v8::Value> args[] = {binding_object};
  860. RunFunction(func, context, 1, args);
  861. EXPECT_EQ("[\"foo\"]", GetStringPropertyFromObject(
  862. context->Global(), context, "requestArguments"));
  863. // Other methods, like stringAndInt(), should behave normally.
  864. ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
  865. false);
  866. }
  867. // Tests the updateArgumentsPreValidate hook.
  868. TEST_F(APIBindingUnittest, TestUpdateArgumentsPreValidate) {
  869. // Register a hook for the test.oneString method.
  870. const char kRegisterHook[] = R"(
  871. (function(hooks) {
  872. hooks.setUpdateArgumentsPreValidate('oneString', function() {
  873. this.requestArguments = Array.from(arguments);
  874. if (this.requestArguments[0] === true)
  875. return ['hooked']
  876. return this.requestArguments
  877. });
  878. }))";
  879. InitializeJSHooks(kRegisterHook);
  880. SetFunctions(kFunctions);
  881. InitializeBinding();
  882. v8::HandleScope handle_scope(isolate());
  883. v8::Local<v8::Context> context = MainContext();
  884. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  885. // Call the method with a hook. Since the hook updates arguments before
  886. // validation, we should be able to pass in invalid arguments and still
  887. // have the hook called.
  888. ExpectFailure(binding_object, "obj.oneString(false);",
  889. api_errors::InvocationError("test.oneString", "string str",
  890. api_errors::NoMatchingSignature()));
  891. EXPECT_EQ("[false]", GetStringPropertyFromObject(
  892. context->Global(), context, "requestArguments"));
  893. ExpectPass(binding_object, "obj.oneString(true);", "['hooked']", false);
  894. EXPECT_EQ("[true]", GetStringPropertyFromObject(
  895. context->Global(), context, "requestArguments"));
  896. // Other methods, like stringAndInt(), should behave normally.
  897. ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
  898. false);
  899. }
  900. // Tests the updateArgumentsPreValidate hook.
  901. TEST_F(APIBindingUnittest, TestThrowInUpdateArgumentsPreValidate) {
  902. // Register a hook for the test.oneString method.
  903. const char kRegisterHook[] = R"(
  904. (function(hooks) {
  905. hooks.setUpdateArgumentsPreValidate('oneString', function() {
  906. throw new Error('Custom Hook Error');
  907. });
  908. }))";
  909. InitializeJSHooks(kRegisterHook);
  910. SetFunctions(kFunctions);
  911. InitializeBinding();
  912. v8::HandleScope handle_scope(isolate());
  913. v8::Local<v8::Context> context = MainContext();
  914. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  915. v8::Local<v8::Function> function =
  916. FunctionFromString(context,
  917. "(function(obj) { return obj.oneString('ping'); })");
  918. v8::Local<v8::Value> args[] = {binding_object};
  919. {
  920. TestJSRunner::AllowErrors allow_errors;
  921. RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
  922. std::size(args), args,
  923. "Uncaught Error: Custom Hook Error");
  924. }
  925. // Other methods, like stringAndInt(), should behave normally.
  926. ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]",
  927. false);
  928. }
  929. // Tests that custom JS hooks can return results synchronously.
  930. TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) {
  931. // Register a hook for the test.oneString method.
  932. const char kRegisterHook[] = R"(
  933. (function(hooks) {
  934. hooks.setHandleRequest('oneString', str => {
  935. return str + ' pong';
  936. });
  937. }))";
  938. InitializeJSHooks(kRegisterHook);
  939. SetFunctions(kFunctions);
  940. InitializeBinding();
  941. v8::HandleScope handle_scope(isolate());
  942. v8::Local<v8::Context> context = MainContext();
  943. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  944. v8::Local<v8::Function> function =
  945. FunctionFromString(context,
  946. "(function(obj) { return obj.oneString('ping'); })");
  947. v8::Local<v8::Value> args[] = {binding_object};
  948. v8::Local<v8::Value> result =
  949. RunFunction(function, context, std::size(args), args);
  950. ASSERT_FALSE(result.IsEmpty());
  951. std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
  952. ASSERT_TRUE(json_result);
  953. EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
  954. }
  955. // Tests that the setHandleRequest hook can use callbacks and promises.
  956. TEST_F(APIBindingUnittest, TestReturningPromiseFromHandleRequestHook) {
  957. bool context_allows_promises = true;
  958. SetPromiseAvailabilityFlag(&context_allows_promises);
  959. // Register a hook for supportsPromises.
  960. const char kRegisterHook[] = R"(
  961. (function(hooks) {
  962. hooks.setHandleRequest('supportsPromises', (firstArg, callback) => {
  963. this.firstArgument = firstArg;
  964. this.secondArgument = callback;
  965. });
  966. }))";
  967. InitializeJSHooks(kRegisterHook);
  968. SetFunctions(kFunctionsWithPromiseSignatures);
  969. InitializeBinding();
  970. v8::HandleScope handle_scope(isolate());
  971. v8::Local<v8::Context> context = MainContext();
  972. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  973. {
  974. // Calling supportsPromises normally with a callback should work fine and
  975. // the callback should be invoked immediately.
  976. const char kFunctionCall[] =
  977. R"((function(obj) {
  978. return obj.supportsPromises(5, (arg) => {
  979. this.sentToCallback = arg;
  980. });
  981. }))";
  982. v8::Local<v8::Function> function =
  983. FunctionFromString(context, kFunctionCall);
  984. v8::Local<v8::Value> args[] = {binding_object};
  985. auto result = RunFunction(function, context, v8::Undefined(isolate()),
  986. std::size(args), args);
  987. ASSERT_FALSE(result.IsEmpty());
  988. EXPECT_TRUE(result->IsUndefined());
  989. EXPECT_EQ("5", GetStringPropertyFromObject(context->Global(), context,
  990. "firstArgument"));
  991. v8::Local<v8::Function> resolve_callback;
  992. ASSERT_TRUE(GetPropertyFromObjectAs(context->Global(), context,
  993. "secondArgument", &resolve_callback));
  994. // The callback arg will not be set until the callback has been invoked.
  995. EXPECT_TRUE(
  996. GetPropertyFromObject(context->Global(), context, "sentToCallabck")
  997. ->IsUndefined());
  998. v8::Local<v8::Value> callback_arguments[] = {
  999. gin::StringToV8(isolate(), "foo")};
  1000. RunFunctionOnGlobal(resolve_callback, context,
  1001. std::size(callback_arguments), callback_arguments);
  1002. EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(
  1003. context->Global(), context, "sentToCallback"));
  1004. }
  1005. {
  1006. // Calling supportsPromises normally without the callback should work fine
  1007. // and a promise should be returned that is resolved when the callback is
  1008. // invoked.
  1009. v8::Local<v8::Function> function = FunctionFromString(
  1010. context, "(function(obj) { return obj.supportsPromises(6); })");
  1011. v8::Local<v8::Value> args[] = {binding_object};
  1012. v8::Local<v8::Value> result = RunFunction(
  1013. function, context, v8::Undefined(isolate()), std::size(args), args);
  1014. v8::Local<v8::Promise> promise;
  1015. ASSERT_TRUE(GetValueAs(result, &promise));
  1016. EXPECT_EQ(v8::Promise::kPending, promise->State());
  1017. EXPECT_EQ("6", GetStringPropertyFromObject(context->Global(), context,
  1018. "firstArgument"));
  1019. // Since we trigger the promise to be resolved with a function that calls
  1020. // back into the C++ side, the second argument is actually a function here.
  1021. v8::Local<v8::Function> resolve_callback;
  1022. ASSERT_TRUE(GetPropertyFromObjectAs(context->Global(), context,
  1023. "secondArgument", &resolve_callback));
  1024. // Invoking this callback should result in the promise being resolved.
  1025. v8::Local<v8::Value> callback_arguments[] = {
  1026. gin::StringToV8(isolate(), "bar")};
  1027. RunFunctionOnGlobal(resolve_callback, context,
  1028. std::size(callback_arguments), callback_arguments);
  1029. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  1030. EXPECT_EQ(R"("bar")", V8ToString(promise->Result(), context));
  1031. }
  1032. {
  1033. // If the context doesn't support promises, there should be an error if a
  1034. // required callback isn't supplied.
  1035. context_allows_promises = false;
  1036. v8::Local<v8::Function> function = FunctionFromString(
  1037. context, "(function(obj) { return obj.supportsPromises(7); })");
  1038. v8::Local<v8::Value> args[] = {binding_object};
  1039. auto expected_error =
  1040. "Uncaught TypeError: " +
  1041. api_errors::InvocationError("test.supportsPromises",
  1042. "integer int, function callback",
  1043. api_errors::NoMatchingSignature());
  1044. RunFunctionAndExpectError(function, context, std::size(args), args,
  1045. expected_error);
  1046. }
  1047. }
  1048. // Tests that JS custom hooks can throw exceptions for bad invocations.
  1049. TEST_F(APIBindingUnittest, TestThrowingFromCustomJSHook) {
  1050. // Register a hook for the test.oneString method.
  1051. const char kRegisterHook[] = R"(
  1052. (function(hooks) {
  1053. hooks.setHandleRequest('oneString', str => {
  1054. throw new Error('Custom Hook Error');
  1055. });
  1056. }))";
  1057. InitializeJSHooks(kRegisterHook);
  1058. SetFunctions(kFunctions);
  1059. InitializeBinding();
  1060. v8::HandleScope handle_scope(isolate());
  1061. v8::Local<v8::Context> context = MainContext();
  1062. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1063. v8::Local<v8::Function> function =
  1064. FunctionFromString(context,
  1065. "(function(obj) { return obj.oneString('ping'); })");
  1066. v8::Local<v8::Value> args[] = {binding_object};
  1067. TestJSRunner::AllowErrors allow_errors;
  1068. RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
  1069. std::size(args), args,
  1070. "Uncaught Error: Custom Hook Error");
  1071. }
  1072. // Tests that JS setHandleRequestHooks can use the failure callback to return a
  1073. // failure result for an API.
  1074. TEST_F(APIBindingUnittest, TestHandleRequestFailureCallback) {
  1075. bool context_allows_promises = true;
  1076. SetPromiseAvailabilityFlag(&context_allows_promises);
  1077. // Register a hook for supportsPromises that calls the failure callback when
  1078. // the API is called with the integer 6.
  1079. const char kRegisterHook[] = R"(
  1080. (function(hooks) {
  1081. function handler(firstArg, callback, failureCallback) {
  1082. if (firstArg == 6)
  1083. failureCallback('This is the error');
  1084. else
  1085. callback(firstArg);
  1086. };
  1087. hooks.setHandleRequest('supportsPromises', handler);
  1088. hooks.setHandleRequest('callbackOptional', handler);
  1089. }))";
  1090. InitializeJSHooks(kRegisterHook);
  1091. SetFunctions(kFunctionsWithPromiseSignatures);
  1092. InitializeBinding();
  1093. v8::HandleScope handle_scope(isolate());
  1094. v8::Local<v8::Context> context = MainContext();
  1095. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1096. v8::Local<v8::Object> last_error_parent = v8::Object::New(isolate());
  1097. auto get_last_error_parent = [&last_error_parent]() {
  1098. return last_error_parent;
  1099. };
  1100. SetLastErrorParentCallback(base::BindLambdaForTesting(get_last_error_parent));
  1101. {
  1102. // Calling supportsPromises normally should resolve as expected with no
  1103. // error.
  1104. v8::Local<v8::Function> function = FunctionFromString(
  1105. context, "(function(obj) { return obj.supportsPromises(42); })");
  1106. v8::Local<v8::Value> args[] = {binding_object};
  1107. v8::Local<v8::Value> result = RunFunction(
  1108. function, context, v8::Undefined(isolate()), std::size(args), args);
  1109. v8::Local<v8::Promise> promise;
  1110. ASSERT_TRUE(GetValueAs(result, &promise));
  1111. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  1112. EXPECT_EQ(R"(42)", V8ToString(promise->Result(), context));
  1113. }
  1114. {
  1115. // Calling supportsPromises to trigger the failureCallback should result in
  1116. // the promise being rejected.
  1117. v8::Local<v8::Function> function = FunctionFromString(
  1118. context, "(function(obj) { return obj.supportsPromises(6); })");
  1119. v8::Local<v8::Value> args[] = {binding_object};
  1120. v8::Local<v8::Value> result = RunFunction(
  1121. function, context, v8::Undefined(isolate()), std::size(args), args);
  1122. v8::Local<v8::Promise> promise;
  1123. ASSERT_TRUE(GetValueAs(result, &promise));
  1124. EXPECT_EQ(v8::Promise::kRejected, promise->State());
  1125. ASSERT_TRUE(promise->Result()->IsObject());
  1126. EXPECT_EQ(R"("This is the error")",
  1127. GetStringPropertyFromObject(promise->Result().As<v8::Object>(),
  1128. context, "message"));
  1129. }
  1130. {
  1131. // Calling supportsPromises with a callback and triggering the
  1132. // failureCallback should call the callback with lastError set.
  1133. const char kFunctionCall[] =
  1134. R"((function(obj, lastErrorParent) {
  1135. return obj.supportsPromises(6, (arg) => {
  1136. this.sentToCallback = arg;
  1137. // LastError is only set for the duration of the callback, so set
  1138. // it to a global we retrieve and can check later.
  1139. this.lastError = lastErrorParent.lastError;
  1140. });
  1141. }))";
  1142. v8::Local<v8::Function> function =
  1143. FunctionFromString(context, kFunctionCall);
  1144. v8::Local<v8::Value> args[] = {binding_object, last_error_parent};
  1145. RunFunction(function, context, v8::Undefined(isolate()), std::size(args),
  1146. args);
  1147. // In the case of errors, callbacks are not passed any arguments.
  1148. EXPECT_TRUE(
  1149. GetPropertyFromObject(context->Global(), context, "sentToCallabck")
  1150. ->IsUndefined());
  1151. v8::Local<v8::Object> last_error;
  1152. ASSERT_TRUE(GetPropertyFromObjectAs(context->Global(), context, "lastError",
  1153. &last_error));
  1154. EXPECT_EQ(R"("This is the error")",
  1155. GetStringPropertyFromObject(last_error, context, "message"));
  1156. }
  1157. // Set the context to not support promises for the following test cases.
  1158. context_allows_promises = false;
  1159. {
  1160. // Calling callbackOptional without a callback and triggering the
  1161. // failureCallback in a context that does not support promises should result
  1162. // in a console error about an unchecked last error.
  1163. const char kFunctionCall[] =
  1164. R"((function(obj) {
  1165. return obj.callbackOptional(6);
  1166. }))";
  1167. v8::Local<v8::Function> function =
  1168. FunctionFromString(context, kFunctionCall);
  1169. v8::Local<v8::Value> args[] = {binding_object, last_error_parent};
  1170. RunFunction(function, context, v8::Undefined(isolate()), std::size(args),
  1171. args);
  1172. ASSERT_EQ(1u, console_errors().size());
  1173. EXPECT_THAT(console_errors()[0],
  1174. "Unchecked runtime.lastError: This is the error");
  1175. // Clear the console errors in case any other test case uses them.
  1176. ClearConsoleErrors();
  1177. }
  1178. }
  1179. // Tests that a JS handle request hook that calls the resolver callback more
  1180. // than once will cause a crash on a DCHECK build, but fail gracefully on a
  1181. // release build. Regression test for https://crbug.com/1298409.
  1182. TEST_F(APIBindingDeathTest, TestHandleRequestFailureCallback) {
  1183. bool context_allows_promises = true;
  1184. SetPromiseAvailabilityFlag(&context_allows_promises);
  1185. // Register a hook for supportsPromises that calls the success callback twice.
  1186. static const char* const kRegisterHook = R"(
  1187. (function(hooks) {
  1188. function handler(firstArg, callback, failureCallback) {
  1189. callback(firstArg);
  1190. // Calling the callback to resolve the request a second time is
  1191. // something our custom hooks shouldn't be doing, but this test
  1192. // intentionally does it to verify behavior if it does happen by
  1193. // accident.
  1194. callback(firstArg);
  1195. };
  1196. hooks.setHandleRequest('supportsPromises', handler);
  1197. }))";
  1198. InitializeJSHooks(kRegisterHook);
  1199. SetFunctions(kFunctionsWithPromiseSignatures);
  1200. InitializeBinding();
  1201. v8::HandleScope handle_scope(isolate());
  1202. v8::Local<v8::Context> context = MainContext();
  1203. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1204. v8::Local<v8::Function> function = FunctionFromString(
  1205. context, "(function(obj) { return obj.supportsPromises(42); })");
  1206. v8::Local<v8::Value> args[] = {binding_object};
  1207. // Calling supportsPromises will trigger the HandleRequest hook which attempts
  1208. // to resolve the request twice by calling the success callback twice. This
  1209. // should cause a crash if DCHECKs are on, but otherwise should gracefully
  1210. // fail without a crash and still result in the request resolving as expected.
  1211. #if DCHECK_IS_ON()
  1212. EXPECT_DEATH(
  1213. {
  1214. RunFunction(function, context, v8::Undefined(isolate()),
  1215. std::size(args), args);
  1216. },
  1217. "Check failed: false. No callback found for the specified request ID.");
  1218. #else
  1219. v8::Local<v8::Value> result = RunFunction(
  1220. function, context, v8::Undefined(isolate()), std::size(args), args);
  1221. v8::Local<v8::Promise> promise;
  1222. ASSERT_TRUE(GetValueAs(result, &promise));
  1223. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  1224. EXPECT_EQ(R"(42)", V8ToString(promise->Result(), context));
  1225. #endif
  1226. }
  1227. // Tests that JS custom hooks correctly handle the context being invalidated.
  1228. // Regression test for https://crbug.com/944014.
  1229. TEST_F(APIBindingUnittest, TestInvalidatingInCustomHook) {
  1230. v8::HandleScope handle_scope(isolate());
  1231. v8::Local<v8::Context> context = MainContext();
  1232. auto context_invalidator =
  1233. [](const v8::FunctionCallbackInfo<v8::Value>& info) {
  1234. gin::Arguments arguments(info);
  1235. binding::InvalidateContext(arguments.GetHolderCreationContext());
  1236. };
  1237. v8::Local<v8::Function> v8_context_invalidator =
  1238. v8::Function::New(context, context_invalidator).ToLocalChecked();
  1239. // Register two hooks. Since the context is invalidated in the first, the
  1240. // second should never run.
  1241. const char kRegisterHook[] = R"(
  1242. (function(hooks, contextInvalidator) {
  1243. hooks.setUpdateArgumentsPreValidate('oneString', () => {
  1244. contextInvalidator();
  1245. return ['foo'];
  1246. });
  1247. hooks.setHandleRequest('oneString', () => {
  1248. this.ranHandleHook = true;
  1249. });
  1250. }))";
  1251. InitializeJSHooks(kRegisterHook, v8_context_invalidator);
  1252. SetFunctions(kFunctions);
  1253. InitializeBinding();
  1254. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1255. v8::Local<v8::Function> function = FunctionFromString(
  1256. context, "(function(obj) { return obj.oneString('ping'); })");
  1257. v8::Local<v8::Value> args[] = {binding_object};
  1258. RunFunction(function, context, v8::Undefined(isolate()), std::size(args),
  1259. args);
  1260. // The context should be properly invalidated, and the second hook (which
  1261. // sets "ranHandleHook") shouldn't have ran.
  1262. EXPECT_FALSE(binding::IsContextValid(context));
  1263. EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), context,
  1264. "ranHandleHook"));
  1265. }
  1266. // Tests that native custom hooks can return results synchronously, or throw
  1267. // exceptions for bad invocations.
  1268. TEST_F(APIBindingUnittest,
  1269. TestReturningResultAndThrowingExceptionFromCustomNativeHook) {
  1270. v8::HandleScope handle_scope(isolate());
  1271. v8::Local<v8::Context> context = MainContext();
  1272. // Register a hook for the test.oneString method.
  1273. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1274. bool did_call = false;
  1275. auto hook = [](bool* did_call, const APISignature* signature,
  1276. v8::Local<v8::Context> context,
  1277. std::vector<v8::Local<v8::Value>>* arguments,
  1278. const APITypeReferenceMap& ref_map) {
  1279. APIBindingHooks::RequestResult result(
  1280. APIBindingHooks::RequestResult::HANDLED);
  1281. if (arguments->size() != 1u) { // ASSERT* messes with the return type.
  1282. EXPECT_EQ(1u, arguments->size());
  1283. return result;
  1284. }
  1285. v8::Isolate* isolate = context->GetIsolate();
  1286. std::string arg_value = gin::V8ToString(isolate, arguments->at(0));
  1287. if (arg_value == "throw") {
  1288. isolate->ThrowException(v8::Exception::Error(
  1289. gin::StringToV8(isolate, "Custom Hook Error")));
  1290. result.code = APIBindingHooks::RequestResult::THROWN;
  1291. return result;
  1292. }
  1293. result.return_value =
  1294. gin::StringToV8(context->GetIsolate(), arg_value + " pong");
  1295. return result;
  1296. };
  1297. hooks->AddHandler("test.oneString", base::BindRepeating(hook, &did_call));
  1298. SetHooksDelegate(std::move(hooks));
  1299. SetFunctions(kFunctions);
  1300. InitializeBinding();
  1301. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1302. {
  1303. // Test an invocation that we expect to throw an exception.
  1304. v8::Local<v8::Function> function =
  1305. FunctionFromString(
  1306. context, "(function(obj) { return obj.oneString('throw'); })");
  1307. v8::Local<v8::Value> args[] = {binding_object};
  1308. RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
  1309. std::size(args), args,
  1310. "Uncaught Error: Custom Hook Error");
  1311. }
  1312. {
  1313. // Test an invocation we expect to succeed.
  1314. v8::Local<v8::Function> function =
  1315. FunctionFromString(context,
  1316. "(function(obj) { return obj.oneString('ping'); })");
  1317. v8::Local<v8::Value> args[] = {binding_object};
  1318. v8::Local<v8::Value> result =
  1319. RunFunction(function, context, std::size(args), args);
  1320. ASSERT_FALSE(result.IsEmpty());
  1321. std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
  1322. ASSERT_TRUE(json_result);
  1323. EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
  1324. }
  1325. }
  1326. // Tests the updateArgumentsPostValidate hook.
  1327. TEST_F(APIBindingUnittest, TestUpdateArgumentsPostValidate) {
  1328. // Register a hook for the test.oneString method.
  1329. const char kRegisterHook[] = R"(
  1330. (function(hooks) {
  1331. hooks.setUpdateArgumentsPostValidate('oneString', function() {
  1332. this.requestArguments = Array.from(arguments);
  1333. return ['pong'];
  1334. });
  1335. }))";
  1336. InitializeJSHooks(kRegisterHook);
  1337. SetFunctions(kFunctions);
  1338. InitializeBinding();
  1339. v8::HandleScope handle_scope(isolate());
  1340. v8::Local<v8::Context> context = MainContext();
  1341. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1342. // Try calling the method with an invalid signature. Since it's invalid, we
  1343. // should never enter the hook.
  1344. ExpectFailure(binding_object, "obj.oneString(false);",
  1345. api_errors::InvocationError("test.oneString", "string str",
  1346. api_errors::NoMatchingSignature()));
  1347. EXPECT_EQ("undefined", GetStringPropertyFromObject(
  1348. context->Global(), context, "requestArguments"));
  1349. // Call the method with a valid signature. The hook should be entered and
  1350. // manipulate the arguments.
  1351. ExpectPass(binding_object, "obj.oneString('ping');", "['pong']", false);
  1352. EXPECT_EQ("[\"ping\"]", GetStringPropertyFromObject(
  1353. context->Global(), context, "requestArguments"));
  1354. // Other methods, like stringAndInt(), should behave normally.
  1355. ExpectPass(binding_object, "obj.stringAndInt('foo', 42);",
  1356. "['foo',42]", false);
  1357. }
  1358. // Tests using setUpdateArgumentsPostValidate to return a list of arguments
  1359. // that violates the function schema. Sadly, this should succeed. :(
  1360. // See comment in api_binding.cc.
  1361. TEST_F(APIBindingUnittest, TestUpdateArgumentsPostValidateViolatingSchema) {
  1362. // Register a hook for the test.oneString method.
  1363. const char kRegisterHook[] = R"(
  1364. (function(hooks) {
  1365. hooks.setUpdateArgumentsPostValidate('oneString', function() {
  1366. return [{}];
  1367. });
  1368. }))";
  1369. InitializeJSHooks(kRegisterHook);
  1370. SetFunctions(kFunctions);
  1371. InitializeBinding();
  1372. v8::HandleScope handle_scope(isolate());
  1373. v8::Local<v8::Context> context = MainContext();
  1374. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1375. // Call the method with a valid signature. The hook should be entered and
  1376. // manipulate the arguments.
  1377. ExpectPass(binding_object, "obj.oneString('ping');", "[{}]", false);
  1378. }
  1379. // Test that user gestures are properly recorded when calling APIs.
  1380. TEST_F(APIBindingUnittest, TestUserGestures) {
  1381. SetFunctions(kFunctions);
  1382. InitializeBinding();
  1383. v8::HandleScope handle_scope(isolate());
  1384. v8::Local<v8::Context> context = MainContext();
  1385. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1386. v8::Local<v8::Function> function =
  1387. FunctionFromString(context, "(function(obj) { obj.oneString('foo');})");
  1388. ASSERT_FALSE(function.IsEmpty());
  1389. v8::Local<v8::Value> argv[] = {binding_object};
  1390. RunFunction(function, context, std::size(argv), argv);
  1391. ASSERT_TRUE(last_request());
  1392. EXPECT_FALSE(last_request()->has_user_gesture);
  1393. reset_last_request();
  1394. ScopedTestUserActivation test_user_activation;
  1395. RunFunction(function, context, std::size(argv), argv);
  1396. ASSERT_TRUE(last_request());
  1397. EXPECT_TRUE(last_request()->has_user_gesture);
  1398. reset_last_request();
  1399. }
  1400. TEST_F(APIBindingUnittest, FilteredEvents) {
  1401. const char kEvents[] =
  1402. "[{"
  1403. " 'name': 'unfilteredOne',"
  1404. " 'parameters': []"
  1405. "}, {"
  1406. " 'name': 'unfilteredTwo',"
  1407. " 'filters': [],"
  1408. " 'parameters': []"
  1409. "}, {"
  1410. " 'name': 'unfilteredThree',"
  1411. " 'options': {'supportsFilters': false},"
  1412. " 'parameters': []"
  1413. "}, {"
  1414. " 'name': 'filteredOne',"
  1415. " 'options': {'supportsFilters': true},"
  1416. " 'parameters': []"
  1417. "}, {"
  1418. " 'name': 'filteredTwo',"
  1419. " 'filters': ["
  1420. " {'name': 'url', 'type': 'array', 'items': {'type': 'any'}}"
  1421. " ],"
  1422. " 'parameters': []"
  1423. "}]";
  1424. SetEvents(kEvents);
  1425. InitializeBinding();
  1426. v8::HandleScope handle_scope(isolate());
  1427. v8::Local<v8::Context> context = MainContext();
  1428. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1429. const char kAddFilteredListener[] =
  1430. "(function(evt) {\n"
  1431. " evt.addListener(function() {},\n"
  1432. " {url: [{pathContains: 'simple2.html'}]});\n"
  1433. "})";
  1434. v8::Local<v8::Function> function =
  1435. FunctionFromString(context, kAddFilteredListener);
  1436. ASSERT_FALSE(function.IsEmpty());
  1437. auto check_supports_filters = [context, binding_object, function](
  1438. base::StringPiece name,
  1439. bool expect_supports) {
  1440. SCOPED_TRACE(name);
  1441. v8::Local<v8::Value> event =
  1442. GetPropertyFromObject(binding_object, context, name);
  1443. v8::Local<v8::Value> args[] = {event};
  1444. if (expect_supports) {
  1445. RunFunction(function, context, context->Global(), std::size(args), args);
  1446. } else {
  1447. RunFunctionAndExpectError(
  1448. function, context, context->Global(), std::size(args), args,
  1449. "Uncaught TypeError: This event does not support filters");
  1450. }
  1451. };
  1452. check_supports_filters("unfilteredOne", false);
  1453. check_supports_filters("unfilteredTwo", false);
  1454. check_supports_filters("unfilteredThree", false);
  1455. check_supports_filters("filteredOne", true);
  1456. check_supports_filters("filteredTwo", true);
  1457. }
  1458. TEST_F(APIBindingUnittest, HooksTemplateInitializer) {
  1459. SetFunctions(kFunctions);
  1460. // Register a hook for the test.oneString method.
  1461. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1462. auto hook = [](v8::Isolate* isolate,
  1463. v8::Local<v8::ObjectTemplate> object_template,
  1464. const APITypeReferenceMap& type_refs) {
  1465. object_template->Set(gin::StringToSymbol(isolate, "hookedProperty"),
  1466. gin::ConvertToV8(isolate, 42));
  1467. };
  1468. hooks->SetTemplateInitializer(base::BindRepeating(hook));
  1469. SetHooksDelegate(std::move(hooks));
  1470. InitializeBinding();
  1471. v8::HandleScope handle_scope(isolate());
  1472. v8::Local<v8::Context> context = MainContext();
  1473. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1474. // The extra property should be present on the binding object.
  1475. EXPECT_EQ("42", GetStringPropertyFromObject(binding_object, context,
  1476. "hookedProperty"));
  1477. // Sanity check: other values should still be there.
  1478. EXPECT_EQ("function",
  1479. GetStringPropertyFromObject(binding_object, context, "oneString"));
  1480. }
  1481. TEST_F(APIBindingUnittest, HooksInstanceInitializer) {
  1482. SetFunctions(kFunctions);
  1483. static constexpr char kHookedProperty[] = "hookedProperty";
  1484. // Register a hook for the test.oneString method.
  1485. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1486. int count = 0;
  1487. auto hook = [](int* count, v8::Local<v8::Context> context,
  1488. v8::Local<v8::Object> object) {
  1489. v8::Isolate* isolate = context->GetIsolate();
  1490. // Add a new property only for the first instance.
  1491. if ((*count)++ == 0) {
  1492. object
  1493. ->Set(context, gin::StringToSymbol(isolate, kHookedProperty),
  1494. gin::ConvertToV8(isolate, 42))
  1495. .ToChecked();
  1496. }
  1497. };
  1498. hooks->SetInstanceInitializer(base::BindRepeating(hook, &count));
  1499. SetHooksDelegate(std::move(hooks));
  1500. InitializeBinding();
  1501. v8::HandleScope handle_scope(isolate());
  1502. // Create two instances.
  1503. v8::Local<v8::Context> context1 = MainContext();
  1504. v8::Local<v8::Object> binding_object1 = binding()->CreateInstance(context1);
  1505. v8::Local<v8::Context> context2 = AddContext();
  1506. v8::Local<v8::Object> binding_object2 = binding()->CreateInstance(context2);
  1507. // We should have run the hooks twice (once per instance).
  1508. EXPECT_EQ(2, count);
  1509. // The extra property should be present on the first binding object, but not
  1510. // the second.
  1511. EXPECT_EQ("42", GetStringPropertyFromObject(binding_object1, context1,
  1512. kHookedProperty));
  1513. EXPECT_EQ("undefined", GetStringPropertyFromObject(binding_object2, context2,
  1514. kHookedProperty));
  1515. // Sanity check: other values should still be there.
  1516. EXPECT_EQ("function", GetStringPropertyFromObject(binding_object1, context1,
  1517. "oneString"));
  1518. EXPECT_EQ("function", GetStringPropertyFromObject(binding_object2, context1,
  1519. "oneString"));
  1520. }
  1521. // Test that running hooks returning different results correctly sends requests
  1522. // or notifies of silent requests.
  1523. TEST_F(APIBindingUnittest, TestSendingRequestsAndSilentRequestsWithHooks) {
  1524. SetFunctions(
  1525. "[{"
  1526. " 'name': 'modifyArgs',"
  1527. " 'parameters': []"
  1528. "}, {"
  1529. " 'name': 'invalidInvocation',"
  1530. " 'parameters': []"
  1531. "}, {"
  1532. " 'name': 'throwException',"
  1533. " 'parameters': []"
  1534. "}, {"
  1535. " 'name': 'dontHandle',"
  1536. " 'parameters': []"
  1537. "}, {"
  1538. " 'name': 'handle',"
  1539. " 'parameters': []"
  1540. "}, {"
  1541. " 'name': 'handleAndSendRequest',"
  1542. " 'parameters': []"
  1543. "}, {"
  1544. " 'name': 'handleWithArgs',"
  1545. " 'parameters': [{"
  1546. " 'name': 'first',"
  1547. " 'type': 'string'"
  1548. " }, {"
  1549. " 'name': 'second',"
  1550. " 'type': 'integer'"
  1551. " }]"
  1552. "}]");
  1553. using RequestResult = APIBindingHooks::RequestResult;
  1554. auto basic_handler = [](RequestResult::ResultCode code, const APISignature*,
  1555. v8::Local<v8::Context> context,
  1556. std::vector<v8::Local<v8::Value>>* arguments,
  1557. const APITypeReferenceMap& map) {
  1558. return RequestResult(code);
  1559. };
  1560. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1561. hooks->AddHandler(
  1562. "test.modifyArgs",
  1563. base::BindRepeating(basic_handler, RequestResult::ARGUMENTS_UPDATED));
  1564. hooks->AddHandler(
  1565. "test.invalidInvocation",
  1566. base::BindRepeating(basic_handler, RequestResult::INVALID_INVOCATION));
  1567. hooks->AddHandler(
  1568. "test.dontHandle",
  1569. base::BindRepeating(basic_handler, RequestResult::NOT_HANDLED));
  1570. hooks->AddHandler("test.handle",
  1571. base::BindRepeating(basic_handler, RequestResult::HANDLED));
  1572. hooks->AddHandler(
  1573. "test.throwException",
  1574. base::BindRepeating([](const APISignature*,
  1575. v8::Local<v8::Context> context,
  1576. std::vector<v8::Local<v8::Value>>* arguments,
  1577. const APITypeReferenceMap& map) {
  1578. context->GetIsolate()->ThrowException(
  1579. gin::StringToV8(context->GetIsolate(), "some error"));
  1580. return RequestResult(RequestResult::THROWN);
  1581. }));
  1582. hooks->AddHandler(
  1583. "test.handleWithArgs",
  1584. base::BindRepeating([](const APISignature*,
  1585. v8::Local<v8::Context> context,
  1586. std::vector<v8::Local<v8::Value>>* arguments,
  1587. const APITypeReferenceMap& map) {
  1588. arguments->push_back(v8::Integer::New(context->GetIsolate(), 42));
  1589. return RequestResult(RequestResult::HANDLED);
  1590. }));
  1591. auto handle_and_send_request =
  1592. [](APIRequestHandler* handler, const APISignature*,
  1593. v8::Local<v8::Context> context,
  1594. std::vector<v8::Local<v8::Value>>* arguments,
  1595. const APITypeReferenceMap& map) {
  1596. handler->StartRequest(
  1597. context, "test.handleAndSendRequest",
  1598. std::make_unique<base::ListValue>(),
  1599. binding::AsyncResponseType::kNone, v8::Local<v8::Function>(),
  1600. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  1601. return RequestResult(RequestResult::HANDLED);
  1602. };
  1603. hooks->AddHandler(
  1604. "test.handleAndSendRequest",
  1605. base::BindRepeating(handle_and_send_request, request_handler()));
  1606. SetHooksDelegate(std::move(hooks));
  1607. auto on_silent_request =
  1608. [](absl::optional<std::string>* name_out,
  1609. absl::optional<std::vector<std::string>>* args_out,
  1610. v8::Local<v8::Context> context, const std::string& call_name,
  1611. const std::vector<v8::Local<v8::Value>>& arguments) {
  1612. *name_out = call_name;
  1613. *args_out = std::vector<std::string>();
  1614. (*args_out)->reserve(arguments.size());
  1615. for (const auto& arg : arguments)
  1616. (*args_out)->push_back(V8ToString(arg, context));
  1617. };
  1618. absl::optional<std::string> silent_request;
  1619. absl::optional<std::vector<std::string>> request_arguments;
  1620. SetOnSilentRequest(base::BindRepeating(on_silent_request, &silent_request,
  1621. &request_arguments));
  1622. InitializeBinding();
  1623. v8::HandleScope handle_scope(isolate());
  1624. v8::Local<v8::Context> context = MainContext();
  1625. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1626. auto call_api_method = [binding_object, context](
  1627. base::StringPiece name,
  1628. base::StringPiece string_args) {
  1629. v8::Local<v8::Function> call = FunctionFromString(
  1630. context, base::StringPrintf("(function(binding) { binding.%s(%s); })",
  1631. name.data(), string_args.data()));
  1632. v8::Local<v8::Value> args[] = {binding_object};
  1633. v8::TryCatch try_catch(context->GetIsolate());
  1634. // The throwException call will throw an exception; ignore it.
  1635. std::ignore = call->Call(context, v8::Undefined(context->GetIsolate()),
  1636. std::size(args), args);
  1637. };
  1638. call_api_method("modifyArgs", "");
  1639. ASSERT_TRUE(last_request());
  1640. EXPECT_EQ("test.modifyArgs", last_request()->method_name);
  1641. EXPECT_FALSE(silent_request);
  1642. reset_last_request();
  1643. silent_request.reset();
  1644. request_arguments.reset();
  1645. call_api_method("invalidInvocation", "");
  1646. EXPECT_FALSE(last_request());
  1647. EXPECT_FALSE(silent_request);
  1648. reset_last_request();
  1649. silent_request.reset();
  1650. request_arguments.reset();
  1651. call_api_method("throwException", "");
  1652. EXPECT_FALSE(last_request());
  1653. EXPECT_FALSE(silent_request);
  1654. reset_last_request();
  1655. silent_request.reset();
  1656. request_arguments.reset();
  1657. call_api_method("dontHandle", "");
  1658. ASSERT_TRUE(last_request());
  1659. EXPECT_EQ("test.dontHandle", last_request()->method_name);
  1660. EXPECT_FALSE(silent_request);
  1661. reset_last_request();
  1662. silent_request.reset();
  1663. request_arguments.reset();
  1664. call_api_method("handle", "");
  1665. EXPECT_FALSE(last_request());
  1666. ASSERT_TRUE(silent_request);
  1667. EXPECT_EQ("test.handle", *silent_request);
  1668. ASSERT_TRUE(request_arguments);
  1669. EXPECT_TRUE(request_arguments->empty());
  1670. reset_last_request();
  1671. silent_request.reset();
  1672. request_arguments.reset();
  1673. call_api_method("handleAndSendRequest", "");
  1674. ASSERT_TRUE(last_request());
  1675. EXPECT_EQ("test.handleAndSendRequest", last_request()->method_name);
  1676. EXPECT_FALSE(silent_request);
  1677. reset_last_request();
  1678. silent_request.reset();
  1679. request_arguments.reset();
  1680. call_api_method("handleWithArgs", "'str'");
  1681. EXPECT_FALSE(last_request());
  1682. ASSERT_TRUE(silent_request);
  1683. ASSERT_EQ("test.handleWithArgs", *silent_request);
  1684. ASSERT_TRUE(request_arguments);
  1685. EXPECT_THAT(
  1686. *request_arguments,
  1687. testing::ElementsAre("\"str\"", "42")); // 42 was added by the handler.
  1688. reset_last_request();
  1689. silent_request.reset();
  1690. request_arguments.reset();
  1691. }
  1692. // Test native hooks that don't handle the result, but set a custom callback
  1693. // instead.
  1694. TEST_F(APIBindingUnittest, TestHooksWithCustomCallback) {
  1695. SetFunctions(kFunctions);
  1696. // Register a hook for the test.oneString method.
  1697. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1698. auto hook_with_custom_callback =
  1699. [](const APISignature* signature, v8::Local<v8::Context> context,
  1700. std::vector<v8::Local<v8::Value>>* arguments,
  1701. const APITypeReferenceMap& ref_map) {
  1702. constexpr char kCustomCallback[] =
  1703. "(function() { this.calledCustomCallback = true; })";
  1704. v8::Local<v8::Function> custom_callback =
  1705. FunctionFromString(context, kCustomCallback);
  1706. APIBindingHooks::RequestResult result(
  1707. APIBindingHooks::RequestResult::NOT_HANDLED, custom_callback);
  1708. return result;
  1709. };
  1710. hooks->AddHandler("test.oneString",
  1711. base::BindRepeating(hook_with_custom_callback));
  1712. SetHooksDelegate(std::move(hooks));
  1713. InitializeBinding();
  1714. v8::HandleScope handle_scope(isolate());
  1715. v8::Local<v8::Context> context = MainContext();
  1716. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1717. // First try calling the oneString() method, which has a custom hook
  1718. // installed.
  1719. v8::Local<v8::Function> func =
  1720. FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
  1721. v8::Local<v8::Value> args[] = {binding_object};
  1722. RunFunction(func, context, 1, args);
  1723. ASSERT_TRUE(last_request());
  1724. EXPECT_TRUE(last_request()->has_async_response_handler);
  1725. request_handler()->CompleteRequest(last_request()->request_id,
  1726. base::Value::List(), std::string());
  1727. EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
  1728. "calledCustomCallback"));
  1729. }
  1730. // Test native hooks that don't handle the result, but add a result modifier.
  1731. TEST_F(APIBindingUnittest, TestHooksWithResultModifier) {
  1732. SetFunctions(kFunctionsWithPromiseSignatures);
  1733. bool context_allows_promises = true;
  1734. SetPromiseAvailabilityFlag(&context_allows_promises);
  1735. // Register a hook for the test.supportsPromises method with a result modifier
  1736. // that changes the result when the async response type is callback based.
  1737. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1738. int total_modifier_call_count = 0;
  1739. auto result_modifier =
  1740. [&total_modifier_call_count](
  1741. const std::vector<v8::Local<v8::Value>>& result_args,
  1742. v8::Local<v8::Context> context,
  1743. binding::AsyncResponseType async_type) {
  1744. total_modifier_call_count++;
  1745. if (async_type == binding::AsyncResponseType::kCallback) {
  1746. // For callback based calls change the result to a vector with
  1747. // multiple arguments by appending "bar" to the end.
  1748. std::vector<v8::Local<v8::Value>> new_args{
  1749. result_args[0], gin::StringToV8(context->GetIsolate(), "bar")};
  1750. return new_args;
  1751. }
  1752. return result_args;
  1753. };
  1754. auto hook_with_result_modifier =
  1755. [&result_modifier](const APISignature* signature,
  1756. v8::Local<v8::Context> context,
  1757. std::vector<v8::Local<v8::Value>>* arguments,
  1758. const APITypeReferenceMap& ref_map) {
  1759. APIBindingHooks::RequestResult result(
  1760. APIBindingHooks::RequestResult::NOT_HANDLED,
  1761. v8::Local<v8::Function>(),
  1762. base::BindLambdaForTesting(result_modifier));
  1763. return result;
  1764. };
  1765. hooks->AddHandler("test.supportsPromises",
  1766. base::BindLambdaForTesting(hook_with_result_modifier));
  1767. SetHooksDelegate(std::move(hooks));
  1768. InitializeBinding();
  1769. v8::HandleScope handle_scope(isolate());
  1770. v8::Local<v8::Context> context = MainContext();
  1771. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1772. // A promise-based call should remain unmodified and return as normal.
  1773. {
  1774. v8::Local<v8::Function> promise_api_call = FunctionFromString(
  1775. context, "(function(api) { return api.supportsPromises(1); });");
  1776. v8::Local<v8::Value> args[] = {binding_object};
  1777. v8::Local<v8::Value> api_result =
  1778. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  1779. v8::Local<v8::Promise> promise;
  1780. ASSERT_TRUE(GetValueAs(api_result, &promise));
  1781. EXPECT_EQ(v8::Promise::kPending, promise->State());
  1782. ASSERT_TRUE(last_request());
  1783. request_handler()->CompleteRequest(last_request()->request_id,
  1784. ListValueFromString(R"(["foo"])"),
  1785. std::string());
  1786. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  1787. EXPECT_EQ(R"("foo")", V8ToString(promise->Result(), context));
  1788. EXPECT_EQ(1, total_modifier_call_count);
  1789. }
  1790. // A callback-based call will be modified by the hook and return with multiple
  1791. // parameters.
  1792. {
  1793. constexpr char kFunctionCall[] =
  1794. R"((function(api) {
  1795. api.supportsPromises(2, (normalResult, addedResult) => {
  1796. this.argument1 = normalResult;
  1797. this.argument2 = addedResult;
  1798. });
  1799. }))";
  1800. v8::Local<v8::Function> callback_api_call =
  1801. FunctionFromString(context, kFunctionCall);
  1802. v8::Local<v8::Value> args[] = {binding_object};
  1803. RunFunctionOnGlobal(callback_api_call, context, std::size(args), args);
  1804. ASSERT_TRUE(last_request());
  1805. request_handler()->CompleteRequest(last_request()->request_id,
  1806. ListValueFromString(R"(["foo"])"),
  1807. std::string());
  1808. EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(context->Global(),
  1809. context, "argument1"));
  1810. EXPECT_EQ(R"("bar")", GetStringPropertyFromObject(context->Global(),
  1811. context, "argument2"));
  1812. EXPECT_EQ(2, total_modifier_call_count);
  1813. }
  1814. // A call which results in an error should reject as expected and the result
  1815. // modifier should never be called.
  1816. {
  1817. v8::Local<v8::Function> promise_api_call = FunctionFromString(
  1818. context, "(function(api) { return api.supportsPromises(3) });");
  1819. v8::Local<v8::Value> args[] = {binding_object};
  1820. v8::Local<v8::Value> api_result =
  1821. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  1822. v8::Local<v8::Promise> promise = api_result.As<v8::Promise>();
  1823. ASSERT_FALSE(api_result.IsEmpty());
  1824. EXPECT_EQ(v8::Promise::kPending, promise->State());
  1825. ASSERT_TRUE(last_request());
  1826. request_handler()->CompleteRequest(last_request()->request_id,
  1827. base::Value::List(), "Error message");
  1828. EXPECT_EQ(v8::Promise::kRejected, promise->State());
  1829. ASSERT_TRUE(promise->Result()->IsObject());
  1830. EXPECT_EQ(R"("Error message")",
  1831. GetStringPropertyFromObject(promise->Result().As<v8::Object>(),
  1832. context, "message"));
  1833. // Since the result modifier should have never been called, the total call
  1834. // count should still be the same as in the previous test case.
  1835. EXPECT_EQ(2, total_modifier_call_count);
  1836. }
  1837. }
  1838. // Test native hooks that add a result modifier are compatible with JS hooks
  1839. // which handle the request.
  1840. TEST_F(APIBindingUnittest, TestHooksWithResultModifierAndJSHook) {
  1841. bool context_allows_promises = true;
  1842. SetPromiseAvailabilityFlag(&context_allows_promises);
  1843. // Register a JS hook for supportsPromises.
  1844. const char kRegisterHook[] = R"(
  1845. (function(hooks) {
  1846. hooks.setHandleRequest('supportsPromises', (firstArg, callback) => {
  1847. // Call the callback, appending "-foo" to the argument passed in.
  1848. callback(firstArg + '-foo');
  1849. });
  1850. }))";
  1851. InitializeJSHooks(kRegisterHook);
  1852. SetFunctions(kFunctionsWithPromiseSignatures);
  1853. // Register a native hook for test.supportsPromises with a result modifier
  1854. // that changes the result when the async response type is callback based.
  1855. auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
  1856. auto result_modifier =
  1857. [](const std::vector<v8::Local<v8::Value>>& result_args,
  1858. v8::Local<v8::Context> context,
  1859. binding::AsyncResponseType async_type) {
  1860. if (async_type == binding::AsyncResponseType::kCallback) {
  1861. // For callback based calls change the result to a vector with
  1862. // multiple arguments by appending "bar" to the end.
  1863. std::vector<v8::Local<v8::Value>> new_args{
  1864. result_args[0], gin::StringToV8(context->GetIsolate(), "bar")};
  1865. return new_args;
  1866. }
  1867. return result_args;
  1868. };
  1869. auto hook_with_result_modifier =
  1870. [&result_modifier](const APISignature* signature,
  1871. v8::Local<v8::Context> context,
  1872. std::vector<v8::Local<v8::Value>>* arguments,
  1873. const APITypeReferenceMap& ref_map) {
  1874. APIBindingHooks::RequestResult result(
  1875. APIBindingHooks::RequestResult::NOT_HANDLED,
  1876. v8::Local<v8::Function>(), base::BindOnce(result_modifier));
  1877. return result;
  1878. };
  1879. // Normally handlers are bound using base::BindRepeating, but to bind a lambda
  1880. // with a capture we have to use BindLambdaForTesting.
  1881. hooks->AddHandler("test.supportsPromises",
  1882. base::BindLambdaForTesting(hook_with_result_modifier));
  1883. SetHooksDelegate(std::move(hooks));
  1884. InitializeBinding();
  1885. v8::HandleScope handle_scope(isolate());
  1886. v8::Local<v8::Context> context = MainContext();
  1887. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1888. // A promise-based call should just be modified by the JS hook..
  1889. {
  1890. v8::Local<v8::Function> promise_api_call = FunctionFromString(
  1891. context, "(function(api) { return api.supportsPromises(1); });");
  1892. v8::Local<v8::Value> args[] = {binding_object};
  1893. v8::Local<v8::Value> api_result =
  1894. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  1895. // Since the JS callback completes the request right away, the promise
  1896. // should already be fulfilled without us needing to manually complete the
  1897. // request.
  1898. v8::Local<v8::Promise> promise;
  1899. ASSERT_TRUE(GetValueAs(api_result, &promise));
  1900. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  1901. EXPECT_EQ(R"("1-foo")", V8ToString(promise->Result(), context));
  1902. }
  1903. // A callback-based call will be modified by the native hook to return with
  1904. // multiple parameters, as well as having the first parameter modified by the
  1905. // JS hook.
  1906. {
  1907. constexpr char kFunctionCall[] =
  1908. R"((function(api) {
  1909. api.supportsPromises(2, (normalResult, addedResult) => {
  1910. this.argument1 = normalResult;
  1911. this.argument2 = addedResult;
  1912. });
  1913. }))";
  1914. v8::Local<v8::Function> promise_api_call =
  1915. FunctionFromString(context, kFunctionCall);
  1916. v8::Local<v8::Value> args[] = {binding_object};
  1917. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  1918. EXPECT_EQ(R"("2-foo")", GetStringPropertyFromObject(context->Global(),
  1919. context, "argument1"));
  1920. EXPECT_EQ(R"("bar")", GetStringPropertyFromObject(context->Global(),
  1921. context, "argument2"));
  1922. }
  1923. }
  1924. TEST_F(APIBindingUnittest, AccessAPIMethodsAndEventsAfterInvalidation) {
  1925. SetEvents(R"([{"name": "onFoo"}])");
  1926. InitializeBinding();
  1927. v8::HandleScope handle_scope(isolate());
  1928. v8::Local<v8::Context> context = MainContext();
  1929. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1930. v8::Local<v8::Function> function = FunctionFromString(
  1931. context, "(function(obj) { obj.onFoo.addListener(function() {}); })");
  1932. binding::InvalidateContext(context);
  1933. v8::Local<v8::Value> argv[] = {binding_object};
  1934. RunFunctionAndExpectError(function, context, std::size(argv), argv,
  1935. "Uncaught Error: Extension context invalidated.");
  1936. }
  1937. TEST_F(APIBindingUnittest, CallbackSignaturesAreAdded) {
  1938. std::unique_ptr<base::AutoReset<bool>> response_validation_override =
  1939. binding::SetResponseValidationEnabledForTesting(true);
  1940. SetFunctions(kFunctionsWithCallbackSignatures);
  1941. InitializeBinding();
  1942. {
  1943. const APISignature* signature =
  1944. type_refs().GetAPIMethodSignature("test.noCallback");
  1945. ASSERT_TRUE(signature);
  1946. EXPECT_FALSE(signature->has_async_return());
  1947. EXPECT_FALSE(signature->has_async_return_signature());
  1948. }
  1949. {
  1950. const APISignature* signature =
  1951. type_refs().GetAPIMethodSignature("test.intCallback");
  1952. ASSERT_TRUE(signature);
  1953. EXPECT_TRUE(signature->has_async_return());
  1954. EXPECT_TRUE(signature->has_async_return_signature());
  1955. }
  1956. {
  1957. const APISignature* signature =
  1958. type_refs().GetAPIMethodSignature("test.noParamCallback");
  1959. ASSERT_TRUE(signature);
  1960. EXPECT_TRUE(signature->has_async_return());
  1961. EXPECT_TRUE(signature->has_async_return_signature());
  1962. }
  1963. }
  1964. TEST_F(APIBindingUnittest,
  1965. CallbackSignaturesAreNotAddedWhenValidationDisabled) {
  1966. std::unique_ptr<base::AutoReset<bool>> response_validation_override =
  1967. binding::SetResponseValidationEnabledForTesting(false);
  1968. SetFunctions(kFunctionsWithCallbackSignatures);
  1969. InitializeBinding();
  1970. EXPECT_FALSE(
  1971. type_refs().GetAPIMethodSignature("test.noCallback")->has_async_return());
  1972. EXPECT_TRUE(type_refs()
  1973. .GetAPIMethodSignature("test.intCallback")
  1974. ->has_async_return());
  1975. EXPECT_FALSE(type_refs()
  1976. .GetAPIMethodSignature("test.intCallback")
  1977. ->has_async_return_signature());
  1978. EXPECT_TRUE(type_refs()
  1979. .GetAPIMethodSignature("test.noParamCallback")
  1980. ->has_async_return());
  1981. EXPECT_FALSE(type_refs()
  1982. .GetAPIMethodSignature("test.noParamCallback")
  1983. ->has_async_return_signature());
  1984. }
  1985. // Tests promise-based APIs exposed on bindings.
  1986. TEST_F(APIBindingUnittest, PromiseBasedAPIs) {
  1987. SetFunctions(kFunctionsWithPromiseSignatures);
  1988. // Set a local boolean we can change to simulate if the context supports
  1989. // promises or not.
  1990. bool context_allows_promises = true;
  1991. SetPromiseAvailabilityFlag(&context_allows_promises);
  1992. InitializeBinding();
  1993. v8::HandleScope handle_scope(isolate());
  1994. v8::Local<v8::Context> context = MainContext();
  1995. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  1996. // A normal call into the promised based API should return a promise.
  1997. {
  1998. constexpr char kFunctionCall[] =
  1999. R"((function(api) {
  2000. this.apiResult = api.supportsPromises(3);
  2001. this.apiResult.then((strResult) => {
  2002. this.promiseResult = strResult;
  2003. });
  2004. }))";
  2005. v8::Local<v8::Function> promise_api_call =
  2006. FunctionFromString(context, kFunctionCall);
  2007. v8::Local<v8::Value> args[] = {binding_object};
  2008. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2009. v8::Local<v8::Promise> promise;
  2010. ASSERT_TRUE(GetPropertyFromObjectAs(context->Global(), context, "apiResult",
  2011. &promise));
  2012. EXPECT_EQ(v8::Promise::kPending, promise->State());
  2013. ASSERT_TRUE(last_request());
  2014. request_handler()->CompleteRequest(last_request()->request_id,
  2015. ListValueFromString(R"(["foo"])"),
  2016. std::string());
  2017. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  2018. EXPECT_EQ(R"("foo")", V8ToString(promise->Result(), context));
  2019. EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(
  2020. context->Global(), context, "promiseResult"));
  2021. }
  2022. // Also test that promise-based APIs still support passing a callback.
  2023. {
  2024. constexpr char kFunctionCall[] =
  2025. R"((function(api) {
  2026. api.supportsPromises(3, (strResult) => {
  2027. this.callbackResult = strResult
  2028. });
  2029. }))";
  2030. v8::Local<v8::Function> promise_api_call =
  2031. FunctionFromString(context, kFunctionCall);
  2032. v8::Local<v8::Value> args[] = {binding_object};
  2033. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2034. ASSERT_TRUE(last_request());
  2035. request_handler()->CompleteRequest(last_request()->request_id,
  2036. ListValueFromString(R"(["bar"])"),
  2037. std::string());
  2038. EXPECT_EQ(R"("bar")", GetStringPropertyFromObject(
  2039. context->Global(), context, "callbackResult"));
  2040. }
  2041. // If the context doesn't support promises, there should be an error if a
  2042. // required callback isn't supplied.
  2043. context_allows_promises = false;
  2044. {
  2045. constexpr char kPromiseFunctionCall[] =
  2046. R"((function(api) {
  2047. this.apiResult = api.supportsPromises(3);
  2048. }))";
  2049. v8::Local<v8::Function> promise_api_call =
  2050. FunctionFromString(context, kPromiseFunctionCall);
  2051. v8::Local<v8::Value> args[] = {binding_object};
  2052. auto expected_error =
  2053. "Uncaught TypeError: " +
  2054. api_errors::InvocationError("test.supportsPromises",
  2055. "integer int, function callback",
  2056. api_errors::NoMatchingSignature());
  2057. RunFunctionAndExpectError(promise_api_call, context, std::size(args), args,
  2058. expected_error);
  2059. }
  2060. // Test that required callbacks still work when the context doesn't support
  2061. // promises.
  2062. {
  2063. constexpr char kFunctionCall[] =
  2064. R"((function(api) {
  2065. api.supportsPromises(3, (strResult) => {
  2066. this.callbackResult = strResult
  2067. });
  2068. }))";
  2069. v8::Local<v8::Function> promise_api_call =
  2070. FunctionFromString(context, kFunctionCall);
  2071. v8::Local<v8::Value> args[] = {binding_object};
  2072. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2073. ASSERT_TRUE(last_request());
  2074. request_handler()->CompleteRequest(last_request()->request_id,
  2075. ListValueFromString(R"(["foo"])"),
  2076. std::string());
  2077. EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(
  2078. context->Global(), context, "callbackResult"));
  2079. }
  2080. // If a returns_async field is marked as optional, then a context which
  2081. // doesn't support promises should be able to leave it off of the call.
  2082. {
  2083. constexpr char kCallbackOptionalFunctionCall[] =
  2084. R"((function(api) {
  2085. this.callbackOptionalResult = api.callbackOptional(3);
  2086. }))";
  2087. v8::Local<v8::Function> promise_api_call =
  2088. FunctionFromString(context, kCallbackOptionalFunctionCall);
  2089. v8::Local<v8::Value> args[] = {binding_object};
  2090. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2091. ASSERT_TRUE(last_request());
  2092. v8::Local<v8::Value> api_result = GetPropertyFromObject(
  2093. context->Global(), context, "callbackOptionalResult");
  2094. ASSERT_TRUE(api_result->IsNullOrUndefined());
  2095. }
  2096. }
  2097. TEST_F(APIBindingUnittest, TestPromisesWithJSCustomCallback) {
  2098. // Set a local boolean we can change to simulate if the context supports
  2099. // promises or not.
  2100. bool context_allows_promises = true;
  2101. SetPromiseAvailabilityFlag(&context_allows_promises);
  2102. // Register a custom callback hook for the supportsPromises method.
  2103. const char kRegisterHook[] = R"(
  2104. (function(hooks) {
  2105. hooks.setCustomCallback('supportsPromises',
  2106. (callback, response) => {
  2107. this.response = response;
  2108. this.resolveCallback = callback;
  2109. if (response == 'resolveNow')
  2110. callback('bar');
  2111. });
  2112. }))";
  2113. InitializeJSHooks(kRegisterHook);
  2114. SetFunctions(kFunctionsWithPromiseSignatures);
  2115. InitializeBinding();
  2116. v8::HandleScope handle_scope(isolate());
  2117. v8::Local<v8::Context> context = MainContext();
  2118. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  2119. // A normal call into the promise-based API should return a promise.
  2120. {
  2121. v8::Local<v8::Function> promise_api_call = FunctionFromString(
  2122. context, "(function(api) { return api.supportsPromises(1); });");
  2123. v8::Local<v8::Value> args[] = {binding_object};
  2124. v8::Local<v8::Value> api_result =
  2125. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2126. v8::Local<v8::Promise> promise;
  2127. ASSERT_TRUE(GetValueAs(api_result, &promise));
  2128. EXPECT_EQ(v8::Promise::kPending, promise->State());
  2129. ASSERT_TRUE(last_request());
  2130. request_handler()->CompleteRequest(last_request()->request_id,
  2131. ListValueFromString(R"(["foo"])"),
  2132. std::string());
  2133. // The promise should still be unfulfilled until the callback is invoked.
  2134. EXPECT_EQ(v8::Promise::kPending, promise->State());
  2135. v8::Local<v8::Function> resolve_callback;
  2136. ASSERT_TRUE(GetPropertyFromObjectAs(context->Global(), context,
  2137. "resolveCallback", &resolve_callback));
  2138. v8::Local<v8::Value> callback_arguments[] = {
  2139. GetPropertyFromObject(context->Global(), context, "response")};
  2140. EXPECT_EQ(R"("foo")", V8ToString(callback_arguments[0], context));
  2141. RunFunctionOnGlobal(resolve_callback, context,
  2142. std::size(callback_arguments), callback_arguments);
  2143. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  2144. EXPECT_EQ(R"("foo")", V8ToString(promise->Result(), context));
  2145. }
  2146. // Sending a response to the hook to make it resolve immediately should result
  2147. // in the promise being resolved right after CompleteRequest is called.
  2148. {
  2149. v8::Local<v8::Function> promise_api_call = FunctionFromString(
  2150. context, "(function(api) { return api.supportsPromises(2); });");
  2151. v8::Local<v8::Value> args[] = {binding_object};
  2152. v8::Local<v8::Value> api_result =
  2153. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2154. v8::Local<v8::Promise> promise;
  2155. ASSERT_TRUE(GetValueAs(api_result, &promise));
  2156. EXPECT_EQ(v8::Promise::kPending, promise->State());
  2157. ASSERT_TRUE(last_request());
  2158. request_handler()->CompleteRequest(last_request()->request_id,
  2159. ListValueFromString(R"(["resolveNow"])"),
  2160. std::string());
  2161. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  2162. EXPECT_EQ(R"("bar")", V8ToString(promise->Result(), context));
  2163. }
  2164. // Completing the request with an error should reject the promise with the
  2165. // error.
  2166. {
  2167. v8::Local<v8::Function> promise_api_call = FunctionFromString(
  2168. context, "(function(api) { return api.supportsPromises(3) });");
  2169. v8::Local<v8::Value> args[] = {binding_object};
  2170. v8::Local<v8::Value> api_result =
  2171. RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
  2172. v8::Local<v8::Promise> promise = api_result.As<v8::Promise>();
  2173. ASSERT_FALSE(api_result.IsEmpty());
  2174. EXPECT_EQ(v8::Promise::kPending, promise->State());
  2175. ASSERT_TRUE(last_request());
  2176. request_handler()->CompleteRequest(last_request()->request_id,
  2177. ListValueFromString(R"(["baz"])"),
  2178. "Error message");
  2179. EXPECT_EQ(v8::Promise::kPending, promise->State());
  2180. v8::Local<v8::Value> resolve_callback =
  2181. GetPropertyFromObject(context->Global(), context, "resolveCallback");
  2182. ASSERT_TRUE(resolve_callback->IsFunction());
  2183. RunFunctionOnGlobal(resolve_callback.As<v8::Function>(), context, 0,
  2184. nullptr);
  2185. EXPECT_EQ(v8::Promise::kRejected, promise->State());
  2186. ASSERT_TRUE(promise->Result()->IsObject());
  2187. EXPECT_EQ(R"("Error message")",
  2188. GetStringPropertyFromObject(promise->Result().As<v8::Object>(),
  2189. context, "message"));
  2190. }
  2191. }
  2192. TEST_F(APIBindingUnittest, TestPromiseWithJSUpdateArgumentsPreValidate) {
  2193. bool context_allows_promises = true;
  2194. SetPromiseAvailabilityFlag(&context_allows_promises);
  2195. // Register an update arguments pre validate hook for supportsPromises.
  2196. const char kRegisterHook[] = R"(
  2197. (function(hooks) {
  2198. hooks.setUpdateArgumentsPreValidate('supportsPromises',
  2199. (...arguments) => {
  2200. this.firstArgument = arguments[0];
  2201. this.secondArgument = arguments[1];
  2202. if (arguments[0] == 'hooked')
  2203. arguments[0] = 42;
  2204. return arguments;
  2205. });
  2206. }))";
  2207. InitializeJSHooks(kRegisterHook);
  2208. SetFunctions(kFunctionsWithPromiseSignatures);
  2209. InitializeBinding();
  2210. v8::HandleScope handle_scope(isolate());
  2211. v8::Local<v8::Context> context = MainContext();
  2212. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  2213. {
  2214. // Calling supportsPromises normally with a callback should work fine.
  2215. auto result =
  2216. ExpectPass(binding_object, "return obj.supportsPromises(5, () => {});",
  2217. "[5]", true);
  2218. ASSERT_FALSE(result.IsEmpty());
  2219. EXPECT_TRUE(result->IsUndefined());
  2220. EXPECT_TRUE(
  2221. GetPropertyFromObject(context->Global(), context, "secondArgument")
  2222. ->IsFunction());
  2223. }
  2224. {
  2225. // Calling supportsPromises normally while omitting the callback should work
  2226. // fine.
  2227. auto result = ExpectPass(binding_object, "return obj.supportsPromises(5);",
  2228. "[5]", true);
  2229. EXPECT_TRUE(V8ValueIs<v8::Promise>(result));
  2230. }
  2231. {
  2232. // Calling supportsPromises with a string which we have not set up the
  2233. // custom hook for should cause an error.
  2234. ExpectFailure(binding_object, "obj.supportsPromises('foo');",
  2235. api_errors::InvocationError(
  2236. "test.supportsPromises", "integer int, function callback",
  2237. api_errors::NoMatchingSignature()));
  2238. EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(
  2239. context->Global(), context, "firstArgument"));
  2240. }
  2241. {
  2242. // supportsPromises expects an int, but our custom hook should allow the
  2243. // string 'hooked' to work as well.
  2244. auto result = ExpectPass(
  2245. binding_object, "return obj.supportsPromises('hooked');", "[42]", true);
  2246. EXPECT_TRUE(V8ValueIs<v8::Promise>(result));
  2247. EXPECT_EQ(R"("hooked")", GetStringPropertyFromObject(
  2248. context->Global(), context, "firstArgument"));
  2249. }
  2250. {
  2251. // We should also be able to hit the custom hook with a callback still.
  2252. auto result = ExpectPass(binding_object,
  2253. "return obj.supportsPromises('hooked', () => {});",
  2254. "[42]", true);
  2255. ASSERT_FALSE(result.IsEmpty());
  2256. EXPECT_TRUE(result->IsUndefined());
  2257. EXPECT_EQ(R"("hooked")", GetStringPropertyFromObject(
  2258. context->Global(), context, "firstArgument"));
  2259. EXPECT_TRUE(
  2260. GetPropertyFromObject(context->Global(), context, "secondArgument")
  2261. ->IsFunction());
  2262. }
  2263. }
  2264. TEST_F(APIBindingUnittest, TestPromiseWithJSUpdateArgumentsPostValidate) {
  2265. bool context_allows_promises = true;
  2266. SetPromiseAvailabilityFlag(&context_allows_promises);
  2267. // Register an update arguments post validate hook for supportsPromises.
  2268. const char kRegisterHook[] = R"(
  2269. (function(hooks) {
  2270. hooks.setUpdateArgumentsPostValidate('supportsPromises',
  2271. (...arguments) => {
  2272. this.firstArgument = arguments[0];
  2273. this.secondArgument = arguments[1];
  2274. arguments[0] = 'bar' + this.firstArgument;
  2275. return arguments;
  2276. });
  2277. }))";
  2278. InitializeJSHooks(kRegisterHook);
  2279. SetFunctions(kFunctionsWithPromiseSignatures);
  2280. InitializeBinding();
  2281. v8::HandleScope handle_scope(isolate());
  2282. v8::Local<v8::Context> context = MainContext();
  2283. v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
  2284. {
  2285. // Calling the method with an invalid signature should never enter the hook.
  2286. ExpectFailure(binding_object, "return obj.supportsPromises('foo');",
  2287. api_errors::InvocationError(
  2288. "test.supportsPromises", "integer int, function callback",
  2289. api_errors::NoMatchingSignature()));
  2290. EXPECT_EQ("undefined", GetStringPropertyFromObject(
  2291. context->Global(), context, "firstArgument"));
  2292. }
  2293. {
  2294. // Calling supportsPromises normally with a callback should work fine and
  2295. // the arguments should be manipulated.
  2296. auto result =
  2297. ExpectPass(binding_object, "return obj.supportsPromises(5, () => {});",
  2298. R"(["bar5"])", true);
  2299. ASSERT_FALSE(result.IsEmpty());
  2300. EXPECT_TRUE(result->IsUndefined());
  2301. EXPECT_EQ(R"(5)", GetStringPropertyFromObject(context->Global(), context,
  2302. "firstArgument"));
  2303. EXPECT_TRUE(
  2304. GetPropertyFromObject(context->Global(), context, "secondArgument")
  2305. ->IsFunction());
  2306. }
  2307. {
  2308. // Calling supportsPromises normally while omitting the callback should work
  2309. // fine, we should get a promise back and the arguments should be
  2310. // manipulated.
  2311. auto result = ExpectPass(binding_object, "return obj.supportsPromises(6);",
  2312. R"(["bar6"])", true);
  2313. EXPECT_TRUE(V8ValueIs<v8::Promise>(result));
  2314. EXPECT_EQ(R"(6)", GetStringPropertyFromObject(context->Global(), context,
  2315. "firstArgument"));
  2316. }
  2317. }
  2318. } // namespace extensions