selenium-webdriver 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. data/{common/src/rb/CHANGES → CHANGES} +13 -0
  2. data/{common/src/rb/README → README} +1 -1
  3. data/{common/src/rb/lib → lib}/selenium-webdriver.rb +0 -0
  4. data/{common/src/rb/lib → lib}/selenium/webdriver.rb +2 -1
  5. data/{common/src/rb/lib → lib}/selenium/webdriver/bridge_helper.rb +0 -0
  6. data/{common/src/rb/lib → lib}/selenium/webdriver/child_process.rb +0 -0
  7. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome.rb +0 -0
  8. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome/bridge.rb +2 -2
  9. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome/command_executor.rb +0 -0
  10. data/lib/selenium/webdriver/chrome/extension.zip +0 -0
  11. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome/launcher.rb +26 -20
  12. data/{common/src/rb/lib → lib}/selenium/webdriver/core_ext/dir.rb +0 -0
  13. data/{common/src/rb/lib → lib}/selenium/webdriver/core_ext/string.rb +0 -0
  14. data/{common/src/rb/lib → lib}/selenium/webdriver/driver.rb +0 -0
  15. data/{common/src/rb/lib → lib}/selenium/webdriver/driver_extensions/takes_screenshot.rb +0 -0
  16. data/{common/src/rb/lib → lib}/selenium/webdriver/element.rb +0 -0
  17. data/{common/src/rb/lib → lib}/selenium/webdriver/error.rb +0 -0
  18. data/{common/src/rb/lib → lib}/selenium/webdriver/file_reaper.rb +2 -1
  19. data/{common/src/rb/lib → lib}/selenium/webdriver/find.rb +0 -0
  20. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox.rb +2 -1
  21. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/binary.rb +32 -6
  22. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/bridge.rb +0 -0
  23. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  24. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/launcher.rb +0 -0
  25. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  26. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  27. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/profile.rb +37 -72
  28. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/profiles_ini.rb +0 -0
  29. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/util.rb +0 -0
  30. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie.rb +2 -2
  31. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie/bridge.rb +7 -2
  32. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie/lib.rb +0 -0
  33. data/{jobbie/prebuilt/Win32/Release → lib/selenium/webdriver/ie/native/win32}/InternetExplorerDriver.dll +0 -0
  34. data/lib/selenium/webdriver/ie/native/x64/InternetExplorerDriver.dll +0 -0
  35. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie/util.rb +0 -0
  36. data/{common/src/rb/lib → lib}/selenium/webdriver/keys.rb +0 -0
  37. data/{common/src/rb/lib → lib}/selenium/webdriver/navigation.rb +0 -0
  38. data/{common/src/rb/lib → lib}/selenium/webdriver/options.rb +0 -0
  39. data/{common/src/rb/lib → lib}/selenium/webdriver/platform.rb +5 -1
  40. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote.rb +0 -0
  41. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/bridge.rb +0 -0
  42. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/capabilities.rb +0 -0
  43. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/commands.rb +0 -0
  44. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/http/common.rb +0 -0
  45. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/http/curb.rb +0 -0
  46. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/http/default.rb +5 -1
  47. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/response.rb +0 -0
  48. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/server_error.rb +0 -0
  49. data/{common/src/rb/lib → lib}/selenium/webdriver/target_locator.rb +0 -0
  50. data/{common/src/rb/lib → lib}/selenium/webdriver/timeouts.rb +0 -0
  51. data/lib/selenium/webdriver/zip_helper.rb +30 -0
  52. metadata +73 -214
  53. data/COPYING +0 -204
  54. data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
  55. data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
  56. data/chrome/src/extension/background.html +0 -9
  57. data/chrome/src/extension/background.js +0 -1029
  58. data/chrome/src/extension/content_script.js +0 -1488
  59. data/chrome/src/extension/icons/busy.png +0 -0
  60. data/chrome/src/extension/icons/free.png +0 -0
  61. data/chrome/src/extension/manifest-nonwin.json +0 -19
  62. data/chrome/src/extension/manifest-win.json +0 -20
  63. data/chrome/src/extension/utils.js +0 -231
  64. data/common/src/js/abstractcommandprocessor.js +0 -132
  65. data/common/src/js/asserts.js +0 -296
  66. data/common/src/js/by.js +0 -149
  67. data/common/src/js/command.js +0 -380
  68. data/common/src/js/core/Blank.html +0 -7
  69. data/common/src/js/core/InjectedRemoteRunner.html +0 -8
  70. data/common/src/js/core/RemoteRunner.html +0 -101
  71. data/common/src/js/core/SeleniumLog.html +0 -109
  72. data/common/src/js/core/TestPrompt.html +0 -145
  73. data/common/src/js/core/TestRunner-splash.html +0 -55
  74. data/common/src/js/core/TestRunner.html +0 -165
  75. data/common/src/js/core/icons/all.png +0 -0
  76. data/common/src/js/core/icons/continue.png +0 -0
  77. data/common/src/js/core/icons/continue_disabled.png +0 -0
  78. data/common/src/js/core/icons/pause.png +0 -0
  79. data/common/src/js/core/icons/pause_disabled.png +0 -0
  80. data/common/src/js/core/icons/selected.png +0 -0
  81. data/common/src/js/core/icons/step.png +0 -0
  82. data/common/src/js/core/icons/step_disabled.png +0 -0
  83. data/common/src/js/core/lib/cssQuery/cssQuery-p.js +0 -6
  84. data/common/src/js/core/lib/cssQuery/src/cssQuery-level2.js +0 -142
  85. data/common/src/js/core/lib/cssQuery/src/cssQuery-level3.js +0 -150
  86. data/common/src/js/core/lib/cssQuery/src/cssQuery-standard.js +0 -53
  87. data/common/src/js/core/lib/cssQuery/src/cssQuery.js +0 -356
  88. data/common/src/js/core/lib/prototype.js +0 -2006
  89. data/common/src/js/core/lib/scriptaculous/builder.js +0 -101
  90. data/common/src/js/core/lib/scriptaculous/controls.js +0 -815
  91. data/common/src/js/core/lib/scriptaculous/dragdrop.js +0 -915
  92. data/common/src/js/core/lib/scriptaculous/effects.js +0 -958
  93. data/common/src/js/core/lib/scriptaculous/scriptaculous.js +0 -47
  94. data/common/src/js/core/lib/scriptaculous/slider.js +0 -283
  95. data/common/src/js/core/lib/scriptaculous/unittest.js +0 -383
  96. data/common/src/js/core/lib/snapsie.js +0 -91
  97. data/common/src/js/core/scripts/find_matching_child.js +0 -69
  98. data/common/src/js/core/scripts/htmlutils.js +0 -8716
  99. data/common/src/js/core/scripts/injection.html +0 -72
  100. data/common/src/js/core/scripts/selenium-api.js +0 -3291
  101. data/common/src/js/core/scripts/selenium-browserbot.js +0 -2457
  102. data/common/src/js/core/scripts/selenium-browserdetect.js +0 -153
  103. data/common/src/js/core/scripts/selenium-commandhandlers.js +0 -379
  104. data/common/src/js/core/scripts/selenium-executionloop.js +0 -175
  105. data/common/src/js/core/scripts/selenium-logging.js +0 -148
  106. data/common/src/js/core/scripts/selenium-remoterunner.js +0 -695
  107. data/common/src/js/core/scripts/selenium-testrunner.js +0 -1362
  108. data/common/src/js/core/scripts/selenium-version.js +0 -5
  109. data/common/src/js/core/scripts/ui-doc.html +0 -808
  110. data/common/src/js/core/scripts/ui-element.js +0 -1644
  111. data/common/src/js/core/scripts/ui-map-sample.js +0 -979
  112. data/common/src/js/core/scripts/user-extensions.js +0 -3
  113. data/common/src/js/core/scripts/user-extensions.js.sample +0 -75
  114. data/common/src/js/core/scripts/xmlextras.js +0 -153
  115. data/common/src/js/core/selenium-logo.png +0 -0
  116. data/common/src/js/core/selenium-test.css +0 -43
  117. data/common/src/js/core/selenium.css +0 -316
  118. data/common/src/js/core/xpath/dom.js +0 -566
  119. data/common/src/js/core/xpath/javascript-xpath-0.1.11.js +0 -2816
  120. data/common/src/js/core/xpath/util.js +0 -549
  121. data/common/src/js/core/xpath/xmltoken.js +0 -149
  122. data/common/src/js/core/xpath/xpath.js +0 -2481
  123. data/common/src/js/extension/README +0 -2
  124. data/common/src/js/extension/dommessenger.js +0 -152
  125. data/common/src/js/factory.js +0 -55
  126. data/common/src/js/future.js +0 -141
  127. data/common/src/js/jsunit.js +0 -40
  128. data/common/src/js/jsunit/app/css/jsUnitStyle.css +0 -50
  129. data/common/src/js/jsunit/app/css/readme +0 -10
  130. data/common/src/js/jsunit/app/emptyPage.html +0 -11
  131. data/common/src/js/jsunit/app/jsUnitCore.js +0 -534
  132. data/common/src/js/jsunit/app/jsUnitMockTimeout.js +0 -81
  133. data/common/src/js/jsunit/app/jsUnitTestManager.js +0 -705
  134. data/common/src/js/jsunit/app/jsUnitTestSuite.js +0 -44
  135. data/common/src/js/jsunit/app/jsUnitTracer.js +0 -102
  136. data/common/src/js/jsunit/app/jsUnitVersionCheck.js +0 -59
  137. data/common/src/js/jsunit/app/main-counts-errors.html +0 -12
  138. data/common/src/js/jsunit/app/main-counts-failures.html +0 -13
  139. data/common/src/js/jsunit/app/main-counts-runs.html +0 -13
  140. data/common/src/js/jsunit/app/main-counts.html +0 -21
  141. data/common/src/js/jsunit/app/main-data.html +0 -178
  142. data/common/src/js/jsunit/app/main-errors.html +0 -23
  143. data/common/src/js/jsunit/app/main-frame.html +0 -19
  144. data/common/src/js/jsunit/app/main-loader.html +0 -45
  145. data/common/src/js/jsunit/app/main-progress.html +0 -25
  146. data/common/src/js/jsunit/app/main-results.html +0 -67
  147. data/common/src/js/jsunit/app/main-status.html +0 -13
  148. data/common/src/js/jsunit/app/testContainer.html +0 -16
  149. data/common/src/js/jsunit/app/testContainerController.html +0 -77
  150. data/common/src/js/jsunit/app/xbDebug.js +0 -306
  151. data/common/src/js/jsunit/changelog.txt +0 -60
  152. data/common/src/js/jsunit/css/jsUnitStyle.css +0 -83
  153. data/common/src/js/jsunit/images/green.gif +0 -0
  154. data/common/src/js/jsunit/images/logo_jsunit.gif +0 -0
  155. data/common/src/js/jsunit/images/powerby-transparent.gif +0 -0
  156. data/common/src/js/jsunit/images/red.gif +0 -0
  157. data/common/src/js/jsunit/licenses/JDOM_license.txt +0 -56
  158. data/common/src/js/jsunit/licenses/Jetty_license.html +0 -213
  159. data/common/src/js/jsunit/licenses/MPL-1.1.txt +0 -470
  160. data/common/src/js/jsunit/licenses/gpl-2.txt +0 -340
  161. data/common/src/js/jsunit/licenses/index.html +0 -141
  162. data/common/src/js/jsunit/licenses/lgpl-2.1.txt +0 -504
  163. data/common/src/js/jsunit/licenses/mpl-tri-license-c.txt +0 -35
  164. data/common/src/js/jsunit/licenses/mpl-tri-license-html.txt +0 -35
  165. data/common/src/js/jsunit/readme.txt +0 -19
  166. data/common/src/js/jsunit/testRunner.html +0 -167
  167. data/common/src/js/jsunit/version.txt +0 -1
  168. data/common/src/js/key.js +0 -117
  169. data/common/src/js/localcommandprocessor.js +0 -171
  170. data/common/src/js/testcase.js +0 -219
  171. data/common/src/js/timing.js +0 -89
  172. data/common/src/js/webdriver.js +0 -860
  173. data/common/src/js/webelement.js +0 -483
  174. data/firefox/prebuilt/Win32/Release/webdriver-firefox.dll +0 -0
  175. data/firefox/prebuilt/amd64/libnoblur64.so +0 -0
  176. data/firefox/prebuilt/i386/libnoblur.so +0 -0
  177. data/firefox/prebuilt/linux/Release/libwebdriver-firefox.so +0 -0
  178. data/firefox/prebuilt/linux64/Release/libwebdriver-firefox.so +0 -0
  179. data/firefox/prebuilt/nsICommandProcessor.xpt +0 -0
  180. data/firefox/prebuilt/nsINativeEvents.xpt +0 -0
  181. data/firefox/prebuilt/nsIResponseHandler.xpt +0 -0
  182. data/firefox/src/extension/chrome.manifest +0 -3
  183. data/firefox/src/extension/components/badCertListener.js +0 -295
  184. data/firefox/src/extension/components/dispatcher.js +0 -495
  185. data/firefox/src/extension/components/driver-component.js +0 -130
  186. data/firefox/src/extension/components/errorcode.js +0 -70
  187. data/firefox/src/extension/components/firefoxDriver.js +0 -831
  188. data/firefox/src/extension/components/json2.js +0 -273
  189. data/firefox/src/extension/components/keytest.html +0 -554
  190. data/firefox/src/extension/components/nsCommandProcessor.js +0 -684
  191. data/firefox/src/extension/components/promptService.js +0 -208
  192. data/firefox/src/extension/components/request.js +0 -219
  193. data/firefox/src/extension/components/response.js +0 -276
  194. data/firefox/src/extension/components/screenshooter.js +0 -81
  195. data/firefox/src/extension/components/session.js +0 -314
  196. data/firefox/src/extension/components/sessionstore.js +0 -226
  197. data/firefox/src/extension/components/socketListener.js +0 -435
  198. data/firefox/src/extension/components/utils.js +0 -1335
  199. data/firefox/src/extension/components/webLoadingListener.js +0 -57
  200. data/firefox/src/extension/components/webdriverserver.js +0 -110
  201. data/firefox/src/extension/components/wrappedElement.js +0 -706
  202. data/firefox/src/extension/content/fxdriver.xul +0 -30
  203. data/firefox/src/extension/content/server.js +0 -95
  204. data/firefox/src/extension/idl/nsICommandProcessor.idl +0 -38
  205. data/firefox/src/extension/idl/nsIResponseHandler.idl +0 -34
  206. data/firefox/src/extension/install.rdf +0 -29
  207. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
