Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556// Original path: bite-project/tools/rpf/extension/src/libs/recordmanager.js (Google BITE, Apache 2.0, copied unmodified below this line, for reading only, not built)// Copyright 2010 Google Inc. All Rights Reserved.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
/** * @fileoverview This file contains the recording manager, * which has functions to communicate with the page under * record, for example: init page, start recording, etc.. * * @author phu@google.com (Po Hu) */
goog.provide('rpf.RecordManager');goog.require('rpf.ScriptManager');
/** * A class for managing the recorder part of RPF. * @param {rpf.ScriptManager} scriptMgr The script manager. * @param {function()} initAllListeners To init all the listeners. * @param {function()} removeAllListeners To remove all the listeners. * @param {function()} addTestTabRemovedListener To add the test tab removed * listener. * @param {function(Object, function(*)=)} sendMessageToConsole The * function to send message to console world. * @param {function(Array, number, boolean, number, function()=)} * executeMultipleScripts The function to execute multiple scripts. * @constructor * @export */rpf.RecordManager = function(scriptMgr, initAllListeners, removeAllListeners, addTestTabRemovedListener, sendMessageToConsole, executeMultipleScripts) { /** * The script manager. * @type {rpf.ScriptManager} * @private */ this.scriptMgr_ = scriptMgr;
/** * To init all the listeners. * @type {function()} * @private */ this.initAllListeners_ = initAllListeners;
/** * To remove all the listeners. * @type {function()} * @private */ this.removeAllListeners_ = removeAllListeners;
/** * To add the test tab removed listener. * @type {function()} * @private */ this.addTestTabRemovedListener_ = addTestTabRemovedListener;
/** * The function to send message to console world. * @type {function(Object, function(*)=)} * @private */ this.sendMessageToConsole_ = sendMessageToConsole;
/** * The function to send message to console world. * @type {function(Array, number, boolean, number, function()=)} * @private */ this.executeMultipleScripts_ = executeMultipleScripts;
/** * Whether the page has been initialized. * @type {boolean} * @private */ this.pageInitialized_ = false;
/** * The tab id of the page under test. * @type {number} * @private */ this.testTabId_ = -1;
/** * The window id of the page under test. * @type {number} * @private */ this.testWindowId_ = 0;
/** * The latest recorded event type. * @type {string} * @private */ this.latestEvent_ = '';
/** * Whether it is recording. * @type {boolean} * @private */ this.isRecording_ = false;
/** * The recording mode. * @type {string} * @private */ this.recordingMode_ = '';
/** * The recording info. * @type {Object} * @private */ this.recordingInfo_ = {};};
/** * @const The user action related JS lib. * @type {string} * @private */rpf.RecordManager.GET_ACTION_INFO_JS_ = 'getactioninfo_script.js';
/** * @const The element's descriptive info related JS lib. * @type {string} * @export */rpf.RecordManager.ELEMENT_HELPER = 'elementhelper_script.js';
/** * Starts recording. * @param {Object=} opt_info The optional recording config info. * @param {number=} opt_tabId The tab id. * @param {number=} opt_windowId The window id. * @return {boolean} Whether it successfully started recording. */rpf.RecordManager.prototype.startRecording = function( opt_info, opt_tabId, opt_windowId) { var info = opt_info || {}; if (opt_tabId && opt_windowId) { this.setRecordingTab(opt_tabId, opt_windowId); } this.startRecording_(info); return true;};
/** * Callback after checking whether the tab under record exists. * @param {Function} callback The callback function. * @param {Object} injectedTabs The injected tabs. * @param {Tab} tab The tab. * @private */rpf.RecordManager.prototype.checkTestTabExistsCallback_ = function( callback, injectedTabs, tab) { var result = {}; if (!tab) { result['success'] = false; result['message'] = 'The page under record is missing. ' + 'For more details, please check ' + '<a href="https://sites.google.com/site/' + 'rpfwiki/q-a#selectnewtab" target="_blank">here</a>.'; } else { if (injectedTabs[tab.id]) { result['success'] = true; result['url'] = tab.url; this.scriptMgr_.startUrl = tab.url; } else { result['success'] = false; result['message'] = 'Maybe you would need to refresh the page' + ' under record. ' + 'For more details, please check ' + '<a href="https://sites.google.com/site/' + 'rpfwiki/q-a#cannotrecord" target="_blank">here</a>.'; } } callback(result);};
/** * Checks whether the tab under record exists. * @param {Function} callback The callback function. * @param {Object} tabs The tabs that have injection. */rpf.RecordManager.prototype.checkTestTabExists = function(callback, tabs) { chrome.tabs.get( this.testTabId_, goog.bind(this.checkTestTabExistsCallback_, this, callback, tabs));};
/** * Starts recording. * @param {Object} info The recording config info. * @private */rpf.RecordManager.prototype.startRecording_ = function(info) { this.recordingMode_ = 'rpf'; this.recordingInfo_ = info; this.initAllListeners_(); this.isRecording_ = true; this.highlightRecordTab(); this.executePageInit(goog.bind(this.startRecordingInPage, this));};
/** * Enum for commands will be run in page. * @enum {string} * @export */rpf.RecordManager.CmdCode = { CLEAR: 'recordHelper.stopRecording();', BLOCK_MODE: 'recordHelper.validator_.setWaitBlockElemMode();', ELEMS_IN_BLOCK_MODE: 'recordHelper.validator_.setWaitElemsInBlockMode();', CLEAR_BLOCK_MODE: 'recordHelper.validator_.clearBlockMode();', START: 'recordHelper.startRecording();'};
/** * Clears the listeners inside the page under test. * @param {rpf.RecordManager.CmdCode|string} cmd * The command that will be run in page. * @param {boolean=} opt_allFrame Whether to execute the command in all frames. * @export */rpf.RecordManager.prototype.runCode = function(cmd, opt_allFrame) { var allFrame = opt_allFrame || false; var wrappedCmd = 'try {' + cmd + '} catch(e) {console.log("The cmd was not found.")}'; chrome.tabs.executeScript( this.testTabId_, {code: wrappedCmd, allFrames: allFrame});};
/** * Stops recording. * @export */rpf.RecordManager.prototype.stopRecording = function() { this.isRecording_ = false; this.removeAllListeners_(); if (this.testTabId_ > 0) { chrome.tabs.sendRequest( this.testTabId_, {recordAction: Bite.Constants.RECORD_ACTION.STOP_RECORDING, params: {}}); }};
/** * Sets the tab and window ids of the page under test. * @param {number=} opt_tabId The sender tab id. * @param {number=} opt_windowId The sender window id. * @param {Function=} opt_callback The callback function. * @export */rpf.RecordManager.prototype.setRecordingTab = function( opt_tabId, opt_windowId, opt_callback) { var callback = opt_callback || goog.nullFunction; if (opt_tabId && opt_windowId) { this.setStatusBeforeRecording_(opt_tabId, opt_windowId, callback); } else { chrome.tabs.getSelected( null, goog.bind(this.callbackSetRecordingTab_, this, callback)); }};
/** * Sets the tab and window ids. * @param {Function} callback The callback function. * @param {Object} tab Chrome tab object. * @private */rpf.RecordManager.prototype.callbackSetRecordingTab_ = function(callback, tab) { this.checkUrlMatching_(tab.url); this.setStatusBeforeRecording_(tab.id, tab.windowId, callback);};
/** * Warns users if the URL is not matching. * @param {string} url The tab under record's url. * @private */rpf.RecordManager.prototype.checkUrlMatching_ = function(url) { // To avoid making users confused about why pages are not recordable, we // have to warn them if the current URL is not allowed. if (goog.string.contains(url, 'chrome.google.com/webstore') || goog.string.startsWith(url, 'chrome://') || goog.string.startsWith(url, 'about:')) { alert('The selected page does not allow content script. ' + 'For example, blank page, webstore page, other non-http(s) pages. ' + 'Please select a valid page to record. For more details,' + ' please refer to https://sites.google.com/site' + '/rpfwiki/q-a#cannotrecord'); }};
/** * Sets status and to prepare recording. * @param {number} tabId The sender tab id. * @param {number} windowId The sender window id. * @param {Function} callback The callback function. * @private */rpf.RecordManager.prototype.setStatusBeforeRecording_ = function( tabId, windowId, callback) { this.testTabId_ = tabId; this.testWindowId_ = windowId; this.addTestTabRemovedListener_(); callback();};
/** * Brings the tab under record foreground. * @param {function()=} opt_callback An optional callback function. * @export */rpf.RecordManager.prototype.highlightRecordTab = function(opt_callback) { var callback = opt_callback || callback; var tabCallback = /** @type {function(Tab)} */ (callback); var windowCallback = /** @type {function()} */ (callback); chrome.tabs.update(this.testTabId_, { selected: true }, tabCallback); chrome.windows.update(this.testWindowId_, { focused: true }, windowCallback);};
/** * Enters updater mode. * @export */rpf.RecordManager.prototype.enterUpdaterMode = function() { this.recordingMode_ = 'updater'; this.highlightRecordTab(); chrome.tabs.executeScript(this.testTabId_, {code: 'recordHelper.enterUpdaterMode();'});};
/** * Opens the xpath finder in page. * @export */rpf.RecordManager.prototype.openXpathFinder = function() { this.highlightRecordTab(); chrome.tabs.sendRequest( this.testTabId_, {recordAction: Bite.Constants.RECORD_ACTION.OPEN_XPATH_FINDER});};
/** * Ends updater mode. * @export */rpf.RecordManager.prototype.endUpdaterMode = function() { chrome.tabs.executeScript(this.testTabId_, {code: 'recordHelper.endUpdaterMode();'});};
/** * Tests a locator. * @param {Array} locators The locator array. * @param {function(Object)} callback The callback function. * @export */rpf.RecordManager.prototype.testLocator = function(locators, callback) { chrome.tabs.sendRequest( this.testTabId_, {command: 'testLocator', locators: locators}, /** @type {function(*)} */ (callback));};
/** * Tests a descriptor in the page under test. * @param {Object} descriptor A descriptor object for an element. * @private */rpf.RecordManager.prototype.testDescriptor_ = function(descriptor) { this.highlightRecordTab(); chrome.tabs.executeScript(this.testTabId_, {code: 'recordHelper.outlineElems_(' + descriptor + ');'});};
/** * Tests a descriptor in the page under test. * @param {Object} descriptor A descriptor object for an element. * @export */rpf.RecordManager.prototype.testDescriptor = function(descriptor) { var descStr = JSON.stringify(descriptor); console.log('Test descriptor in page!'); this.executePageInit(goog.bind(this.testDescriptor_, this, descStr));};
/** * Tests block validation in the page under test. * @param {Object} blockObj An object of block info. * @export */rpf.RecordManager.prototype.testBlockValidation = function(blockObj) { var blockStr = JSON.stringify(blockObj); chrome.tabs.sendRequest( this.testTabId_, {command: 'testValidateBlock', descriptor: blockStr}, function(response) { if (response.result != Bite.Constants.WorkerResults.PASS) { console.log('Testing Error:' + response.result); } });};
/** * Gets the root array. * @return {Array} The root element array. * @private */rpf.RecordManager.prototype.getRoots_ = function() { var results = []; if (this.recordingInfo_ && this.recordingInfo_['pageMap']) { var pageMap = this.recordingInfo_['pageMap']; if (typeof pageMap == 'string') { pageMap = goog.json.parse(pageMap); } for (var item in pageMap) { results.push( {'xpath': item, 'className': pageMap[item]}); } } return results;};
/** * Starts recording in content script. * @export */rpf.RecordManager.prototype.startRecordingInPage = function() { if (this.recordingMode_ == 'updater') { this.enterUpdaterMode(); } else { var roots = this.getRoots_(); chrome.tabs.sendRequest( this.testTabId_, {recordAction: Bite.Constants.RECORD_ACTION.START_RECORDING, params: {'rootArr': roots, 'xpathFinderOn': this.recordingInfo_['xpathFinderOn']}}); }};
/** * Executes three content scripts to initialize recording on a page. * @param {function()=} opt_callback An optional callback function. * @export */rpf.RecordManager.prototype.executePageInit = function(opt_callback) { if (this.pageInitialized_) { this.pageInitialized_ = false; this.runCode(rpf.RecordManager.CmdCode.CLEAR, true); } this.pageInitialized_ = true; if (opt_callback) { opt_callback(); }};
/** * @return {boolean} Whether is recording. * @export */rpf.RecordManager.prototype.isRecording = function() { return this.isRecording_;};
/** * @return {number} The tab id of the page under test. * @export */rpf.RecordManager.prototype.getTestTabId = function() { return this.testTabId_;};
/** * @param {number} tabId The tab id of the page under test. * @export */rpf.RecordManager.prototype.setTestTabId = function(tabId) { this.testTabId_ = tabId;};
/** * @return {number} The tab id of the page under test. * @export */rpf.RecordManager.prototype.getTestWindowId = function() { return this.testWindowId_;};
/** * @param {number} windowId The tab id of the page under test. * @export */rpf.RecordManager.prototype.setTestWindowId = function(windowId) { this.testWindowId_ = windowId;};