├── 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 | Web API Design Cookbook 6 | 31 | 32 | 33 | 34 |
35 |

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 |
40 |
41 |

42 | This document has been developed progressively over time with help from the WebApps WG 43 | and the ScriptLib CG. 44 |

45 |
46 |
47 |

Introduction

48 |

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 |
75 |
76 |

Basics

77 |

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 |
82 |

Casing

83 |

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 |

125 |
 126 |           SomethingHappenedEvent
 127 |           obj.addEventListener("somethinghappened", handler, false);
 128 |           el.onsomethinghappened = handler;
 129 |         
130 |
131 |
132 |

Using attributes

133 |

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 |

143 |
 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 |
160 |
Modifies the value
161 |
162 | In some cases there may be relations between objects such that when one is set as the value of another 163 | it needs to be updated to reflect its end of the relationship as well. For instance, when setting 164 | 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 |
167 |
Asynchronous access
168 |
169 | In some instances, an object may be available but it is best if some of its fields are only loaded 170 | lazily because they require time to compute. The canonical example is the size of a file. If processing 171 | objects representing files, making the size of each file available immediately will be costly, and 172 | acquiring it synchronously upon attribute access would lead to poor performance (those 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 |
178 |
179 |
180 |
181 |

When To Be Asynchronous

182 |

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 |

209 |

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 |
217 |
218 |

Don't Use Numerical Constants

219 |

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 |
273 |
274 |

Namespacing and Prefixing

275 |

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 |
297 |
298 |
299 |

WebIDL Constructs

300 |

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 |
311 |

Using Dictionaries

312 |

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 |

318 |

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 |
325 |

When to use

326 |

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 |
452 |
453 |

How to use

454 |
455 |

Use sequence type

456 |

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 |
490 |
491 |

Inheritance

492 |

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 |
506 |
507 |

Optional Dictionary Member and Defaulting Values

508 |

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 |

525 |
526 |
527 |

Attribute, Constant, or Exception fields

528 |

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 |
552 |
553 |

No Nullable Dictionary

554 |

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 |

559 |
 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 |
574 |
575 |

Partial Dictionary

576 |

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 |
590 |
591 |

Extended attributes

592 |

593 | Only the following two extended attributes are applicable to dictionary 594 | members: [Clamp], [EnforceRange]. 595 |

596 |
597 |
598 |
599 |
600 |

Interfaces

601 |

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 |

607 |
 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 |
635 |

Partial Interfaces

636 |

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 |
    641 |
  • 642 | The interface may be defined in another specification and extended by a new one. This is a 643 | relatively common pattern for core interfaces off which things may be attached (e.g. 644 | Navigator). It's an important aspect of the Web platform's core extensibility. 645 |
  • 646 |
  • 647 | Some editors occasionally consider it useful to split the definition of an interface into 648 | smaller pieces as part of the same document. This is sometimes done to make the specification 649 | clearer (though the advantages here are debatable), or sometimes because part of the interface 650 | is optional. 651 |
  • 652 |
653 |

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 |

664 |
 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 |

682 |

683 | The previous example can therefore best be rewritten as: 684 |

685 |
 686 |             partial interface Navigator {
 687 |                 Unicorns findUnicorns (DOMString name);
 688 |             };
 689 |           
690 |
691 |
692 |

[Constructor] and [NamedConstructor]

693 |

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 |

711 |
 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 |

720 |

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 |

746 |
 747 |             // new Rose();
 748 |             // new Rose("pink");
 749 |             [NamedConstructor=Rose,
 750 |              NamedConstructor=Rose(DOMString colour)]
 751 |             interface RosoideaeRosa {
 752 |                 // ...
 753 |             };
 754 |           
755 |
756 |
757 |

[ArrayClass]

758 |

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 |

764 |

765 | These keywords are rather straightforward: 766 |

767 |
    768 |
  • 769 | The 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 |
  • 772 |
  • 773 | The 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 |
  • 779 |
  • 780 | Finally, the deleter defines what happens when a value is removed from the array, for instance 781 | with: shoppingList.pop();. 782 |
  • 783 |
784 |

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 |

800 |
 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 |
810 |
811 |

When to use [NoInterfaceObject]

812 |

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 |

817 |

818 | In practice it should only be used for abstract interfaces that enrich a concrete one through the 819 | implements statement. 820 |

821 |
822 |
823 |
824 |

Constants

825 |

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 |

830 |

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 |

835 |
 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 |
848 |
849 |

Enums

850 |

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 |

859 |
 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 |
878 |
879 |

Attributes

880 |

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 |

884 |

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 |

904 |

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 |

909 |
 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 |

921 |
 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 |
938 |
939 |

Methods

940 |

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 |

950 |
 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 |

959 |

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 |

969 |

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 |

984 |
 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 |

997 |

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 |

1015 |
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 |
1023 |
1024 |

Overloading and Union Types

1025 |

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 |

1042 |
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 |
1063 |
1064 |

Exceptions

1065 |

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 |

1074 |

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 |

1079 |

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 |

1085 |

1086 | Due to these constraints on usage, we have decided not to delve deeply into exceptions in this 1087 | section. 1088 |

1089 |
1090 |
1091 |

Callbacks

1092 |

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 |

1098 |

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 |
1122 |
1123 |

Sequences and Arrays

1124 |

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 |
1129 |
1130 |

WebIDL Legacy Features

1131 |

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 |
1147 | 1155 |
1156 |
1157 |

Common Patterns

1158 |

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 |
1164 |

Ensuring that Feature Detection is Possible

1165 |

1166 | We had talked of Yehuda writing this section. I will see if I can hunt him down. 1167 |

1168 |
1169 |
1170 |

Specifying Callbacks

1171 |

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.

1206 |

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 |

1239 |
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 |
1268 |
1269 |

Specifying Events

1270 |

1271 | This ought to be relatively straightforward. 1272 |

1273 |
1274 |
1275 |

1276 | Should we have a section on specifying with Web Intents? 1277 |

1278 |
1279 |

Other Sources

1280 |

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 |
1290 |
1291 |

Acknowledgements

1292 |

1293 | Many thanks to the following people, in no particular order: Cameron McCormack, Marcos Càceres, 1294 | Andreas Gal, Rick Waldron. 1295 |

1296 |
1297 | 1298 | 1299 | -------------------------------------------------------------------------------- /FPWD/Overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Web API Design Cookbook 6 | 7 | 8 | 202 |
203 |

204 | 205 | W3C 206 | 207 |

208 |

Web API Design Cookbook

209 | 210 |

W3C Working Group Note 02 October 2012

211 |
212 | 213 |
This version:
214 |
http://www.w3.org/TR/2012/NOTE-api-design-20121002/
215 |
Latest published version:
216 |
http://www.w3.org/TR/api-design/
217 | 218 | 219 |
Latest editor's draft:
220 |
http://darobin.github.com/api-design-cookbook/
221 | 222 | 223 | 224 | 225 | 226 | 227 |
Editors:
228 |
Robin Berjon, Robineko
229 |
송정기(Jungkee Song), Samsung Electronics Co., Ltd.
230 | 231 | 232 |
233 | 234 | 235 | 236 | 237 | 238 | 250 | 251 | 252 |
253 |
254 |

Abstract

255 |

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 |

Status of This Document

260 | 261 | 262 | 263 |

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 |

Table of Contents

317 | 318 |
319 |

1. Introduction

320 |

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 |
347 |
348 |

2. Basics

349 |

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 |
354 |

2.1 Casing

355 |

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 |
Example 1
FascinatingDocument
 365 | Node
 366 | SomethingBadHappened
 367 | XMLHttpRequest
368 |

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 |
Example 2
obj.thisIsAnAttribute
 374 | obj.simple
 375 | obj.doSomethingNow()
 376 | obj.run()
377 |

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 |
Example 3
REALLY_STUPID_ERROR
 384 | PASTA_SAUCE_TYPE
385 |

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 |

391 |
Example 4
SomethingHappenedEvent
 392 | obj.addEventListener("somethinghappened", handler, false);
 393 | el.onsomethinghappened = handler;
394 |
395 |
396 |

2.2 Using attributes

397 |

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 |

407 |
Example 5: Bad usage
var len = obj.getLength();
 408 | arr.setLength(num);
409 |

410 | But rather: 411 |

412 |
Example 6
var len = obj.length;
 413 | arr.length = num;
414 |

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 |
420 |
Modifies the value
421 |
422 | In some cases there may be relations between objects such that when one is set as the value of another 423 | it needs to be updated to reflect its end of the relationship as well. For instance, when setting 424 | 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 |
427 |
Asynchronous access
428 |
429 | In some instances, an object may be available but it is best if some of its fields are only loaded 430 | lazily because they require time to compute. The canonical example is the size of a file. If processing 431 | objects representing files, making the size of each file available immediately will be costly, and 432 | acquiring it synchronously upon attribute access would lead to poor performance (those 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 |
438 |
439 |
440 |
441 |

2.3 When To Be Asynchronous

442 |

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 |

469 |

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 |
477 |
478 |

2.4 Don't Use Numerical Constants

479 |

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 |
Example 7
if (foo.type === 17) doSomething();
489 |

490 | Then there is the comment you have to paste every time: 491 |

492 |
Example 8
// check if this of the fungible type
 493 | if (foo.type === 17) doSomething();
494 |

495 | Or we can have the uselessly long variant: 496 |

497 |
Example 9
if (foo.type === FooInterface.FUNGIBLE_TYPE) doSomething();
498 |

499 | And there's the meh option: 500 |

501 |
Example 10
var isFungible = FooInterface.FUNGIBLE_TYPE;
 502 | // ...
 503 | if (foo.type === isFungible) doSomething();
504 |

505 | None of the above is as simple, terse, and self-documenting as: 506 |

507 |
Example 11
if (foo.type === "fungible") doSomething();
508 |

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 |
523 |
524 |

2.5 Namespacing and Prefixing

525 |

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 |
Example 12
xhr.acmePassiveFTP();
 536 | document.acmeDigitalSignature;
537 |

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 |
Example 13
var toaster = new acme.Toaster();
542 |
543 |
544 |
545 |

3. WebIDL Constructs

546 |

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 |
557 |

3.1 Using Dictionaries

558 |

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 |

564 |

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 |
571 |

3.1.1 When to use

572 |

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 |
Example 14
// 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);
602 |

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 |
Example 15
// 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 |                       });
643 |

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 |
Example 16
// 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);
685 |

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 |
692 |
693 |

3.1.2 How to use

694 |
695 |
3.1.2.1 Use sequence type
696 |

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 |
Example 17
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 | };
711 |

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 |
Example 18
dictionary DoNotUseLikeThis {
 719 |     IntentParameters[] params;
 720 | };
 721 | 
 722 | dictionary UseLikeThis {
 723 |     sequence<IntentParameters> params;
 724 | };
725 |
726 |
727 |
3.1.2.2 Inheritance
728 |

729 | Dictionaries support inheritance as interface does. A dictionary must not 730 | be defined having a cycle in its hierarchy. 731 |

732 |
Example 19
dictionary Novel : Book {
 733 |     // dictionary-members...
 734 | };
 735 | 
 736 | dictionary Book : Novel {
 737 |     // dictionary-members...
 738 | };
739 |
740 |
741 |
3.1.2.3 Optional Dictionary Member and Defaulting Values
742 |

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 |
Example 20
dictionary PropertyBag {
 749 |     DOMString iAmRequired = "default";
 750 |     DOMString iAmOptional;
 751 | };
752 |

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 |

757 |
758 |
759 |
3.1.2.4 Attribute, Constant, or Exception fields
760 |

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 |
Example 21
interface HasWrongUsage {
 766 |     readonly attribute IAmDictionary dict;
 767 | };
768 |

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 |
Example 22
interface HasRightUsage {
 773 |     readonly attribute any dict;
 774 | };
775 |

776 | In your spec, write "dict is of type IAmDictionary dictionary as defined in 777 | [REFERENCE]". 778 |

779 |
780 |
781 |
3.1.2.5 No Nullable Dictionary
782 |

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 |

787 |
Example 23
dictionary HasWrongUsage {
 788 |     IAmDictionary? dict;
 789 | };
 790 | 
 791 | dictionary HasRightUsage {
 792 |     IAmDictionary dict;
 793 | };
794 |

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 |
800 |
801 |
3.1.2.6 Partial Dictionary
802 |

803 | As with interfaces, the IDL for dictionaries can be split into multiple 804 | parts by using partial dictionary definitions. 805 |

806 |
Example 24
dictionary SomeDictionary {
 807 |     // dictionary-members…
 808 | };
 809 | 
 810 | partial dictionary SomeDictionary {
 811 |     // dictionary-members…
 812 | };
813 |
814 |
815 |
3.1.2.7 Extended attributes
816 |

817 | Only the following two extended attributes are applicable to dictionary 818 | members: [Clamp], [EnforceRange]. 819 |

820 |
821 |
822 |
823 |
824 |

3.2 Interfaces

825 |

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 |

831 |
Example 25
interface BeerBottle {
 832 |     // body goes here
 833 | };
834 |

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 |
Example 26
interface BeerBottle : Bottle {
 843 |     // body
 844 | };
 845 | interface Spork : Spoon, Fork {
 846 |     // body
 847 | };
848 |

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 |
855 |

3.2.1 Partial Interfaces

856 |

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 |
    861 |
  • 862 | The interface may be defined in another specification and extended by a new one. This is a 863 | relatively common pattern for core interfaces off which things may be attached (e.g. 864 | Navigator). It's an important aspect of the Web platform's core extensibility. 865 |
  • 866 |
  • 867 | Some editors occasionally consider it useful to split the definition of an interface into 868 | smaller pieces as part of the same document. This is sometimes done to make the specification 869 | clearer (though the advantages here are debatable), or sometimes because part of the interface 870 | is optional. 871 |
  • 872 |
873 |

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 |

884 |
Example 27
// 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");
891 |

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 |

900 |

901 | The previous example can therefore best be rewritten as: 902 |

903 |
Example 28
partial interface Navigator {
 904 |     Unicorns findUnicorns (DOMString name);
 905 | };
906 |
907 |
908 |

3.2.2 [Constructor] and [NamedConstructor]

909 |

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 |

927 |
Example 29
new Node()
 928 | // TypeError: Node is not a constructor
929 |

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 |

934 |

935 | An example might make this clearer: 936 |

937 |
Example 30
// 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 | };
952 |

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 |

958 |
Example 31
// new Rose();
 959 | // new Rose("pink");
 960 | [NamedConstructor=Rose,
 961 |  NamedConstructor=Rose(DOMString colour)]
 962 | interface RosoideaeRosa {
 963 |     // ...
 964 | };
965 |
966 |
967 |

3.2.3 [ArrayClass]

968 |

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 |

974 |

975 | These keywords are rather straightforward: 976 |

977 |
    978 |
  • 979 | The 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 |
  • 982 |
  • 983 | The 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 |
  • 989 |
  • 990 | Finally, the deleter defines what happens when a value is removed from the array, for instance 991 | with: shoppingList.pop();. 992 |
  • 993 |
994 |

995 | Assembling this all together looks like this: 996 |

997 |
Example 32
[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 | };
1005 |

1006 | The above give you a ShoppingList interface. You could use it as follows: 1007 |

1008 |
Example 33
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"));
1015 |
1016 |
1017 |

3.2.4 When to use [NoInterfaceObject]

1018 |

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 |

1023 |

1024 | In practice it should only be used for abstract interfaces that enrich a concrete one through the 1025 | implements statement. 1026 |

1027 |
1028 |
1029 |
1030 |

3.3 Constants

1031 |

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 |

1036 |

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 |

1041 |
Example 34
interface Something {
1042 |     const short ANSWER = 42;
1043 |     const boolean WRONG = false;
1044 |     const unsigned long BIG_NUMBER = Infinity;
1045 | };
1046 |

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 |
1052 |
1053 |

3.4 Enums

1054 |

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 |

1063 |
Example 35
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");
1075 |

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 |
1080 |
1081 |

3.5 Attributes

1082 |

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 |

1086 |

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 |
Example 36
interface Name {
1092 |     attribute DOMString givenName;
1093 |     attribute DOMString familyName;
1094 | };
1095 | interface Person {
1096 |     readonly attribute Name fullName;
1097 | };
1098 |

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 |

1104 |

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 |

1109 |
Example 37
// set the debug flag globally
1110 | interface Runtime {
1111 |     static attribute boolean debug;
1112 | };
1113 |

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 |

1119 |
Example 38
// another Person interface
1120 | [Constructor(DOMString name, short age)]
1121 | interface Unicorn {
1122 |     stringifier attribute DOMString fullName;
1123 |     attribute short age;
1124 | };
1125 |

1126 | This can then be used with the following code: 1127 |

1128 |
Example 39
var uni = new Unicorn("Tab", 42);
1129 | document.getElementById("output-unicorn").textContent = uni;
1130 | // that element now has content "Tab";
1131 |
1132 |
1133 |

3.6 Methods

1134 |

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 |

1144 |
Example 40
interface KittenFactory {
1145 |     static Kitten makeMeANewKitten (DOMString name);
1146 | };
1147 | // var billie = KittenFactory.makeMeANewKitten("Billie The Cat");
1148 |

1149 | The return type can be any type or void, which indicates that nothing is returned. 1150 |

1151 |

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 |

1161 |

1162 | A basic method therefore looks like this: 1163 |

1164 |
Example 41
interface Unicorn {
1165 |     void gallop (unsigned long speed, Pattern rhythm);
1166 |     AudioStream speak ();
1167 | };
1168 |

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 |

1174 |
Example 42
enum Direction { "left", "right" };
1175 | interface Dahut {
1176 |     void turnAround (optional Direction whichWay, short degrees);
1177 | };
1178 | // I can call someDahut.turnAround()
1179 |

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 |

1185 |

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 |
Example 43
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
1196 |

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 |

1201 |
Example 44
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");
1206 |
1207 |
1208 |

3.7 Overloading and Union Types

1209 |

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 |

1226 |
Example 45
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");
1236 |

1237 | This can be useful for return values as well: 1238 |

1239 |
Example 46
interface Cryptozoology {
1240 |     (Dahut or Unicorn) findAnimal ();
1241 | };
1242 |
1243 |
1244 |

3.8 Exceptions

1245 |

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 |

1254 |

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 |

1259 |

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 |

1265 |

1266 | Due to these constraints on usage, we have decided not to delve deeply into exceptions in this 1267 | section. 1268 |

1269 |
1270 |
1271 |

3.9 Callbacks

1272 |

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 |

1278 |

1279 | Specifying callbacks is simple: 1280 |

1281 |
Example 47
// 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 | );
1299 |
1300 |
1301 |

3.10 Sequences and Arrays

1302 |
Issue 1

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 |

1306 |
1307 |
1308 |

3.11 WebIDL Legacy Features

1309 |

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 |
Issue 2

1316 | Get an actual list. 1317 |

1318 | 1324 |
1325 | 1333 |
1334 |
1335 |

4. Common Patterns

1336 |

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 |
1342 |

4.1 Ensuring that Feature Detection is Possible

1343 |
Issue 3

1344 | We had talked of Yehuda writing this section. I will see if I can hunt him down. 1345 |

1346 |
1347 |
1348 |

4.2 Specifying Callbacks

1349 |

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 |
Example 48
navigator.petKitten(
1359 |     function() {
1360 |         console.log("kitten is purring");
1361 |     },
1362 |     function(err) {
1363 |         console.log("kitten hates you, because: " + err);
1364 |     }
1365 | );
1366 |

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 |
Example 49
window.addEventListener("kittenpurr", function() {
1376 |     console.log("kitten is purring");
1377 | }, false);
1378 |

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.

1380 |

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 |
Example 50
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();
1400 |

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 |

1411 |
Example 51
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 | });
1415 |

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 |
Example 52
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();
1432 |

