├── .gitignore ├── README.md ├── api-test-extension ├── css │ └── styles.css ├── icons │ ├── icon128.png │ ├── icon16.png │ ├── icon19.png │ ├── icon48.png │ └── icon80.png ├── js │ └── jquery │ │ └── jquery.js ├── manifest.json └── src │ ├── APITest │ └── test.js │ ├── browser_action │ ├── bg.js │ ├── browser_action.html │ ├── browser_action.js │ ├── popup.html │ └── run_test.js │ └── tests │ ├── alarms.js │ ├── bookmarks.js │ ├── browser_action.js │ ├── browsing_data.js │ ├── common.js │ ├── content_scripts.js │ ├── content_scripts_bg.js │ ├── content_settings.js │ ├── context_menus.js │ ├── cookies.js │ ├── downloads.js │ ├── extension.js │ ├── history.js │ ├── idle.js │ ├── notifications.js │ ├── popup_scripts.js │ ├── privacy.js │ ├── sessions.js │ ├── storage.js │ ├── tabs.js │ ├── tabs_bg.js │ ├── top_sites.js │ ├── web_navigation.js │ ├── web_request.js │ └── windows.js └── docs └── images ├── developer-mode.png ├── home-screen.png ├── installed-extensions-settings.png ├── load-unpacked-extension.png ├── pick-manifest.png └── successfull-installation.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | *.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to install extensions 2 | - Install Yandex Browser from Google Play (https://play.google.com/store/apps/details?id=com.yandex.browser) 3 | 4 | ## What's new 5 | 2016-11-14 6 | - Added tests for alarms API. 7 | 8 | 2016-11-10 9 | - Added tests for privacy API. 10 | 11 | 2016-09-07 (build 16.9.0.886) 12 | - We have published Yandex Browser Alpha with extensions support. You can download it from Google Play: 13 | https://play.google.com/store/apps/details?id=com.yandex.browser.alpha 14 | 15 | 2016-08-24 (build 16.7.211760) 16 | - Fix Issue #1: Action menu doesn't dynamically resize to content. 17 | 18 | 2016-08-16 19 | - Add more permissions (for screenshot mostly) 20 | - Update UI 21 | 22 | 2016-08-11 23 | - Report generated by Browser API Test extension 24 | 25 | 2016-08-07 26 | - add tabCapture and desktopCapture permissions support 27 | - add activeTab permission support 28 | 29 | 2016-08-05 30 | - Implement chrome.tabs.remove support 31 | - Implement chomre.tabs events 32 | 33 | ## Custom Extension Install In Developer Mode 34 | 35 | ### 1. Copy an unzipped folder with an extension source code to your mobile device 36 | 37 | ### 2. Type browser://extensions/ 38 | 39 | 40 | ### 3. Turn on the Developer Mode by checking the box 41 | 42 | 43 | ### 4. Tap "Load unpacked extension" 44 | 45 | 46 | ### 5. Find your extension folder and pick manifest.json 47 | 48 | 49 | 50 | ## Install From Store 51 | 52 | 1) Using the "Settings -> Extensions -> Opera Store" or "Settings -> Extensions -> Google Chrome Webstore" menu open the store page 53 | 54 | 2) Find the extension and add them to browser 55 | -------------------------------------------------------------------------------- /api-test-extension/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f2f2f2; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 12px; 5 | } 6 | 7 | .title { 8 | padding: 0 10px 0 10px; 9 | margin-bottom: 20px; 10 | } 11 | 12 | .report-container { 13 | width: 100%; 14 | } 15 | 16 | .test-set-container { 17 | padding: 0 10px 0 10px; 18 | margin-bottom: 30px; 19 | } 20 | 21 | .auto-test-set-title { 22 | min-height: 40px; 23 | min-width: 240px; 24 | padding-left: 10px; 25 | line-height: 40px; 26 | font-size: 1.6em; 27 | border: #808080 2px solid; 28 | } 29 | 30 | .manual-test-set-title { 31 | min-height: 40px; 32 | line-height: 40px; 33 | font-size: 1.6em; 34 | min-width: 240px; 35 | padding-left: 10px; 36 | margin-top: -2px; 37 | border: gray 2px solid; 38 | background-color: #337ab7; 39 | } 40 | 41 | .auto-tests-list { 42 | border: gray 2px solid; 43 | border-bottom-width: 1px; 44 | margin-top: -2px; 45 | } 46 | 47 | .manual-tests-list { 48 | border: gray 2px solid; 49 | border-bottom-width: 1px; 50 | margin-top: -2px 51 | } 52 | 53 | .auto-test-item { 54 | padding-left: 10px; 55 | min-height: 32px; 56 | line-height: 32px; 57 | font-size: 1.4em; 58 | } 59 | 60 | .manual-test-item { 61 | padding-left: 10px; 62 | min-height: 32px; 63 | line-height: 32px; 64 | font-size: 1.4em; 65 | background-color: #d9edf7; 66 | color: #333; 67 | } 68 | 69 | .line { 70 | height: 1px; 71 | background-color: gray 72 | } 73 | 74 | .button-red, #history-delete-all-button { 75 | font-size: 1em; 76 | background-color: #c52222; 77 | color: white; 78 | border: 1px grey solid; 79 | cursor: pointer; 80 | } 81 | 82 | .button-blue, #check-idle-button, #check-lock-button { 83 | font-size: 1em; 84 | background-color: #2323c4; 85 | color: white; 86 | border: 1px grey solid; 87 | cursor: pointer; 88 | } 89 | 90 | .button-green { 91 | font-size: 1em; 92 | background-color: #23c223; 93 | color: white; 94 | border: 1px #006400 solid; 95 | cursor: pointer; 96 | } 97 | 98 | .title-container { 99 | display: inline-block; 100 | width: 180px; 101 | margin-right: 10px; 102 | } 103 | 104 | .save-report-container { 105 | display: inline-block; 106 | float: right; 107 | margin: 0.7em 0 0 0; 108 | font-size: 1.4em; 109 | } 110 | -------------------------------------------------------------------------------- /api-test-extension/icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/browser-extensions/117c68024e86da840454940adbd147ab42e8b0bd/api-test-extension/icons/icon128.png -------------------------------------------------------------------------------- /api-test-extension/icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/browser-extensions/117c68024e86da840454940adbd147ab42e8b0bd/api-test-extension/icons/icon16.png -------------------------------------------------------------------------------- /api-test-extension/icons/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/browser-extensions/117c68024e86da840454940adbd147ab42e8b0bd/api-test-extension/icons/icon19.png -------------------------------------------------------------------------------- /api-test-extension/icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/browser-extensions/117c68024e86da840454940adbd147ab42e8b0bd/api-test-extension/icons/icon48.png -------------------------------------------------------------------------------- /api-test-extension/icons/icon80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/browser-extensions/117c68024e86da840454940adbd147ab42e8b0bd/api-test-extension/icons/icon80.png -------------------------------------------------------------------------------- /api-test-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "version": "0.1.9", 4 | "name": "Browser API Test", 5 | "description": "Tests Chrome API for browser extensions.", 6 | "icons": { 7 | "16": "icons/icon16.png", 8 | "128": "icons/icon128.png" 9 | }, 10 | "browser_action": { 11 | "default_title": "", 12 | "default_icon": "icons/icon19.png", 13 | "default_popup": "src/browser_action/popup.html" 14 | }, 15 | "background": { 16 | "scripts": ["src/browser_action/bg.js"] 17 | }, 18 | "content_scripts": [ 19 | { 20 | "matches": [ 21 | "http://ya.ru/", 22 | "https://ya.ru/" 23 | ], 24 | "js": ["src/tests/content_scripts_bg.js"] 25 | } 26 | ], 27 | "commands": {}, 28 | "permissions": [ 29 | "tabs", 30 | "contextMenus", 31 | "cookies", 32 | "http://*/*", "https://*/*", 33 | "idle", 34 | "notifications", 35 | "bookmarks", 36 | "browsingData", 37 | "history", 38 | "downloads", 39 | "idle", 40 | "notifications", 41 | "topSites", 42 | "sessions", 43 | "webNavigation", 44 | "storage", 45 | "webRequest", 46 | "privacy", 47 | "alarms", 48 | "contentSettings" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /api-test-extension/src/APITest/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const TestType = { 4 | REQUIRE: 0, 5 | SUGGEST: 1 6 | }; 7 | 8 | const TestSetResult = { 9 | PASS: 0, 10 | PARTIALLY: 1, 11 | FAIL: 2 12 | }; 13 | 14 | const default_params = { 15 | async: false, 16 | hideOnSuccess: false 17 | }; 18 | 19 | const SINGLE_TEST_TIME_LIMIT = 3000; // ms 20 | 21 | class Test { 22 | constructor(fn, type, params) { 23 | this.params = JSON.parse(JSON.stringify(default_params)); 24 | if (params !== undefined) { 25 | for (let key in params) { 26 | if (params.hasOwnProperty(key)) { 27 | this.params[key] = params[key]; 28 | } 29 | } 30 | } 31 | 32 | if (this.params.async) { 33 | this.fn = fn; 34 | } else { 35 | this.fn = function () { 36 | return new Promise((resolve, reject) => { 37 | let msg = fn(); 38 | if (msg === '') { 39 | resolve(msg); 40 | } else { 41 | reject(msg); 42 | } 43 | }); 44 | }; 45 | } 46 | 47 | this.type = type; 48 | } 49 | 50 | run() { 51 | let self = this; 52 | return new Promise(resolve => { 53 | let resolved = false, tl_exceeded = false; 54 | 55 | try { 56 | this.fn().then(msg => { 57 | if (tl_exceeded) return; 58 | resolved = true; 59 | 60 | resolve({ 61 | status: TestSetResult.PASS, 62 | msg: msg, 63 | hideOnSuccess: self.params.hideOnSuccess 64 | }); 65 | }, msg => { 66 | if (tl_exceeded) return; 67 | resolved = true; 68 | 69 | if (this.type == TestType.SUGGEST) { 70 | resolve({ 71 | status: TestSetResult.PARTIALLY, 72 | msg: msg, 73 | hideOnSuccess: self.params.hideOnSuccess 74 | }); 75 | } else { 76 | resolve({ 77 | status: TestSetResult.FAIL, 78 | msg: msg, 79 | hideOnSuccess: self.params.hideOnSuccess 80 | }); 81 | } 82 | } 83 | ); 84 | } catch (e) { 85 | resolve({ 86 | status: TestSetResult.FAIL, 87 | msg: "Exception occurred during test running: " + e, 88 | hideOnSuccess: false 89 | }) 90 | } 91 | 92 | setTimeout(() => { 93 | if (!resolved) { 94 | resolve({ 95 | status: TestSetResult.FAIL, 96 | msg: `Time limit for test exceeded: ${SINGLE_TEST_TIME_LIMIT}ms` 97 | }); 98 | } 99 | }, SINGLE_TEST_TIME_LIMIT); 100 | }); 101 | } 102 | } 103 | 104 | class TestSet { 105 | constructor() { 106 | this.tests = []; 107 | this.manual_tests = []; 108 | this.fns = []; 109 | } 110 | 111 | suggest(name, fn, params) { 112 | this.tests[name] = new Test(fn, TestType.SUGGEST, params); 113 | return this; 114 | } 115 | 116 | require(name, fn, params) { 117 | this.tests[name] = new Test(fn, TestType.REQUIRE, params); 118 | return this; 119 | } 120 | 121 | manual(id, html) { 122 | this.manual_tests[id] = html; 123 | return this; 124 | } 125 | 126 | report_ready(fn) { 127 | this.fns.push(fn); 128 | return this; 129 | } 130 | 131 | runAll() { 132 | let tests = this.tests; 133 | return { 134 | auto_tests: [...function* () { 135 | for (let key in tests) { 136 | if (tests.hasOwnProperty(key)){ 137 | yield key; 138 | } 139 | } 140 | }()].reduce((prev, curr) => { 141 | return prev.then(results => new Promise(resolve => { 142 | tests[curr].run().then(res => { 143 | resolve(results.concat([{ 144 | name: curr, 145 | result: res 146 | }])); 147 | }); 148 | })) 149 | }, Promise.resolve([{}])), 150 | manual_tests: this.manual_tests 151 | } 152 | } 153 | } 154 | 155 | class APITest { 156 | constructor() { 157 | this.sets = []; 158 | this.res = undefined; 159 | this.fns = []; 160 | } 161 | 162 | addTestSet(name, test_set) { 163 | this.sets[name] = test_set; 164 | return this; 165 | } 166 | 167 | runAll() { 168 | let sets = this.sets; 169 | let idx = 1, len = Object.keys(sets).length; 170 | 171 | this.res = [...function* () { 172 | for (let key in sets) { 173 | if (sets.hasOwnProperty(key)) { 174 | yield key 175 | } 176 | } 177 | }()].reduce((prev, curr) => { 178 | return prev.then(results => new Promise(resolve => { 179 | this.fns = this.fns.concat(sets[curr].fns); 180 | let tmp = sets[curr].runAll(); 181 | tmp.auto_tests.then(res => { 182 | if (idx == len) { 183 | $('#status').detach(); 184 | } else { 185 | $('#status').text(`Running... ${idx}/${len}`); 186 | idx++; 187 | } 188 | 189 | resolve(results.concat([{ 190 | title: curr, 191 | auto_tests: res.slice(1), 192 | manual_tests: tmp.manual_tests 193 | }])); 194 | }); 195 | })) 196 | }, Promise.resolve([{}])); 197 | 198 | return this.res; 199 | } 200 | 201 | htmlReport() { 202 | let bg_color = ["#dff0d8", "#fcf8e3", "#f2dede"]; 203 | let text_color = ["#3c763d", "#8a6d3b", "#a94442"]; 204 | let title_color = ["#5cb85c", "#f0ad4e", "#d9534f"]; 205 | let test_results = ["[Success]", "[Partially]", "[Failed]"]; 206 | const div = '
'; 207 | 208 | if (this.res) { 209 | return new Promise(resolve => { 210 | this.res.then(res => { 211 | let $html = $(div).addClass('report-container').append([...function* () { 212 | for (let test_set of res.slice(1)) { 213 | yield $(div).addClass('test-set-container').append([...function* () { 214 | let test_set_status = [...function* () { 215 | for (let i of test_set.auto_tests) { 216 | yield i.result.status; 217 | } 218 | }()].reduce((prev, next) => Math.max(prev, next)); 219 | 220 | yield $(div) 221 | .addClass('auto-test-set-title') 222 | .css('background-color', title_color[test_set_status]) 223 | .text(`${test_set.title}: ${test_results[test_set_status]}`); 224 | 225 | let $tests_list = $(div).addClass('auto-tests-list'); 226 | if (test_set_status == TestSetResult.PASS) { 227 | $tests_list.hide(); 228 | } 229 | 230 | yield $tests_list.append([...function* () { 231 | for (let test of test_set.auto_tests) { 232 | let status = test.result.status == TestSetResult.PASS ? "OK" : "Fail"; 233 | let msg = test.result.msg === '' ? '' : '[' + test.result.msg + ']'; 234 | 235 | if (test.result.status != TestSetResult.PASS || !test.result.hideOnSuccess) { 236 | yield $(div) 237 | .addClass('auto-test-item') 238 | .css({ 239 | 'background-color': bg_color[test.result.status], 240 | 'color': text_color[test.result.status], 241 | }) 242 | .text(`${test.name}: ${status} ${msg}`); 243 | 244 | yield $(div).addClass('line'); 245 | } 246 | } 247 | }()]); 248 | 249 | if (Object.keys(test_set.manual_tests).length) { 250 | yield $(div) 251 | .addClass('manual-test-set-title') 252 | .text("Manual Checklist"); 253 | 254 | yield $(div).addClass('manual-tests-list').append([...function* () { 255 | for (let test in test_set.manual_tests) { 256 | if (test_set.manual_tests.hasOwnProperty(test)) { 257 | yield $(div) 258 | .addClass('manual-test-item') 259 | .attr('id', test) 260 | .html(test_set.manual_tests[test] + ": Not done yet"); 261 | yield $(div).addClass('line'); 262 | } 263 | } 264 | }()]); 265 | } 266 | }()]); 267 | } 268 | }()]); 269 | 270 | resolve($html); 271 | }); 272 | }); 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /api-test-extension/src/browser_action/bg.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let runtimeMsgFromPopup = { data: 'runtime_msg_from_popup' }; // Incoming 4 | let runtimeMsgToTab = { data: 'runtime_msg_from_background'}; // Outgoing 5 | 6 | let listener = function (response, sender, sendResponse) { 7 | if (response.data == runtimeMsgFromPopup.data) { 8 | sendResponse('Background'); 9 | chrome.runtime.sendMessage(runtimeMsgToTab, response => { 10 | if (response != 'Tab') { 11 | failManualTest( 12 | 'popup-scripts-test', 13 | "Message received by the background from the tab differs from expected" 14 | ) 15 | } else { 16 | chrome.runtime.onMessage.removeListener(listener); 17 | } 18 | }); 19 | } 20 | }; 21 | 22 | chrome.runtime.onMessage.addListener(listener); 23 | 24 | document.addEventListener("DOMContentLoaded", () => { 25 | let div = document.createElement("div"); 26 | div.setAttribute('id', 'bg-div-elem'); 27 | 28 | document.querySelector('body').appendChild(div); 29 | }); 30 | -------------------------------------------------------------------------------- /api-test-extension/src/browser_action/browser_action.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |

Browser API Tests

42 |
43 |
44 | 45 | 46 |
47 |
48 |
49 |
50 | 51 | -------------------------------------------------------------------------------- /api-test-extension/src/browser_action/browser_action.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | $(() => { 4 | let $body = $('body'); 5 | 6 | let test = new APITest(); 7 | 8 | test 9 | .addTestSet("Tabs", tabs_test) 10 | .addTestSet("Cookies", cookies_test) 11 | .addTestSet("Browser Action", browser_action_test) 12 | .addTestSet("Context Menus", context_menus_test) 13 | .addTestSet("Bookmarks", bookmarks_test) 14 | .addTestSet("History", history_test) 15 | .addTestSet("Browsing Data", browsing_data_test) 16 | .addTestSet("Downloads", downloads_test) 17 | .addTestSet("Idle", idle_test) 18 | .addTestSet("Notifications", notifications_test) 19 | .addTestSet("Top Sites", top_sites_test) 20 | .addTestSet("Sessions", sessions_test) 21 | .addTestSet("Web Navigation", web_navigation_test) 22 | .addTestSet("Storage", storage_test) 23 | .addTestSet("Web Request", web_request_test) 24 | .addTestSet("Windows", windows_test) 25 | .addTestSet("Privacy", privacy_test) 26 | .addTestSet("Alarms", alarms_test) 27 | .addTestSet("Content Settings", content_settings_test) 28 | .addTestSet("Content Scripts", content_scripts) 29 | .addTestSet("Popup Scripts", popup_scripts) 30 | .addTestSet("Extension", extension_test) 31 | ; // -- test -- // 32 | 33 | test.runAll(); 34 | test.htmlReport().then(res => $body.append(res)).then(() => { 35 | let $t = $(".auto-test-set-title, .manual-test-set-title"); 36 | 37 | $t.css('cursor', 'pointer').click(function () { 38 | $(this).next().toggle('fast'); 39 | }); 40 | 41 | test.fns.reduce((prev, curr) => { 42 | return prev.then(() => new Promise(resolve => { 43 | resolve(curr()); 44 | })); 45 | }, Promise.resolve()).then(() => {}); 46 | 47 | $('#save-report-button') 48 | .toggle('fast') 49 | .click(function () { 50 | $.ajax({ url: "/css/styles.css" }).done((css) => { 51 | let $body = $('html').clone(); 52 | 53 | $body.find('button').detach(); 54 | $body.find('#save-report-link').detach(); 55 | $body.find('script').detach(); 56 | $body.find(".auto-test-set-title, .manual-test-set-title").css('cursor', 'auto'); 57 | $('