diff options
Diffstat (limited to 'chrome/common/extensions')
126 files changed, 4153 insertions, 1141 deletions
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 3fdd1ce..45d9409 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -503,7 +503,12 @@ "optional": true, "description": "The speak options. This parameter is currently ignored.", "properties": { - "languageName": { + "enqueue": { + "type": "boolean", + "optional": true, + "description": "If true, enqueues this utterance if TTS is already in progress. If false (the default), interrupts any current speech and flushes the speech queue before speaking this new utterance." + }, + "languageName": { "type": "string", "optional": true, "description": "The language name for synthesis specified in the form <language>-<locale>, e.g. en-US, en-GB, fr-CA, zh-CN, etc." @@ -804,6 +809,7 @@ "index": {"type": "integer", "minimum": 0, "description": "The zero-based index of the tab within its window."}, "windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window the tab is contained within."}, "selected": {"type": "boolean", "description": "Whether the tab is selected."}, + "pinned": {"type": "boolean", "description": "Whether the tab is pinned."}, "url": {"type": "string", "description": "The URL the tab is displaying."}, "title": {"type": "string", "optional": true, "description": "The title of the tab. This may not be available if the tab is loading."}, "favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This may not be available if the tab is loading."}, @@ -871,7 +877,7 @@ ], "returns": { "$ref": "Port", - "description": "A port that can be used to communicate with the content scripts running in the specified tab." + "description": "A port that can be used to communicate with the content scripts running in the specified tab. The port's <a href='extension.html#type-Port'>onDisconnect</a> event is fired if the tab closes or does not exist. " } }, { @@ -896,7 +902,7 @@ { "name": "response", "type": "any", - "description": "The JSON response object sent by the handler of the request." + "description": "The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will be set to the error message." } ] } @@ -974,6 +980,11 @@ "type": "boolean", "optional": true, "description": "Whether the tab should become the selected tab in the window. Defaults to <var>true</var>" + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be pinned. Defaults to <var>false</var>" } } }, @@ -1001,8 +1012,20 @@ "type": "object", "name": "updateProperties", "properties": { - "url": {"type": "string", "optional": true}, - "selected": {"type": "boolean", "optional": true} + "url": { + "optional": true, + "description": "A URL to navigate the tab to." + }, + "selected": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be selected." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "Whether the tab should be pinned." + } } }, { @@ -1209,12 +1232,18 @@ "properties": { "status": { "type": "string", + "optional": true, "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>." }, "url": { "type": "string", "optional": true, - "description": "Only specified if the tab's URL changed." + "description": "The tab's URL if it has changed." + }, + "pinned": { + "type": "boolean", + "optional": true, + "description": "The tab's new pinned state." } } }, @@ -2665,12 +2694,12 @@ "properties": { "menuItemId": { "type": "integer", - "description": "The id of the menu item that was clicked." + "description": "The ID of the menu item that was clicked." }, "parentMenuItemId": { "type": "integer", "optional": true, - "description": "The parent id, if any, for the item clicked." + "description": "The parent ID, if any, for the item clicked." }, "mediaType": { "type": "string", @@ -2680,21 +2709,21 @@ "linkUrl": { "type": "string", "optional": true, - "description": "If the element is a link, the url it points to." + "description": "If the element is a link, the URL it points to." }, "srcUrl": { "type": "string", "optional": true, - "description": "Will be present for elements with a 'src' url." + "description": "Will be present for elements with a 'src' URL." }, "pageUrl": { "type": "string", - "description": "The url of the page where the menu item was clicked." + "description": "The URL of the page where the menu item was clicked." }, "frameUrl": { "type": "string", "optional": true, - "description": " The url of the frame of the element where the context menu was clicked, if it was in a frame." + "description": " The URL of the frame of the element where the context menu was clicked, if it was in a frame." }, "selectionText": { "type": "string", @@ -2715,7 +2744,7 @@ "description": "Creates a new context menu item. Note that if an error occurs during creation, you may not find out until the creation callback fires (the details will be in chrome.extension.lastError).", "returns": { "type": "integer", - "description": "The id of the newly created item." + "description": "The ID of the newly created item." }, "parameters": [ { @@ -2730,12 +2759,12 @@ "title": { "type": "string", "optional": "true", - "description": "This must be specified unless type is 'separator'." + "description": "The text to be displayed in the item; this is <em>required</em> unless <em>type</em> is 'separator'. When the context is 'selection', you can use <code>%s</code> within the string to show the selected text. For example, if this parameter's value is \"Translate '%s' to Pig Latin\" and the user selects the word \"cool\", the context menu item for the selection is \"Translate 'cool' to Pig Latin\"." }, "checked": { "type": "boolean", "optional": true, - "description": "This only applies to the initial state of a checkbox or radio item. It indicates if the item should initially be checked/selected. Only one radio item can be selected at a time in a given group of radio items." + "description": "The initial state of a checkbox or radio item: true for selected and false for unselected. Only one radio item can be selected at a time in a given group of radio items." }, "contexts": { "type": "array", @@ -2764,19 +2793,19 @@ "parentId": { "type": "integer", "optional": true, - "description": "The id of a parent menu item - this makes the item a child of a previously added item." + "description": "The ID of a parent menu item; this makes the item a child of a previously added item." }, "documentUrlPatterns": { "type": "array", "items": {"type": "string"}, "optional": true, - "description": "Lets you restrict the item to only apply to documents whose URL matches one of the given patterns. (This applies to frames as well). For details on the format of a pattern, see <a href='match_patterns.html'>Match Patterns</a>." + "description": "Lets you restrict the item to apply only to documents whose URL matches one of the given patterns. (This applies to frames as well.) For details on the format of a pattern, see <a href='match_patterns.html'>Match Patterns</a>." }, "targetUrlPatterns": { "type": "array", "items": {"type": "string"}, "optional": true, - "description": "Similar to documentUrlPatterns, but this lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags." + "description": "Similar to documentUrlPatterns, but lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags." } } }, @@ -2784,7 +2813,7 @@ "type": "function", "name": "callback", "optional": true, - "description": "Called when the item has been created in the browser. If there were any problems creating the item, details will be avilable in chrome.extension.lastError.", + "description": "Called when the item has been created in the browser. If there were any problems creating the item, details will be available in chrome.extension.lastError.", "parameters": [] } ] @@ -2797,7 +2826,7 @@ { "type": "integer", "name": "id", - "description": "The id of the item to update." + "description": "The ID of the item to update." }, { "type": "object", @@ -2860,7 +2889,7 @@ { "type": "integer", "name": "menuItemId", - "description": "The id of the context menu item to remove." + "description": "The ID of the context menu item to remove." }, { "type": "function", @@ -4029,10 +4058,6 @@ "description": "Whether it is currently enabled or disabled.", "type": "boolean" }, - "enabled": { - "description": "Whether it is currently enabled or disabled.", - "type": "boolean" - }, "isApp": { "description": "True if this is an app.", "type": "boolean" @@ -4080,6 +4105,28 @@ ] }, { + "name": "get", + "description": "Return information about the installed extension with the given ID.", + "parameters": [ + { + "name": "id", + "type": "string", + "description": "The ID from an item of $ref:ExtensionInfo." + }, + { + "type": "function", + "name": "callback", + "optional": "true", + "parameters": [ + { + "name": "result", + "$ref": "ExtensionInfo" + } + ] + } + ] + }, + { "name": "setEnabled", "description": "Enable or disable an app or extension.", "parameters": [ diff --git a/chrome/common/extensions/docs/a11y.html b/chrome/common/extensions/docs/a11y.html index c8e2646..5c34cff 100644 --- a/chrome/common/extensions/docs/a11y.html +++ b/chrome/common/extensions/docs/a11y.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Accessibility (a11y) - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/api_index.html b/chrome/common/extensions/docs/api_index.html index 46ddd6e..299f041 100644 --- a/chrome/common/extensions/docs/api_index.html +++ b/chrome/common/extensions/docs/api_index.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.* APIs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/api_other.html b/chrome/common/extensions/docs/api_other.html index c1ef26b..a4c11c6 100644 --- a/chrome/common/extensions/docs/api_other.html +++ b/chrome/common/extensions/docs/api_other.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Other APIs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -327,7 +331,7 @@ E.g. window.open(someUrl). --></dd> <ul> <li> audio (<a href="http://www.html5rocks.com/tutorials/audio/quick/">tutorial</a>) </li> - <li> app cache + <li> application cache (<a href="http://www.html5rocks.com/tutorials/appcache/beginner/">tutorial</a>) </li> <li> canvas </li> <li> geolocation diff --git a/chrome/common/extensions/docs/autoupdate.html b/chrome/common/extensions/docs/autoupdate.html index f474abb..b8c8ea5 100644 --- a/chrome/common/extensions/docs/autoupdate.html +++ b/chrome/common/extensions/docs/autoupdate.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Autoupdating - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/background_pages.html b/chrome/common/extensions/docs/background_pages.html index 5227b51..e0760ef 100644 --- a/chrome/common/extensions/docs/background_pages.html +++ b/chrome/common/extensions/docs/background_pages.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Background Pages - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/bookmarks.html b/chrome/common/extensions/docs/bookmarks.html index 8283626..b906190 100644 --- a/chrome/common/extensions/docs/bookmarks.html +++ b/chrome/common/extensions/docs/bookmarks.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Bookmarks - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/browserAction.html b/chrome/common/extensions/docs/browserAction.html index 77c2b6c..4e00a55 100644 --- a/chrome/common/extensions/docs/browserAction.html +++ b/chrome/common/extensions/docs/browserAction.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Browser Actions - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -473,7 +477,7 @@ in the <a href="#manifest">manifest</a>, or call the Browser action icons should seem a little bigger and heavier than page action icons. </li><li><b>Don't</b> attempt to mimic - Google Chrome's monochrome "wrench" and "page" icons. + Google Chrome's monochrome "wrench" icon. That doesn't work well with themes, and anyway, extensions should stand out a little. </li><li><b>Do</b> use alpha transparency diff --git a/chrome/common/extensions/docs/build/build.py b/chrome/common/extensions/docs/build/build.py index 524e989..1ff2e0f 100755 --- a/chrome/common/extensions/docs/build/build.py +++ b/chrome/common/extensions/docs/build/build.py @@ -162,8 +162,14 @@ def main(): "build.sh script instead, which uses depot_tools python.") parser = OptionParser() - parser.add_option("--test-shell-path", dest="test_shell_path") - parser.add_option("--page-name", dest="page_name") + parser.add_option("--test-shell-path", dest="test_shell_path", + metavar="PATH", + help="path to test_shell executable") + parser.add_option("--page-name", dest="page_name", metavar="PAGE", + help="only generate docs for PAGE.html") + parser.add_option("--nozip", dest="zips", action="store_false", + help="do not generate zip files for samples", + default=True) (options, args) = parser.parse_args() if (options.test_shell_path and os.path.isfile(options.test_shell_path)): @@ -195,6 +201,11 @@ def main(): samples_manifest = SamplesManifest(_samples_dir, _base_dir, api_manifest) samples_manifest.writeToFile(_samples_json) + # Write zipped versions of the samples listed in the manifest to the + # filesystem, unless the user has disabled it + if options.zips: + samples_manifest.writeZippedSamples() + modified_files = RenderPages(page_names, test_shell) if (len(modified_files) == 0): diff --git a/chrome/common/extensions/docs/build/directory.py b/chrome/common/extensions/docs/build/directory.py index 312e5ac..79f5dd7 100644 --- a/chrome/common/extensions/docs/build/directory.py +++ b/chrome/common/extensions/docs/build/directory.py @@ -221,16 +221,11 @@ class SamplesManifest(object): def writeToFile(self, path): """ Writes the contents of this manifest file as a JSON-encoded text file. - For each sample written to the manifest, create a zip file with the sample - contents in the sample's parent directory. Args: path: The path to write the samples manifest file to. """ - for sample in self._manifest_data['samples']: - sample.write_zip() - manifest_text = json.dumps(self._manifest_data, indent=2) output_path = os.path.realpath(path) try: @@ -241,6 +236,13 @@ class SamplesManifest(object): output_file.write(manifest_text) output_file.close() + def writeZippedSamples(self): + """ For each sample in the current manifest, create a zip file with the + sample contents in the sample's parent directory. """ + + for sample in self._manifest_data['samples']: + sample.write_zip() + class Sample(dict): """ Represents metadata about a Chrome extension sample. @@ -275,6 +277,7 @@ class Sample(dict): self['search_string'] = self._get_search_string() self['source_files'] = self._parse_source_files() self['id'] = hashlib.sha1(self['path']).hexdigest() + self['zip_path'] = self._get_relative_zip_path() _FEATURE_ATTRIBUTES = ( 'browser_action', @@ -371,6 +374,19 @@ class Sample(dict): return real_manifest_path.replace(real_base_path, '')\ .replace('manifest.json', '')[1:] + def _get_relative_zip_path(self): + """ Returns a relative path from the base dir to the sample's zip file. + + Intended for locating the zip file for the sample in the samples manifest. + + Returns: + A relative directory path form the sample manifest's directory to this + sample's zip file. + """ + zip_filename = self._get_zip_filename() + zip_relpath = os.path.dirname(os.path.dirname(self._get_relative_path())) + return os.path.join(zip_relpath, zip_filename) + def _get_search_string(self): """ Constructs a string to be used when searching the samples list. @@ -394,6 +410,17 @@ class Sample(dict): .upper() return search_string + def _get_zip_filename(self): + """ Returns the filename to be used for a generated zip of the sample. + + Returns: + A string in the form of "<dirname>.zip" where <dirname> is the name + of the directory containing this sample's manifest.json. + """ + sample_path = os.path.realpath(os.path.dirname(self._manifest_path)) + sample_dirname = os.path.basename(sample_path) + return "%s.zip" % sample_dirname + def _parse_api_calls(self, api_methods): """ Returns a list of Chrome extension API calls the sample makes. @@ -589,7 +616,7 @@ class Sample(dict): sample_dirname = os.path.basename(sample_path) sample_parentpath = os.path.dirname(sample_path) - zip_filename = "%s.zip" % sample_dirname + zip_filename = self._get_zip_filename() zip_path = os.path.join(sample_parentpath, zip_filename) zip_file = zipfile.ZipFile(zip_path, 'w') @@ -603,11 +630,6 @@ class Sample(dict): # Relative path to store the file in under the zip. relpath = sample_dirname + abspath.replace(sample_path, "") zip_file.write(abspath, relpath) - - self['zip_path'] = os.path.join( - os.path.dirname(os.path.dirname(self._get_relative_path())), - zip_filename) - except RuntimeError, msg: raise Exception("Could not write zip at " % zip_path) finally: diff --git a/chrome/common/extensions/docs/content_scripts.html b/chrome/common/extensions/docs/content_scripts.html index 4a5352e..c2845b8 100644 --- a/chrome/common/extensions/docs/content_scripts.html +++ b/chrome/common/extensions/docs/content_scripts.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Content Scripts - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/contextMenus.html b/chrome/common/extensions/docs/contextMenus.html index a7b2cdb..6f055fd 100644 --- a/chrome/common/extensions/docs/contextMenus.html +++ b/chrome/common/extensions/docs/contextMenus.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Context Menus - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -537,7 +541,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>This must be specified unless type is 'separator'.</dd> + <dd>The text to be displayed in the item; this is <em>required</em> unless <em>type</em> is 'separator'. When the context is 'selection', you can use <code>%s</code> within the string to show the selected text. For example, if this parameter's value is "Translate '%s' to Pig Latin" and the user selects the word "cool", the context menu item for the selection is "Translate 'cool' to Pig Latin".</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -595,7 +599,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>This only applies to the initial state of a checkbox or radio item. It indicates if the item should initially be checked/selected. Only one radio item can be selected at a time in a given group of radio items.</dd> + <dd>The initial state of a checkbox or radio item: true for selected and false for unselected. Only one radio item can be selected at a time in a given group of radio items.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -865,7 +869,7 @@ You can find samples of this API on the </div> </div> - </dl> + </dl> </div> </dd> @@ -901,7 +905,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of a parent menu item - this makes the item a child of a previously added item.</dd> + <dd>The ID of a parent menu item; this makes the item a child of a previously added item.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -970,7 +974,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Lets you restrict the item to only apply to documents whose URL matches one of the given patterns. (This applies to frames as well). For details on the format of a pattern, see <a href="match_patterns.html">Match Patterns</a>.</dd> + <dd>Lets you restrict the item to apply only to documents whose URL matches one of the given patterns. (This applies to frames as well.) For details on the format of a pattern, see <a href="match_patterns.html">Match Patterns</a>.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1039,7 +1043,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Similar to documentUrlPatterns, but this lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags.</dd> + <dd>Similar to documentUrlPatterns, but lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1107,7 +1111,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Called when the item has been created in the browser. If there were any problems creating the item, details will be avilable in chrome.extension.lastError.</dd> + <dd>Called when the item has been created in the browser. If there were any problems creating the item, details will be available in chrome.extension.lastError.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1171,7 +1175,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the newly created item.</dd> + <dd>The ID of the newly created item.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1282,7 +1286,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the context menu item to remove.</dd> + <dd>The ID of the context menu item to remove.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1580,7 +1584,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the item to update.</dd> + <dd>The ID of the item to update.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2401,7 +2405,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The id of the menu item that was clicked.</dd> + <dd>The ID of the menu item that was clicked.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2459,7 +2463,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The parent id, if any, for the item clicked.</dd> + <dd>The parent ID, if any, for the item clicked.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2575,7 +2579,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>If the element is a link, the url it points to.</dd> + <dd>If the element is a link, the URL it points to.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2633,7 +2637,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Will be present for elements with a 'src' url.</dd> + <dd>Will be present for elements with a 'src' URL.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2691,7 +2695,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The url of the page where the menu item was clicked.</dd> + <dd>The URL of the page where the menu item was clicked.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -2749,7 +2753,7 @@ You can find samples of this API on the <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd> The url of the frame of the element where the context menu was clicked, if it was in a frame.</dd> + <dd> The URL of the frame of the element where the context menu was clicked, if it was in a frame.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. diff --git a/chrome/common/extensions/docs/cookies.html b/chrome/common/extensions/docs/cookies.html index ea79e4c..482a342 100644 --- a/chrome/common/extensions/docs/cookies.html +++ b/chrome/common/extensions/docs/cookies.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Cookies - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/crx.html b/chrome/common/extensions/docs/crx.html index 9cd28ff..22ad2b5 100644 --- a/chrome/common/extensions/docs/crx.html +++ b/chrome/common/extensions/docs/crx.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>CRX Package Format - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/css/ApiRefStyles.css b/chrome/common/extensions/docs/css/ApiRefStyles.css index b3b86f3..ebc8669 100644 --- a/chrome/common/extensions/docs/css/ApiRefStyles.css +++ b/chrome/common/extensions/docs/css/ApiRefStyles.css @@ -800,6 +800,29 @@ a.leftNavSelected,.leftNavSelected a,a.leftNavSelected:visited,.leftNavSelected background:#fff } +#gc-toc .toggle { + background: url('../images/toggle_sprite.png') no-repeat 0px 0px; + width: 9px; + height: 9px; + overflow: hidden; + float: left; + text-decoration: none; + margin: 4px 1px 4px -13px; + cursor: pointer; +} + +#gc-toc .toggle.selected { + background-position: 0 -9px; +} + +#gc-toc .toggle:hover { + background-position: 0 -18px; +} + +#gc-toc .toggle.selected:hover { + background-position: 0 -27px; +} + #toc h2 { font-weight:bold; font-size:100%; diff --git a/chrome/common/extensions/docs/devguide.html b/chrome/common/extensions/docs/devguide.html index 298996e..2669a04 100644 --- a/chrome/common/extensions/docs/devguide.html +++ b/chrome/common/extensions/docs/devguide.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Developer's Guide - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/docs.html b/chrome/common/extensions/docs/docs.html index 2a81648..685f6aa 100644 --- a/chrome/common/extensions/docs/docs.html +++ b/chrome/common/extensions/docs/docs.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Hello There! - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/events.html b/chrome/common/extensions/docs/events.html index 7fbf71e..be42e7b 100644 --- a/chrome/common/extensions/docs/events.html +++ b/chrome/common/extensions/docs/events.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Events - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs.zip b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs.zip Binary files differnew file mode 100644 index 0000000..7e81cea --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs.zip diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html new file mode 100644 index 0000000..19fcc45 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html @@ -0,0 +1,466 @@ +<!DOCTYPE html> +<!-- + * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<html> + <head> + </head> + <body> + <script> + /** + * Allows for binding callbacks to a specific scope. + * @param {Object} scope Scope to bind to. + * @returns {Function} A wrapped call to this function. + */ + Function.prototype.bind = function(scope) { + var func = this; + return function() { + return func.apply(scope, arguments); + }; + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Holds the search index and exposes operations to search the API docs. + * @constructor + */ + function APISearchCorpus() { + this.corpus_ = []; + }; + + /** + * Adds an entry to the index. + * @param {String} name Name of the function (e.g. chrome.tabs.get). + * @param {String} url Url to the documentation. + * @param {String} desc Description (optional). + * @param {String} type The type of entry (e.g. method, event). + */ + APISearchCorpus.prototype.addEntry = function(name, url, desc, type) { + this.corpus_.push({ + 'name' : name, + 'url' : url, + 'ranges' : [], + 'description' : desc, + 'type' : type + }); + }; + + /** + * Locates a match from the supplied keywords against text. + * + * Keywords are matched in the order supplied, and a non-overlapping + * search is used. The ranges are returned in a way that is easily + * converted to the style array required by the omnibox API. + * + * @param {Array.<String>} keywords A list of keywords to check. + * @param {String} name The name to search against. + * @returns {Array.<Array.<number>>|null} A list of indexes corresponding + * to matches, or null if no match was found. + */ + APISearchCorpus.prototype.findMatch_ = function(keywords, name) { + var ranges = []; + var indexFrom = 0; + for (var i = 0; i < keywords.length; i++) { + var keyword = keywords[i].toLowerCase(); + var start = name.indexOf(keyword, indexFrom); + if (start == -1) { + return null; + } + var end = start + keyword.length; + ranges.push([start, end]); + indexFrom = end + 1; + } + return ranges; + }; + + /** + * Searches this corpus for the supplied text. + * @param {String} text Query text. + * @param {Number} limit Max results to return. + * @returns {Array.<Object>} A list of entries corresponding with + * matches (@see APISearchCorpus.findMatch_ for keyword search + * algorithm. Results are returned in a sorted order, first by + * length, then alphabetically by name. An exact match will be + * returned first. + */ + APISearchCorpus.prototype.search = function(text, limit) { + var results = []; + var match = null; + if (!text || text.length == 0) { + return this.corpus_.slice(0, limit); // No text, start listing APIs. + } + var searchText = text.toLowerCase(); + var keywords = searchText.split(' '); + for (var i = 0; i < this.corpus_.length; i++) { + var name = this.corpus_[i]['name'].toLowerCase(); + if (results.length < limit) { + var result = this.findMatch_(keywords, name); + if (result) { + this.corpus_[i]['ranges'] = result; + results.push(this.corpus_[i]); + } + } + if (!match && searchText == name) { + match = this.corpus_[i]; // An exact match. + } + if (match && results.length >= limit) { + break; // Have an exact match and have reached the search limit. + } + } + if (match) { + results.unshift(match); // Add any exact match to the front. + } + return results; + }; + + /** + * Sorts the corpus according to name length, then name alphabetically. + */ + APISearchCorpus.prototype.sort = function() { + function compareLength(a, b) { + return a['name'].length - b['name'].length; + }; + + function compareAlpha(a, b) { + if (a['name'] < b['name']) return -1; + if (a['name'] > b['name']) return 1; + return 0; + }; + + function compare(a, b) { + var result = compareLength(a, b); + if (result == 0) result = compareAlpha(a, b); + return result; + }; + + this.corpus_.sort(compare); + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Provides an interface to the Chrome Extensions documentation site. + * @param {APISearchCorpus} corpus The search corpus to populate. + * @constructor + */ + function DocsManager(corpus) { + this.CODE_URL_PREFIX = 'http://code.google.com/chrome/extensions/'; + this.API_MANIFEST_URL = [ + 'http://src.chromium.org/viewvc/chrome/trunk/src/', + 'chrome/common/extensions/api/extension_api.json' + ].join(''); + this.corpus_ = corpus; + }; + + /** + * Initiates a fetch of the docs and populates the corpus. + */ + DocsManager.prototype.fetch = function() { + this.fetchApi_(this.onApi_.bind(this)); + }; + + /** + * Retrieves the API manifest from cache or fetches a new one if none. + * @param {Function} callback The function to pass the parsed manifest + * data to. + */ + DocsManager.prototype.fetchApi_ = function(callback) { + var currentCacheTime = this.getCacheTime_(); + if (localStorage['cache-time'] && localStorage['cache']) { + var cacheTime = JSON.parse(localStorage['cache-time']); + if (cacheTime < currentCacheTime) { + callback(JSON.parse(localStorage['cache'])); + return; + } + } + var xhr = new XMLHttpRequest(); + xhr.addEventListener('readystatechange', function(evt) { + if (xhr.readyState == 4 && xhr.responseText) { + localStorage['cache-time'] = JSON.stringify(currentCacheTime); + localStorage['cache'] = xhr.responseText; + var json = JSON.parse(xhr.responseText); + callback(json); + } + }); + xhr.open('GET', this.API_MANIFEST_URL, true); + xhr.send(); + }; + + /** + * Returns a time which can be used to cache a manifest response. + * @returns {Number} A timestamp divided by the number of ms in a day, + * rounded to the nearest integer. This means the number should + * change only once per day, invalidating the cache that often. + */ + DocsManager.prototype.getCacheTime_ = function() { + var time = new Date().getTime(); + time = Math.round(time / (1000 * 60 * 60 * 24)); + return time; + }; + + /** + * Returns an URL for the documentation given an API element. + * @param {String} namespace The namespace (e.g. tabs, windows). + * @param {String} type The type of element (e.g. event, method, type). + * @param {String} name The name of the element (e.g. onRemoved). + * @returns {String} An URL corresponding with the documentation for the + * given element. + */ + DocsManager.prototype.getDocLink_ = function(namespace, type, name) { + var linkparts = [ this.CODE_URL_PREFIX, namespace, '.html' ]; + if (type && name) { + linkparts.push('#', type, '-', name); + } + return linkparts.join(''); + }; + + /** + * Returns a qualified name for an API element. + * @param {String} namespace The namespace (e.g. tabs, windows). + * @param {String} name The name of the element (e.g. onRemoved). + * @returns {String} A qualified API name (e.g. chrome.tabs.onRemoved). + */ + DocsManager.prototype.getName_ = function(namespace, name) { + var nameparts = [ 'chrome', namespace ]; + if (name) { + nameparts.push(name); + } + return nameparts.join('.'); + }; + + /** + * Parses an API manifest data structure and populates the search index. + * @param {Object} api The api manifest, as a JSON-parsed object. + */ + DocsManager.prototype.onApi_ = function(api) { + for (var i = 0; i < api.length; i++) { + var module = api[i]; + if (module.nodoc) { + continue; + } + var ns = module.namespace; + var nsName = this.getName_(ns); + var nsUrl = this.getDocLink_(ns); + this.corpus_.addEntry(nsName, nsUrl, null, 'namespace'); + this.parseAPIArray_('method', ns, module.functions); + this.parseAPIArray_('event', ns, module.events); + this.parseAPIArray_('type', ns, module.types); + this.parseAPIObject_('property', ns, module.properties); + this.corpus_.sort(); + } + }; + + /** + * Parses an API manifest subsection which is formatted as an Array. + * @param {String} type The type of data (e.g. method, event, type). + * @param {String} ns The namespace (e.g. tabs, windows). + * @param {Array} list The list of API elements. + */ + DocsManager.prototype.parseAPIArray_ = function(type, ns, list) { + if (!list) return; + for (var j = 0; j < list.length; j++) { + var item = list[j]; + if (item.nodoc) continue; + var name = item.name || item.id; + var fullname = this.getName_(ns, name); + var url = this.getDocLink_(ns, type, name); + var description = item.description; + this.corpus_.addEntry(fullname, url, description, type); + } + }; + + /** + * Parses an API manifest subsection which is formatted as an Object. + * @param {String} type The type of data (e.g. property). + * @param {String} ns The namespace (e.g. tabs, windows). + * @param {Object} list The object containing API elements. + */ + DocsManager.prototype.parseAPIObject_ = function(type, ns, list) { + for (var prop in list) { + if (list.hasOwnProperty(prop)) { + var name = this.getName_(ns, prop); + var url = this.getDocLink_(ns, type, prop); + var description = list[prop].description; + this.corpus_.addEntry(name, url, description, type); + } + } + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Manages text input into the omnibox and returns search results. + * @param {APISearchCorpus} Populated search corpus. + * @param {TabManager} Manager to use to open tabs. + * @constructor + */ + function OmniboxManager(corpus, tabManager) { + this.SEPARATOR = ' - '; + this.corpus_ = corpus; + this.tabManager_ = tabManager; + chrome.experimental.omnibox.onInputChanged.addListener( + this.onChanged_.bind(this)); + chrome.experimental.omnibox.onInputEntered.addListener( + this.onEntered_.bind(this)); + }; + + /** + * Converts a corpus match to an object suitable for the omnibox API. + * @param {Object} match The match to convert. + * @returns {Object} A suggestion object formatted for the omnibox API. + */ + OmniboxManager.prototype.matchToSuggestion_ = function(match) { + var styles = [ ]; + var ranges = match['ranges']; + var desc = match['name']; + var name = match['name']; + var lastIndex = 0; + for (var i = 0; i < ranges.length; i++) { + styles.push(chrome.experimental.omnibox.styleMatch(ranges[i][0])); + styles.push(chrome.experimental.omnibox.styleNone(ranges[i][1])); + lastIndex = ranges[i][1]; + } + + if (match['type']) { + // Abusing the URL style a little, but want this to stand out. + desc = this.pushStyle_(styles, 'Url', desc, match['type']); + lastIndex = desc.length; + } + if (match['description']) { + desc = this.pushStyle_(styles, 'Dim', desc, match['description']); + lastIndex = desc.length; + } + + if (lastIndex == desc.length) styles.pop(); + + return { + 'content' : name, + 'description' : desc, + 'descriptionStyles' : styles + }; + }; + + /** + * Suggests a list of possible matches when omnibox text changes. + * @param {String} text Text input from the omnibox. + * @param {Function} suggest Callback to execute with a list of + * suggestion objects, if any matches were found. + */ + OmniboxManager.prototype.onChanged_ = function(text, suggest) { + var matches = this.corpus_.search(text, 10); + var suggestions = []; + for (var i = 0; i < matches.length; i++) { + var suggestion = this.matchToSuggestion_(matches[i]); + suggestions.push(suggestion); + } + suggest(suggestions); + }; + + /** + * Opens the most appropriate URL when enter is pressed in the omnibox. + * + * Note that the entered text does not have to be exact - the first + * search result is automatically opened when enter is pressed. + * + * @param {String} text The text entered. + */ + OmniboxManager.prototype.onEntered_ = function(text) { + var matches = this.corpus_.search(text, 1); + if (matches.length > 0) { + this.tabManager_.open(matches[0]['url']); + } + }; + + /** + * Helper function for constructing a suggestion style list. + * + * Adds a separator and text to a description, and modifies an array + * of styles so that the separator is not styled and the additional text + * obtains the requested style type. + * + * This method expects the list of styles to end with a styleNone style. + * It will modify the list so that the last element is a styleNone style. + * The last element will always be at the end of the returned string, + * which will throw an error unless it is removed before being passed + * to the API (this method is intended to be called a few times in a row). + * @see OmniboxManager.matchToSuggestion_ for code that removes this last + * entry. + * + * @param {Array.<Object>} styles An array of styles, in the format + * expected by the omnibox API. + * @param {String} type The style type to apply - must be one of + * "Dim", "Match", "None", and "Url". + * @param {String} desc The description text to append to and style. + * @param {String} text The text to append to the description. + * @returns {String} The description plus a separator and the supplied + * text, intended to overwrite the variable which was passed into + * this function as "desc". + */ + OmniboxManager.prototype.pushStyle_ = function(styles, type, desc, text) { + desc += this.SEPARATOR; + styles.push(chrome.experimental.omnibox['style' + type](desc.length)); + desc += text; + styles.push(chrome.experimental.omnibox.styleNone(desc.length)); + return desc; + }; + + ////////////////////////////////////////////////////////////////////////// + + /** + * Manages opening urls in tabs. + * @constructor + */ + function TabManager() { + this.tab_ = null; + chrome.tabs.onRemoved.addListener(this.onRemoved_.bind(this)); + }; + + /** + * When a tab is removed, see if it was opened by us and null out if yes. + * @param {Number} tabid ID of the removed tab. + */ + TabManager.prototype.onRemoved_ = function(tabid) { + if (this.tab_ && tabid == this.tab_.id) this.tab_ = null; + }; + + /** + * When a tab opened by us is created, store it for future updates. + * @param {Tab} tab The tab which was just opened. + */ + TabManager.prototype.onTab_ = function(tab) { + this.tab_ = tab; + }; + + /** + * Opens the supplied URL. + * + * The first time this method is called a new tab is created. Subsequent + * times this is called, the opened tab will be updated. If that tab + * is ever closed, then a new tab will be created for the next call. + * + * @param {String} url The URL to open. + */ + TabManager.prototype.open = function(url) { + if (url) { + var args = { 'url' : url, 'selected' : true }; + if (this.tab_) { + chrome.tabs.update(this.tab_.id, args); + } else { + chrome.tabs.create(args, this.onTab_.bind(this)); + } + } + }; + + ////////////////////////////////////////////////////////////////////////// + + var corpus = new APISearchCorpus(); + var docsManager = new DocsManager(corpus); + docsManager.fetch(); + var tabManager = new TabManager(); + var omnibox = new OmniboxManager(corpus, tabManager); + </script> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-128.png b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-128.png Binary files differnew file mode 100644 index 0000000..c7e114f --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-128.png diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-16.png b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-16.png Binary files differnew file mode 100644 index 0000000..23b34a0 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/icon-16.png diff --git a/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json new file mode 100644 index 0000000..8e6b3b9 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json @@ -0,0 +1,16 @@ +{ + "name" : "Extension Docs Search", + "description" : "Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.", + "version" : "1.0.0", + "permissions" : [ + "experimental", + "http://src.chromium.org/viewvc/*", + "tabs" + ], + "icons" : { + "16" : "icon-16.png", + "128" : "icon-128.png" + }, + "omnibox_keyword" : "crdoc", + "background_page" : "background.html" +} diff --git a/chrome/common/extensions/docs/examples/api/omnibox.zip b/chrome/common/extensions/docs/examples/api/omnibox/simple-example.zip Binary files differindex 58ba199..532eb00 100644 --- a/chrome/common/extensions/docs/examples/api/omnibox.zip +++ b/chrome/common/extensions/docs/examples/api/omnibox/simple-example.zip diff --git a/chrome/common/extensions/docs/examples/api/omnibox/background.html b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/background.html index ff83ff2..ff83ff2 100644 --- a/chrome/common/extensions/docs/examples/api/omnibox/background.html +++ b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/background.html diff --git a/chrome/common/extensions/docs/examples/api/omnibox/manifest.json b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/manifest.json index 2b63ca2..c48a345 100644 --- a/chrome/common/extensions/docs/examples/api/omnibox/manifest.json +++ b/chrome/common/extensions/docs/examples/api/omnibox/simple-example/manifest.json @@ -1,5 +1,6 @@ { "name": "Omnibox Example", + "description" : "To use, type 'omnix' plus a search term into the Omnibox.", "version": "1.0", "permissions": [ "experimental" ], "background_page": "background.html", diff --git a/chrome/common/extensions/docs/examples/api/processes/process_monitor.zip b/chrome/common/extensions/docs/examples/api/processes/process_monitor.zip Binary files differnew file mode 100644 index 0000000..60d5641 --- /dev/null +++ b/chrome/common/extensions/docs/examples/api/processes/process_monitor.zip diff --git a/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip b/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip Binary files differindex 770b435..ba3c761 100644 --- a/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip +++ b/chrome/common/extensions/docs/examples/api/processes/show_tabs.zip diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark.zip b/chrome/common/extensions/docs/examples/extensions/benchmark.zip Binary files differindex af258d4..7f43bdb 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark.zip +++ b/chrome/common/extensions/docs/examples/extensions/benchmark.zip diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt b/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt index 8136ab3..2267580 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/README.txt @@ -14,5 +14,14 @@ To use this benchmark, you'll need to run chrome with the the the benchmark can clear idle connections and the cache. The code found in the jst/ subdirectory is JSTemplate code from -http://code.google.com/p/google-jstemplate/ +http://code.google.com/p/google-jstemplate/. + +In jquery/, jquery-1.4.2.min.js is from http://jquery.com/. jquery.flot.min.js +is a plotting library and from http://code.google.com/p/flot/. +jquery.flot.dashes.js is an enhancement of Flot for dashed lines and from +http://code.google.com/p/flot/issues/detail?id=61. + +In util/, sortable.js serves for sorting table content and is from +http://www.kryogenix.org/code/browser/sorttable/. table2CSV.js is for exporting +table data to .csv and from http://www.kunalbabre.com/projects/table2CSV.php. diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/background.html b/chrome/common/extensions/docs/examples/extensions/benchmark/background.html index ca712ff..36e4c31 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark/background.html +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/background.html @@ -163,19 +163,22 @@ function Benchmark() { current_ = {}; current_.url = url; + current_.timestamp = new Date(); current_.viaSpdy = false; current_.startLoadResults = new Array(); // times to start current_.commitLoadResults = new Array(); // times to commit current_.docLoadResults = new Array(); // times to docload current_.paintResults = new Array(); // times to paint current_.totalResults = new Array(); // times to complete load - current_.bytesRead = 0; - current_.bytesWritten = 0; + current_.KbytesRead = new Array(); + current_.KbytesWritten = new Array(); + current_.readbpsResults = new Array(); + current_.writebpsResults = new Array(); current_.totalTime = 0; current_.iterations = 0; - current_.requests = 0; - current_.connects = 0; - current_.spdySessions = 0; + current_.requests = new Array(); + current_.connects = new Array(); + current_.spdySessions = new Array(); current_.domNum = 0; current_.maxDepth = 0; current_.minDepth = 0; @@ -237,7 +240,7 @@ function Benchmark() { this.openNextPage = function() { var benchmark = nextBenchmark(); benchmark.pageStart(); - chrome.tabs.create({"url": benchmark.url(),"selected": false}, + chrome.tabs.create({"url": benchmark.url(),"selected": true}, function(tab) { benchmarkWindow = tab; // script.js only executes on tested pages @@ -297,6 +300,43 @@ function Benchmark() { // Called when a page finishes loading. this.pageFinished = function(load_times, domNum, depths) { + + // Make sure the content can be fetched via spdy if it is enabled. + if (window.enableSpdy && !load_times.wasFetchedViaSpdy) { + alert("Can not fetch current url via spdy.\n" + + "Ending current test."); + me_.finish(); + // Move on to next benchmarked pages. + if (benchmarks.length > 0) { + if (window.clearConnections) { + chrome.benchmarking.closeConnections(); + } + setTimeout(me_.runPage, 100); + } + return; + } + + // If last fetch was via spdy, current fetch should use spdy too. Same + // for vise versa. + if (current_.iterations > 0 && + current_.viaSpdy != load_times.wasFetchedViaSpdy) { + alert("Error: viaSpdy for current fetch is different from last fetch!\n" + + "Ending current test."); + // Current data set is invalid: remove from the result array. + var currIndex; + currIndex = window.results.data.indexOf(current_, 0); + window.results.data.splice(currIndex, 1); + me_.displayResults(); + me_.finish(); + if (benchmarks.length > 0) { + if (window.clearConnections) { + chrome.benchmarking.closeConnections(); + } + setTimeout(me_.runPage, 100); + } + return; + } + var requested = load_times.requestTime; var started = load_times.startLoadTime; var startLoadTime = @@ -326,22 +366,6 @@ function Benchmark() { totalTime_ += totalTime; count_++; - // Make sure the content can be fetched via spdy if it is enabled. - if (window.enableSpdy && !load_times.wasFetchedViaSpdy) { - alert("Can not fetch current url via spdy.\n" + - "Ending current test."); - runCount_ = 1; - } - - // If last fetch was via spdy, current fetch should use spdy too. Same - // for vise versa. - if (current_.iterations > 0 && - current_.viaSpdy != load_times.wasFetchedViaSpdy) { - alert("Error: viaSpdy for current fetch is different from last fetch!\n" + - "Ending current test."); - runCount_ = 1; - } - // Get the index of current benchmarked page in the result array. var currIndex; currIndex = window.results.data.indexOf(current_, 0); @@ -354,16 +378,20 @@ function Benchmark() { current_.docLoadResults.push(docLoadTime); current_.paintResults.push(paintTime); current_.totalResults.push(totalTime); - current_.bytesRead += chrome.benchmarking.counter(kTCPReadBytes) - - initialReadBytes_; - current_.bytesWritten += chrome.benchmarking.counter(kTCPWriteBytes) - - initialWriteBytes_; - current_.requests += chrome.benchmarking.counter(kRequestCount) - - initialRequestCount_; - current_.connects += chrome.benchmarking.counter(kConnectCount) - - initialConnectCount_; - current_.spdySessions += chrome.benchmarking.counter(kSpdySessionCount) - - initialSpdySessionCount_; + var bytesRead = chrome.benchmarking.counter(kTCPReadBytes) - + initialReadBytes_; + var bytesWrite = chrome.benchmarking.counter(kTCPWriteBytes) - + initialWriteBytes_; + current_.KbytesRead.push(bytesRead / 1024); + current_.KbytesWritten.push(bytesWrite / 1024); + current_.readbpsResults.push(bytesRead * 8 / totalTime); + current_.writebpsResults.push(bytesWrite * 8 / totalTime); + current_.requests.push(chrome.benchmarking.counter(kRequestCount) - + initialRequestCount_); + current_.connects.push(chrome.benchmarking.counter(kConnectCount) - + initialConnectCount_); + current_.spdySessions.push(chrome.benchmarking.counter(kSpdySessionCount) - + initialSpdySessionCount_); current_.totalTime += totalTime; current_.domNum = domNum; current_.maxDepth = depths[0]; @@ -374,7 +402,7 @@ function Benchmark() { if (currIndex == -1) { window.results.data.push(current_); } else { - window.results.data[currIndex]=current_; + window.results.data[currIndex] = current_; } if (--runCount_ == 0) { @@ -386,7 +414,6 @@ function Benchmark() { if (window.clearConnections) { chrome.benchmarking.closeConnections(); } - setTimeout(me_.runPage, 100); } diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i? +e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r= +j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g, +"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e= +true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)|| +c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded", +L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype, +"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+ +a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f], +d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]=== +a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&& +!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari= +true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ", +i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ", +" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className= +this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i= +e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!= +null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), +fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| +d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this, +"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent= +a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y, +isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit= +{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}}; +if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&& +!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}}, +toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector, +u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "), +function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q]; +if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[]; +for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length- +1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, +CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}}, +relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]= +l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[]; +h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m= +m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition|| +!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m= +h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>"; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/, +gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length; +c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)? +a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&& +this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]|| +u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length=== +1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay"); +this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a], +"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)}, +animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing= +j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]); +this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length|| +c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement? +function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b= +this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle; +k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&& +f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js new file mode 100644 index 0000000..7cebc92 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js @@ -0,0 +1,237 @@ +/* + * jQuery.flot.dashes + * + * options = { + * series: { + * dashes: { + * + * // show + * // default: false + * // Whether to show dashes for the series. + * show: <boolean>, + * + * // lineWidth + * // default: 2 + * // The width of the dashed line in pixels. + * lineWidth: <number>, + * + * // dashLength + * // default: 10 + * // Controls the length of the individual dashes and the amount of + * // space between them. + * // If this is a number, the dashes and spaces will have that length. + * // If this is an array, it is read as [ dashLength, spaceLength ] + * dashLength: <number> or <array[2]> + * } + * } + * } + */ +(function($){ + + function init(plot) { + + plot.hooks.processDatapoints.push(function(plot, series, datapoints) { + + if (!series.dashes.show) return; + + plot.hooks.draw.push(function(plot, ctx) { + + var plotOffset = plot.getPlotOffset(), + axisx = series.xaxis, + axisy = series.yaxis; + + function plotDashes(xoffset, yoffset) { + + var points = datapoints.points, + ps = datapoints.pointsize, + prevx = null, + prevy = null, + dashRemainder = 0, + dashOn = true, + dashOnLength, + dashOffLength; + + if (series.dashes.dashLength[0]) { + dashOnLength = series.dashes.dashLength[0]; + if (series.dashes.dashLength[1]) { + dashOffLength = series.dashes.dashLength[1]; + } else { + dashOffLength = dashOnLength; + } + } else { + dashOffLength = dashOnLength = series.dashes.dashLength; + } + + ctx.beginPath(); + + for (var i = ps; i < points.length; i += ps) { + + var x1 = points[i - ps], + y1 = points[i - ps + 1], + x2 = points[i], + y2 = points[i + 1]; + + if (x1 == null || x2 == null) continue; + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) continue; // line segment is outside + // compute new intersection point + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) continue; + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max) { + if (y2 > axisy.max) continue; + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) continue; + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (x1 != prevx || y1 != prevy) { + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + } + + var ax1 = axisx.p2c(x1) + xoffset, + ay1 = axisy.p2c(y1) + yoffset, + ax2 = axisx.p2c(x2) + xoffset, + ay2 = axisy.p2c(y2) + yoffset, + dashOffset; + + function lineSegmentOffset(segmentLength) { + + var c = Math.sqrt(Math.pow(ax2 - ax1, 2) + Math.pow(ay2 - ay1, 2)); + + if (c <= segmentLength) { + return { + deltaX: ax2 - ax1, + deltaY: ay2 - ay1, + distance: c, + remainder: segmentLength - c + } + } else { + var xsign = ax2 > ax1 ? 1 : -1, + ysign = ay2 > ay1 ? 1 : -1; + return { + deltaX: xsign * Math.sqrt(Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))), + deltaY: ysign * Math.sqrt(Math.pow(segmentLength, 2) - Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))), + distance: segmentLength, + remainder: 0 + }; + } + } + //-end lineSegmentOffset + + do { + + dashOffset = lineSegmentOffset( + dashRemainder > 0 ? dashRemainder : + dashOn ? dashOnLength : dashOffLength); + + if (dashOffset.deltaX != 0 || dashOffset.deltaY != 0) { + if (dashOn) { + ctx.lineTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY); + } else { + ctx.moveTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY); + } + } + + dashOn = !dashOn; + dashRemainder = dashOffset.remainder; + ax1 += dashOffset.deltaX; + ay1 += dashOffset.deltaY; + + } while (dashOffset.distance > 0); + + prevx = x2; + prevy = y2; + } + + ctx.stroke(); + } + //-end plotDashes + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = 'round'; + + var lw = series.dashes.lineWidth, + sw = series.shadowSize; + + // FIXME: consider another form of shadow when filling is turned on + if (lw > 0 && sw > 0) { + // draw shadow as a thick and thin line with transparency + ctx.lineWidth = sw; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + // position shadow at angle from the mid of line + var angle = Math.PI/18; + plotDashes(Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2)); + ctx.lineWidth = sw/2; + plotDashes(Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4)); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + + if (lw > 0) { + plotDashes(0, 0); + } + + ctx.restore(); + + }); + //-end draw hook + + }); + //-end processDatapoints hook + + } + //-end init + + $.plot.plugins.push({ + init: init, + options: { + series: { + dashes: { + show: false, + lineWidth: 2, + dashLength: 10 + } + } + }, + name: 'dashes', + version: '0.1' + }); + +})(jQuery) + diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js new file mode 100644 index 0000000..31f465b --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js @@ -0,0 +1 @@ +(function(){jQuery.color={};jQuery.color.make=function(G,H,J,I){var A={};A.r=G||0;A.g=H||0;A.b=J||0;A.a=I!=null?I:1;A.add=function(C,D){for(var E=0;E<C.length;++E){A[C.charAt(E)]+=D}return A.normalize()};A.scale=function(C,D){for(var E=0;E<C.length;++E){A[C.charAt(E)]*=D}return A.normalize()};A.toString=function(){if(A.a>=1){return"rgb("+[A.r,A.g,A.b].join(",")+")"}else{return"rgba("+[A.r,A.g,A.b,A.a].join(",")+")"}};A.normalize=function(){function C(E,D,F){return D<E?E:(D>F?F:D)}A.r=C(0,parseInt(A.r),255);A.g=C(0,parseInt(A.g),255);A.b=C(0,parseInt(A.b),255);A.a=C(0,A.a,1);return A};A.clone=function(){return jQuery.color.make(A.r,A.b,A.g,A.a)};return A.normalize()};jQuery.color.extract=function(E,F){var A;do{A=E.css(F).toLowerCase();if(A!=""&&A!="transparent"){break}E=E.parent()}while(!jQuery.nodeName(E.get(0),"body"));if(A=="rgba(0, 0, 0, 0)"){A="transparent"}return jQuery.color.parse(A)};jQuery.color.parse=function(A){var F,H=jQuery.color.make;if(F=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(A)){return H(parseInt(F[1],10),parseInt(F[2],10),parseInt(F[3],10))}if(F=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(A)){return H(parseInt(F[1],10),parseInt(F[2],10),parseInt(F[3],10),parseFloat(F[4]))}if(F=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(A)){return H(parseFloat(F[1])*2.55,parseFloat(F[2])*2.55,parseFloat(F[3])*2.55)}if(F=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(A)){return H(parseFloat(F[1])*2.55,parseFloat(F[2])*2.55,parseFloat(F[3])*2.55,parseFloat(F[4]))}if(F=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(A)){return H(parseInt(F[1],16),parseInt(F[2],16),parseInt(F[3],16))}if(F=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(A)){return H(parseInt(F[1]+F[1],16),parseInt(F[2]+F[2],16),parseInt(F[3]+F[3],16))}var G=jQuery.trim(A).toLowerCase();if(G=="transparent"){return H(255,255,255,0)}else{F=B[G];return H(F[0],F[1],F[2])}};var B={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})();(function(C){function B(l,W,X,E){var O=[],g={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{mode:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02},x2axis:{autoscaleMargin:null},y2axis:{autoscaleMargin:0.02},series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,tickColor:"rgba(0,0,0,0.15)",labelMargin:5,borderWidth:2,borderColor:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},P=null,AC=null,AD=null,Y=null,AJ=null,s={xaxis:{},yaxis:{},x2axis:{},y2axis:{}},e={left:0,right:0,top:0,bottom:0},y=0,Q=0,I=0,t=0,L={processOptions:[],processRawData:[],processDatapoints:[],draw:[],bindEvents:[],drawOverlay:[]},G=this;G.setData=f;G.setupGrid=k;G.draw=AH;G.getPlaceholder=function(){return l};G.getCanvas=function(){return P};G.getPlotOffset=function(){return e};G.width=function(){return I};G.height=function(){return t};G.offset=function(){var AK=AD.offset();AK.left+=e.left;AK.top+=e.top;return AK};G.getData=function(){return O};G.getAxes=function(){return s};G.getOptions=function(){return g};G.highlight=AE;G.unhighlight=x;G.triggerRedrawOverlay=q;G.pointOffset=function(AK){return{left:parseInt(T(AK,"xaxis").p2c(+AK.x)+e.left),top:parseInt(T(AK,"yaxis").p2c(+AK.y)+e.top)}};G.hooks=L;b(G);r(X);c();f(W);k();AH();AG();function Z(AM,AK){AK=[G].concat(AK);for(var AL=0;AL<AM.length;++AL){AM[AL].apply(this,AK)}}function b(){for(var AK=0;AK<E.length;++AK){var AL=E[AK];AL.init(G);if(AL.options){C.extend(true,g,AL.options)}}}function r(AK){C.extend(true,g,AK);if(g.grid.borderColor==null){g.grid.borderColor=g.grid.color}if(g.xaxis.noTicks&&g.xaxis.ticks==null){g.xaxis.ticks=g.xaxis.noTicks}if(g.yaxis.noTicks&&g.yaxis.ticks==null){g.yaxis.ticks=g.yaxis.noTicks}if(g.grid.coloredAreas){g.grid.markings=g.grid.coloredAreas}if(g.grid.coloredAreasColor){g.grid.markingsColor=g.grid.coloredAreasColor}if(g.lines){C.extend(true,g.series.lines,g.lines)}if(g.points){C.extend(true,g.series.points,g.points)}if(g.bars){C.extend(true,g.series.bars,g.bars)}if(g.shadowSize){g.series.shadowSize=g.shadowSize}for(var AL in L){if(g.hooks[AL]&&g.hooks[AL].length){L[AL]=L[AL].concat(g.hooks[AL])}}Z(L.processOptions,[g])}function f(AK){O=M(AK);U();m()}function M(AN){var AL=[];for(var AK=0;AK<AN.length;++AK){var AM=C.extend(true,{},g.series);if(AN[AK].data){AM.data=AN[AK].data;delete AN[AK].data;C.extend(true,AM,AN[AK]);AN[AK].data=AM.data}else{AM.data=AN[AK]}AL.push(AM)}return AL}function T(AM,AK){var AL=AM[AK];if(!AL||AL==1){return s[AK]}if(typeof AL=="number"){return s[AK.charAt(0)+AL+AK.slice(1)]}return AL}function U(){var AP;var AV=O.length,AK=[],AN=[];for(AP=0;AP<O.length;++AP){var AS=O[AP].color;if(AS!=null){--AV;if(typeof AS=="number"){AN.push(AS)}else{AK.push(C.color.parse(O[AP].color))}}}for(AP=0;AP<AN.length;++AP){AV=Math.max(AV,AN[AP]+1)}var AL=[],AO=0;AP=0;while(AL.length<AV){var AR;if(g.colors.length==AP){AR=C.color.make(100,100,100)}else{AR=C.color.parse(g.colors[AP])}var AM=AO%2==1?-1:1;AR.scale("rgb",1+AM*Math.ceil(AO/2)*0.2);AL.push(AR);++AP;if(AP>=g.colors.length){AP=0;++AO}}var AQ=0,AW;for(AP=0;AP<O.length;++AP){AW=O[AP];if(AW.color==null){AW.color=AL[AQ].toString();++AQ}else{if(typeof AW.color=="number"){AW.color=AL[AW.color].toString()}}if(AW.lines.show==null){var AU,AT=true;for(AU in AW){if(AW[AU].show){AT=false;break}}if(AT){AW.lines.show=true}}AW.xaxis=T(AW,"xaxis");AW.yaxis=T(AW,"yaxis")}}function m(){var AW=Number.POSITIVE_INFINITY,AQ=Number.NEGATIVE_INFINITY,Ac,Aa,AZ,AV,AL,AR,Ab,AX,AP,AO,AK,Ai,Af,AT;for(AK in s){s[AK].datamin=AW;s[AK].datamax=AQ;s[AK].used=false}function AN(Al,Ak,Aj){if(Ak<Al.datamin){Al.datamin=Ak}if(Aj>Al.datamax){Al.datamax=Aj}}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];AR.datapoints={points:[]};Z(L.processRawData,[AR,AR.data,AR.datapoints])}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];var Ah=AR.data,Ae=AR.datapoints.format;if(!Ae){Ae=[];Ae.push({x:true,number:true,required:true});Ae.push({y:true,number:true,required:true});if(AR.bars.show){Ae.push({y:true,number:true,required:false,defaultValue:0})}AR.datapoints.format=Ae}if(AR.datapoints.pointsize!=null){continue}if(AR.datapoints.pointsize==null){AR.datapoints.pointsize=Ae.length}AX=AR.datapoints.pointsize;Ab=AR.datapoints.points;insertSteps=AR.lines.show&&AR.lines.steps;AR.xaxis.used=AR.yaxis.used=true;for(Aa=AZ=0;Aa<Ah.length;++Aa,AZ+=AX){AT=Ah[Aa];var AM=AT==null;if(!AM){for(AV=0;AV<AX;++AV){Ai=AT[AV];Af=Ae[AV];if(Af){if(Af.number&&Ai!=null){Ai=+Ai;if(isNaN(Ai)){Ai=null}}if(Ai==null){if(Af.required){AM=true}if(Af.defaultValue!=null){Ai=Af.defaultValue}}}Ab[AZ+AV]=Ai}}if(AM){for(AV=0;AV<AX;++AV){Ai=Ab[AZ+AV];if(Ai!=null){Af=Ae[AV];if(Af.x){AN(AR.xaxis,Ai,Ai)}if(Af.y){AN(AR.yaxis,Ai,Ai)}}Ab[AZ+AV]=null}}else{if(insertSteps&&AZ>0&&Ab[AZ-AX]!=null&&Ab[AZ-AX]!=Ab[AZ]&&Ab[AZ-AX+1]!=Ab[AZ+1]){for(AV=0;AV<AX;++AV){Ab[AZ+AX+AV]=Ab[AZ+AV]}Ab[AZ+1]=Ab[AZ-AX+1];AZ+=AX}}}}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];Z(L.processDatapoints,[AR,AR.datapoints])}for(Ac=0;Ac<O.length;++Ac){AR=O[Ac];Ab=AR.datapoints.points,AX=AR.datapoints.pointsize;var AS=AW,AY=AW,AU=AQ,Ad=AQ;for(Aa=0;Aa<Ab.length;Aa+=AX){if(Ab[Aa]==null){continue}for(AV=0;AV<AX;++AV){Ai=Ab[Aa+AV];Af=Ae[AV];if(!Af){continue}if(Af.x){if(Ai<AS){AS=Ai}if(Ai>AU){AU=Ai}}if(Af.y){if(Ai<AY){AY=Ai}if(Ai>Ad){Ad=Ai}}}}if(AR.bars.show){var Ag=AR.bars.align=="left"?0:-AR.bars.barWidth/2;if(AR.bars.horizontal){AY+=Ag;Ad+=Ag+AR.bars.barWidth}else{AS+=Ag;AU+=Ag+AR.bars.barWidth}}AN(AR.xaxis,AS,AU);AN(AR.yaxis,AY,Ad)}for(AK in s){if(s[AK].datamin==AW){s[AK].datamin=null}if(s[AK].datamax==AQ){s[AK].datamax=null}}}function c(){function AK(AM,AL){var AN=document.createElement("canvas");AN.width=AM;AN.height=AL;if(C.browser.msie){AN=window.G_vmlCanvasManager.initElement(AN)}return AN}y=l.width();Q=l.height();l.html("");if(l.css("position")=="static"){l.css("position","relative")}if(y<=0||Q<=0){throw"Invalid dimensions for plot, width = "+y+", height = "+Q}if(C.browser.msie){window.G_vmlCanvasManager.init_(document)}P=C(AK(y,Q)).appendTo(l).get(0);Y=P.getContext("2d");AC=C(AK(y,Q)).css({position:"absolute",left:0,top:0}).appendTo(l).get(0);AJ=AC.getContext("2d");AJ.stroke()}function AG(){AD=C([AC,P]);if(g.grid.hoverable){AD.mousemove(D)}if(g.grid.clickable){AD.click(d)}Z(L.bindEvents,[AD])}function k(){function AL(AT,AU){function AP(AV){return AV}var AS,AO,AQ=AU.transform||AP,AR=AU.inverseTransform;if(AT==s.xaxis||AT==s.x2axis){AS=AT.scale=I/(AQ(AT.max)-AQ(AT.min));AO=AQ(AT.min);if(AQ==AP){AT.p2c=function(AV){return(AV-AO)*AS}}else{AT.p2c=function(AV){return(AQ(AV)-AO)*AS}}if(!AR){AT.c2p=function(AV){return AO+AV/AS}}else{AT.c2p=function(AV){return AR(AO+AV/AS)}}}else{AS=AT.scale=t/(AQ(AT.max)-AQ(AT.min));AO=AQ(AT.max);if(AQ==AP){AT.p2c=function(AV){return(AO-AV)*AS}}else{AT.p2c=function(AV){return(AO-AQ(AV))*AS}}if(!AR){AT.c2p=function(AV){return AO-AV/AS}}else{AT.c2p=function(AV){return AR(AO-AV/AS)}}}}function AN(AR,AT){var AQ,AS=[],AP;AR.labelWidth=AT.labelWidth;AR.labelHeight=AT.labelHeight;if(AR==s.xaxis||AR==s.x2axis){if(AR.labelWidth==null){AR.labelWidth=y/(AR.ticks.length>0?AR.ticks.length:1)}if(AR.labelHeight==null){AS=[];for(AQ=0;AQ<AR.ticks.length;++AQ){AP=AR.ticks[AQ].label;if(AP){AS.push('<div class="tickLabel" style="float:left;width:'+AR.labelWidth+'px">'+AP+"</div>")}}if(AS.length>0){var AO=C('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'+AS.join("")+'<div style="clear:left"></div></div>').appendTo(l);AR.labelHeight=AO.height();AO.remove()}}}else{if(AR.labelWidth==null||AR.labelHeight==null){for(AQ=0;AQ<AR.ticks.length;++AQ){AP=AR.ticks[AQ].label;if(AP){AS.push('<div class="tickLabel">'+AP+"</div>")}}if(AS.length>0){var AO=C('<div style="position:absolute;top:-10000px;font-size:smaller">'+AS.join("")+"</div>").appendTo(l);if(AR.labelWidth==null){AR.labelWidth=AO.width()}if(AR.labelHeight==null){AR.labelHeight=AO.find("div").height()}AO.remove()}}}if(AR.labelWidth==null){AR.labelWidth=0}if(AR.labelHeight==null){AR.labelHeight=0}}function AM(){var AP=g.grid.borderWidth;for(i=0;i<O.length;++i){AP=Math.max(AP,2*(O[i].points.radius+O[i].points.lineWidth/2))}e.left=e.right=e.top=e.bottom=AP;var AO=g.grid.labelMargin+g.grid.borderWidth;if(s.xaxis.labelHeight>0){e.bottom=Math.max(AP,s.xaxis.labelHeight+AO)}if(s.yaxis.labelWidth>0){e.left=Math.max(AP,s.yaxis.labelWidth+AO)}if(s.x2axis.labelHeight>0){e.top=Math.max(AP,s.x2axis.labelHeight+AO)}if(s.y2axis.labelWidth>0){e.right=Math.max(AP,s.y2axis.labelWidth+AO)}I=y-e.left-e.right;t=Q-e.bottom-e.top}var AK;for(AK in s){K(s[AK],g[AK])}if(g.grid.show){for(AK in s){F(s[AK],g[AK]);p(s[AK],g[AK]);AN(s[AK],g[AK])}AM()}else{e.left=e.right=e.top=e.bottom=0;I=y;t=Q}for(AK in s){AL(s[AK],g[AK])}if(g.grid.show){h()}AI()}function K(AN,AQ){var AM=+(AQ.min!=null?AQ.min:AN.datamin),AK=+(AQ.max!=null?AQ.max:AN.datamax),AP=AK-AM;if(AP==0){var AL=AK==0?1:0.01;if(AQ.min==null){AM-=AL}if(AQ.max==null||AQ.min!=null){AK+=AL}}else{var AO=AQ.autoscaleMargin;if(AO!=null){if(AQ.min==null){AM-=AP*AO;if(AM<0&&AN.datamin!=null&&AN.datamin>=0){AM=0}}if(AQ.max==null){AK+=AP*AO;if(AK>0&&AN.datamax!=null&&AN.datamax<=0){AK=0}}}}AN.min=AM;AN.max=AK}function F(AP,AS){var AO;if(typeof AS.ticks=="number"&&AS.ticks>0){AO=AS.ticks}else{if(AP==s.xaxis||AP==s.x2axis){AO=0.3*Math.sqrt(y)}else{AO=0.3*Math.sqrt(Q)}}var AX=(AP.max-AP.min)/AO,AZ,AT,AV,AW,AR,AM,AL;if(AS.mode=="time"){var AU={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var AY=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var AN=0;if(AS.minTickSize!=null){if(typeof AS.tickSize=="number"){AN=AS.tickSize}else{AN=AS.minTickSize[0]*AU[AS.minTickSize[1]]}}for(AR=0;AR<AY.length-1;++AR){if(AX<(AY[AR][0]*AU[AY[AR][1]]+AY[AR+1][0]*AU[AY[AR+1][1]])/2&&AY[AR][0]*AU[AY[AR][1]]>=AN){break}}AZ=AY[AR][0];AV=AY[AR][1];if(AV=="year"){AM=Math.pow(10,Math.floor(Math.log(AX/AU.year)/Math.LN10));AL=(AX/AU.year)/AM;if(AL<1.5){AZ=1}else{if(AL<3){AZ=2}else{if(AL<7.5){AZ=5}else{AZ=10}}}AZ*=AM}if(AS.tickSize){AZ=AS.tickSize[0];AV=AS.tickSize[1]}AT=function(Ac){var Ah=[],Af=Ac.tickSize[0],Ai=Ac.tickSize[1],Ag=new Date(Ac.min);var Ab=Af*AU[Ai];if(Ai=="second"){Ag.setUTCSeconds(A(Ag.getUTCSeconds(),Af))}if(Ai=="minute"){Ag.setUTCMinutes(A(Ag.getUTCMinutes(),Af))}if(Ai=="hour"){Ag.setUTCHours(A(Ag.getUTCHours(),Af))}if(Ai=="month"){Ag.setUTCMonth(A(Ag.getUTCMonth(),Af))}if(Ai=="year"){Ag.setUTCFullYear(A(Ag.getUTCFullYear(),Af))}Ag.setUTCMilliseconds(0);if(Ab>=AU.minute){Ag.setUTCSeconds(0)}if(Ab>=AU.hour){Ag.setUTCMinutes(0)}if(Ab>=AU.day){Ag.setUTCHours(0)}if(Ab>=AU.day*4){Ag.setUTCDate(1)}if(Ab>=AU.year){Ag.setUTCMonth(0)}var Ak=0,Aj=Number.NaN,Ad;do{Ad=Aj;Aj=Ag.getTime();Ah.push({v:Aj,label:Ac.tickFormatter(Aj,Ac)});if(Ai=="month"){if(Af<1){Ag.setUTCDate(1);var Aa=Ag.getTime();Ag.setUTCMonth(Ag.getUTCMonth()+1);var Ae=Ag.getTime();Ag.setTime(Aj+Ak*AU.hour+(Ae-Aa)*Af);Ak=Ag.getUTCHours();Ag.setUTCHours(0)}else{Ag.setUTCMonth(Ag.getUTCMonth()+Af)}}else{if(Ai=="year"){Ag.setUTCFullYear(Ag.getUTCFullYear()+Af)}else{Ag.setTime(Aj+Ab)}}}while(Aj<Ac.max&&Aj!=Ad);return Ah};AW=function(Aa,Ad){var Af=new Date(Aa);if(AS.timeformat!=null){return C.plot.formatDate(Af,AS.timeformat,AS.monthNames)}var Ab=Ad.tickSize[0]*AU[Ad.tickSize[1]];var Ac=Ad.max-Ad.min;var Ae=(AS.twelveHourClock)?" %p":"";if(Ab<AU.minute){fmt="%h:%M:%S"+Ae}else{if(Ab<AU.day){if(Ac<2*AU.day){fmt="%h:%M"+Ae}else{fmt="%b %d %h:%M"+Ae}}else{if(Ab<AU.month){fmt="%b %d"}else{if(Ab<AU.year){if(Ac<AU.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return C.plot.formatDate(Af,fmt,AS.monthNames)}}else{var AK=AS.tickDecimals;var AQ=-Math.floor(Math.log(AX)/Math.LN10);if(AK!=null&&AQ>AK){AQ=AK}AM=Math.pow(10,-AQ);AL=AX/AM;if(AL<1.5){AZ=1}else{if(AL<3){AZ=2;if(AL>2.25&&(AK==null||AQ+1<=AK)){AZ=2.5;++AQ}}else{if(AL<7.5){AZ=5}else{AZ=10}}}AZ*=AM;if(AS.minTickSize!=null&&AZ<AS.minTickSize){AZ=AS.minTickSize}if(AS.tickSize!=null){AZ=AS.tickSize}AP.tickDecimals=Math.max(0,(AK!=null)?AK:AQ);AT=function(Ac){var Ae=[];var Af=A(Ac.min,Ac.tickSize),Ab=0,Aa=Number.NaN,Ad;do{Ad=Aa;Aa=Af+Ab*Ac.tickSize;Ae.push({v:Aa,label:Ac.tickFormatter(Aa,Ac)});++Ab}while(Aa<Ac.max&&Aa!=Ad);return Ae};AW=function(Aa,Ab){return Aa.toFixed(Ab.tickDecimals)}}AP.tickSize=AV?[AZ,AV]:AZ;AP.tickGenerator=AT;if(C.isFunction(AS.tickFormatter)){AP.tickFormatter=function(Aa,Ab){return""+AS.tickFormatter(Aa,Ab)}}else{AP.tickFormatter=AW}}function p(AO,AQ){AO.ticks=[];if(!AO.used){return }if(AQ.ticks==null){AO.ticks=AO.tickGenerator(AO)}else{if(typeof AQ.ticks=="number"){if(AQ.ticks>0){AO.ticks=AO.tickGenerator(AO)}}else{if(AQ.ticks){var AP=AQ.ticks;if(C.isFunction(AP)){AP=AP({min:AO.min,max:AO.max})}var AN,AK;for(AN=0;AN<AP.length;++AN){var AL=null;var AM=AP[AN];if(typeof AM=="object"){AK=AM[0];if(AM.length>1){AL=AM[1]}}else{AK=AM}if(AL==null){AL=AO.tickFormatter(AK,AO)}AO.ticks[AN]={v:AK,label:AL}}}}}if(AQ.autoscaleMargin!=null&&AO.ticks.length>0){if(AQ.min==null){AO.min=Math.min(AO.min,AO.ticks[0].v)}if(AQ.max==null&&AO.ticks.length>1){AO.max=Math.max(AO.max,AO.ticks[AO.ticks.length-1].v)}}}function AH(){Y.clearRect(0,0,y,Q);var AL=g.grid;if(AL.show&&!AL.aboveData){S()}for(var AK=0;AK<O.length;++AK){AA(O[AK])}Z(L.draw,[Y]);if(AL.show&&AL.aboveData){S()}}function N(AL,AR){var AO=AR+"axis",AK=AR+"2axis",AN,AQ,AP,AM;if(AL[AO]){AN=s[AO];AQ=AL[AO].from;AP=AL[AO].to}else{if(AL[AK]){AN=s[AK];AQ=AL[AK].from;AP=AL[AK].to}else{AN=s[AO];AQ=AL[AR+"1"];AP=AL[AR+"2"]}}if(AQ!=null&&AP!=null&&AQ>AP){return{from:AP,to:AQ,axis:AN}}return{from:AQ,to:AP,axis:AN}}function S(){var AO;Y.save();Y.translate(e.left,e.top);if(g.grid.backgroundColor){Y.fillStyle=R(g.grid.backgroundColor,t,0,"rgba(255, 255, 255, 0)");Y.fillRect(0,0,I,t)}var AL=g.grid.markings;if(AL){if(C.isFunction(AL)){AL=AL({xmin:s.xaxis.min,xmax:s.xaxis.max,ymin:s.yaxis.min,ymax:s.yaxis.max,xaxis:s.xaxis,yaxis:s.yaxis,x2axis:s.x2axis,y2axis:s.y2axis})}for(AO=0;AO<AL.length;++AO){var AK=AL[AO],AQ=N(AK,"x"),AN=N(AK,"y");if(AQ.from==null){AQ.from=AQ.axis.min}if(AQ.to==null){AQ.to=AQ.axis.max}if(AN.from==null){AN.from=AN.axis.min}if(AN.to==null){AN.to=AN.axis.max}if(AQ.to<AQ.axis.min||AQ.from>AQ.axis.max||AN.to<AN.axis.min||AN.from>AN.axis.max){continue}AQ.from=Math.max(AQ.from,AQ.axis.min);AQ.to=Math.min(AQ.to,AQ.axis.max);AN.from=Math.max(AN.from,AN.axis.min);AN.to=Math.min(AN.to,AN.axis.max);if(AQ.from==AQ.to&&AN.from==AN.to){continue}AQ.from=AQ.axis.p2c(AQ.from);AQ.to=AQ.axis.p2c(AQ.to);AN.from=AN.axis.p2c(AN.from);AN.to=AN.axis.p2c(AN.to);if(AQ.from==AQ.to||AN.from==AN.to){Y.beginPath();Y.strokeStyle=AK.color||g.grid.markingsColor;Y.lineWidth=AK.lineWidth||g.grid.markingsLineWidth;Y.moveTo(AQ.from,AN.from);Y.lineTo(AQ.to,AN.to);Y.stroke()}else{Y.fillStyle=AK.color||g.grid.markingsColor;Y.fillRect(AQ.from,AN.to,AQ.to-AQ.from,AN.from-AN.to)}}}Y.lineWidth=1;Y.strokeStyle=g.grid.tickColor;Y.beginPath();var AM,AP=s.xaxis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=s.xaxis.max){continue}Y.moveTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,0);Y.lineTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,t)}AP=s.yaxis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=AP.max){continue}Y.moveTo(0,Math.floor(AP.p2c(AM))+Y.lineWidth/2);Y.lineTo(I,Math.floor(AP.p2c(AM))+Y.lineWidth/2)}AP=s.x2axis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=AP.max){continue}Y.moveTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,-5);Y.lineTo(Math.floor(AP.p2c(AM))+Y.lineWidth/2,5)}AP=s.y2axis;for(AO=0;AO<AP.ticks.length;++AO){AM=AP.ticks[AO].v;if(AM<=AP.min||AM>=AP.max){continue}Y.moveTo(I-5,Math.floor(AP.p2c(AM))+Y.lineWidth/2);Y.lineTo(I+5,Math.floor(AP.p2c(AM))+Y.lineWidth/2)}Y.stroke();if(g.grid.borderWidth){var AR=g.grid.borderWidth;Y.lineWidth=AR;Y.strokeStyle=g.grid.borderColor;Y.strokeRect(-AR/2,-AR/2,I+AR,t+AR)}Y.restore()}function h(){l.find(".tickLabels").remove();var AK=['<div class="tickLabels" style="font-size:smaller;color:'+g.grid.color+'">'];function AM(AP,AQ){for(var AO=0;AO<AP.ticks.length;++AO){var AN=AP.ticks[AO];if(!AN.label||AN.v<AP.min||AN.v>AP.max){continue}AK.push(AQ(AN,AP))}}var AL=g.grid.labelMargin+g.grid.borderWidth;AM(s.xaxis,function(AN,AO){return'<div style="position:absolute;top:'+(e.top+t+AL)+"px;left:"+Math.round(e.left+AO.p2c(AN.v)-AO.labelWidth/2)+"px;width:"+AO.labelWidth+'px;text-align:center" class="tickLabel">'+AN.label+"</div>"});AM(s.yaxis,function(AN,AO){return'<div style="position:absolute;top:'+Math.round(e.top+AO.p2c(AN.v)-AO.labelHeight/2)+"px;right:"+(e.right+I+AL)+"px;width:"+AO.labelWidth+'px;text-align:right" class="tickLabel">'+AN.label+"</div>"});AM(s.x2axis,function(AN,AO){return'<div style="position:absolute;bottom:'+(e.bottom+t+AL)+"px;left:"+Math.round(e.left+AO.p2c(AN.v)-AO.labelWidth/2)+"px;width:"+AO.labelWidth+'px;text-align:center" class="tickLabel">'+AN.label+"</div>"});AM(s.y2axis,function(AN,AO){return'<div style="position:absolute;top:'+Math.round(e.top+AO.p2c(AN.v)-AO.labelHeight/2)+"px;left:"+(e.left+I+AL)+"px;width:"+AO.labelWidth+'px;text-align:left" class="tickLabel">'+AN.label+"</div>"});AK.push("</div>");l.append(AK.join(""))}function AA(AK){if(AK.lines.show){a(AK)}if(AK.bars.show){n(AK)}if(AK.points.show){o(AK)}}function a(AN){function AM(AY,AZ,AR,Ad,Ac){var Ae=AY.points,AS=AY.pointsize,AW=null,AV=null;Y.beginPath();for(var AX=AS;AX<Ae.length;AX+=AS){var AU=Ae[AX-AS],Ab=Ae[AX-AS+1],AT=Ae[AX],Aa=Ae[AX+1];if(AU==null||AT==null){continue}if(Ab<=Aa&&Ab<Ac.min){if(Aa<Ac.min){continue}AU=(Ac.min-Ab)/(Aa-Ab)*(AT-AU)+AU;Ab=Ac.min}else{if(Aa<=Ab&&Aa<Ac.min){if(Ab<Ac.min){continue}AT=(Ac.min-Ab)/(Aa-Ab)*(AT-AU)+AU;Aa=Ac.min}}if(Ab>=Aa&&Ab>Ac.max){if(Aa>Ac.max){continue}AU=(Ac.max-Ab)/(Aa-Ab)*(AT-AU)+AU;Ab=Ac.max}else{if(Aa>=Ab&&Aa>Ac.max){if(Ab>Ac.max){continue}AT=(Ac.max-Ab)/(Aa-Ab)*(AT-AU)+AU;Aa=Ac.max}}if(AU<=AT&&AU<Ad.min){if(AT<Ad.min){continue}Ab=(Ad.min-AU)/(AT-AU)*(Aa-Ab)+Ab;AU=Ad.min}else{if(AT<=AU&&AT<Ad.min){if(AU<Ad.min){continue}Aa=(Ad.min-AU)/(AT-AU)*(Aa-Ab)+Ab;AT=Ad.min}}if(AU>=AT&&AU>Ad.max){if(AT>Ad.max){continue}Ab=(Ad.max-AU)/(AT-AU)*(Aa-Ab)+Ab;AU=Ad.max}else{if(AT>=AU&&AT>Ad.max){if(AU>Ad.max){continue}Aa=(Ad.max-AU)/(AT-AU)*(Aa-Ab)+Ab;AT=Ad.max}}if(AU!=AW||Ab!=AV){Y.moveTo(Ad.p2c(AU)+AZ,Ac.p2c(Ab)+AR)}AW=AT;AV=Aa;Y.lineTo(Ad.p2c(AT)+AZ,Ac.p2c(Aa)+AR)}Y.stroke()}function AO(AX,Ae,Ac){var Af=AX.points,AR=AX.pointsize,AS=Math.min(Math.max(0,Ac.min),Ac.max),Aa,AV=0,Ad=false;for(var AW=AR;AW<Af.length;AW+=AR){var AU=Af[AW-AR],Ab=Af[AW-AR+1],AT=Af[AW],AZ=Af[AW+1];if(Ad&&AU!=null&&AT==null){Y.lineTo(Ae.p2c(AV),Ac.p2c(AS));Y.fill();Ad=false;continue}if(AU==null||AT==null){continue}if(AU<=AT&&AU<Ae.min){if(AT<Ae.min){continue}Ab=(Ae.min-AU)/(AT-AU)*(AZ-Ab)+Ab;AU=Ae.min}else{if(AT<=AU&&AT<Ae.min){if(AU<Ae.min){continue}AZ=(Ae.min-AU)/(AT-AU)*(AZ-Ab)+Ab;AT=Ae.min}}if(AU>=AT&&AU>Ae.max){if(AT>Ae.max){continue}Ab=(Ae.max-AU)/(AT-AU)*(AZ-Ab)+Ab;AU=Ae.max}else{if(AT>=AU&&AT>Ae.max){if(AU>Ae.max){continue}AZ=(Ae.max-AU)/(AT-AU)*(AZ-Ab)+Ab;AT=Ae.max}}if(!Ad){Y.beginPath();Y.moveTo(Ae.p2c(AU),Ac.p2c(AS));Ad=true}if(Ab>=Ac.max&&AZ>=Ac.max){Y.lineTo(Ae.p2c(AU),Ac.p2c(Ac.max));Y.lineTo(Ae.p2c(AT),Ac.p2c(Ac.max));AV=AT;continue}else{if(Ab<=Ac.min&&AZ<=Ac.min){Y.lineTo(Ae.p2c(AU),Ac.p2c(Ac.min));Y.lineTo(Ae.p2c(AT),Ac.p2c(Ac.min));AV=AT;continue}}var Ag=AU,AY=AT;if(Ab<=AZ&&Ab<Ac.min&&AZ>=Ac.min){AU=(Ac.min-Ab)/(AZ-Ab)*(AT-AU)+AU;Ab=Ac.min}else{if(AZ<=Ab&&AZ<Ac.min&&Ab>=Ac.min){AT=(Ac.min-Ab)/(AZ-Ab)*(AT-AU)+AU;AZ=Ac.min}}if(Ab>=AZ&&Ab>Ac.max&&AZ<=Ac.max){AU=(Ac.max-Ab)/(AZ-Ab)*(AT-AU)+AU;Ab=Ac.max}else{if(AZ>=Ab&&AZ>Ac.max&&Ab<=Ac.max){AT=(Ac.max-Ab)/(AZ-Ab)*(AT-AU)+AU;AZ=Ac.max}}if(AU!=Ag){if(Ab<=Ac.min){Aa=Ac.min}else{Aa=Ac.max}Y.lineTo(Ae.p2c(Ag),Ac.p2c(Aa));Y.lineTo(Ae.p2c(AU),Ac.p2c(Aa))}Y.lineTo(Ae.p2c(AU),Ac.p2c(Ab));Y.lineTo(Ae.p2c(AT),Ac.p2c(AZ));if(AT!=AY){if(AZ<=Ac.min){Aa=Ac.min}else{Aa=Ac.max}Y.lineTo(Ae.p2c(AT),Ac.p2c(Aa));Y.lineTo(Ae.p2c(AY),Ac.p2c(Aa))}AV=Math.max(AT,AY)}if(Ad){Y.lineTo(Ae.p2c(AV),Ac.p2c(AS));Y.fill()}}Y.save();Y.translate(e.left,e.top);Y.lineJoin="round";var AP=AN.lines.lineWidth,AK=AN.shadowSize;if(AP>0&&AK>0){Y.lineWidth=AK;Y.strokeStyle="rgba(0,0,0,0.1)";var AQ=Math.PI/18;AM(AN.datapoints,Math.sin(AQ)*(AP/2+AK/2),Math.cos(AQ)*(AP/2+AK/2),AN.xaxis,AN.yaxis);Y.lineWidth=AK/2;AM(AN.datapoints,Math.sin(AQ)*(AP/2+AK/4),Math.cos(AQ)*(AP/2+AK/4),AN.xaxis,AN.yaxis)}Y.lineWidth=AP;Y.strokeStyle=AN.color;var AL=V(AN.lines,AN.color,0,t);if(AL){Y.fillStyle=AL;AO(AN.datapoints,AN.xaxis,AN.yaxis)}if(AP>0){AM(AN.datapoints,0,0,AN.xaxis,AN.yaxis)}Y.restore()}function o(AN){function AP(AU,AT,Ab,AR,AV,AZ,AY){var Aa=AU.points,AQ=AU.pointsize;for(var AS=0;AS<Aa.length;AS+=AQ){var AX=Aa[AS],AW=Aa[AS+1];if(AX==null||AX<AZ.min||AX>AZ.max||AW<AY.min||AW>AY.max){continue}Y.beginPath();Y.arc(AZ.p2c(AX),AY.p2c(AW)+AR,AT,0,AV,false);if(Ab){Y.fillStyle=Ab;Y.fill()}Y.stroke()}}Y.save();Y.translate(e.left,e.top);var AO=AN.lines.lineWidth,AL=AN.shadowSize,AK=AN.points.radius;if(AO>0&&AL>0){var AM=AL/2;Y.lineWidth=AM;Y.strokeStyle="rgba(0,0,0,0.1)";AP(AN.datapoints,AK,null,AM+AM/2,Math.PI,AN.xaxis,AN.yaxis);Y.strokeStyle="rgba(0,0,0,0.2)";AP(AN.datapoints,AK,null,AM/2,Math.PI,AN.xaxis,AN.yaxis)}Y.lineWidth=AO;Y.strokeStyle=AN.color;AP(AN.datapoints,AK,V(AN.points,AN.color),0,2*Math.PI,AN.xaxis,AN.yaxis);Y.restore()}function AB(AV,AU,Ad,AQ,AY,AN,AL,AT,AS,Ac,AZ){var AM,Ab,AR,AX,AO,AK,AW,AP,Aa;if(AZ){AP=AK=AW=true;AO=false;AM=Ad;Ab=AV;AX=AU+AQ;AR=AU+AY;if(Ab<AM){Aa=Ab;Ab=AM;AM=Aa;AO=true;AK=false}}else{AO=AK=AW=true;AP=false;AM=AV+AQ;Ab=AV+AY;AR=Ad;AX=AU;if(AX<AR){Aa=AX;AX=AR;AR=Aa;AP=true;AW=false}}if(Ab<AT.min||AM>AT.max||AX<AS.min||AR>AS.max){return }if(AM<AT.min){AM=AT.min;AO=false}if(Ab>AT.max){Ab=AT.max;AK=false}if(AR<AS.min){AR=AS.min;AP=false}if(AX>AS.max){AX=AS.max;AW=false}AM=AT.p2c(AM);AR=AS.p2c(AR);Ab=AT.p2c(Ab);AX=AS.p2c(AX);if(AL){Ac.beginPath();Ac.moveTo(AM,AR);Ac.lineTo(AM,AX);Ac.lineTo(Ab,AX);Ac.lineTo(Ab,AR);Ac.fillStyle=AL(AR,AX);Ac.fill()}if(AO||AK||AW||AP){Ac.beginPath();Ac.moveTo(AM,AR+AN);if(AO){Ac.lineTo(AM,AX+AN)}else{Ac.moveTo(AM,AX+AN)}if(AW){Ac.lineTo(Ab,AX+AN)}else{Ac.moveTo(Ab,AX+AN)}if(AK){Ac.lineTo(Ab,AR+AN)}else{Ac.moveTo(Ab,AR+AN)}if(AP){Ac.lineTo(AM,AR+AN)}else{Ac.moveTo(AM,AR+AN)}Ac.stroke()}}function n(AM){function AL(AS,AR,AU,AP,AT,AW,AV){var AX=AS.points,AO=AS.pointsize;for(var AQ=0;AQ<AX.length;AQ+=AO){if(AX[AQ]==null){continue}AB(AX[AQ],AX[AQ+1],AX[AQ+2],AR,AU,AP,AT,AW,AV,Y,AM.bars.horizontal)}}Y.save();Y.translate(e.left,e.top);Y.lineWidth=AM.bars.lineWidth;Y.strokeStyle=AM.color;var AK=AM.bars.align=="left"?0:-AM.bars.barWidth/2;var AN=AM.bars.fill?function(AO,AP){return V(AM.bars,AM.color,AO,AP)}:null;AL(AM.datapoints,AK,AK+AM.bars.barWidth,0,AN,AM.xaxis,AM.yaxis);Y.restore()}function V(AM,AK,AL,AO){var AN=AM.fill;if(!AN){return null}if(AM.fillColor){return R(AM.fillColor,AL,AO,AK)}var AP=C.color.parse(AK);AP.a=typeof AN=="number"?AN:0.4;AP.normalize();return AP.toString()}function AI(){l.find(".legend").remove();if(!g.legend.show){return }var AP=[],AN=false,AV=g.legend.labelFormatter,AU,AR;for(i=0;i<O.length;++i){AU=O[i];AR=AU.label;if(!AR){continue}if(i%g.legend.noColumns==0){if(AN){AP.push("</tr>")}AP.push("<tr>");AN=true}if(AV){AR=AV(AR,AU)}AP.push('<td class="legendColorBox"><div style="border:1px solid '+g.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+AU.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+AR+"</td>")}if(AN){AP.push("</tr>")}if(AP.length==0){return }var AT='<table style="font-size:smaller;color:'+g.grid.color+'">'+AP.join("")+"</table>";if(g.legend.container!=null){C(g.legend.container).html(AT)}else{var AQ="",AL=g.legend.position,AM=g.legend.margin;if(AM[0]==null){AM=[AM,AM]}if(AL.charAt(0)=="n"){AQ+="top:"+(AM[1]+e.top)+"px;"}else{if(AL.charAt(0)=="s"){AQ+="bottom:"+(AM[1]+e.bottom)+"px;"}}if(AL.charAt(1)=="e"){AQ+="right:"+(AM[0]+e.right)+"px;"}else{if(AL.charAt(1)=="w"){AQ+="left:"+(AM[0]+e.left)+"px;"}}var AS=C('<div class="legend">'+AT.replace('style="','style="position:absolute;'+AQ+";")+"</div>").appendTo(l);if(g.legend.backgroundOpacity!=0){var AO=g.legend.backgroundColor;if(AO==null){AO=g.grid.backgroundColor;if(AO&&typeof AO=="string"){AO=C.color.parse(AO)}else{AO=C.color.extract(AS,"background-color")}AO.a=1;AO=AO.toString()}var AK=AS.children();C('<div style="position:absolute;width:'+AK.width()+"px;height:"+AK.height()+"px;"+AQ+"background-color:"+AO+';"> </div>').prependTo(AS).css("opacity",g.legend.backgroundOpacity)}}}var w=[],J=null;function AF(AR,AP,AM){var AX=g.grid.mouseActiveRadius,Aj=AX*AX+1,Ah=null,Aa=false,Af,Ad;for(Af=0;Af<O.length;++Af){if(!AM(O[Af])){continue}var AY=O[Af],AQ=AY.xaxis,AO=AY.yaxis,Ae=AY.datapoints.points,Ac=AY.datapoints.pointsize,AZ=AQ.c2p(AR),AW=AO.c2p(AP),AL=AX/AQ.scale,AK=AX/AO.scale;if(AY.lines.show||AY.points.show){for(Ad=0;Ad<Ae.length;Ad+=Ac){var AT=Ae[Ad],AS=Ae[Ad+1];if(AT==null){continue}if(AT-AZ>AL||AT-AZ<-AL||AS-AW>AK||AS-AW<-AK){continue}var AV=Math.abs(AQ.p2c(AT)-AR),AU=Math.abs(AO.p2c(AS)-AP),Ab=AV*AV+AU*AU;if(Ab<=Aj){Aj=Ab;Ah=[Af,Ad/Ac]}}}if(AY.bars.show&&!Ah){var AN=AY.bars.align=="left"?0:-AY.bars.barWidth/2,Ag=AN+AY.bars.barWidth;for(Ad=0;Ad<Ae.length;Ad+=Ac){var AT=Ae[Ad],AS=Ae[Ad+1],Ai=Ae[Ad+2];if(AT==null){continue}if(O[Af].bars.horizontal?(AZ<=Math.max(Ai,AT)&&AZ>=Math.min(Ai,AT)&&AW>=AS+AN&&AW<=AS+Ag):(AZ>=AT+AN&&AZ<=AT+Ag&&AW>=Math.min(Ai,AS)&&AW<=Math.max(Ai,AS))){Ah=[Af,Ad/Ac]}}}}if(Ah){Af=Ah[0];Ad=Ah[1];Ac=O[Af].datapoints.pointsize;return{datapoint:O[Af].datapoints.points.slice(Ad*Ac,(Ad+1)*Ac),dataIndex:Ad,series:O[Af],seriesIndex:Af}}return null}function D(AK){if(g.grid.hoverable){H("plothover",AK,function(AL){return AL.hoverable!=false})}}function d(AK){H("plotclick",AK,function(AL){return AL.clickable!=false})}function H(AL,AK,AM){var AN=AD.offset(),AS={pageX:AK.pageX,pageY:AK.pageY},AQ=AK.pageX-AN.left-e.left,AO=AK.pageY-AN.top-e.top;if(s.xaxis.used){AS.x=s.xaxis.c2p(AQ)}if(s.yaxis.used){AS.y=s.yaxis.c2p(AO)}if(s.x2axis.used){AS.x2=s.x2axis.c2p(AQ)}if(s.y2axis.used){AS.y2=s.y2axis.c2p(AO)}var AT=AF(AQ,AO,AM);if(AT){AT.pageX=parseInt(AT.series.xaxis.p2c(AT.datapoint[0])+AN.left+e.left);AT.pageY=parseInt(AT.series.yaxis.p2c(AT.datapoint[1])+AN.top+e.top)}if(g.grid.autoHighlight){for(var AP=0;AP<w.length;++AP){var AR=w[AP];if(AR.auto==AL&&!(AT&&AR.series==AT.series&&AR.point==AT.datapoint)){x(AR.series,AR.point)}}if(AT){AE(AT.series,AT.datapoint,AL)}}l.trigger(AL,[AS,AT])}function q(){if(!J){J=setTimeout(v,30)}}function v(){J=null;AJ.save();AJ.clearRect(0,0,y,Q);AJ.translate(e.left,e.top);var AL,AK;for(AL=0;AL<w.length;++AL){AK=w[AL];if(AK.series.bars.show){z(AK.series,AK.point)}else{u(AK.series,AK.point)}}AJ.restore();Z(L.drawOverlay,[AJ])}function AE(AM,AK,AN){if(typeof AM=="number"){AM=O[AM]}if(typeof AK=="number"){AK=AM.data[AK]}var AL=j(AM,AK);if(AL==-1){w.push({series:AM,point:AK,auto:AN});q()}else{if(!AN){w[AL].auto=false}}}function x(AM,AK){if(AM==null&&AK==null){w=[];q()}if(typeof AM=="number"){AM=O[AM]}if(typeof AK=="number"){AK=AM.data[AK]}var AL=j(AM,AK);if(AL!=-1){w.splice(AL,1);q()}}function j(AM,AN){for(var AK=0;AK<w.length;++AK){var AL=w[AK];if(AL.series==AM&&AL.point[0]==AN[0]&&AL.point[1]==AN[1]){return AK}}return -1}function u(AN,AM){var AL=AM[0],AR=AM[1],AQ=AN.xaxis,AP=AN.yaxis;if(AL<AQ.min||AL>AQ.max||AR<AP.min||AR>AP.max){return }var AO=AN.points.radius+AN.points.lineWidth/2;AJ.lineWidth=AO;AJ.strokeStyle=C.color.parse(AN.color).scale("a",0.5).toString();var AK=1.5*AO;AJ.beginPath();AJ.arc(AQ.p2c(AL),AP.p2c(AR),AK,0,2*Math.PI,false);AJ.stroke()}function z(AN,AK){AJ.lineWidth=AN.bars.lineWidth;AJ.strokeStyle=C.color.parse(AN.color).scale("a",0.5).toString();var AM=C.color.parse(AN.color).scale("a",0.5).toString();var AL=AN.bars.align=="left"?0:-AN.bars.barWidth/2;AB(AK[0],AK[1],AK[2]||0,AL,AL+AN.bars.barWidth,0,function(){return AM},AN.xaxis,AN.yaxis,AJ,AN.bars.horizontal)}function R(AM,AL,AQ,AO){if(typeof AM=="string"){return AM}else{var AP=Y.createLinearGradient(0,AQ,0,AL);for(var AN=0,AK=AM.colors.length;AN<AK;++AN){var AR=AM.colors[AN];if(typeof AR!="string"){AR=C.color.parse(AO).scale("rgb",AR.brightness);AR.a*=AR.opacity;AR=AR.toString()}AP.addColorStop(AN/(AK-1),AR)}return AP}}}C.plot=function(G,E,D){var F=new B(C(G),E,D,C.plot.plugins);return F};C.plot.plugins=[];C.plot.formatDate=function(H,E,G){var L=function(N){N=""+N;return N.length==1?"0"+N:N};var D=[];var M=false;var K=H.getUTCHours();var I=K<12;if(G==null){G=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(E.search(/%p|%P/)!=-1){if(K>12){K=K-12}else{if(K==0){K=12}}}for(var F=0;F<E.length;++F){var J=E.charAt(F);if(M){switch(J){case"h":J=""+K;break;case"H":J=L(K);break;case"M":J=L(H.getUTCMinutes());break;case"S":J=L(H.getUTCSeconds());break;case"d":J=""+H.getUTCDate();break;case"m":J=""+(H.getUTCMonth()+1);break;case"y":J=""+H.getUTCFullYear();break;case"b":J=""+G[H.getUTCMonth()];break;case"p":J=(I)?("am"):("pm");break;case"P":J=(I)?("AM"):("PM");break}D.push(J);M=false}else{if(J=="%"){M=true}else{D.push(J)}}}return D.join("")};function A(E,D){return D*Math.floor(E/D)}})(jQuery);
\ No newline at end of file diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/options.html b/chrome/common/extensions/docs/examples/extensions/benchmark/options.html index 4a09047..24adc77 100644 --- a/chrome/common/extensions/docs/examples/extensions/benchmark/options.html +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/options.html @@ -4,7 +4,11 @@ <script src="jst/util.js" type="text/javascript"></script> <script src="jst/jsevalcontext.js" type="text/javascript"></script> <script src="jst/jstemplate.js" type="text/javascript"></script> - <script src="jst/jstemplate_example.js" type="text/javascript"></script> + <script src="jquery/jquery-1.4.2.min.js" type="text/javascript"></script> + <script src="jquery/jquery.flot.min.js" type="text/javascript"></script> + <script src="jquery/jquery.flot.dashes.js" type="text/javascript"></script> + <script src="util/table2CSV.js" type="text/javascript"></script> + <script src="util/sorttable.js" type="text/javascript"></script> <style> body { @@ -54,16 +58,16 @@ div#header p { display: inline; } -table.list { +table.sortable { font-size: 84%; table-layout: fixed; } -table.list:not([class*='filtered']) tr:nth-child(odd) td:not([class*='filtered']) { +table.sortable:not([class*='filtered']) tr:nth-child(even) td:not([class*='filtered']) { background: #eff3ff; } -table.list th { +.nobg { padding: 0 0.5em; vertical-align: bottom; font-weight: bold; @@ -72,6 +76,20 @@ table.list th { text-align: center; } +.bg{ + padding: 0 0.5em; + vertical-align: bottom; + font-weight: bold; + color: #315d94; + color: black; + text-align: center; + cursor: pointer; +} + +.bg:hover { + background: #eff3aa; +} + .avg { font-weight: bold; text-align: center; @@ -83,14 +101,35 @@ table.list th { } .bggraph { - background-color: #faa; white-space: nowrap; } +.file_input +{ + position: absolute; + width: 140px; + height: 26px; + overflow: hidden; +} + +.file_input_button +{ + width: 140px; + position: absolute; + top: 0px; +} + +.file_input_hidden +{ + font-size: 25px; + position: absolute; + right: 0px; + top: 0px; + opacity: 0; +} </style> <script> - var max_sample = 0; Array.max = function(array) { @@ -222,6 +261,293 @@ function restoreTable() { document.getElementById("expand").value = FULL_VIEW; } } + +// A class to store the data to plot. +function PData() { + this.xAxis = "Iteration(s)"; + this.yAxis = ""; + this.A = []; // Two data sets for comparison. + this.B = []; + this.avgA = []; // Avg value is plotted as a line. + this.avgB = []; + this.maxA = 0; + this.maxB = 0; + this.countA = 0; // Size of the data sets. + this.countB = 0; + + this.setYAxis = function (str) { + this.yAxis = str; + } + + this.setAvg = function (arr, cha) { + if (cha == 'A') { + var avgA = Array.avg(arr); + for (var i = 1; i <= this.countA; i++) { + this.avgA.push([i, avgA]); + } + } else if (cha == 'B') { + var avgB = Array.avg(arr); + for (var i = 1; i <= this.countB; i++) { + this.avgB.push([i, avgB]); + } + } + } + + this.setMax = function (arr, cha) { + if (cha == 'A') { + this.maxA = Array.max(arr); + } else if (cha == 'B') { + this.maxB = Array.max(arr); + } + } + + // Add an entry to the array. + this.addArr = function (val, cha) { + if (cha == 'A') { + this.countA++; + this.A.push([this.countA, val]); + } else if (cha == 'B') { + this.countB++; + this.B.push([this.countB, val]); + } + } + + // Plot the graph at the specified place. + this.plot = function (placeholder) { + $.plot(placeholder, + [// Line A + { + data: this.A, + label: "A's " + this.yAxis + " in " + this.countA + " " + this.xAxis, + points: { + show: true + }, + lines: { + show: true + } + }, + + // Line B + { + data: this.B, + label: "B's " + this.yAxis + " in " + this.countB + " " + this.xAxis, + points: { + show: true + }, + lines: { + show: true + } + }, + + // Line avgA + { + data: this.avgA, + label: "A's avg " + this.yAxis, + dashes: { + show: true + } + }, + + // Line avgB + { + data: this.avgB, + label: "B's avg " + this.yAxis, + dashes: { + show: true + } + }], + + // Axis and legend setup. + { xaxis: { + max: this.countA > this.countB ? this.countA : this.countB, + tickSize: 1, + tickDecimals: 0 + }, + yaxis: { + // Leave some space for legend. + max: this.maxA > this.maxB ? this.maxA * 1.5 : this.maxB * 1.5 + }, + legend: { + backgroundOpacity: 0 + } + }); + } +} + +// Compare the selected metric of the two selected data sets. +function compare() { + var checkboxArr = document.getElementsByName("checkboxArr"); + var radioArr = document.getElementsByName("radioArr"); + + if (checkAmount(checkboxArr) != 2) { + alert("please select two rows to compare"); + return; + } + + var rowIndexArr = getSelectedIndex(checkboxArr); + var colIndexArr = getSelectedIndex(radioArr); + // To this point, it is for sure that rowIndexArr has two elements + // while colIndexArr has one. + var selectedRowA = rowIndexArr[0]; + var selectedRowB = rowIndexArr[1]; + var selectedCol = colIndexArr[0]; + + var extension = chrome.extension.getBackgroundPage(); + var data = extension.results.data; + var selectedA = getSelectedResults(data,selectedRowA,selectedCol); + var selectedB = getSelectedResults(data,selectedRowB,selectedCol); + var yAxis = getMetricName(selectedCol); + + // Indicate A and B on selected rows. + checkboxArr[selectedRowA].parentElement.firstChild.data = "A"; + checkboxArr[selectedRowB].parentElement.firstChild.data = "B"; + + plot(selectedA, selectedB, yAxis); +} + +// Show the comparison graph. +function plot(A, B, axis) { + var plotData = new PData(); + + plotData.setYAxis(axis); + for (var i = 0; i < A.length; i++) { + plotData.addArr(A[i],'A'); + } + for (var i = 0; i < B.length; i++) { + plotData.addArr(B[i],'B'); + } + plotData.setAvg(A,'A'); + plotData.setAvg(B,'B'); + plotData.setMax(A,'A'); + plotData.setMax(B,'B'); + + var placeholder = document.getElementById("placeholder"); + placeholder.style.display = ""; + plotData.plot(placeholder); +} + +var METRIC = {"STARTLOAD": 0, "COMMITLOAD": 1, "DOCLOAD": 2, "PAINT": 3, + "TOTAL": 4, "REQUESTS": 5, "CONNECTS": 6, "READKB": 7, + "WRITEKB": 8, "READKBPS": 9, "WRITEKBPS": 10}; + +// Retrieve the metric name from index. +function getMetricName (index) { + switch (index) { + case METRIC.STARTLOAD: + return "Start Load Time"; + case METRIC.COMMITLOAD: + return "Commit Load Time"; + case METRIC.DOCLOAD: + return "Doc Load Time"; + case METRIC.PAINT: + return "Paint Time"; + case METRIC.TOTAL: + return "Total Load Time"; + case METRIC.REQUESTS: + return "# Requests"; + case METRIC.CONNECTS: + return "# Connects"; + case METRIC.READKB: + return "Read KB"; + case METRIC.WRITEKB: + return "Write KB"; + case METRIC.READKBPS: + return "Read KBps"; + case METRIC.WRITEKBPS: + return "Write KBps"; + default: + return ""; + } +} + +// Get the results with a specific row (data set) and column (metric). +function getSelectedResults(arr, rowIndex, colIndex) { + switch (colIndex) { + case METRIC.STARTLOAD: + return arr[rowIndex].startLoadResults; + case METRIC.COMMITLOAD: + return arr[rowIndex].commitLoadResults; + case METRIC.DOCLOAD: + return arr[rowIndex].docLoadResults; + case METRIC.PAINT: + return arr[rowIndex].paintResults; + case METRIC.TOTAL: + return arr[rowIndex].totalResults; + case METRIC.REQUESTS: + return arr[rowIndex].requests; + case METRIC.CONNECTS: + return arr[rowIndex].connects; + case METRIC.READKB: + return arr[rowIndex].KbytesRead; + case METRIC.WRITEKB: + return arr[rowIndex].KbytesWritten; + case METRIC.READKBPS: + return arr[rowIndex].readbpsResults; + case METRIC.WRITEKBPS: + return arr[rowIndex].writebpsResults; + default: + return undefined; + } +} + +// Ensure only two data sets (rows) are selected. +function checkAmount(arr) { + var amount = 0; + for (var i = 0; i < arr.length; i++) { + if (arr[i].checked) { + amount++; + } + } + return amount; +} + +// Get the index of selected row or column. +function getSelectedIndex(arr) { + var selectedArr = new Array(); + for (var i = 0; i < arr.length; i++) { + if(arr[i].checked) { + selectedArr.push(i); + } + } + return selectedArr; +} + +// Repaint or hide the chart. +function updateChart(caller) { + var placeholder = document.getElementById("placeholder"); + if (caller.type == "radio") { + // Other radio button is clicked. + if (placeholder.style.display == "") { + compare(); + } + } else { + // Other checkbox or clearing results is clicked. + if (placeholder.style.display == "") { + placeholder.style.display = "none"; + } + } +} + +// Clear indicators besides checkbox. +function clearIndicator () { + var checkboxArr = document.getElementsByName("checkboxArr"); + for (var i = 0; i < checkboxArr.length; i++) { + checkboxArr[i].parentElement.firstChild.data = ""; + } +} + +// Enable/Disable buttons according to checkbox change. +function checkSelected () { + var checkboxArr = document.getElementsByName("checkboxArr"); + if (checkAmount(checkboxArr) !=0) { + document.getElementById("clearSelected").disabled = false; + document.getElementById("compare").disabled = false; + } else { + document.getElementById("clearSelected").disabled = true; + document.getElementById("compare").disabled = true; + } +} + // Object to summarize everything var totals = {}; @@ -230,7 +556,8 @@ function computeDisplayResults(data) { var count = data.data.length; for (var i = 0; i < count; i++) { var obj = data.data[i]; - var resultList = obj.totalResults; + obj.displayTime = setDisplayTime(obj.timestamp); + var resultList = obj.totalResults; obj.mean = Array.avg(resultList); obj.stddev = Array.stddev(resultList); obj.stderr = obj.stddev / Math.sqrt(obj.iterations); @@ -239,18 +566,18 @@ function computeDisplayResults(data) { obj.cilow = obj.mean - ci; obj.min = Array.min(resultList); obj.max = Array.max(resultList); - obj.readbps = obj.bytesRead * 8 / obj.totalTime; - obj.writebps = obj.bytesWritten * 8 / obj.totalTime; - obj.readKB = obj.bytesRead / 1024; - obj.writeKB = obj.bytesWritten / 1024; + obj.readbps = Array.avg(obj.readbpsResults); + obj.writebps = Array.avg(obj.writebpsResults); + obj.readKB = Array.avg(obj.KbytesRead); + obj.writeKB = Array.avg(obj.KbytesWritten); obj.paintMean = Array.avg(obj.paintResults); obj.startLoadMean = Array.avg(obj.startLoadResults); obj.commitLoadMean = Array.avg(obj.commitLoadResults); obj.docLoadMean = Array.avg(obj.docLoadResults); - obj.displayRequests = obj.requests; - obj.displayConnects = obj.connects; - obj.displaySpdySessions = obj.spdySessions; + obj.displayRequests = Array.avg(obj.requests); + obj.displayConnects = Array.avg(obj.connects); + obj.displaySpdySessions = Array.avg(obj.spdySessions); obj.displayDomNum = obj.domNum; obj.displayMaxDepth = obj.maxDepth; @@ -260,6 +587,23 @@ function computeDisplayResults(data) { return count; } +// Convert timestamp to readable string. +function setDisplayTime(ts) { + var year = ts.getFullYear(); + var mon = ts.getMonth()+1; + var date = ts.getDate(); + var hrs = ts.getHours(); + var mins = ts.getMinutes(); + var secs = ts.getSeconds(); + + mon = ( mon < 10 ? "0" : "" ) + mon; + date = ( date < 10 ? "0" : "" ) + date; + mins = ( mins < 10 ? "0" : "" ) + mins; + secs = ( secs < 10 ? "0" : "" ) + secs; + + return (year + "/" + mon + "/" + date + " " + hrs + ":" + mins + ":" + secs); +} + // Subtract the results from two data sets. // This function could be smarter about what it subtracts, // for now it just subtracts everything. @@ -442,20 +786,51 @@ function run() { function showConfirm() { var r = confirm("Are you sure to clear results?"); if (r) { - clearResults(); + // Find out the event source element. + var evtSrc = window.event.srcElement; + if (evtSrc.value == "Clear Selected") { + clearSelected(); + } else if (evtSrc.value == "Clear All") { + clearResults(); + } + } +} + +// Clear the selected results +function clearSelected() { + var extension = chrome.extension.getBackgroundPage(); + var checkboxArr = document.getElementsByName("checkboxArr"); + var rowIndexArr = getSelectedIndex(checkboxArr); + var currIndex; + for (var i = 0; i < rowIndexArr.length; i++) { + currIndex = rowIndexArr[i]; + // Update the index of the original row in the modified array. + currIndex -= i; + extension.results.data.splice(currIndex, 1); + document.location.reload(true); + updateChart(this); + jsinit(); } } -// Clear the results +// Clear all the results function clearResults() { var extension = chrome.extension.getBackgroundPage(); extension.results = {}; extension.results.data = new Array(); document.getElementById("json").value = ""; document.getElementById("baseline").value = ""; + updateChart(this); jsinit(); } +// Export html table into CSV format. +function export() { + var checkboxArr = document.getElementsByName("checkboxArr"); + var rowNum = checkboxArr.length + 1; // # of data rows plus total-stats row. + $('#t').table2CSV(rowNum); +} + // Toggle display of an element function toggle(id) { var elt = document.getElementById(id); @@ -481,52 +856,58 @@ Clear Connections?<input id="clearconns" type="checkbox"> Clear Cache?<input id="clearcache" type="checkbox"> Enable Spdy?<input id="enablespdy" type="checkbox"> <br> -<span>URLs to load</span> <input type="text" id="testurl" size="100"> -<br> -Load URLs from file<input type="file" id="files" name="files[]" multiple /> -<br> -<form onsubmit="config();run()" style="display:inline"> +<span>URLs to load</span> <input type="text" id="testurl" size="80"> +<span class="file_input"> +<input class="file_input_button" type="button" value="Load URLs From File" /> +<input class="file_input_hidden" type="file" id="files" name="files[]" multiple /> +</span> +<form onsubmit="config();run()"> <input type="submit" value="Run"> </form> -<input type="button" value="Clear Results" onclick="showConfirm()"> <p> <h1>Results</h1> <input id="expand" type="button" value="Show More Details" onclick="expand()"> - -<table id="t" class="list" width="100%"> +<input id="clearSelected" type="button" value="Clear Selected" disabled="true" onclick="showConfirm()"> +<input id="clearAll" type="button" value="Clear All" onclick="showConfirm()"> +<input type="button" value="Export As .csv" onclick="export()"> +<table id="t" class="sortable" width="100%"> <tr> - <th width=150>url</th> - <th width=50>iterations</th> - <th width=50 >via spdy</th> - <th width=50 style="display:none">start load mean</th> - <th width=50 style="display:none">commit load mean</th> - <th width=50>doc load mean</th> - <th width=50>paint mean</th> - <th width=50>total load mean</th> - <th width=50>stddev</th> - <th width=50 style="display:none">stderr</th> - <th width=50 style="display:none">95% CI-low</th> - <th width=50 style="display:none">95% CI-high</th> - <th width=50 style="display:none">min</th> - <th width=50 style="display:none">max</th> - <th width=50 style="display:none"># Requests</th> - <th width=50 style="display:none"># Connects</th> - <th width=50 style="display:none"># SPDY Sessions</th> - <th width=50 style="display:none">Read KB</th> - <th width=50 style="display:none">Write KB</th> - <th width=50>Read KBps</th> - <th width=50>Write KBps</th> - <th width=50># DOM</th> - <th width=70 style="display:none">max DOM depth</th> - <th width=30 style="display:none">min</th> - <th width=30 style="display:none">avg</th> - <th samples style="display:none"></th> + <th width=35 class="nobg"></th> + <th width=215 class="nobg">url</th> + <th width=110 class="nobg" style="display:none">timestamp</th> + <th width=50 class="nobg">iterations</th> + <th width=50 class="nobg">via spdy</th> + <th width=50 class="bg" style="display:none">start load mean</th> + <th width=50 class="bg" style="display:none">commit load mean</th> + <th width=50 class="bg">doc load mean</th> + <th width=50 class="bg">paint mean</th> + <th width=50 class="bg">total load mean</th> + <th width=50 class="bg">stddev</th> + <th width=50 class="bg" style="display:none">stderr</th> + <th width=50 class="bg" style="display:none">95% CI-low</th> + <th width=50 class="bg" style="display:none">95% CI-high</th> + <th width=50 class="bg" style="display:none">min</th> + <th width=50 class="bg" style="display:none">max</th> + <th width=60 class="bg" style="display:none"># Requests</th> + <th width=60 class="bg" style="display:none"># Connects</th> + <th width=50 class="bg" style="display:none"># SPDY Sessions</th> + <th width=50 class="bg" style="display:none">Read KB</th> + <th width=50 class="bg" style="display:none">Write KB</th> + <th width=50 class="bg">Read KBps</th> + <th width=50 class="bg">Write KBps</th> + <th width=50 class="bg"># DOM</th> + <th width=70 class="bg" style="display:none">max DOM depth</th> + <th width=30 class="bg" style="display:none">min</th> + <th width=30 class="bg" style="display:none">avg</th> + <th samples class="nobg" style="display:none">total loan time samples</th> </tr> <tr id="t.total" jsselect="totals"> + <td class="avg" jseval="1"></td> <td class="url">TOTALS <span jscontent="url"></span></td> + <td class="avg" style="display:none"></td> <td class="avg" jseval="1"></td> <td class="avg" jseval="1"></td> <td class="avg" style="display:none"><span jseval="val = startLoadMean.toFixed(1)" jscontent="val"></span></td> @@ -544,11 +925,20 @@ Load URLs from file<input type="file" id="files" name="files[]" multiple /> <td class="avg" jseval="1"></td> <td class="avg" jseval="1"></td> <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> <td class="data"></td> </tr> <tr jsselect="data"> + <td align=right> <input type="checkbox" name="checkboxArr" onclick="updateChart(this);clearIndicator();checkSelected()"></td> <td class="url" jseval="$width = getWidth($this.mean, this); url.length > 40 ? $suburl = url.substring(0,27) + '...' + url.substring(url.length-10, url.length) : $suburl=url"><div jsvalues=".style.width:$width" class="bggraph"><a jsvalues="href:$this.url" jscontent="$suburl"></a></div></td> + <td class="avg" style="display:none" jseval="val = displayTime" jscontent="val"></td> <td class="avg" jseval="val = iterations" jscontent="val"></td> <td class="avg" jseval="val = viaSpdy" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = startLoadMean.toFixed(1)" jscontent="val"></td> @@ -562,9 +952,9 @@ Load URLs from file<input type="file" id="files" name="files[]" multiple /> <td class="avg" style="display:none" jseval="val = cihigh.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = min.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = max.toFixed(1)" jscontent="val"></td> - <td class="avg" style="display:none" jseval="val = displayRequests" jscontent="val"></td> - <td class="avg" style="display:none" jseval="val = displayConnects" jscontent="val"></td> - <td class="avg" style="display:none" jseval="val = displaySpdySessions" jscontent="val"></td> + <td class="avg" style="display:none" jseval="val = displayRequests.toFixed(1)" jscontent="val"></td> + <td class="avg" style="display:none" jseval="val = displayConnects.toFixed(1)" jscontent="val"></td> + <td class="avg" style="display:none" jseval="val = displaySpdySessions.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = readKB.toFixed(1)" jscontent="val"></td> <td class="avg" style="display:none" jseval="val = writeKB.toFixed(1)" jscontent="val"></td> <td class="avg" jseval="val = readbps.toFixed(1)" jscontent="val"></td> @@ -576,14 +966,45 @@ Load URLs from file<input type="file" id="files" name="files[]" multiple /> <td class="data" style="display:none"><span jsselect="totalResults"><span jscontent="$this"></span>,</span> </td> </tr> <tr jsdisplay="data.length == 0"> - <td colspan=11>No tests have been run yet.</td> + <td colspan=2>No tests have been run yet.</td> + </tr> + <tr jsdisplay="data.length > 1"> + <td width=25 jseval="1"></td> + <td class="url" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" jseval="1"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"> </td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)" checked></td> + <td class="avg" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg" style="display:none" jseval="1"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td> + </tr> + <tr jsdisplay="data.length > 1"> + <td> <input id="compare" type="button" value="Compare" disabled="true" onclick="compare()"></td> </tr> </table> <hr> +<center> +<div id="placeholder" style="width:430px;height:230px;display:none">graph place</div> +</center> <span onclick="toggle('json')">JSON data</span><br> -<textarea style="display: none" type=text id=json rows=10 cols=50></textarea><p> +<textarea style="display:none" type=text id=json rows=10 cols=50></textarea><p> <span onclick="toggle('baseline')">COMPARE to</span><br> -<textarea style="display: none" type=text id=baseline rows=10 cols=50 +<textarea style="display:none" type=text id=baseline rows=10 cols=50 onchange="jsinit()"></textarea><p> </body> diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js b/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js new file mode 100644 index 0000000..5583053 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js @@ -0,0 +1,489 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + +/* +Changes by jning: +-Specify which coloumns to sort or not by changing index in makeSortable(). +-To avoid including the non-data rows (e.g., radio buttions) into sorting, + add index and row handling (deletion and appending) in makeSortable() and + reverse(). +-Remove the init parts for other browsers. +*/ + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=5; i<headrow.length-1; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=1; j<rows.length-3; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + var noTestRow = tb.rows[tb.rows.length-3]; + var radioRow = tb.rows[tb.rows.length-2]; + var compareRow = tb.rows[tb.rows.length-1]; + tb.deleteRow(tb.rows.length-1); + tb.deleteRow(tb.rows.length-1); + tb.deleteRow(tb.rows.length-1); + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + tb.appendChild(noTestRow); + tb.appendChild(radioRow); + tb.appendChild(compareRow); + delete row_array; + delete noTestRow; + delete radioRow; + delete compareRow; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_numeric; + for (var i=0; i<table.tBodies[0].rows.length-3; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + tbody.appendChild(newrows[0]); + for (var i=newrows.length-4; i>0; i--) { + tbody.appendChild(newrows[i]); + } + tbody.appendChild(newrows[newrows.length-3]); + tbody.appendChild(newrows[newrows.length-2]); + tbody.appendChild(newrows[newrows.length-1]); + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; diff --git a/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js b/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js new file mode 100644 index 0000000..04a5166 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js @@ -0,0 +1,90 @@ +/* +This is a small JQuery utility to export HTML table as CSV file. + +The author is Kunal Babre and the original script can be found in +http://www.kunalbabre.com/projects/table2CSV.php. Permissions are +granted by the author to make changes and redistribute. + +Changes made by jning: To avoid exporting the textbox, radio buttons and etc. +in the table, the parameters rowNum and index in $().find().each() or +$().filter().find.each() help to ignore non-data cells. +*/ + +jQuery.fn.table2CSV = function(rowNum, options) { + var options = jQuery.extend({ + separator: ',', + header: [], + delivery: 'popup' // popup, value + }, + options); + + var csvData = []; + var headerArr = []; + var el = this; + + //header + var numCols = options.header.length; + var tmpRow = []; // construct header avalible array + + if (numCols > 0) { + for (var i = 0; i < numCols; i++) { + tmpRow[tmpRow.length] = formatData(options.header[i]); + } + } else { + $(el).filter(':visible').find('th').each(function(index) { + if (index > 0 && $(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()); + }); + } + + row2CSV(tmpRow); + + // actual data + $(el).find('tr').each(function(index) { + if (index < rowNum + 1) { + var tmpRow = []; + $(this).filter(':visible').find('td').each(function(index) { + if (index > 0 && $(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()); + }); + row2CSV(tmpRow); + } + }); + if (options.delivery == 'popup') { + var mydata = csvData.join('\n'); + return popup(mydata); + } else { + var mydata = csvData.join('\n'); + return mydata; + } + + function row2CSV(tmpRow) { + var tmp = tmpRow.join('') // to remove any blank rows + // alert(tmp); + if (tmpRow.length > 0 && tmp != '') { + var mystr = tmpRow.join(options.separator); + csvData[csvData.length] = mystr; + } + } + function formatData(input) { + // replace " with “ + var regexp = new RegExp(/["]/g); + var output = input.replace(regexp, "“"); + //HTML + var regexp = new RegExp(/\<[^\<]+\>/g); + var output = output.replace(regexp, ""); + if (output == "") return ''; + return '"' + output + '"'; + } + function popup(data) { + var generator = window.open('', 'csv', 'height=400,width=600'); + generator.document.write('<html><head><title>CSV</title>'); + generator.document.write('</head><body >'); + generator.document.write('<textArea cols=70 rows=15 wrap="off" >'); + generator.document.write(data); + generator.document.write('</textArea>'); + generator.document.write('</body></html>'); + generator.document.close(); + return true; + } +}; diff --git a/chrome/common/extensions/docs/experimental.clipboard.html b/chrome/common/extensions/docs/experimental.clipboard.html index 0bf145d..dc9d20b 100644 --- a/chrome/common/extensions/docs/experimental.clipboard.html +++ b/chrome/common/extensions/docs/experimental.clipboard.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.clipboard - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.contextMenus.html b/chrome/common/extensions/docs/experimental.contextMenus.html index db22bb4..5c11ce2 100644 --- a/chrome/common/extensions/docs/experimental.contextMenus.html +++ b/chrome/common/extensions/docs/experimental.contextMenus.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.contextMenus - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.cookies.html b/chrome/common/extensions/docs/experimental.cookies.html index 1eaeee9..b57ea7f 100644 --- a/chrome/common/extensions/docs/experimental.cookies.html +++ b/chrome/common/extensions/docs/experimental.cookies.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.cookies - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.history.html b/chrome/common/extensions/docs/experimental.history.html index 5b1b412..7a2deda 100644 --- a/chrome/common/extensions/docs/experimental.history.html +++ b/chrome/common/extensions/docs/experimental.history.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.history - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.html b/chrome/common/extensions/docs/experimental.html index 08480af..3fe2e15 100644 --- a/chrome/common/extensions/docs/experimental.html +++ b/chrome/common/extensions/docs/experimental.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.* APIs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.idle.html b/chrome/common/extensions/docs/experimental.idle.html index d7b2d80..7712706 100644 --- a/chrome/common/extensions/docs/experimental.idle.html +++ b/chrome/common/extensions/docs/experimental.idle.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>experimental.chrome.idle - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.infobars.html b/chrome/common/extensions/docs/experimental.infobars.html index 97d859d..0c9295d 100644 --- a/chrome/common/extensions/docs/experimental.infobars.html +++ b/chrome/common/extensions/docs/experimental.infobars.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.infobars - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.omnibox.html b/chrome/common/extensions/docs/experimental.omnibox.html index 995f838..1a668ed 100644 --- a/chrome/common/extensions/docs/experimental.omnibox.html +++ b/chrome/common/extensions/docs/experimental.omnibox.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.omnibox - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -1259,7 +1263,7 @@ You can find samples of this API on the </div> </div> - </dl> + </dl> </div> </dd> diff --git a/chrome/common/extensions/docs/experimental.processes.html b/chrome/common/extensions/docs/experimental.processes.html index 801ff1e..a5e3b05 100644 --- a/chrome/common/extensions/docs/experimental.processes.html +++ b/chrome/common/extensions/docs/experimental.processes.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.processes - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.proxy.html b/chrome/common/extensions/docs/experimental.proxy.html index 57d48f5..3d27695 100644 --- a/chrome/common/extensions/docs/experimental.proxy.html +++ b/chrome/common/extensions/docs/experimental.proxy.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.proxy - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.sidebar.html b/chrome/common/extensions/docs/experimental.sidebar.html index eb9493b..35b757b 100644 --- a/chrome/common/extensions/docs/experimental.sidebar.html +++ b/chrome/common/extensions/docs/experimental.sidebar.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.sidebar - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.webNavigation.html b/chrome/common/extensions/docs/experimental.webNavigation.html index 8ab76fc..aebd5ca 100644 --- a/chrome/common/extensions/docs/experimental.webNavigation.html +++ b/chrome/common/extensions/docs/experimental.webNavigation.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.webNavigation - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/experimental.webRequest.html b/chrome/common/extensions/docs/experimental.webRequest.html index 9615bcc..0541584 100644 --- a/chrome/common/extensions/docs/experimental.webRequest.html +++ b/chrome/common/extensions/docs/experimental.webRequest.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.experimental.webRequest - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/extension.html b/chrome/common/extensions/docs/extension.html index b361568..36c768f 100644 --- a/chrome/common/extensions/docs/extension.html +++ b/chrome/common/extensions/docs/extension.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.extension - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -1891,7 +1895,7 @@ For details, see </div> </div> - </dl> + </dl> </div> </dd> diff --git a/chrome/common/extensions/docs/external_extensions.html b/chrome/common/extensions/docs/external_extensions.html index 6bf9e4f..d3f18ad 100644 --- a/chrome/common/extensions/docs/external_extensions.html +++ b/chrome/common/extensions/docs/external_extensions.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Other Deployment Options - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/faq.html b/chrome/common/extensions/docs/faq.html index 94ec93b..ea4af3a 100644 --- a/chrome/common/extensions/docs/faq.html +++ b/chrome/common/extensions/docs/faq.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Frequently Asked Questions - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -376,12 +380,21 @@ try the <p> As long as you are using a version of Google Chrome that supports extensions, you already have everything you need to start writing an - extension of your own. Select <b>Extensions</b> from the Tools menu - <img style="vertical-align: middle; margin:0; padding:0" src="images/toolsmenu.gif"> (or from - the Window menu on Mac) and click <b>Developer Mode</b>. From there, you can load - an unpacked directory of files as if it were a packaged extension, reload - extensions, and more. For a complete tutorial, please view - <a href="http://code.google.com/chrome/extensions/getstarted.html">this getting started guide</a>. + extension of your own. + You can start by turning on Developer mode. + </p> + + <p> + Click the wrench icon + <img src="images/toolsmenu.gif" height="29" width="29" alt="" class="nomargin"> + and select <b>Extensions</b> from the <b>Tools</b> menu + (or from the <b>Window</b> menu on Mac). + If there's a "+" next to "Developer mode", + click the "+" so it turns into a "-". + Now you can reload extensions, + load an unpacked directory of files as if it were a packaged extension, + and more. For a complete tutorial, see + <a href="http://code.google.com/chrome/extensions/getstarted.html">Getting Started</a>. </p> <h3 id="faq-dev-02">Can I make cross-domain Ajax requests in an extension?</h3> @@ -679,7 +692,9 @@ win,stable,#.#.###.#,#.#.###.#</pre> to the ticket you starred or opened. This will make it easier for others with the same request to find the correct ticket. </li> -</ol></div> +</ol> + +</div> <!-- API PAGE --> <div class="apiPage" style="display: none; "> diff --git a/chrome/common/extensions/docs/getstarted.html b/chrome/common/extensions/docs/getstarted.html index a3a10fc..5590e32 100644 --- a/chrome/common/extensions/docs/getstarted.html +++ b/chrome/common/extensions/docs/getstarted.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: Getting Started (Hello, World!) - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -358,14 +362,12 @@ to the toolbar of Google Chrome. </li> <li id="load-ext"> Load the extension. <ol type="a"> - <li> + <li style="margin-top:0"> Bring up the extensions management page - by clicking the Tools menu - <img src="images/toolsmenu.gif" width="43" height="34" alt="" align="absmiddle" style="margin:0; padding:0"> - (or the Window menu on Mac) - and choosing <b>Extensions</b>. - (If you're using version 6 or later, - choose <b>Tools > Extensions</b>.) + by clicking the wrench icon + <img src="images/toolsmenu.gif" width="29" height="29" alt="" style="margin-top:0"> + and choosing <b>Tools > Extensions</b>. + (On Mac, use <b>Window > Extensions</b>.) </li> <li> diff --git a/chrome/common/extensions/docs/history.html b/chrome/common/extensions/docs/history.html index 41b93ab..5fed0af 100644 --- a/chrome/common/extensions/docs/history.html +++ b/chrome/common/extensions/docs/history.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>chrome.history - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/hosting.html b/chrome/common/extensions/docs/hosting.html index 20267e7..55c0178 100644 --- a/chrome/common/extensions/docs/hosting.html +++ b/chrome/common/extensions/docs/hosting.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Hosting - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/i18n-messages.html b/chrome/common/extensions/docs/i18n-messages.html index 7ff4c33..823bfc3 100644 --- a/chrome/common/extensions/docs/i18n-messages.html +++ b/chrome/common/extensions/docs/i18n-messages.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Formats: Locale-Specific Messages - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/i18n.html b/chrome/common/extensions/docs/i18n.html index 6809923..b33c856 100644 --- a/chrome/common/extensions/docs/i18n.html +++ b/chrome/common/extensions/docs/i18n.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Internationalization (i18n) - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -767,7 +771,7 @@ Here's how to change the locale using the UI on Google Chrome for Windows: </p> <ol> - <li> Tools menu (wrench) > <b>Options</b> </li> + <li> Wrench icon > <b>Options</b> </li> <li> Choose the <b>Under the Hood</b> tab </li> <li> Scroll down to <b>Web Content</b> </li> <li> Click <b>Change font and language settings</b> </li> diff --git a/chrome/common/extensions/docs/idle.html b/chrome/common/extensions/docs/idle.html index 2fd5dd8..0d6988c 100644 --- a/chrome/common/extensions/docs/idle.html +++ b/chrome/common/extensions/docs/idle.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Idle - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/images/toggle_sprite.png b/chrome/common/extensions/docs/images/toggle_sprite.png Binary files differnew file mode 100644 index 0000000..a91c2d4 --- /dev/null +++ b/chrome/common/extensions/docs/images/toggle_sprite.png diff --git a/chrome/common/extensions/docs/images/toolsmenu.gif b/chrome/common/extensions/docs/images/toolsmenu.gif Binary files differindex d6afcab..1b0b1e5 100644 --- a/chrome/common/extensions/docs/images/toolsmenu.gif +++ b/chrome/common/extensions/docs/images/toolsmenu.gif diff --git a/chrome/common/extensions/docs/index.html b/chrome/common/extensions/docs/index.html index 826d74b..12e3114 100644 --- a/chrome/common/extensions/docs/index.html +++ b/chrome/common/extensions/docs/index.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/js/sidebar.js b/chrome/common/extensions/docs/js/sidebar.js new file mode 100644 index 0000000..51d9292 --- /dev/null +++ b/chrome/common/extensions/docs/js/sidebar.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. + */ + +var TEXT_NODE = 3; // Text nodes have nodeType of 3. + +/** + * Toggles the display of nodes given the status of their associated controls. + * + * For each node passed to this function, check to see if a toggle has been + * inserted into the node's parent. If yes, change the state of the toggle and + * hide/reveal the node as needed. + * + * @param {NodeList|Node|Array.<Node>} Nodes to operate on. + */ +function toggleList(list) { + if (typeof list.length != 'number') { + list = Array(list); + } + + for (var i = 0; i < list.length; i++) { + var toggle = list[i].parentNode && + list[i].parentNode.firstChild; + if (toggle && toggle.className.substring(0, 6) == 'toggle') { + var visible = toggle.className == 'toggle'; + list[i].style.display = visible ? 'block' : 'none'; + toggle.className = visible ? 'toggle selected' : 'toggle'; + } + } +}; + +/** + * Reveals the hidden ancestor of the passed node, adjusts toggles as needed. + * + * @param {Node} node The node whose ancestor is a hidden toggleable element. + */ +function revealAncestor(node) { + while (node.parentNode) { + if (node.style.display == 'none') { + toggleList(node); + break; + } + node = node.parentNode; + } +}; + +/** + * Adds toggle controls to the sidebar list. + * + * Controls are inserted as the first children of list items in the sidebar + * which contain only text (not a link). Handlers are set up so that when a + * toggle control is clicked, any <ul> elements who are siblings of the control + * are hidden/revealed as appropriate given the control's state. + * + * If a list item possesses the class "leftNavSelected" its ancestor <ul> is + * revealed by default (it represents the current page). + */ +function initToggles() { + var toc = document.getElementById('gc-toc'); + var items = toc.getElementsByTagName('li'); + var selectedNode = null; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item.className == 'leftNavSelected') { + selectedNode = item; + } else if (item.firstChild && + item.firstChild.nodeType == TEXT_NODE) { + // Only assign toggles to text nodes in the sidebar. + var a = document.createElement('a'); + a.className = 'toggle selected'; + a.appendChild(document.createTextNode(' ')); + a.onclick = function() { + toggleList(this.parentNode.getElementsByTagName('ul')); + }; + item.insertBefore(a, item.firstChild); + toggleList(item.getElementsByTagName('ul')); + } + } + if (selectedNode) { + revealAncestor(selectedNode); + } +}; diff --git a/chrome/common/extensions/docs/management.html b/chrome/common/extensions/docs/management.html index 8fb7add..2792019 100644 --- a/chrome/common/extensions/docs/management.html +++ b/chrome/common/extensions/docs/management.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Management - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -234,10 +238,10 @@ <div id="toc"> <h2>Contents</h2> <ol> - <li style="display: none; "> - <a>h2Name</a> + <li> + <a href="#manifest">Manifest</a> <ol> - <li> + <li style="display: none; "> <a>h3Name</a> </li> </ol> @@ -257,6 +261,8 @@ <a href="#methods">Methods</a> <ol> <li> + <a href="#method-get">get</a> + </li><li> <a href="#method-getAll">getAll</a> </li><li> <a href="#method-launchApp">launchApp</a> @@ -304,10 +310,27 @@ <!-- STATIC CONTENT PLACEHOLDER --> <div id="static"><div id="pageData-name" class="pageData">Management</div> + <!-- BEGIN AUTHORED CONTENT --> <p id="classSummary"> The <code>chrome.management</code> module provides ways to manage the list of extensions/apps that are installed and running. It is particularly useful for extensions that <a href="override.html">override</a> the built-in New Tab page. </p> + +<h2 id="manifest">Manifest</h2> + +<p>You must declare the "management" permission +in the <a href="manifest.html">extension manifest</a> +to use the management API. +For example:</p> +<pre>{ + "name": "My extension", + ... + <b>"permissions": [ + "management" + ]</b>, + ... +}</pre> + <!-- END AUTHORED CONTENT --> </div> @@ -341,6 +364,243 @@ The <code>chrome.management</code> module provides ways to manage the list of ex <!-- iterates over all functions --> <div class="apiItem"> + <a name="method-get"></a> <!-- method-anchor --> + <h4>get</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.management.get</span>(<span class="null"><span style="display: none; ">, </span><span>string</span> + <var><span>id</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Return information about the installed extension with the given ID.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + <dt> + <var>id</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The ID from an item of <a href="management.html#type-ExtensionInfo">ExtensionInfo</a>.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>callback</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>function</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>ExtensionInfo result</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + <dt> + <var>result</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span> + <a href="management.html#type-ExtensionInfo">ExtensionInfo</a> + </span> + <span style="display: none; "> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> <a name="method-getAll"></a> <!-- method-anchor --> <h4>getAll</h4> diff --git a/chrome/common/extensions/docs/manifest.html b/chrome/common/extensions/docs/manifest.html index d8bd4f4..b593f31 100644 --- a/chrome/common/extensions/docs/manifest.html +++ b/chrome/common/extensions/docs/manifest.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Formats: Manifest Files - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -245,11 +249,15 @@ <a href="#H2-1">Field details</a> <ol> <li> + <a href="#default_locale">default_locale</a> + </li><li> <a href="#description">description</a> </li><li> + <a href="#homepage_url">homepage_url</a> + </li><li> <a href="#icons">icons</a> </li><li> - <a href="#default_locale">default_locale</a> + <a href="#incognito">incognito</a> </li><li> <a href="#key">key</a> </li><li> @@ -260,8 +268,6 @@ <a href="#permissions">permissions</a> </li><li> <a href="#version">version</a> - </li><li> - <a href="#incognito">incognito</a> </li> </ol> </li> @@ -350,13 +356,14 @@ are <b>name</b> and <b>version</b>. "<a href="background_pages.html">background_page</a>": "<em>aFile</em>.html", "<a href="override.html">chrome_url_overrides</a>": {...}, "<a href="content_scripts.html">content_scripts</a>": [...], + "<a href="#homepage_url">homepage_url</a>": "http://<em>path/to/homepage</em>", + "<a href="#incognito">incognito</a>": "spanning" <em>or</em> "split", "<a href="#key">key</a>": "<em>publicKey</em>", "<a href="#minimum_chrome_version">minimum_chrome_version</a>": "<em>versionString</em>", "<a href="options.html">options_page</a>": "<em>aFile</em>.html", "<a href="#permissions">permissions</a>": [...], "<a href="npapi.html">plugins</a>": [...], "<a href="autoupdate.html">update_url</a>": "http://<em>path/to/updateInfo</em>.xml" - "<a href="#incognito">incognito</a>": "<em>split</em> or <em>spanning</em>", } </pre> @@ -370,6 +377,19 @@ with links to where they're described in detail, see the <a href="#overview">Field summary</a>. </p> +<h3 id="default_locale">default_locale</h3> + +<p> +Specifies the subdirectory of <code>_locales</code> +that contains the default strings for this extension. +This field is <b>required</b> in extensions +that have a <code>_locales</code> directory; +it <b>must be absent</b> in extensions +that have no <code>_locales</code> directory. +For details, see +<a href="i18n.html">Internationalization</a>. +</p> + <h3 id="description">description</h3> <p> @@ -384,6 +404,15 @@ You can specify locale-specific strings for this field; see <a href="i18n.html">Internationalization</a> for details. </p> +<h3 id="homepage_url">homepage_url</h3> + +<p> +The URL of the homepage for this extension. The extensions management page (chrome://extensions) +will contain a link to this URL. This field is particularly useful if you +<a href="hosting.html">host the extension on your own site</a>. If you distribute your +extension using the <a href="http://chrome.google.com/extensions">gallery</a>, +the homepage URL defaults to the extension's own gallery page. +</p> <h3 id="icons">icons</h3> @@ -428,8 +457,6 @@ extension developers are using the documented sizes. If you use other sizes, your icon may look bad in future versions of the browser. </p> - - <p> If you submit your extension to the <a href="https://chrome.google.com/extensions">gallery</a>, @@ -441,17 +468,35 @@ see the <a href="http://www.google.com/support/chrome/bin/answer.py?answer=113909">gallery help</a>. </p> -<h3 id="default_locale">default_locale</h3> +<h3 id="incognito">incognito</h3> <p> -Specifies the subdirectory of <code>_locales</code> -that contains the default strings for this extension. -This field is <b>required</b> in extensions -that have a <code>_locales</code> directory; -it <b>must be absent</b> in extensions -that have no <code>_locales</code> directory. -For details, see -<a href="i18n.html">Internationalization</a>. +Either "spanning" or "split", to specify how this extension will +behave if allowed to run in incognito mode. +</p> + +<p> +The default for extensions is "spanning", which means that the extension +will run in a single shared process. Any events or messages from an incognito +tab will be sent to the shared process, with an <em>incognito</em> flag +indicating where it came from. +</p> + +<p> +The default for installable web apps is "split", +which means that all app pages in +an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. +This incognito process runs along side the regular process, but has a separate +memory-only cookie store. Each process sees events and messages only from its +own context (for example, the incognito process will see only incognito tab updates). +The processes are unable to communicate with each other. +</p> + +<p> +As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use +<em>split</em> incognito behavior. If your extension or app needs to be logged +into a remote server or persist settings locally, use <em>spanning</em> +incognito behavior. </p> <h3 id="key">key</h3> @@ -607,6 +652,11 @@ The following table lists the permissions an extension can use. <a href="idle.html">chrome.idle</a> module. </td> </tr> <tr> + <td> "management" </td> + <td> Required if the extension uses the + <a href="management.html">chrome.management</a> module. </td> +</tr> +<tr> <td> "notifications" </td> <td> Allows the extension to use the proposed HTML5 <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">notification API</a> @@ -626,7 +676,16 @@ The following table lists the permissions an extension can use. <td> Provides an unlimited quota for storing HTML5 client-side data, such as databases and local storage files. Without this permission, the extension is limited to - 5 MB of local storage. </td> + 5 MB of local storage. + + <p class="note"> + <b>Note:</b> + This permission applies only to Web SQL Database and application cache + (see issue <a href="http://crbug.com/58985">58985</a>). + Also, it doesn't currently work with wildcard subdomains such as + <code>http://*.example.com</code>. + </p> + </td> </tr> </tbody></table> @@ -680,36 +739,6 @@ For more information, see <a href="autoupdate.html">Autoupdating</a>. </p> -<h3 id="incognito">incognito</h3> - -<p> -Either <em>split</em> or <em>spanning</em>, to specify how this extension will -behave if allowed to run in incognito. -</p> - -<p> -<em>spanning</em> is the default for extensions, and means that the extension -will run in a single shared process. Any events or messages from an incognito -tab will be sent to the shared process, with an <em>incognito</em> flag -indicating where it came from. -</p> - -<p> -<em>split</em> is the default for apps, and it means that all app pages in -an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. -This incognito process runs along side the regular process, but has a separate -memory-only cookie store. Each process sees events and messages only from its -own context (e.g. the incognito process will only see incognito tab updates). -The processes are unable to communicate with each other. -</p> - -<p> -As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use -<em>split</em> incognito behavior. If your extension or app needs to be logged -into a remote server or persist settings locally, use <em>spanning</em> -incognito behavior. -</p> - <!-- [PENDING: Possibly: point to the gallery and make a big deal of the fact that autoupdating is free if you use the gallery.] --> </div> diff --git a/chrome/common/extensions/docs/match_patterns.html b/chrome/common/extensions/docs/match_patterns.html index 5e511b3..9a58c28 100644 --- a/chrome/common/extensions/docs/match_patterns.html +++ b/chrome/common/extensions/docs/match_patterns.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Match Patterns - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/messaging.html b/chrome/common/extensions/docs/messaging.html index 120a03c..343f987 100644 --- a/chrome/common/extensions/docs/messaging.html +++ b/chrome/common/extensions/docs/messaging.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Message Passing - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/notifications.html b/chrome/common/extensions/docs/notifications.html index fd0567a..6732897 100644 --- a/chrome/common/extensions/docs/notifications.html +++ b/chrome/common/extensions/docs/notifications.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Desktop Notifications - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/npapi.html b/chrome/common/extensions/docs/npapi.html index 5190257..c622beb 100644 --- a/chrome/common/extensions/docs/npapi.html +++ b/chrome/common/extensions/docs/npapi.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>NPAPI Plugins - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/options.html b/chrome/common/extensions/docs/options.html index 94dbfcf..9b469ff 100644 --- a/chrome/common/extensions/docs/options.html +++ b/chrome/common/extensions/docs/options.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Options - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/override.html b/chrome/common/extensions/docs/override.html index d1236c9..0701ea0 100644 --- a/chrome/common/extensions/docs/override.html +++ b/chrome/common/extensions/docs/override.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Override - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -339,7 +343,7 @@ An extension can replace any one of the following pages: <li> <b>Bookmark Manager:</b> The page that appears when the user chooses the Bookmark Manager menu item - from the Tools (wrench) menu or, on Mac, + from the wrench menu or, on Mac, the Bookmark Manager item from the Bookmarks menu. You can also get to this page by entering the URL <b>chrome://bookmarks</b>. diff --git a/chrome/common/extensions/docs/overview.html b/chrome/common/extensions/docs/overview.html index 2d9f9a1..714aa26 100644 --- a/chrome/common/extensions/docs/overview.html +++ b/chrome/common/extensions/docs/overview.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Overview - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/packaging.html b/chrome/common/extensions/docs/packaging.html index 8637ec4..6c20134 100644 --- a/chrome/common/extensions/docs/packaging.html +++ b/chrome/common/extensions/docs/packaging.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Packaging - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/pageAction.html b/chrome/common/extensions/docs/pageAction.html index 9bd9388..302279c 100644 --- a/chrome/common/extensions/docs/pageAction.html +++ b/chrome/common/extensions/docs/pageAction.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Page Actions - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/permission_warnings.html b/chrome/common/extensions/docs/permission_warnings.html index ce53446..4363047 100644 --- a/chrome/common/extensions/docs/permission_warnings.html +++ b/chrome/common/extensions/docs/permission_warnings.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Permission Warnings - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,11 @@ <div> </div> </div> +<<<<<<< HEAD + </dl> +======= </dl> +>>>>>>> All communication with the cloud print proxy service from the browser now happens in the CloudPrintProxyService class. Added code to start the service process if the cloud print proxy was enabled. Also, when detect an auto-update, we send an IPC to the service process. The service process then shuts down when the browser disconnects. </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +230,12 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> +<<<<<<< HEAD + <script> + initToggles(); + </script> +======= +>>>>>>> All communication with the cloud print proxy service from the browser now happens in the CloudPrintProxyService class. Added code to start the service process if the cloud print proxy was enabled. Also, when detect an auto-update, we send an IPC to the service process. The service process then shuts down when the browser disconnects. <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/samples.html b/chrome/common/extensions/docs/samples.html index 1593349..21ff76e 100644 --- a/chrome/common/extensions/docs/samples.html +++ b/chrome/common/extensions/docs/samples.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Samples - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -290,7 +294,7 @@ <!-- STATIC CONTENT PLACEHOLDER --> <div id="static"><link rel="stylesheet" href="css/samples.css"> -<script>var search_data = {"0262260daf0c8f7b28feff2ef23b05e7abf9d1e0":"A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON","ea2894c41cb8e80a4433a3e6c5772dadce9be90d":"A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT","ede3c47b7757245be42ec33fd5ca63df4b490066":"A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT","fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a":"ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE","9a6e4ec46997fb92b324974afa08a3d007e2537f":"ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED","9747e3d6a3eab39bc7c17f11a80573c62d44c7e5":"BLANK NEW TAB PAGE CHROME_URL_OVERRIDES","903e7277139e1e6caec123d3319cab295d8d1b3a":"CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED","0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5":"CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL","ac31228200b41a87982e386cc90d3a6eee4ad885":"CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.EXPERIMENTAL.OMNIBOX.STYLEURL CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","7d5d6cf195bc25480256618e360aa38c6e6fba82":"CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED","5d81304a17cf7ac2887484f730fbd2b01e51e166":"CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE","4daa6becd0899a54776d9cf7f09613ed1a9f4d77":"COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL","6871d09f4a96bf9d4b6cc724d00e909cee0f3902":"CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","028eb5364924344029bcbe1d527f132fc72b34e5":"EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE","763a08e9b06595d785568a8d392b95a2f3700258":"EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP","4e35caa9742fb82dbd628892d23a781614f6eff6":"GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca":"GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","1682e05ea9a1bde985123b04f6f8ac50a8a64033":"GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","14b9651fda4e57b2a5914ba73a779812201b750a":"HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP","2020d72f2577f53caf8e94e3dbac0fb849ceaa4d":"IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE","0ea1588bd07b20338fc21f725de1542a5fdf9726":"IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES","646325c25f572a1d15edc73d057f821d847a4fbe":"IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE","ec97ec20ca2f095d081e39f1565fc12af09ef067":"MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST","b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d":"MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT","51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e":"MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST","4f6785ec4f937add6728615682dd37c9a42d9548":"MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE","3aea027164cb9b732ba4a8c51cb93708891726ef":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","597015d3bcce3da693b02314afd607bec4f55291":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","6444e5c8ae112a6a433909c5e770669cd16e2e5f":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","f799e26ceef2367cf836f24bcb47df4398b0df58":"NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE","a88ab12b0241ee3dac6e74bb04da7964fab0f57d":"OMNIBOX EXAMPLE BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED","8d0a50b57c26bb498be592e871001ffed91541b4":"PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW","80b86ccc6e8520660fa591caa565826f0ed1b12c":"PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED","d74c3c18a1c1dd18b035149105a306f837c8823e":"PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT","e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd":"PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE","beff6ecd9677dea0a7c648c5042165b48bb66f09":"PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED","56a8d2ac24ca7bba78fd88ad57f43fc13c784497":"SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","38f6e1e17756ede38b1364c7114a738ca717dcbb":"SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","fc89b35755483af30b66cd72cefa34a43a3e8312":"SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE","230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64":"TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE","e1697cacebad05218798bf3e8a0f724517f0e8c3":"TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED","b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6":"TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE"}</script> +<script>var search_data = {"0262260daf0c8f7b28feff2ef23b05e7abf9d1e0":"A BROWSER ACTION WHICH CHANGES ITS ICON WHEN CLICKED. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON","ea2894c41cb8e80a4433a3e6c5772dadce9be90d":"A BROWSER ACTION WITH A POPUP THAT CHANGES THE PAGE COLOR. BROWSER_ACTION POPUP TABS CHROME.TABS.EXECUTESCRIPT","ede3c47b7757245be42ec33fd5ca63df4b490066":"A BROWSER ACTION WITH NO ICON THAT MAKES THE PAGE RED BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.EXECUTESCRIPT","fbf0aa1a09a15ff8cc4fc7de4fd176d6c663d07a":"ACCEPTLANGUAGE RETURNS ACCEPT LANGUAGES OF THE BROWSER BROWSER_ACTION POPUP CHROME.I18N.GETACCEPTLANGUAGES CHROME.I18N.GETMESSAGE","9a6e4ec46997fb92b324974afa08a3d007e2537f":"ANIMATED PAGE ACTION THIS EXTENSION ADDS AN ANIMATED BROWSER ACTION TO THE TOOLBAR. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.ONCLICKED CHROME.PAGEACTION.SETICON CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED","9747e3d6a3eab39bc7c17f11a80573c62d44c7e5":"BLANK NEW TAB PAGE CHROME_URL_OVERRIDES","903e7277139e1e6caec123d3319cab295d8d1b3a":"CHROME SOUNDS ENJOY A MORE MAGICAL AND IMMERSIVE EXPERIENCE WHEN BROWSING THE WEB USING THE POWER OF SOUND. BACKGROUND_PAGE BOOKMARKS OPTIONS_PAGE TABS CHROME.BOOKMARKS.ONCREATED CHROME.BOOKMARKS.ONMOVED CHROME.BOOKMARKS.ONREMOVED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.TABS.GET CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED","0e790e035a4a00b6f1def5ef9a7d7be1bce95ab5":"CHROMIUM BUILDBOT MONITOR DISPLAYS THE STATUS OF THE CHROMIUM BUILDBOT IN THE TOOLBAR. CLICK TO SEE MORE DETAILED STATUS IN A POPUP. BACKGROUND_PAGE BROWSER_ACTION NOTIFICATIONS OPTIONS_PAGE POPUP CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.GETURL","ac31228200b41a87982e386cc90d3a6eee4ad885":"CHROMIUM SEARCH ADD SUPPORT TO THE OMNIBOX TO SEARCH THE CHROMIUM SOURCE CODE. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.EXPERIMENTAL.OMNIBOX.STYLEURL CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE","7d5d6cf195bc25480256618e360aa38c6e6fba82":"CLD DISPLAYS THE LANGUAGE OF A TAB BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.TABS.DETECTLANGUAGE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED","5d81304a17cf7ac2887484f730fbd2b01e51e166":"CONTEXT MENUS SAMPLE SHOWS SOME OF THE FEATURES OF THE CONTEXT MENUS API BACKGROUND_PAGE CONTEXTMENUS CHROME.CONTEXTMENUS.CREATE","4daa6becd0899a54776d9cf7f09613ed1a9f4d77":"COOKIE API TEST EXTENSION TESTING COOKIE API BACKGROUND_PAGE BROWSER_ACTION COOKIES TABS CHROME.BROWSERACTION.ONCLICKED CHROME.COOKIES.GET CHROME.COOKIES.GETALL CHROME.COOKIES.ONCHANGED CHROME.COOKIES.REMOVE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL","6871d09f4a96bf9d4b6cc724d00e909cee0f3902":"CROSS-DOMAIN XMLHTTPREQUEST FROM A CONTENT SCRIPT DEMONSTRATES A METHOD TO MAKE A CROSS-DOMAIN XMLHTTPREQUEST FETCH FROM A CONTENT SCRIPT. THIS EXTENSION FETCHES THE CURRENT TRENDING TOPICS FROM TWITTER AND INSERTS THEM IN AN OVERLAY AT THE TOP OF GOOGLE NEWS. VISIT HTTP://NEWS.GOOGLE.COM TO TEST THIS EXTENSION. BACKGROUND_PAGE CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","028eb5364924344029bcbe1d527f132fc72b34e5":"EMAIL THIS PAGE (BY GOOGLE) THIS EXTENSION ADDS AN EMAIL BUTTON TO THE TOOLBAR WHICH ALLOWS YOU TO EMAIL THE PAGE LINK USING YOUR DEFAULT MAIL CLIENT OR GMAIL. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.CONNECT CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.UPDATE","763a08e9b06595d785568a8d392b95a2f3700258":"EVENT TRACKING WITH GOOGLE ANALYTICS A SAMPLE EXTENSION WHICH USES GOOGLE ANALYTICS TO TRACK USAGE. BACKGROUND_PAGE BROWSER_ACTION POPUP","e3df888a89e35bdeb9c8bc8d03be5e1851b97c68":"EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE","4e35caa9742fb82dbd628892d23a781614f6eff6":"GOOGLE DOCUMENT LIST VIEWER DEMONSTRATES HOW TO USE OAUTH TO CONNECT THE GOOGLE DOCUMENTS LIST DATA API. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.BROWSERACTION.SETBADGETEXT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","bb57f7a0132cbeb36ad7e7bb0ab75c21704234ca":"GOOGLE MAIL CHECKER DISPLAYS THE NUMBER OF UNREAD MESSAGES IN YOUR GOOGLE MAIL INBOX. YOU CAN ALSO CLICK THE BUTTON TO OPEN YOUR INBOX. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.ONUPDATED CHROME.TABS.UPDATE","1682e05ea9a1bde985123b04f6f8ac50a8a64033":"GOOGLE WAVE NOTIFIER FIND OUT WHEN YOU HAVE NEW WAVES AND PREVIEW THEM FAST. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","14b9651fda4e57b2a5914ba73a779812201b750a":"HELLO WORLD THE FIRST EXTENSION THAT I MADE. BROWSER_ACTION POPUP","2020d72f2577f53caf8e94e3dbac0fb849ceaa4d":"IDLE - SIMPLE EXAMPLE DEMONSTRATES THE IDLE API BACKGROUND_PAGE BROWSER_ACTION IDLE CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.IDLE.ONSTATECHANGED CHROME.IDLE.QUERYSTATE","0ea1588bd07b20338fc21f725de1542a5fdf9726":"IGOOGLE NEW TAB PAGE CHROME_URL_OVERRIDES","646325c25f572a1d15edc73d057f821d847a4fbe":"IMAGEINFO GET IMAGE INFO FOR IMAGES, INCLUDING EXIF DATA BACKGROUND_PAGE CONTEXTMENUS TABS CHROME.CONTEXTMENUS.CREATE CHROME.TABS.GET CHROME.TABS.GETCURRENT CHROME.WINDOWS.CREATE CHROME.WINDOWS.UPDATE","ec97ec20ca2f095d081e39f1565fc12af09ef067":"MAPPY FINDS ADDRESSES IN THE WEB PAGE YOURE ON AND POPS UP A MAP WINDOW. BACKGROUND_PAGE PAGE_ACTION POPUP TABS CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.ONREQUEST CHROME.PAGEACTION.HIDE CHROME.PAGEACTION.SETTITLE CHROME.PAGEACTION.SHOW CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.SENDREQUEST","b2f5f8a790e16f091a7e4e0a39b2d0a6d32e3a6d":"MERGE WINDOWS MERGES ALL OF THE BROWSERS WINDOWS INTO THE CURRENT WINDOW BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.MOVE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT","51a83d2ba3a32e3ff1bdb624d4e18ccec4c4038e":"MESSAGE TIMER TIMES HOW LONG IT TAKES TO SEND A MESSAGE TO A CONTENT SCRIPT AND BACK. BROWSER_ACTION POPUP TABS CHROME.EXTENSION.ONCONNECT CHROME.EXTENSION.ONREQUEST CHROME.TABS.CONNECT CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.SENDREQUEST","4f6785ec4f937add6728615682dd37c9a42d9548":"MY BOOKMARKS A BROWSER ACTION WITH A POPUP DUMP OF ALL BOOKMARKS, INCLUDING SEARCH, ADD, EDIT AND DELETE. BOOKMARKS BROWSER_ACTION POPUP TABS CHROME.BOOKMARKS.CREATE CHROME.BOOKMARKS.GET CHROME.BOOKMARKS.GETTREE CHROME.BOOKMARKS.REMOVE CHROME.BOOKMARKS.UPDATE CHROME.TABS.CREATE","3aea027164cb9b732ba4a8c51cb93708891726ef":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","597015d3bcce3da693b02314afd607bec4f55291":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.TABS.CREATE","6444e5c8ae112a6a433909c5e770669cd16e2e5f":"NEWS READER DISPLAYS THE FIRST 5 ITEMS FROM THE GOOGLE NEWS - TOP NEWS RSS FEED IN A POPUP. BROWSER_ACTION POPUP TABS CHROME.I18N.GETMESSAGE CHROME.TABS.CREATE","f799e26ceef2367cf836f24bcb47df4398b0df58":"NOTIFICATION DEMO SHOWS OFF DESKTOP NOTIFICATIONS, WHICH ARE TOAST WINDOWS THAT POP UP ON THE DESKTOP. BACKGROUND_PAGE NOTIFICATIONS OPTIONS_PAGE TABS CHROME.TABS.CREATE","e787b322bddbc6289bb31b7d7550b1bf6456a80b":"OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED","8d0a50b57c26bb498be592e871001ffed91541b4":"PAGE ACTION BY CONTENT SHOWS A PAGE ACTION FOR HTML PAGES CONTAINING THE WORD SANDWICH BACKGROUND_PAGE PAGE_ACTION CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST CHROME.PAGEACTION.SHOW","80b86ccc6e8520660fa591caa565826f0ed1b12c":"PAGE ACTION BY URL SHOWS A PAGE ACTION FOR URLS WHICH HAVE THE LETTER G IN THEM. BACKGROUND_PAGE PAGE_ACTION TABS CHROME.PAGEACTION.SHOW CHROME.TABS.ONUPDATED","d74c3c18a1c1dd18b035149105a306f837c8823e":"PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT","e6ae17ab4ccfd7e059c8c01f25760ca5d894c7fd":"PRINT THIS PAGE ADDS A PRINT BUTTON TO THE BROWSER. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.TABS.UPDATE","beff6ecd9677dea0a7c648c5042165b48bb66f09":"PROCESS MONITOR ADDS A BROWSER ACTION THAT MONITORS RESOURCE USAGE OF ALL BROWSER PROCESSES. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.ONUPDATED","56a8d2ac24ca7bba78fd88ad57f43fc13c784497":"SAMPLE - OAUTH CONTACTS USES OAUTH TO CONNECT TO GOOGLES CONTACTS SERVICE AND DISPLAY A LIST OF YOUR CONTACTS. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETICON CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE","38f6e1e17756ede38b1364c7114a738ca717dcbb":"SANDWICHBAR SHOWS AN INFOBAR ON PAGES WHICH CONTAIN THE WORD SANDWICH BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.INFOBARS.SHOW CHROME.EXTENSION.ONREQUEST CHROME.EXTENSION.SENDREQUEST","fc89b35755483af30b66cd72cefa34a43a3e8312":"SHOW TABS IN PROCESS ADDS A BROWSER ACTION SHOWING WHICH TABS SHARE THE CURRENT TABS PROCESS. BROWSER_ACTION EXPERIMENTAL POPUP TABS CHROME.EXPERIMENTAL.PROCESSES.GETPROCESSIDFORTAB CHROME.TABS.GET CHROME.TABS.GETSELECTED CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.UPDATE","230463f2d5c3d4d0ca13c230e1f00f2aae0a8a64":"TAB INSPECTOR UTILITY FOR WORKING WITH THE EXTENSION TABS API BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.MOVE CHROME.TABS.ONATTACHED CHROME.TABS.ONCREATED CHROME.TABS.ONDETACHED CHROME.TABS.ONMOVED CHROME.TABS.ONREMOVED CHROME.TABS.ONSELECTIONCHANGED CHROME.TABS.ONUPDATED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.CREATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETALL CHROME.WINDOWS.GETCURRENT CHROME.WINDOWS.GETLASTFOCUSED CHROME.WINDOWS.ONCREATED CHROME.WINDOWS.ONFOCUSCHANGED CHROME.WINDOWS.ONREMOVED CHROME.WINDOWS.REMOVE CHROME.WINDOWS.UPDATE","e1697cacebad05218798bf3e8a0f724517f0e8c3":"TEST SCREENSHOT EXTENSION DEMONSTRATE SCREENSHOT FUNCTIONALITY IN THE CHROME.TABS API. BACKGROUND_PAGE BROWSER_ACTION TABS CHROME.BROWSERACTION.ONCLICKED CHROME.EXTENSION.GETURL CHROME.EXTENSION.GETVIEWS CHROME.TABS.CAPTUREVISIBLETAB CHROME.TABS.CREATE CHROME.TABS.ONUPDATED","b3de91ab04b7d7a2670ca7ee9d740eb42cead0b6":"TYPED URL HISTORY READS YOUR HISTORY, AND SHOWS THE TOP TEN PAGES YOU GO TO BY TYPING THE URL. BROWSER_ACTION HISTORY TABS CHROME.HISTORY.GETVISITS CHROME.HISTORY.SEARCH CHROME.TABS.CREATE"}</script> <script src="js/sample_search.js"></script> @@ -1019,6 +1023,56 @@ </ul> </div> <div><a href="examples/tutorials/analytics.zip">Download .zip</a></div> +</div><div class="sample" id="e3df888a89e35bdeb9c8bc8d03be5e1851b97c68"> + <img class="icon" src="examples/api/omnibox/extension-docs/icon-128.png"> + <img class="icon" src="images/sample-default-icon.png" style="display: none; "> + <h2 class="name"> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/">Extension Docs Search</a> + </h2> + <p class="metadata features">Uses + <span> + <strong>background_page</strong><span>, </span> + <span style="display: none; "> and</span> + </span><span> + <strong>experimental</strong><span style="display: none; ">, </span> + <span> and</span> + </span><span> + <strong>tabs</strong><span style="display: none; ">, </span> + <span style="display: none; "> and</span> + </span> + </p> + <p>Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.</p> + <div class="apicalls"><strong>Calls:</strong> + <ul> + <li> + <code><a href="experimental.omnibox.html#event-onInputChanged">chrome.experimental.omnibox.onInputChanged</a></code> + </li><li> + <code><a href="experimental.omnibox.html#event-onInputEntered">chrome.experimental.omnibox.onInputEntered</a></code> + </li><li> + <code><a href="experimental.omnibox.html#method-styleMatch">chrome.experimental.omnibox.styleMatch</a></code> + </li><li> + <code><a href="experimental.omnibox.html#method-styleNone">chrome.experimental.omnibox.styleNone</a></code> + </li><li> + <code><a href="tabs.html#method-create">chrome.tabs.create</a></code> + </li><li> + <code><a href="tabs.html#method-get">chrome.tabs.get</a></code> + </li><li> + <code><a href="tabs.html#event-onRemoved">chrome.tabs.onRemoved</a></code> + </li><li> + <code><a href="tabs.html#method-update">chrome.tabs.update</a></code> + </li> + </ul> + </div> + <div class="sourcefiles"><strong>Source files:</strong> + <ul> + <li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/background.html?content-type=text/plain">background.html</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/extension-docs/manifest.json?content-type=text/plain">manifest.json</a></code> + </li> + </ul> + </div> + <div><a href="examples/api/omnibox/extension-docs.zip">Download .zip</a></div> </div><div class="sample" id="4e35caa9742fb82dbd628892d23a781614f6eff6"> <img class="icon" src="examples/extensions/gdocs/img/docs_spreadsheets-128.gif"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -1822,11 +1876,11 @@ </ul> </div> <div><a href="examples/api/notifications.zip">Download .zip</a></div> -</div><div class="sample" id="a88ab12b0241ee3dac6e74bb04da7964fab0f57d"> +</div><div class="sample" id="e787b322bddbc6289bb31b7d7550b1bf6456a80b"> <img class="icon" style="display: none; "> <img class="icon" src="images/sample-default-icon.png"> <h2 class="name"> - <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/">Omnibox Example</a> + <a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/simple-example/">Omnibox Example</a> </h2> <p class="metadata features">Uses <span> @@ -1837,7 +1891,7 @@ <span style="display: none; "> and</span> </span> </p> - <p></p> + <p>To use, type 'omnix' plus a search term into the Omnibox.</p> <div class="apicalls"><strong>Calls:</strong> <ul> <li> @@ -1850,13 +1904,13 @@ <div class="sourcefiles"><strong>Source files:</strong> <ul> <li> - <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/background.html?content-type=text/plain">background.html</a></code> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/simple-example/background.html?content-type=text/plain">background.html</a></code> </li><li> - <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/manifest.json?content-type=text/plain">manifest.json</a></code> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/omnibox/simple-example/manifest.json?content-type=text/plain">manifest.json</a></code> </li> </ul> </div> - <div><a href="examples/api/omnibox.zip">Download .zip</a></div> + <div><a href="examples/api/omnibox/simple-example.zip">Download .zip</a></div> </div><div class="sample" id="8d0a50b57c26bb498be592e871001ffed91541b4"> <img class="icon" src="examples/api/pageAction/pageaction_by_content/sandwich-128.png"> <img class="icon" src="images/sample-default-icon.png" style="display: none; "> @@ -2002,6 +2056,22 @@ <li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/background.html?content-type=text/plain">background.html</a></code> </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-1.4.2.min.js?content-type=text/plain">jquery/jquery-1.4.2.min.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery-ui-1.8.4.custom.min.js?content-type=text/plain">jquery/jquery-ui-1.8.4.custom.min.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.client.js?content-type=text/plain">jquery/jquery.client.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.dashes.js?content-type=text/plain">jquery/jquery.flot.dashes.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.js?content-type=text/plain">jquery/jquery.flot.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.min.js?content-type=text/plain">jquery/jquery.flot.min.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.navigate.js?content-type=text/plain">jquery/jquery.flot.navigate.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jquery/jquery.flot.valuelabels.js?content-type=text/plain">jquery/jquery.flot.valuelabels.js</a></code> + </li><li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jst/jsevalcontext.js?content-type=text/plain">jst/jsevalcontext.js</a></code> </li><li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/jst/jstemplate.js?content-type=text/plain">jst/jstemplate.js</a></code> @@ -2015,6 +2085,10 @@ <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/options.html?content-type=text/plain">options.html</a></code> </li><li> <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/script.js?content-type=text/plain">script.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/util/sorttable.js?content-type=text/plain">util/sorttable.js</a></code> + </li><li> + <code><a target="_blank" href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/benchmark/util/table2CSV.js?content-type=text/plain">util/table2CSV.js</a></code> </li> </ul> </div> diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json index 30a3b02..bc67080 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -51,6 +51,7 @@ "chrome.experimental.clipboard.executeCopy": "experimental.clipboard.html#method-executeCopy", "chrome.bookmarks.move": "bookmarks.html#method-move", "chrome.experimental.webRequest.onBeforeRedirect": "experimental.webRequest.html#event-onBeforeRedirect", + "chrome.management.get": "management.html#method-get", "chrome.tabs.getCurrent": "tabs.html#method-getCurrent", "chrome.experimental.sidebar.getState": "experimental.sidebar.html#method-getState", "chrome.management.launchApp": "management.html#method-launchApp", @@ -567,6 +568,37 @@ { "features": [ "background_page", + "experimental", + "tabs" + ], + "icon": "icon-128.png", + "description": "Search the Chrome Extensions documentation. To use, type 'crdoc' plus a search term into the Omnibox.", + "search_string": "EXTENSION DOCS SEARCH SEARCH THE CHROME EXTENSIONS DOCUMENTATION. TO USE, TYPE CRDOC PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL TABS CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED CHROME.EXPERIMENTAL.OMNIBOX.STYLEMATCH CHROME.EXPERIMENTAL.OMNIBOX.STYLENONE CHROME.TABS.CREATE CHROME.TABS.GET CHROME.TABS.ONREMOVED CHROME.TABS.UPDATE", + "source_files": [ + "background.html", + "manifest.json" + ], + "zip_path": "examples/api/omnibox/extension-docs.zip", + "path": "examples/api/omnibox/extension-docs/", + "api_calls": [ + "chrome.experimental.omnibox.onInputChanged", + "chrome.experimental.omnibox.onInputEntered", + "chrome.experimental.omnibox.styleMatch", + "chrome.experimental.omnibox.styleNone", + "chrome.tabs.create", + "chrome.tabs.get", + "chrome.tabs.onRemoved", + "chrome.tabs.update" + ], + "id": "e3df888a89e35bdeb9c8bc8d03be5e1851b97c68", + "protocols": [ + "http://" + ], + "name": "Extension Docs Search" + }, + { + "features": [ + "background_page", "browser_action", "options_page", "popup", @@ -1050,19 +1082,19 @@ "experimental" ], "icon": null, - "description": "", - "search_string": "OMNIBOX EXAMPLE BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED", + "description": "To use, type 'omnix' plus a search term into the Omnibox.", + "search_string": "OMNIBOX EXAMPLE TO USE, TYPE OMNIX PLUS A SEARCH TERM INTO THE OMNIBOX. BACKGROUND_PAGE EXPERIMENTAL CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTCHANGED CHROME.EXPERIMENTAL.OMNIBOX.ONINPUTENTERED", "source_files": [ "background.html", "manifest.json" ], - "zip_path": "examples/api/omnibox.zip", - "path": "examples/api/omnibox/", + "zip_path": "examples/api/omnibox/simple-example.zip", + "path": "examples/api/omnibox/simple-example/", "api_calls": [ "chrome.experimental.omnibox.onInputChanged", "chrome.experimental.omnibox.onInputEntered" ], - "id": "a88ab12b0241ee3dac6e74bb04da7964fab0f57d", + "id": "e787b322bddbc6289bb31b7d7550b1bf6456a80b", "protocols": [], "name": "Omnibox Example" }, @@ -1125,13 +1157,23 @@ "search_string": "PAGE BENCHMARKER CHROMIUM PAGE BENCHMARKER. BACKGROUND_PAGE BROWSER_ACTION OPTIONS_PAGE TABS CHROME.BROWSERACTION.ONCLICKED CHROME.BROWSERACTION.SETBADGEBACKGROUNDCOLOR CHROME.BROWSERACTION.SETBADGETEXT CHROME.BROWSERACTION.SETTITLE CHROME.EXTENSION.CONNECT CHROME.EXTENSION.GETBACKGROUNDPAGE CHROME.EXTENSION.GETEXTENSIONTABS CHROME.EXTENSION.GETURL CHROME.EXTENSION.ONCONNECT CHROME.TABS.CREATE CHROME.TABS.EXECUTESCRIPT CHROME.TABS.GET CHROME.TABS.GETALLINWINDOW CHROME.TABS.GETSELECTED CHROME.TABS.REMOVE CHROME.TABS.UPDATE CHROME.WINDOWS.GET CHROME.WINDOWS.GETCURRENT", "source_files": [ "background.html", + "jquery/jquery-1.4.2.min.js", + "jquery/jquery-ui-1.8.4.custom.min.js", + "jquery/jquery.client.js", + "jquery/jquery.flot.dashes.js", + "jquery/jquery.flot.js", + "jquery/jquery.flot.min.js", + "jquery/jquery.flot.navigate.js", + "jquery/jquery.flot.valuelabels.js", "jst/jsevalcontext.js", "jst/jstemplate.js", "jst/jstemplate_test.js", "jst/util.js", "manifest.json", "options.html", - "script.js" + "script.js", + "util/sorttable.js", + "util/table2CSV.js" ], "zip_path": "examples/extensions/benchmark.zip", "path": "examples/extensions/benchmark/", @@ -1409,4 +1451,4 @@ "name": "Typed URL History" } ] -}
\ No newline at end of file +} diff --git a/chrome/common/extensions/docs/static/api_other.html b/chrome/common/extensions/docs/static/api_other.html index 95bf464..e9ba86f 100644 --- a/chrome/common/extensions/docs/static/api_other.html +++ b/chrome/common/extensions/docs/static/api_other.html @@ -36,7 +36,7 @@ E.g. window.open(someUrl). --></dd> <ul> <li> audio (<a href="http://www.html5rocks.com/tutorials/audio/quick/">tutorial</a>) </li> - <li> app cache + <li> application cache (<a href="http://www.html5rocks.com/tutorials/appcache/beginner/">tutorial</a>) </li> <li> canvas </li> <li> geolocation diff --git a/chrome/common/extensions/docs/static/browserAction.html b/chrome/common/extensions/docs/static/browserAction.html index 224c247..2a35b5d 100644 --- a/chrome/common/extensions/docs/static/browserAction.html +++ b/chrome/common/extensions/docs/static/browserAction.html @@ -148,7 +148,7 @@ in the <a href="#manifest">manifest</a>, or call the Browser action icons should seem a little bigger and heavier than page action icons. <li><b>Don't</b> attempt to mimic - Google Chrome's monochrome "wrench" and "page" icons. + Google Chrome's monochrome "wrench" icon. That doesn't work well with themes, and anyway, extensions should stand out a little. <li><b>Do</b> use alpha transparency diff --git a/chrome/common/extensions/docs/static/content_scripts.html b/chrome/common/extensions/docs/static/content_scripts.html index c11a247..9cbf0c3 100644 --- a/chrome/common/extensions/docs/static/content_scripts.html +++ b/chrome/common/extensions/docs/static/content_scripts.html @@ -173,7 +173,7 @@ can have the following properties:</p> </tr> </table> -<h3 id="include-exclude-globs">Include and exclude globs</h2> +<h3 id="include-exclude-globs">Include and exclude globs</h3> <p> The content script will be injected into a page if its URL matches any <code>matches</code> pattern and any <code>include_globs</code> pattern, as long as the URL doesn't also match an <code>exclude_globs</code> pattern. Because the <code>matches</code> property is required, <code>include_globs</code> and <code>exclude_globs</code> can only be used to limit which pages will be affected. diff --git a/chrome/common/extensions/docs/static/crx.html b/chrome/common/extensions/docs/static/crx.html index 69f5a3d..422148b 100644 --- a/chrome/common/extensions/docs/static/crx.html +++ b/chrome/common/extensions/docs/static/crx.html @@ -6,7 +6,7 @@ CRX files are ZIP files with a special header and the <code>.crx</code> file extension. </p> -<h2>Package header</h3> +<h2>Package header</h2> <p> The header contains the author's public key and the extension's signature. @@ -63,7 +63,7 @@ the <code>.crx</code> header in order: </tr> </table> -<h2>Extension contents</h3> +<h2>Extension contents</h2> <p> The extension's ZIP file is appended to the <code>*.crx</code> package after the @@ -71,7 +71,7 @@ header. This should be the same ZIP file that the signature in the header was generated from. </p> -<h2>Example</h3> +<h2>Example</h2> <p> The following is an example hex dump from the beginning of a <code>.crx</code> diff --git a/chrome/common/extensions/docs/static/faq.html b/chrome/common/extensions/docs/static/faq.html index 0c94d9d..e9c6908 100644 --- a/chrome/common/extensions/docs/static/faq.html +++ b/chrome/common/extensions/docs/static/faq.html @@ -85,12 +85,22 @@ try the <p> As long as you are using a version of Google Chrome that supports extensions, you already have everything you need to start writing an - extension of your own. Select <b>Extensions</b> from the Tools menu - <img style="vertical-align: middle; margin:0; padding:0" src="images/toolsmenu.gif" /> (or from - the Window menu on Mac) and click <b>Developer Mode</b>. From there, you can load - an unpacked directory of files as if it were a packaged extension, reload - extensions, and more. For a complete tutorial, please view - <a href="http://code.google.com/chrome/extensions/getstarted.html">this getting started guide</a>. + extension of your own. + You can start by turning on Developer mode. + </p> + + <p> + Click the wrench icon + <img src="images/toolsmenu.gif" height="29" width="29" alt="" + class="nomargin" /> + and select <b>Extensions</b> from the <b>Tools</b> menu + (or from the <b>Window</b> menu on Mac). + If there's a "+" next to "Developer mode", + click the "+" so it turns into a "-". + Now you can reload extensions, + load an unpacked directory of files as if it were a packaged extension, + and more. For a complete tutorial, see + <a href="http://code.google.com/chrome/extensions/getstarted.html">Getting Started</a>. </p> <h3 id="faq-dev-02">Can I make cross-domain Ajax requests in an extension?</h3> @@ -388,4 +398,5 @@ win,stable,#.#.###.#,#.#.###.#</pre> to the ticket you starred or opened. This will make it easier for others with the same request to find the correct ticket. </li> -</ol>
\ No newline at end of file +</ol> + diff --git a/chrome/common/extensions/docs/static/getstarted.html b/chrome/common/extensions/docs/static/getstarted.html index 497652c..e1b907e 100644 --- a/chrome/common/extensions/docs/static/getstarted.html +++ b/chrome/common/extensions/docs/static/getstarted.html @@ -59,15 +59,13 @@ to the toolbar of Google Chrome. </li> <li id="load-ext"> Load the extension. <ol type="a"> - <li> + <li style="margin-top:0" /> Bring up the extensions management page - by clicking the Tools menu - <img src="images/toolsmenu.gif" width="43" height="34" alt="" - align="absmiddle" style="margin:0; padding:0"> - (or the Window menu on Mac) - and choosing <b>Extensions</b>. - (If you're using version 6 or later, - choose <b>Tools > Extensions</b>.) + by clicking the wrench icon + <img src="images/toolsmenu.gif" width="29" height="29" alt="" + style="margin-top:0" /> + and choosing <b>Tools > Extensions</b>. + (On Mac, use <b>Window > Extensions</b>.) </li> <li> diff --git a/chrome/common/extensions/docs/static/i18n.html b/chrome/common/extensions/docs/static/i18n.html index 207ab3e..c56abc6 100644 --- a/chrome/common/extensions/docs/static/i18n.html +++ b/chrome/common/extensions/docs/static/i18n.html @@ -462,7 +462,7 @@ Here's how to change the locale using the UI on Google Chrome for Windows: </p> <ol> - <li> Tools menu (wrench) > <b>Options</b> </li> + <li> Wrench icon > <b>Options</b> </li> <li> Choose the <b>Under the Hood</b> tab </li> <li> Scroll down to <b>Web Content</b> </li> <li> Click <b>Change font and language settings</b> </li> diff --git a/chrome/common/extensions/docs/static/management.html b/chrome/common/extensions/docs/static/management.html index 11bdc96..f9ee486 100644 --- a/chrome/common/extensions/docs/static/management.html +++ b/chrome/common/extensions/docs/static/management.html @@ -1,6 +1,23 @@ <div id="pageData-name" class="pageData">Management</div> + <!-- BEGIN AUTHORED CONTENT --> <p id="classSummary"> The <code>chrome.management</code> module provides ways to manage the list of extensions/apps that are installed and running. It is particularly useful for extensions that <a href="override.html">override</a> the built-in New Tab page. </p> + +<h2 id="manifest">Manifest</h2> + +<p>You must declare the "management" permission +in the <a href="manifest.html">extension manifest</a> +to use the management API. +For example:</p> +<pre>{ + "name": "My extension", + ... + <b>"permissions": [ + "management" + ]</b>, + ... +}</pre> + <!-- END AUTHORED CONTENT --> diff --git a/chrome/common/extensions/docs/static/manifest.html b/chrome/common/extensions/docs/static/manifest.html index 5e1758c..bbefe61 100644 --- a/chrome/common/extensions/docs/static/manifest.html +++ b/chrome/common/extensions/docs/static/manifest.html @@ -37,13 +37,14 @@ are <b>name</b> and <b>version</b>. "<a href="background_pages.html">background_page</a>": "<em>aFile</em>.html", "<a href="override.html">chrome_url_overrides</a>": {...}, "<a href="content_scripts.html">content_scripts</a>": [...], + "<a href="#homepage_url">homepage_url</a>": "http://<em>path/to/homepage</em>", + "<a href="#incognito">incognito</a>": "spanning" <em>or</em> "split", "<a href="#key">key</a>": "<em>publicKey</em>", "<a href="#minimum_chrome_version">minimum_chrome_version</a>": "<em>versionString</em>", "<a href="options.html">options_page</a>": "<em>aFile</em>.html", "<a href="#permissions">permissions</a>": [...], "<a href="npapi.html">plugins</a>": [...], "<a href="autoupdate.html">update_url</a>": "http://<em>path/to/updateInfo</em>.xml" - "<a href="#incognito">incognito</a>": "<em>split</em> or <em>spanning</em>", } </pre> @@ -57,6 +58,19 @@ with links to where they're described in detail, see the <a href="#overview">Field summary</a>. </p> +<h3 id="default_locale">default_locale</h3> + +<p> +Specifies the subdirectory of <code>_locales</code> +that contains the default strings for this extension. +This field is <b>required</b> in extensions +that have a <code>_locales</code> directory; +it <b>must be absent</b> in extensions +that have no <code>_locales</code> directory. +For details, see +<a href="i18n.html">Internationalization</a>. +</p> + <h3 id="description">description</h3> <p> @@ -71,6 +85,15 @@ You can specify locale-specific strings for this field; see <a href="i18n.html">Internationalization</a> for details. </p> +<h3 id="homepage_url">homepage_url</h3> + +<p> +The URL of the homepage for this extension. The extensions management page (chrome://extensions) +will contain a link to this URL. This field is particularly useful if you +<a href="hosting.html">host the extension on your own site</a>. If you distribute your +extension using the <a href="http://chrome.google.com/extensions">gallery</a>, +the homepage URL defaults to the extension's own gallery page. +</p> <h3 id="icons">icons</h3> @@ -116,8 +139,6 @@ extension developers are using the documented sizes. If you use other sizes, your icon may look bad in future versions of the browser. </p> - - <p> If you submit your extension to the <a href="https://chrome.google.com/extensions">gallery</a>, @@ -129,17 +150,35 @@ see the <a href="http://www.google.com/support/chrome/bin/answer.py?answer=113909">gallery help</a>. </p> -<h3 id="default_locale">default_locale</h3> +<h3 id="incognito">incognito</h3> <p> -Specifies the subdirectory of <code>_locales</code> -that contains the default strings for this extension. -This field is <b>required</b> in extensions -that have a <code>_locales</code> directory; -it <b>must be absent</b> in extensions -that have no <code>_locales</code> directory. -For details, see -<a href="i18n.html">Internationalization</a>. +Either "spanning" or "split", to specify how this extension will +behave if allowed to run in incognito mode. +</p> + +<p> +The default for extensions is "spanning", which means that the extension +will run in a single shared process. Any events or messages from an incognito +tab will be sent to the shared process, with an <em>incognito</em> flag +indicating where it came from. +</p> + +<p> +The default for installable web apps is "split", +which means that all app pages in +an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. +This incognito process runs along side the regular process, but has a separate +memory-only cookie store. Each process sees events and messages only from its +own context (for example, the incognito process will see only incognito tab updates). +The processes are unable to communicate with each other. +</p> + +<p> +As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use +<em>split</em> incognito behavior. If your extension or app needs to be logged +into a remote server or persist settings locally, use <em>spanning</em> +incognito behavior. </p> <h3 id="key">key</h3> @@ -296,6 +335,11 @@ The following table lists the permissions an extension can use. <a href="idle.html">chrome.idle</a> module. </td> </tr> <tr> + <td> "management" </td> + <td> Required if the extension uses the + <a href="management.html">chrome.management</a> module. </td> +</tr> +<tr> <td> "notifications" </td> <td> Allows the extension to use the proposed HTML5 <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">notification API</a> @@ -315,7 +359,16 @@ The following table lists the permissions an extension can use. <td> Provides an unlimited quota for storing HTML5 client-side data, such as databases and local storage files. Without this permission, the extension is limited to - 5 MB of local storage. </td> + 5 MB of local storage. + + <p class="note"> + <b>Note:</b> + This permission applies only to Web SQL Database and application cache + (see issue <a href="http://crbug.com/58985">58985</a>). + Also, it doesn't currently work with wildcard subdomains such as + <code>http://*.example.com</code>. + </p> + </td> </tr> </table> @@ -369,34 +422,4 @@ For more information, see <a href="autoupdate.html">Autoupdating</a>. </p> -<h3 id="incognito">incognito</h3> - -<p> -Either <em>split</em> or <em>spanning</em>, to specify how this extension will -behave if allowed to run in incognito. -</p> - -<p> -<em>spanning</em> is the default for extensions, and means that the extension -will run in a single shared process. Any events or messages from an incognito -tab will be sent to the shared process, with an <em>incognito</em> flag -indicating where it came from. -</p> - -<p> -<em>split</em> is the default for apps, and it means that all app pages in -an incognito window will run in their own incognito process. If the app or extension contains a background page, that will also run in the incognito process. -This incognito process runs along side the regular process, but has a separate -memory-only cookie store. Each process sees events and messages only from its -own context (e.g. the incognito process will only see incognito tab updates). -The processes are unable to communicate with each other. -</p> - -<p> -As a rule of thumb, if your extension or app needs to load a tab in an incognito browser, use -<em>split</em> incognito behavior. If your extension or app needs to be logged -into a remote server or persist settings locally, use <em>spanning</em> -incognito behavior. -</p> - <!-- [PENDING: Possibly: point to the gallery and make a big deal of the fact that autoupdating is free if you use the gallery.] --> diff --git a/chrome/common/extensions/docs/static/override.html b/chrome/common/extensions/docs/static/override.html index a04b5de..6a4848d 100644 --- a/chrome/common/extensions/docs/static/override.html +++ b/chrome/common/extensions/docs/static/override.html @@ -34,7 +34,7 @@ An extension can replace any one of the following pages: <li> <b>Bookmark Manager:</b> The page that appears when the user chooses the Bookmark Manager menu item - from the Tools (wrench) menu or, on Mac, + from the wrench menu or, on Mac, the Bookmark Manager item from the Bookmarks menu. You can also get to this page by entering the URL <b>chrome://bookmarks</b>. diff --git a/chrome/common/extensions/docs/tabs.html b/chrome/common/extensions/docs/tabs.html index 4660e79..9aa03b4 100644 --- a/chrome/common/extensions/docs/tabs.html +++ b/chrome/common/extensions/docs/tabs.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tabs - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> @@ -1039,7 +1043,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>A port that can be used to communicate with the content scripts running in the specified tab.</dd> + <dd>A port that can be used to communicate with the content scripts running in the specified tab. The port's <a href="extension.html#type-Port">onDisconnect</a> event is fired if the tab closes or does not exist. </dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1398,6 +1402,64 @@ For other examples and for help in viewing the source code, see </dd> </div> + </div><div> + <div> + <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the tab should be pinned. Defaults to <var>false</var></dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> </div> </dl> </dd> @@ -4342,7 +4404,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The JSON response object sent by the handler of the request.</dd> + <dd>The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4370,7 +4432,7 @@ For other examples and for help in viewing the source code, see </div> </div> - </dl> + </dl> </div> </dd> @@ -4434,7 +4496,7 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The JSON response object sent by the handler of the request.</dd> + <dd>The JSON response object sent by the handler of the request. If an error occurs while connecting to the specified tab, the callback will be called with no arguments and <a href="extension.html#property-lastError">chrome.extension.lastError</a> will be set to the error message.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4619,7 +4681,7 @@ For other examples and for help in viewing the source code, see <span style="display: none; "> array of <span><span></span></span> </span> - <span>string</span> + <span>undefined</span> <span style="display: none; "></span> </span> </span> @@ -4628,12 +4690,10 @@ For other examples and for help in viewing the source code, see </em> </dt> - <dd class="todo"> + <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd style="display: none; "> - Description of this parameter from the json schema. - </dd> + <dd>A URL to navigate the tab to.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -4688,12 +4748,68 @@ For other examples and for help in viewing the source code, see </em> </dt> - <dd class="todo"> + <dd class="todo" style="display: none; "> Undocumented. </dd> + <dd>Whether the tab should be selected.</dd> <dd style="display: none; "> - Description of this parameter from the json schema. + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the tab should be pinned.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -6333,7 +6449,7 @@ For other examples and for help in viewing the source code, see <!-- TYPE --> <div style="display:inline"> ( - <span class="optional" style="display: none; ">optional</span> + <span class="optional">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -6413,7 +6529,65 @@ For other examples and for help in viewing the source code, see <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Only specified if the tab's URL changed.</dd> + <dd>The tab's URL if it has changed.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The tab's new pinned state.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -6808,6 +6982,64 @@ For other examples and for help in viewing the source code, see </div><div> <div> <dt> + <var>pinned</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>boolean</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Whether the tab is pinned.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> + <dt> <var>url</var> <em> diff --git a/chrome/common/extensions/docs/template/api_template.html b/chrome/common/extensions/docs/template/api_template.html index 75f089d..dd4f074 100644 --- a/chrome/common/extensions/docs/template/api_template.html +++ b/chrome/common/extensions/docs/template/api_template.html @@ -64,7 +64,7 @@ </dd> <!-- FUNCTION PARAMETERS --> - <dd jsdisplay="isFunction($this) && $this.parameters && + <dd jsdisplay="isFunction($this) && $this.parameters && $this.name != 'callback'"> <div transclude="functionParametersTemplate"></div> </dd> @@ -78,7 +78,7 @@ <div transclude="valueTemplate"> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -212,6 +212,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/template/page_shell.html b/chrome/common/extensions/docs/template/page_shell.html index 9980469..fdca7c6 100644 --- a/chrome/common/extensions/docs/template/page_shell.html +++ b/chrome/common/extensions/docs/template/page_shell.html @@ -19,6 +19,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> </head> <body> </body> diff --git a/chrome/common/extensions/docs/themes.html b/chrome/common/extensions/docs/themes.html index 0f4912d..9c46460 100644 --- a/chrome/common/extensions/docs/themes.html +++ b/chrome/common/extensions/docs/themes.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Themes - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tut_analytics.html b/chrome/common/extensions/docs/tut_analytics.html index 6f6dafd..4a09e0e 100644 --- a/chrome/common/extensions/docs/tut_analytics.html +++ b/chrome/common/extensions/docs/tut_analytics.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: Google Analytics - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tut_debugging.html b/chrome/common/extensions/docs/tut_debugging.html index 9d62494..dad2b44 100644 --- a/chrome/common/extensions/docs/tut_debugging.html +++ b/chrome/common/extensions/docs/tut_debugging.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: Debugging - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tut_oauth.html b/chrome/common/extensions/docs/tut_oauth.html index bab5072..a7b4cb5 100644 --- a/chrome/common/extensions/docs/tut_oauth.html +++ b/chrome/common/extensions/docs/tut_oauth.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorial: OAuth - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/tutorials.html b/chrome/common/extensions/docs/tutorials.html index c7bc92d..4d270ac 100644 --- a/chrome/common/extensions/docs/tutorials.html +++ b/chrome/common/extensions/docs/tutorials.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Tutorials - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/whats_new.html b/chrome/common/extensions/docs/whats_new.html index 61192cb..86f79b9 100644 --- a/chrome/common/extensions/docs/whats_new.html +++ b/chrome/common/extensions/docs/whats_new.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>What's New in Extensions? - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/windows.html b/chrome/common/extensions/docs/windows.html index 97518ea..209fd58 100644 --- a/chrome/common/extensions/docs/windows.html +++ b/chrome/common/extensions/docs/windows.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Windows - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/docs/xhr.html b/chrome/common/extensions/docs/xhr.html index 5c0176f..8987f40 100644 --- a/chrome/common/extensions/docs/xhr.html +++ b/chrome/common/extensions/docs/xhr.html @@ -15,6 +15,7 @@ </script> <script type="text/javascript" src="js/api_page_generator.js"></script> <script type="text/javascript" src="js/bootstrap.js"></script> + <script type="text/javascript" src="js/sidebar.js"></script> <title>Cross-Origin XMLHttpRequest - Google Chrome Extensions - Google Code</title></head> <body> <div id="gc-container" class="labs"> <div id="devModeWarning"> @@ -94,7 +95,7 @@ <div> </div> </div> - </dl> + </dl> </div> </div> <!-- /SUBTEMPLATES --> @@ -225,6 +226,9 @@ <li><h2><a href="samples.html">Samples</a></h2></li> </ul> </div> + <script> + initToggles(); + </script> <div class="g-unit" id="gc-pagecontent"> <div id="pageTitle"> diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index fbb262f..2bbf99b 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -64,7 +64,7 @@ const int kRSAKeySize = 1024; static void ConvertHexadecimalToIDAlphabet(std::string* id) { for (size_t i = 0; i < id->size(); ++i) { int val; - if (base::HexStringToInt(id->substr(i, 1), &val)) + if (base::HexStringToInt(id->begin() + i, id->begin() + i + 1, &val)) (*id)[i] = val + 'a'; else (*id)[i] = 'a'; @@ -249,45 +249,27 @@ const size_t Extension::kNumHostedAppPermissions = const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage"; // -// Extension::StaticData -// - -Extension::StaticData::StaticData() - : incognito_split_mode(false), - location(INVALID), - converted_from_user_script(false), - is_theme(false), - is_app(false), - launch_container(extension_misc::LAUNCH_TAB), - launch_width(0), - launch_height(0) { -} - -Extension::StaticData::~StaticData() { -} - -// -// Extension::RuntimeData +// Extension // -Extension::RuntimeData::RuntimeData() - : background_page_ready(false), - being_upgraded(false) { -} - -Extension::RuntimeData::~RuntimeData() { +// static +scoped_refptr<Extension> Extension::Create(const FilePath& path, + Location location, + const DictionaryValue& value, + bool require_key, + std::string* error) { + scoped_refptr<Extension> extension = new Extension(path, location); + if (!extension->InitFromValue(value, require_key, error)) + return NULL; + return extension; } -// -// Extension -// - // static int Extension::GetPermissionMessageId(const std::string& permission) { return ExtensionConfig::GetSingleton()->GetPermissionMessageId(permission); } -std::vector<string16> Extension::GetPermissionMessages() { +std::vector<string16> Extension::GetPermissionMessages() const { std::vector<string16> messages; if (!plugins().empty()) { messages.push_back( @@ -305,7 +287,7 @@ std::vector<string16> Extension::GetPermissionMessages() { return messages; } -std::set<string16> Extension::GetSimplePermissionMessages() { +std::set<string16> Extension::GetSimplePermissionMessages() const { std::set<string16> messages; std::set<std::string>::const_iterator i; for (i = api_permissions().begin(); i != api_permissions().end(); ++i) { @@ -316,7 +298,7 @@ std::set<string16> Extension::GetSimplePermissionMessages() { return messages; } -std::vector<std::string> Extension::GetDistinctHosts() { +std::vector<std::string> Extension::GetDistinctHosts() const { return GetDistinctHosts(GetEffectiveHostPermissions().patterns()); } @@ -347,7 +329,7 @@ std::vector<std::string> Extension::GetDistinctHosts( return distinct_hosts; } -string16 Extension::GetHostPermissionMessage() { +string16 Extension::GetHostPermissionMessage() const { if (HasEffectiveAccessToAllHosts()) return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT2_WARNING_ALL_HOSTS); @@ -401,9 +383,6 @@ bool Extension::IsHostedAppPermission(const std::string& str) { return false; } -Extension::~Extension() { -} - const std::string Extension::VersionString() const { return version()->GetString(); } @@ -438,7 +417,7 @@ std::string Extension::GenerateIdForPath(const FilePath& path) { return id; } -Extension::HistogramType Extension::GetHistogramType() { +Extension::HistogramType Extension::GetHistogramType() const { if (is_theme()) return TYPE_THEME; if (converted_from_user_script()) @@ -534,12 +513,11 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, return false; } - URLPattern pattern; + URLPattern pattern(UserScript::kValidUserScriptSchemes); if (CanExecuteScriptEverywhere()) - pattern = URLPattern(URLPattern::SCHEME_ALL); - else - pattern = URLPattern(UserScript::kValidUserScriptSchemes); - if (!pattern.Parse(match_str)) { + pattern.set_valid_schemes(URLPattern::SCHEME_ALL); + + if (URLPattern::PARSE_SUCCESS != pattern.Parse(match_str)) { *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatch, base::IntToString(definition_index), base::IntToString(j)); return false; @@ -774,7 +752,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( return result.release(); } -bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) { +bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) const { for (DictionaryValue::key_iterator key = source.begin_keys(); key != source.end_keys(); ++key) { if (!IsBaseCrxKey(*key) && *key != keys::kTheme) @@ -786,7 +764,7 @@ bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) { bool Extension::LoadIsApp(const DictionaryValue* manifest, std::string* error) { if (manifest->HasKey(keys::kApp)) - mutable_static_data_->is_app = true; + is_app_ = true; return true; } @@ -816,7 +794,12 @@ bool Extension::LoadExtent(const DictionaryValue* manifest, } URLPattern pattern(kValidWebExtentSchemes); - if (!pattern.Parse(pattern_string)) { + URLPattern::ParseResult result = pattern.Parse(pattern_string); + if (result == URLPattern::PARSE_ERROR_EMPTY_PATH) { + pattern_string += "/"; + result = pattern.Parse(pattern_string); + } + if (URLPattern::PARSE_SUCCESS != result) { *error = ExtensionErrorUtils::FormatErrorMessage(value_error, base::UintToString(i)); return false; @@ -869,7 +852,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, return false; } - mutable_static_data_->launch_local_path = launch_path; + launch_local_path_ = launch_path; } else if (manifest->Get(keys::kLaunchWebURL, &temp)) { std::string launch_url; if (!temp->GetAsString(&launch_url)) { @@ -883,7 +866,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, return false; } - mutable_static_data_->launch_web_url = launch_url; + launch_web_url_ = launch_url; } else if (is_app()) { *error = errors::kLaunchURLRequired; return false; @@ -899,7 +882,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, } pattern.set_host(launch_url.host()); pattern.set_path("/*"); - mutable_static_data_->extent.AddPattern(pattern); + extent_.AddPattern(pattern); } // In order for the --apps-gallery-url switch to work with the gallery @@ -909,12 +892,12 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, GURL gallery_url(CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kAppsGalleryURL)); if (gallery_url.is_valid()) { - mutable_static_data_->launch_web_url = gallery_url.spec(); + launch_web_url_ = gallery_url.spec(); URLPattern pattern(URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS); pattern.Parse(gallery_url.spec()); pattern.set_path(pattern.path() + '*'); - mutable_static_data_->extent.AddPattern(pattern); + extent_.AddPattern(pattern); } } @@ -934,9 +917,9 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, } if (launch_container_string == values::kLaunchContainerPanel) { - mutable_static_data_->launch_container = extension_misc::LAUNCH_PANEL; + launch_container_ = extension_misc::LAUNCH_PANEL; } else if (launch_container_string == values::kLaunchContainerTab) { - mutable_static_data_->launch_container = extension_misc::LAUNCH_TAB; + launch_container_ = extension_misc::LAUNCH_TAB; } else { *error = errors::kInvalidLaunchContainer; return false; @@ -949,9 +932,9 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, *error = errors::kInvalidLaunchWidthContainer; return false; } - if (!temp->GetAsInteger(&mutable_static_data_->launch_width) || - mutable_static_data_->launch_width < 0) { - mutable_static_data_->launch_width = 0; + if (!temp->GetAsInteger(&launch_width_) || + launch_width_ < 0) { + launch_width_ = 0; *error = errors::kInvalidLaunchWidth; return false; } @@ -964,9 +947,8 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, *error = errors::kInvalidLaunchHeightContainer; return false; } - if (!temp->GetAsInteger(&mutable_static_data_->launch_height) || - mutable_static_data_->launch_height < 0) { - mutable_static_data_->launch_height = 0; + if (!temp->GetAsInteger(&launch_height_) || launch_height_ < 0) { + launch_height_ = 0; *error = errors::kInvalidLaunchHeight; return false; } @@ -994,18 +976,22 @@ bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, return true; } -Extension::Extension(const FilePath& path) - : mutable_static_data_(new StaticData), - runtime_data_(new RuntimeData) { +Extension::Extension(const FilePath& path, Location location) + : incognito_split_mode_(false), + location_(location), + converted_from_user_script_(false), + is_theme_(false), + is_app_(false), + launch_container_(extension_misc::LAUNCH_TAB), + launch_width_(0), + launch_height_(0) { DCHECK(path.IsAbsolute()); - - static_data_ = mutable_static_data_; - mutable_static_data_->location = INVALID; - - mutable_static_data_->path = MaybeNormalizePath(path); + path_ = MaybeNormalizePath(path); } - -ExtensionResource Extension::GetResource(const std::string& relative_path) { +Extension::~Extension() { +} +ExtensionResource Extension::GetResource( + const std::string& relative_path) const { #if defined(OS_POSIX) FilePath relative_file_path(relative_path); #elif defined(OS_WIN) @@ -1014,7 +1000,8 @@ ExtensionResource Extension::GetResource(const std::string& relative_path) { return ExtensionResource(id(), path(), relative_file_path); } -ExtensionResource Extension::GetResource(const FilePath& relative_file_path) { +ExtensionResource Extension::GetResource( + const FilePath& relative_file_path) const { return ExtensionResource(id(), path(), relative_file_path); } @@ -1094,8 +1081,8 @@ bool Extension::FormatPEMForFileOutput(const std::string input, // extensions that require less permissions than the current version, but then // we don't silently allow them to go back. In order to fix this, we would need // to remember the max set of permissions we ever granted a single extension. -bool Extension::IsPrivilegeIncrease(Extension* old_extension, - Extension* new_extension) { +bool Extension::IsPrivilegeIncrease(const Extension* old_extension, + const Extension* new_extension) { // If the old extension had native code access, we don't need to go any // further. Things can't get any worse. if (old_extension->plugins().size() > 0) @@ -1142,7 +1129,7 @@ bool Extension::IsPrivilegeIncrease(Extension* old_extension, } // static -void Extension::DecodeIcon(Extension* extension, +void Extension::DecodeIcon(const Extension* extension, Icons icon_size, scoped_ptr<SkBitmap>* result) { FilePath icon_path = extension->GetIconResource( @@ -1195,17 +1182,13 @@ GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, std::string* error) { - // Unit tests reuse Extension objects, so we need to reset mutable_static_data - // when we re-initialize. - mutable_static_data_ = const_cast<StaticData*>(static_data_.get()); - if (source.HasKey(keys::kPublicKey)) { std::string public_key_bytes; if (!source.GetString(keys::kPublicKey, - &mutable_static_data_->public_key) || - !ParsePEMKeyBytes(mutable_static_data_->public_key, + &public_key_) || + !ParsePEMKeyBytes(public_key_, &public_key_bytes) || - !GenerateId(public_key_bytes, &mutable_static_data_->id)) { + !GenerateId(public_key_bytes, &id_)) { *error = errors::kInvalidKey; return false; } @@ -1216,19 +1199,19 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // If there is a path, we generate the ID from it. This is useful for // development mode, because it keeps the ID stable across restarts and // reloading the extension. - mutable_static_data_->id = Extension::GenerateIdForPath(path()); - if (mutable_static_data_->id.empty()) { + id_ = Extension::GenerateIdForPath(path()); + if (id_.empty()) { NOTREACHED() << "Could not create ID from path."; return false; } } // Make a copy of the manifest so we can store it in prefs. - mutable_static_data_->manifest_value.reset( + manifest_value_.reset( static_cast<DictionaryValue*>(source.DeepCopy())); // Initialize the URL. - mutable_static_data_->extension_url = + extension_url_ = Extension::GetBaseURLFromExtensionId(id()); // Initialize version. @@ -1237,10 +1220,10 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidVersion; return false; } - mutable_static_data_->version.reset( + version_.reset( Version::GetVersionFromString(version_str)); - if (!mutable_static_data_->version.get() || - mutable_static_data_->version->components().size() > 4) { + if (!version_.get() || + version_->components().size() > 4) { *error = errors::kInvalidVersion; return false; } @@ -1252,17 +1235,33 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } base::i18n::AdjustStringForLocaleDirection(localized_name, &localized_name); - mutable_static_data_->name = UTF16ToUTF8(localized_name); + name_ = UTF16ToUTF8(localized_name); // Initialize description (if present). if (source.HasKey(keys::kDescription)) { if (!source.GetString(keys::kDescription, - &mutable_static_data_->description)) { + &description_)) { *error = errors::kInvalidDescription; return false; } } + // Initialize homepage url (if present). + if (source.HasKey(keys::kHomepageURL)) { + std::string tmp; + if (!source.GetString(keys::kHomepageURL, &tmp)) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidHomepageURL, ""); + return false; + } + homepage_url_ = GURL(tmp); + if (!homepage_url_.is_valid()) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidHomepageURL, tmp); + return false; + } + } + // Initialize update url (if present). if (source.HasKey(keys::kUpdateURL)) { std::string tmp; @@ -1271,9 +1270,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, errors::kInvalidUpdateURL, ""); return false; } - mutable_static_data_->update_url = GURL(tmp); - if (!mutable_static_data_->update_url.is_valid() || - mutable_static_data_->update_url.has_ref()) { + update_url_ = GURL(tmp); + if (!update_url_.is_valid() || + update_url_.has_ref()) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidUpdateURL, tmp); return false; @@ -1321,7 +1320,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // Initialize converted_from_user_script (if present) source.GetBoolean(keys::kConvertedFromUserScript, - &mutable_static_data_->converted_from_user_script); + &converted_from_user_script_); // Initialize icons (if present). if (source.HasKey(keys::kIcons)) { @@ -1350,13 +1349,13 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } - mutable_static_data_->icons.Add(kIconSizes[i], icon_path); + icons_.Add(kIconSizes[i], icon_path); } } } // Initialize themes (if present). - mutable_static_data_->is_theme = false; + is_theme_ = false; if (source.HasKey(keys::kTheme)) { // Themes cannot contain extension keys. if (ContainsNonThemeKeys(source)) { @@ -1369,7 +1368,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidTheme; return false; } - mutable_static_data_->is_theme = true; + is_theme_ = true; DictionaryValue* images_value; if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { @@ -1382,7 +1381,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } } - mutable_static_data_->theme_images.reset( + theme_images_.reset( static_cast<DictionaryValue*>(images_value->DeepCopy())); } @@ -1411,7 +1410,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } } - mutable_static_data_->theme_colors.reset( + theme_colors_.reset( static_cast<DictionaryValue*>(colors_value->DeepCopy())); } @@ -1432,14 +1431,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } } - mutable_static_data_->theme_tints.reset( + theme_tints_.reset( static_cast<DictionaryValue*>(tints_value->DeepCopy())); } DictionaryValue* display_properties_value; if (theme_value->GetDictionary(keys::kThemeDisplayProperties, &display_properties_value)) { - mutable_static_data_->theme_display_properties.reset( + theme_display_properties_.reset( static_cast<DictionaryValue*>(display_properties_value->DeepCopy())); } @@ -1487,9 +1486,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } } - mutable_static_data_->plugins.push_back(PluginInfo()); - mutable_static_data_->plugins.back().path = path().AppendASCII(path_str); - mutable_static_data_->plugins.back().is_public = is_public; + plugins_.push_back(PluginInfo()); + plugins_.back().path = path().AppendASCII(path_str); + plugins_.back().is_public = is_public; } } @@ -1500,7 +1499,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidBackground; return false; } - mutable_static_data_->background_url = GetResourceURL(background_str); + background_url_ = GetResourceURL(background_str); } // Initialize toolstrips. This is deprecated for public use. @@ -1535,7 +1534,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, errors::kInvalidToolstrip, base::IntToString(i)); return false; } - mutable_static_data_->toolstrips.push_back(toolstrip); + toolstrips_.push_back(toolstrip); } } @@ -1559,11 +1558,11 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (!LoadUserScriptHelper(content_script, i, error, &script)) return false; // Failed to parse script context definition. script.set_extension_id(id()); - if (mutable_static_data_->converted_from_user_script) { + if (converted_from_user_script_) { script.set_emulate_greasemonkey(true); script.set_match_all_frames(true); // Greasemonkey matches all frames. } - mutable_static_data_->content_scripts.push_back(script); + content_scripts_.push_back(script); } } @@ -1600,9 +1599,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // If page_action_value is not NULL, then there was a valid page action. if (page_action_value) { - mutable_static_data_->page_action.reset( + page_action_.reset( LoadExtensionActionHelper(page_action_value, error)); - if (!mutable_static_data_->page_action.get()) + if (!page_action_.get()) return false; // Failed to parse page action definition. } @@ -1614,25 +1613,25 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } - mutable_static_data_->browser_action.reset( + browser_action_.reset( LoadExtensionActionHelper(browser_action_value, error)); - if (!mutable_static_data_->browser_action.get()) + if (!browser_action_.get()) return false; // Failed to parse browser action definition. } // Load App settings. - if (!LoadIsApp(mutable_static_data_->manifest_value.get(), error) || - !LoadExtent(mutable_static_data_->manifest_value.get(), keys::kWebURLs, - &mutable_static_data_->extent, + if (!LoadIsApp(manifest_value_.get(), error) || + !LoadExtent(manifest_value_.get(), keys::kWebURLs, + &extent_, errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || - !EnsureNotHybridApp(mutable_static_data_->manifest_value.get(), error) || - !LoadLaunchURL(mutable_static_data_->manifest_value.get(), error) || - !LoadLaunchContainer(mutable_static_data_->manifest_value.get(), error)) { + !EnsureNotHybridApp(manifest_value_.get(), error) || + !LoadLaunchURL(manifest_value_.get(), error) || + !LoadLaunchContainer(manifest_value_.get(), error)) { return false; } // Initialize options page url (optional). - // Funtion LoadIsApp() set mutable_static_data_->is_app above. + // Funtion LoadIsApp() set is_app_ above. if (source.HasKey(keys::kOptionsPage)) { std::string options_str; if (!source.GetString(keys::kOptionsPage, &options_str)) { @@ -1648,7 +1647,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidOptionsPageInHostedApp; return false; } - mutable_static_data_->options_url = options_url; + options_url_ = options_url; } else { GURL absolute(options_str); @@ -1656,8 +1655,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kInvalidOptionsPageExpectUrlInPackage; return false; } - mutable_static_data_->options_url = GetResourceURL(options_str); - if (!mutable_static_data_->options_url.is_valid()) { + options_url_ = GetResourceURL(options_str); + if (!options_url_.is_valid()) { *error = errors::kInvalidOptionsPage; return false; } @@ -1685,7 +1684,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // TODO(asargent) - We want a more general purpose mechanism for this, // and better error messages. (http://crbug.com/54013) if (permission_str == kWebstorePrivatePermission && - mutable_static_data_->location != Extension::COMPONENT) { + location_ != Extension::COMPONENT) { continue; } @@ -1696,13 +1695,13 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (web_extent().is_empty() || location() == Extension::COMPONENT) { // Check if it's a module permission. If so, enable that permission. if (IsAPIPermission(permission_str)) { - mutable_static_data_->api_permissions.insert(permission_str); + api_permissions_.insert(permission_str); continue; } } else { // Hosted apps only get access to a subset of the valid permissions. if (IsHostedAppPermission(permission_str)) { - mutable_static_data_->api_permissions.insert(permission_str); + api_permissions_.insert(permission_str); continue; } } @@ -1713,7 +1712,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, (UserScript::kValidUserScriptSchemes | URLPattern::SCHEME_CHROMEUI) & ~URLPattern::SCHEME_FILE); - if (!pattern.Parse(permission_str)) { + if (URLPattern::PARSE_SUCCESS != pattern.Parse(permission_str)) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidPermission, base::IntToString(i)); return false; @@ -1729,14 +1728,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // match all paths. pattern.set_path("/*"); - mutable_static_data_->host_permissions.push_back(pattern); + host_permissions_.push_back(pattern); } } if (source.HasKey(keys::kDefaultLocale)) { if (!source.GetString(keys::kDefaultLocale, - &mutable_static_data_->default_locale) || - mutable_static_data_->default_locale.empty()) { + &default_locale_) || + default_locale_.empty()) { *error = errors::kInvalidDefaultLocale; return false; } @@ -1767,7 +1766,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } // Replace the entry with a fully qualified chrome-extension:// URL. - mutable_static_data_->chrome_url_overrides[page] = GetResourceURL(val); + chrome_url_overrides_[page] = GetResourceURL(val); } // An extension may override at most one page. @@ -1779,8 +1778,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (source.HasKey(keys::kOmniboxKeyword)) { if (!source.GetString(keys::kOmniboxKeyword, - &mutable_static_data_->omnibox_keyword) || - mutable_static_data_->omnibox_keyword.empty()) { + &omnibox_keyword_) || + omnibox_keyword_.empty()) { *error = errors::kInvalidOmniboxKeyword; return false; } @@ -1801,12 +1800,12 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, *error = errors::kDevToolsExperimental; return false; } - mutable_static_data_->devtools_url = GetResourceURL(devtools_str); + devtools_url_ = GetResourceURL(devtools_str); } // Initialize incognito behavior. Apps default to split mode, extensions // default to spanning. - mutable_static_data_->incognito_split_mode = is_app(); + incognito_split_mode_ = is_app(); if (source.HasKey(keys::kIncognito)) { std::string value; if (!source.GetString(keys::kIncognito, &value)) { @@ -1814,9 +1813,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, return false; } if (value == values::kIncognitoSpanning) { - mutable_static_data_->incognito_split_mode = false; + incognito_split_mode_ = false; } else if (value == values::kIncognitoSplit) { - mutable_static_data_->incognito_split_mode = true; + incognito_split_mode_ = true; } else { *error = errors::kInvalidIncognitoBehavior; return false; @@ -1835,10 +1834,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // it calls InitFromValue, passing it up to the browser process which calls // InitFromValue again. As a result, we need to make sure that nobody // accidentally modifies it. - DCHECK(source.Equals(mutable_static_data_->manifest_value.get())); - - // Ensure we can't modify our static data anymore. - mutable_static_data_ = NULL; + DCHECK(source.Equals(manifest_value_.get())); return true; } @@ -1854,19 +1850,22 @@ std::string Extension::ChromeStoreLaunchURL() { return gallery_prefix; } -GURL Extension::GalleryUrl() const { - if (!update_url().DomainIs("google.com")) +GURL Extension::GetHomepageURL() const { + if (homepage_url_.is_valid()) + return homepage_url_; + + if (update_url()!= GURL(extension_urls::kGalleryUpdateHttpsUrl) && + update_url()!= GURL(extension_urls::kGalleryUpdateHttpUrl)) return GURL(); // TODO(erikkay): This may not be entirely correct with the webstore. // I think it will have a mixture of /extensions/detail and /webstore/detail // URLs. Perhaps they'll handle this nicely with redirects? GURL url(ChromeStoreLaunchURL() + std::string("/detail/") + id()); - return url; } -std::set<FilePath> Extension::GetBrowserImages() { +std::set<FilePath> Extension::GetBrowserImages() const { std::set<FilePath> image_paths; // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| // indicate that we're doing something wrong. @@ -1916,20 +1915,6 @@ GURL Extension::GetFullLaunchURL() const { return GURL(launch_web_url()); } -bool Extension::GetBackgroundPageReady() { - return (GetRuntimeData()->background_page_ready || - background_url().is_empty()); -} - -void Extension::SetBackgroundPageReady() { - DCHECK(!background_url().is_empty()); - GetRuntimeData()->background_page_ready = true; - NotificationService::current()->Notify( - NotificationType::EXTENSION_BACKGROUND_PAGE_READY, - Source<Extension>(this), - NotificationService::NoDetails()); -} - static std::string SizeToString(const gfx::Size& max_size) { return base::IntToString(max_size.width()) + "x" + base::IntToString(max_size.height()); @@ -1949,29 +1934,27 @@ void Extension::SetScriptingWhitelist( void Extension::SetCachedImage(const ExtensionResource& source, const SkBitmap& image, - const gfx::Size& original_size) { + const gfx::Size& original_size) const { DCHECK(source.extension_root() == path()); // The resource must come from // this extension. const FilePath& path = source.relative_path(); gfx::Size actual_size(image.width(), image.height()); if (actual_size == original_size) { - GetRuntimeData()->image_cache_[ - RuntimeData::ImageCacheKey(path, std::string())] = image; + image_cache_[ImageCacheKey(path, std::string())] = image; } else { - GetRuntimeData()->image_cache_[ - RuntimeData::ImageCacheKey(path, SizeToString(actual_size))] = image; + image_cache_[ImageCacheKey(path, SizeToString(actual_size))] = image; } } bool Extension::HasCachedImage(const ExtensionResource& source, - const gfx::Size& max_size) { + const gfx::Size& max_size) const { DCHECK(source.extension_root() == path()); // The resource must come from // this extension. return GetCachedImageImpl(source, max_size) != NULL; } SkBitmap Extension::GetCachedImage(const ExtensionResource& source, - const gfx::Size& max_size) { + const gfx::Size& max_size) const { DCHECK(source.extension_root() == path()); // The resource must come from // this extension. SkBitmap* image = GetCachedImageImpl(source, max_size); @@ -1979,20 +1962,19 @@ SkBitmap Extension::GetCachedImage(const ExtensionResource& source, } SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, - const gfx::Size& max_size) { + const gfx::Size& max_size) const { const FilePath& path = source.relative_path(); // Look for exact size match. - RuntimeData::ImageCache::iterator i = GetRuntimeData()->image_cache_.find( - RuntimeData::ImageCacheKey(path, SizeToString(max_size))); - if (i != GetRuntimeData()->image_cache_.end()) + ImageCache::iterator i = image_cache_.find( + ImageCacheKey(path, SizeToString(max_size))); + if (i != image_cache_.end()) return &(i->second); // If we have the original size version cached, return that if it's small // enough. - i = GetRuntimeData()->image_cache_.find( - RuntimeData::ImageCacheKey(path, std::string())); - if (i != GetRuntimeData()->image_cache_.end()) { + i = image_cache_.find(ImageCacheKey(path, std::string())); + if (i != image_cache_.end()) { SkBitmap& image = i->second; if (image.width() <= max_size.width() && image.height() <= max_size.height()) @@ -2003,14 +1985,15 @@ SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, } ExtensionResource Extension::GetIconResource( - int size, ExtensionIconSet::MatchType match_type) { + int size, ExtensionIconSet::MatchType match_type) const { std::string path = icons().Get(size, match_type); if (path.empty()) return ExtensionResource(); return GetResource(path); } -GURL Extension::GetIconURL(int size, ExtensionIconSet::MatchType match_type) { +GURL Extension::GetIconURL(int size, + ExtensionIconSet::MatchType match_type) const { std::string path = icons().Get(size, match_type); if (path.empty()) return GURL(); @@ -2018,7 +2001,7 @@ GURL Extension::GetIconURL(int size, ExtensionIconSet::MatchType match_type) { return GetResourceURL(path); } -bool Extension::CanSpecifyHostPermission(const URLPattern pattern) const { +bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { if (!pattern.match_all_urls() && pattern.MatchesScheme(chrome::kChromeUIScheme)) { // Only allow access to chrome://favicon to regular extensions. Component @@ -2031,7 +2014,7 @@ bool Extension::CanSpecifyHostPermission(const URLPattern pattern) const { return true; } -// static. +// static bool Extension::HasApiPermission( const std::set<std::string>& api_permissions, const std::string& function_name) { @@ -2077,7 +2060,7 @@ bool Extension::HasHostPermission(const GURL& url) const { void Extension::InitEffectiveHostPermissions() { for (URLPatternList::const_iterator host = host_permissions().begin(); host != host_permissions().end(); ++host) - mutable_static_data_->effective_host_permissions.AddPattern(*host); + effective_host_permissions_.AddPattern(*host); for (UserScriptList::const_iterator content_script = content_scripts().begin(); @@ -2085,7 +2068,7 @@ void Extension::InitEffectiveHostPermissions() { UserScript::PatternList::const_iterator pattern = content_script->url_patterns().begin(); for (; pattern != content_script->url_patterns().end(); ++pattern) - mutable_static_data_->effective_host_permissions.AddPattern(*pattern); + effective_host_permissions_.AddPattern(*pattern); } } @@ -2171,7 +2154,7 @@ bool Extension::HasEffectiveAccessToAllHosts() const { return false; } -bool Extension::IsAPIPermission(const std::string& str) { +bool Extension::IsAPIPermission(const std::string& str) const { for (size_t i = 0; i < Extension::kNumPermissions; ++i) { if (str == Extension::kPermissions[i].name) { // Only allow the experimental API permission if the command line @@ -2210,12 +2193,6 @@ bool Extension::CanExecuteScriptEverywhere() const { return false; } -Extension::RuntimeData* Extension::GetRuntimeData() const { - // TODO(mpcomplete): it would be nice if I could verify we were on the UI - // thread, but we're in common and don't have access to BrowserThread. - return const_cast<Extension::RuntimeData*>(runtime_data_.get()); -} - ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, const std::string& id, const FilePath& path, diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index db1c0c3..ce7e628 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -30,7 +30,7 @@ class SkBitmap; class Version; // Represents a Chrome extension. -class Extension { +class Extension : public base::RefCountedThreadSafe<Extension> { public: typedef std::map<const std::string, GURL> URLOverrideMap; typedef std::vector<std::string> ScriptingWhitelist; @@ -49,8 +49,12 @@ class Extension { COMPONENT, // An integral component of Chrome itself, which // happens to be implemented as an extension. We don't // show these in the management UI. - EXTERNAL_PREF_DOWNLOAD // A crx file from an external directory (via + EXTERNAL_PREF_DOWNLOAD, // A crx file from an external directory (via // prefs), installed from an update URL. + EXTERNAL_POLICY_DOWNLOAD, // A crx file from an external directory (via + // admin policies), installed from an update URL. + + NUM_LOCATIONS }; enum State { @@ -96,181 +100,6 @@ class Extension { bool is_public; // False if only this extension can load this plugin. }; - // Contains a subset of the extension's data that doesn't change once - // initialized, and therefore shareable across threads without locking. - struct StaticData : public base::RefCountedThreadSafe<StaticData> { - StaticData(); - - // TODO(mpcomplete): RefCountedThreadSafe does not allow AddRef/Release on - // const objects. I think that is a mistake. Until we can fix that, here's - // a workaround. - void AddRef() const { - const_cast<StaticData*>(this)-> - base::RefCountedThreadSafe<StaticData>::AddRef(); - } - void Release() const { - const_cast<StaticData*>(this)-> - base::RefCountedThreadSafe<StaticData>::Release(); - } - - // A persistent, globally unique ID. An extension's ID is used in things - // like directory structures and URLs, and is expected to not change across - // versions. It is generated as a SHA-256 hash of the extension's public - // key, or as a hash of the path in the case of unpacked extensions. - std::string id; - - // The extension's human-readable name. Name is used for display purpose. It - // might be wrapped with unicode bidi control characters so that it is - // displayed correctly in RTL context. - // NOTE: Name is UTF-8 and may contain non-ascii characters. - std::string name; - - // The absolute path to the directory the extension is stored in. - FilePath path; - - // Default locale for fall back. Can be empty if extension is not localized. - std::string default_locale; - - // If true, a separate process will be used for the extension in incognito - // mode. - bool incognito_split_mode; - - // Defines the set of URLs in the extension's web content. - ExtensionExtent extent; - - // The set of host permissions that the extension effectively has access to, - // which is a merge of host_permissions_ and all of the match patterns in - // any content scripts the extension has. This is used to determine which - // URLs have the ability to load an extension's resources via embedded - // chrome-extension: URLs (see extension_protocols.cc). - ExtensionExtent effective_host_permissions; - - // The set of module-level APIs this extension can use. - std::set<std::string> api_permissions; - - // The icons for the extension. - ExtensionIconSet icons; - - // The base extension url for the extension. - GURL extension_url; - - // The location the extension was loaded from. - Location location; - - // The extension's version. - scoped_ptr<Version> version; - - // An optional longer description of the extension. - std::string description; - - // True if the extension was generated from a user script. (We show slightly - // different UI if so). - bool converted_from_user_script; - - // Paths to the content scripts the extension contains. - UserScriptList content_scripts; - - // The extension's page action, if any. - scoped_ptr<ExtensionAction> page_action; - - // The extension's browser action, if any. - scoped_ptr<ExtensionAction> browser_action; - - // Optional list of NPAPI plugins and associated properties. - std::vector<PluginInfo> plugins; - - // Optional URL to a master page of which a single instance should be always - // loaded in the background. - GURL background_url; - - // Optional URL to a page for setting options/preferences. - GURL options_url; - - // Optional URL to a devtools extension page. - GURL devtools_url; - - // Optional list of toolstrips and associated properties. - std::vector<GURL> toolstrips; - - // The public key used to sign the contents of the crx package. - std::string public_key; - - // A map of resource id's to relative file paths. - scoped_ptr<DictionaryValue> theme_images; - - // A map of color names to colors. - scoped_ptr<DictionaryValue> theme_colors; - - // A map of color names to colors. - scoped_ptr<DictionaryValue> theme_tints; - - // A map of display properties. - scoped_ptr<DictionaryValue> theme_display_properties; - - // Whether the extension is a theme. - bool is_theme; - - // The sites this extension has permission to talk to (using XHR, etc). - URLPatternList host_permissions; - - // URL for fetching an update manifest - GURL update_url; - - // A copy of the manifest that this extension was created from. - scoped_ptr<DictionaryValue> manifest_value; - - // A map of chrome:// hostnames (newtab, downloads, etc.) to Extension URLs - // which override the handling of those URLs. - URLOverrideMap chrome_url_overrides; - - // Whether this extension uses app features. - bool is_app; - - // The local path inside the extension to use with the launcher. - std::string launch_local_path; - - // A web url to use with the launcher. Note that this might be relative or - // absolute. If relative, it is relative to web_origin. - std::string launch_web_url; - - // The type of container to launch into. - extension_misc::LaunchContainer launch_container; - - // The default size of the container when launching. Only respected for - // containers like panels and windows. - int launch_width; - int launch_height; - - // The Omnibox keyword for this extension, or empty if there is none. - std::string omnibox_keyword; - - protected: - friend class base::RefCountedThreadSafe<StaticData>; - ~StaticData(); - }; - - // Contains the subset of the extension's (private) data that can be modified - // after initialization. This class should only be accessed on the UI thread. - struct RuntimeData { - // We keep a cache of images loaded from extension resources based on their - // path and a string representation of a size that may have been used to - // scale it (or the empty string if the image is at its original size). - typedef std::pair<FilePath, std::string> ImageCacheKey; - typedef std::map<ImageCacheKey, SkBitmap> ImageCache; - - RuntimeData(); - ~RuntimeData(); - - // True if the background page is ready. - bool background_page_ready; - - // True while the extension is being upgraded. - bool being_upgraded; - - // Cached images for this extension. - ImageCache image_cache_; - }; - // A permission is defined by its |name| (what is used in the manifest), // and the |message_id| that's used by install/update UI. struct Permission { @@ -278,12 +107,18 @@ class Extension { const int message_id; }; + static scoped_refptr<Extension> Create(const FilePath& path, + Location location, + const DictionaryValue& value, + bool require_key, + std::string* error); + // The install message id for |permission|. Returns 0 if none exists. static int GetPermissionMessageId(const std::string& permission); // Returns the full list of permission messages that this extension // should display at install time. - std::vector<string16> GetPermissionMessages(); + std::vector<string16> GetPermissionMessages() const; // Returns the distinct hosts that should be displayed in the install UI. This // discards some of the detail that is present in the manifest to make it as @@ -291,7 +126,7 @@ class Extension { // and path components of URLPatterns and de-dupe the result. static std::vector<std::string> GetDistinctHosts( const URLPatternList& host_patterns); - std::vector<std::string> GetDistinctHosts(); + std::vector<std::string> GetDistinctHosts() const; // Icon sizes used by the extension system. static const int kIconSizes[]; @@ -351,9 +186,6 @@ class Extension { // The mimetype used for extensions. static const char kMimeType[]; - explicit Extension(const FilePath& path); - virtual ~Extension(); - // Checks to see if the extension has a valid ID. static bool IdIsValid(const std::string& id); @@ -367,11 +199,19 @@ class Extension { static inline bool IsExternalLocation(Location location) { return location == Extension::EXTERNAL_PREF || location == Extension::EXTERNAL_REGISTRY || - location == Extension::EXTERNAL_PREF_DOWNLOAD; + location == Extension::EXTERNAL_PREF_DOWNLOAD || + location == Extension::EXTERNAL_POLICY_DOWNLOAD; + } + + // Whether extensions with |location| are auto-updatable or not. + static inline bool IsAutoUpdateableLocation(Location location) { + // Only internal and external extensions can be autoupdated. + return location == Extension::INTERNAL || + IsExternalLocation(location); } // See HistogramType definition above. - HistogramType GetHistogramType(); + HistogramType GetHistogramType() const; // Returns an absolute url to a resource inside of an extension. The // |extension_url| argument should be the url() from an Extension object. The @@ -386,10 +226,10 @@ class Extension { // Returns an extension resource object. |relative_path| should be UTF8 // encoded. - ExtensionResource GetResource(const std::string& relative_path); + ExtensionResource GetResource(const std::string& relative_path) const; // As above, but with |relative_path| following the file system's encoding. - ExtensionResource GetResource(const FilePath& relative_path); + ExtensionResource GetResource(const FilePath& relative_path) const; // |input| is expected to be the text of an rsa public or private key. It // tolerates the presence or absence of bracking header/footer like this: @@ -411,14 +251,14 @@ class Extension { // Determine whether |new_extension| has increased privileges compared to // |old_extension|. - static bool IsPrivilegeIncrease(Extension* old_extension, - Extension* new_extension); + static bool IsPrivilegeIncrease(const Extension* old_extension, + const Extension* new_extension); // Given an extension and icon size, read it if present and decode it into // result. In the browser process, this will DCHECK if not called on the // file thread. To easily load extension images on the UI thread, see // ImageLoadingTracker. - static void DecodeIcon(Extension* extension, + static void DecodeIcon(const Extension* extension, Icons icon_size, scoped_ptr<SkBitmap>* result); @@ -457,57 +297,6 @@ class Extension { // Adds an extension to the scripting whitelist. Used for testing only. static void SetScriptingWhitelist(const ScriptingWhitelist& whitelist); - // Initialize the extension from a parsed manifest. - // Usually, the id of an extension is generated by the "key" property of - // its manifest, but if |require_key| is |false|, a temporary ID will be - // generated based on the path. - bool InitFromValue(const DictionaryValue& value, bool require_key, - std::string* error); - - const StaticData* static_data() const { return static_data_; } - - const FilePath& path() const { return static_data_->path; } - const GURL& url() const { return static_data_->extension_url; } - Location location() const { return static_data_->location; } - void set_location(Location location) { - mutable_static_data_->location = location; - } - - const std::string& id() const { return static_data_->id; } - const Version* version() const { return static_data_->version.get(); } - // String representation of the version number. - const std::string VersionString() const; - const std::string& name() const { return static_data_->name; } - const std::string& public_key() const { return static_data_->public_key; } - const std::string& description() const { return static_data_->description; } - bool converted_from_user_script() const { - return static_data_->converted_from_user_script; - } - const UserScriptList& content_scripts() const { - return static_data_->content_scripts; - } - ExtensionAction* page_action() const { - return static_data_->page_action.get(); - } - ExtensionAction* browser_action() const { - return static_data_->browser_action.get(); - } - const std::vector<PluginInfo>& plugins() const { - return static_data_->plugins; - } - const GURL& background_url() const { return static_data_->background_url; } - const GURL& options_url() const { return static_data_->options_url; } - const GURL& devtools_url() const { return static_data_->devtools_url; } - const std::vector<GURL>& toolstrips() const { - return static_data_->toolstrips; - } - const std::set<std::string>& api_permissions() const { - return static_data_->api_permissions; - } - const URLPatternList& host_permissions() const { - return static_data_->host_permissions; - } - // Returns true if the extension has the specified API permission. static bool HasApiPermission(const std::set<std::string>& api_permissions, const std::string& function_name); @@ -517,14 +306,14 @@ class Extension { } const ExtensionExtent& GetEffectiveHostPermissions() const { - return static_data_->effective_host_permissions; + return effective_host_permissions_; } // Whether or not the extension is allowed permission for a URL pattern from // the manifest. http, https, and chrome://favicon/ is allowed for all // extensions, while component extensions are allowed access to // chrome://resources. - bool CanSpecifyHostPermission(const URLPattern pattern) const; + bool CanSpecifyHostPermission(const URLPattern& pattern) const; // Whether the extension has access to the given URL. bool HasHostPermission(const GURL& url) const; @@ -536,120 +325,125 @@ class Extension { // network, etc.) bool HasEffectiveAccessToAllHosts() const; - const GURL& update_url() const { return static_data_->update_url; } - - const ExtensionIconSet& icons() const { - return static_data_->icons; - } - - // Returns the Google Gallery URL for this extension, if one exists. For + // Returns the Homepage URL for this extension. If homepage_url was not + // specified in the manifest, this returns the Google Gallery URL. For // third-party extensions, this returns a blank GURL. - GURL GalleryUrl() const; - - // Theme-related. - DictionaryValue* GetThemeImages() const { - return static_data_->theme_images.get(); - } - DictionaryValue* GetThemeColors() const { - return static_data_->theme_colors.get(); - } - DictionaryValue* GetThemeTints() const { - return static_data_->theme_tints.get(); - } - DictionaryValue* GetThemeDisplayProperties() const { - return static_data_->theme_display_properties.get(); - } - bool is_theme() const { return static_data_->is_theme; } + GURL GetHomepageURL() const; // Returns a list of paths (relative to the extension dir) for images that // the browser might load (like themes and page action icons). - std::set<FilePath> GetBrowserImages(); + std::set<FilePath> GetBrowserImages() const; // Get an extension icon as a resource or URL. - ExtensionResource GetIconResource(int size, - ExtensionIconSet::MatchType match_type); - GURL GetIconURL(int size, ExtensionIconSet::MatchType match_type); - - const DictionaryValue* manifest_value() const { - return static_data_->manifest_value.get(); - } - - const std::string default_locale() const { - return static_data_->default_locale; - } - - // Chrome URL overrides (see ExtensionOverrideUI). - const URLOverrideMap& GetChromeURLOverrides() const { - return static_data_->chrome_url_overrides; - } - - const std::string omnibox_keyword() const { - return static_data_->omnibox_keyword; - } - - bool is_app() const { return static_data_->is_app; } - const ExtensionExtent& web_extent() const { - return static_data_->extent; - } - const std::string& launch_local_path() const { - return static_data_->launch_local_path; - } - const std::string& launch_web_url() const { - return static_data_->launch_web_url; - } - extension_misc::LaunchContainer launch_container() const { - return static_data_->launch_container; - } - int launch_width() const { return static_data_->launch_width; } - int launch_height() const { return static_data_->launch_height; } - bool incognito_split_mode() const { - return static_data_->incognito_split_mode; - } + ExtensionResource GetIconResource( + int size, ExtensionIconSet::MatchType match_type) const; + GURL GetIconURL(int size, ExtensionIconSet::MatchType match_type) const; // Gets the fully resolved absolute launch URL. GURL GetFullLaunchURL() const; - - // Whether the background page, if any, is ready. We don't load other - // components until then. If there is no background page, we consider it to - // be ready. - bool GetBackgroundPageReady(); - void SetBackgroundPageReady(); - - // Getter and setter for the flag that specifies whether the extension is - // being upgraded. - bool being_upgraded() const { return GetRuntimeData()->being_upgraded; } - void set_being_upgraded(bool value) { - GetRuntimeData()->being_upgraded = value; - } - // Image cache related methods. These are only valid on the UI thread and // not maintained by this class. See ImageLoadingTracker for usage. The // |original_size| parameter should be the size of the image at |source| // before any scaling may have been done to produce the pixels in |image|. void SetCachedImage(const ExtensionResource& source, const SkBitmap& image, - const gfx::Size& original_size); + const gfx::Size& original_size) const; bool HasCachedImage(const ExtensionResource& source, - const gfx::Size& max_size); + const gfx::Size& max_size) const; SkBitmap GetCachedImage(const ExtensionResource& source, - const gfx::Size& max_size); - bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); } - bool is_packaged_app() const { return is_app() && web_extent().is_empty(); } - + const gfx::Size& max_size) const; // Returns true if this extension is a COMPONENT extension, or if it is // on the whitelist of extensions that can script all pages. bool CanExecuteScriptEverywhere() const; + // Accessors: + + const FilePath& path() const { return path_; } + const GURL& url() const { return extension_url_; } + Location location() const { return location_; } + const std::string& id() const { return id_; } + const Version* version() const { return version_.get(); } + const std::string VersionString() const; + const std::string& name() const { return name_; } + const std::string& public_key() const { return public_key_; } + const std::string& description() const { return description_; } + bool converted_from_user_script() const { + return converted_from_user_script_; + } + const UserScriptList& content_scripts() const { return content_scripts_; } + ExtensionAction* page_action() const { return page_action_.get(); } + ExtensionAction* browser_action() const { return browser_action_.get(); } + const std::vector<PluginInfo>& plugins() const { return plugins_; } + const GURL& background_url() const { return background_url_; } + const GURL& options_url() const { return options_url_; } + const GURL& devtools_url() const { return devtools_url_; } + const std::vector<GURL>& toolstrips() const { return toolstrips_; } + const std::set<std::string>& api_permissions() const { + return api_permissions_; + } + const URLPatternList& host_permissions() const { return host_permissions_; } + const GURL& update_url() const { return update_url_; } + const ExtensionIconSet& icons() const { return icons_; } + const DictionaryValue* manifest_value() const { + return manifest_value_.get(); + } + const std::string default_locale() const { return default_locale_; } + const URLOverrideMap& GetChromeURLOverrides() const { + return chrome_url_overrides_; + } + const std::string omnibox_keyword() const { return omnibox_keyword_; } + bool incognito_split_mode() const { return incognito_split_mode_; } + + // App-related. + bool is_app() const { return is_app_; } + bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); } + bool is_packaged_app() const { return is_app() && web_extent().is_empty(); } + const ExtensionExtent& web_extent() const { return extent_; } + const std::string& launch_local_path() const { return launch_local_path_; } + const std::string& launch_web_url() const { return launch_web_url_; } + extension_misc::LaunchContainer launch_container() const { + return launch_container_; + } + int launch_width() const { return launch_width_; } + int launch_height() const { return launch_height_; } + + // Theme-related. + bool is_theme() const { return is_theme_; } + DictionaryValue* GetThemeImages() const { return theme_images_.get(); } + DictionaryValue* GetThemeColors() const {return theme_colors_.get(); } + DictionaryValue* GetThemeTints() const { return theme_tints_.get(); } + DictionaryValue* GetThemeDisplayProperties() const { + return theme_display_properties_.get(); + } + private: + friend class base::RefCountedThreadSafe<Extension>; + + // We keep a cache of images loaded from extension resources based on their + // path and a string representation of a size that may have been used to + // scale it (or the empty string if the image is at its original size). + typedef std::pair<FilePath, std::string> ImageCacheKey; + typedef std::map<ImageCacheKey, SkBitmap> ImageCache; + // Normalize the path for use by the extension. On Windows, this will make // sure the drive letter is uppercase. static FilePath MaybeNormalizePath(const FilePath& path); + Extension(const FilePath& path, Location location); + ~Extension(); + + // Initialize the extension from a parsed manifest. + // Usually, the id of an extension is generated by the "key" property of + // its manifest, but if |require_key| is |false|, a temporary ID will be + // generated based on the path. + bool InitFromValue(const DictionaryValue& value, bool require_key, + std::string* error); + // Helper function for implementing HasCachedImage/GetCachedImage. A return // value of NULL means there is no matching image cached (we allow caching an // empty SkBitmap). SkBitmap* GetCachedImageImpl(const ExtensionResource& source, - const gfx::Size& max_size); + const gfx::Size& max_size) const; // Helper method that loads a UserScript object from a // dictionary in the content_script list of the manifest. @@ -691,43 +485,171 @@ class Extension { // Figures out if a source contains keys not associated with themes - we // don't want to allow scripts and such to be bundled with themes. - bool ContainsNonThemeKeys(const DictionaryValue& source); + bool ContainsNonThemeKeys(const DictionaryValue& source) const; // Returns true if the string is one of the known api permissions (see // kPermissions). - bool IsAPIPermission(const std::string& permission); + bool IsAPIPermission(const std::string& permission) const; // The set of unique API install messages that the extension has. // NOTE: This only includes messages related to permissions declared in the // "permissions" key in the manifest. Permissions implied from other features // of the manifest, like plugins and content scripts are not included. - std::set<string16> GetSimplePermissionMessages(); + std::set<string16> GetSimplePermissionMessages() const; // The permission message displayed related to the host permissions for // this extension. - string16 GetHostPermissionMessage(); + string16 GetHostPermissionMessage() const; + + // Cached images for this extension. This should only be touched on the UI + // thread. + mutable ImageCache image_cache_; + + // A persistent, globally unique ID. An extension's ID is used in things + // like directory structures and URLs, and is expected to not change across + // versions. It is generated as a SHA-256 hash of the extension's public + // key, or as a hash of the path in the case of unpacked extensions. + std::string id_; + + // The extension's human-readable name. Name is used for display purpose. It + // might be wrapped with unicode bidi control characters so that it is + // displayed correctly in RTL context. + // NOTE: Name is UTF-8 and may contain non-ascii characters. + std::string name_; + + // The absolute path to the directory the extension is stored in. + FilePath path_; + + // Default locale for fall back. Can be empty if extension is not localized. + std::string default_locale_; + + // If true, a separate process will be used for the extension in incognito + // mode. + bool incognito_split_mode_; + + // Defines the set of URLs in the extension's web content. + ExtensionExtent extent_; + + // The set of host permissions that the extension effectively has access to, + // which is a merge of host_permissions_ and all of the match patterns in + // any content scripts the extension has. This is used to determine which + // URLs have the ability to load an extension's resources via embedded + // chrome-extension: URLs (see extension_protocols.cc). + ExtensionExtent effective_host_permissions_; + + // The set of module-level APIs this extension can use. + std::set<std::string> api_permissions_; + + // The icons for the extension. + ExtensionIconSet icons_; + + // The base extension url for the extension. + GURL extension_url_; + + // The location the extension was loaded from. + Location location_; + + // The extension's version. + scoped_ptr<Version> version_; + + // An optional longer description of the extension. + std::string description_; + + // True if the extension was generated from a user script. (We show slightly + // different UI if so). + bool converted_from_user_script_; + + // Paths to the content scripts the extension contains. + UserScriptList content_scripts_; + + // The extension's page action, if any. + scoped_ptr<ExtensionAction> page_action_; + + // The extension's browser action, if any. + scoped_ptr<ExtensionAction> browser_action_; + + // Optional list of NPAPI plugins and associated properties. + std::vector<PluginInfo> plugins_; + + // Optional URL to a master page of which a single instance should be always + // loaded in the background. + GURL background_url_; + + // Optional URL to a page for setting options/preferences. + GURL options_url_; + + // Optional URL to a devtools extension page. + GURL devtools_url_; + + // Optional list of toolstrips and associated properties. + std::vector<GURL> toolstrips_; + + // The public key used to sign the contents of the crx package. + std::string public_key_; + + // A map of resource id's to relative file paths. + scoped_ptr<DictionaryValue> theme_images_; + + // A map of color names to colors. + scoped_ptr<DictionaryValue> theme_colors_; + + // A map of color names to colors. + scoped_ptr<DictionaryValue> theme_tints_; + + // A map of display properties. + scoped_ptr<DictionaryValue> theme_display_properties_; + + // Whether the extension is a theme. + bool is_theme_; + + // The sites this extension has permission to talk to (using XHR, etc). + URLPatternList host_permissions_; + + // The homepage for this extension. Useful if it is not hosted by Google and + // therefore does not have a Gallery URL. + GURL homepage_url_; + + // URL for fetching an update manifest + GURL update_url_; + + // A copy of the manifest that this extension was created from. + scoped_ptr<DictionaryValue> manifest_value_; + + // A map of chrome:// hostnames (newtab, downloads, etc.) to Extension URLs + // which override the handling of those URLs. (see ExtensionOverrideUI). + URLOverrideMap chrome_url_overrides_; + + // Whether this extension uses app features. + bool is_app_; + + // The local path inside the extension to use with the launcher. + std::string launch_local_path_; + + // A web url to use with the launcher. Note that this might be relative or + // absolute. If relative, it is relative to web_origin. + std::string launch_web_url_; - // Returns a mutable pointer to our runtime data. Can only be called on - // the UI thread. - RuntimeData* GetRuntimeData() const; + // The type of container to launch into. + extension_misc::LaunchContainer launch_container_; - // Collection of extension data that doesn't change doesn't change once an - // Extension object has been initialized. The mutable version is valid only - // until InitFromValue finishes, to ensure we don't accidentally modify it - // post-initialization. - StaticData* mutable_static_data_; - scoped_refptr<const StaticData> static_data_; + // The default size of the container when launching. Only respected for + // containers like panels and windows. + int launch_width_; + int launch_height_; - // Runtime data. - scoped_ptr<const RuntimeData> runtime_data_; + // The Omnibox keyword for this extension, or empty if there is none. + std::string omnibox_keyword_; FRIEND_TEST_ALL_PREFIXES(ExtensionTest, LoadPageActionHelper); + FRIEND_TEST_ALL_PREFIXES(ExtensionTest, InitFromValueInvalid); + FRIEND_TEST_ALL_PREFIXES(ExtensionTest, InitFromValueValid); + FRIEND_TEST_ALL_PREFIXES(ExtensionTest, InitFromValueValidNameInRTL); FRIEND_TEST_ALL_PREFIXES(TabStripModelTest, Apps); DISALLOW_COPY_AND_ASSIGN(Extension); }; -typedef std::vector<Extension*> ExtensionList; +typedef std::vector< scoped_refptr<const Extension> > ExtensionList; typedef std::set<std::string> ExtensionIdSet; // Handy struct to pass core extension info around. diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h index a50ebf3..a1e5bc4 100644 --- a/chrome/common/extensions/extension_action.h +++ b/chrome/common/extensions/extension_action.h @@ -151,7 +151,7 @@ class ExtensionAction { }; template<class T> - void SetValue(std::map<int, T>* map, int tab_id, T val) { + void SetValue(std::map<int, T>* map, int tab_id, const T& val) { (*map)[tab_id] = val; } diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 983fda4..5e418b5 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -19,6 +19,7 @@ const char* kDefaultLocale = "default_locale"; const char* kDescription = "description"; const char* kDevToolsPage = "devtools_page"; const char* kExcludeGlobs = "exclude_globs"; +const char* kHomepageURL = "homepage_url"; const char* kIcons = "icons"; const char* kIncognito = "incognito"; const char* kIncludeGlobs = "include_globs"; @@ -124,6 +125,8 @@ const char* kInvalidGlob = "Invalid value for 'content_scripts[*].*[*]'."; const char* kInvalidGlobList = "Invalid value for 'content_scripts[*].*'."; +const char* kInvalidHomepageURL = + "Invalid value for homepage url: '[*]'."; const char* kInvalidIconPath = "Invalid value for 'icons[\"*\"]'."; const char* kInvalidIcons = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index e76ce24..332f386 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -24,6 +24,7 @@ namespace extension_manifest_keys { extern const char* kDescription; extern const char* kDevToolsPage; extern const char* kExcludeGlobs; + extern const char* kHomepageURL; extern const char* kIcons; extern const char* kIncognito; extern const char* kIncludeGlobs; @@ -108,6 +109,7 @@ namespace extension_manifest_errors { extern const char* kInvalidDevToolsPage; extern const char* kInvalidGlob; extern const char* kInvalidGlobList; + extern const char* kInvalidHomepageURL; extern const char* kInvalidIconPath; extern const char* kInvalidIcons; extern const char* kInvalidIncognitoBehavior; diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc index d530357..f2350cc 100644 --- a/chrome/common/extensions/extension_file_util.cc +++ b/chrome/common/extensions/extension_file_util.cc @@ -54,7 +54,7 @@ FilePath InstallExtension(const FilePath& unpacked_source_dir, const int kMaxAttempts = 100; for (int i = 0; i < kMaxAttempts; ++i) { FilePath candidate = extension_dir.AppendASCII( - StringPrintf("%s_%u", version.c_str(), i)); + base::StringPrintf("%s_%u", version.c_str(), i)); if (!file_util::PathExists(candidate)) { version_dir = candidate; break; @@ -81,15 +81,14 @@ void UninstallExtension(const FilePath& extensions_dir, file_util::Delete(extensions_dir.AppendASCII(id), true); // recursive. } -Extension* LoadExtension(const FilePath& extension_path, - Extension::Location location, - bool require_key, - std::string* error) { +scoped_refptr<Extension> LoadExtension(const FilePath& extension_path, + Extension::Location location, + bool require_key, + std::string* error) { FilePath manifest_path = extension_path.Append(Extension::kManifestFilename); if (!file_util::PathExists(manifest_path)) { - *error = - l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); + *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); return NULL; } @@ -101,37 +100,33 @@ Extension* LoadExtension(const FilePath& extension_path, // It would be cleaner to have the JSON reader give a specific error // in this case, but other code tests for a file error with // error->empty(). For now, be consistent. - *error = - l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); + *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); } else { - *error = StringPrintf("%s %s", - errors::kManifestParseError, - error->c_str()); + *error = base::StringPrintf("%s %s", + errors::kManifestParseError, + error->c_str()); } return NULL; } if (!root->IsType(Value::TYPE_DICTIONARY)) { - *error = - l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID); + *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID); return NULL; } DictionaryValue* manifest = static_cast<DictionaryValue*>(root.get()); - - scoped_ptr<Extension> extension(new Extension(extension_path)); - extension->set_location(location); - - if (!extension_l10n_util::LocalizeExtension(extension.get(), manifest, error)) + if (!extension_l10n_util::LocalizeExtension(extension_path, manifest, error)) return NULL; - if (!extension->InitFromValue(*manifest, require_key, error)) + scoped_refptr<Extension> extension(Extension::Create( + extension_path, location, *manifest, require_key, error)); + if (!extension.get()) return NULL; if (!ValidateExtension(extension.get(), error)) return NULL; - return extension.release(); + return extension; } bool ValidateExtension(Extension* extension, std::string* error) { @@ -411,7 +406,7 @@ static bool ValidateLocaleInfo(const Extension& extension, std::string* error) { locale_path.Append(Extension::kMessagesFilename); if (!file_util::PathExists(messages_path)) { - *error = StringPrintf( + *error = base::StringPrintf( "%s %s", errors::kLocalesMessagesFileMissing, WideToUTF8(messages_path.ToWStringHack()).c_str()); return false; @@ -480,10 +475,10 @@ bool CheckForIllegalFilenames(const FilePath& extension_path, if (filename.find_first_of(FILE_PATH_LITERAL("_")) != 0) continue; if (reserved_underscore_names.find(filename) == reserved_underscore_names.end()) { - *error = StringPrintf( - "Cannot load extension with file or directory name %s. " - "Filenames starting with \"_\" are reserved for use by the system.", - filename.c_str()); + *error = base::StringPrintf( + "Cannot load extension with file or directory name %s. " + "Filenames starting with \"_\" are reserved for use by the system.", + filename.c_str()); return false; } } diff --git a/chrome/common/extensions/extension_file_util.h b/chrome/common/extensions/extension_file_util.h index 1b8fb76..f6a72bc 100644 --- a/chrome/common/extensions/extension_file_util.h +++ b/chrome/common/extensions/extension_file_util.h @@ -37,10 +37,10 @@ void UninstallExtension(const FilePath& extensions_dir, // Loads and validates an extension from the specified directory. Returns NULL // on failure, with a description of the error in |error|. -Extension* LoadExtension(const FilePath& extension_root, - Extension::Location location, - bool require_key, - std::string* error); +scoped_refptr<Extension> LoadExtension(const FilePath& extension_root, + Extension::Location location, + bool require_key, + std::string* error); // Returns true if the given extension object is valid and consistent. // Otherwise, a description of the error is returned in |error|. diff --git a/chrome/common/extensions/extension_file_util_unittest.cc b/chrome/common/extensions/extension_file_util_unittest.cc index 634df05..230712d 100644 --- a/chrome/common/extensions/extension_file_util_unittest.cc +++ b/chrome/common/extensions/extension_file_util_unittest.cc @@ -78,7 +78,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithValidLocales) { .AppendASCII("1.0.0.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension != NULL); EXPECT_EQ("The first extension that I made.", extension->description()); @@ -94,7 +94,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithoutLocalesFolder) { .AppendASCII("1.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_FALSE(extension == NULL); EXPECT_TRUE(error.empty()); @@ -152,7 +152,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnMissingManifest) { .AppendASCII("1.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); @@ -169,7 +169,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnBadManifest) { .AppendASCII("1.0"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); @@ -185,7 +185,7 @@ TEST(ExtensionFileUtil, FailLoadingNonUTF8Scripts) { .AppendASCII("bad_encoding"); std::string error; - scoped_ptr<Extension> extension(extension_file_util::LoadExtension( + scoped_refptr<Extension> extension(extension_file_util::LoadExtension( install_dir, Extension::LOAD, false, &error)); ASSERT_TRUE(extension == NULL); ASSERT_STREQ("Could not load file 'bad_encoding.js' for content script. " diff --git a/chrome/common/extensions/extension_l10n_util.cc b/chrome/common/extensions/extension_l10n_util.cc index 4187689..8c7826c 100644 --- a/chrome/common/extensions/extension_l10n_util.cc +++ b/chrome/common/extensions/extension_l10n_util.cc @@ -113,7 +113,7 @@ bool LocalizeManifest(const ExtensionMessageBundle& messages, return true; } -bool LocalizeExtension(Extension* extension, +bool LocalizeExtension(const FilePath& extension_path, DictionaryValue* manifest, std::string* error) { DCHECK(manifest); @@ -122,7 +122,7 @@ bool LocalizeExtension(Extension* extension, scoped_ptr<ExtensionMessageBundle> message_bundle( extension_file_util::LoadExtensionMessageBundle( - extension->path(), default_locale, error)); + extension_path, default_locale, error)); if (!message_bundle.get() && !error->empty()) return false; @@ -146,8 +146,8 @@ bool AddLocale(const std::set<std::string>& chrome_locales, if (chrome_locales.find(locale_name) == chrome_locales.end()) { // Warn if there is an extension locale that's not in the Chrome list, // but don't fail. - LOG(WARNING) << StringPrintf("Supplied locale %s is not supported.", - locale_name.c_str()); + LOG(WARNING) << base::StringPrintf("Supplied locale %s is not supported.", + locale_name.c_str()); return true; } // Check if messages file is actually present (but don't check content). @@ -155,8 +155,8 @@ bool AddLocale(const std::set<std::string>& chrome_locales, locale_folder.Append(Extension::kMessagesFilename))) { valid_locales->insert(locale_name); } else { - *error = StringPrintf("Catalog file is missing for locale %s.", - locale_name.c_str()); + *error = base::StringPrintf("Catalog file is missing for locale %s.", + locale_name.c_str()); return false; } @@ -250,8 +250,8 @@ static DictionaryValue* LoadMessageFile(const FilePath& locale_path, if (!dictionary && error->empty()) { // JSONFileValueSerializer just returns NULL if file cannot be found. It // doesn't set the error, so we have to do it. - *error = StringPrintf("Catalog file is missing for locale %s.", - extension_locale.c_str()); + *error = base::StringPrintf("Catalog file is missing for locale %s.", + extension_locale.c_str()); } return static_cast<DictionaryValue*>(dictionary); diff --git a/chrome/common/extensions/extension_l10n_util.h b/chrome/common/extensions/extension_l10n_util.h index 48dc901..e1fdfa0 100644 --- a/chrome/common/extensions/extension_l10n_util.h +++ b/chrome/common/extensions/extension_l10n_util.h @@ -44,7 +44,7 @@ bool LocalizeManifest(const ExtensionMessageBundle& messages, // Load message catalogs, localize manifest and attach message bundle to the // extension. -bool LocalizeExtension(Extension* extension, +bool LocalizeExtension(const FilePath& extension_path, DictionaryValue* manifest, std::string* error); diff --git a/chrome/common/extensions/extension_localization_peer.cc b/chrome/common/extensions/extension_localization_peer.cc index 06e0317..e04bd0b 100644 --- a/chrome/common/extensions/extension_localization_peer.cc +++ b/chrome/common/extensions/extension_localization_peer.cc @@ -90,10 +90,6 @@ void ExtensionLocalizationPeer::OnCompletedRequest( original_peer_->OnCompletedRequest(status, security_info, completion_time); } -GURL ExtensionLocalizationPeer::GetURLForDebugging() const { - return original_peer_->GetURLForDebugging(); -} - void ExtensionLocalizationPeer::ReplaceMessages() { if (!message_sender_ || data_.empty()) return; diff --git a/chrome/common/extensions/extension_localization_peer.h b/chrome/common/extensions/extension_localization_peer.h index b2b7c7a..1274421 100644 --- a/chrome/common/extensions/extension_localization_peer.h +++ b/chrome/common/extensions/extension_localization_peer.h @@ -45,7 +45,6 @@ class ExtensionLocalizationPeer virtual void OnCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time); - virtual GURL GetURLForDebugging() const; private: friend class ExtensionLocalizationPeerTest; diff --git a/chrome/common/extensions/extension_localization_peer_unittest.cc b/chrome/common/extensions/extension_localization_peer_unittest.cc index bc6f73f..dc67d5b 100644 --- a/chrome/common/extensions/extension_localization_peer_unittest.cc +++ b/chrome/common/extensions/extension_localization_peer_unittest.cc @@ -72,7 +72,6 @@ class MockResourceLoaderBridgePeer const URLRequestStatus& status, const std::string& security_info, const base::Time& completion_time)); - MOCK_CONST_METHOD0(GetURLForDebugging, GURL()); private: DISALLOW_COPY_AND_ASSIGN(MockResourceLoaderBridgePeer); @@ -153,7 +152,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestEmptyData) { // It will self-delete once it exits OnCompletedRequest. ExtensionLocalizationPeer* filter_peer = filter_peer_.release(); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); EXPECT_CALL(*original_peer_, OnReceivedData(_, _)).Times(0); EXPECT_CALL(*sender_, Send(_)).Times(0); @@ -172,7 +170,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestNoCatalogs) { SetData(filter_peer, "some text"); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); EXPECT_CALL(*sender_, Send(_)); std::string data = GetData(filter_peer); @@ -208,7 +205,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestWithCatalogs) { SetData(filter_peer, "some __MSG_text__"); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); // We already have messages in memory, Send will be skipped. EXPECT_CALL(*sender_, Send(_)).Times(0); @@ -240,7 +236,6 @@ TEST_F(ExtensionLocalizationPeerTest, OnCompletedRequestReplaceMessagesFails) { std::string message("some __MSG_missing_message__"); SetData(filter_peer, message); - EXPECT_CALL(*original_peer_, GetURLForDebugging()).Times(0); // We already have messages in memory, Send will be skipped. EXPECT_CALL(*sender_, Send(_)).Times(0); diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index ac56f70..3a89166 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -37,53 +37,48 @@ class ExtensionManifestTest : public testing::Test { return static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error)); } - Extension* LoadExtensionWithLocation(DictionaryValue* value, - Extension::Location location, - std::string* error) { + scoped_refptr<Extension> LoadExtensionWithLocation( + DictionaryValue* value, + Extension::Location location, + std::string* error) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions").AppendASCII("manifest_tests"); - - scoped_ptr<Extension> extension(new Extension(path.DirName())); - extension->set_location(location); - - if (!extension->InitFromValue(*value, false, error)) - return NULL; - - return extension.release(); + return Extension::Create(path.DirName(), location, *value, false, error); } - Extension* LoadExtension(const std::string& name, - std::string* error) { + scoped_refptr<Extension> LoadExtension(const std::string& name, + std::string* error) { return LoadExtensionWithLocation(name, Extension::INTERNAL, error); } - Extension* LoadExtension(DictionaryValue* value, - std::string* error) { + scoped_refptr<Extension> LoadExtension(DictionaryValue* value, + std::string* error) { return LoadExtensionWithLocation(value, Extension::INTERNAL, error); } - Extension* LoadExtensionWithLocation(const std::string& name, - Extension::Location location, - std::string* error) { + scoped_refptr<Extension> LoadExtensionWithLocation( + const std::string& name, + Extension::Location location, + std::string* error) { scoped_ptr<DictionaryValue> value(LoadManifestFile(name, error)); if (!value.get()) return NULL; return LoadExtensionWithLocation(value.get(), location, error); } - Extension* LoadAndExpectSuccess(const std::string& name) { + scoped_refptr<Extension> LoadAndExpectSuccess(const std::string& name) { std::string error; - Extension* extension = LoadExtension(name, &error); + scoped_refptr<Extension> extension = LoadExtension(name, &error); EXPECT_TRUE(extension) << name; EXPECT_EQ("", error) << name; return extension; } - Extension* LoadAndExpectSuccess(DictionaryValue* manifest, - const std::string& name) { + scoped_refptr<Extension> LoadAndExpectSuccess(DictionaryValue* manifest, + const std::string& name) { std::string error; - Extension* extension = LoadExtension(manifest, &error); + scoped_refptr<Extension> extension = LoadExtension(manifest, &error); EXPECT_TRUE(extension) << "Unexpected success for " << name; EXPECT_EQ("", error) << "Unexpected no error for " << name; return extension; @@ -103,7 +98,7 @@ class ExtensionManifestTest : public testing::Test { void LoadAndExpectError(const std::string& name, const std::string& expected_error) { std::string error; - scoped_ptr<Extension> extension(LoadExtension(name, &error)); + scoped_refptr<Extension> extension(LoadExtension(name, &error)); VerifyExpectedError(extension.get(), name, error, expected_error); } @@ -111,7 +106,7 @@ class ExtensionManifestTest : public testing::Test { const std::string& name, const std::string& expected_error) { std::string error; - scoped_ptr<Extension> extension(LoadExtension(manifest, &error)); + scoped_refptr<Extension> extension(LoadExtension(manifest, &error)); VerifyExpectedError(extension.get(), name, error, expected_error); } @@ -119,7 +114,7 @@ class ExtensionManifestTest : public testing::Test { }; TEST_F(ExtensionManifestTest, ValidApp) { - scoped_ptr<Extension> extension(LoadAndExpectSuccess("valid_app.json")); + scoped_refptr<Extension> extension(LoadAndExpectSuccess("valid_app.json")); ASSERT_EQ(2u, extension->web_extent().patterns().size()); EXPECT_EQ("http://www.google.com/mail/*", extension->web_extent().patterns()[0].GetAsString()); @@ -145,7 +140,7 @@ TEST_F(ExtensionManifestTest, AppWebUrls) { ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidWebURL, "0")); - scoped_ptr<Extension> extension( + scoped_refptr<Extension> extension( LoadAndExpectSuccess("web_urls_default.json")); ASSERT_EQ(1u, extension->web_extent().patterns().size()); EXPECT_EQ("*://www.google.com/*", @@ -153,21 +148,21 @@ TEST_F(ExtensionManifestTest, AppWebUrls) { } TEST_F(ExtensionManifestTest, AppLaunchContainer) { - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("launch_tab.json")); + extension = LoadAndExpectSuccess("launch_tab.json"); EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); - extension.reset(LoadAndExpectSuccess("launch_panel.json")); + extension = LoadAndExpectSuccess("launch_panel.json"); EXPECT_EQ(extension_misc::LAUNCH_PANEL, extension->launch_container()); - extension.reset(LoadAndExpectSuccess("launch_default.json")); + extension = LoadAndExpectSuccess("launch_default.json"); EXPECT_EQ(extension_misc::LAUNCH_TAB, extension->launch_container()); - extension.reset(LoadAndExpectSuccess("launch_width.json")); + extension = LoadAndExpectSuccess("launch_width.json"); EXPECT_EQ(640, extension->launch_width()); - extension.reset(LoadAndExpectSuccess("launch_height.json")); + extension = LoadAndExpectSuccess("launch_height.json"); EXPECT_EQ(480, extension->launch_height()); LoadAndExpectError("launch_window.json", @@ -198,15 +193,15 @@ TEST_F(ExtensionManifestTest, AppLaunchURL) { LoadAndExpectError("launch_url_invalid_type.json", errors::kInvalidLaunchWebURL); - scoped_ptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("launch_local_path.json")); + scoped_refptr<Extension> extension; + extension = LoadAndExpectSuccess("launch_local_path.json"); EXPECT_EQ(extension->url().spec() + "launch.html", extension->GetFullLaunchURL().spec()); LoadAndExpectError("launch_web_url_relative.json", errors::kInvalidLaunchWebURL); - extension.reset(LoadAndExpectSuccess("launch_web_url_absolute.json")); + extension = LoadAndExpectSuccess("launch_web_url_absolute.json"); EXPECT_EQ(GURL("http://www.google.com/launch.html"), extension->GetFullLaunchURL()); } @@ -217,13 +212,13 @@ TEST_F(ExtensionManifestTest, Override) { LoadAndExpectError("override_invalid_page.json", errors::kInvalidChromeURLOverrides); - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("override_new_tab.json")); + extension = LoadAndExpectSuccess("override_new_tab.json"); EXPECT_EQ(extension->url().spec() + "newtab.html", extension->GetChromeURLOverrides().find("newtab")->second.spec()); - extension.reset(LoadAndExpectSuccess("override_history.json")); + extension = LoadAndExpectSuccess("override_history.json"); EXPECT_EQ(extension->url().spec() + "history.html", extension->GetChromeURLOverrides().find("history")->second.spec()); } @@ -237,11 +232,11 @@ TEST_F(ExtensionManifestTest, ChromeResourcesPermissionValidOnlyForComponents) { LoadAndExpectError("permission_chrome_resources_url.json", errors::kInvalidPermissionScheme); std::string error; - scoped_ptr<Extension> extension; - extension.reset(LoadExtensionWithLocation( + scoped_refptr<Extension> extension; + extension = LoadExtensionWithLocation( "permission_chrome_resources_url.json", Extension::COMPONENT, - &error)); + &error); EXPECT_EQ("", error); } @@ -260,8 +255,8 @@ TEST_F(ExtensionManifestTest, DevToolsExtensions) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); - scoped_ptr<Extension> extension; - extension.reset(LoadAndExpectSuccess("devtools_extension.json")); + scoped_refptr<Extension> extension; + extension = LoadAndExpectSuccess("devtools_extension.json"); EXPECT_EQ(extension->url().spec() + "devtools.html", extension->devtools_url().spec()); *CommandLine::ForCurrentProcess() = old_command_line; @@ -275,11 +270,10 @@ TEST_F(ExtensionManifestTest, DisallowHybridApps) { } TEST_F(ExtensionManifestTest, OptionsPageInApps) { - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; // Allow options page with absolute URL in hosed apps. - extension.reset( - LoadAndExpectSuccess("hosted_app_absolute_options.json")); + extension = LoadAndExpectSuccess("hosted_app_absolute_options.json"); EXPECT_STREQ("http", extension->options_url().scheme().c_str()); EXPECT_STREQ("example.com", @@ -313,10 +307,10 @@ TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) { StringValue* p = new StringValue(name); permissions->Clear(); permissions->Append(p); - std::string message_name = StringPrintf("permission-%s", name); + std::string message_name = base::StringPrintf("permission-%s", name); if (Extension::IsHostedAppPermission(name)) { - scoped_ptr<Extension> extension; - extension.reset(LoadAndExpectSuccess(manifest.get(), message_name)); + scoped_refptr<Extension> extension; + extension = LoadAndExpectSuccess(manifest.get(), message_name); } else { LoadAndExpectError(manifest.get(), message_name, errors::kInvalidPermission); @@ -325,7 +319,7 @@ TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) { } TEST_F(ExtensionManifestTest, NormalizeIconPaths) { - scoped_ptr<Extension> extension( + scoped_refptr<Extension> extension( LoadAndExpectSuccess("normalize_icon_paths.json")); EXPECT_EQ("16.png", extension->icons().Get(16, ExtensionIconSet::MATCH_EXACTLY)); @@ -338,3 +332,38 @@ TEST_F(ExtensionManifestTest, DisallowMultipleUISurfaces) { LoadAndExpectError("multiple_ui_surfaces_2.json", errors::kOneUISurfaceOnly); LoadAndExpectError("multiple_ui_surfaces_3.json", errors::kOneUISurfaceOnly); } + +TEST_F(ExtensionManifestTest, ParseHomepageURLs) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("homepage_valid.json")); + LoadAndExpectError("homepage_empty.json", + extension_manifest_errors::kInvalidHomepageURL); + LoadAndExpectError("homepage_invalid.json", + extension_manifest_errors::kInvalidHomepageURL); +} + +TEST_F(ExtensionManifestTest, GetHomepageURL) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("homepage_valid.json")); + EXPECT_EQ(GURL("http://foo.com#bar"), extension->GetHomepageURL()); + + // The Google Gallery URL ends with the id, which depends on the path, which + // can be different in testing, so we just check the part before id. + extension = LoadAndExpectSuccess("homepage_google_hosted.json"); + EXPECT_TRUE(StartsWithASCII(extension->GetHomepageURL().spec(), + "https://chrome.google.com/extensions/detail/", + false)); + + extension = LoadAndExpectSuccess("homepage_externally_hosted.json"); + EXPECT_EQ(GURL(), extension->GetHomepageURL()); +} + +TEST_F(ExtensionManifestTest, DefaultPathForExtent) { + scoped_refptr<Extension> extension( + LoadAndExpectSuccess("default_path_for_extent.json")); + + ASSERT_EQ(1u, extension->web_extent().patterns().size()); + EXPECT_EQ("/*", extension->web_extent().patterns()[0].path()); + EXPECT_TRUE(extension->web_extent().ContainsURL( + GURL("http://www.google.com/monkey"))); +} diff --git a/chrome/common/extensions/extension_message_bundle.cc b/chrome/common/extensions/extension_message_bundle.cc index 4de6b45..3f7c152 100644 --- a/chrome/common/extensions/extension_message_bundle.cc +++ b/chrome/common/extensions/extension_message_bundle.cc @@ -48,8 +48,10 @@ const char* ExtensionMessageBundle::kBidiRightEdgeValue = "right"; // Formats message in case we encounter a bad formed key in the JSON object. // Returns false and sets |error| to actual error message. static bool BadKeyMessage(const std::string& name, std::string* error) { - *error = StringPrintf("Name of a key \"%s\" is invalid. Only ASCII [a-z], " - "[A-Z], [0-9] and \"_\" are allowed.", name.c_str()); + *error = base::StringPrintf( + "Name of a key \"%s\" is invalid. Only ASCII [a-z], " + "[A-Z], [0-9] and \"_\" are allowed.", + name.c_str()); return false; } @@ -134,13 +136,13 @@ bool ExtensionMessageBundle::GetMessageValue(const std::string& key, // Get the top level tree for given key (name part). DictionaryValue* name_tree; if (!catalog.GetDictionaryWithoutPathExpansion(key, &name_tree)) { - *error = StringPrintf("Not a valid tree for key %s.", key.c_str()); + *error = base::StringPrintf("Not a valid tree for key %s.", key.c_str()); return false; } // Extract message from it. if (!name_tree->GetString(kMessageKey, value)) { - *error = StringPrintf("There is no \"%s\" element for key %s.", kMessageKey, - key.c_str()); + *error = base::StringPrintf( + "There is no \"%s\" element for key %s.", kMessageKey, key.c_str()); return false; } @@ -166,8 +168,8 @@ bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree, DictionaryValue* placeholders_tree; if (!name_tree.GetDictionary(kPlaceholdersKey, &placeholders_tree)) { - *error = StringPrintf("Not a valid \"%s\" element for key %s.", - kPlaceholdersKey, name_key.c_str()); + *error = base::StringPrintf("Not a valid \"%s\" element for key %s.", + kPlaceholdersKey, name_key.c_str()); return false; } @@ -179,15 +181,15 @@ bool ExtensionMessageBundle::GetPlaceholders(const DictionaryValue& name_tree, return BadKeyMessage(content_key, error); if (!placeholders_tree->GetDictionaryWithoutPathExpansion(content_key, &placeholder)) { - *error = StringPrintf("Invalid placeholder %s for key %s", - content_key.c_str(), - name_key.c_str()); + *error = base::StringPrintf("Invalid placeholder %s for key %s", + content_key.c_str(), + name_key.c_str()); return false; } std::string content; if (!placeholder->GetString(kContentKey, &content)) { - *error = StringPrintf("Invalid \"%s\" element for key %s.", - kContentKey, name_key.c_str()); + *error = base::StringPrintf("Invalid \"%s\" element for key %s.", + kContentKey, name_key.c_str()); return false; } (*placeholders)[StringToLowerASCII(content_key)] = content; @@ -253,10 +255,10 @@ bool ExtensionMessageBundle::ReplaceVariables( SubstitutionMap::const_iterator it = variables.find(StringToLowerASCII(var_name)); if (it == variables.end()) { - *error = StringPrintf("Variable %s%s%s used but not defined.", - var_begin_delimiter.c_str(), - var_name.c_str(), - var_end_delimiter.c_str()); + *error = base::StringPrintf("Variable %s%s%s used but not defined.", + var_begin_delimiter.c_str(), + var_name.c_str(), + var_end_delimiter.c_str()); return false; } diff --git a/chrome/common/extensions/extension_resource.cc b/chrome/common/extensions/extension_resource.cc index d3c5898..be22ec9 100644 --- a/chrome/common/extensions/extension_resource.cc +++ b/chrome/common/extensions/extension_resource.cc @@ -6,6 +6,7 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/thread_restrictions.h" PlatformThreadId ExtensionResource::file_thread_id_ = 0; @@ -45,6 +46,12 @@ const FilePath& ExtensionResource::GetFilePath() const { // static FilePath ExtensionResource::GetFilePathOnAnyThreadHack( const FilePath& extension_root, const FilePath& relative_path) { + // This function is a hack, and causes us to block the IO thread. + // Fixing + // http://code.google.com/p/chromium/issues/detail?id=59849 + // would also fix this. Suppress the error for now. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // We need to resolve the parent references in the extension_root // path on its own because IsParent doesn't like parent references. FilePath clean_extension_root(extension_root); diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index d2a7049..3f86bbd 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -77,7 +77,9 @@ TEST(ExtensionTest, InitFromValueInvalid) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; int error_code = 0; std::string error; @@ -303,7 +305,9 @@ TEST(ExtensionTest, InitFromValueValid) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; std::string error; DictionaryValue input_value; @@ -366,7 +370,9 @@ TEST(ExtensionTest, InitFromValueValidNameInRTL) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; std::string error; DictionaryValue input_value; @@ -403,27 +409,31 @@ TEST(ExtensionTest, GetResourceURLAndPath) { #elif defined(OS_POSIX) FilePath path(FILE_PATH_LITERAL("/foo")); #endif - Extension extension(path); DictionaryValue input_value; input_value.SetString(keys::kVersion, "1.0.0.0"); input_value.SetString(keys::kName, "my extension"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, NULL)); - - EXPECT_EQ(extension.url().spec() + "bar/baz.js", - Extension::GetResourceURL(extension.url(), "bar/baz.js").spec()); - EXPECT_EQ(extension.url().spec() + "baz.js", - Extension::GetResourceURL(extension.url(), "bar/../baz.js").spec()); - EXPECT_EQ(extension.url().spec() + "baz.js", - Extension::GetResourceURL(extension.url(), "../baz.js").spec()); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, input_value, false, NULL)); + EXPECT_TRUE(extension.get()); + + EXPECT_EQ(extension->url().spec() + "bar/baz.js", + Extension::GetResourceURL(extension->url(), "bar/baz.js").spec()); + EXPECT_EQ(extension->url().spec() + "baz.js", + Extension::GetResourceURL(extension->url(), + "bar/../baz.js").spec()); + EXPECT_EQ(extension->url().spec() + "baz.js", + Extension::GetResourceURL(extension->url(), "../baz.js").spec()); } TEST(ExtensionTest, LoadPageActionHelper) { #if defined(OS_WIN) - FilePath path(StringPrintf(L"c:\\extension")); + FilePath path(base::StringPrintf(L"c:\\extension")); #else - FilePath path(StringPrintf("/extension")); + FilePath path(base::StringPrintf("/extension")); #endif - Extension extension(path); + scoped_refptr<Extension> extension_ptr(new Extension(path, + Extension::INVALID)); + Extension& extension = *extension_ptr; std::string error_msg; scoped_ptr<ExtensionAction> action; DictionaryValue input; @@ -539,7 +549,7 @@ TEST(ExtensionTest, LoadPageActionHelper) { // LoadExtensionActionHelper expects the extension member |extension_url| // to be set. - extension.mutable_static_data_->extension_url = + extension.extension_url_ = GURL(std::string(chrome::kExtensionScheme) + chrome::kStandardSchemeSeparator + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/"); @@ -651,18 +661,19 @@ TEST(ExtensionTest, UpdateUrls) { #if defined(OS_WIN) // (Why %Iu below? This is the single file in the whole code base that // might make use of a WidePRIuS; let's not encourage any more.) - FilePath path(StringPrintf(L"c:\\extension%Iu", i)); + FilePath path(base::StringPrintf(L"c:\\extension%Iu", i)); #else - FilePath path(StringPrintf("/extension%" PRIuS, i)); + FilePath path(base::StringPrintf("/extension%" PRIuS, i)); #endif - Extension extension(path); std::string error; input_value.SetString(keys::kVersion, "1.0"); input_value.SetString(keys::kName, "Test"); input_value.SetString(keys::kUpdateURL, url.spec()); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, input_value, false, &error)); + EXPECT_TRUE(extension.get()) << error; } // Test some invalid update urls @@ -675,17 +686,18 @@ TEST(ExtensionTest, UpdateUrls) { #if defined(OS_WIN) // (Why %Iu below? This is the single file in the whole code base that // might make use of a WidePRIuS; let's not encourage any more.) - FilePath path(StringPrintf(L"c:\\extension%Iu", i)); + FilePath path(base::StringPrintf(L"c:\\extension%Iu", i)); #else - FilePath path(StringPrintf("/extension%" PRIuS, i)); + FilePath path(base::StringPrintf("/extension%" PRIuS, i)); #endif - Extension extension(path); std::string error; input_value.SetString(keys::kVersion, "1.0"); input_value.SetString(keys::kName, "Test"); input_value.SetString(keys::kUpdateURL, invalid[i]); - EXPECT_FALSE(extension.InitFromValue(input_value, false, &error)); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, input_value, false, &error)); + EXPECT_FALSE(extension.get()); EXPECT_TRUE(MatchPattern(error, errors::kInvalidUpdateURL)); } } @@ -714,8 +726,8 @@ TEST(ExtensionTest, MimeTypeSniffing) { EXPECT_EQ("application/octet-stream", result); } -static Extension* LoadManifest(const std::string& dir, - const std::string& test_file) { +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions") @@ -730,74 +742,70 @@ static Extension* LoadManifest(const std::string& dir, return NULL; } - scoped_ptr<Extension> extension(new Extension(path.DirName())); - EXPECT_TRUE(extension->InitFromValue( - *static_cast<DictionaryValue*>(result.get()), false, &error)) << error; - - return extension.release(); + scoped_refptr<Extension> extension = Extension::Create( + path.DirName(), Extension::INVALID, + *static_cast<DictionaryValue*>(result.get()), false, &error); + EXPECT_TRUE(extension) << error; + return extension; } TEST(ExtensionTest, EffectiveHostPermissions) { - scoped_ptr<Extension> extension; + scoped_refptr<Extension> extension; ExtensionExtent hosts; - extension.reset(LoadManifest("effective_host_permissions", "empty.json")); + extension = LoadManifest("effective_host_permissions", "empty.json"); EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size()); EXPECT_FALSE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", "one_host.json")); + extension = LoadManifest("effective_host_permissions", "one_host.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_FALSE(hosts.ContainsURL(GURL("https://www.google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "one_host_wildcard.json")); + extension = LoadManifest("effective_host_permissions", + "one_host_wildcard.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://foo.google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "two_hosts.json")); + extension = LoadManifest("effective_host_permissions", "two_hosts.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "https_not_considered.json")); + extension = LoadManifest("effective_host_permissions", + "https_not_considered.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("https://google.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "two_content_scripts.json")); + extension = LoadManifest("effective_host_permissions", + "two_content_scripts.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.reddit.com"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://news.ycombinator.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "all_hosts.json")); + extension = LoadManifest("effective_host_permissions", "all_hosts.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/"))); EXPECT_FALSE(hosts.ContainsURL(GURL("https://test/"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "all_hosts2.json")); + extension = LoadManifest("effective_host_permissions", "all_hosts2.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.ContainsURL(GURL("http://test/"))); EXPECT_TRUE(hosts.ContainsURL(GURL("http://www.google.com"))); EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); - extension.reset(LoadManifest("effective_host_permissions", - "all_hosts3.json")); + extension = LoadManifest("effective_host_permissions", "all_hosts3.json"); hosts = extension->GetEffectiveHostPermissions(); EXPECT_FALSE(hosts.ContainsURL(GURL("http://test/"))); EXPECT_TRUE(hosts.ContainsURL(GURL("https://test/"))); @@ -834,10 +842,10 @@ TEST(ExtensionTest, IsPrivilegeIncrease) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { - scoped_ptr<Extension> old_extension( + scoped_refptr<Extension> old_extension( LoadManifest("allow_silent_upgrade", std::string(kTests[i].base_name) + "_old.json")); - scoped_ptr<Extension> new_extension( + scoped_refptr<Extension> new_extension( LoadManifest("allow_silent_upgrade", std::string(kTests[i].base_name) + "_new.json")); @@ -912,11 +920,12 @@ TEST(ExtensionTest, ImageCaching) { // Initialize the Extension. std::string errors; - scoped_ptr<Extension> extension(new Extension(path)); DictionaryValue values; values.SetString(keys::kName, "test"); values.SetString(keys::kVersion, "0.1"); - ASSERT_TRUE(extension->InitFromValue(values, false, &errors)); + scoped_refptr<Extension> extension(Extension::Create( + path, Extension::INVALID, values, false, &errors)); + ASSERT_TRUE(extension.get()); // Create an ExtensionResource pointing at an icon. FilePath icon_relative_path(FILE_PATH_LITERAL("icon3.png")); @@ -997,10 +1006,11 @@ TEST(ExtensionTest, OldUnlimitedStoragePermission) { // Initialize the extension and make sure the permission for unlimited storage // is present. - Extension extension(extension_path); std::string errors; - EXPECT_TRUE(extension.InitFromValue(dictionary, false, &errors)); - EXPECT_TRUE(extension.HasApiPermission( + scoped_refptr<Extension> extension(Extension::Create( + extension_path, Extension::INVALID, dictionary, false, &errors)); + EXPECT_TRUE(extension.get()); + EXPECT_TRUE(extension->HasApiPermission( Extension::kUnlimitedStoragePermission)); } @@ -1036,8 +1046,8 @@ TEST(ExtensionTest, ApiPermissions) { { "tabs.getSelected", false}, }; - scoped_ptr<Extension> extension; - extension.reset(LoadManifest("empty_manifest", "empty.json")); + scoped_refptr<Extension> extension; + extension = LoadManifest("empty_manifest", "empty.json"); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { EXPECT_EQ(kTests[i].expect_success, diff --git a/chrome/common/extensions/extension_unpacker.cc b/chrome/common/extensions/extension_unpacker.cc index d431bcb..e270870 100644 --- a/chrome/common/extensions/extension_unpacker.cc +++ b/chrome/common/extensions/extension_unpacker.cc @@ -175,20 +175,21 @@ bool ExtensionUnpacker::Run() { // InitFromValue is allowed to generate a temporary id for the extension. // ANY CODE THAT FOLLOWS SHOULD NOT DEPEND ON THE CORRECT ID OF THIS // EXTENSION. - Extension extension(temp_install_dir_); std::string error; - if (!extension.InitFromValue(*parsed_manifest_, false, &error)) { + scoped_refptr<Extension> extension(Extension::Create( + temp_install_dir_, Extension::INVALID, *parsed_manifest_, false, &error)); + if (!extension.get()) { SetError(error); return false; } - if (!extension_file_util::ValidateExtension(&extension, &error)) { + if (!extension_file_util::ValidateExtension(extension.get(), &error)) { SetError(error); return false; } // Decode any images that the browser needs to display. - std::set<FilePath> image_paths = extension.GetBrowserImages(); + std::set<FilePath> image_paths = extension->GetBrowserImages(); for (std::set<FilePath>::iterator it = image_paths.begin(); it != image_paths.end(); ++it) { if (!AddDecodedImage(*it)) @@ -197,8 +198,8 @@ bool ExtensionUnpacker::Run() { // Parse all message catalogs (if any). parsed_catalogs_.reset(new DictionaryValue); - if (!extension.default_locale().empty()) { - if (!ReadAllMessageCatalogs(extension.default_locale())) + if (!extension->default_locale().empty()) { + if (!ReadAllMessageCatalogs(extension->default_locale())) return false; // Error was already reported. } @@ -288,10 +289,11 @@ bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) { std::string messages_file = WideToASCII(message_path.ToWStringHack()); if (error.empty()) { // If file is missing, Deserialize will fail with empty error. - SetError(StringPrintf("%s %s", errors::kLocalesMessagesFileMissing, - messages_file.c_str())); + SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing, + messages_file.c_str())); } else { - SetError(StringPrintf("%s: %s", messages_file.c_str(), error.c_str())); + SetError(base::StringPrintf("%s: %s", messages_file.c_str(), + error.c_str())); } return false; } diff --git a/chrome/common/extensions/url_pattern.cc b/chrome/common/extensions/url_pattern.cc index aa7e252..b6e09b7 100644 --- a/chrome/common/extensions/url_pattern.cc +++ b/chrome/common/extensions/url_pattern.cc @@ -58,14 +58,14 @@ URLPattern::URLPattern(int valid_schemes) URLPattern::URLPattern(int valid_schemes, const std::string& pattern) : valid_schemes_(valid_schemes), match_all_urls_(false), match_subdomains_(false) { - if (!Parse(pattern)) + if (PARSE_SUCCESS != Parse(pattern)) NOTREACHED() << "URLPattern is invalid: " << pattern; } URLPattern::~URLPattern() { } -bool URLPattern::Parse(const std::string& pattern) { +URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { // Special case pattern to match every valid URL. if (pattern == kAllUrlsPattern) { match_all_urls_ = true; @@ -73,40 +73,51 @@ bool URLPattern::Parse(const std::string& pattern) { scheme_ = "*"; host_.clear(); path_ = "/*"; - return true; + return PARSE_SUCCESS; + } + + // Parse out the scheme. + size_t scheme_end_pos = pattern.find(chrome::kStandardSchemeSeparator); + bool has_standard_scheme_separator = true; + + // Some urls also use ':' alone as the scheme separator. + if (scheme_end_pos == std::string::npos) { + scheme_end_pos = pattern.find(':'); + has_standard_scheme_separator = false; } - size_t scheme_end_pos = pattern.find(":"); if (scheme_end_pos == std::string::npos) - return false; + return PARSE_ERROR_MISSING_SCHEME_SEPARATOR; if (!SetScheme(pattern.substr(0, scheme_end_pos))) - return false; + return PARSE_ERROR_INVALID_SCHEME; - std::string separator = - pattern.substr(scheme_end_pos, strlen(chrome::kStandardSchemeSeparator)); - if (separator == chrome::kStandardSchemeSeparator) - scheme_end_pos += strlen(chrome::kStandardSchemeSeparator); - else - scheme_end_pos += 1; + bool standard_scheme = IsStandardScheme(scheme_); + if (standard_scheme != has_standard_scheme_separator) + return PARSE_ERROR_WRONG_SCHEME_SEPARATOR; // Advance past the scheme separator. - size_t host_start_pos = scheme_end_pos; - if (host_start_pos >= pattern.length()) - return false; + scheme_end_pos += + (standard_scheme ? strlen(chrome::kStandardSchemeSeparator) : 1); + if (scheme_end_pos >= pattern.size()) + return PARSE_ERROR_EMPTY_HOST; // Parse out the host and path. + size_t host_start_pos = scheme_end_pos; size_t path_start_pos = 0; - bool standard_scheme = IsStandardScheme(scheme_); - // File URLs are special because they have no host. if (scheme_ == chrome::kFileScheme || !standard_scheme) { path_start_pos = host_start_pos; } else { size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); + + // Host is required. + if (host_start_pos == host_end_pos) + return PARSE_ERROR_EMPTY_HOST; + if (host_end_pos == std::string::npos) - return false; + return PARSE_ERROR_EMPTY_PATH; host_ = pattern.substr(host_start_pos, host_end_pos - host_start_pos); @@ -124,14 +135,14 @@ bool URLPattern::Parse(const std::string& pattern) { // done as a convenience to developers who might otherwise be confused and // think '*' works as a glob in the host. if (host_.find('*') != std::string::npos) - return false; + return PARSE_ERROR_INVALID_HOST_WILDCARD; path_start_pos = host_end_pos; } path_ = pattern.substr(path_start_pos); - return true; + return PARSE_SUCCESS; } bool URLPattern::SetScheme(const std::string& scheme) { diff --git a/chrome/common/extensions/url_pattern.h b/chrome/common/extensions/url_pattern.h index 832673c..72810ba 100644 --- a/chrome/common/extensions/url_pattern.h +++ b/chrome/common/extensions/url_pattern.h @@ -91,13 +91,20 @@ class URLPattern { SCHEME_ALL = -1, }; + // Error codes returned from Parse(). + enum ParseResult { + PARSE_SUCCESS, + PARSE_ERROR_MISSING_SCHEME_SEPARATOR, + PARSE_ERROR_INVALID_SCHEME, + PARSE_ERROR_WRONG_SCHEME_SEPARATOR, + PARSE_ERROR_EMPTY_HOST, + PARSE_ERROR_INVALID_HOST_WILDCARD, + PARSE_ERROR_EMPTY_PATH, + }; + // The <all_urls> string pattern. static const char kAllUrlsPattern[]; - // Note: don't use this directly. This exists so URLPattern can be used - // with STL containers. - URLPattern(); - // Construct an URLPattern with the given set of allowable schemes. See // valid_schemes_ for more info. explicit URLPattern(int valid_schemes); @@ -134,9 +141,11 @@ class URLPattern { bool match_all_urls() const { return match_all_urls_; } void set_match_all_urls(bool val) { match_all_urls_ = val; } - // Initializes this instance by parsing the provided string. On failure, the - // instance will have some intermediate values and is in an invalid state. - bool Parse(const std::string& pattern_str); + // Initializes this instance by parsing the provided string. Returns + // URLPattern::PARSE_SUCCESS on success, or an error code otherwise. On + // failure, this instance will have some intermediate values and is in an + // invalid state. + ParseResult Parse(const std::string& pattern_str); // Sets the scheme for pattern matches. This can be a single '*' if the // pattern matches all valid schemes (as defined by the valid_schemes_ @@ -192,6 +201,21 @@ class URLPattern { }; private: + friend class std::vector<URLPattern>; + +// See clang bug: http://llvm.org/bugs/show_bug.cgi?id=8479 +#if defined(__clang__) + public: +#endif + + // Note: don't use this directly. This exists so URLPattern can be used + // with STL containers. + URLPattern(); + +#if defined(__clang__) + private: +#endif + // A bitmask containing the schemes which are considered valid for this // pattern. Parse() uses this to decide whether a pattern contains a valid // scheme. MatchesScheme uses this to decide whether a wildcard scheme_ diff --git a/chrome/common/extensions/url_pattern_unittest.cc b/chrome/common/extensions/url_pattern_unittest.cc index a9ef21e..e0be087 100644 --- a/chrome/common/extensions/url_pattern_unittest.cc +++ b/chrome/common/extensions/url_pattern_unittest.cc @@ -17,29 +17,34 @@ static const int kAllSchemes = URLPattern::SCHEME_CHROMEUI; TEST(URLPatternTest, ParseInvalid) { - const char* kInvalidPatterns[] = { - "http", // no scheme - "http:", - "http:/", - "http://", // no path separator - "http://foo", // no path separator - "http://*foo/bar", // not allowed as substring of host component - "http://foo.*.bar/baz", // must be first component - "http:/bar", // scheme separator not found - "foo://*", // invalid scheme - "chrome-extension://*/*", // we don't support chrome extension URLs + const struct { + const char* pattern; + URLPattern::ParseResult expected_result; + } kInvalidPatterns[] = { + { "http", URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR }, + { "http:", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "http:/", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "about://", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "http://", URLPattern::PARSE_ERROR_EMPTY_HOST }, + { "http:///", URLPattern::PARSE_ERROR_EMPTY_HOST }, + { "http://*foo/bar", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, + { "http://foo.*.bar/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, + { "http:/bar", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, + { "http://bar", URLPattern::PARSE_ERROR_EMPTY_PATH }, }; - for (size_t i = 0; i < arraysize(kInvalidPatterns); ++i) { - URLPattern pattern; - EXPECT_FALSE(pattern.Parse(kInvalidPatterns[i])) << kInvalidPatterns[i]; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInvalidPatterns); ++i) { + URLPattern pattern(URLPattern::SCHEME_ALL); + EXPECT_EQ(kInvalidPatterns[i].expected_result, + pattern.Parse(kInvalidPatterns[i].pattern)) + << kInvalidPatterns[i].pattern; } }; // all pages for a given scheme TEST(URLPatternTest, Match1) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://*/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -55,7 +60,7 @@ TEST(URLPatternTest, Match1) { // all domains TEST(URLPatternTest, Match2) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("https://*/foo*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("https://*/foo*")); EXPECT_EQ("https", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -70,7 +75,8 @@ TEST(URLPatternTest, Match2) { // subdomains TEST(URLPatternTest, Match3) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://*.google.com/foo*bar")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*.google.com/foo*bar")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("google.com", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -86,7 +92,8 @@ TEST(URLPatternTest, Match3) { // glob escaping TEST(URLPatternTest, Match5) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("file:///foo?bar\\*baz")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("file:///foo?bar\\*baz")); EXPECT_EQ("file", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -99,7 +106,8 @@ TEST(URLPatternTest, Match5) { // ip addresses TEST(URLPatternTest, Match6) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://127.0.0.1/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://127.0.0.1/*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("127.0.0.1", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -111,7 +119,8 @@ TEST(URLPatternTest, Match6) { // subdomain matching with ip addresses TEST(URLPatternTest, Match7) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("http://*.0.0.1/*")); // allowed, but useless + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*.0.0.1/*")); // allowed, but useless EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("0.0.1", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -126,7 +135,8 @@ TEST(URLPatternTest, Match8) { URLPattern pattern(kAllSchemes); // The below is the ASCII encoding of the following URL: // http://*.\xe1\x80\xbf/a\xc2\x81\xe1* - EXPECT_TRUE(pattern.Parse("http://*.xn--gkd/a%C2%81%E1*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*.xn--gkd/a%C2%81%E1*")); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("xn--gkd", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -141,7 +151,8 @@ TEST(URLPatternTest, Match8) { // chrome:// TEST(URLPatternTest, Match9) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("chrome://favicon/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("chrome://favicon/*")); EXPECT_EQ("chrome", pattern.scheme()); EXPECT_EQ("favicon", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -155,7 +166,7 @@ TEST(URLPatternTest, Match9) { // *:// TEST(URLPatternTest, Match10) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("*://*/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("*://*/*")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); EXPECT_FALSE(pattern.MatchesScheme("chrome")); @@ -172,7 +183,7 @@ TEST(URLPatternTest, Match10) { // <all_urls> TEST(URLPatternTest, Match11) { URLPattern pattern(kAllSchemes); - EXPECT_TRUE(pattern.Parse("<all_urls>")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("<all_urls>")); EXPECT_TRUE(pattern.MatchesScheme("chrome")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); @@ -188,7 +199,7 @@ TEST(URLPatternTest, Match11) { // SCHEME_ALL matches all schemes. TEST(URLPatternTest, Match12) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse("<all_urls>")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("<all_urls>")); EXPECT_TRUE(pattern.MatchesScheme("chrome")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); @@ -226,7 +237,8 @@ static const struct MatchPatterns { TEST(URLPatternTest, Match13) { for (size_t i = 0; i < arraysize(kMatch13UrlPatternTestCases); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse(kMatch13UrlPatternTestCases[i].pattern)) + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse(kMatch13UrlPatternTestCases[i].pattern)) << " while parsing " << kMatch13UrlPatternTestCases[i].pattern; EXPECT_TRUE(pattern.MatchesUrl( GURL(kMatch13UrlPatternTestCases[i].matches))) @@ -235,7 +247,7 @@ TEST(URLPatternTest, Match13) { // Negative test. URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse("data:*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("data:*")); EXPECT_FALSE(pattern.MatchesUrl(GURL("about:blank"))); }; @@ -259,7 +271,8 @@ static const struct GetAsStringPatterns { TEST(URLPatternTest, GetAsString) { for (size_t i = 0; i < arraysize(kGetAsStringTestCases); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_TRUE(pattern.Parse(kGetAsStringTestCases[i].pattern)); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse(kGetAsStringTestCases[i].pattern)); EXPECT_STREQ(kGetAsStringTestCases[i].pattern, pattern.GetAsString().c_str()); } diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index cea7cf6..f71de5c 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -195,7 +195,7 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { std::string pattern_str; URLPattern pattern(valid_schemes); CHECK(pickle.ReadString(iter, &pattern_str)); - CHECK(pattern.Parse(pattern_str)); + CHECK(URLPattern::PARSE_SUCCESS == pattern.Parse(pattern_str)); url_patterns_.push_back(pattern); } diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc index ab3e218..949ad1a 100644 --- a/chrome/common/extensions/user_script_unittest.cc +++ b/chrome/common/extensions/user_script_unittest.cc @@ -71,7 +71,7 @@ TEST(UserScriptTest, Match5) { TEST(UserScriptTest, Match6) { URLPattern pattern(kAllSchemes); - ASSERT_TRUE(pattern.Parse("http://*/foo*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/foo*")); UserScript script; script.add_url_pattern(pattern); @@ -86,7 +86,8 @@ TEST(UserScriptTest, UrlPatternGlobInteraction) { UserScript script; URLPattern pattern(kAllSchemes); - ASSERT_TRUE(pattern.Parse("http://www.google.com/*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://www.google.com/*")); script.add_url_pattern(pattern); script.add_glob("*bar*"); @@ -114,8 +115,8 @@ TEST(UserScriptTest, UrlPatternGlobInteraction) { TEST(UserScriptTest, Pickle) { URLPattern pattern1(kAllSchemes); URLPattern pattern2(kAllSchemes); - ASSERT_TRUE(pattern1.Parse("http://*/foo*")); - ASSERT_TRUE(pattern2.Parse("http://bar/baz*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://*/foo*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse("http://bar/baz*")); UserScript script1; script1.js_scripts().push_back(UserScript::File( |
