selenium-webdriver 0.0.17 → 0.0.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
  2. data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
  3. data/chrome/src/extension/background.js +64 -48
  4. data/chrome/src/extension/content_script.js +253 -132
  5. data/chrome/src/extension/manifest-nonwin.json +1 -1
  6. data/chrome/src/extension/manifest-win.json +1 -1
  7. data/chrome/src/extension/utils.js +8 -8
  8. data/chrome/src/rb/lib/selenium/webdriver/chrome.rb +9 -0
  9. data/chrome/src/rb/lib/selenium/webdriver/chrome/bridge.rb +38 -280
  10. data/chrome/src/rb/lib/selenium/webdriver/chrome/command_executor.rb +119 -117
  11. data/chrome/src/rb/lib/selenium/webdriver/chrome/launcher.rb +36 -26
  12. data/common/src/js/abstractcommandprocessor.js +9 -11
  13. data/common/src/js/command.js +159 -83
  14. data/common/src/js/core/RemoteRunner.html +2 -2
  15. data/common/src/js/core/TestRunner-splash.html +3 -3
  16. data/common/src/js/core/TestRunner.html +5 -17
  17. data/common/src/js/core/scripts/htmlutils.js +4208 -2506
  18. data/common/src/js/core/scripts/selenium-api.js +2 -2
  19. data/common/src/js/core/scripts/selenium-browserbot.js +66 -58
  20. data/common/src/js/core/scripts/selenium-version.js +1 -1
  21. data/common/src/js/localcommandprocessor.js +5 -19
  22. data/common/src/js/testcase.js +2 -0
  23. data/common/src/js/webdriver.js +63 -93
  24. data/common/src/js/webelement.js +40 -42
  25. data/common/src/rb/lib/selenium/webdriver.rb +23 -14
  26. data/common/src/rb/lib/selenium/webdriver/bridge_helper.rb +8 -35
  27. data/common/src/rb/lib/selenium/webdriver/child_process.rb +2 -0
  28. data/common/src/rb/lib/selenium/webdriver/core_ext/dir.rb +1 -0
  29. data/common/src/rb/lib/selenium/webdriver/core_ext/string.rb +5 -0
  30. data/common/src/rb/lib/selenium/webdriver/driver.rb +20 -15
  31. data/common/src/rb/lib/selenium/webdriver/driver_extensions/takes_screenshot.rb +7 -2
  32. data/common/src/rb/lib/selenium/webdriver/element.rb +11 -2
  33. data/common/src/rb/lib/selenium/webdriver/error.rb +9 -5
  34. data/common/src/rb/lib/selenium/webdriver/keys.rb +1 -2
  35. data/common/src/rb/lib/selenium/webdriver/navigation.rb +16 -0
  36. data/common/src/rb/lib/selenium/webdriver/options.rb +32 -0
  37. data/common/src/rb/lib/selenium/webdriver/platform.rb +17 -1
  38. data/firefox/prebuilt/Win32/Release/webdriver-firefox.dll +0 -0
  39. data/firefox/src/extension/components/dispatcher.js +492 -0
  40. data/firefox/src/extension/components/driver-component.js +4 -1
  41. data/firefox/src/extension/components/errorcode.js +70 -0
  42. data/firefox/src/extension/components/firefoxDriver.js +173 -154
  43. data/firefox/src/extension/components/nsCommandProcessor.js +171 -132
  44. data/firefox/src/extension/components/promptService.js +5 -5
  45. data/firefox/src/extension/components/request.js +219 -0
  46. data/firefox/src/extension/components/response.js +276 -0
  47. data/firefox/src/extension/components/session.js +281 -0
  48. data/firefox/src/extension/components/sessionstore.js +226 -0
  49. data/firefox/src/extension/components/socketListener.js +350 -100
  50. data/firefox/src/extension/components/utils.js +166 -98
  51. data/firefox/src/extension/components/webdriverserver.js +9 -5
  52. data/firefox/src/extension/components/wrappedElement.js +189 -166
  53. data/firefox/src/extension/install.rdf +1 -1
  54. data/firefox/src/rb/lib/selenium/webdriver/firefox.rb +2 -0
  55. data/firefox/src/rb/lib/selenium/webdriver/firefox/binary.rb +39 -33
  56. data/firefox/src/rb/lib/selenium/webdriver/firefox/bridge.rb +7 -421
  57. data/firefox/src/rb/lib/selenium/webdriver/firefox/extension_connection.rb +7 -64
  58. data/firefox/src/rb/lib/selenium/webdriver/firefox/launcher.rb +2 -3
  59. data/firefox/src/rb/lib/selenium/webdriver/firefox/profile.rb +54 -10
  60. data/firefox/src/rb/lib/selenium/webdriver/firefox/profiles_ini.rb +2 -0
  61. data/firefox/src/rb/lib/selenium/webdriver/firefox/util.rb +6 -0
  62. data/jobbie/prebuilt/Win32/Release/InternetExplorerDriver.dll +0 -0
  63. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
  64. data/jobbie/src/rb/lib/selenium/webdriver/ie.rb +2 -0
  65. data/jobbie/src/rb/lib/selenium/webdriver/ie/bridge.rb +38 -13
  66. data/jobbie/src/rb/lib/selenium/webdriver/ie/lib.rb +9 -2
  67. data/jobbie/src/rb/lib/selenium/webdriver/ie/util.rb +5 -0
  68. data/remote/client/src/rb/lib/selenium/webdriver/remote.rb +2 -0
  69. data/remote/client/src/rb/lib/selenium/webdriver/remote/bridge.rb +42 -38
  70. data/remote/client/src/rb/lib/selenium/webdriver/remote/commands.rb +56 -47
  71. data/remote/client/src/rb/lib/selenium/webdriver/remote/default_http_client.rb +26 -26
  72. data/remote/client/src/rb/lib/selenium/webdriver/remote/patron_http_client.rb +58 -0
  73. data/remote/client/src/rb/lib/selenium/webdriver/remote/response.rb +10 -12
  74. data/remote/client/src/rb/lib/selenium/webdriver/remote/server_error.rb +2 -17
  75. metadata +44 -23
  76. data/common/src/js/context.js +0 -58
  77. data/firefox/src/extension/components/context.js +0 -37
