{"id":2787,"date":"2015-06-08T20:55:26","date_gmt":"2015-06-09T01:55:26","guid":{"rendered":"http:\/\/mikeconley.ca\/blog\/?p=2787"},"modified":"2023-12-20T16:25:10","modified_gmt":"2023-12-20T21:25:10","slug":"things-ive-learned-this-week-june-1-june-5-2015","status":"publish","type":"post","link":"https:\/\/mikeconley.ca\/blog\/2015\/06\/08\/things-ive-learned-this-week-june-1-june-5-2015\/","title":{"rendered":"Things I&#8217;ve Learned This Week (June 1 &#8211; June 5, 2015)"},"content":{"rendered":"<h2>How to get an nsIGlobalObject* from a JSContext*<\/h2>\n<p>I&#8217;m working on <a href=\"https:\/\/reviewboard.mozilla.org\/r\/10011\/\">a patch<\/a> for <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1116188\">bug 1116188<\/a> to make gathering profiles from subprocesses asynchronous. In order to do that, I&#8217;m exposing a new method on nsIProfiler called getProfileDataAsync that is returning a DOM Promise. What&#8217;s interesting about this is that I&#8217;m returning a DOM Promise from C++!\u00a0<sup id=\"rf1-2787\"><a href=\"#fn1-2787\" title=\"&lt;a href=&quot;http:\/\/mikeconley.ca\/blog\/2015\/05\/02\/things-ive-learned-this-week-april-27-may-1-2015\/&quot;&gt;I learned how to do that a few weeks back&lt;\/a&gt;.\" rel=\"footnote\">1<\/a><\/sup><\/p>\n<p>In order to construct a DOM Promise in C++, I need to hand it something that implements nsIGlobalObject. I suspect that this helps the Promise determine which memory region that it belongs to.<\/p>\n<p>My new method gets a JSContext* because I&#8217;ve got the [implicit_jscontext] bit about the method definition in the nsIProfiler.idl file&#8230; so how do I go about turning that into an nsIGlobalObject?<\/p>\n<p>Here&#8217;s the maneuver:<\/p>\n<pre>\/\/ Where aCX is your JSContext*:\r\nnsIGlobalObject* go = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));<\/pre>\n<p>That will, as the name suggests, return either an nsIGlobalObject*, or a nullptr.<\/p>\n<h2>Resolving a DOM Promise from C++ with a JS Object<\/h2>\n<p>For <a href=\"https:\/\/reviewboard.mozilla.org\/r\/10011\/\">my patch<\/a> for <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1116188\">bug 1116188<\/a>, it&#8217;s all well and good to create a DOM Promise, but you have to resolve or reject that Promise for it to have any real value.<\/p>\n<p>In my case, I wanted to take a string, parse it into a JS Object, and resolve with that.<\/p>\n<p><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Promise\">Resolving or rejecting a DOM Promise in Javascript is pretty straight-forward<\/a> &#8211; you&#8217;re given back resolve \/ reject function, and you just need to call those with your results and you&#8217;re done.<\/p>\n<p>In C++, things get a little hairier. As I discovered in <a href=\"http:\/\/mikeconley.ca\/blog\/2015\/06\/06\/the-joy-of-coding-ep-16-frustrations-in-the-key-of-c-plus-plus\/\">my most recent episode of The Joy of Coding<\/a>, conditions need to be right in order for this to work out.<\/p>\n<p>Here&#8217;s what I ended up doing (I&#8217;ve simplified the method somewhat to remove noise):<\/p>\n<pre>void\r\nProfileGatherer::Finish()\r\n{\r\n\u00a0 AutoJSAPI jsapi;\r\n\u00a0 jsapi.Init();\r\n\u00a0 JSContext* cx = jsapi.cx();\r\n\u00a0 JSAutoCompartment ac(cx, mPromise-&gt;GlobalJSObject());\r\n\r\n\u00a0 \/\/ Now parse the JSON so that we resolve with a JS Object.\r\n\u00a0 JS::RootedValue val(cx);\r\n\u00a0 {\r\n\u00a0\u00a0\u00a0 UniquePtr&lt;char[]&gt; buf = mWriter.WriteFunc()-&gt;CopyData();\r\n\u00a0\u00a0\u00a0 NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));\r\n\u00a0\u00a0\u00a0 MOZ_ALWAYS_TRUE(JS_ParseJSON(cx, static_cast&lt;const char16_t*&gt;(js_string.get()),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 js_string.Length(), &amp;val));\r\n\u00a0 }\r\n\u00a0 mPromise-&gt;MaybeResolve(val);\r\n}<\/pre>\n<p>The key parts here are getting the AutoJSAPI on the stack, initting it, gettings its JSContext, and then putting the JSAutoCompartment on the stack. Note that I had to pass not only the JSContext, but the global JS Object for the Promise as well &#8211; I suspect that&#8217;s, again, to ensure that the right compartment is being entered. Otherwise, I start failing assertions like crazy.<\/p>\n<p>Note that the code above is by no means perfect &#8211; I&#8217;m missing error handling functions for when the JSON parsing goes wrong. In that case, I should probably reject the Promise instead. bz pointed me to <a href=\"https:\/\/hg.mozilla.org\/mozilla-central\/file\/4700d1cdf489\/dom\/fetch\/Fetch.cpp#l1618\">a good example of that going on here in Fetch.cpp<\/a>:<\/p>\n<pre>      if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &amp;json)) {\r\n        if (!JS_IsExceptionPending(cx)) {\r\n          localPromise-&gt;MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);\r\n          return;\r\n        }\r\n\r\n        JS::Rooted&lt;JS::Value&gt; exn(cx);\r\n        DebugOnly&lt;bool&gt; gotException = JS_GetPendingException(cx, &amp;exn);\r\n        MOZ_ASSERT(gotException);\r\n\r\n        JS_ClearPendingException(cx);\r\n        localPromise-&gt;MaybeReject(cx, exn);\r\n        return;\r\n      }\r\n\r\n      localPromise-&gt;MaybeResolve(cx, json);\r\n      return;<\/pre>\n<p>I&#8217;ll probably end up doing something similar in the next iteration of my patch.<\/p>\n<hr class=\"footnotes\"><ol class=\"footnotes\" style=\"list-style-type:decimal\"><li id=\"fn1-2787\"><p ><a href=\"http:\/\/mikeconley.ca\/blog\/2015\/05\/02\/things-ive-learned-this-week-april-27-may-1-2015\/\">I learned how to do that a few weeks back<\/a>.&nbsp;<a href=\"#rf1-2787\" class=\"backlink\" title=\"Return to footnote 1.\">&#8617;<\/a><\/p><\/li><\/ol>","protected":false},"excerpt":{"rendered":"<p>How to get an nsIGlobalObject* from a JSContext* I&#8217;m working on a patch for bug 1116188 to make gathering profiles from subprocesses asynchronous. In order to do that, I&#8217;m exposing a new method on nsIProfiler called getProfileDataAsync that is returning a DOM Promise. What&#8217;s interesting about this is that I&#8217;m returning a DOM Promise from [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[874,861,110],"tags":[1188,69,125,1183,1187,1186,35,1190,1184,1189],"class_list":["post-2787","post","type-post","status-publish","format-standard","hentry","category-firefox-mozilla-2","category-mozilla-2","category-musings","tag-c","tag-dom","tag-firefox","tag-jsapi","tag-jsautocompartment","tag-jscontext","tag-mozilla","tag-nsiglobalobject","tag-promises","tag-spidermonkey"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/prmTy-IX","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts\/2787","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/comments?post=2787"}],"version-history":[{"count":1,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts\/2787\/revisions"}],"predecessor-version":[{"id":2788,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts\/2787\/revisions\/2788"}],"wp:attachment":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/media?parent=2787"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/categories?post=2787"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/tags?post=2787"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}