├── README.md ├── chrome-user-agent.crx └── src ├── background.html ├── background.js ├── icon128.png ├── icon16.png ├── icon19.png ├── icon48.png ├── json2.js ├── manifest.json ├── options.html ├── options.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | # This project has been deprecated. 2 | 3 | Starting with version 17, the Google Chrome browser ships with support for 4 | modifying the User-Agent string. ([Click here for the Chromium 5 | issue](http://code.google.com/p/chromium/issues/detail?id=67063) and [here for 6 | more info on the 7 | feature](http://techdows.com/2011/12/google-chrome-now-has-built-in-user-agent-switcher.html).) 8 | This renders the dedicated user-agent switching extension obsolete, but the 9 | approach outlined here may still be relevant in the context of a larger 10 | extension. 11 | 12 | Chrome User-Agent Modifier Extension 13 | ==================================== 14 | 15 | Allows you to re-set the "User-Agent" string in Chrome's HTTP request headers. 16 | 17 | Installation Instructions 18 | ------------------------- 19 | 20 | **Download the Extension.** You can get the latest version of the [extension 21 | here](https://raw.github.com/jugglinmike/chrome-user-agent/master/chrome-user-agent.crx) 22 | (of course, you'll need [Google Chrome](http://www.google.com/chrome) installed 23 | first).

24 | 25 | **Install the Application.** Drag the extension file (it is named 26 | 'chrome-ua-modifier.crx' by default) into a Chrome browser window.

27 | 28 | How to Run 29 | ---------- 30 | 31 | **Changing Your User-Agent.** The extension options can be accessed either by 32 | selecting "Options" under the extension's entry on chrome://extensions or clicking on the 34 | extension's logo () next to Chrome's omnibox. Select 35 | any entry from the device list, and all future HTTP requests will be sent with 36 | the user-agent string displayed in the text area. Select "default" to use your 37 | default user-agent string. 38 | 39 | **Adding/Modifying "Devices".** Click "Add a Device" to create a new device--be 40 | sure to type a device name in the text area that appears. (You can double-click 41 | any device to change its name.) If you want to change the User-Agent string 42 | associated with any device, select it from the list and modify the text in the 43 | "Current User-Agent" text area. (I have found this page on zytrax.com to be a great resource for valid 46 | user-agent strings.) 47 | 48 | **Deleting "Devices".** If you no longer need a saved device, simply 49 | double-click on its entry in the list and clear the text area. 50 | -------------------------------------------------------------------------------- /chrome-user-agent.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jugglinmike/chrome-user-agent/64e82ed8b44bc4b77bcfc9c84bc414cb3bd34aae/chrome-user-agent.crx -------------------------------------------------------------------------------- /src/background.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | var requestFilter = { 2 | urls: [ 3 | "" 4 | ] 5 | }; 6 | 7 | chrome.webRequest.onBeforeSendHeaders.addListener(function(details) { 8 | var headers = details.requestHeaders; 9 | if( !localStorage['user-agent'] ) { 10 | return; 11 | } 12 | for(var i = 0, l = headers.length; i < l; ++i) { 13 | if( headers[i].name == 'User-Agent' ) { 14 | break; 15 | } 16 | } 17 | if(i < headers.length) { 18 | headers[i].value = localStorage['user-agent']; 19 | } 20 | return {requestHeaders: headers}; 21 | }, requestFilter, ['requestHeaders','blocking']); -------------------------------------------------------------------------------- /src/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jugglinmike/chrome-user-agent/64e82ed8b44bc4b77bcfc9c84bc414cb3bd34aae/src/icon128.png -------------------------------------------------------------------------------- /src/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jugglinmike/chrome-user-agent/64e82ed8b44bc4b77bcfc9c84bc414cb3bd34aae/src/icon16.png -------------------------------------------------------------------------------- /src/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jugglinmike/chrome-user-agent/64e82ed8b44bc4b77bcfc9c84bc414cb3bd34aae/src/icon19.png -------------------------------------------------------------------------------- /src/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jugglinmike/chrome-user-agent/64e82ed8b44bc4b77bcfc9c84bc414cb3bd34aae/src/icon48.png -------------------------------------------------------------------------------- /src/json2.js: -------------------------------------------------------------------------------- 1 | var JSON;if(!JSON){JSON={};} 2 | (function(){"use strict";function f(n){return n<10?'0'+n:n;} 3 | if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ 4 | f(this.getUTCMonth()+1)+'-'+ 5 | f(this.getUTCDate())+'T'+ 6 | f(this.getUTCHours())+':'+ 7 | f(this.getUTCMinutes())+':'+ 8 | f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};} 9 | var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} 10 | function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);} 11 | if(typeof rep==='function'){value=rep.call(holder,key,value);} 12 | switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} 13 | gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i", 22 | "tabs" 23 | ] 24 | } -------------------------------------------------------------------------------- /src/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Chrome UA Options