@@ -0,0 +1,492 @@
1
+ /*
2
+ Copyright 2007-2010 WebDriver committers
3
+ Copyright 2007-2010 Google Inc.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ */
17
+
18
+
19
+ /**
20
+ * Dispatches commands received by the WebDriver server.
21
+ * @constructor
22
+ */
23
+ function Dispatcher() {
24
+ this.resources_ = [];
25
+ this.init_();
26
+ }
27
+
28
+
29
+ /**
30
+ * Utility function used to respond to a command that is recognised, but not
31
+ * implemented. Returns a 501.
32
+ * @param {Request} The request to respond to.
33
+ * @param {Response} Class used to send the response.
34
+ */
35
+ Dispatcher.notImplemented = function(request, response) {
36
+ response.sendError(Response.NOT_IMPLEMENTED, 'Unsupported command',
37
+ 'text/plain');
38
+ };
39
+
40
+
41
+ /**
42
+ * Returns a function that translates a WebDriver HTTP request to a legacy
43
+ * command.
44
+ * @param {string} name The legacy command name.
45
+ * @return {function(Request, Response)} The translation function.
46
+ * @private
47
+ */
48
+ Dispatcher.executeAs = function(name) {
49
+ return function(request, response) {
50
+ var json = {
51
+ 'name': name,
52
+ 'sessionId': {
53
+ 'value': request.getAttribute('sessionId')
54
+ },
55
+ 'parameters': JSON.parse(request.getBody() || '{}')
56
+ };
57
+
58
+ // All request attributes, excluding sessionId and parameters also passed
59
+ // the body payload, should be added to the parameters.
60
+ var attributeNames = request.getAttributeNames();
61
+ for (var attrName; attrName = attributeNames.shift();) {
62
+ if (attrName != 'sessionId' && !json['parameters'][attrName]) {
63
+ json['parameters'][attrName] = request.getAttribute(attrName);
64
+ }
65
+ }
66
+
67
+ var jsonString = JSON.stringify(json);
68
+ var callback = function(jsonResponseString) {
69
+ var jsonResponse = JSON.parse(jsonResponseString);
70
+ // Going to need more granularity here I think.
71
+ if (jsonResponse.status != ErrorCode.SUCCESS) {
72
+ response.setStatus(Response.INTERNAL_ERROR);
73
+ }
74
+
75
+ response.setContentType('application/json');
76
+ response.setBody(jsonResponseString);
77
+ response.commit();
78
+ };
79
+
80
+ // Dispatch the command.
81
+ Components.classes['@googlecode.com/webdriver/command-processor;1'].
82
+ getService(Components.interfaces.nsICommandProcessor).
83
+ execute(jsonString, callback);
84
+ };
85
+ };
86
+
87
+
88
+ /**
89
+ * Creates a special handler for translating a request for a new session to a
90
+ * request understood by the legacy nsICommandProcessor.
91
+ */
92
+ Dispatcher.translateNewSession = function() {
93
+ return function(request, response) {
94
+ var callback = function(jsonResponseString) {
95
+ var jsonResponse = JSON.parse(jsonResponseString);
96
+ // Going to need more granularity here I think.
97
+ if (jsonResponse.status != 0) {
98
+ response.sendError(Response.INTERNAL_ERROR,
99
+ jsonResponseString, 'application/json');
100
+ } else {
101
+ var url = request.getRequestUrl();
102
+ response.setStatus(Response.SEE_OTHER);
103
+ response.setHeader('Location',
104
+ url.scheme + '://' + url.hostPort + url.path + '/' +
105
+ jsonResponse.value);
106
+ response.commit();
107
+ }
108
+ };
109
+
110
+ // Dispatch the command.
111
+ Components.classes['@googlecode.com/webdriver/command-processor;1'].
112
+ getService(Components.interfaces.nsICommandProcessor).
113
+ execute('{"name":"newSession"}', callback);
114
+ };
115
+ };
116
+
117
+
118
+ /**
119
+ * Initializes the command bindings for this dispatcher.
120
+ * @private
121
+ */
122
+ Dispatcher.prototype.init_ = function() {
123
+ this.bind_('/config/drivers'). // Recognised, but not supported.
124
+ on(Request.Method.POST, Dispatcher.notImplemented);
125
+
126
+ this.bind_('/session').
127
+ on(Request.Method.POST, Dispatcher.translateNewSession());
128
+
129
+ this.bind_('/session/:sessionId').
130
+ on(Request.Method.GET, Dispatcher.executeAs('getSessionCapabilities')).
131
+ on(Request.Method.DELETE, Dispatcher.executeAs('quit'));
132
+
133
+ this.bind_('/session/:sessionId/window_handle').
134
+ on(Request.Method.GET, Dispatcher.executeAs('getCurrentWindowHandle'));
135
+ this.bind_('/session/:sessionId/window_handles').
136
+ on(Request.Method.GET, Dispatcher.executeAs('getWindowHandles'));
137
+
138
+ this.bind_('/session/:sessionId/speed').
139
+ on(Request.Method.GET, Dispatcher.executeAs('getSpeed')).
140
+ on(Request.Method.POST, Dispatcher.executeAs('setSpeed'));
141
+
142
+ this.bind_('/session/:sessionId/url').
143
+ on(Request.Method.GET, Dispatcher.executeAs('getCurrentUrl')).
144
+ on(Request.Method.POST, Dispatcher.executeAs('get'));
145
+
146
+ this.bind_('/session/:sessionId/forward').
147
+ on(Request.Method.POST, Dispatcher.executeAs('goForward'));
148
+ this.bind_('/session/:sessionId/back').
149
+ on(Request.Method.POST, Dispatcher.executeAs('goBack'));
150
+ this.bind_('/session/:sessionId/refresh').
151
+ on(Request.Method.POST, Dispatcher.executeAs('refresh'));
152
+
153
+ this.bind_('/session/:sessionId/execute').
154
+ on(Request.Method.POST, Dispatcher.executeAs('executeScript'));
155
+
156
+ this.bind_('/session/:sessionId/source').
157
+ on(Request.Method.GET, Dispatcher.executeAs('getPageSource'));
158
+ this.bind_('/session/:sessionId/title').
159
+ on(Request.Method.GET, Dispatcher.executeAs('getTitle'));
160
+
161
+ this.bind_('/session/:sessionId/element').
162
+ on(Request.Method.POST, Dispatcher.executeAs('findElement'));
163
+ this.bind_('/session/:sessionId/elements').
164
+ on(Request.Method.POST, Dispatcher.executeAs('findElements'));
165
+ this.bind_('/session/:sessionId/element/active').
166
+ on(Request.Method.POST, Dispatcher.executeAs('getActiveElement'));
167
+
168
+ this.bind_('/session/:sessionId/element/:id').
169
+ // TODO: implement
170
+ on(Request.Method.GET, Dispatcher.notImplemented);
171
+
172
+ this.bind_('/session/:sessionId/element/:id/element').
173
+ on(Request.Method.POST, Dispatcher.executeAs('findChildElement'));
174
+ this.bind_('/session/:sessionId/element/:id/elements').
175
+ on(Request.Method.POST, Dispatcher.executeAs('findChildElements'));
176
+
177
+ this.bind_('/session/:sessionId/element/:id/click').
178
+ on(Request.Method.POST, Dispatcher.executeAs('clickElement'));
179
+ this.bind_('/session/:sessionId/element/:id/text').
180
+ on(Request.Method.GET, Dispatcher.executeAs('getElementText'));
181
+ this.bind_('/session/:sessionId/element/:id/submit').
182
+ on(Request.Method.POST, Dispatcher.executeAs('submitElement'));
183
+
184
+ this.bind_('/session/:sessionId/element/:id/value').
185
+ on(Request.Method.POST, Dispatcher.executeAs('sendKeysToElement')).
186
+ on(Request.Method.GET, Dispatcher.executeAs('getElementValue'));
187
+
188
+ this.bind_('/session/:sessionId/element/:id/name').
189
+ on(Request.Method.GET, Dispatcher.executeAs('getElementTagName'));
190
+
191
+ this.bind_('/session/:sessionId/element/:id/clear').
192
+ on(Request.Method.POST, Dispatcher.executeAs('clearElement'));
193
+
194
+ this.bind_('/session/:sessionId/element/:id/selected').
195
+ on(Request.Method.GET, Dispatcher.executeAs('isElementSelected')).
196
+ on(Request.Method.POST, Dispatcher.executeAs('setElementSelected'));
197
+
198
+ this.bind_('/session/:sessionId/element/:id/enabled').
199
+ on(Request.Method.GET, Dispatcher.executeAs('isElementEnabled'));
200
+ this.bind_('/session/:sessionId/element/:id/displayed').
201
+ on(Request.Method.GET, Dispatcher.executeAs('isElementDisplayed'));
202
+
203
+ this.bind_('/session/:sessionId/element/:id/location').
204
+ on(Request.Method.GET, Dispatcher.executeAs('getElementLocation'));
205
+ this.bind_('/session/:sessionId/element/:id/location_in_view').
206
+ on(Request.Method.GET, Dispatcher.executeAs(
207
+ 'getElementLocationOnceScrolledIntoView'));
208
+
209
+ this.bind_('/session/:sessionId/element/:id/size').
210
+ on(Request.Method.GET, Dispatcher.executeAs('getElementSize'));
211
+
212
+ this.bind_('/session/:sessionId/element/:id/css/:propertyName').
213
+ on(Request.Method.GET,
214
+ Dispatcher.executeAs('getElementValueOfCssProperty'));
215
+ this.bind_('/session/:sessionId/element/:id/attribute/:name').
216
+ on(Request.Method.GET, Dispatcher.executeAs('getElementAttribute'));
217
+ this.bind_('/session/:sessionId/element/:id/equals/:other').
218
+ on(Request.Method.GET, Dispatcher.executeAs('elementEquals'));
219
+
220
+ this.bind_('/session/:sessionId/element/:id/toggle').
221
+ on(Request.Method.POST, Dispatcher.executeAs('toggleElement'));
222
+ this.bind_('/session/:sessionId/element/:id/hover').
223
+ on(Request.Method.POST, Dispatcher.executeAs('hoverOverElement'));
224
+ this.bind_('/session/:sessionId/element/:id/drag').
225
+ on(Request.Method.POST, Dispatcher.executeAs('dragElement'));
226
+
227
+ this.bind_('/session/:sessionId/cookie').
228
+ on(Request.Method.GET, Dispatcher.executeAs('getCookies')).
229
+ on(Request.Method.POST, Dispatcher.executeAs('addCookie')).
230
+ on(Request.Method.DELETE, Dispatcher.executeAs('deleteAllCookies'));
231
+
232
+ this.bind_('/session/:sessionId/cookie/:name').
233
+ on(Request.Method.DELETE, Dispatcher.executeAs('deleteCookie'));
234
+
235
+ this.bind_('/session/:sessionId/frame').
236
+ on(Request.Method.POST, Dispatcher.executeAs('switchToFrame'));
237
+ this.bind_('/session/:sessionId/window').
238
+ on(Request.Method.POST, Dispatcher.executeAs('switchToWindow')).
239
+ on(Request.Method.DELETE, Dispatcher.executeAs('close'));
240
+
241
+ this.bind_('/session/:sessionId/screenshot').
242
+ on(Request.Method.GET, Dispatcher.executeAs('screenshot'));
243
+
244
+
245
+ // --------------------------------------------------------------------------
246
+ // Firefox extensions to the wire protocol.
247
+ // --------------------------------------------------------------------------
248
+
249
+ this.bind_('/extensions/firefox/quit').
250
+ on(Request.Method.POST, Dispatcher.executeAs('quit'));
251
+ };
252
+
253
+
254
+ /**
255
+ * Binds a resource to the given path.
256
+ * @param {string} path The resource path.
257
+ * @return {Resource} The bound resource.
258
+ */
259
+ Dispatcher.prototype.bind_ = function(path) {
260
+ var resource = new Resource(path);
261
+ this.resources_.push(resource);
262
+ return resource;
263
+ };
264
+
265
+
266
+
267
+ /**
268
+ * Dispatches a request to the appropriately registered handler.
269
+ * @param {Request} request The request to dispatch.
270
+ * @param {Response} response The request response.
271
+ */
272
+ Dispatcher.prototype.dispatch = function(request, response) {
273
+ // We only support one servlet, mapped to /hub/*
274
+ // TODO: be more flexible.
275
+ var path = request.getRequestUrl().path;
276
+ if (path.indexOf('/hub') != 0) {
277
+ response.sendError(Response.NOT_FOUND);
278
+ return;
279
+ }
280
+ request.setServletPath('/hub');
281
+ path = request.getPathInfo();
282
+
283
+ var bestMatchResource;
284
+ for (var i = 0; i < this.resources_.length; i++) {
285
+ if (this.resources_[i].isResourceFor(path)) {
286
+ if (!bestMatchResource ||
287
+ bestMatchResource.getNumVariablePathSegments() <
288
+ this.resources_[i].getNumVariablePathSegments()) {
289
+ bestMatchResource = this.resources_[i];
290
+ }
291
+ }
292
+ }
293
+
294
+ if (bestMatchResource) {
295
+ try {
296
+ bestMatchResource.setRequestAttributes(request);
297
+ bestMatchResource.handle(request, response);
298
+ } catch (ex) {
299
+ Utils.dump(ex);
300
+ response.sendError(Response.INTERNAL_ERROR, JSON.stringify({
301
+ status: ErrorCode.UNHANDLED_ERROR,
302
+ value: ErrorCode.toJSON(ex)
303
+ }), 'application/json');
304
+ }
305
+ } else {
306
+ response.sendError(Response.NOT_FOUND,
307
+ 'Unrecognized command: ' + request.getMethod() + ' ' +
308
+ request.getPathInfo(),
309
+ 'text/plain');
310
+ }
311
+ };
312
+
313
+
314
+ /**
315
+ * Defines a resource in the WebDriver REST service locatable at the given path.
316
+ * Any path segments prefixed with a ":" indicate that segment is a variable
317
+ * unique to a resource. For example, in the path "/session/:sessionId",
318
+ * ":sessionId" is a variable that can be changed to specify different sessions.
319
+ * @param {!string} path The path that this resource is accessible from.
320
+ */
321
+ function Resource(path) {
322
+
323
+ /**
324
+ * The request pattern that this resource is located at.
325
+ * @type {!string}
326
+ * @const
327
+ * @private
328
+ */
329
+ this.path_ = path;
330
+
331
+ /**
332
+ * The individual path segments for this resource.
333
+ * @type {Array.<string>}
334
+ * @const
335
+ * @private
336
+ */
337
+ this.pathSegments_ = path.split('/');
338
+
339
+ /**
340
+ * A map of handler functions, by HTTP method, that can service requests to
341
+ * this resource.
342
+ * @type {!Object}
343
+ * @const
344
+ * @private
345
+ */
346
+ this.handlers_ = {};
347
+
348
+ for (var i = 0; i < this.pathSegments_.length; i++) {
349
+ if (this.pathSegments_[i].indexOf(Resource.VARIABLE_PATH_SEGMENT_PREFIX_)) {
350
+ this.numVariablePathSegments_ += 1;
351
+ }
352
+ }
353
+ };
354
+
355
+
356
+ /**
357
+ * The number of path segments for this resource that are variables.
358
+ * @type {number}
359
+ * @private
360
+ */
361
+ Resource.prototype.numVariablePathSegments_ = 0;
362
+
363
+
364
+ /** @return {string} The path mapped to this resource. */
365
+ Resource.prototype.getPath = function() {
366
+ return this.path_;
367
+ };
368
+
369
+
370
+ /** @return {number} The number of variable path segments for this resource. */
371
+ Resource.prototype.getNumVariablePathSegments = function() {
372
+ return this.numVariablePathSegments_;
373
+ };
374
+
375
+
376
+ /**
377
+ * Sets the handler function for this resource when a request is received using
378
+ * the given HTTP method. This function will override any previously set
379
+ * handlers.
380
+ * @param {!Request.Method} httpMethod The request method the function can
381
+ * handle.
382
+ * @param {function(Request, Response)} handlerFn The function that will handle
383
+ * all requests for this resource using the given HTTP method.
384
+ * @return {!Resource} A self reference for chained calls.
385
+ */
386
+ Resource.prototype.on = function(httpMethod, handlerFn) {
387
+ this.handlers_[httpMethod] = handlerFn;
388
+ return this;
389
+ };
390
+
391
+
392
+ /**
393
+ * Determines if this is the resource for the given path.
394
+ * @param {!string} path The resource path to test.
395
+ * @return {boolean} Whether this resource is mapped to the given path.
396
+ */
397
+ Resource.prototype.isResourceFor = function(path) {
398
+ var allParts = path.split('/');
399
+ if (this.pathSegments_.length != allParts.length) {
400
+ return false;
401
+ }
402
+ for (var i = 0; i < this.pathSegments_.length; i++) {
403
+ if (this.pathSegments_[i] != allParts[i] &&
404
+ !/^:/.test(this.pathSegments_[i])) {
405
+ return false;
406
+ }
407
+ }
408
+ return true;
409
+ };
410
+
411
+
412
+ /**
413
+ * Sets request attributes by the named path variables for this resource. For
414
+ * each named path segment variable for this resource, the value of the
415
+ * corresponding path segment in the request will be stored as the request
416
+ * attribute's value.
417
+ * @param {Request} request The request to update.
418
+ */
419
+ Resource.prototype.setRequestAttributes = function(request) {
420
+ var allParts = request.getPathInfo().split('/');
421
+ for (var i = 0; i < this.pathSegments_.length; i++) {
422
+ if (/^:/.test(this.pathSegments_[i])) {
423
+ var decodedValue = decodeURIComponent(allParts[i]);
424
+ request.setAttribute(
425
+ this.pathSegments_[i].replace(/^:/, ''), decodedValue);
426
+ }
427
+ }
428
+ };
429
+
430
+
431
+ /**
432
+ * Handles a request to this resource. Will return a 405 if this resource does
433
+ * not permit the HTTP method used for the request.
434
+ * @param {Request} request The request to handle.
435
+ * @param {Response} response For sending the response.
436
+ * @throws If this resource cannot handle the request.
437
+ */
438
+ Resource.prototype.handle = function(request, response) {
439
+ if (!this.isResourceFor(request.getPathInfo())) {
440
+ throw Error('Request does not map to this resource:' +
441
+ '\n requestPath: ' + request.getPathInfo() +
442
+ '\n resourcePath: ' + this.path_);;
443
+ }
444
+
445
+ var requestMethod = request.getMethod();
446
+
447
+ if (requestMethod == Request.Method.OPTIONS) {
448
+ response.setHeader('Allow', this.getAllowedMethods_());
449
+ response.setStatus(Response.OK);
450
+ response.setBody('');
451
+ response.commit();
452
+ return;
453
+ }
454
+
455
+ if (requestMethod == Request.Method.HEAD) {
456
+ requestMethod = Request.Method.GET;
457
+ }
458
+
459
+ var handlerFn = this.handlers_[requestMethod];
460
+ if (handlerFn) {
461
+ handlerFn(request, response);
462
+ } else {
463
+ response.setHeader('Allow', this.getAllowedMethods_());
464
+ response.setContentType('text/plain');
465
+ response.sendError(Response.METHOD_NOT_ALLOWED,
466
+ 'Method "' + request.getMethod() + '" not allowed for command ' +
467
+ '"' + this.path_ + '"');
468
+ }
469
+ };
470
+
471
+
472
+ /**
473
+ * @return {string} A comma-delimitted list of HTTP methods allowed by this
474
+ * resource.
475
+ * @private
476
+ */
477
+ Resource.prototype.getAllowedMethods_ = function() {
478
+ var allowed = [];
479
+ for (var method in this.handlers_) {
480
+ allowed.push(method);
481
+ }
482
+
483
+ // We always respond to OPTIONS
484
+ allowed.push(Request.Method.OPTIONS);
485
+
486
+ // If we respond to GET, then we respond to HEAD.
487
+ if (Request.Method.GET in this.handlers_) {
488
+ allowed.push(Request.Method.HEAD);
489
+ }
490
+
491
+ return allowed.join(',');
492
+ };