| /*
|
| * Licensed to the Apache Software Foundation (ASF) under one or more
|
| * contributor license agreements. See the NOTICE file distributed with
|
| * this work for additional information regarding copyright ownership.
|
| * The ASF licenses this file to You 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.
|
| */ |
| |
| /* eslint-disable */ |
| |
| // these are default values, which can be overridden by the user on the options page |
| var userAleHost = 'http://localhost:8000'; |
| var userAleScript = 'userale-2.4.0.min.js'; |
| var toolUser = 'nobody'; |
| var toolName = 'test_app'; |
| var toolVersion = '2.4.0'; |
| |
| /* eslint-enable */ |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| var prefix = 'USERALE_'; |
| var CONFIG_CHANGE = prefix + 'CONFIG_CHANGE'; |
| var ADD_LOG = prefix + 'ADD_LOG'; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| /** |
| * Creates a function to normalize the timestamp of the provided event. |
| * @param {Object} e An event containing a timeStamp property. |
| * @return {timeStampScale~tsScaler} The timestamp normalizing function. |
| */ |
| function timeStampScale(e) { |
| var tsScaler; |
| if (e.timeStamp && e.timeStamp > 0) { |
| var delta = Date.now() - e.timeStamp; |
| /** |
| * Returns a timestamp depending on various browser quirks. |
| * @param {?Number} ts A timestamp to use for normalization. |
| * @return {Number} A normalized timestamp. |
| */ |
| |
| if (delta < 0) { |
| tsScaler = function tsScaler() { |
| return e.timeStamp / 1000; |
| }; |
| } else if (delta > e.timeStamp) { |
| var navStart = performance.timing.navigationStart; |
| tsScaler = function tsScaler(ts) { |
| return ts + navStart; |
| }; |
| } else { |
| tsScaler = function tsScaler(ts) { |
| return ts; |
| }; |
| } |
| } else { |
| tsScaler = function tsScaler() { |
| return Date.now(); |
| }; |
| } |
| return tsScaler; |
| } |
| |
| var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) { |
| if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { |
| if (ar || !(i in from)) { |
| if (!ar) ar = Array.prototype.slice.call(from, 0, i); |
| ar[i] = from[i]; |
| } |
| } |
| return to.concat(ar || Array.prototype.slice.call(from)); |
| }; |
| var BrowserInfo = /** @class */ (function () { |
| function BrowserInfo(name, version, os) { |
| this.name = name; |
| this.version = version; |
| this.os = os; |
| this.type = 'browser'; |
| } |
| return BrowserInfo; |
| }()); |
| var NodeInfo = /** @class */ (function () { |
| function NodeInfo(version) { |
| this.version = version; |
| this.type = 'node'; |
| this.name = 'node'; |
| this.os = process.platform; |
| } |
| return NodeInfo; |
| }()); |
| var SearchBotDeviceInfo = /** @class */ (function () { |
| function SearchBotDeviceInfo(name, version, os, bot) { |
| this.name = name; |
| this.version = version; |
| this.os = os; |
| this.bot = bot; |
| this.type = 'bot-device'; |
| } |
| return SearchBotDeviceInfo; |
| }()); |
| var BotInfo = /** @class */ (function () { |
| function BotInfo() { |
| this.type = 'bot'; |
| this.bot = true; // NOTE: deprecated test name instead |
| this.name = 'bot'; |
| this.version = null; |
| this.os = null; |
| } |
| return BotInfo; |
| }()); |
| var ReactNativeInfo = /** @class */ (function () { |
| function ReactNativeInfo() { |
| this.type = 'react-native'; |
| this.name = 'react-native'; |
| this.version = null; |
| this.os = null; |
| } |
| return ReactNativeInfo; |
| }()); |
| // tslint:disable-next-line:max-line-length |
| var SEARCHBOX_UA_REGEX = /alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/; |
| var SEARCHBOT_OS_REGEX = /(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/; |
| var REQUIRED_VERSION_PARTS = 3; |
| var userAgentRules = [ |
| ['aol', /AOLShield\/([0-9\._]+)/], |
| ['edge', /Edge\/([0-9\._]+)/], |
| ['edge-ios', /EdgiOS\/([0-9\._]+)/], |
| ['yandexbrowser', /YaBrowser\/([0-9\._]+)/], |
| ['kakaotalk', /KAKAOTALK\s([0-9\.]+)/], |
| ['samsung', /SamsungBrowser\/([0-9\.]+)/], |
| ['silk', /\bSilk\/([0-9._-]+)\b/], |
| ['miui', /MiuiBrowser\/([0-9\.]+)$/], |
| ['beaker', /BeakerBrowser\/([0-9\.]+)/], |
| ['edge-chromium', /EdgA?\/([0-9\.]+)/], |
| [ |
| 'chromium-webview', |
| /(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/, |
| ], |
| ['chrome', /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/], |
| ['phantomjs', /PhantomJS\/([0-9\.]+)(:?\s|$)/], |
| ['crios', /CriOS\/([0-9\.]+)(:?\s|$)/], |
| ['firefox', /Firefox\/([0-9\.]+)(?:\s|$)/], |
| ['fxios', /FxiOS\/([0-9\.]+)/], |
| ['opera-mini', /Opera Mini.*Version\/([0-9\.]+)/], |
| ['opera', /Opera\/([0-9\.]+)(?:\s|$)/], |
| ['opera', /OPR\/([0-9\.]+)(:?\s|$)/], |
| ['pie', /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/], |
| ['pie', /^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/], |
| ['netfront', /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/], |
| ['ie', /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/], |
| ['ie', /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/], |
| ['ie', /MSIE\s(7\.0)/], |
| ['bb10', /BB10;\sTouch.*Version\/([0-9\.]+)/], |
| ['android', /Android\s([0-9\.]+)/], |
| ['ios', /Version\/([0-9\._]+).*Mobile.*Safari.*/], |
| ['safari', /Version\/([0-9\._]+).*Safari/], |
| ['facebook', /FB[AS]V\/([0-9\.]+)/], |
| ['instagram', /Instagram\s([0-9\.]+)/], |
| ['ios-webview', /AppleWebKit\/([0-9\.]+).*Mobile/], |
| ['ios-webview', /AppleWebKit\/([0-9\.]+).*Gecko\)$/], |
| ['curl', /^curl\/([0-9\.]+)$/], |
| ['searchbot', SEARCHBOX_UA_REGEX], |
| ]; |
| var operatingSystemRules = [ |
| ['iOS', /iP(hone|od|ad)/], |
| ['Android OS', /Android/], |
| ['BlackBerry OS', /BlackBerry|BB10/], |
| ['Windows Mobile', /IEMobile/], |
| ['Amazon OS', /Kindle/], |
| ['Windows 3.11', /Win16/], |
| ['Windows 95', /(Windows 95)|(Win95)|(Windows_95)/], |
| ['Windows 98', /(Windows 98)|(Win98)/], |
| ['Windows 2000', /(Windows NT 5.0)|(Windows 2000)/], |
| ['Windows XP', /(Windows NT 5.1)|(Windows XP)/], |
| ['Windows Server 2003', /(Windows NT 5.2)/], |
| ['Windows Vista', /(Windows NT 6.0)/], |
| ['Windows 7', /(Windows NT 6.1)/], |
| ['Windows 8', /(Windows NT 6.2)/], |
| ['Windows 8.1', /(Windows NT 6.3)/], |
| ['Windows 10', /(Windows NT 10.0)/], |
| ['Windows ME', /Windows ME/], |
| ['Windows CE', /Windows CE|WinCE|Microsoft Pocket Internet Explorer/], |
| ['Open BSD', /OpenBSD/], |
| ['Sun OS', /SunOS/], |
| ['Chrome OS', /CrOS/], |
| ['Linux', /(Linux)|(X11)/], |
| ['Mac OS', /(Mac_PowerPC)|(Macintosh)/], |
| ['QNX', /QNX/], |
| ['BeOS', /BeOS/], |
| ['OS/2', /OS\/2/], |
| ]; |
| function detect(userAgent) { |
| if (!!userAgent) { |
| return parseUserAgent(userAgent); |
| } |
| if (typeof document === 'undefined' && |
| typeof navigator !== 'undefined' && |
| navigator.product === 'ReactNative') { |
| return new ReactNativeInfo(); |
| } |
| if (typeof navigator !== 'undefined') { |
| return parseUserAgent(navigator.userAgent); |
| } |
| return getNodeVersion(); |
| } |
| function matchUserAgent(ua) { |
| // opted for using reduce here rather than Array#first with a regex.test call |
| // this is primarily because using the reduce we only perform the regex |
| // execution once rather than once for the test and for the exec again below |
| // probably something that needs to be benchmarked though |
| return (ua !== '' && |
| userAgentRules.reduce(function (matched, _a) { |
| var browser = _a[0], regex = _a[1]; |
| if (matched) { |
| return matched; |
| } |
| var uaMatch = regex.exec(ua); |
| return !!uaMatch && [browser, uaMatch]; |
| }, false)); |
| } |
| function parseUserAgent(ua) { |
| var matchedRule = matchUserAgent(ua); |
| if (!matchedRule) { |
| return null; |
| } |
| var name = matchedRule[0], match = matchedRule[1]; |
| if (name === 'searchbot') { |
| return new BotInfo(); |
| } |
| // Do not use RegExp for split operation as some browser do not support it (See: http://blog.stevenlevithan.com/archives/cross-browser-split) |
| var versionParts = match[1] && match[1].split('.').join('_').split('_').slice(0, 3); |
| if (versionParts) { |
| if (versionParts.length < REQUIRED_VERSION_PARTS) { |
| versionParts = __spreadArray(__spreadArray([], versionParts, true), createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length), true); |
| } |
| } |
| else { |
| versionParts = []; |
| } |
| var version = versionParts.join('.'); |
| var os = detectOS(ua); |
| var searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua); |
| if (searchBotMatch && searchBotMatch[1]) { |
| return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]); |
| } |
| return new BrowserInfo(name, version, os); |
| } |
| function detectOS(ua) { |
| for (var ii = 0, count = operatingSystemRules.length; ii < count; ii++) { |
| var _a = operatingSystemRules[ii], os = _a[0], regex = _a[1]; |
| var match = regex.exec(ua); |
| if (match) { |
| return os; |
| } |
| } |
| return null; |
| } |
| function getNodeVersion() { |
| var isNode = typeof process !== 'undefined' && process.version; |
| return isNode ? new NodeInfo(process.version.slice(1)) : null; |
| } |
| function createVersionParts(count) { |
| var output = []; |
| for (var ii = 0; ii < count; ii++) { |
| output.push('0'); |
| } |
| return output; |
| } |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| detect(); |
| |
| /** |
| * Extract the millisecond and microsecond portions of a timestamp. |
| * @param {Number} timeStamp The timestamp to split into millisecond and microsecond fields. |
| * @return {Object} An object containing the millisecond |
| * and microsecond portions of the timestamp. |
| */ |
| function extractTimeFields(timeStamp) { |
| return { |
| milli: Math.floor(timeStamp), |
| micro: Number((timeStamp % 1).toFixed(3)) |
| }; |
| } |
| |
| function _arrayWithHoles(arr) { |
| if (Array.isArray(arr)) return arr; |
| } |
| |
| function _iterableToArrayLimit(r, l) { |
| var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; |
| if (null != t) { |
| var e, |
| n, |
| i, |
| u, |
| a = [], |
| f = !0, |
| o = !1; |
| try { |
| if (i = (t = t.call(r)).next, 0 === l) { |
| if (Object(t) !== t) return; |
| f = !1; |
| } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); |
| } catch (r) { |
| o = !0, n = r; |
| } finally { |
| try { |
| if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; |
| } finally { |
| if (o) throw n; |
| } |
| } |
| return a; |
| } |
| } |
| |
| function _arrayLikeToArray(arr, len) { |
| if (len == null || len > arr.length) len = arr.length; |
| for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; |
| return arr2; |
| } |
| |
| function _unsupportedIterableToArray(o, minLen) { |
| if (!o) return; |
| if (typeof o === "string") return _arrayLikeToArray(o, minLen); |
| var n = Object.prototype.toString.call(o).slice(8, -1); |
| if (n === "Object" && o.constructor) n = o.constructor.name; |
| if (n === "Map" || n === "Set") return Array.from(o); |
| if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); |
| } |
| |
| function _nonIterableRest() { |
| throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); |
| } |
| |
| function _slicedToArray(arr, i) { |
| return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); |
| } |
| |
| var sendIntervalId = null; |
| |
| /** |
| * Initializes the log queue processors. |
| * @param {Array} logs Array of logs to append to. |
| * @param {Object} config Configuration object to use when logging. |
| */ |
| function initSender(logs, config) { |
| if (sendIntervalId !== null) { |
| clearInterval(sendIntervalId); |
| } |
| sendIntervalId = sendOnInterval(logs, config); |
| sendOnClose(logs, config); |
| } |
| |
| /** |
| * Checks the provided log array on an interval, flushing the logs |
| * if the queue has reached the threshold specified by the provided config. |
| * @param {Array} logs Array of logs to read from. |
| * @param {Object} config Configuration object to be read from. |
| * @return {Number} The newly created interval id. |
| */ |
| function sendOnInterval(logs, config) { |
| return setInterval(function () { |
| if (!config.on) { |
| return; |
| } |
| if (logs.length >= config.logCountThreshold) { |
| sendLogs(logs.slice(0), config, 0); // Send a copy |
| logs.splice(0); // Clear array reference (no reassignment) |
| } |
| }, config.transmitInterval); |
| } |
| |
| /** |
| * Attempts to flush the remaining logs when the window is closed. |
| * @param {Array} logs Array of logs to be flushed. |
| * @param {Object} config Configuration object to be read from. |
| */ |
| function sendOnClose(logs, config) { |
| window.addEventListener("pagehide", function () { |
| if (config.on && logs.length > 0) { |
| // NOTE: sendBeacon does not support auth headers, |
| // so this will fail if auth is required. |
| // The alternative is to use fetch() with keepalive: true |
| // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon#description |
| // https://stackoverflow.com/a/73062712/9263449 |
| navigator.sendBeacon(config.url, JSON.stringify(logs)); |
| logs.splice(0); // clear log queue |
| } |
| }); |
| } |
| |
| /** |
| * Sends the provided array of logs to the specified url, |
| * retrying the request up to the specified number of retries. |
| * @param {Array} logs Array of logs to send. |
| * @param {string} config configuration parameters (e.g., to extract URL from & send the POST request to). |
| * @param {Number} retries Maximum number of attempts to send the logs. |
| */ |
| |
| // @todo expose config object to sendLogs replate url with config.url |
| function sendLogs(logs, config, retries) { |
| var req = new XMLHttpRequest(); |
| var data = JSON.stringify(logs); |
| req.open("POST", config.url); |
| if (config.authHeader) { |
| req.setRequestHeader("Authorization", config.authHeader); |
| } |
| req.setRequestHeader("Content-type", "application/json;charset=UTF-8"); |
| if (config.headers) { |
| Object.entries(config.headers).forEach(function (_ref) { |
| var _ref2 = _slicedToArray(_ref, 2), |
| header = _ref2[0], |
| value = _ref2[1]; |
| req.setRequestHeader(header, value); |
| }); |
| } |
| req.onreadystatechange = function () { |
| if (req.readyState === 4 && req.status !== 200) { |
| if (retries > 0) { |
| sendLogs(logs, config, retries--); |
| } |
| } |
| }; |
| req.send(data); |
| } |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| // inherent dependency on globals.js, loaded by the webext |
| |
| // browser is defined in firefox, but not in chrome. In chrome, they use |
| // the 'chrome' global instead. Let's map it to browser so we don't have |
| // to have if-conditions all over the place. |
| |
| var browser = browser || chrome; |
| var logs = []; |
| var config = { |
| autostart: true, |
| url: 'http://localhost:8000', |
| transmitInterval: 5000, |
| logCountThreshold: 5, |
| userId: null, |
| version: null, |
| resolution: 500, |
| time: timeStampScale({}), |
| on: true |
| }; |
| var sessionId = 'session_' + Date.now(); |
| var getTimestamp = typeof performance !== 'undefined' && typeof performance.now !== 'undefined' ? function () { |
| return performance.now() + performance.timing.navigationStart; |
| } : Date.now; |
| browser.storage.local.set({ |
| sessionId: sessionId |
| }); |
| browser.storage.local.get({ |
| userAleHost: userAleHost, |
| userAleScript: userAleScript, |
| toolUser: toolUser, |
| toolName: toolName, |
| toolVersion: toolVersion |
| }, storeCallback); |
| function storeCallback(item) { |
| config = Object.assign({}, config, { |
| url: item.userAleHost, |
| userId: item.toolUser, |
| sessionID: sessionId, |
| toolName: item.toolName, |
| toolVersion: item.toolVersion |
| }); |
| initSender(logs, config); |
| } |
| function dispatchTabMessage(message) { |
| browser.tabs.query({}, function (tabs) { |
| tabs.forEach(function (tab) { |
| browser.tabs.sendMessage(tab.id, message); |
| }); |
| }); |
| } |
| function packageBrowserLog(type, logDetail) { |
| var timeFields = extractTimeFields(getTimestamp()); |
| logs.push({ |
| 'target': null, |
| 'path': null, |
| 'clientTime': timeFields.milli, |
| 'microTime': timeFields.micro, |
| 'location': null, |
| 'type': 'browser.' + type, |
| 'logType': 'raw', |
| 'userAction': true, |
| 'details': logDetail, |
| 'userId': toolUser, |
| 'toolVersion': null, |
| 'toolName': null, |
| 'useraleVersion': null, |
| 'sessionID': sessionId |
| }); |
| } |
| browser.runtime.onMessage.addListener(function (message) { |
| switch (message.type) { |
| case CONFIG_CHANGE: |
| (function () { |
| var updatedConfig = Object.assign({}, config, { |
| url: message.payload.userAleHost, |
| userId: message.payload.toolUser, |
| toolName: message.payload.toolName, |
| toolVersion: message.payload.toolVersion |
| }); |
| initSender(logs, updatedConfig); |
| dispatchTabMessage(message); |
| })(); |
| break; |
| case ADD_LOG: |
| (function () { |
| logs.push(message.payload); |
| })(); |
| break; |
| default: |
| console.log('got unknown message type ', message); |
| } |
| }); |
| function getTabDetailById(tabId, onReady) { |
| browser.tabs.get(tabId, function (tab) { |
| onReady({ |
| active: tab.active, |
| audible: tab.audible, |
| incognito: tab.incognito, |
| index: tab.index, |
| muted: tab.mutedInfo ? tab.mutedInfo.muted : null, |
| pinned: tab.pinned, |
| selected: tab.selected, |
| tabId: tab.id, |
| title: tab.title, |
| url: tab.url, |
| windowId: tab.windowId |
| }); |
| }); |
| } |
| browser.tabs.onActivated.addListener(function (e) { |
| getTabDetailById(e.tabId, function (detail) { |
| packageBrowserLog('tabs.onActivated', detail); |
| }); |
| }); |
| browser.tabs.onCreated.addListener(function (tab, e) { |
| packageBrowserLog('tabs.onCreated', { |
| active: tab.active, |
| audible: tab.audible, |
| incognito: tab.incognito, |
| index: tab.index, |
| muted: tab.mutedInfo ? tab.mutedInfo.muted : null, |
| pinned: tab.pinned, |
| selected: tab.selected, |
| tabId: tab.id, |
| title: tab.title, |
| url: tab.url, |
| windowId: tab.windowId |
| }); |
| }); |
| browser.tabs.onDetached.addListener(function (tabId) { |
| getTabDetailById(tabId, function (detail) { |
| packageBrowserLog('tabs.onDetached', detail); |
| }); |
| }); |
| browser.tabs.onMoved.addListener(function (tabId) { |
| getTabDetailById(tabId, function (detail) { |
| packageBrowserLog('tabs.onMoved', detail); |
| }); |
| }); |
| browser.tabs.onRemoved.addListener(function (tabId) { |
| packageBrowserLog('tabs.onRemoved', { |
| tabId: tabId |
| }); |
| }); |
| browser.tabs.onZoomChange.addListener(function (e) { |
| getTabDetailById(e.tabId, function (detail) { |
| packageBrowserLog('tabs.onZoomChange', Object.assign({}, { |
| oldZoomFactor: e.oldZoomFactor, |
| newZoomFactor: e.newZoomFactor |
| }, detail)); |
| }); |
| }); |
| |
| /* |
| eslint-enable |
| */ |