frames.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. <?php
  2. /**
  3. * Base include file for SimpleTest
  4. * @package SimpleTest
  5. * @subpackage WebTester
  6. * @version $Id$
  7. */
  8. /**#@+
  9. * include other SimpleTest class files
  10. */
  11. require_once(dirname(__FILE__) . '/page.php');
  12. require_once(dirname(__FILE__) . '/user_agent.php');
  13. /**#@-*/
  14. /**
  15. * A composite page. Wraps a frameset page and
  16. * adds subframes. The original page will be
  17. * mostly ignored. Implements the SimplePage
  18. * interface so as to be interchangeable.
  19. * @package SimpleTest
  20. * @subpackage WebTester
  21. */
  22. class SimpleFrameset {
  23. private $frameset;
  24. private $frames;
  25. private $focus;
  26. private $names;
  27. /**
  28. * Stashes the frameset page. Will make use of the
  29. * browser to fetch the sub frames recursively.
  30. * @param SimplePage $page Frameset page.
  31. */
  32. function __construct($page) {
  33. $this->frameset = $page;
  34. $this->frames = array();
  35. $this->focus = false;
  36. $this->names = array();
  37. }
  38. /**
  39. * Adds a parsed page to the frameset.
  40. * @param SimplePage $page Frame page.
  41. * @param string $name Name of frame in frameset.
  42. * @access public
  43. */
  44. function addFrame($page, $name = false) {
  45. $this->frames[] = $page;
  46. if ($name) {
  47. $this->names[$name] = count($this->frames) - 1;
  48. }
  49. }
  50. /**
  51. * Replaces existing frame with another. If the
  52. * frame is nested, then the call is passed down
  53. * one level.
  54. * @param array $path Path of frame in frameset.
  55. * @param SimplePage $page Frame source.
  56. * @access public
  57. */
  58. function setFrame($path, $page) {
  59. $name = array_shift($path);
  60. if (isset($this->names[$name])) {
  61. $index = $this->names[$name];
  62. } else {
  63. $index = $name - 1;
  64. }
  65. if (count($path) == 0) {
  66. $this->frames[$index] = &$page;
  67. return;
  68. }
  69. $this->frames[$index]->setFrame($path, $page);
  70. }
  71. /**
  72. * Accessor for current frame focus. Will be
  73. * false if no frame has focus. Will have the nested
  74. * frame focus if any.
  75. * @return array Labels or indexes of nested frames.
  76. * @access public
  77. */
  78. function getFrameFocus() {
  79. if ($this->focus === false) {
  80. return array();
  81. }
  82. return array_merge(
  83. array($this->getPublicNameFromIndex($this->focus)),
  84. $this->frames[$this->focus]->getFrameFocus());
  85. }
  86. /**
  87. * Turns an internal array index into the frames list
  88. * into a public name, or if none, then a one offset
  89. * index.
  90. * @param integer $subject Internal index.
  91. * @return integer/string Public name.
  92. * @access private
  93. */
  94. protected function getPublicNameFromIndex($subject) {
  95. foreach ($this->names as $name => $index) {
  96. if ($subject == $index) {
  97. return $name;
  98. }
  99. }
  100. return $subject + 1;
  101. }
  102. /**
  103. * Sets the focus by index. The integer index starts from 1.
  104. * If already focused and the target frame also has frames,
  105. * then the nested frame will be focused.
  106. * @param integer $choice Chosen frame.
  107. * @return boolean True if frame exists.
  108. * @access public
  109. */
  110. function setFrameFocusByIndex($choice) {
  111. if (is_integer($this->focus)) {
  112. if ($this->frames[$this->focus]->hasFrames()) {
  113. return $this->frames[$this->focus]->setFrameFocusByIndex($choice);
  114. }
  115. }
  116. if (($choice < 1) || ($choice > count($this->frames))) {
  117. return false;
  118. }
  119. $this->focus = $choice - 1;
  120. return true;
  121. }
  122. /**
  123. * Sets the focus by name. If already focused and the
  124. * target frame also has frames, then the nested frame
  125. * will be focused.
  126. * @param string $name Chosen frame.
  127. * @return boolean True if frame exists.
  128. * @access public
  129. */
  130. function setFrameFocus($name) {
  131. if (is_integer($this->focus)) {
  132. if ($this->frames[$this->focus]->hasFrames()) {
  133. return $this->frames[$this->focus]->setFrameFocus($name);
  134. }
  135. }
  136. if (in_array($name, array_keys($this->names))) {
  137. $this->focus = $this->names[$name];
  138. return true;
  139. }
  140. return false;
  141. }
  142. /**
  143. * Clears the frame focus.
  144. * @access public
  145. */
  146. function clearFrameFocus() {
  147. $this->focus = false;
  148. $this->clearNestedFramesFocus();
  149. }
  150. /**
  151. * Clears the frame focus for any nested frames.
  152. * @access private
  153. */
  154. protected function clearNestedFramesFocus() {
  155. for ($i = 0; $i < count($this->frames); $i++) {
  156. $this->frames[$i]->clearFrameFocus();
  157. }
  158. }
  159. /**
  160. * Test for the presence of a frameset.
  161. * @return boolean Always true.
  162. * @access public
  163. */
  164. function hasFrames() {
  165. return true;
  166. }
  167. /**
  168. * Accessor for frames information.
  169. * @return array/string Recursive hash of frame URL strings.
  170. * The key is either a numerical
  171. * index or the name attribute.
  172. * @access public
  173. */
  174. function getFrames() {
  175. $report = array();
  176. for ($i = 0; $i < count($this->frames); $i++) {
  177. $report[$this->getPublicNameFromIndex($i)] =
  178. $this->frames[$i]->getFrames();
  179. }
  180. return $report;
  181. }
  182. /**
  183. * Accessor for raw text of either all the pages or
  184. * the frame in focus.
  185. * @return string Raw unparsed content.
  186. * @access public
  187. */
  188. function getRaw() {
  189. if (is_integer($this->focus)) {
  190. return $this->frames[$this->focus]->getRaw();
  191. }
  192. $raw = '';
  193. for ($i = 0; $i < count($this->frames); $i++) {
  194. $raw .= $this->frames[$i]->getRaw();
  195. }
  196. return $raw;
  197. }
  198. /**
  199. * Accessor for plain text of either all the pages or
  200. * the frame in focus.
  201. * @return string Plain text content.
  202. * @access public
  203. */
  204. function getText() {
  205. if (is_integer($this->focus)) {
  206. return $this->frames[$this->focus]->getText();
  207. }
  208. $raw = '';
  209. for ($i = 0; $i < count($this->frames); $i++) {
  210. $raw .= ' ' . $this->frames[$i]->getText();
  211. }
  212. return trim($raw);
  213. }
  214. /**
  215. * Accessor for last error.
  216. * @return string Error from last response.
  217. * @access public
  218. */
  219. function getTransportError() {
  220. if (is_integer($this->focus)) {
  221. return $this->frames[$this->focus]->getTransportError();
  222. }
  223. return $this->frameset->getTransportError();
  224. }
  225. /**
  226. * Request method used to fetch this frame.
  227. * @return string GET, POST or HEAD.
  228. * @access public
  229. */
  230. function getMethod() {
  231. if (is_integer($this->focus)) {
  232. return $this->frames[$this->focus]->getMethod();
  233. }
  234. return $this->frameset->getMethod();
  235. }
  236. /**
  237. * Original resource name.
  238. * @return SimpleUrl Current url.
  239. * @access public
  240. */
  241. function getUrl() {
  242. if (is_integer($this->focus)) {
  243. $url = $this->frames[$this->focus]->getUrl();
  244. $url->setTarget($this->getPublicNameFromIndex($this->focus));
  245. } else {
  246. $url = $this->frameset->getUrl();
  247. }
  248. return $url;
  249. }
  250. /**
  251. * Page base URL.
  252. * @return SimpleUrl Current url.
  253. * @access public
  254. */
  255. function getBaseUrl() {
  256. if (is_integer($this->focus)) {
  257. $url = $this->frames[$this->focus]->getBaseUrl();
  258. } else {
  259. $url = $this->frameset->getBaseUrl();
  260. }
  261. return $url;
  262. }
  263. /**
  264. * Expands expandomatic URLs into fully qualified
  265. * URLs for the frameset page.
  266. * @param SimpleUrl $url Relative URL.
  267. * @return SimpleUrl Absolute URL.
  268. * @access public
  269. */
  270. function expandUrl($url) {
  271. return $this->frameset->expandUrl($url);
  272. }
  273. /**
  274. * Original request data.
  275. * @return mixed Sent content.
  276. * @access public
  277. */
  278. function getRequestData() {
  279. if (is_integer($this->focus)) {
  280. return $this->frames[$this->focus]->getRequestData();
  281. }
  282. return $this->frameset->getRequestData();
  283. }
  284. /**
  285. * Accessor for current MIME type.
  286. * @return string MIME type as string; e.g. 'text/html'
  287. * @access public
  288. */
  289. function getMimeType() {
  290. if (is_integer($this->focus)) {
  291. return $this->frames[$this->focus]->getMimeType();
  292. }
  293. return $this->frameset->getMimeType();
  294. }
  295. /**
  296. * Accessor for last response code.
  297. * @return integer Last HTTP response code received.
  298. * @access public
  299. */
  300. function getResponseCode() {
  301. if (is_integer($this->focus)) {
  302. return $this->frames[$this->focus]->getResponseCode();
  303. }
  304. return $this->frameset->getResponseCode();
  305. }
  306. /**
  307. * Accessor for last Authentication type. Only valid
  308. * straight after a challenge (401).
  309. * @return string Description of challenge type.
  310. * @access public
  311. */
  312. function getAuthentication() {
  313. if (is_integer($this->focus)) {
  314. return $this->frames[$this->focus]->getAuthentication();
  315. }
  316. return $this->frameset->getAuthentication();
  317. }
  318. /**
  319. * Accessor for last Authentication realm. Only valid
  320. * straight after a challenge (401).
  321. * @return string Name of security realm.
  322. * @access public
  323. */
  324. function getRealm() {
  325. if (is_integer($this->focus)) {
  326. return $this->frames[$this->focus]->getRealm();
  327. }
  328. return $this->frameset->getRealm();
  329. }
  330. /**
  331. * Accessor for outgoing header information.
  332. * @return string Header block.
  333. * @access public
  334. */
  335. function getRequest() {
  336. if (is_integer($this->focus)) {
  337. return $this->frames[$this->focus]->getRequest();
  338. }
  339. return $this->frameset->getRequest();
  340. }
  341. /**
  342. * Accessor for raw header information.
  343. * @return string Header block.
  344. * @access public
  345. */
  346. function getHeaders() {
  347. if (is_integer($this->focus)) {
  348. return $this->frames[$this->focus]->getHeaders();
  349. }
  350. return $this->frameset->getHeaders();
  351. }
  352. /**
  353. * Accessor for parsed title.
  354. * @return string Title or false if no title is present.
  355. * @access public
  356. */
  357. function getTitle() {
  358. return $this->frameset->getTitle();
  359. }
  360. /**
  361. * Accessor for a list of all fixed links.
  362. * @return array List of urls as strings.
  363. * @access public
  364. */
  365. function getUrls() {
  366. if (is_integer($this->focus)) {
  367. return $this->frames[$this->focus]->getUrls();
  368. }
  369. $urls = array();
  370. foreach ($this->frames as $frame) {
  371. $urls = array_merge($urls, $frame->getUrls());
  372. }
  373. return array_values(array_unique($urls));
  374. }
  375. /**
  376. * Accessor for URLs by the link label. Label will match
  377. * regardess of whitespace issues and case.
  378. * @param string $label Text of link.
  379. * @return array List of links with that label.
  380. * @access public
  381. */
  382. function getUrlsByLabel($label) {
  383. if (is_integer($this->focus)) {
  384. return $this->tagUrlsWithFrame(
  385. $this->frames[$this->focus]->getUrlsByLabel($label),
  386. $this->focus);
  387. }
  388. $urls = array();
  389. foreach ($this->frames as $index => $frame) {
  390. $urls = array_merge(
  391. $urls,
  392. $this->tagUrlsWithFrame(
  393. $frame->getUrlsByLabel($label),
  394. $index));
  395. }
  396. return $urls;
  397. }
  398. /**
  399. * Accessor for a URL by the id attribute. If in a frameset
  400. * then the first link found with that ID attribute is
  401. * returned only. Focus on a frame if you want one from
  402. * a specific part of the frameset.
  403. * @param string $id Id attribute of link.
  404. * @return string URL with that id.
  405. * @access public
  406. */
  407. function getUrlById($id) {
  408. foreach ($this->frames as $index => $frame) {
  409. if ($url = $frame->getUrlById($id)) {
  410. if (! $url->gettarget()) {
  411. $url->setTarget($this->getPublicNameFromIndex($index));
  412. }
  413. return $url;
  414. }
  415. }
  416. return false;
  417. }
  418. /**
  419. * Attaches the intended frame index to a list of URLs.
  420. * @param array $urls List of SimpleUrls.
  421. * @param string $frame Name of frame or index.
  422. * @return array List of tagged URLs.
  423. * @access private
  424. */
  425. protected function tagUrlsWithFrame($urls, $frame) {
  426. $tagged = array();
  427. foreach ($urls as $url) {
  428. if (! $url->getTarget()) {
  429. $url->setTarget($this->getPublicNameFromIndex($frame));
  430. }
  431. $tagged[] = $url;
  432. }
  433. return $tagged;
  434. }
  435. /**
  436. * Finds a held form by button label. Will only
  437. * search correctly built forms.
  438. * @param SimpleSelector $selector Button finder.
  439. * @return SimpleForm Form object containing
  440. * the button.
  441. * @access public
  442. */
  443. function getFormBySubmit($selector) {
  444. return $this->findForm('getFormBySubmit', $selector);
  445. }
  446. /**
  447. * Finds a held form by image using a selector.
  448. * Will only search correctly built forms. The first
  449. * form found either within the focused frame, or
  450. * across frames, will be the one returned.
  451. * @param SimpleSelector $selector Image finder.
  452. * @return SimpleForm Form object containing
  453. * the image.
  454. * @access public
  455. */
  456. function getFormByImage($selector) {
  457. return $this->findForm('getFormByImage', $selector);
  458. }
  459. /**
  460. * Finds a held form by the form ID. A way of
  461. * identifying a specific form when we have control
  462. * of the HTML code. The first form found
  463. * either within the focused frame, or across frames,
  464. * will be the one returned.
  465. * @param string $id Form label.
  466. * @return SimpleForm Form object containing the matching ID.
  467. * @access public
  468. */
  469. function getFormById($id) {
  470. return $this->findForm('getFormById', $id);
  471. }
  472. /**
  473. * General form finder. Will search all the frames or
  474. * just the one in focus.
  475. * @param string $method Method to use to find in a page.
  476. * @param string $attribute Label, name or ID.
  477. * @return SimpleForm Form object containing the matching ID.
  478. * @access private
  479. */
  480. protected function findForm($method, $attribute) {
  481. if (is_integer($this->focus)) {
  482. return $this->findFormInFrame(
  483. $this->frames[$this->focus],
  484. $this->focus,
  485. $method,
  486. $attribute);
  487. }
  488. for ($i = 0; $i < count($this->frames); $i++) {
  489. $form = $this->findFormInFrame(
  490. $this->frames[$i],
  491. $i,
  492. $method,
  493. $attribute);
  494. if ($form) {
  495. return $form;
  496. }
  497. }
  498. $null = null;
  499. return $null;
  500. }
  501. /**
  502. * Finds a form in a page using a form finding method. Will
  503. * also tag the form with the frame name it belongs in.
  504. * @param SimplePage $page Page content of frame.
  505. * @param integer $index Internal frame representation.
  506. * @param string $method Method to use to find in a page.
  507. * @param string $attribute Label, name or ID.
  508. * @return SimpleForm Form object containing the matching ID.
  509. * @access private
  510. */
  511. protected function findFormInFrame($page, $index, $method, $attribute) {
  512. $form = $this->frames[$index]->$method($attribute);
  513. if (isset($form)) {
  514. $form->setDefaultTarget($this->getPublicNameFromIndex($index));
  515. }
  516. return $form;
  517. }
  518. /**
  519. * Sets a field on each form in which the field is
  520. * available.
  521. * @param SimpleSelector $selector Field finder.
  522. * @param string $value Value to set field to.
  523. * @return boolean True if value is valid.
  524. * @access public
  525. */
  526. function setField($selector, $value) {
  527. if (is_integer($this->focus)) {
  528. $this->frames[$this->focus]->setField($selector, $value);
  529. } else {
  530. for ($i = 0; $i < count($this->frames); $i++) {
  531. $this->frames[$i]->setField($selector, $value);
  532. }
  533. }
  534. }
  535. /**
  536. * Accessor for a form element value within a page.
  537. * @param SimpleSelector $selector Field finder.
  538. * @return string/boolean A string if the field is
  539. * present, false if unchecked
  540. * and null if missing.
  541. * @access public
  542. */
  543. function getField($selector) {
  544. for ($i = 0; $i < count($this->frames); $i++) {
  545. $value = $this->frames[$i]->getField($selector);
  546. if (isset($value)) {
  547. return $value;
  548. }
  549. }
  550. return null;
  551. }
  552. }
  553. ?>