├── README.md ├── index.html └── FPWD └── Overview.html /README.md: -------------------------------------------------------------------------------- 1 | 2 | This is a cookbook produced jointly at W3C by: 3 | 4 | * The Script Library Community Group (http://www.w3.org/community/scriptlib/) 5 | * The Device APIs Working Group (http://www.w3.org/2009/dap/) 6 | * The WebApps Working Group (http://www.w3.org/2008/webapps/) 7 | 8 | Its goal is to document common practices in the production of Web APIs for usage in JavaScript. 9 | We are shying away from calling these "Best Practices" for two reasons: 10 | 11 | * Sometimes there are several options and picking one is a matter of tradeoffs rather than 12 | obvious superiority. 13 | * It is possible that some options may not be the best in an ideal "start from scratch" world, 14 | but are nevertheless the ones recommended here for consistency with the rest of the platform. 15 | 16 | We are also refraining from calling these "patterns". Our goals are more concrete than that. 17 | 18 | Broad feedback from everyone is very much desired. If you are interested, please join the 19 | Script Library Group (it is not just open but also friendly to all). Details can be found at 20 | http://www.w3.org/community/scriptlib/. 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |36 | This document captures common practices in designing APIs that fit well into the Web platform as 37 | a whole, using WebIDL [[!WEBIDL]]. 38 |
39 |42 | This document has been developed progressively over time with help from the WebApps WG 43 | and the ScriptLib CG. 44 |
45 |49 | Over a relatively short period of time the number of different APIs being created for use on the 50 | Web has grown at a sustained pace. In working on these interfaces, many in the community discuss 51 | the benefits of certain approaches over others and reach agreement as to the preferred solution when 52 | facing a given problem. 53 |
54 |55 | Keeping track of all these gems is however difficult given the volume of work being carried on 56 | in parallel and the sometimes disjointed nature of the groups involved. As a result, it can take 57 | a long while and many arguments repeated almost identically before a good solution becomes common. 58 |
59 |60 | What's more, it is altogether too rare to garner feedback from developers who are actually using 61 | these features in their day-to-day work. Because of this, several APIs have shipped with issues 62 | that could have been caught earlier on in the process. This document therefore also endeavours 63 | to elicit feedback from developers in order to make it more readily available to API designers. 64 |
65 |66 | The goal of this document is to capture such ideas as they appear and accrete them over time so as 67 | to constitute a repository of knowledge on this topic. As a guide it does not however attempt to 68 | supplant editors' brains in making decisions as to how to design their APIs, and consequently one 69 | must keep in mind that the reasoning behind a specific recommendation is often more important than 70 | its result. Furthermore, in some cases there may not be a single consensual best approach, and 71 | editors will need to understand the tradeoffs involved in order to pick what works for them amongst 72 | a set of options. 73 |
74 |78 | This section covers the very basics of API creation for the Web platform. If you are familiar with 79 | the APIs found there, you might wish to simply skim the titles. 80 |
81 |84 | Casing is relatively coherent across the platform, and if you are familiar with JavaScript development 85 | it should come to you rather naturally. 86 |
87 |88 | Interfaces, dictionaries, exceptions, and callbacks all use “CamelCase”. That is to say no underscores are used 89 | and the first letter of every word is in uppercase. The rules when one of those words is an acronym 90 | are not necessarily well established — follow your instinct (or try to avoid acronyms). 91 |
92 |93 | FascinatingDocument 94 | Node 95 | SomethingBadHappened 96 | XMLHttpRequest 97 |98 |
99 | Both attributes and methods use “pascalCase” also known as “interCaps”. Which is to say that the first 100 | letter is lowercase, no underscores are used, and all the following logical words in the name 101 | start with an uppercase letter. 102 |
103 |104 | obj.thisIsAnAttribute 105 | obj.simple 106 | obj.doSomethingNow() 107 | obj.run() 108 |109 |
110 | Constants are all uppercase, with logical words separated by underscores. Families of constants tend 111 | to have common suffixes. Please note that constants are decreasingly used and that it is likely you 112 | will not be having recourse to this construct very much (see Don't 113 | Use Numerical Constants). 114 |
115 |116 | REALLY_STUPID_ERROR 117 | PASTA_SAUCE_TYPE 118 |119 |
120 | Events are a bit special and for historical reasons have somewhat more convoluted rules. The interface
121 | that represents a specific event will follow the general rules for interfaces, ending with Event.
122 | But the event name will be all lowercase and with no underscores. Likewise, the on* event
123 | attribute will be the event name (all lowercase and glued together) simply prefixed with “on”.
124 |
126 | SomethingHappenedEvent
127 | obj.addEventListener("somethinghappened", handler, false);
128 | el.onsomethinghappened = handler;
129 |
130 | 134 | It is not always obvious when one should use an attribute rather than a method. The basic rule is simple: 135 | use attributes as often as possible. 136 |
137 |
138 | Never use getFoo()/setFoo() accessor pairs unless it is very clear that these are
139 | operations that have strong effects beyond just changing the property of the object. If you wish your interface
140 | to also be usable in a language other than JavaScript and for which such accessors tend to be needed, the bindings
141 | for that language can naturally generate the accessor pair. Don't do this:
142 |
144 | var len = obj.getLength(); 145 | arr.setLength(num); 146 |147 |
148 | But rather: 149 |
150 |151 | var len = obj.length; 152 | arr.length = num; 153 |154 |
155 | Beyond operations that obviously require a method (that take parameters, return a computed value, carry out 156 | an action, etc.) there are specific cases in which what might at first seem like a candidate for being 157 | an attribute is best handled as a method: 158 |
159 |objA.child = objB it may be necessary to set objB.parent
165 | to objA behind the scenes. Such side effects call for a method: objA.setChild(objB).
166 | stat
173 | calls are not cheap). In such cases it is best to make the accessor an asynchronous method that is
174 | passed a callback such as someFile.size(function (bytes) { … })
175 | (or, if there are many such properties on the object, have a single call to load them all, for instance
176 | someFile.stat(function (info) { … })).
177 | 183 | The Web platform's runtime model is based on a single-threaded event loop. Because of that, any time 184 | a method requires even a little bit of time to run, it blocks the thread, and therefore the execution 185 | of anything else on the page — leading to bad user experience. 186 |
187 |188 | Example of operations that can take a while to complete include reading from the network, accessing the 189 | disk, performing a database search, or waking a sensor up. 190 |
191 |192 | Another case in which asynchronous operations are required is for all security entry points. Some operations 193 | (e.g. obtaining the device's physical location) can require the user agent to obtain the user's permission. 194 | If that were to be done synchronously, the entire application would freeze while the user makes her decision. 195 | This is undesirable in terms of user experience, but it also a bad security model. The user needs to have 196 | all the time she wants in order to make such decisions and should not feel pressured by a frozen application 197 | (people make bad security decisions when they want to get something done). 198 |
199 |
200 | It can seem tempting to make methods either synchronous or asynchronous at the developer's discretion.
201 | The simple answer to that is: don't. The result can be catastrophic: for instance, while in development
202 | a method that has to interact with a remote resource may return quickly because of light load on the
203 | server and proximity between the developer (who is using a powerful device on a fast network) and the
204 | server, so that the synchronous call may seem fine. But once deployed, it can cause long, very
205 | perceivable freezes for the end-users. The XMLHttpRequest interface does make that option
206 | available on its open() method and there is broad consensus that it was a bad decision that
207 | should never be emulated.
208 |
210 | A context that is not similarly affected by synchronous issues is that of Web Workers (since they run in a 211 | separate conceptual thread from the Web application's user interface). In such cases, it can sometimes be useful to 212 | provide a synchronous variant of an asynchronous interface that would be available solely in a Worker context. 213 | This is primarily done in order to make development simpler in such contexts and should not be considered 214 | required. 215 |
216 |220 | Interfaces regularly require constant identifiers. You have a node and you want to know if it's 221 | an element or a comment. You have a message and you want to know if it's email or SMS. That much makes 222 | sense. What does not make sense is insisting on naming these things with numbers when we could name them 223 | with, you know, names. 224 |
225 |226 | There are several variants in this bad practice. First, is the lovingly terse approach: 227 |
228 |229 | if (foo.type === 17) doSomething(); 230 |231 |
232 | Then there is the comment you have to paste every time: 233 |
234 |235 | // check if this of the fungible type 236 | if (foo.type === 17) doSomething(); 237 |238 |
239 | Or we can have the uselessly long variant: 240 |
241 |242 | if (foo.type === FooInterface.FUNGIBLE_TYPE) doSomething(); 243 |244 |
245 | And there's the meh option: 246 |
247 |248 | var isFungible = FooInterface.FUNGIBLE_TYPE; 249 | // ... 250 | if (foo.type === isFungible) doSomething(); 251 |252 |
253 | None of the above is as simple, terse, and self-documenting as: 254 |
255 |256 | if (foo.type === "fungible") doSomething(); 257 |258 |
259 | We're not in the 1970s, standardising UNIX system calls. Strings are lightweight enough, 260 | and readable. There is no need to define string constants to support these: just the plain 261 | strings, defined as such in specification prose, work. In some languages, this can lose 262 | static verification, but in JavaScript it makes no difference: if you misspell the the 263 | string you might have equally misspelt the constant, with the same failure. 264 |
265 |266 | Additionally, interfaces that don't start out by listing a dozen (useless) constants are 267 | more readable. The only downside to doing away with numerical constants is the negative 268 | impact it may on occasion have on feature detection. It would however be better to rely 269 | on other aspects that enable feature detection, since these constants can easily be added 270 | for a half-baked feature (and frequently have been). 271 |
272 |276 | If you are developing functionality for use inside of a Web runtime (but not a browser) that is likely to 277 | both share code with existing Web projects (typically, libraries like jQuery) and that also plans to 278 | evolve and integrate improvements to the Web platform on a regular basis, then you should make sure 279 | that the interfaces and methods you add cannot conflict with ones added to the core platform. 280 |
281 |282 | For methods and attributes that extend existing interfaces, you should rely on vendor prefixing, i.e. simply 283 | prefixing your additions with a short string that is your company's or project's name. 284 |
285 |286 | xhr.acmePassiveFTP(); 287 | document.acmeDigitalSignature; 288 |289 |
290 | For interfaces that you add, rather than prefixing all of their names which can get tedious, it can 291 | be simpler to just make them available under your own namespaced object. 292 |
293 |294 | var toaster = new acme.Toaster(); 295 |296 |
301 | WebIDL [[WEBIDL]] is a powerful and flexible schema language for APIs. It is a useful foundation in 302 | ensuring that APIs can be defined interoperably without each and everyone of them needing to repeat 303 | all manners of behavioural specifics. 304 |
305 |306 | It can however be somewhat daunting to use at times, and the intricacies of the platform mean that 307 | it has some dark or at least non-obvious corners. This chapter endeavours to help clarify at least 308 | enough of it that you will feel comfortable making use of it. 309 |
310 |
313 | While designing Web APIs, you might encounter situations where you need to define an interface
314 | that has no method. That is, for certain requirements, you need a method to define a property
315 | bag object that would be expressed as an Object object or as an object literal in
316 | JavaScript implementation. This is where the dictionaries come into play.
317 |
319 | This section presents a guideline to use dictionaries at the right place in your specification 320 | without any common mistake. First, in the "When to use" section, we show the typical cases you can 321 | make use of dictionaries. Second, in the "How to use" section, we provide a list of features that you 322 | can check in order not to make a common mistake with dictionary usage. 323 |
324 |327 | Property bag objects are commonly used to define optional parameters for certain 328 | methods of given interfaces. Also, they can be used to define necessary data 329 | structures and data formats in a given application context. Here are the 330 | representative examples: 331 |
332 |333 | Case 1: Arguments to Interface Method 334 | When designing interfaces using WebIDL, you can use dictionaries to define a 335 | property bag object that carries multiple arguments to its method. In many 336 | practices, the methods take dictionary value as an optional argument 337 | although it is not mandatory. 338 |
339 |
340 | // WebIDL
341 | partial interface IDBDatabaseSync {
342 | IDBObjectStoreSync createObjectStore (DOMString name,
343 | optional IDBObjectStoreParameters optionalParameters);
344 | };
345 |
346 | dictionary IDBObjectStoreParameters {
347 | DOMString? keyPath = null;
348 | boolean autoIncrement = false;
349 | };
350 |
351 | // JavaScript
352 | var parameters = {
353 | keyPath: "id"
354 | , autoIncrement: true
355 | };
356 | trans.db.createObjectStore("Contact", parameters);
357 |
358 | 359 | More examples in published specifications: 360 | http://www.w3.org/TR/FileAPI/#creating-revoking 361 |
362 |363 | Case 2: Arguments to Constructor 364 | Using dictionaries, you can also define a constructor arguments to the given 365 | interfaces. This allows the developers create and pass an object or an 366 | object literal to the constructor of the JavaScript objects. 367 |
368 |
369 | // WebIDL
370 | [Constructor(IntentParameters params),
371 | Constructor(DOMString action, DOMString type, optional any data, optional
372 | sequence<Transferable> transferList)]
373 | interface Intent {
374 | readonly attribute DOMString action;
375 | readonly attribute DOMString type;
376 | readonly attribute any data;
377 | readonly attribute MessagePort[] ports;
378 | readonly attribute any extras;
379 | void postResult (any data, optional sequence<Transferable> transferable);
380 | void postFailure (any data);
381 | };
382 |
383 | dictionary IntentParameters {
384 | DOMString action;
385 | DOMString type;
386 | any data;
387 | sequence<Transferable> transfer;
388 | Object extras;
389 | URL service;
390 | sequence<URL> suggestions;
391 | };
392 |
393 | // JavaScript
394 | var intent = new Intent({
395 | action: "http://intents.w3.org/pick",
396 | type: "image/png",
397 | extras: { multiple: true, count: 5 },
398 | service: "http://example.com/pick-image"
399 | });
400 |
401 | 402 | More examples in published specifications: 403 | http://www.w3.org/TR/2012/WD-dom-20120405/#interface-event 404 | or http://www.w3.org/TR/FileAPI/#blob 405 |
406 |407 | Case 3: User object 408 | A dictionary is normatively defined as an associative array data type with a 409 | fixed, ordered set of key-value pairs. That is, you can use a 410 | dictionary to define a custom data type carrying a set of properties as an 411 | object format used in the given application context. 412 |
413 |
414 | // WebIDL
415 | partial dictionary Media {
416 | MediaContent content = {};
417 | DOMString title;
418 | DOMString description;
419 | DOMString type = "*.*";
420 | DOMString author;
421 | DOMString path;
422 | float size;
423 | sequence<DOMString> tags;
424 | Date publishDate;
425 | Resolution resolution;
426 | };
427 |
428 | // JavaScript
429 | var mediaSequence = [];
430 | var mediaObject = {
431 | content: { uri: "http://example.com/images/kitten.png" },
432 | title: "",
433 | description: "",
434 | type: "image/png",
435 | author: "",
436 | path: "/local/path/to/png/kitten.png",
437 | size: 1000000,
438 | tags: ["tom and jerry", "miyau"],
439 | publishedDate: "",
440 | resolution: { width: 1920, height: 1080 }
441 | };
442 | mediaSequence.push(mediaObject);
443 | intent.postResult(mediaSequence);
444 |
445 | 446 | More examples in published specifications: 447 | http://www.w3.org/TR/2012/WD-gallery-20120712/#the-media-dictionary 448 | or 449 | http://www.w3.org/TR/2012/WD-contacts-api-20120712/#the-contact-dictionary 450 |
451 |457 | Dictionaries are always passed by value. Hence, when you define a dictionary 458 | member that should carry an array of certain value, use sequence type rather 459 | than Array type. Sequences are always passed by value while arrays are 460 | passed by reference. 461 |
462 |
463 | dictionary IntentParameters {
464 | DOMString action;
465 | DOMString type;
466 | any data;
467 | sequence<Transferable> transfer;
468 | Object extras;
469 | URL service;
470 | sequence<URL> suggestions;
471 | };
472 |
473 | 474 | In addition, there is another check point where sequence type should be used 475 | rather than array type. WebIDL describes that the dictionary 476 | type must not be used as a type of element of an array. Hence, when your 477 | dictionary has to carry an array of the dictionary values as its member, 478 | use sequence type. 479 |
480 |
481 | dictionary DoNotUseLikeThis {
482 | IntentParameters[] params;
483 | };
484 |
485 | dictionary UseLikeThis {
486 | sequence<IntentParameters> params;
487 | };
488 |
489 | 493 | Dictionaries support inheritance as interface does. A dictionary must not 494 | be defined having a cycle in its hierarchy. 495 |
496 |
497 | dictionary Novel : Book {
498 | // dictionary-members...
499 | };
500 |
501 | dictionary Book : Novel {
502 | // dictionary-members...
503 | };
504 |
505 | 509 | On a given dictionary value, the presence of each dictionary member is 510 | optional. Therefore, in order to indicate that certain members are required fields in 511 | your specification, use default values. Dictionary members with default values 512 | are always considered to be present. 513 |
514 |
515 | dictionary PropertyBag {
516 | DOMString iAmRequired = "default";
517 | DOMString iAmOptional;
518 | };
519 |
520 |
521 | In the example, since iAmRequired has default value, it is always
522 | considered as present when the dictionary value is passed. On the other hand, when
523 | iAmOptional is missing, it is regarded as absent.
524 |
529 | It is one of common mistakes that a dictionary type is used with attribute 530 | field in interface definition. In face, dictionaries must not be used as the type 531 | of attribute, constant or exception fields. 532 |
533 |
534 | interface HasWrongUsage {
535 | readonly attribute IAmDictionary dict;
536 | };
537 |
538 | 539 | To correct it, use type any or object and describe in prose that the 540 | attribute takes the type of the dictionary. 541 |
542 |
543 | interface HasRightUsage {
544 | readonly attribute any dict;
545 | };
546 |
547 | 548 | In your spec, write "dict is of type IAmDictionary dictionary as defined in 549 | [REFERENCE]". 550 |
551 |
555 | Do not use dictionary type as inner type of a nullable type. By definition in WebIDL dictionary,
556 | valued with null, is regarded to be an empty dictionary, which is not null.
557 | (It can even have the value of members defined in their default values.)
558 |
560 | dictionary HasWrongUsage {
561 | IAmDictionary? dict;
562 | };
563 |
564 | dictionary HasRightUsage {
565 | IAmDictionary dict;
566 | };
567 |
568 | 569 | As members of a dictionary are optional by definition, the developer who 570 | reads your specification can omit the member that she intends to be not present in 571 | the given application context. 572 |
573 |577 | As with interfaces, the IDL for dictionaries can be split into multiple 578 | parts by using partial dictionary definitions. 579 |
580 |
581 | dictionary SomeDictionary {
582 | // dictionary-members…
583 | };
584 |
585 | partial dictionary SomeDictionary {
586 | // dictionary-members…
587 | };
588 |
589 |
593 | Only the following two extended attributes are applicable to dictionary
594 | members: [Clamp], [EnforceRange].
595 |
602 | Interfaces are the core work horse of WebIDL as they are the means through which functionality is
603 | exposed. They are introduced with the interface keyword, have a name, and a body that
604 | contains their definition in terms of constants, attributes, and methods (known as “operations” in
605 | WebIDL). A typical interface looks like this:
606 |
608 | interface BeerBottle {
609 | // body goes here
610 | };
611 |
612 | 613 | Without modifiers, an interface will become available in the global context as an object with the 614 | same name. 615 |
616 |617 | Interfaces can inherit from other interfaces, in which case they acquire their properties. Inheritance 618 | is specified like this: 619 |
620 |
621 | interface BeerBottle : Bottle {
622 | // body
623 | };
624 | interface Spork : Spoon, Fork {
625 | // body
626 | };
627 |
628 | 629 | As in the last example above, an interface can inherit from any number of other interfaces. This practice 630 | however is discouraged if it can be avoided as it entails more complex resolution rules and can be 631 | difficult to implement in some contexts. Multiple inheritance is often a sign that the API design should 632 | be revisited and can be simplified. 633 |
634 |637 | It is common to have to add functionality to an existing interface, or to wish to define a single 638 | interface spread over multiple locations. This can happen primarily for two reasons: 639 |
640 |Navigator). It's an important aspect of the Web platform's core extensibility.
645 | 654 | In both cases there is a need to extend an existing interface, and inheritance is not the solution 655 | since one effectively wishes to mix functionality into the existing interface, and not create a 656 | new one. 657 |
658 |
659 | A construct that was previously used for this but is no longer recommended was
660 | to create the extending interface with [NoInterfaceObject] and then state
661 | that the existing interface implements the extending one. This is
662 | exemplified below:
663 |
665 | // we wish to add findUnicorns() to Navigator
666 | [NoInterfaceObject]
667 | interface UnicornNavigator {
668 | Unicorns findUnicorns (DOMString name);
669 | };
670 | Navigator implements UnicornNavigator;
671 | // you can now call navigator.findUnicorns("tab");
672 |
673 | 674 | This is clunky however, and no longer necessary. It can still be found in some drafts but should 675 | not be copied. 676 |
677 |
678 | The proper way of implementing this feature is to use partial interfaces. These
679 | are just like regular interfaces, except that they are defined to specify simply a fragment of
680 | the whole, and can therefore add to existing interfaces that may be defined anywhere else.
681 |
683 | The previous example can therefore best be rewritten as: 684 |
685 |
686 | partial interface Navigator {
687 | Unicorns findUnicorns (DOMString name);
688 | };
689 |
690 | 694 | If you have read WebIDL as found in many specifications, you will have noticed that there are 695 | special constructs that can decorate existing ones in order to enhance them. These are called 696 | “extended attributes” and their goal is not to change the WebIDL itself — which is to say that 697 | they don't modify the abstract schema described by WebIDL — but to influence how the concrete 698 | language bindings operate. 699 |
700 |701 | In this section we look at several extended attributes that have a bearing on how interfaces 702 | are bound in JavaScript. 703 |
704 |
705 | By default, when an interface is bound in JavaScript, an interface object for it is available
706 | globally but it is not constructible. That is to say that it can be used for instance for
707 | instanceof testing, or that it can expose some members such as static methods,
708 | static attributes, or constants but you can't construct it with new. Try out
709 | for instance in your JavaScript console:
710 |
712 | new Node() 713 | // TypeError: Node is not a constructor 714 |715 |
716 | Therefore, in order to use a constructor in JavaScript, you will need to use the [Constructor]
717 | extended attribute. [Constructor] can optionally take parameters (not giving it parentheses is
718 | the same as giving it an empty set) and you can specify multiple ones.
719 |
721 | An example might make this clearer: 722 |
723 |
724 | // new Unicorn();
725 | [Constructor] // the same as [Constructor()]
726 | interface Unicorn {
727 | // ...
728 | };
729 |
730 | // new Unicorn();
731 | // new Unicorn("tab");
732 | // new Unicorn(5, 82);
733 | [Constructor,
734 | Constructor(DOMString name),
735 | Constructor(unsigned long wingspan, unsigned long age)]
736 | interface Unicorn {
737 | // ...
738 | };
739 |
740 |
741 | At times it is desirable to have the constructor for an interface have a different name from that
742 | of the interface. For such cases, use the [NamedConstructor] extended attribute. It
743 | works in the exact same way as [Constructor], except that it also accepts an identifier
744 | that specifies the name to use for the constructor. An example will show the syntax:
745 |
747 | // new Rose();
748 | // new Rose("pink");
749 | [NamedConstructor=Rose,
750 | NamedConstructor=Rose(DOMString colour)]
751 | interface RosoideaeRosa {
752 | // ...
753 | };
754 |
755 |
759 | Another useful extended attribute for interfaces is [ArrayClass]. It comes in handy when
760 | it is necessary to define an interface that can behave just like a real JavaScript array. It is meant
761 | to be used with the getter, setter, creator, and deleter
762 | keywords for indexed properties.
763 |
765 | These keywords are rather straightforward: 766 |
767 |getter defines what happens when a value is fetched from the array, so that for instance
770 | the following can work: var carrots = shoppingList[7];.
771 | setter and creator is the reverse and defines what happens when a value is
774 | set in the array (on an existing entry or a newly minted one — for arrays you usually want to use the
775 | same operation for setter and creator), for instance with the following:
776 | shoppingList[0] = "beer"; or
777 | shoppingList.push("unicorns");
778 | deleter defines what happens when a value is removed from the array, for instance
781 | with: shoppingList.pop();.
782 | 785 | Assembling this all together looks like this: 786 |
787 |
788 | [ArrayClass]
789 | interface ShoppingList {
790 | attribute unsigned long length;
791 | getter object getShoppingItem (unsigned long index);
792 | // note how both creator and setter are used here
793 | creator setter object setShoppingItem (unsigned long index, object item);
794 | deleter void removeShoppingItem (unsigned long index);
795 | };
796 |
797 |
798 | The above give you a ShoppingList interface. You could use it as follows:
799 |
801 | var list = obj.findShoppingListFor("robin");
802 | list = list.concat("beer tea basil ham cat-food".split(" "));
803 | list.forEach(function (item) {
804 | remindUser("Shop for " + item);
805 | });
806 | list.pop(); // bad cat
807 | console.log("Getting " + list.join("\n"));
808 |
809 |
813 | There have been many excessive uses of [NoInterfaceObject] — unless you are absolutely
814 | certain that it is what you want, you should avoid it. The odds are good that what you are looking
815 | for is either a partial interface or a dictionary.
816 |
818 | In practice it should only be used for abstract interfaces that enrich a concrete one through the
819 | implements statement.
820 |
826 | Constants are a simple construct that does exactly what it say on the tin. It creates a member of a given type on
827 | an interface (or an exception) the value of which cannot be changed, introduced with the const
828 | keyword.
829 |
831 | Constants are limited in the values they can accept. They can take numerical values (including the likes of
832 | Infinity and NaN), booleans, or null (for cases in which you might forget
833 | what the value of null is). Examples tell you all you need to know about the syntax:
834 |
836 | interface Something {
837 | const short ANSWER = 42;
838 | const boolean WRONG = false;
839 | const unsigned long BIG_NUMBER = Infinity;
840 | };
841 |
842 | 843 | While they were previously used rather commonly, there are decreasing reasons to want to resort to 844 | constants on interfaces. Before using them, be sure to be aware of Enums and 845 | Don't Use Numerical Constants. 846 |
847 |851 | An enumeration is used to specify a set of strings that are acceptable, it is typically used 852 | to define options that are restricted to a limited vocabulary. 853 |
854 |
855 | Enumerations are defined with the enum keyword, a type name, and then a list of
856 | strings (with no duplicates) inside curly brackets. The name can then be used where type
857 | identifiers usually appear.
858 |
860 | enum Unit {
861 | "m",
862 | "cm",
863 | "mm"
864 | };
865 | interface Unicorn {
866 | void setHornSize (unsigned long length, Unit unit);
867 | };
868 | // This works
869 | // unicorn.setHornSize(42, "cm");
870 | // This blows up as well it should
871 | // unicorn.setHornSize(7, "in");
872 |
873 | 874 | When an attribute is defined to be an enumeration type, then assigning a value to it that is 875 | not in the enumerated set simply does nothing. 876 |
877 |
881 | Attributes are a fundamental building block of interfaces. They are used to specify data fields of
882 | a given type. An attribute is introduced with the attribute keyword.
883 |
885 | They can be read-only in which case they cannot be assigned to, or read-write. It is important to 886 | note that if the value of a read-only attribute is not atomic but has attributes of its own, those 887 | are not made read-only as well — they keep operating just as specified by that type. An example: 888 |
889 |
890 | interface Name {
891 | attribute DOMString givenName;
892 | attribute DOMString familyName;
893 | };
894 | interface Person {
895 | readonly attribute Name fullName;
896 | };
897 |
898 |
899 | In the above definition, it is impossible to assign a new value of the fullName field
900 | of Person. So you could not do the following: billie.name = someName;.
901 | But the fields of the Name object itself stay just as re-write so that they can still
902 | be set, as in billie.name.familyName = "The Cat";.
903 |
905 | An attribute can also be said to be static, in which case it is an attribute of the
906 | interface rather than being associated with the object. Static attributes are rarely good ideas
907 | and can tend to indicate a bad design.
908 |
910 | // set the debug flag globally
911 | interface Runtime {
912 | static attribute boolean debug;
913 | };
914 |
915 |
916 | A specific behaviour can be added to attributes to define how a given object is turned into a string,
917 | using the stringifier modifier. When a object is cast to a string, if a stringifier
918 | has been specified (there can be only one) then the string produced will be the value of that
919 | attribute. Naturally, that attribute has to be of type DOMString.
920 |
922 | // another Person interface
923 | [Constructor(DOMString name, short age)]
924 | interface Unicorn {
925 | stringifier attribute DOMString fullName;
926 | attribute short age;
927 | };
928 |
929 | 930 | This can then be used with the following code: 931 |
932 |
933 | var uni = new Unicorn("Tab", 42);
934 | document.getElementById("output-unicorn").textContent = uni;
935 | // that element now has content "Tab";
936 |
937 | 941 | Methods are the workhorses of WebIDL. In the specification they are known as “operations”, but 942 | since everyone calls them methods we have decided to stick to this term as it will be more readily 943 | understood. They are used to define behaviour. 944 |
945 |
946 | Methods have a return type, and a list of arguments which can be precisely specified. As for attributes,
947 | a method can also be said to be static, in which case they become available on the
948 | interface object directly.
949 |
951 | interface KittenFactory {
952 | static Kitten makeMeANewKitten (DOMString name);
953 | };
954 | // var billie = KittenFactory.makeMeANewKitten("Billie The Cat");
955 |
956 |
957 | The return type can be any type or void, which indicates that nothing is returned.
958 |
960 | The most complex part of a method's definition is its arguments list. It can contain any number of 961 | argument definitions, each of which has a type and a name; all of them separated by commas. 962 |
963 |
964 | Note that it used to be that arguments were all preceded by the in keyword, which was
965 | a leftover from OMG IDL. Since Web specifications make no use of the out and inout
966 | argument styles of the aptly named OMG specification, the in keyword has been dropped and
967 | must not be used.
968 |
970 | A basic method therefore looks like this: 971 |
972 |
973 | interface Unicorn {
974 | void gallop (unsigned long speed, Pattern rhythm);
975 | AudioStream speak ();
976 | };
977 |
978 |
979 | The arguments can be modified in a number of ways. First, an argument can be marked as
980 | optional in which case it can be omitted when the method is invoked. If so,
981 | it should be noted that all the arguments that are positioned after it in the list will
982 | also be considered optional.
983 |
985 | enum Direction { "left", "right" };
986 | interface Dahut {
987 | void turnAround (optional Direction whichWay, short degrees);
988 | };
989 | // I can call someDahut.turnAround()
990 |
991 |
992 | Note that in the above it is not indicated that the degrees argument is optional even
993 | though it implicitly is. For readability purposes, it is best to always indicate that an argument
994 | is optional even when it implicitly is as someone scanning the method definition quickly from the
995 | end might not notice it.
996 |
998 | A problem with the above method definition is also that while my dahut can only turn left or 999 | right, there is no indication of what happens if I omit the argument. Is it right or left? Or 1000 | random? This can be remedied in specification prose but it is better to specify a default 1001 | directly in the WebIDL, as follows: 1002 |
1003 |
1004 | enum Direction { "left", "right" };
1005 | interface Dahut {
1006 | void turnAround (optional Direction whichWay = "left", short degrees);
1007 | };
1008 | // when I call someDahut.turnAround(), it now turns left
1009 |
1010 |
1011 | An argument can also be variadic. This indicates that that argument can be repeated zero or
1012 | more times when the method is called. This can only be done if it is the last argument in the arguments
1013 | list. Variadic arguments are specified by appending "..." to the type.
1014 |
1016 | interface Restaurant {
1017 | DeferredFood orderFood (DOMString... dishName);
1018 | };
1019 | // I can now call
1020 | // resto.orderFood("spam", "spam", "spam", "spam", "spam", "spam", "spam", "spam", "spam");
1021 |
1022 | 1026 | WebIDL makes it possible to use overloading of methods in interfaces so that there can be 1027 | more than one method with a given name that takes a different set of arguments. This happens 1028 | simply by specifying the method more than once. 1029 |
1030 |1031 | Note that method overloading is rarely needed and should usually be avoided unless there is 1032 | no better solution, if only because the restrictions on how it can be used and the resolution 1033 | rules can be quite complex. A lot of the cases in which one may think that overloading may 1034 | be required are actually better addressed with optional arguments, variadic arguments, 1035 | or at times union types. 1036 |
1037 |
1038 | Union types are used where types can be used, but instead of specifying just one type they
1039 | indicated that the values can be of several different types. They are specified between parentheses,
1040 | with each type separated by the or keyword.
1041 |
1043 | interface Party {
1044 | void bookDate ((Date or unsigned long or DOMString) date);
1045 | };
1046 | // I can now call with...
1047 | // a JS Date object
1048 | // party.bookDate(new Date(…));
1049 | // time since epoch
1050 | // party.bookDate(1337998907987);
1051 | // a magic string
1052 | // party.bookDate("tomorrow");
1053 |
1054 | 1055 | This can be useful for return values as well: 1056 |
1057 |
1058 | interface Cryptozoology {
1059 | (Dahut or Unicorn) findAnimal ();
1060 | };
1061 |
1062 | 1066 | Exceptions are special classes that can be used to be thrown. They are very limited interfaces 1067 | that can only define constants, and a limited kind of attribute (known as “fields”) that are 1068 | defined solely by a type and a name. 1069 |
1070 |
1071 | All exceptions automatically get a message and a name field (for which
1072 | the specification should provide values), such that those need not be explicitly specified.
1073 |
1075 | It is strongly recommended that new specifications do not define
1076 | new exceptions, but rather reuse DOMException while providing a specific name
1077 | for it.
1078 |
1080 | In addition to DOMException, a number of predefined exceptions are also available that
1081 | can be reused by specifications. This list is: Error, EvalError, RangeError,
1082 | ReferenceError, SyntaxError, TypeError and URIError
1083 | (these correspond to their JavaScript equivalents as defined in [[ECMA-262]]).
1084 |
1086 | Due to these constraints on usage, we have decided not to delve deeply into exceptions in this 1087 | section. 1088 |
1089 |
1093 | It is very common for asynchronous methods in Web APIs to accept callbacks, which are functions
1094 | that can be called with specific arguments when called back. These are specified with the
1095 | callback keyword, a name, a return type, and an arguments list which is similar
1096 | to that used for a method.
1097 |
1099 | Specifying callbacks is simple: 1100 |
1101 |
1102 | // defining callbacks
1103 | callback LandingOk = void (unsigned long landingSpeed);
1104 | callback LandingFail = void (DOMString gravity, short casualties);
1105 |
1106 | // using callbacks
1107 | interface FlyingUnicorn {
1108 | void land (LandingOk, LandingFail);
1109 | };
1110 |
1111 | // in code
1112 | myFlyingUnicorn.land(
1113 | function (speed) {
1114 | console.log("Successfully landed at " + speed + "kph!");
1115 | }
1116 | , function (severity, dead) {
1117 | console.log("Crashed landing. Damage level: " + severity + ". Dead elves: " + dead);
1118 | }
1119 | );
1120 |
1121 | 1125 | I admit to still being largely mystified by this. Recent discussion on the mailing list does 1126 | not at all dispel that feeling. 1127 |
1128 |1132 | The features of WebIDL listed in this section have been specified for the sole purpose of describing 1133 | legacy APIs such as are found in “DOM 0”. It is particularly important that you do not use them unless 1134 | you are trying to document one such API — they must not be used for any kind of new work. The list 1135 | includes: 1136 |
1137 |1138 | Get an actual list. 1139 |
1140 | 1146 |1159 | Beyond understanding the basics of WebIDL there are common and useful idiomatic constructs that 1160 | can usefully be shared. This section endeavours to progressively accrue some more, so please send 1161 | yours in! 1162 |
1163 |1166 | We had talked of Yehuda writing this section. I will see if I can hunt him down. 1167 |
1168 |1172 | Asynchronous method calls require a point of reentry in the form of a callback. There are multiple 1173 | ways of specifying these, none of which is consensually consider the one and only approach. 1174 |
1175 |1176 | One is success/error callback functions, as used in [[GEOLOCATION-API]]. It takes the shape of a method 1177 | call that accepts a callback function for success followed by one for errors. There may be arguments 1178 | before the success callback, and the error callback is usually optional. 1179 |
1180 |
1181 | navigator.petKitten(
1182 | function() {
1183 | console.log("kitten is purring");
1184 | },
1185 | function(err) {
1186 | console.log("kitten hates you, because: " + err);
1187 | }
1188 | );
1189 |
1190 | 1191 | Downsides of this approach are that it is a little more verbose and does not encourage error handling. It 1192 | also lacks in extensibility since adding new arguments to the method will typically require placing them 1193 | after the optional error handler — making them de facto optional. Likewise, extending it to support not 1194 | just callbacks for success and error but also for other situations that may warrant reporting information 1195 | back (e.g. progress being made) is clumsy at best. Since it is only used in [[GEOLOCATION-API]] it does not 1196 | appear to be a required pattern for consistency. 1197 |
1198 |Another approach is to define new DOM Events that are called when an underlying system changes.
1199 |
1200 | window.addEventListener("kittenpurr", function() {
1201 | console.log("kitten is purring");
1202 | }, false);
1203 |
1204 | On one hand, most developers are quite familiar with DOM Events, and they make it easy to have several handlers for a given event.
1205 |On the other hand, using DOM Events for listening to systems that need specific activation (e.g. powering up) are usually not a good idea: addEventListener is supposed to be free from side effects, and associating activation code with it breaks that promise. The [[DEVICE-ORIENTATION]] specification is as such an anti-pattern that should not be replicated. See some discussions on side effects in addEventListener.
1207 | Another is returning an EventTarget object, as pioneered by [[XMLHTTPREQUEST]]. This consists 1208 | in opening a request that returns an object on which event handlers can be registered, then starting 1209 | that request with a specific call. 1210 |
1211 |
1212 | var kitpet = navigator.openKittenPetting();
1213 |
1214 | kitpet.onpurr = function () {
1215 | console.log("kitten is purring");
1216 | };
1217 |
1218 | kitpet.ondrool = function () {
1219 | console.log("kitten is now drooling");
1220 | };
1221 |
1222 | kitpet.onviciousbite = function () {
1223 | console.log("kitten bit your hand off");
1224 | };
1225 |
1226 | kitpet.start();
1227 |
1228 | 1229 | The approach is the most verbose and does not encourage error handling, but it does have the advantages 1230 | of being trivially extended for greater feedback, and of being familiar to users of [[XMLHTTPREQUEST]] 1231 | (and now [[INDEXEDDB]]). It can, however, feel rather overkill if extensions for additional events does 1232 | not seem likely. 1233 |
1234 |
1235 | Then there are NodeJS style callbacks. For every asynchronous operation, every callback takes
1236 | as first parameter an error object which is null when the operation was a success, followed
1237 | by as many pieces of information as are needed to capture the success.
1238 |
1240 | navigator.petKitten(function (err, state) {
1241 | if (err) console.log("kitten mauled your face with extreme prejudice");
1242 | else console.log("kittem manifest joy by " + state);
1243 | });
1244 |
1245 | 1246 | The primary value in this approach is that it puts errors "right there" where they are likely to be handled. 1247 | Its consistency is also very helpful in creating chains of callbacks, a feature that proves precious 1248 | when multiple callbacks can become nested. The major downside of this approach is that it is currently 1249 | not used in the rest of stack and therefore probably only familiar to NodeJS developers (which granted 1250 | represent a fast-growing proportion of the general web developer population). 1251 |
1252 |1253 | Finally there are promises. These are similar to returning an EventTarget object but 1254 | more generic and use a slightly different style. 1255 |
1256 |
1257 | var promise = navigator.petKitten();
1258 | promise.then(function () {
1259 | console.log("kitten is purring");
1260 | }).fail(function () {
1261 | console.log("kitten bit your hand off");
1262 | }).run();
1263 |
1264 | 1265 | There is elegance and flexibility in promises, but to date they are used in no Web specification. 1266 |
1267 |1271 | This ought to be relatively straightforward. 1272 |
1273 |1276 | Should we have a section on specifying with Web Intents? 1277 |
1278 |1281 | A number of other sources provide information that can be useful in writing specifications. This list 1282 | is by no means complete, suggestions are welcome. 1283 |
1284 |1285 | Anne maintains a document called How To Spec on 1286 | how to specify certain features of the platform. Some parts of it are tailored to the specific markup 1287 | that is used when processing specifications with Anolis, but the general advice is sound. 1288 |
1289 |1293 | Many thanks to the following people, in no particular order: Cameron McCormack, Marcos Càceres, 1294 | Andreas Gal, Rick Waldron. 1295 |
1296 |239 | Copyright © 240 | 2011-2012 241 | 242 | W3C® 243 | (MIT, 244 | ERCIM, 245 | Keio), All Rights Reserved. 246 | W3C liability, 247 | trademark and 248 | document use rules apply. 249 |
250 | 251 | 252 |256 | This document captures common practices in designing APIs that fit well into the Web platform as 257 | a whole, using WebIDL [WEBIDL]. 258 |
259 |264 | This section describes the status of this document at the time of its publication. Other 265 | documents may supersede this document. A list of current W3C publications and the latest revision 266 | of this technical report can be found in the W3C technical reports 267 | index at http://www.w3.org/TR/. 268 |
269 | 270 |271 | This document has been developed progressively over time with help from the WebApps WG 272 | and the ScriptLib CG. 273 |
274 | 275 |276 | This document was published by the Device APIs Working Group as a First Public Working Group Note. 277 | 278 | If you wish to make comments regarding this document, please send them to 279 | public-device-apis@w3.org 280 | (subscribe, 281 | archives). 282 | 283 | 284 | All feedback is welcome. 285 |
286 | 287 |288 | Publication as a Working Group Note does not imply endorsement by the W3C Membership. 289 | This is a draft document and may be updated, replaced or obsoleted by other documents at 290 | any time. It is inappropriate to cite this document as other than work in progress. 291 |
292 | 293 | 294 |295 | 296 | This document was produced by a group operating under the 297 | 5 February 2004 W3C Patent Policy. 298 | 299 | 300 | 301 | 302 | W3C maintains a public list of any patent disclosures 303 | 304 | made in connection with the deliverables of the group; that page also includes instructions for 305 | disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains 306 | Essential Claim(s) must disclose the 307 | information in accordance with section 308 | 6 of the W3C Patent Policy. 309 | 310 | 311 |
312 | 313 | 314 | 315 | 316 |321 | Over a relatively short period of time the number of different APIs being created for use on the 322 | Web has grown at a sustained pace. In working on these interfaces, many in the community discuss 323 | the benefits of certain approaches over others and reach agreement as to the preferred solution when 324 | facing a given problem. 325 |
326 |327 | Keeping track of all these gems is however difficult given the volume of work being carried on 328 | in parallel and the sometimes disjointed nature of the groups involved. As a result, it can take 329 | a long while and many arguments repeated almost identically before a good solution becomes common. 330 |
331 |332 | What's more, it is altogether too rare to garner feedback from developers who are actually using 333 | these features in their day-to-day work. Because of this, several APIs have shipped with issues 334 | that could have been caught earlier on in the process. This document therefore also endeavours 335 | to elicit feedback from developers in order to make it more readily available to API designers. 336 |
337 |338 | The goal of this document is to capture such ideas as they appear and accrete them over time so as 339 | to constitute a repository of knowledge on this topic. As a guide it does not however attempt to 340 | supplant editors' brains in making decisions as to how to design their APIs, and consequently one 341 | must keep in mind that the reasoning behind a specific recommendation is often more important than 342 | its result. Furthermore, in some cases there may not be a single consensual best approach, and 343 | editors will need to understand the tradeoffs involved in order to pick what works for them amongst 344 | a set of options. 345 |
346 |350 | This section covers the very basics of API creation for the Web platform. If you are familiar with 351 | the APIs found there, you might wish to simply skim the titles. 352 |
353 |356 | Casing is relatively coherent across the platform, and if you are familiar with JavaScript development 357 | it should come to you rather naturally. 358 |
359 |360 | Interfaces, dictionaries, exceptions, and callbacks all use “CamelCase”. That is to say no underscores are used 361 | and the first letter of every word is in uppercase. The rules when one of those words is an acronym 362 | are not necessarily well established — follow your instinct (or try to avoid acronyms). 363 |
364 |FascinatingDocument 365 | Node 366 | SomethingBadHappened 367 | XMLHttpRequest
369 | Both attributes and methods use “pascalCase” also known as “interCaps”. Which is to say that the first 370 | letter is lowercase, no underscores are used, and all the following logical words in the name 371 | start with an uppercase letter. 372 |
373 |obj.thisIsAnAttribute 374 | obj.simple 375 | obj.doSomethingNow() 376 | obj.run()
378 | Constants are all uppercase, with logical words separated by underscores. Families of constants tend 379 | to have common suffixes. Please note that constants are decreasingly used and that it is likely you 380 | will not be having recourse to this construct very much (see Don't 381 | Use Numerical Constants). 382 |
383 |REALLY_STUPID_ERROR 384 | PASTA_SAUCE_TYPE
386 | Events are a bit special and for historical reasons have somewhat more convoluted rules. The interface
387 | that represents a specific event will follow the general rules for interfaces, ending with Event.
388 | But the event name will be all lowercase and with no underscores. Likewise, the on* event
389 | attribute will be the event name (all lowercase and glued together) simply prefixed with “on”.
390 |
SomethingHappenedEvent
392 | obj.addEventListener("somethinghappened", handler, false);
393 | el.onsomethinghappened = handler;398 | It is not always obvious when one should use an attribute rather than a method. The basic rule is simple: 399 | use attributes as often as possible. 400 |
401 |
402 | Never use getFoo()/setFoo() accessor pairs unless it is very clear that these are
403 | operations that have strong effects beyond just changing the property of the object. If you wish your interface
404 | to also be usable in a language other than JavaScript and for which such accessors tend to be needed, the bindings
405 | for that language can naturally generate the accessor pair. Don't do this:
406 |
var len = obj.getLength(); 408 | arr.setLength(num);
410 | But rather: 411 |
412 |var len = obj.length; 413 | arr.length = num;
415 | Beyond operations that obviously require a method (that take parameters, return a computed value, carry out 416 | an action, etc.) there are specific cases in which what might at first seem like a candidate for being 417 | an attribute is best handled as a method: 418 |
419 |objA.child = objB it may be necessary to set objB.parent
425 | to objA behind the scenes. Such side effects call for a method: objA.setChild(objB).
426 | stat
433 | calls are not cheap). In such cases it is best to make the accessor an asynchronous method that is
434 | passed a callback such as someFile.size(function (bytes) { … })
435 | (or, if there are many such properties on the object, have a single call to load them all, for instance
436 | someFile.stat(function (info) { … })).
437 | 443 | The Web platform's runtime model is based on a single-threaded event loop. Because of that, any time 444 | a method requires even a little bit of time to run, it blocks the thread, and therefore the execution 445 | of anything else on the page — leading to bad user experience. 446 |
447 |448 | Example of operations that can take a while to complete include reading from the network, accessing the 449 | disk, performing a database search, or waking a sensor up. 450 |
451 |452 | Another case in which asynchronous operations are required is for all security entry points. Some operations 453 | (e.g. obtaining the device's physical location) can require the user agent to obtain the user's permission. 454 | If that were to be done synchronously, the entire application would freeze while the user makes her decision. 455 | This is undesirable in terms of user experience, but it also a bad security model. The user needs to have 456 | all the time she wants in order to make such decisions and should not feel pressured by a frozen application 457 | (people make bad security decisions when they want to get something done). 458 |
459 |
460 | It can seem tempting to make methods either synchronous or asynchronous at the developer's discretion.
461 | The simple answer to that is: don't. The result can be catastrophic: for instance, while in development
462 | a method that has to interact with a remote resource may return quickly because of light load on the
463 | server and proximity between the developer (who is using a powerful device on a fast network) and the
464 | server, so that the synchronous call may seem fine. But once deployed, it can cause long, very
465 | perceivable freezes for the end-users. The XMLHttpRequest interface does make that option
466 | available on its open() method and there is broad consensus that it was a bad decision that
467 | should never be emulated.
468 |
470 | A context that is not similarly affected by synchronous issues is that of Web Workers (since they run in a 471 | separate conceptual thread from the Web application's user interface). In such cases, it can sometimes be useful to 472 | provide a synchronous variant of an asynchronous interface that would be available solely in a Worker context. 473 | This is primarily done in order to make development simpler in such contexts and should not be considered 474 | required. 475 |
476 |480 | Interfaces regularly require constant identifiers. You have a node and you want to know if it's 481 | an element or a comment. You have a message and you want to know if it's email or SMS. That much makes 482 | sense. What does not make sense is insisting on naming these things with numbers when we could name them 483 | with, you know, names. 484 |
485 |486 | There are several variants in this bad practice. First, is the lovingly terse approach: 487 |
488 |if (foo.type === 17) doSomething();
490 | Then there is the comment you have to paste every time: 491 |
492 |// check if this of the fungible type 493 | if (foo.type === 17) doSomething();
495 | Or we can have the uselessly long variant: 496 |
497 |if (foo.type === FooInterface.FUNGIBLE_TYPE) doSomething();
499 | And there's the meh option: 500 |
501 |var isFungible = FooInterface.FUNGIBLE_TYPE; 502 | // ... 503 | if (foo.type === isFungible) doSomething();
505 | None of the above is as simple, terse, and self-documenting as: 506 |
507 |if (foo.type === "fungible") doSomething();
509 | We're not in the 1970s, standardising UNIX system calls. Strings are lightweight enough, 510 | and readable. There is no need to define string constants to support these: just the plain 511 | strings, defined as such in specification prose, work. In some languages, this can lose 512 | static verification, but in JavaScript it makes no difference: if you misspell the the 513 | string you might have equally misspelt the constant, with the same failure. 514 |
515 |516 | Additionally, interfaces that don't start out by listing a dozen (useless) constants are 517 | more readable. The only downside to doing away with numerical constants is the negative 518 | impact it may on occasion have on feature detection. It would however be better to rely 519 | on other aspects that enable feature detection, since these constants can easily be added 520 | for a half-baked feature (and frequently have been). 521 |
522 |526 | If you are developing functionality for use inside of a Web runtime (but not a browser) that is likely to 527 | both share code with existing Web projects (typically, libraries like jQuery) and that also plans to 528 | evolve and integrate improvements to the Web platform on a regular basis, then you should make sure 529 | that the interfaces and methods you add cannot conflict with ones added to the core platform. 530 |
531 |532 | For methods and attributes that extend existing interfaces, you should rely on vendor prefixing, i.e. simply 533 | prefixing your additions with a short string that is your company's or project's name. 534 |
535 |xhr.acmePassiveFTP(); 536 | document.acmeDigitalSignature;
538 | For interfaces that you add, rather than prefixing all of their names which can get tedious, it can 539 | be simpler to just make them available under your own namespaced object. 540 |
541 |var toaster = new acme.Toaster();
547 | WebIDL [WEBIDL] is a powerful and flexible schema language for APIs. It is a useful foundation in 548 | ensuring that APIs can be defined interoperably without each and everyone of them needing to repeat 549 | all manners of behavioural specifics. 550 |
551 |552 | It can however be somewhat daunting to use at times, and the intricacies of the platform mean that 553 | it has some dark or at least non-obvious corners. This chapter endeavours to help clarify at least 554 | enough of it that you will feel comfortable making use of it. 555 |
556 |
559 | While designing Web APIs, you might encounter situations where you need to define an interface
560 | that has no method. That is, for certain requirements, you need a method to define a property
561 | bag object that would be expressed as an Object object or as an object literal in
562 | JavaScript implementation. This is where the dictionaries come into play.
563 |
565 | This section presents a guideline to use dictionaries at the right place in your specification 566 | without any common mistake. First, in the "When to use" section, we show the typical cases you can 567 | make use of dictionaries. Second, in the "How to use" section, we provide a list of features that you 568 | can check in order not to make a common mistake with dictionary usage. 569 |
570 |573 | Property bag objects are commonly used to define optional parameters for certain 574 | methods of given interfaces. Also, they can be used to define necessary data 575 | structures and data formats in a given application context. Here are the 576 | representative examples: 577 |
578 |579 | Case 1: Arguments to Interface Method 580 | When designing interfaces using WebIDL, you can use dictionaries to define a 581 | property bag object that carries multiple arguments to its method. In many 582 | practices, the methods take dictionary value as an optional argument 583 | although it is not mandatory. 584 |
585 |// WebIDL 586 | partial interface IDBDatabaseSync { 587 | IDBObjectStoreSync createObjectStore (DOMString name, 588 | optional IDBObjectStoreParameters optionalParameters); 589 | }; 590 | 591 | dictionary IDBObjectStoreParameters { 592 | DOMString? keyPath = null; 593 | boolean autoIncrement = false; 594 | }; 595 | 596 | // JavaScript 597 | var parameters = { 598 | keyPath: "id" 599 | , autoIncrement: true 600 | }; 601 | trans.db.createObjectStore("Contact", parameters);
603 | More examples in published specifications: 604 | http://www.w3.org/TR/FileAPI/#creating-revoking 605 |
606 |607 | Case 2: Arguments to Constructor 608 | Using dictionaries, you can also define a constructor arguments to the given 609 | interfaces. This allows the developers create and pass an object or an 610 | object literal to the constructor of the JavaScript objects. 611 |
612 |// WebIDL 613 | [Constructor(IntentParameters params), 614 | Constructor(DOMString action, DOMString type, optional any data, optional 615 | sequence<Transferable> transferList)] 616 | interface Intent { 617 | readonly attribute DOMString action; 618 | readonly attribute DOMString type; 619 | readonly attribute any data; 620 | readonly attribute MessagePort[] ports; 621 | readonly attribute any extras; 622 | void postResult (any data, optional sequence<Transferable> transferable); 623 | void postFailure (any data); 624 | }; 625 | 626 | dictionary IntentParameters { 627 | DOMString action; 628 | DOMString type; 629 | any data; 630 | sequence<Transferable> transfer; 631 | Object extras; 632 | URL service; 633 | sequence<URL> suggestions; 634 | }; 635 | 636 | // JavaScript 637 | var intent = new Intent({ 638 | action: "http://intents.w3.org/pick", 639 | type: "image/png", 640 | extras: { multiple: true, count: 5 }, 641 | service: "http://example.com/pick-image" 642 | });
644 | More examples in published specifications: 645 | http://www.w3.org/TR/2012/WD-dom-20120405/#interface-event 646 | or http://www.w3.org/TR/FileAPI/#blob 647 |
648 |649 | Case 3: User object 650 | A dictionary is normatively defined as an associative array data type with a 651 | fixed, ordered set of key-value pairs. That is, you can use a 652 | dictionary to define a custom data type carrying a set of properties as an 653 | object format used in the given application context. 654 |
655 |// WebIDL 656 | partial dictionary Media { 657 | MediaContent content = {}; 658 | DOMString title; 659 | DOMString description; 660 | DOMString type = "*.*"; 661 | DOMString author; 662 | DOMString path; 663 | float size; 664 | sequence<DOMString> tags; 665 | Date publishDate; 666 | Resolution resolution; 667 | }; 668 | 669 | // JavaScript 670 | var mediaSequence = []; 671 | var mediaObject = { 672 | content: { uri: "http://example.com/images/kitten.png" }, 673 | title: "", 674 | description: "", 675 | type: "image/png", 676 | author: "", 677 | path: "/local/path/to/png/kitten.png", 678 | size: 1000000, 679 | tags: ["tom and jerry", "miyau"], 680 | publishedDate: "", 681 | resolution: { width: 1920, height: 1080 } 682 | }; 683 | mediaSequence.push(mediaObject); 684 | intent.postResult(mediaSequence);
686 | More examples in published specifications: 687 | http://www.w3.org/TR/2012/WD-gallery-20120712/#the-media-dictionary 688 | or 689 | http://www.w3.org/TR/2012/WD-contacts-api-20120712/#the-contact-dictionary 690 |
691 |697 | Dictionaries are always passed by value. Hence, when you define a dictionary 698 | member that should carry an array of certain value, use sequence type rather 699 | than Array type. Sequences are always passed by value while arrays are 700 | passed by reference. 701 |
702 |dictionary IntentParameters {
703 | DOMString action;
704 | DOMString type;
705 | any data;
706 | sequence<Transferable> transfer;
707 | Object extras;
708 | URL service;
709 | sequence<URL> suggestions;
710 | };712 | In addition, there is another check point where sequence type should be used 713 | rather than array type. WebIDL describes that the dictionary 714 | type must not be used as a type of element of an array. Hence, when your 715 | dictionary has to carry an array of the dictionary values as its member, 716 | use sequence type. 717 |
718 |dictionary DoNotUseLikeThis {
719 | IntentParameters[] params;
720 | };
721 |
722 | dictionary UseLikeThis {
723 | sequence<IntentParameters> params;
724 | };729 | Dictionaries support inheritance as interface does. A dictionary must not 730 | be defined having a cycle in its hierarchy. 731 |
732 |dictionary Novel : Book {
733 | // dictionary-members...
734 | };
735 |
736 | dictionary Book : Novel {
737 | // dictionary-members...
738 | };743 | On a given dictionary value, the presence of each dictionary member is 744 | optional. Therefore, in order to indicate that certain members are required fields in 745 | your specification, use default values. Dictionary members with default values 746 | are always considered to be present. 747 |
748 |dictionary PropertyBag {
749 | DOMString iAmRequired = "default";
750 | DOMString iAmOptional;
751 | };
753 | In the example, since iAmRequired has default value, it is always
754 | considered as present when the dictionary value is passed. On the other hand, when
755 | iAmOptional is missing, it is regarded as absent.
756 |
761 | It is one of common mistakes that a dictionary type is used with attribute 762 | field in interface definition. In face, dictionaries must not be used as the type 763 | of attribute, constant or exception fields. 764 |
765 |interface HasWrongUsage {
766 | readonly attribute IAmDictionary dict;
767 | };769 | To correct it, use type any or object and describe in prose that the 770 | attribute takes the type of the dictionary. 771 |
772 |interface HasRightUsage {
773 | readonly attribute any dict;
774 | };776 | In your spec, write "dict is of type IAmDictionary dictionary as defined in 777 | [REFERENCE]". 778 |
779 |
783 | Do not use dictionary type as inner type of a nullable type. By definition in WebIDL dictionary,
784 | valued with null, is regarded to be an empty dictionary, which is not null.
785 | (It can even have the value of members defined in their default values.)
786 |
dictionary HasWrongUsage {
788 | IAmDictionary? dict;
789 | };
790 |
791 | dictionary HasRightUsage {
792 | IAmDictionary dict;
793 | };795 | As members of a dictionary are optional by definition, the developer who 796 | reads your specification can omit the member that she intends to be not present in 797 | the given application context. 798 |
799 |803 | As with interfaces, the IDL for dictionaries can be split into multiple 804 | parts by using partial dictionary definitions. 805 |
806 |dictionary SomeDictionary {
807 | // dictionary-members…
808 | };
809 |
810 | partial dictionary SomeDictionary {
811 | // dictionary-members…
812 | };
817 | Only the following two extended attributes are applicable to dictionary
818 | members: [Clamp], [EnforceRange].
819 |
826 | Interfaces are the core work horse of WebIDL as they are the means through which functionality is
827 | exposed. They are introduced with the interface keyword, have a name, and a body that
828 | contains their definition in terms of constants, attributes, and methods (known as “operations” in
829 | WebIDL). A typical interface looks like this:
830 |
interface BeerBottle { 832 | // body goes here 833 | };
835 | Without modifiers, an interface will become available in the global context as an object with the 836 | same name. 837 |
838 |839 | Interfaces can inherit from other interfaces, in which case they acquire their properties. Inheritance 840 | is specified like this: 841 |
842 |interface BeerBottle : Bottle { 843 | // body 844 | }; 845 | interface Spork : Spoon, Fork { 846 | // body 847 | };
849 | As in the last example above, an interface can inherit from any number of other interfaces. This practice 850 | however is discouraged if it can be avoided as it entails more complex resolution rules and can be 851 | difficult to implement in some contexts. Multiple inheritance is often a sign that the API design should 852 | be revisited and can be simplified. 853 |
854 |857 | It is common to have to add functionality to an existing interface, or to wish to define a single 858 | interface spread over multiple locations. This can happen primarily for two reasons: 859 |
860 |Navigator). It's an important aspect of the Web platform's core extensibility.
865 | 874 | In both cases there is a need to extend an existing interface, and inheritance is not the solution 875 | since one effectively wishes to mix functionality into the existing interface, and not create a 876 | new one. 877 |
878 |
879 | A construct that was previously used for this but is no longer recommended was
880 | to create the extending interface with [NoInterfaceObject] and then state
881 | that the existing interface implements the extending one. This is
882 | exemplified below:
883 |
// we wish to add findUnicorns() to Navigator 885 | [NoInterfaceObject] 886 | interface UnicornNavigator { 887 | Unicorns findUnicorns (DOMString name); 888 | }; 889 | Navigator implements UnicornNavigator; 890 | // you can now call navigator.findUnicorns("tab");
892 | This is clunky however, and no longer necessary. It can still be found in some drafts but should 893 | not be copied. 894 |
895 |
896 | The proper way of implementing this feature is to use partial interfaces. These
897 | are just like regular interfaces, except that they are defined to specify simply a fragment of
898 | the whole, and can therefore add to existing interfaces that may be defined anywhere else.
899 |
901 | The previous example can therefore best be rewritten as: 902 |
903 |partial interface Navigator { 904 | Unicorns findUnicorns (DOMString name); 905 | };
910 | If you have read WebIDL as found in many specifications, you will have noticed that there are 911 | special constructs that can decorate existing ones in order to enhance them. These are called 912 | “extended attributes” and their goal is not to change the WebIDL itself — which is to say that 913 | they don't modify the abstract schema described by WebIDL — but to influence how the concrete 914 | language bindings operate. 915 |
916 |917 | In this section we look at several extended attributes that have a bearing on how interfaces 918 | are bound in JavaScript. 919 |
920 |
921 | By default, when an interface is bound in JavaScript, an interface object for it is available
922 | globally but it is not constructible. That is to say that it can be used for instance for
923 | instanceof testing, or that it can expose some members such as static methods,
924 | static attributes, or constants but you can't construct it with new. Try out
925 | for instance in your JavaScript console:
926 |
new Node() 928 | // TypeError: Node is not a constructor
930 | Therefore, in order to use a constructor in JavaScript, you will need to use the [Constructor]
931 | extended attribute. [Constructor] can optionally take parameters (not giving it parentheses is
932 | the same as giving it an empty set) and you can specify multiple ones.
933 |
935 | An example might make this clearer: 936 |
937 |// new Unicorn(); 938 | [Constructor] // the same as [Constructor()] 939 | interface Unicorn { 940 | // ... 941 | }; 942 | 943 | // new Unicorn(); 944 | // new Unicorn("tab"); 945 | // new Unicorn(5, 82); 946 | [Constructor, 947 | Constructor(DOMString name), 948 | Constructor(unsigned long wingspan, unsigned long age)] 949 | interface Unicorn { 950 | // ... 951 | };
953 | At times it is desirable to have the constructor for an interface have a different name from that
954 | of the interface. For such cases, use the [NamedConstructor] extended attribute. It
955 | works in the exact same way as [Constructor], except that it also accepts an identifier
956 | that specifies the name to use for the constructor. An example will show the syntax:
957 |
// new Rose(); 959 | // new Rose("pink"); 960 | [NamedConstructor=Rose, 961 | NamedConstructor=Rose(DOMString colour)] 962 | interface RosoideaeRosa { 963 | // ... 964 | };
969 | Another useful extended attribute for interfaces is [ArrayClass]. It comes in handy when
970 | it is necessary to define an interface that can behave just like a real JavaScript array. It is meant
971 | to be used with the getter, setter, creator, and deleter
972 | keywords for indexed properties.
973 |
975 | These keywords are rather straightforward: 976 |
977 |getter defines what happens when a value is fetched from the array, so that for instance
980 | the following can work: var carrots = shoppingList[7];.
981 | setter and creator is the reverse and defines what happens when a value is
984 | set in the array (on an existing entry or a newly minted one — for arrays you usually want to use the
985 | same operation for setter and creator), for instance with the following:
986 | shoppingList[0] = "beer"; or
987 | shoppingList.push("unicorns");
988 | deleter defines what happens when a value is removed from the array, for instance
991 | with: shoppingList.pop();.
992 | 995 | Assembling this all together looks like this: 996 |
997 |[ArrayClass] 998 | interface ShoppingList { 999 | attribute unsigned long length; 1000 | getter object getShoppingItem (unsigned long index); 1001 | // note how both creator and setter are used here 1002 | creator setter object setShoppingItem (unsigned long index, object item); 1003 | deleter void removeShoppingItem (unsigned long index); 1004 | };
1006 | The above give you a ShoppingList interface. You could use it as follows:
1007 |
var list = obj.findShoppingListFor("robin"); 1009 | list = list.concat("beer tea basil ham cat-food".split(" ")); 1010 | list.forEach(function (item) { 1011 | remindUser("Shop for " + item); 1012 | }); 1013 | list.pop(); // bad cat 1014 | console.log("Getting " + list.join("\n"));
1019 | There have been many excessive uses of [NoInterfaceObject] — unless you are absolutely
1020 | certain that it is what you want, you should avoid it. The odds are good that what you are looking
1021 | for is either a partial interface or a dictionary.
1022 |
1024 | In practice it should only be used for abstract interfaces that enrich a concrete one through the
1025 | implements statement.
1026 |
1032 | Constants are a simple construct that does exactly what it say on the tin. It creates a member of a given type on
1033 | an interface (or an exception) the value of which cannot be changed, introduced with the const
1034 | keyword.
1035 |
1037 | Constants are limited in the values they can accept. They can take numerical values (including the likes of
1038 | Infinity and NaN), booleans, or null (for cases in which you might forget
1039 | what the value of null is). Examples tell you all you need to know about the syntax:
1040 |
interface Something { 1042 | const short ANSWER = 42; 1043 | const boolean WRONG = false; 1044 | const unsigned long BIG_NUMBER = Infinity; 1045 | };
1047 | While they were previously used rather commonly, there are decreasing reasons to want to resort to 1048 | constants on interfaces. Before using them, be sure to be aware of Enums and 1049 | Don't Use Numerical Constants. 1050 |
1051 |1055 | An enumeration is used to specify a set of strings that are acceptable, it is typically used 1056 | to define options that are restricted to a limited vocabulary. 1057 |
1058 |
1059 | Enumerations are defined with the enum keyword, a type name, and then a list of
1060 | strings (with no duplicates) inside curly brackets. The name can then be used where type
1061 | identifiers usually appear.
1062 |
enum Unit { 1064 | "m", 1065 | "cm", 1066 | "mm" 1067 | }; 1068 | interface Unicorn { 1069 | void setHornSize (unsigned long length, Unit unit); 1070 | }; 1071 | // This works 1072 | // unicorn.setHornSize(42, "cm"); 1073 | // This blows up as well it should 1074 | // unicorn.setHornSize(7, "in");
1076 | When an attribute is defined to be an enumeration type, then assigning a value to it that is 1077 | not in the enumerated set simply does nothing. 1078 |
1079 |
1083 | Attributes are a fundamental building block of interfaces. They are used to specify data fields of
1084 | a given type. An attribute is introduced with the attribute keyword.
1085 |
1087 | They can be read-only in which case they cannot be assigned to, or read-write. It is important to 1088 | note that if the value of a read-only attribute is not atomic but has attributes of its own, those 1089 | are not made read-only as well — they keep operating just as specified by that type. An example: 1090 |
1091 |interface Name { 1092 | attribute DOMString givenName; 1093 | attribute DOMString familyName; 1094 | }; 1095 | interface Person { 1096 | readonly attribute Name fullName; 1097 | };
1099 | In the above definition, it is impossible to assign a new value of the fullName field
1100 | of Person. So you could not do the following: billie.name = someName;.
1101 | But the fields of the Name object itself stay just as re-write so that they can still
1102 | be set, as in billie.name.familyName = "The Cat";.
1103 |
1105 | An attribute can also be said to be static, in which case it is an attribute of the
1106 | interface rather than being associated with the object. Static attributes are rarely good ideas
1107 | and can tend to indicate a bad design.
1108 |
// set the debug flag globally 1110 | interface Runtime { 1111 | static attribute boolean debug; 1112 | };
1114 | A specific behaviour can be added to attributes to define how a given object is turned into a string,
1115 | using the stringifier modifier. When a object is cast to a string, if a stringifier
1116 | has been specified (there can be only one) then the string produced will be the value of that
1117 | attribute. Naturally, that attribute has to be of type DOMString.
1118 |
// another Person interface 1120 | [Constructor(DOMString name, short age)] 1121 | interface Unicorn { 1122 | stringifier attribute DOMString fullName; 1123 | attribute short age; 1124 | };
1126 | This can then be used with the following code: 1127 |
1128 |var uni = new Unicorn("Tab", 42); 1129 | document.getElementById("output-unicorn").textContent = uni; 1130 | // that element now has content "Tab";
1135 | Methods are the workhorses of WebIDL. In the specification they are known as “operations”, but 1136 | since everyone calls them methods we have decided to stick to this term as it will be more readily 1137 | understood. They are used to define behaviour. 1138 |
1139 |
1140 | Methods have a return type, and a list of arguments which can be precisely specified. As for attributes,
1141 | a method can also be said to be static, in which case they become available on the
1142 | interface object directly.
1143 |
interface KittenFactory { 1145 | static Kitten makeMeANewKitten (DOMString name); 1146 | }; 1147 | // var billie = KittenFactory.makeMeANewKitten("Billie The Cat");
1149 | The return type can be any type or void, which indicates that nothing is returned.
1150 |
1152 | The most complex part of a method's definition is its arguments list. It can contain any number of 1153 | argument definitions, each of which has a type and a name; all of them separated by commas. 1154 |
1155 |
1156 | Note that it used to be that arguments were all preceded by the in keyword, which was
1157 | a leftover from OMG IDL. Since Web specifications make no use of the out and inout
1158 | argument styles of the aptly named OMG specification, the in keyword has been dropped and
1159 | must not be used.
1160 |
1162 | A basic method therefore looks like this: 1163 |
1164 |interface Unicorn { 1165 | void gallop (unsigned long speed, Pattern rhythm); 1166 | AudioStream speak (); 1167 | };
1169 | The arguments can be modified in a number of ways. First, an argument can be marked as
1170 | optional in which case it can be omitted when the method is invoked. If so,
1171 | it should be noted that all the arguments that are positioned after it in the list will
1172 | also be considered optional.
1173 |
enum Direction { "left", "right" }; 1175 | interface Dahut { 1176 | void turnAround (optional Direction whichWay, short degrees); 1177 | }; 1178 | // I can call someDahut.turnAround()
1180 | Note that in the above it is not indicated that the degrees argument is optional even
1181 | though it implicitly is. For readability purposes, it is best to always indicate that an argument
1182 | is optional even when it implicitly is as someone scanning the method definition quickly from the
1183 | end might not notice it.
1184 |
1186 | A problem with the above method definition is also that while my dahut can only turn left or 1187 | right, there is no indication of what happens if I omit the argument. Is it right or left? Or 1188 | random? This can be remedied in specification prose but it is better to specify a default 1189 | directly in the WebIDL, as follows: 1190 |
1191 |enum Direction { "left", "right" }; 1192 | interface Dahut { 1193 | void turnAround (optional Direction whichWay = "left", short degrees); 1194 | }; 1195 | // when I call someDahut.turnAround(), it now turns left
1197 | An argument can also be variadic. This indicates that that argument can be repeated zero or
1198 | more times when the method is called. This can only be done if it is the last argument in the arguments
1199 | list. Variadic arguments are specified by appending "..." to the type.
1200 |
interface Restaurant { 1202 | DeferredFood orderFood (DOMString... dishName); 1203 | }; 1204 | // I can now call 1205 | // resto.orderFood("spam", "spam", "spam", "spam", "spam", "spam", "spam", "spam", "spam");
1210 | WebIDL makes it possible to use overloading of methods in interfaces so that there can be 1211 | more than one method with a given name that takes a different set of arguments. This happens 1212 | simply by specifying the method more than once. 1213 |
1214 |1215 | Note that method overloading is rarely needed and should usually be avoided unless there is 1216 | no better solution, if only because the restrictions on how it can be used and the resolution 1217 | rules can be quite complex. A lot of the cases in which one may think that overloading may 1218 | be required are actually better addressed with optional arguments, variadic arguments, 1219 | or at times union types. 1220 |
1221 |
1222 | Union types are used where types can be used, but instead of specifying just one type they
1223 | indicated that the values can be of several different types. They are specified between parentheses,
1224 | with each type separated by the or keyword.
1225 |
interface Party { 1227 | void bookDate ((Date or unsigned long or DOMString) date); 1228 | }; 1229 | // I can now call with... 1230 | // a JS Date object 1231 | // party.bookDate(new Date(…)); 1232 | // time since epoch 1233 | // party.bookDate(1337998907987); 1234 | // a magic string 1235 | // party.bookDate("tomorrow");
1237 | This can be useful for return values as well: 1238 |
1239 |interface Cryptozoology { 1240 | (Dahut or Unicorn) findAnimal (); 1241 | };
1246 | Exceptions are special classes that can be used to be thrown. They are very limited interfaces 1247 | that can only define constants, and a limited kind of attribute (known as “fields”) that are 1248 | defined solely by a type and a name. 1249 |
1250 |
1251 | All exceptions automatically get a message and a name field (for which
1252 | the specification should provide values), such that those need not be explicitly specified.
1253 |
1255 | It is strongly recommended that new specifications do not define
1256 | new exceptions, but rather reuse DOMException while providing a specific name
1257 | for it.
1258 |
1260 | In addition to DOMException, a number of predefined exceptions are also available that
1261 | can be reused by specifications. This list is: Error, EvalError, RangeError,
1262 | ReferenceError, SyntaxError, TypeError and URIError
1263 | (these correspond to their JavaScript equivalents as defined in [ECMA-262]).
1264 |
1266 | Due to these constraints on usage, we have decided not to delve deeply into exceptions in this 1267 | section. 1268 |
1269 |
1273 | It is very common for asynchronous methods in Web APIs to accept callbacks, which are functions
1274 | that can be called with specific arguments when called back. These are specified with the
1275 | callback keyword, a name, a return type, and an arguments list which is similar
1276 | to that used for a method.
1277 |
1279 | Specifying callbacks is simple: 1280 |
1281 |// defining callbacks 1282 | callback LandingOk = void (unsigned long landingSpeed); 1283 | callback LandingFail = void (DOMString gravity, short casualties); 1284 | 1285 | // using callbacks 1286 | interface FlyingUnicorn { 1287 | void land (LandingOk, LandingFail); 1288 | }; 1289 | 1290 | // in code 1291 | myFlyingUnicorn.land( 1292 | function (speed) { 1293 | console.log("Successfully landed at " + speed + "kph!"); 1294 | } 1295 | , function (severity, dead) { 1296 | console.log("Crashed landing. Damage level: " + severity + ". Dead elves: " + dead); 1297 | } 1298 | );
1303 | I admit to still being largely mystified by this. Recent discussion on the mailing list does 1304 | not at all dispel that feeling. 1305 |
1310 | The features of WebIDL listed in this section have been specified for the sole purpose of describing 1311 | legacy APIs such as are found in “DOM 0”. It is particularly important that you do not use them unless 1312 | you are trying to document one such API — they must not be used for any kind of new work. The list 1313 | includes: 1314 |
1315 |1316 | Get an actual list. 1317 |
1337 | Beyond understanding the basics of WebIDL there are common and useful idiomatic constructs that 1338 | can usefully be shared. This section endeavours to progressively accrue some more, so please send 1339 | yours in! 1340 |
1341 |1344 | We had talked of Yehuda writing this section. I will see if I can hunt him down. 1345 |
1350 | Asynchronous method calls require a point of reentry in the form of a callback. There are multiple 1351 | ways of specifying these, none of which is consensually consider the one and only approach. 1352 |
1353 |1354 | One is success/error callback functions, as used in [GEOLOCATION-API]. It takes the shape of a method 1355 | call that accepts a callback function for success followed by one for errors. There may be arguments 1356 | before the success callback, and the error callback is usually optional. 1357 |
1358 |navigator.petKitten( 1359 | function() { 1360 | console.log("kitten is purring"); 1361 | }, 1362 | function(err) { 1363 | console.log("kitten hates you, because: " + err); 1364 | } 1365 | );
1367 | Downsides of this approach are that it is a little more verbose and does not encourage error handling. It 1368 | also lacks in extensibility since adding new arguments to the method will typically require placing them 1369 | after the optional error handler — making them de facto optional. Likewise, extending it to support not 1370 | just callbacks for success and error but also for other situations that may warrant reporting information 1371 | back (e.g. progress being made) is clumsy at best. Since it is only used in [GEOLOCATION-API] it does not 1372 | appear to be a required pattern for consistency. 1373 |
1374 |Another approach is to define new DOM Events that are called when an underlying system changes.
1375 |window.addEventListener("kittenpurr", function() {
1376 | console.log("kitten is purring");
1377 | }, false);On one hand, most developers are quite familiar with DOM Events, and they make it easy to have several handlers for a given event.
1379 |On the other hand, using DOM Events for listening to systems that need specific activation (e.g. powering up) are usually not a good idea: addEventListener is supposed to be free from side effects, and associating activation code with it breaks that promise. The [DEVICE-ORIENTATION] specification is as such an anti-pattern that should not be replicated. See some discussions on side effects in addEventListener.
1381 | Another is returning an EventTarget object, as pioneered by [XMLHTTPREQUEST]. This consists 1382 | in opening a request that returns an object on which event handlers can be registered, then starting 1383 | that request with a specific call. 1384 |
1385 |var kitpet = navigator.openKittenPetting(); 1386 | 1387 | kitpet.onpurr = function () { 1388 | console.log("kitten is purring"); 1389 | }; 1390 | 1391 | kitpet.ondrool = function () { 1392 | console.log("kitten is now drooling"); 1393 | }; 1394 | 1395 | kitpet.onviciousbite = function () { 1396 | console.log("kitten bit your hand off"); 1397 | }; 1398 | 1399 | kitpet.start();
1401 | The approach is the most verbose and does not encourage error handling, but it does have the advantages 1402 | of being trivially extended for greater feedback, and of being familiar to users of [XMLHTTPREQUEST] 1403 | (and now [INDEXEDDB]). It can, however, feel rather overkill if extensions for additional events does 1404 | not seem likely. 1405 |
1406 |
1407 | Then there are NodeJS style callbacks. For every asynchronous operation, every callback takes
1408 | as first parameter an error object which is null when the operation was a success, followed
1409 | by as many pieces of information as are needed to capture the success.
1410 |
navigator.petKitten(function (err, state) { 1412 | if (err) console.log("kitten mauled your face with extreme prejudice"); 1413 | else console.log("kittem manifest joy by " + state); 1414 | });
1416 | The primary value in this approach is that it puts errors "right there" where they are likely to be handled. 1417 | Its consistency is also very helpful in creating chains of callbacks, a feature that proves precious 1418 | when multiple callbacks can become nested. The major downside of this approach is that it is currently 1419 | not used in the rest of stack and therefore probably only familiar to NodeJS developers (which granted 1420 | represent a fast-growing proportion of the general web developer population). 1421 |
1422 |1423 | Finally there are promises. These are similar to returning an EventTarget object but 1424 | more generic and use a slightly different style. 1425 |
1426 |var promise = navigator.petKitten(); 1427 | promise.then(function () { 1428 | console.log("kitten is purring"); 1429 | }).fail(function () { 1430 | console.log("kitten bit your hand off"); 1431 | }).run();
1433 | There is elegance and flexibility in promises, but to date they are used in no Web specification. 1434 |
1435 |1439 | This ought to be relatively straightforward. 1440 |
1444 | Should we have a section on specifying with Web Intents? 1445 |
1449 | A number of other sources provide information that can be useful in writing specifications. This list 1450 | is by no means complete, suggestions are welcome. 1451 |
1452 |1453 | Anne maintains a document called How To Spec on 1454 | how to specify certain features of the platform. Some parts of it are tailored to the specific markup 1455 | that is used when processing specifications with Anolis, but the general advice is sound. 1456 |
1457 |1461 | Many thanks to the following people, in no particular order: Cameron McCormack, Marcos Càceres, 1462 | Andreas Gal, Rick Waldron. 1463 |
1464 |