1 module awebview.gui.html;
2 
3 import carbon.utils;
4 import awebview.gui.activity;
5 import awebview.gui.methodhandler;
6 import awebview.gui.application;
7 
8 import awebview.wrapper;
9 import awebview.cssgrammar;
10 
11 import std.variant;
12 import std.typecons;
13 
14 import std.array : appender;
15 import std.format : formattedWrite, format;
16 import std.functional : forward;
17 import std.conv : to;
18 import std.datetime;
19 public import core.time : Duration;
20 
21 public import carbon.event : FiredContext;
22 import carbon.templates : Lstr;
23 import carbon.utils : toLiteral;
24 
25 
26 string buildHTMLTagAttr(in string[string] attrs)
27 {
28     auto app = appender!string();
29     foreach(k, v; attrs)
30         app.formattedWrite("%s=%s ", k, toLiteral(v));
31 
32     return app.data;
33 }
34 
35 
36 string buildHTMLTagAttr(string tag, string value)
37 {
38     import std.string : format;
39     return format("%s=%s ", tag, toLiteral(value));
40 }
41 
42 unittest
43 {
44     assert(buildHTMLTagAttr("a", "b") == "a=b ");
45     assert(buildHTMLTagAttr(["a": "b"]) == "a=b ");
46 }
47 
48 
49 abstract class HTMLPage
50 {
51     this(string id)
52     {
53         _id =  id;
54     }
55 
56 
57     final
58     @property
59     inout(Activity) activity() inout pure nothrow @safe @nogc
60     {
61         return _activity;
62     }
63 
64 
65     final
66     @property
67     inout(Application) application() inout pure nothrow @safe @nogc
68     {
69         if(_activity is null)
70             return null;
71         else
72             return _activity.application;
73     }
74 
75 
76     final
77     string id() const pure nothrow @safe @nogc @property { return _id; }
78 
79 
80     string html() @property;
81 
82 
83     inout(HTMLElement[string]) elements() inout @property;
84 
85 
86     final
87     inout(HTMLElement) opIndex(string id) inout
88     {
89         return this.elements[id];
90     }
91 
92 
93     void onStart(Activity activity)
94     {
95         _activity = activity;
96 
97         foreach(key, elem; elements.maybeModified){
98             elem.onStart(this);
99         }
100     }
101 
102 
103     void onAttach(bool isInitPhase)
104     {
105         _activity = activity;
106         foreach(key, elem; elements.maybeModified)
107             elem.onAttach(isInitPhase);
108     }
109 
110 
111     void onLoad(bool isInit)
112     {
113         foreach(key, elem; elements.maybeModified)
114             elem.onLoad(isInit);
115 
116         _clientWidth = activity.evalJS(`document.documentElement.clientWidth`).get!uint;
117         _clientHeight = activity.evalJS(`document.documentElement.clientHeight`).get!uint;
118         _resizeStatements = generateResizeStatements(this.activity);
119         onResize(activity.width, activity.height);
120     }
121 
122 
123     void onUpdate()
124     {
125         auto newW = activity.evalJS(`document.documentElement.clientWidth`).get!uint;
126         auto newH = activity.evalJS(`document.documentElement.clientHeight`).get!uint;
127 
128         if(newW != _clientWidth || newH != _clientHeight)
129             onResize(activity.width, activity.height);
130 
131         _clientWidth = newW;
132         _clientHeight = newH;
133 
134         foreach(k, elem; elements.maybeModified)
135             elem.onUpdate();
136     }
137 
138 
139     void onDetach()
140     {
141         foreach(key, elem; elements.maybeModified)
142             elem.onDetach();
143     }
144 
145 
146     void onDestroy()
147     {
148         foreach(k, elem; elements.maybeModified)
149             elem.onDestroy();
150     }
151 
152 
153     void onResize(size_t w, size_t h)
154     {
155         this.activity.runJS(_resizeStatements);
156 
157         foreach(k, elem; elements.maybeModified)
158             elem.onResize(w, h);
159     }
160 
161 
162     void onReceiveImmediateMessage(ImmediateMessage msg)
163     {
164         foreach(k, e; elements.maybeModified){
165             e.onReceiveImmediateMessage(msg);
166         }
167     }
168 
169 
170   private:
171     Activity _activity;
172     string _id;
173 
174     size_t _clientWidth;
175     size_t _clientHeight;
176 
177     string _resizeStatements;
178 }
179 
180 
181 class WebPage : HTMLPage
182 {
183     this(string id, string url)
184     {
185         super(id);
186         _url = url;
187     }
188 
189 
190     @property
191     void location(string str)
192     {
193         if(activity){
194             _url = str;
195             activity.view.loadURL(WebURL(str));
196         }
197         else
198             _url = str;
199     }
200 
201 
202     @property
203     string location()
204     {
205         if(activity)
206             return activity.evalJS(q{document.location}).to!string;
207         else
208             return _url;
209     }
210 
211 
212     override
213     void onAttach(bool isInitPhase)
214     {
215         _bLoaded = false;
216     }
217 
218 
219     override
220     void onUpdate()
221     {
222         if(!_bLoaded)
223             this.location = _url;
224 
225         _bLoaded = true;
226     }
227 
228 
229     override
230     inout(HTMLElement[string]) elements() inout @property { return null; }
231 
232 
233     override
234     string html() @property
235     {
236         return `<html><head><title>Jump</title></head><body>Now loading...</body></html>`;
237     }
238 
239   private:
240     string _url;
241     bool _bLoaded;
242 }
243 
244 
245 class TemplateHTMLPage(string form) : HTMLPage
246 {
247     this(string id, Variant[string] exts = null)
248     {
249         super(id);
250         _exts = exts;
251     }
252 
253 
254     final
255     auto js(this This)() @property pure nothrow @safe @nogc inout
256     {
257         static struct Result
258         {
259             string html() const
260             {
261                 auto app = appender!string();
262                 foreach(_, e; _this._js)
263                     app.formattedWrite(`<script src="%s"></script>`);
264                 return app.data;
265             }
266 
267 
268             alias html this;
269 
270 
271             void opOpAssign(string op : "~")(string src)
272             {
273                 import std.path : baseName;
274 
275                 auto bn = src.baseName;
276                 _this._js[bn] = src;
277             }
278 
279 
280           private:
281             This _this;
282         }
283 
284 
285         return Result(this);
286     }
287 
288 
289     final
290     auto css(this This)() @property pure nothrow @safe @nogc inout
291     {
292         static struct Result
293         {
294             string html() const
295             {
296                 auto app = appender!string();
297                 foreach(_, e; _this._css)
298                     app.formattedWrite(`<link rel="stylesheet" href="%s">`);
299                 return app.data;
300             }
301 
302 
303             alias html this;
304 
305 
306             void opOpAssign(string op : "~")(string src)
307             {
308                 import std.path : baseName;
309 
310                 auto bn = src.baseName;
311                 _this._css[bn] = src;
312             }
313 
314 
315           private:
316             This _this;
317         }
318 
319 
320         return Result(this);
321     }
322 
323 
324     override
325     @property
326     string html()
327     {
328         return mixin(Lstr!(form));
329     }
330 
331 
332     override
333     @property
334     inout(HTMLElement[string]) elements() inout { return _elems; }
335 
336 
337     void opOpAssign(string op : "~")(HTMLElement element)
338     {
339         addElement(element);
340     }
341 
342 
343     void addElement(HTMLElement element)
344     {
345         _elems[element.id] = element;
346     }
347 
348 
349     @property
350     inout(Variant[string]) exts() inout { return _exts; }
351 
352     @property
353     inout(T) exts(T)(string str) inout { return *_exts[str].peek!T; }
354 
355 
356   private:
357     HTMLElement[string] _elems;
358     Variant[string] _exts;
359     string[string] _js;
360     string[string] _css;
361 }
362 
363 
364 class HTMLElement
365 {
366     import awebview.jsvariable;
367 
368 
369     this(string id, bool doCreateObject)
370     in{
371         if(id is null)
372             assert(!doCreateObject);
373     }
374     body{
375         _id = id;
376         _hasObj = doCreateObject;
377     }
378 
379 
380     final
381     @property
382     inout(HTMLPage) page() inout pure nothrow @safe @nogc { return _page; }
383 
384 
385     final
386     @property
387     inout(Activity) activity() inout pure nothrow @safe @nogc
388     {
389         if(_page is null)
390             return null;
391         else
392             return _page.activity;
393     }
394 
395 
396     final
397     @property
398     inout(Application) application() inout pure nothrow @safe @nogc
399     {
400         if(this.activity is null)
401             return null;
402         else
403             return this.activity.application;
404     }
405 
406 
407     final
408     @property
409     bool hasObject() const pure nothrow @safe @nogc
410     {
411         return _hasObj;
412     }
413 
414 
415     @property
416     WeakRef!JSObject jsobject()
417     in {
418         assert(_v.isObject);
419     }
420     body {
421         return _v.get!(WeakRef!JSObject);
422     }
423 
424 
425     @property
426     string jsExpr()
427     in {
428         assert(hasObject || hasId);
429     }
430     body {
431         if(this.hasObject)
432             return mixin(Lstr!q{_tmp_%[_id%].domObject});
433         else
434             return `document.getElementById("` ~ id ~ `")`;
435     }
436 
437 
438     final
439     @property
440     auto domObject()
441     {
442         return jsExpression(this.activity, this.jsExpr);
443     }
444 
445 
446     final @property bool hasId() const pure nothrow @safe @nogc { return _id !is null; }
447 
448 
449     final @property string id() const pure nothrow @safe @nogc { return _id; }
450 
451 
452     @property string html() { return ""; }
453     @property string mime() { return "text/html"; }
454     @property const(void)[] rawResource() { return this.html; }
455 
456 
457     void onStart(HTMLPage page)
458     {
459         _page = page;
460 
461         if(this.hasObject && !_v.isObject){
462             _v = activity.createObject(_id);
463         }
464     }
465 
466 
467     void onDestroy()
468     {
469         _page = null;
470         _v = JSValue.null_;
471     }
472 
473 
474     void onAttach(bool isInit)
475     {
476         if(isInit && this.hasObject && !_v.isObject){
477             _v = activity.createObject(_id);
478         }
479     }
480 
481 
482     void onUpdate() {}
483 
484 
485     void onDetach() {}
486 
487 
488     void onLoad(bool isInit)
489     {
490         if(this.hasObject){
491             activity.runJS(mixin(Lstr!q{
492                 _tmp_%[_id%] = {};
493                 _tmp_%[_id%].domObject = document.getElementById("%[_id%]");
494             }));
495         }
496 
497         if(this.hasId || this.hasObject){
498             foreach(key, ref v; _staticProperties)
499                 this.opIndexAssign(v, key);
500         }
501     }
502 
503 
504     void onResize(size_t w, size_t h) {}
505 
506 
507     final
508     @property
509     auto staticProps()
510     {
511         static struct Result
512         {
513             void opIndexAssign(T)(T value, string name)
514             if(is(typeof(JSValue(value)) : JSValue))
515             {
516                 _elem.staticPropsSet(name, value);
517             }
518 
519 
520             void remove(string name)
521             {
522                 _elem.staticPropsRemove(name);
523             }
524 
525 
526             bool opBinaryRight(string op : "in")(string name) inout
527             {
528                 return _elem.inStaticProps(name);
529             }
530 
531           private:
532             HTMLElement _elem;
533         }
534 
535         return Result(this);
536     }
537 
538 
539     void staticPropsSet(T)(string name, T value)
540     if(is(typeof(JSValue(value)) : JSValue))
541     in{
542         assert(this.hasId);
543     }
544     body{
545         JSValue jv = JSValue(value);
546         _staticProperties[name] = jv;
547         if(this.activity){
548             this.opIndexAssign(jv, name);
549         }
550     }
551 
552 
553     final
554     void staticPropsRemove(string name)
555     {
556         _staticProperties.remove(name);
557     }
558 
559 
560     final
561     bool inStaticProps(string name) inout
562     {
563         return !(name !in _staticProperties);
564     }
565 
566 
567     final
568     {
569         mixin JSExprDOMEagerOperators!();
570     }
571 
572 
573     final
574     Tuple!(uint, "x", uint, "y", uint, "width", uint, "height")
575      boundingClientRect() @property
576     {
577         this.activity.runJS(mixin(Lstr!q{
578             var e = %[domObject.jsExpr%].getBoundingClientRect();
579             _carrierObject_.x = e.left;
580             _carrierObject_.y = e.top;
581             _carrierObject_.w = e.width;
582             _carrierObject_.h = e.height;
583         }));
584 
585         auto co = activity.carrierObject;
586         typeof(return) res;
587         res.x = co["x"].get!uint;
588         res.y = co["y"].get!uint;
589         res.width = co["w"].get!uint;
590         res.height = co["h"].get!uint;
591 
592         return res;
593     }
594 
595 
596     final
597     uint posY() @property
598     {
599         return domObject.invoke("getBoundingClientRect")["top"].eval().get!uint;
600     }
601 
602 
603     final
604     uint posX() @property
605     {
606         return domObject.invoke("getBoundingClientRect")["left"].eval().get!uint;
607     }
608 
609 
610     final
611     uint[2] pos() @property
612     {
613         auto rec = boundingClientRect();
614         return [rec.x, rec.y];
615     }
616 
617 
618     final
619     uint width() @property
620     {
621         return domObject.invoke("getBoundingClientRect")["width"].eval().get!uint;
622     }
623 
624 
625     final
626     uint height() @property
627     {
628         return domObject.invoke("getBoundingClientRect")["width"].eval().get!uint;
629     }
630 
631 
632 
633     void onReceiveImmediateMessage(ImmediateMessage msg){}
634 
635 
636   private:
637     string _id;
638     bool _hasObj;
639     JSValue _v;
640     HTMLPage _page;
641     JSValue[string] _staticProperties;
642 }
643 
644 
645 class IDOnlyElement : HTMLElement
646 {
647     this(string id)
648     in{
649         assert(id !is null);
650     }
651     body{
652         super(id, false);
653     }
654 }
655 
656 
657 class TagOnlyElement : HTMLElement
658 {
659     this(string id)
660     in {
661         assert(id !is null);
662     }
663     body {
664         super(id, true);
665     }
666 }
667 
668 
669 /**
670 Example:
671 ----------------------
672 class MyButton : TemplateHTMLElement!(HTMLElement,
673     q{<input type="button" id="%[id%]" value="Click me!">})
674 {
675     this(string id)
676     {
677         super(id, null, false);
678     }
679 }
680 
681 auto btn1 = new MyButton("btn1");
682 assert(btn1.html == q{<input type="button" id="btn1" value="Click me!">});
683 ----------------------
684 */
685 abstract class TemplateHTMLElement(Element, string form) : Element
686 if(is(Element : HTMLElement))
687 {
688     this(T...)(auto ref T args)
689     {
690       static if(is(typeof(super(forward!args[0 .. $-1]))) &&
691                 is(typeof(args[$-1]) : Variant[string]))
692       {
693         super(forward!args[0 .. $-1]);
694         _exts = args[$-1];
695       }
696       else
697         super(forward!args);
698     }
699 
700 
701     @property
702     inout(Variant[string]) exts() inout { return _exts; }
703 
704 
705     override
706     @property
707     string html()
708     {
709         import carbon.templates : Lstr;
710         return mixin(Lstr!(form));
711     }
712 
713 
714   private:
715     Variant[string] _exts;
716 }
717 
718 
719 /// ditto
720 alias TemplateHTMLElement(string form) = TemplateHTMLElement!(HTMLElement, form);
721 
722 
723 /**
724 Example:
725 ----------------
726 class MyButton : DefineSignals!(DeclareSignals!(HTMLElement, "onClick"), "onClick")
727 {
728     this(string id)
729     {
730         super(id, true);
731     }
732 
733     string html() const { ... }
734 }
735 
736 
737 MyButton btn1 = new MyButton("btn1");
738 btn1.onClick.strongConnect(delegate(FiredContext ctx, WeakRef!(const(JSArrayCpp)) arr){
739     assert(ctx.sender == btn1);
740 
741     writeln("fired a signal by ", ctx);
742 });
743 ----------------
744 */
745 abstract class DefineSignals(Element, names...) : Element
746 if(is(Element : HTMLElement) && names.length >= 1)
747 {
748     import carbon.event;
749 
750     this(T...)(auto ref T args)
751     {
752         super(forward!args);
753 
754         foreach(name; names)
755             mixin(format(`_%1$sEvent = new typeof(_%1$sEvent)();`, name));
756     }
757 
758 
759     mixin(genMethod());
760 
761 
762   private:
763     mixin(genField());
764 
765     static
766     {
767         string genField()
768         {
769             auto app = appender!string;
770             foreach(s; names)
771                 app.formattedWrite("EventManager!(WeakRef!(const(JSArrayCpp))) _%sEvent;\n", s);
772 
773             return app.data;
774         }
775 
776 
777         string genMethod()
778         {
779             auto app = appender!string;
780             foreach(s; names){
781                 app.formattedWrite("EventManager!(WeakRef!(const(JSArrayCpp))) %1$s() { return _%1$sEvent; }\n", s);
782                 app.formattedWrite("override void %1$s(WeakRef!(const(JSArrayCpp)) arr) { _%1$sEvent.emit(this, arr); }\n", s);
783             }
784 
785             return app.data;
786         }
787     }
788 }
789 
790 
791 /**
792 Example:
793 -------------------
794 class MyButton : DeclareSignals!(HTMLElement, "onClick")
795 {
796     this(string id)
797     {
798         super(id, true);
799     }
800 
801 
802     override
803     void onClick(WeakRef!(JSArrayCpp) args)
804     {
805         writeln("OK");
806     }
807 }
808 -------------------
809 */
810 abstract class DeclareSignals(Element, names...) : Element
811 if(is(Element : HTMLElement) && names.length >= 1)
812 {
813     this(T...)(auto ref T args)
814     {
815         super(forward!args);
816     }
817 
818 
819     final
820     void doJSInitialize(bool b) @property
821     {
822         _doInit = b;
823     }
824 
825 
826     final
827     void stopPropergation(bool b) @property
828     {
829         _stopProp = b;
830     }
831 
832 
833     mixin(genDeclMethods);
834 
835     override
836     void onStart(HTMLPage page)
837     {
838         super.onStart(page);
839         this.activity.methodHandler.set(this);
840     }
841 
842 
843     override
844     void onLoad(bool init)
845     {
846         super.onLoad(init);
847 
848         if(_doInit){
849             this.activity.runJS(genSettingEventHandlers(this.id, this.domObject.jsExpr, _stopProp));
850         }
851     }
852 
853 
854   private:
855     bool _doInit = true;
856     bool _stopProp = false;
857 
858     static
859     {
860         string genDeclMethods()
861         {
862             auto app = appender!string();
863 
864             foreach(s; names)
865                 app.formattedWrite(`@JSMethodTag("%1$s"w) `"void %1$s(WeakRef!(const(JSArrayCpp)));\n", s);
866 
867             return app.data;
868         }
869 
870 
871         string genSettingEventHandlers(string id, string domExpr, bool stopProp)
872         {
873             import std.string : toLower;
874 
875             auto app = appender!string();
876             app.formattedWrite("var e = %s;", domExpr);
877 
878             foreach(s; names){
879                 if(!stopProp)
880                     app.formattedWrite(q{e.%3$s = function() { %1$s.%2$s(); };}, id, s, toLower(s));
881                 else
882                     app.formattedWrite(q{e.%3$s = function(ev) { ev.stopPropergation(); %1$s.%2$s(); };}, id, s, toLower(s));
883             }
884 
885             return app.data;
886         }
887     }
888 }
889 
890 
891 alias DeclDefSignals(Element, names...) = DefineSignals!(DeclareSignals!(Element, names), names);
892 
893 
894 /**
895 Open context menu when user click right button.
896 */
897 abstract class DeclareContextMenu(Element, setting...) : DeclareSignals!(Element, "onContextMenu", setting)
898 {
899     this(T...)(auto ref T args)
900     {
901         super(forward!args);
902     }
903 
904 
905     HTMLPage menuPage() @property;
906 
907 
908     override
909     void onContextMenu(WeakRef!(const(JSArrayCpp)))
910     {
911         this.activity.popup(this.menuPage);
912     }
913 }
914 
915 
916 /**
917 Mouse hover event
918 */
919 abstract class DeclareHoverSignal(Element) : DeclareSignals!(Element, "onMouseOver", "onMouseOut")
920 {
921     this(T...)(auto ref T args)
922     {
923         super(forward!args);
924     }
925 
926 
927     /**
928     */
929     final
930     bool hover() @property { return _hovered; }
931 
932 
933     final
934     void onHoverImpl()
935     {
936         if(_isStarted)
937             onHover(_hovered, Clock.currTime - _startTime);
938     }
939 
940 
941     /**
942     */
943     void onHover(bool bOver, Duration dur);
944 
945 
946     override
947     void onUpdate()
948     {
949         super.onUpdate();
950 
951         onHoverImpl();
952     }
953 
954 
955     override
956     void onMouseOver(WeakRef!(const(JSArrayCpp)))
957     {
958         _isStarted = true;
959         _hovered = true;
960         _startTime = Clock.currTime;
961         onHoverImpl();
962     }
963 
964 
965     override
966     void onMouseOut(WeakRef!(const(JSArrayCpp)))
967     {
968         _hovered = false;
969         _startTime = Clock.currTime;
970         onHoverImpl();
971     }
972 
973 
974   private:
975     bool _isStarted;
976     bool _hovered;
977     SysTime _startTime;
978 }
979 
980 
981 /**
982 EventPipe
983 */
984 final class PageSignalHandler : HTMLElement
985 {
986     import carbon.event;
987 
988     this(string id = "_event_signal_pipe")
989     {
990         super(id, false);
991         _onStartEvent = new EventManager!(Activity)();
992         _onAttachEvent = new EventManager!bool();
993         _onLoadEvent = new EventManager!bool();
994         _onUpdateEvent = new EventManager!();
995         _onDetachEvent = new EventManager!();
996         _onDestroyEvent = new EventManager!();
997         _onResizeEvent = new EventManager!(size_t, size_t)();
998     }
999 
1000   @property
1001   {
1002     EventManager!(Activity)       onStartHandler  () { return _onStartEvent; }
1003     EventManager!(bool)           onAttachHandler () { return _onAttachEvent; }
1004     EventManager!(bool)           onLoadHandler   () { return _onLoadEvent; }
1005     EventManager!()                 onUpdateHandler () { return _onUpdateEvent; }
1006     EventManager!()                 onDetachHandler () { return _onDetachEvent; }
1007     EventManager!()                 onDestroyHandler() { return _onDestroyEvent; }
1008     EventManager!(size_t, size_t) onResizeHandler () { return _onResizeEvent; }
1009   }
1010 
1011     override
1012     void onStart(HTMLPage page)
1013     {
1014         super.onStart(page);
1015         _onStartEvent.emit(this, page.activity);
1016     }
1017 
1018 
1019     override
1020     void onAttach(bool bInit)
1021     {
1022         super.onAttach(bInit);
1023         _onAttachEvent.emit(this, bInit);
1024     }
1025 
1026 
1027     override
1028     void onLoad(bool bInit)
1029     {
1030         super.onLoad(bInit);
1031         _onLoadEvent.emit(this, bInit);
1032     }
1033 
1034 
1035     override
1036     void onUpdate()
1037     {
1038         super.onUpdate();
1039         _onUpdateEvent.emit(this);
1040     }
1041 
1042 
1043     override
1044     void onDetach()
1045     {
1046         super.onDetach();
1047         _onDetachEvent.emit(this);
1048     }
1049 
1050 
1051     override
1052     void onDestroy()
1053     {
1054         super.onDestroy();
1055         _onDestroyEvent.emit(this);
1056     }
1057 
1058 
1059     override
1060     void onResize(size_t w, size_t h)
1061     {
1062         super.onResize(w, h);
1063         _onResizeEvent.emit(this, w, h);
1064     }
1065 
1066 
1067   private:
1068     EventManager!(Activity) _onStartEvent;
1069     EventManager!(bool) _onAttachEvent;
1070     EventManager!(bool) _onLoadEvent;
1071     EventManager!() _onUpdateEvent;
1072     EventManager!() _onDetachEvent;
1073     EventManager!() _onDestroyEvent;
1074     EventManager!(size_t, size_t) _onResizeEvent;
1075 }
1076 
1077 
1078 /**
1079 Selectors API
1080 */
1081 alias querySelector = querySelectorImpl!false;
1082 
1083 
1084 /// ditto
1085 alias querySelectorAll = querySelectorImpl!true;
1086 
1087 
1088 auto querySelectorImpl(bool isAll)(Activity activity, string cssSelector)
1089 {
1090     import awebview.jsvariable;
1091 
1092     static struct QuerySelectorResult
1093     {
1094         string jsExpr() const @property { return _jsExpr; }
1095         Activity activity() @property { return _activity; }
1096 
1097 
1098         mixin JSExprDOMEagerOperators!();
1099 
1100       private:
1101         string _jsExpr;
1102         Activity _activity;
1103     }
1104 
1105     QuerySelectorResult res;
1106     res._jsExpr = mixin(Lstr!q{document.%[isAll ? "querySelectorAll" : "querySelector"%](%[toLiteral(cssSelector)%])});
1107     res._activity = activity;
1108 
1109     return res;
1110 }