9 |
10 |

Current User-Agent

11 | 12 |

Device

13 |
    14 |
15 | 16 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var elem_ids = ['device_default_button', 'device_list', 'current_ua_field', 3 | 'current_ua_field', 4 | 'add_device_button', 'restore_defaults_button'], 5 | elems = {}, 6 | deviceListStr, 7 | defaultDevices = { 8 | apple_iphone_3g: { 9 | name: 'Apple iPhone 3G', 10 | ua: 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_0 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5A347 Safari/525.20' 11 | }, 12 | apple_iphone_4: { 13 | name: 'Apple iPhone 4', 14 | ua: 'Mozilla/5.0 (iPod; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7' 15 | }, 16 | apple_ipad: { 17 | name: 'Apple iPad', 18 | ua: 'Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; es-es) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405' 19 | }, 20 | htc_droid_incredible: { 21 | name: 'HTC Droid Incredible', 22 | ua: 'Mozilla/5.0 (Linux; U; Android 2.1-update1; en-us; ADR6300 Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17' 23 | }, 24 | palm_pre: { 25 | name: 'Palm Pre', 26 | ua: 'Mozilla/5.0 (webOS/1.0; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Pre/1.0' 27 | } 28 | }, 29 | devices = (localStorage['devices']) ? JSON.parse(localStorage['devices']) : defaultDevices, 30 | dom = { 31 | createLabel: function( deviceID, deviceName ) { 32 | var label = document.createElement('label'); 33 | label.setAttribute('for', 'device_' +deviceID+ '_button'); 34 | label.innerHTML = deviceName; 35 | label.addEventListener('click', listeners.deviceLabelClick); 36 | return label; 37 | }, 38 | createRadioButton: function( deviceID ) { 39 | var radioButton = document.createElement('input'); 40 | radioButton.setAttribute('type', 'radio'); 41 | radioButton.setAttribute('name', 'device'); 42 | radioButton.setAttribute('id', 'device_' +deviceID+ '_button'); 43 | radioButton.setAttribute('value', deviceID); 44 | radioButton.addEventListener('click', listeners.deviceButtonClick); 45 | return radioButton; 46 | }, 47 | createTextField: function( deviceID, deviceName ) { 48 | var textField = document.createElement('input'), 49 | elementID = deviceID ? 'device_'+deviceID+'_textfield' : 'new_field'; 50 | textField.setAttribute('type', 'text'); 51 | textField.setAttribute('value', deviceName || ''); 52 | textField.setAttribute('id', elementID); 53 | textField.addEventListener('blur', listeners.nameBlur); 54 | return textField; 55 | } 56 | }, 57 | listeners = { 58 | deviceLabelClick: function( event ) { 59 | var now = new Date().getTime(); 60 | if( !this.lastClicked || now - this.lastClicked > 1000 ) { 61 | this.lastClicked = now; 62 | return; 63 | } 64 | this.lastClicked = 0; 65 | var deviceID = event.target.getAttribute('for').match(/device_(.+)_button/i)[1], 66 | deviceName = event.target.innerHTML, 67 | textField = dom.createTextField(deviceID, deviceName); 68 | event.target.parentNode.replaceChild(textField, event.target); 69 | textField.focus(); 70 | }, 71 | nameBlur: function( event ) { 72 | var oldDeviceID, 73 | newDeviceID, 74 | deviceName = event.target.value; 75 | 76 | newDeviceID = deviceName.replace(/ /gi, '_').toLowerCase(); 77 | oldDeviceID = event.target.id.match(/device_(.+)_textfield/i); 78 | oldDeviceID = oldDeviceID && oldDeviceID[1]; 79 | 80 | 81 | if( oldDeviceID && newDeviceID ) { 82 | // Replace text field with label 83 | var li = document.createElement('li'), 84 | radio = dom.createRadioButton(newDeviceID), 85 | label = dom.createLabel(newDeviceID, deviceName); 86 | li.setAttribute('id', 'device_' +newDeviceID+ '_li'); 87 | li.appendChild(radio); 88 | li.appendChild(label); 89 | 90 | event.target.parentNode.parentNode.replaceChild(li, event.target.parentNode); 91 | devices[newDeviceID] = devices[oldDeviceID]; 92 | devices[newDeviceID].name = deviceName; 93 | if( oldDeviceID !== newDeviceID ) { 94 | delete devices[oldDeviceID]; 95 | } 96 | localStorage['devices'] = JSON.stringify(devices); 97 | } else { 98 | // Delete oldDeviceID's list element 99 | deleteDevice(oldDeviceID); 100 | if( newDeviceID ) { 101 | // Insert new element 102 | while( devices[newDeviceID] ) { 103 | newDeviceID += '-'; 104 | } 105 | devices[newDeviceID] = { 106 | name: deviceName, 107 | ua: current_ua_field.value 108 | }; 109 | addDeviceElement(newDeviceID, deviceName); 110 | localStorage['devices'] = JSON.stringify(devices); 111 | } 112 | } 113 | }, 114 | deviceButtonClick: function(event) { 115 | var regexp = new RegExp('(default' + (deviceListStr ? '|'+deviceListStr : '') + ')', 'i'), 116 | deviceID = event.target.id.match(regexp); 117 | deviceID = deviceID && deviceID[1]; 118 | if( !deviceID ) { 119 | return; 120 | } 121 | localStorage['deviceID'] = deviceID; 122 | // Careful setting the localStorage value to prevent setting it with the string 'undefined' 123 | if( devices[deviceID] ) { 124 | localStorage['user-agent'] = elems.current_ua_field.value = devices[deviceID].ua; 125 | } else { 126 | delete localStorage['user-agent']; 127 | elems.current_ua_field.value = ''; 128 | } 129 | }, 130 | addDeviceButtonClick: function(event) { 131 | var listelement = document.createElement('li'), 132 | textField = dom.createTextField(); 133 | 134 | listelement.appendChild(textField); 135 | listelement.setAttribute('id', 'new_device'); 136 | elems['device_list'].appendChild(listelement); 137 | elems['device_list'].appendChild(document.createTextNode(' ')); 138 | textField.focus(); 139 | }, 140 | restoreDefaultsButtonClick: function(event) { 141 | var li; 142 | devices = defaultDevices; 143 | delete localStorage['devices']; 144 | while( li = elems.device_list.firstChild ) { 145 | elems.device_list.removeChild(li); 146 | } 147 | deviceStr = ''; 148 | loadDevices(); 149 | document.getElementById('device_default_button').click(); 150 | }, 151 | textAreaType: function(event) { 152 | var activeDeviceID = 'default'; 153 | for( var deviceID in devices ) { 154 | if( devices.hasOwnProperty(deviceID) ) { 155 | if(document.getElementById('device_' +deviceID+ '_button').checked) { 156 | activeDeviceID = deviceID; 157 | break; 158 | } 159 | } 160 | } 161 | devices[activeDeviceID].ua = localStorage['user-agent'] = event.target.value; 162 | localStorage['devices'] = JSON.stringify(devices); 163 | } 164 | }, 165 | addDeviceElement = function( deviceID, deviceName ) { 166 | if( deviceListStr ) { 167 | deviceListStr += '|'; 168 | } 169 | deviceListStr += deviceID; 170 | var listItem = document.createElement('li'), 171 | radioButton = dom.createRadioButton(deviceID); 172 | label = dom.createLabel(deviceID, deviceName); 173 | 174 | listItem.appendChild(radioButton); 175 | listItem.appendChild(label); 176 | listItem.setAttribute('id', 'device_' +deviceID+ '_li'); 177 | elems['device_list'].appendChild(listItem); 178 | elems['device_list'].appendChild(document.createTextNode(' ')); 179 | }, 180 | deleteDevice = function( deviceID ) { 181 | var listElementID = deviceID ? 'device_' +deviceID+ '_li' : 'new_device'; 182 | listElement = document.getElementById(listElementID); 183 | elems['device_list'].removeChild(listElement); 184 | delete devices[deviceID]; 185 | deviceListStr = deviceListStr.replace(deviceID, ''); 186 | localStorage['devices'] = JSON.stringify(devices); 187 | }, 188 | loadDevices = function() { 189 | // Add the devices, ensuring that "default" is at the top of the list 190 | addDeviceElement('default', 'Default'); 191 | for( var deviceID in devices ) { 192 | if( devices.hasOwnProperty(deviceID) && deviceID !== 'default' ) { 193 | addDeviceElement(deviceID, devices[deviceID].name); 194 | } 195 | } 196 | devices['default'] = {'name':'Default', 'ua':''}; 197 | 198 | localStorage['deviceID'] = (localStorage['deviceID'] && devices[localStorage['deviceID']]) ? localStorage['deviceID'] : 'default'; 199 | }, 200 | initForm = function() { 201 | if( localStorage['deviceID'] ) { 202 | document.getElementById('device_' + localStorage['deviceID'] + '_button').setAttribute('checked', true); 203 | elems.current_ua_field.value = (devices[localStorage['deviceID']]) ? devices[localStorage['deviceID']].ua : ''; 204 | } 205 | }; 206 | document.addEventListener("DOMContentLoaded", function() { 207 | // Grab the DOM elements that need event handlers attached 208 | elem_ids.forEach(function( element_id ) { 209 | elems[element_id] = document.getElementById(element_id); 210 | }); 211 | 212 | loadDevices(); 213 | initForm(); 214 | 215 | elems.add_device_button.addEventListener('click', listeners.addDeviceButtonClick); 216 | elems.restore_defaults_button.addEventListener('click', listeners.restoreDefaultsButtonClick); 217 | elems.current_ua_field.addEventListener('keyup', listeners.textAreaType); 218 | }); 219 | })(); -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | #control_panel { 2 | background-color:#0099cc; 3 | font-family:Arial,sans-serif; 4 | width:300px; 5 | } #control_panel h1 { 6 | color:#eeeeee; 7 | font-size:20pt; 8 | font-weight:bold; 9 | } #control_panel h2 { 10 | display:block; 11 | font-size:10pt; 12 | font-weight:bold; 13 | margin:8px 0px 3px 0px; 14 | padding:0px; 15 | } #control_panel ul { 16 | display:block; 17 | list-style-type:none; 18 | margin:0px 0px 8px 0px; 19 | padding:0px; 20 | } #control_panel li { 21 | display:inline; 22 | margin:0px 4px 0px 0px; 23 | padding:0px; 24 | white-space:nowrap; 25 | } #control_panel input { 26 | margin:0px 4px 0px 0px; 27 | padding:0px; 28 | } #control_panel input[type=text] { 29 | background-color:#0099cc; 30 | border:1px solid #ffffff; 31 | padding:2px; 32 | } #control_panel input[type=button] { 33 | background-color:#0099cc; 34 | border:solid 1px #eeeeee; 35 | color:#eeeeee; 36 | margin:3px; 37 | padding:3px; 38 | } #control_panel input::selection { 39 | background:#ffffff; 40 | } #control_panel textarea { 41 | background:inherit; 42 | border:solid 1px #eeeeee; 43 | height:4em; 44 | width:100%; 45 | } #control_panel label { 46 | cursor:pointer; 47 | display:inline; 48 | } --------------------------------------------------------------------------------