@@ -1,1488 +0,0 @@
1
- /**
2
- * All functions which take elements assume that they are not null,
3
- * and are present as passed on the DOM.
4
- */
5
-
6
- ChromeDriverContentScript = {};
7
-
8
- ChromeDriverContentScript.internalElementArray = [];
9
- ChromeDriverContentScript.port = null;
10
- ChromeDriverContentScript.currentDocument = window.document;
11
- ChromeDriverContentScript.injectedEmbedElement = null;
12
- //Record this for async calls (execute), so returner knows what to return
13
- //(Also so that we can not re-start commands we have already started executing)
14
- ChromeDriverContentScript.currentSequenceNumber = -1;
15
-
16
- if (ChromeDriverContentScript.currentDocument.location != "about:blank") {
17
- //If loading windows using window.open, the port is opened
18
- //while we are on about:blank (which always reports window.name as ''),
19
- //and we use port-per-tab semantics, so don't open the port if
20
- //we're on about:blank
21
- ChromeDriverContentScript.port = chrome.extension.connect({name: window.name});
22
- ChromeDriverContentScript.port.onMessage.addListener(parsePortMessage);
23
- var isFrameset = (ChromeDriverContentScript.currentDocument.getElementsByTagName("frameset").length > 0);
24
- ChromeDriverContentScript.port.postMessage({response: {response: "newTabInformation",
25
- value: {statusCode: "no-op", isFrameset: isFrameset, frameCount: window.frames.length,
26
- portName: ChromeDriverContentScript.port.name}}, sequenceNumber: -1});
27
- }
28
-
29
- /**
30
- * Parse messages coming in on the port.
31
- * Sends relevant response back down the port
32
- * @param message JSON message of format:
33
- * {request: {request: "some command"
34
- * [, optional params]},
35
- * sequenceNumber: some sequence number
36
- * [, followup: message']}
37
- * where message' is a message to parse after this one
38
- */
39
- function parsePortMessage(message) {
40
- if (message == null || message.request == null) {
41
- console.log("Received bad request: " + JSON.stringify(message));
42
- return;
43
- }
44
- if (message.sequenceNumber <= ChromeDriverContentScript.currentSequenceNumber) {
45
- console.log("Already process{ed,ing} request with sequence number: " + message.sequenceNumber + ", ignoring request: " + message);
46
- return;
47
- }
48
-
49
- ChromeDriverContentScript.currentSequenceNumber = message.sequenceNumber;
50
-
51
- console.log("Received request: " + JSON.stringify(message) + " (" + window.name + ")");
52
- //wait indicates whether this is a potentially page-changing change (see background.js's sendResponseByXHR)
53
- var response = {response: message.request.request, value: null, wait: true};
54
- if (message.request.id !== undefined && message.request.id != null) {
55
- //If it seems an id has been passed, try to resolve that to an element
56
- try {
57
- var element = internalGetElement(message.request.id);
58
- } catch(e) {
59
- response.value = e;
60
- ChromeDriverContentScript.port.postMessage({response: response, sequenceNumber: message.sequenceNumber});
61
- return;
62
- }
63
- }
64
- try {
65
- switch (message.request.request) {
66
- case "addCookie":
67
- response.value = setCookie(message.request.cookie);
68
- response.wait = false;
69
- break;
70
- case "clearElement":
71
- response.value = clearElement(element);
72
- break;
73
- case "clickElement":
74
- response.value = clickElement(element, message.request.id);
75
- break;
76
- case "nonNativeClickElement":
77
- //TODO(danielwh): Focus/blur events for non-native clicking
78
- element.scrollIntoView(true);
79
- //TODO: Work out a way of firing events,
80
- //now that synthesising them gives appendMessage errors
81
- console.log("mouse downing");
82
- Utils.fireMouseEventOn(element, "mousedown");
83
- console.log("mouse up");
84
- Utils.fireMouseEventOn(element, "mouseup");
85
- console.log("mouse click");
86
- Utils.fireMouseEventOn(element, "click");
87
-
88
- if (element.click) {
89
- console.log("click");
90
- execute("try { arguments[0].click(); } catch(e){}", [{
91
- type: "ELEMENT", value: addElementToInternalArray(element)
92
- }]);
93
- }
94
- response.value = {statusCode: 0};
95
- break;
96
- case "deleteAllCookies":
97
- response.value = deleteAllCookies();
98
- response.wait = false;
99
- break;
100
- case "deleteCookie":
101
- response.value = deleteCookie(message.request.name);
102
- response.wait = false;
103
- break;
104
- case "executeScript":
105
- execute(message.request.script, message.request.args);
106
- //Sends port message back to background page from its own callback
107
- break;
108
- case "getCookies":
109
- response.value = getCookies();
110
- response.wait = false;
111
- break;
112
- case "getCookie":
113
- response.value = getCookieNamed(message.request.name);
114
- response.wait = false;
115
- break;
116
- case "findChildElement":
117
- case "findChildElements":
118
- case "findElement":
119
- case "findElements":
120
- response.wait = false;
121
- findElement(message, response);
122
- return; // Nothing more to do.
123
- case "getElementAttribute":
124
- response.value = getElementAttribute(element, message.request.name);
125
- response.wait = false;
126
- break;
127
- case "getElementValueOfCssProperty":
128
- response.value = {statusCode: 0, value: getStyle(element, message.request.propertyName)};
129
- response.wait = false;
130
- break;
131
- case "getElementLocation":
132
- var coords = getElementCoords(element);
133
- response.value = {statusCode: 0, value: {type: "POINT", x: coords[0], y: coords[1]}};
134
- response.wait = false;
135
- break;
136
- case "getElementLocationOnceScrolledIntoView":
137
- element.scrollIntoView(true);
138
- var coords = getElementCoords(element);
139
- response.value = {statusCode: 0, value: {type: "POINT", x: coords[0], y: coords[1]}};
140
- break;
141
- case "getElementSize":
142
- response.value = {statusCode: 0, value: getOffsetSizeFromSubElements(element)};
143
- response.wait = false;
144
- break;
145
- case "getElementTagName":
146
- response.value = {statusCode: 0, value: element.tagName.toLowerCase()};
147
- response.wait = false;
148
- break;
149
- case "getElementText":
150
- response.value = {statusCode: 0, value: Utils.getText(element)};
151
- response.wait = false;
152
- break;
153
- case "getElementValue":
154
- response.value = {statusCode: 0, value: element.value};
155
- response.wait = false;
156
- break;
157
- case "getFrameNameFromIndex":
158
- //TODO(danielwh): Do this by simply looking it up in window.frames when Chrome is fixed. Track: crbug.com 20773
159
- getFrameNameFromIndex(message.request.index);
160
- break;
161
- case "getPageSource":
162
- response.value = getSource();
163
- response.wait = false;
164
- break;
165
- case "getTitle":
166
- response.value = {statusCode: 0, value: Utils.trim(ChromeDriverContentScript.currentDocument.title)};
167
- response.wait = false;
168
- break;
169
- case "getCurrentUrl":
170
- response.value = {statusCode: 0, value: ChromeDriverContentScript.currentDocument.location.href};
171
- response.wait = false;
172
- break;
173
- case "goBack":
174
- history.back();
175
- response.value = {statusCode: 0};
176
- break;
177
- case "goForward":
178
- history.forward();
179
- response.value = {statusCode: 0};
180
- break;
181
- case "hoverOverElement":
182
- response.value = hoverElement(element, message.request.id);
183
- break;
184
- case "injectEmbed":
185
- injectEmbed();
186
- break;
187
- case "isElementDisplayed":
188
- response.value = {statusCode: 0, value: isElementDisplayed(element)};
189
- response.wait = false;
190
- break;
191
- case "isElementEnabled":
192
- response.value = {statusCode: 0, value: !element.disabled};
193
- response.wait = false;
194
- break;
195
- case "isElementSelected":
196
- response.value = {statusCode: 0, value: findWhetherElementIsSelected(element)};
197
- response.wait = false;
198
- break;
199
- case "refresh":
200
- ChromeDriverContentScript.currentDocument.location.reload(true);
201
- response.value = {statusCode: 0};
202
- break;
203
- case "sendKeysToElement":
204
- if (typeof message.request.value.splice == 'function' &&
205
- typeof message.request.value.join == 'function') {
206
- // Looks like we were given an array of strings. Join them together.
207
- message.request.value = message.request.value.join('');
208
- }
209
- response.value = sendElementKeys(element, message.request.value, message.request.id);
210
- response.wait = false;
211
- break;
212
- case "sendElementNonNativeKeys":
213
- response.value = sendElementNonNativeKeys(element, message.request.keys);
214
- response.wait = false;
215
- break;
216
- case "setElementSelected":
217
- response.value = selectElement(element);
218
- break;
219
- case "getActiveElement":
220
- response.value = {statusCode: 0, value: {'ELEMENT':addElementToInternalArray(ChromeDriverContentScript.currentDocument.activeElement).toString()}};
221
- response.wait = false;
222
- break;
223
- case "switchToNamedIFrameIfOneExists":
224
- response.value = switchToNamedIFrameIfOneExists(message.request.name);
225
- response.wait = false;
226
- break;
227
- case "submitElement":
228
- response.value = submitElement(element);
229
- break;
230
- case "toggleElement":
231
- response.value = toggleElement(element);
232
- break;
233
- case "sniffForMetaRedirects":
234
- response.value = sniffForMetaRedirects();
235
- break;
236
- default:
237
- response.value = {statusCode: 9, value: {message: message.request.request + " is unsupported"}};
238
- break;
239
- }
240
- } catch (e) {
241
- console.log("Caught exception " + e + ", sending error response");
242
- ChromeDriverContentScript.port.postMessage({response: {statusCode: 13, value: {message: "An unexpected error occured while executing " + message.request.request + ", exception dump: " + e}}, sequenceNumber: message.sequenceNumber});
243
- }
244
- if (response.value != null) {
245
- ChromeDriverContentScript.port.postMessage({response: response, sequenceNumber: message.sequenceNumber});
246
- console.log("Sent response: " + JSON.stringify(response) + " (seq:" + message.sequenceNumber + ")");
247
- }
248
- if (message.request.followup) {
249
- setTimeout(parsePortMessage(message.request.followup), 100);
250
- }
251
- }
252
-
253
- /**
254
- * Deletes all cookies accessible from the current page.
255
- */
256
- function deleteAllCookies() {
257
- var cookies = getAllCookiesAsStrings();
258
- for (var i = 0; i < cookies.length; ++i) {
259
- var cookie = cookies[i].split("=");
260
- deleteCookie(cookie[0]);
261
- }
262
- return {statusCode: 0};
263
- }
264
-
265
- /**
266
- * Deletes the cookie with the passed name, accessible from the current page (i.e. with path of the current directory or above)
267
- * @param cookieName name of the cookie to delete
268
- */
269
- function deleteCookie(cookieName) {
270
- //It's possible we're trying to delete cookies within iframes.
271
- //iframe stuff is unsupported in Chrome at the moment (track crbug.com/20773)
272
- //But for the iframe to be loaded and have cookies, it must be of same origin,
273
- //so we'll try deleting the cookie as if it was on this page anyway...
274
- //(Yes, this is a hack)
275
- //TODO(danielwh): Remove the cookieDocument stuff when Chrome fix frame support
276
- try {
277
- var fullpath = ChromeDriverContentScript.currentDocument.location.pathname;
278
- var cookieDocument = ChromeDriverContentScript.currentDocument;
279
- } catch (e) {
280
- console.log("Falling back on non-iframe document to delete cookies");
281
- var cookieDocument = document;
282
- var fullpath = cookieDocument.location.pathname;
283
- }
284
- var hostParts = cookieDocument.location.hostname.split(".");
285
-
286
- fullpath = fullpath.split('/');
287
- fullpath.pop(); //Get rid of the file
288
- //TODO(danielwh): Tidy up these loops and this repeated code
289
- for (var segment in fullpath) {
290
- var path = '';
291
- for (var i = 0; i < segment; ++i) {
292
- path += fullpath[i + 1] + '/';
293
- }
294
- //Delete cookie with trailing /
295
- cookieDocument.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/' + path;
296
- //Delete cookie without trailing /
297
- cookieDocument.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/' + path.substring(0, path.length - 1);
298
-
299
- var domain = "";
300
- for (var i = hostParts.length - 1; i >= 0; --i) {
301
- domain = "." + hostParts[i] + domain;
302
- //Delete cookie with trailing /
303
- cookieDocument.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/' + path + ";domain=" + domain;
304
-
305
- cookieDocument.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/' + path + ";domain=" + domain;
306
- //Delete cookie without trailing /
307
- cookieDocument.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/' + path.substring(0, path.length - 1) + ";domain=" + domain;
308
-
309
- cookieDocument.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/' + path.substring(0, path.length - 1) + ";domain=" + domain.substring(1);
310
- }
311
- }
312
- return {statusCode: 0};
313
- }
314
-
315
- /**
316
- * Get all cookies accessible from the current page as an array of
317
- * {name: some name, value: some value, secure: false} values
318
- */
319
- function getCookies() {
320
- var cookies = [];
321
- var cookieStrings = getAllCookiesAsStrings();
322
- for (var i = 0; i < cookieStrings.length; ++i) {
323
- var cookie = cookieStrings[i].split("=");
324
- cookies.push({type: "COOKIE", name: cookie[0], value: cookie[1], secure: false});
325
- }
326
- return {statusCode: 0, value: cookies};
327
- }
328
-
329
- /**
330
- * Get the cookie accessible from the current page with the passed name
331
- * @param name name of the cookie
332
- */
333
- function getCookieNamed(name) {
334
- var cookies = [];
335
- var cookieStrings = getAllCookiesAsStrings();
336
- for (var i = 0; i < cookieStrings.length; ++i) {
337
- var cookie = cookieStrings[i].split("=");
338
- if (cookie[0] == name) {
339
- return {statusCode: 0, value: {type: "COOKIE", name: cookie[0], value: cookie[1], secure: false}};
340
- }
341
- }
342
- return {statusCode: 0, value: null};
343
- }
344
-
345
- /**
346
- * Gets all cookies accessible from the current page as an array of
347
- * key=value strings
348
- */
349
- function getAllCookiesAsStrings() {
350
- //It's possible we're trying to delete cookies within iframes.
351
- //iframe stuff is unsupported in Chrome at the moment (track crbug.com/20773)
352
- //But for the iframe to be loaded and have cookies, it must be of same origin,
353
- //so we'll try deleting the cookie as if it was on this page anyway...
354
- //(Yes, this is a hack)
355
- //TODO(danielwh): Remove the cookieDocument stuff when Chrome fix frame support
356
- var cookieDocument = ChromeDriverContentScript.currentDocument;
357
- try {
358
- var tempFullpath = ChromeDriverContentScript.currentDocument.location.pathname;
359
- } catch (e) {
360
- cookieDocument = document;
361
- }
362
- var cookieStrings = cookieDocument.cookie.split('; ');
363
- var cookies = [];
364
- for (var i = 0; i < cookieStrings.length; ++i) {
365
- if (cookieStrings[i] == '') {
366
- break;
367
- }
368
- cookies.push(cookieStrings[i]);
369
- }
370
- return cookies;
371
- }
372
-
373
- /**
374
- * Add the passed cookie to the page's cookies
375
- * @param cookie org.openqa.selenium.Cookie to add
376
- */
377
- function setCookie(cookie) {
378
- //It's possible we're trying to delete cookies within iframes.
379
- //iframe stuff is unsupported in Chrome at the moment (track crbug.com/20773)
380
- //But for the iframe to be loaded and have cookies, it must be of same origin,
381
- //so we'll try deleting the cookie as if it was on this page anyway...
382
- //(Yes, this is a hack)
383
- //TODO(danielwh): Remove the cookieDocument stuff when Chrome fix frame support
384
- try {
385
- var currLocation = ChromeDriverContentScript.currentDocument.location;
386
- var currDomain = currLocation.host;
387
- var cookieDocument = ChromeDriverContentScript.currentDocument;
388
- } catch (e) {
389
- cookieDocument = document;
390
- var currLocation = ChromeDriverContentScript.currentDocument.location;
391
- var currDomain = currLocation.host;
392
- }
393
-
394
- if (currLocation.port != 80) { currDomain += ":" + currLocation.port; }
395
- if (cookie.domain != null && cookie.domain !== undefined &&
396
- currDomain.indexOf(cookie.domain) == -1) {
397
- // Not quite right, but close enough. (See r783)
398
- return {statusCode: 24, value: {
399
- message: "You may only set cookies for the current domain"}};
400
- } else if (guessPageType() != "html") {
401
- return {statusCode: 25, value: {
402
- message: "You may only set cookies on html documents"}};
403
- } else {
404
- cookieDocument.cookie = cookie.name + '=' + escape(cookie.value) +
405
- ((cookie.expiry == null || cookie.expiry === undefined) ?
406
- '' : ';expires=' + (new Date(cookie.expiry.time)).toGMTString()) +
407
- ((cookie.path == null || cookie.path === undefined) ?
408
- '' : ';path=' + cookie.path);
409
- return {statusCode: 0};
410
- }
411
- }
412
-
413
- /**
414
- * Responds to a request to find an element on the page.
415
- * @param {Object} message The request from the background page.
416
- * @param {Object} resp The response to send when the search has completed.
417
- */
418
- function findElement(message, resp) {
419
- var req = message.request;
420
- var startTime = new Date().getTime();
421
- var plural = req.request == 'findChildElements' ||
422
- req.request == 'findElements';
423
- var wait = message.implicitWait;
424
-
425
- function send(response) {
426
- ChromeDriverContentScript.port.postMessage({
427
- response: response,
428
- sequenceNumber: message.sequenceNumber
429
- });
430
- console.log("Sent response: " + JSON.stringify(response) +
431
- " (seq:" + message.sequenceNumber + ")");
432
- }
433
-
434
- function doSearch() {
435
- var found;
436
- try {
437
- found = getElement(req.using, req.value, req.id);
438
- } catch (ex) {
439
- console.error('Caught exception; sending error response', ex);
440
- send({
441
- statusCode: 13,
442
- value: {
443
- message: "An unexpected error occured while executing " +
444
- req.request + ", exception dump: " + ex
445
- }
446
- });
447
- }
448
-
449
- var done = !wait || found.length;
450
- if (done) {
451
- if (!plural && !found.length) {
452
- resp.value = {
453
- statusCode: 7,
454
- value: {
455
- message: 'After ' + (new Date().getTime() - startTime) + 'ms, ' +
456
- 'unable to find element with ' +
457
- req.using + ' ' + req.value
458
- }
459
- };
460
- } else {
461
- resp.value = {
462
- statusCode: 0,
463
- value: (plural ? found : found[0])
464
- };
465
- }
466
- send(resp);
467
- } else if (new Date().getTime() - startTime > wait) {
468
- if (plural) {
469
- resp.value = {statusCode: 0, value: []};
470
- } else {
471
- resp.value = {
472
- statusCode: 7,
473
- value: {
474
- message: 'Unable to find element with ' +
475
- req.using + ' ' + req.value
476
- }
477
- };
478
- }
479
- send(resp);
480
- } else {
481
- setTimeout(doSearch, 100);
482
- }
483
- }
484
-
485
- doSearch();
486
- }
487
-
488
-
489
- /**
490
- * Get an element, or a set of elements, by some lookup
491
- * @param {string} lookupBy The lookup strategy to use.
492
- * @param {string} lookupValue What to lookup.
493
- * @param {string} id Internal ID of the parent element to restrict the lookup
494
- * to.
495
- * @return {Array} An array of the elements matching the search criteria.
496
- */
497
- function getElement(lookupBy, lookupValue, id) {
498
- var root = "";
499
- var parent = null;
500
- if (id !== undefined && id != null) {
501
- parent = internalGetElement(id);
502
- //Looking for children
503
- root = getXPathOfElement(parent);
504
- } else {
505
- parent = ChromeDriverContentScript.currentDocument;
506
- }
507
-
508
- var elements = [];
509
- var attribute = '';
510
- switch (lookupBy) {
511
- case "class name":
512
- elements = getElementsByXPath(root +
513
- "//*[contains(concat(' ',normalize-space(@class),' '),' " +
514
- lookupValue + " ')]");
515
- break;
516
- case "name":
517
- attribute = 'name';
518
- break;
519
- case "id":
520
- attribute = 'id';
521
- break;
522
- case "link text":
523
- elements = getElementsByLinkText(parent, lookupValue);
524
- break;
525
- case "partial link text":
526
- elements = getElementsByPartialLinkText(parent, lookupValue);
527
- break;
528
- case "tag name":
529
- elements = getElementsByXPath(root + "//" + lookupValue);
530
- break;
531
- case "css":
532
- elements = parent.querySelectorAll(lookupValue);
533
- break;
534
- case "xpath":
535
- if (root != "" && lookupValue[0] != "/") {
536
- //Because xpath is relative to the parent, if there is a parent, and the lookup seems to be implied sub-lookup, we add a /
537
- root = root + "/";
538
- }
539
- var xpath = root + lookupValue;
540
- try {
541
- elements = getElementsByXPath(xpath);
542
- } catch (e) {
543
- return {statusCode: 19, value: {message: "Could not look up element by xpath " + xpath}};
544
- }
545
- break;
546
- }
547
- if (attribute != '') {
548
- elements = getElementsByXPath(root + "//*[@" + attribute + "='" + lookupValue + "']");
549
- }
550
-
551
- if (elements == null || elements.length == 0) {
552
- return [];
553
- }
554
-
555
- var toReturn = [];
556
- //Add all found elements to the page's elements, and push each to the array to return
557
- var addedElements = addElementsToInternalArray(elements);
558
- for (var addedElement in addedElements) {
559
- toReturn.push({
560
- 'ELEMENT': addedElements[addedElement].toString()
561
- });
562
- }
563
- return toReturn;
564
- }
565
-
566
- /**
567
- * Adds the element to the internal element store, if it isn't already there.
568
- * @return index of element in the array
569
- */
570
- function addElementToInternalArray(element) {
571
- for (var existingElement in ChromeDriverContentScript.internalElementArray) {
572
- if (element == ChromeDriverContentScript.internalElementArray[existingElement]) {
573
- return existingElement;
574
- }
575
- }
576
- ChromeDriverContentScript.internalElementArray.push(element);
577
- return (ChromeDriverContentScript.internalElementArray.length - 1);
578
- }
579
-
580
- function addElementsToInternalArray(elements) {
581
- var toReturn = [];
582
- for (var element in elements) {
583
- toReturn.push(addElementToInternalArray(elements[element]));
584
- }
585
- return toReturn;
586
- }
587
-
588
- /**
589
- * Gets an element which we have previously looked up by its internal ID
590
- * @param elementIdAsString the element's internal ID
591
- * @return element with the passed internal ID
592
- * @throws if element ID was stale: wrapped up
593
- * org.openqa.selenium.StaleElementReferenceException ready to send
594
- */
595
- function internalGetElement(elementIdAsString) {
596
- var elementId = parseInt(elementIdAsString);
597
- if (elementId != null && elementId >= 0 &&
598
- ChromeDriverContentScript.internalElementArray.length >= elementId + 1) {
599
- var element = ChromeDriverContentScript.internalElementArray[elementId];
600
- var parent = element;
601
- while (parent && parent != element.ownerDocument.documentElement) {
602
- parent = parent.parentNode;
603
- }
604
- if (parent !== element.ownerDocument.documentElement) {
605
- throw {statusCode: 10, value: {message: "Element is obsolete"}};
606
- }
607
- return element;
608
- } else {
609
- throw {statusCode: 10, value: {message: "Element is obsolete"}};
610
- }
611
- }
612
-
613
- /**
614
- * Ensures the passed element is in view, so that the native click event can be sent
615
- * @return object to send back to background page to trigger a native click
616
- */
617
- function clickElement(element, elementId) {
618
- try {
619
- checkElementIsDisplayed(element)
620
- } catch (e) {
621
- return e;
622
- }
623
- element.scrollIntoView(true);
624
- var size = getOffsetSizeFromSubElements(element);
625
- var coords = getElementCoords(element);
626
- return {statusCode: "no-op", id: elementId,
627
- x: parseInt(coords[0] - ChromeDriverContentScript.currentDocument.body.scrollLeft + (size.width ? size.width / 2 : 0)),
628
- y: parseInt(coords[1] - ChromeDriverContentScript.currentDocument.body.scrollTop + (size.height ? size.height / 2 : 0))};
629
- }
630
-
631
- /**
632
- * Ensures the passed element is in view, so that the native click event can be sent
633
- * @return object to send back to background page to trigger a native click
634
- */
635
- function hoverElement(element, elementId) {
636
- try {
637
- checkElementIsDisplayed(element)
638
- } catch (e) {
639
- return e;
640
- }
641
- element.scrollIntoView(true);
642
- var size = getOffsetSizeFromSubElements(element)
643
- var coords = getElementCoords(element);
644
- console.log("element.clientX: " + element.clientX);
645
- return {statusCode: "no-op", id: elementId,
646
- oldX: 0,
647
- oldY: 0,
648
- newX: coords[0] - ChromeDriverContentScript.currentDocument.body.scrollLeft + (size.width ? size.width / 2 : 0),
649
- newY: coords[1] - ChromeDriverContentScript.currentDocument.body.scrollTop + (size.height ? size.height / 2 : 0)};
650
- }
651
-
652
- /**
653
- * Clears the passed element
654
- */
655
- function clearElement(element) {
656
- var oldValue = element.value;
657
- element.value = '';
658
- if (oldValue != '') {
659
- //TODO: Work out a way of firing events,
660
- //now that synthesising them gives appendMessage errors
661
- Utils.fireHtmlEvent(element, "change");
662
- }
663
- return {statusCode: 0};
664
- }
665
-
666
- /**
667
- * Gets the attribute of the element
668
- * If the attribute is {disabled, selected, checked, index}, always returns
669
- * the sensible default (i.e. never null)
670
- */
671
- function getElementAttribute(element, attribute) {
672
- var value = null;
673
- switch (attribute.toLowerCase()) {
674
- case "disabled":
675
- value = (element.disabled ? element.disabled : "false");
676
- break;
677
- case "selected":
678
- value = findWhetherElementIsSelected(element);
679
- break;
680
- case "checked":
681
- value = (element.checked ? element.checked : "false");
682
- break;
683
- case "index":
684
- value = element.index;
685
- break;
686
- }
687
- if (value == null) {
688
- value = element.getAttribute(attribute);
689
- }
690
- return {statusCode: 0, value: value};
691
- }
692
-
693
- /**
694
- * Gets the source of the current document
695
- */
696
- function getSource() {
697
- if (guessPageType() == "html") {
698
- return {statusCode: 0, value: ChromeDriverContentScript.currentDocument.getElementsByTagName("html")[0].outerHTML};
699
- } else if (guessPageType() == "text") {
700
- return {statusCode: 0, value: ChromeDriverContentScript.currentDocument.getElementsByTagName("pre")[0].innerHTML};
701
- }
702
- }
703
-
704
- /**
705
- * Get whether the element is currently displayed (i.e. can be seen in the browser)
706
- */
707
- function isElementDisplayed(element) {
708
- try {
709
- checkElementIsDisplayed(element)
710
- } catch (e) {
711
- return false;
712
- }
713
- return true;
714
- }
715
-
716
- /**
717
- * Selects the element (i.e. sets its selected/checked value to true)
718
- * @param element An option element or input element with type checkbox or radio
719
- */
720
- function selectElement(element) {
721
- var oldValue = true;
722
- try {
723
- checkElementIsDisplayed(element);
724
- checkElementNotDisabled(element);
725
- var tagName = element.tagName.toLowerCase();
726
- if (tagName == "option") {
727
- oldValue = element.selected;
728
- element.selected = true;
729
- } else if (tagName == "input") {
730
- var type = element.getAttribute("type").toLowerCase();
731
- if (type == "checkbox") {
732
- oldValue = element.checked;
733
- element.checked = true;
734
- } else if (type == "radio") {
735
- oldValue = element.checked;
736
- element.checked = true;
737
- } else {
738
- throw {statusCode: 12, value: {message: "Cannot select an input." + type}};
739
- }
740
- } else {
741
- throw {statusCode: 12, value: {message: "Cannot select a " + tagName}};
742
- }
743
- } catch(e) {
744
- return e;
745
- }
746
- if (!oldValue) {
747
- //TODO: Work out a way of firing events,
748
- //now that synthesising them gives appendMessage errors
749
- if (tagName == "option") {
750
- var select = element;
751
- while (select.parentNode != null && select.tagName.toLowerCase() != "select") {
752
- select = select.parentNode;
753
- }
754
- if (select.tagName.toLowerCase() == "select") {
755
- element = select;
756
- } else {
757
- //If we're not within a select element, fire the event from the option, and hope that it bubbles up
758
- console.log("Falling back to event firing from option, not select element");
759
- }
760
- }
761
- Utils.fireHtmlEvent(element, "change");
762
- }
763
- return {statusCode: 0};
764
- }
765
-
766
- /**
767
- * Focus the element so that native code can type to it
768
- * @param keys the string to type. Must not be an array.
769
- */
770
- function sendElementKeys(element, keys, elementId) {
771
- try {
772
- checkElementIsDisplayed(element)
773
- } catch (e) {
774
- return e;
775
- }
776
- var oldFocusedElement = ChromeDriverContentScript.currentDocument.activeElement;
777
- if (oldFocusedElement != element) {
778
- //TODO: Work out a way of firing events,
779
- //now that synthesising them gives appendMessage errors
780
- Utils.fireHtmlEventAndConditionallyPerformAction(oldFocusedElement, "blur", function() {oldFocusedElement.blur();});
781
- Utils.fireHtmlEventAndConditionallyPerformAction(element, "focus", function() {element.focus();});
782
- }
783
- return {statusCode: "no-op", keys: keys, id: elementId};
784
- }
785
-
786
- /**
787
- * @param keys the string to type. Must not be an array.
788
- */
789
- function sendElementNonNativeKeys(element, keys) {
790
- //TODO(danielwh): Any kind of actually support for non-native keys
791
- for (var i = 0; i < keys.length; i++) {
792
- element.value += keys.charAt(i);
793
- }
794
- return {statusCode: 0};
795
- }
796
-
797
- /**
798
- * Submits the element if it is a form, or the closest enclosing form otherwise
799
- */
800
- function submitElement(element) {
801
- while (element != null) {
802
- if (element.tagName.toLowerCase() == "form") {
803
- Utils.fireHtmlEventAndConditionallyPerformAction(element, "submit", function() {element.submit();});
804
- return {statusCode: 0};
805
- }
806
- element = element.parentNode;
807
- }
808
- return {statusCode: 12, value: {message: "Cannot submit an element not in a form"}};
809
- }
810
-
811
- /**
812
- * Toggles the element if it is an input element of type checkbox,
813
- * or option element in a multi-select select element
814
- */
815
- function toggleElement(element) {
816
- var changed = false;
817
- var oldValue = null;
818
- var newValue = null;
819
- try {
820
- checkElementIsDisplayed(element);
821
- checkElementNotDisabled(element);
822
- var tagName = element.tagName.toLowerCase();
823
- if (tagName == "option") {
824
- var parent = element;
825
- while (parent != null && parent.tagName.toLowerCase() != "select") {
826
- parent = parent.parentNode;
827
- }
828
- if (parent == null) {
829
- throw {statusCode: 12, value: {message: "option tag had no select tag parent"}};
830
- }
831
- oldValue = element.selected;
832
- if (oldValue && !parent.multiple) {
833
- throw {statusCode: 12, value: {message: "Cannot unselect a single element select"}};
834
- }
835
- newValue = element.selected = !oldValue;
836
- } else if (tagName == "input") {
837
- var type = element.getAttribute("type").toLowerCase();
838
- if (type == "checkbox") {
839
- oldValue = element.checked;
840
- newValue = element.checked = !oldValue;
841
- changed = true;
842
- } else {
843
- throw {statusCode: 12, value: {message: "Cannot toggle an input." + type}};
844
- }
845
- } else {
846
- throw {statusCode: 12, value: {message: "Cannot toggle a " + tagName}};
847
- }
848
- } catch (e) {
849
- return e;
850
- }
851
- console.log("New value: " + newValue);
852
-
853
- if (changed) {
854
- //TODO: Work out a way of firing events,
855
- //now that synthesising them gives appendMessage errors
856
- Utils.fireHtmlEvent(element, "change");
857
- }
858
- return {statusCode: 0, value: newValue};
859
- }
860
-
861
- /**
862
- * Gets the CSS property asked for
863
- * @param style CSS property to get
864
- */
865
- function getStyle(element, style) {
866
- var value = ChromeDriverContentScript.currentDocument.defaultView.getComputedStyle(element, null).getPropertyValue(style);
867
- return rgbToRRGGBB(value);
868
- }
869
-
870
- function switchToNamedIFrameIfOneExists(name) {
871
- var iframes = ChromeDriverContentScript.currentDocument.getElementsByTagName("iframe");
872
- for (var i = 0; i < iframes.length; ++i) {
873
- if (iframes[i].name == name || iframes[i].id == name) {
874
- ChromeDriverContentScript.currentDocument = iframes[i].contentDocument;
875
- return {statusCode: 0};
876
- }
877
- }
878
- return {statusCode: 8, value: {message: 'Could not find iframe to switch to by name:' + name}};
879
- }
880
-
881
- function sniffForMetaRedirects() {
882
- for (var i = 0; i < ChromeDriverContentScript.currentDocument.getElementsByTagName("meta").length; ++i) {
883
- if (ChromeDriverContentScript.currentDocument.getElementsByTagName("meta")[i].hasAttribute("http-equiv") &&
884
- ChromeDriverContentScript.currentDocument.getElementsByTagName("meta")[i].getAttribute("http-equiv").toLowerCase == "refresh") {
885
- return {statusCode: "no-op", value: true};
886
- }
887
- }
888
- return {statusCode: "no-op", value: false};
889
- }
890
-
891
- function parseWrappedArguments(wrappedArguments) {
892
- var converted = [];
893
- while (wrappedArguments && wrappedArguments.length > 0) {
894
- var t = wrappedArguments.shift();
895
- switch (typeof t) {
896
- case 'number':
897
- case 'string':
898
- case 'boolean':
899
- converted.push(t);
900
- break;
901
-
902
- case 'object':
903
- if (t == null) {
904
- converted.push(null);
905
-
906
- } else if (typeof t.length === 'number' &&
907
- !(t.propertyIsEnumerable('length'))) {
908
- converted.push(parseWrappedArguments(t));
909
-
910
- } else if (typeof t['ELEMENT'] === 'string' ||
911
- typeof t['ELEMENT'] === 'number') {
912
- //Wrap up as a special object with the element's canonical xpath,
913
- // which the page can work out
914
- var element_id = t['ELEMENT'];
915
- var element = null;
916
- try {
917
- element = internalGetElement(element_id);
918
- } catch (e) {
919
- throw {
920
- statusCode: 10,
921
- message:'Tried to use obsolete element as a JavaScript argument.'
922
- };
923
- }
924
- converted.push({
925
- webdriverElementXPath: getXPathOfElement(element)
926
- });
927
-
928
- } else {
929
- var convertedObj = {};
930
- for (var prop in t) {
931
- convertedObj[prop] = parseWrappedArguments(t[prop]);
932
- }
933
- converted.push(convertedObj);
934
- }
935
- break;
936
-
937
- default:
938
- throw {
939
- statusCode: 17,
940
- message: 'Bad javascript argument: ' + (typeof t)
941
- };
942
- }
943
- }
944
- return converted;
945
- }
946
-
947
- /**
948
- * Execute arbitrary javascript in the page.
949
- * Returns by callback to returnFromJavascriptInPage.
950
- * Yes, this is *horribly* hacky.
951
- * We can't share objects between content script and page, so have to wrap up arguments as JSON
952
- * @param {string} script The javascript snippet to execute in the current page.
953
- * @param {Array.<*>} passedArgs An array of JSON arguments to pass to the
954
- * injected script. DOMElements should be specified as JSON objects of the
955
- * form {ELEMENT: string}.
956
- * @param callback function to call when the result is returned. Passed a DOMAttrModified event which should be parsed as returnFromJavascriptInPage
957
- * TODO: Make the callback be passed the parsed result.
958
- */
959
- function execute_(script, passedArgs, callback) {
960
- console.log("executing " + script + ", args: " + JSON.stringify(passedArgs));
961
- var func = "function(){" + script + "}";
962
- var args;
963
- try {
964
- args = parseWrappedArguments(passedArgs);
965
- } catch (ex) {
966
- ChromeDriverContentScript.port.postMessage({
967
- response: {
968
- statusCode: (ex.statusCode || 17),
969
- message: (ex.message || ex.toString())
970
- },
971
- sequenceNumber: ChromeDriverContentScript.currentSequenceNumber
972
- });
973
- return;
974
- }
975
-
976
- //Add a script tag to the page, containing the script we wish to execute
977
- var scriptTag = ChromeDriverContentScript.currentDocument.createElement('script');
978
- var argsString = JSON.stringify(args).replace(/"/g, "\\\"");
979
-
980
- // We use the fact that Function.prototype.toString() will decompile this to
981
- // its source code so we can inject it into the page in a SCRIPT tag.
982
- function executeInjectedScript(fn, argsAsString) {
983
- var e = document.createEvent("MutationEvent");
984
- var args = JSON.parse(argsAsString);
985
- var element = null;
986
- for (var i = 0; i < args.length; i++) {
987
- if (args[i] && typeof args[i] == 'object' &&
988
- args[i].webdriverElementXPath) {
989
- //If this is an element (because it has the proper xpath), turn it into
990
- //an actual element object
991
- args[i] = document.evaluate(args[i].webdriverElementXPath, document,
992
- null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
993
- }
994
- }
995
- try {
996
- var val = fn.apply(window, args);
997
-
998
- // prepares the injected script result to be converted to json to be sent
999
- // back to the content script.
1000
- function convertResultToJson(result) {
1001
- switch (typeof result) {
1002
- case 'string':
1003
- case 'number':
1004
- case 'boolean':
1005
- return result;
1006
- case 'undefined':
1007
- return null;
1008
- case 'object':
1009
- if (result == null) {
1010
- return result;
1011
- }
1012
- // Result was an array.
1013
- if (typeof result.length === 'number' &&
1014
- !(result.propertyIsEnumerable('length'))) {
1015
- var converted = [];
1016
- for (var i = 0; i < result.length; i++) {
1017
- converted.push(convertResultToJson(result[i]));
1018
- }
1019
- return converted;
1020
- }
1021
- // Result is a DOMNode; make sure it's a DOMElement
1022
- if (typeof result.nodeType == 'number') {
1023
- if (result.nodeType != 1) {
1024
- // Non-valid JSON value; we'll fail over when trying to
1025
- // stringify this, so fail early.
1026
- throw Error('Invalid script return type: result.nodeType == ' +
1027
- result.nodeType);
1028
- }
1029
- var path = '';
1030
- for (; result && result.nodeType == 1;
1031
- result = result.parentNode) {
1032
- var index = 1;
1033
- for (var sibling = result.previousSibling; sibling;
1034
- sibling = sibling.previousSibling) {
1035
- if (sibling.nodeType == 1 && sibling.tagName &&
1036
- sibling.tagName == result.tagName) {
1037
- index++;
1038
- }
1039
- }
1040
- path = '/' + result.tagName + '[' + index + ']' + path;
1041
- }
1042
- return {webdriverElementXPath: path};
1043
- }
1044
- // Result is an object; convert each property.
1045
- var converted = {};
1046
- for (var prop in result) {
1047
- converted[prop] = convertResultToJson(result[prop]);
1048
- }
1049
- return converted;
1050
-
1051
- case 'function':
1052
- default:
1053
- throw Error('Invalid script return type: ' + (typeof result));
1054
- } // switch
1055
- }
1056
-
1057
- val = JSON.stringify({
1058
- value: convertResultToJson(val)
1059
- });
1060
- console.info('returning from injected script: ' + val);
1061
- //Fire mutation event with newValue set to the JSON of our return value
1062
- e.initMutationEvent(
1063
- "DOMAttrModified", true, false, null, null, val, null, 0);
1064
- } catch (ex) {
1065
- //Fire mutation event with prevValue set to EXCEPTION to indicate an error
1066
- //in the script
1067
- console.error('injected script failed: ' + ex.toString());
1068
- e.initMutationEvent("DOMAttrModified", true, false, null, "EXCEPTION",
1069
- null, null, 0);
1070
- }
1071
- var scriptTags = document.getElementsByTagName("script");
1072
- var scriptTag = scriptTags[scriptTags.length - 1];
1073
- scriptTag.dispatchEvent(e);
1074
- document.documentElement.removeChild(scriptTag);
1075
- }
1076
-
1077
- scriptTag.innerHTML =
1078
- '(' + executeInjectedScript + ')(' + func + ', "' + argsString + '");';
1079
-
1080
- scriptTag.addEventListener('DOMAttrModified', callback, false);
1081
- ChromeDriverContentScript.currentDocument.documentElement.
1082
- appendChild(scriptTag);
1083
- }
1084
-
1085
- function execute(script, passedArgs) {
1086
- execute_(script, passedArgs, returnFromJavascriptInPage);
1087
- }
1088
-
1089
- function parseReturnValueFromScript(result) {
1090
- switch (typeof result) {
1091
- case 'string':
1092
- case 'number':
1093
- case 'boolean':
1094
- return result;
1095
-
1096
- case 'object':
1097
- if (result == null) {
1098
- return result;
1099
- }
1100
-
1101
- // Received an array, parse each element.
1102
- if (typeof result.length === 'number' &&
1103
- !(result.propertyIsEnumerable('length'))) {
1104
- var converted = [];
1105
- for (var i = 0; i < result.length; i++) {
1106
- converted.push(parseReturnValueFromScript(result[i]));
1107
- }
1108
- return converted;
1109
- }
1110
-
1111
- // Script returned an element; return it's cached ID.
1112
- if (typeof result.webdriverElementXPath === 'string') {
1113
- //If we're returning an element, turn it into an actual element object
1114
- var element = getElementsByXPath(result.webdriverElementXPath)[0];
1115
- return {'ELEMENT': addElementToInternalArray(element).toString()};
1116
- }
1117
-
1118
- // We were given a plain-old JSON object. Parse each property.
1119
- var convertedObj = {};
1120
- for (var prop in result) {
1121
- convertedObj[prop] = parseReturnValueFromScript(result[prop]);
1122
- }
1123
- return convertedObj;
1124
-
1125
- // The script we inject to the page should never give us a result of type
1126
- // 'function' or 'undefined', so we do not need to check for those, but
1127
- // just go ahead and return null for completeness.
1128
- case 'function':
1129
- case 'undefined':
1130
- default:
1131
- return null;
1132
- }
1133
- }
1134
-
1135
- /**
1136
- * Callback from execute
1137
- */
1138
- function returnFromJavascriptInPage(e) {
1139
- if (e.prevValue == "EXCEPTION") {
1140
- ChromeDriverContentScript.port.postMessage({sequenceNumber: ChromeDriverContentScript.currentSequenceNumber,
1141
- response: {response: "execute", value: {statusCode: 17,
1142
- value: {message: "Tried to execute bad javascript."}}}});
1143
- return;
1144
- }
1145
- console.log("Got result");
1146
- console.log("Result was: " + e.newValue);
1147
- var result = JSON.parse(e.newValue).value;
1148
- var value = parseReturnValueFromScript(result);
1149
- console.log("Return value: " + JSON.stringify(value));
1150
- ChromeDriverContentScript.port.postMessage({sequenceNumber: ChromeDriverContentScript.currentSequenceNumber, response: {response: "execute", value: {statusCode: 0, value: value}}});
1151
- }
1152
-
1153
- function getFrameNameFromIndex(index) {
1154
- var scriptTag = ChromeDriverContentScript.currentDocument.createElement('script');
1155
- scriptTag.innerText = 'var e = document.createEvent("MutationEvent");' +
1156
- 'try {' +
1157
- 'var val = window.frames[' + index + '].name;' +
1158
- 'e.initMutationEvent("DOMAttrModified", true, false, null, null, val, null, 0);' +
1159
- '} catch (exn) {' +
1160
- //Fire mutation event with prevValue set to EXCEPTION to indicate an error in the script
1161
- 'e.initMutationEvent("DOMAttrModified", true, false, null, "EXCEPTION", null, null, 0);' +
1162
- '}' +
1163
- 'document.getElementsByTagName("script")[document.getElementsByTagName("script").length - 1].dispatchEvent(e);' +
1164
- 'document.getElementsByTagName("html")[0].removeChild(document.getElementsByTagName("script")[document.getElementsByTagName("script").length - 1]);';
1165
- scriptTag.addEventListener('DOMAttrModified', returnFromGetFrameNameFromIndexJavascriptInPage, false);
1166
- try {
1167
- if (ChromeDriverContentScript.currentDocument.getElementsByTagName("frameset").length > 0) {
1168
- ChromeDriverContentScript.currentDocument.getElementsByTagName("frameset")[0].appendChild(scriptTag);
1169
- } else {
1170
- ChromeDriverContentScript.currentDocument.getElementsByTagName("html")[0].appendChild(scriptTag);
1171
- }
1172
- } catch (e) {
1173
- ChromeDriverContentScript.port.postMessage({sequenceNumber: ChromeDriverContentScript.currentSequenceNumber,
1174
- response: {response: "getFrameNameFromIndex", value: {statusCode: 8,
1175
- message: "Page seemed not to be a frameset. Couldn't find frame"}}});
1176
- }
1177
- }
1178
-
1179
- function returnFromGetFrameNameFromIndexJavascriptInPage(e) {
1180
- if (e.prevValue == "EXCEPTION") {
1181
- ChromeDriverContentScript.port.postMessage({sequenceNumber: ChromeDriverContentScript.currentSequenceNumber,
1182
- response: {response: "getFrameNameFromIndex", value: {statusCode: 8,
1183
- value: {message: "No such frame"}}}});
1184
- } else {
1185
- ChromeDriverContentScript.port.postMessage({sequenceNumber: ChromeDriverContentScript.currentSequenceNumber,
1186
- response: {response: "getFrameNameFromIndex", value: {statusCode: "no-op",
1187
- name: e.newValue}}});
1188
- }
1189
- }
1190
-
1191
- /**
1192
- * Inject an embed tag so the native code can grab the HWND
1193
- */
1194
- function injectEmbed() {
1195
- ChromeDriverContentScript.injectedEmbedElement = ChromeDriverContentScript.currentDocument.createElement('embed');
1196
- ChromeDriverContentScript.injectedEmbedElement.setAttribute("type", "application/x-chromedriver-reporter");
1197
- ChromeDriverContentScript.injectedEmbedElement.setAttribute("style", "width: 0; height: 0;");
1198
- ChromeDriverContentScript.currentDocument.getElementsByTagName("html")[0].appendChild(ChromeDriverContentScript.injectedEmbedElement);
1199
- //Give the embed time to render. Hope that the followup doesn't count embeds or anything
1200
- setTimeout(removeInjectedEmbed, 100);
1201
- }
1202
-
1203
- function removeInjectedEmbed() {
1204
- if (ChromeDriverContentScript.injectedEmbedElement != null) {
1205
- ChromeDriverContentScript.currentDocument.getElementsByTagName("html")[0].removeChild(ChromeDriverContentScript.injectedEmbedElement);
1206
- ChromeDriverContentScript.injectedEmbedElement = null;
1207
- }
1208
- }
1209
-
1210
- /**
1211
- * Guesses whether we have an HTML document or a text file
1212
- */
1213
- function guessPageType() {
1214
- var source = ChromeDriverContentScript.currentDocument.getElementsByTagName("html")[0].outerHTML;
1215
- var textSourceBegins = '<html><body><pre style="word-wrap: break-word; white-space: pre-wrap;">';
1216
- var textSourceEnds = '</pre></body></html>';
1217
-
1218
- if (source.substr(0, textSourceBegins.length) == textSourceBegins &&
1219
- source.substr(0 - textSourceEnds.length) == textSourceEnds) {
1220
- return "text";
1221
- } else {
1222
- return "html";
1223
- }
1224
- }
1225
-
1226
- /**
1227
- * Gets an array of elements which match the passed xpath string
1228
- */
1229
- function getElementsByXPath(xpath) {
1230
- var elements = [];
1231
- var foundElements = ChromeDriverContentScript.currentDocument.evaluate(xpath, ChromeDriverContentScript.currentDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
1232
- var this_element = foundElements.iterateNext();
1233
- while (this_element) {
1234
- elements.push(this_element);
1235
- this_element = foundElements.iterateNext();
1236
- }
1237
- return elements;
1238
- }
1239
-
1240
- /**
1241
- * Gets canonical xpath of the passed element, e.g. /HTML/BODY/P[1]
1242
- */
1243
- function getXPathOfElement(element) {
1244
- var path = "";
1245
- for (; element && element.nodeType == 1; element = element.parentNode) {
1246
- index = getElementIndexForXPath(element);
1247
- path = "/" + element.tagName + "[" + index + "]" + path;
1248
- }
1249
- return path;
1250
- }
1251
-
1252
- /**
1253
- * Returns n for the nth element of type element.tagName in the page
1254
- */
1255
- function getElementIndexForXPath(element) {
1256
- var index = 1;
1257
- for (var sibling = element.previousSibling; sibling ; sibling = sibling.previousSibling) {
1258
- if (sibling.nodeType == 1 && sibling.tagName == element.tagName) {
1259
- index++;
1260
- }
1261
- }
1262
- return index;
1263
- }
1264
-
1265
- /**
1266
- * Gets an array of link elements whose displayed text is linkText
1267
- */
1268
- function getElementsByLinkText(parent, linkText) {
1269
- var links = parent.getElementsByTagName("a");
1270
- var matchingLinks = [];
1271
- for (var i = 0; i < links.length; i++) {
1272
- if (Utils.getText(links[i]) == linkText) {
1273
- matchingLinks.push(links[i]);
1274
- }
1275
- }
1276
- return matchingLinks;
1277
- }
1278
-
1279
- /**
1280
- * Gets an array of link elements whose displayed text includes linkText
1281
- */
1282
- function getElementsByPartialLinkText(parent, partialLinkText) {
1283
- var links = parent.getElementsByTagName("a");
1284
- var matchingLinks = [];
1285
- for (var i = 0; i < links.length; i++) {
1286
- if (Utils.getText(links[i]).indexOf(partialLinkText) > -1) {
1287
- matchingLinks.push(links[i]);
1288
- }
1289
- }
1290
- return matchingLinks;
1291
- }
1292
-
1293
- /**
1294
- * Throws exception if element is not displayed
1295
- * @return nothing if element is displayed
1296
- * @throws ElementNotVisibleException object ready to be sent if element is not displayed
1297
- */
1298
- function checkElementIsDisplayed(element) {
1299
- if (element.tagName.toLowerCase() == "title") {
1300
- //Always visible
1301
- return;
1302
- }
1303
- if (!Utils.isDisplayed(element)) {
1304
- throw {statusCode: 11, value: {message: "Element was not visible"}};
1305
- }
1306
- }
1307
-
1308
- /**
1309
- * Throws exception if element is disabled
1310
- * @return nothing if element is enabled
1311
- * @throws UnsupoprtedOperationException object ready to be sent if element is disabled
1312
- */
1313
- function checkElementNotDisabled(element) {
1314
- if (element.disabled) {
1315
- throw {statusCode: 12, value: {message: "Cannot operate on disabled element"}};
1316
- }
1317
- }
1318
-
1319
- /**
1320
- * Checks whether element is selected/checked
1321
- * @return true if element is {selectable and selected, checkable and checked},
1322
- * false otherwise
1323
- */
1324
- function findWhetherElementIsSelected(element) {
1325
- var selected = false;
1326
- try {
1327
- var tagName = element.tagName.toLowerCase();
1328
- if (tagName == "option") {
1329
- selected = element.selected;
1330
- } else if (tagName == "input") {
1331
- var type = element.getAttribute("type").toLowerCase();
1332
- if (type == "checkbox" || type == "radio") {
1333
- selected = element.checked;
1334
- }
1335
- } else {
1336
- selected = element.getAttribute("selected");
1337
- }
1338
- } catch (e) {
1339
- selected = false;
1340
- }
1341
- return selected;
1342
- }
1343
-
1344
- /**
1345
- * Gets the coordinates of the top-left corner of the element on the browser window
1346
- * (NOT the displayed portion, the WHOLE page)
1347
- * Heavily influenced by com.google.gwt.dom.client.DOMImplSafari,
1348
- * which is released under Apache 2
1349
- * It's not actually correct...
1350
- * @return array: [x, y]
1351
- */
1352
- function getElementCoords(elem) {
1353
- var left = 0;
1354
- var top = 0;
1355
- if (frameElement) {
1356
- left += frameElement.offsetLeft;
1357
- top += frameElement.offsetTop;
1358
- }
1359
- try {
1360
- if (elem.getBoundingClientRect) {
1361
- var rect = elem.getBoundingClientRect();
1362
- left += rect.left + ChromeDriverContentScript.currentDocument.body.scrollLeft;
1363
- top += rect.top + ChromeDriverContentScript.currentDocument.body.scrollTop;
1364
- return [left, top];
1365
- }
1366
- } catch(e) {
1367
- var left = 0;
1368
- var top = 0;
1369
- if (frameElement) {
1370
- left += frameElement.offsetLeft;
1371
- top += frameElement.offsetTop;
1372
- }
1373
- }
1374
-
1375
- //The below is ugly and NOT ACTUALLY RIGHT
1376
-
1377
- // Unattached elements and elements (or their ancestors) with style
1378
- // 'display: none' have no offset{Top,Left}.
1379
- if (elem.offsetTop == null || elem.offsetLeft == null) {
1380
- return [left, top];
1381
- }
1382
-
1383
- var doc = elem.ownerDocument;
1384
- var curr = elem.parentNode;
1385
- if (curr) {
1386
- // This intentionally excludes body which has a null offsetParent.
1387
- while (curr.offsetParent) {
1388
- top -= curr.scrollTop;
1389
- left -= curr.scrollLeft;
1390
-
1391
- // In RTL mode, offsetLeft is relative to the left edge of the
1392
- // scrollable area when scrolled all the way to the right, so we need
1393
- // to add back that difference.
1394
- if (getStyle(curr, 'direction') == 'rtl') {
1395
- left += (curr.scrollWidth - curr.clientWidth);
1396
- }
1397
-
1398
- curr = curr.parentNode;
1399
- }
1400
- }
1401
-
1402
- while (elem) {
1403
- top += elem.offsetTop;
1404
- left += elem.offsetLeft;
1405
-
1406
- if (getStyle(elem, 'position') == 'fixed') {
1407
- top += doc.body.scrollTop;
1408
- left += doc.body.scrollLeft;
1409
- return [left, top];
1410
- }
1411
-
1412
-
1413
- // Webkit bug: a top-level absolutely positioned element includes the
1414
- // body's offset position already.
1415
- var parent = elem.offsetParent;
1416
- if (parent && (parent.tagName == 'BODY') &&
1417
- (getStyle(elem, 'position') == 'absolute')) {
1418
- break;
1419
- }
1420
-
1421
- elem = parent;
1422
- }
1423
- return [left, top];
1424
- }
1425
-
1426
- /**
1427
- * Gets the maximum offsetHeight and offsetWidth of an element or those of its sub-elements
1428
- * In place because element.offset{Height,Width} returns incorrectly in WebKit (see bug 28810)
1429
- * @param element element to get max dimensions of
1430
- * @param width optional greatest width seen so far (omit when calling)
1431
- * @param height optional greatest height seen so far (omit when calling)
1432
- * @return an object of form: {type: "DIMENSION", width: maxOffsetWidth, height: maxOffsetHeight}
1433
- */
1434
- function getOffsetSizeFromSubElements(element, maxWidth, maxHeight) {
1435
- if (element.getBoundingClientRect) {
1436
- var rect = element.getBoundingClientRect();
1437
- return {type: "DIMENSION", width: rect.width, height: rect.height};
1438
- }
1439
- //The below isn't correct, but is a hack with a decent probability of being correct, if the element has no BoundingClientRect
1440
- //TODO(danielwh): Fix this up a bit
1441
- maxWidth = (maxWidth === undefined || element.offsetWidth > maxWidth) ? element.offsetWidth : maxWidth;
1442
- maxHeight = (maxHeight === undefined || element.offsetHeight > maxHeight) ? element.offsetHeight : maxHeight;
1443
- for (var child in element.children) {
1444
- var childSize = getOffsetSizeFromSubElements(element.children[child], maxWidth, maxHeight);
1445
- maxWidth = (childSize.width > maxWidth) ? childSize.width : maxWidth;
1446
- maxHeight = (childSize.height > maxHeight) ? childSize.height : maxHeight;
1447
- }
1448
- return {type: "DIMENSION", width: maxWidth, height: maxHeight};
1449
- }
1450
-
1451
- /**
1452
- * Converts rgb(x, y, z) colours to #RRGGBB colours
1453
- * @param rgb string of form either rgb(x, y, z) or rgba(x, y, z, a) with x, y, z, a numbers
1454
- * @return string of form #RRGGBB where RR, GG, BB are two-digit lower-case hex values
1455
- */
1456
- function rgbToRRGGBB(rgb) {
1457
- var r, g, b;
1458
- var values = rgb.split(",");
1459
- if (values.length == 3 && values[0].length > 4 && values[0].substr(0, 4) == "rgb(") {
1460
- r = decimalToHex(values[0].substr(4));
1461
- g = decimalToHex(values[1]);
1462
- b = decimalToHex(values[2].substr(0, values[2].length - 1));
1463
- if (r == null || g == null || b == null) {
1464
- return null;
1465
- }
1466
- return "#" + r + g + b;
1467
- } else if (rgb == "rgba(0, 0, 0, 0)") {
1468
- return "transparent";
1469
- } else {
1470
- return rgb;
1471
- }
1472
- }
1473
-
1474
- /**
1475
- * Convert a number from decimal to a hex string of at least two digits
1476
- * @return null if value was not an int, two digit string representation
1477
- * (with leading zero if needed) of value in base 16 otherwise
1478
- */
1479
- function decimalToHex(value) {
1480
- value = parseInt(value).toString(16);
1481
- if (value == null) {
1482
- return null;
1483
- }
1484
- if (value.length == 1) {
1485
- value = '0' + '' + value;
1486
- }
1487
- return value;
1488
- }