1433 | There is elegance and flexibility in promises, but to date they are used in no Web specification. 1434 |

1435 |
1436 |
1437 |

4.3 Specifying Events

1438 |
Issue 4

1439 | This ought to be relatively straightforward. 1440 |

1441 |
1442 |
1443 |
Issue 5

1444 | Should we have a section on specifying with Web Intents? 1445 |

1446 |
1447 |

A. Other Sources

1448 |

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 |
1458 |
1459 |

B. Acknowledgements

1460 |

1461 | Many thanks to the following people, in no particular order: Cameron McCormack, Marcos Càceres, 1462 | Andreas Gal, Rick Waldron. 1463 |

1464 |
1465 | 1466 | 1467 |

C. References

C.1 Normative references

[WEBIDL]
Cameron McCormack. Web IDL. 27 September 2011. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2011/WD-WebIDL-20110927/ 1468 |

C.2 Informative references

[DEVICE-ORIENTATION]
Steve Block, Andrei Popescu. DeviceOrientation Event Specification. 1 December 2011. Last Call Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2011/WD-orientation-event-20111201/ 1469 |
[ECMA-262]
ECMAScript Language Specification. June 2011. URL: http://www.ecma-international.org/publications/standards/Ecma-262.htm 1470 |
[GEOLOCATION-API]
Andrei Popescu. Geolocation API Specification. 22 December 2008. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2008/WD-geolocation-API-20081222/ 1471 |
[INDEXEDDB]
Nikunj Mehta, Jonas Sicking, Eliot Graff, Andrei Popescu, Jeremy Orlow. Indexed Database API. April 2011. Working Draft. (Work in progress.) URL: http://www.w3.org/TR/IndexedDB/ 1472 |
[XMLHTTPREQUEST]
Anne van Kesteren. The XMLHttpRequest Object. 15 April 2008. W3C Working Draft. (Work in progress.) URL: http://www.w3.org/TR/2008/WD-XMLHttpRequest-20080415 1473 |
--------------------------------------------------------------------------------