From 29fb8c50c7714582d0c7d912703f81909825b674 Mon Sep 17 00:00:00 2001 From: Joel Spadin Date: Sun, 4 Sep 2016 20:52:33 -0500 Subject: [PATCH 1/2] Fix strict null checking in chrome Fixed everywhere the Chrome API documentation said something could be null but the types didn't allow it under strict null checking. --- chrome/chrome-tests.ts | 47 +++++++++++++++++++++++++++++++++++------ chrome/index.d.ts | 48 ++++++++++++------------------------------ 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/chrome/chrome-tests.ts b/chrome/chrome-tests.ts index 02273f4385..e7efd0b604 100644 --- a/chrome/chrome-tests.ts +++ b/chrome/chrome-tests.ts @@ -254,11 +254,44 @@ function testOptionsPage() { }); } -chrome.storage.sync.get("myKey", function (loadedData) { - var myValue: { x: number } = loadedData["myKey"]; -}); +// https://developer.chrome.com/extensions/storage#type-StorageArea +function testStorage() { + function getCallback(loadedData: { [key: string]: any; }) { + var myValue: { x: number } = loadedData["myKey"]; + } -chrome.storage.onChanged.addListener(function (changes) { - var myNewValue: { x: number } = changes["myKey"].newValue; - var myOldValue: { x: number } = changes["myKey"].oldValue; -}); + chrome.storage.sync.get(getCallback); + chrome.storage.sync.get("myKey", getCallback); + chrome.storage.sync.get(["myKey", "myKey2"], getCallback); + chrome.storage.sync.get({ foo: 1, bar: 2 }, getCallback); + chrome.storage.sync.get(null, getCallback); + + function getBytesInUseCallback(bytesInUse: number) { + console.log(bytesInUse); + } + + chrome.storage.sync.getBytesInUse(getBytesInUseCallback); + chrome.storage.sync.getBytesInUse("myKey", getBytesInUseCallback); + chrome.storage.sync.getBytesInUse(["myKey", "myKey2"], getBytesInUseCallback); + chrome.storage.sync.getBytesInUse(null, getBytesInUseCallback); + + function doneCallback() { + console.log("done"); + } + + chrome.storage.sync.set({ foo: 1, bar: 2}); + chrome.storage.sync.set({ foo: 1, bar: 2}, doneCallback); + + chrome.storage.sync.remove("myKey"); + chrome.storage.sync.remove("myKey", doneCallback); + chrome.storage.sync.remove(["myKey", "myKey2"]); + chrome.storage.sync.remove(["myKey", "myKey2"], doneCallback); + + chrome.storage.sync.clear(); + chrome.storage.sync.clear(doneCallback); + + chrome.storage.onChanged.addListener(function (changes) { + var myNewValue: { x: number } = changes["myKey"].newValue; + var myOldValue: { x: number } = changes["myKey"].oldValue; + }); +} diff --git a/chrome/index.d.ts b/chrome/index.d.ts index 936616e118..af3dbdc15d 100644 --- a/chrome/index.d.ts +++ b/chrome/index.d.ts @@ -1242,7 +1242,7 @@ declare namespace chrome.cookies { * function( Cookie cookie) {...}; * Optional parameter cookie: Contains details about the cookie that's been set. If setting failed for any reason, this will be "null", and "chrome.runtime.lastError" will be set. */ - export function set(details: SetDetails, callback?: (cookie?: Cookie) => void): void; + export function set(details: SetDetails, callback?: (cookie: Cookie | null) => void): void; /** * Deletes a cookie by name. * @param details Information to identify the cookie to remove. @@ -1257,7 +1257,7 @@ declare namespace chrome.cookies { * function( Cookie cookie) {...}; * Parameter cookie: Contains details about the cookie. This parameter is null if no such cookie was found. */ - export function get(details: Details, callback: (cookie?: Cookie) => void): void; + export function get(details: Details, callback: (cookie: Cookie | null) => void): void; /** Fired when a cookie is set or removed. As a special case, note that updating a cookie's properties is implemented as a two step process: the cookie to be updated is first removed entirely, generating a notification with "cause" of "overwrite" . Afterwards, a new cookie is written with the updated values, generating a second notification with "cause" "explicit". */ var onChanged: CookieChangedEvent; @@ -1755,7 +1755,7 @@ declare namespace chrome.devtools.panels { * @param tooltipText Text shown as a tooltip when user hovers the mouse over the button. * @param disabled Whether the button is disabled. */ - update(iconPath?: string, tooltipText?: string, disabled?: boolean): void; + update(iconPath?: string | null, tooltipText?: string | null, disabled?: boolean | null): void; /** Fired when the button is clicked. */ onClicked: ButtonClickedEvent; } @@ -2458,7 +2458,7 @@ declare namespace chrome.extension { var lastError: LastError; /** Returns the JavaScript 'window' object for the background page running inside the current extension. Returns null if the extension has no background page. */ - export function getBackgroundPage(): Window; + export function getBackgroundPage(): Window | null; /** * Converts a relative path within an extension install directory to a fully-qualified URL. * @param path A path to a resource within an extension expressed relative to its install directory. @@ -2545,7 +2545,7 @@ declare namespace chrome.fileBrowserHandler { interface SelectionResult { /** Optional. Selected file entry. It will be null if a file hasn't been selected. */ - entry?: Object; + entry?: Object | null; /** Whether the file has been selected. */ success: boolean; } @@ -4633,7 +4633,7 @@ declare namespace chrome.platformKeys { * function(object publicKey, object privateKey) {...}; * Optional parameter privateKey: Might be null if this extension does not have access to it. */ - export function getKeyPair(certificate: ArrayBuffer, parameters: Object, callback: (publicKey: CryptoKey, privateKey?: CryptoKey) => void): void; + export function getKeyPair(certificate: ArrayBuffer, parameters: Object, callback: (publicKey: CryptoKey, privateKey: CryptoKey | null) => void): void; /** An implementation of WebCrypto's SubtleCrypto that allows crypto operations on keys of client certificates that are available to this extension. */ export function subtleCrypto(): SubtleCrypto; /** @@ -5476,18 +5476,11 @@ declare namespace chrome.storage { getBytesInUse(callback: (bytesInUse: number) => void): void; /** * Gets the amount of space (in bytes) being used by one or more items. - * @param key A single key to get the total usage for. Pass in null to get the total usage of all of storage. + * @param keys A single key or list of keys to get the total usage for. An empty list will return 0. Pass in null to get the total usage of all of storage. * @param callback Callback with the amount of space being used by storage, or on failure (in which case runtime.lastError will be set). * Parameter bytesInUse: Amount of space being used in storage, in bytes. */ - getBytesInUse(key: string, callback: (bytesInUse: number) => void): void; - /** - * Gets the amount of space (in bytes) being used by one or more items. - * @param keys A list of keys to get the total usage for. An empty list will return 0. Pass in null to get the total usage of all of storage. - * @param callback Callback with the amount of space being used by storage, or on failure (in which case runtime.lastError will be set). - * Parameter bytesInUse: Amount of space being used in storage, in bytes. - */ - getBytesInUse(keys: string[], callback: (bytesInUse: number) => void): void; + getBytesInUse(keys: string | string[] | null, callback: (bytesInUse: number) => void): void; /** * Removes all items from storage. * @param callback Optional. @@ -5524,25 +5517,12 @@ declare namespace chrome.storage { get(callback: (items: { [key: string]: any }) => void): void; /** * Gets one or more items from storage. - * @param key A single key to get. Pass in null to get the entire contents of storage. + * @param keys A single key to get, list of keys to get, or a dictionary specifying default values. + * An empty list or object will return an empty result object. Pass in null to get the entire contents of storage. * @param callback Callback with storage items, or on failure (in which case runtime.lastError will be set). * Parameter items: Object with items in their key-value mappings. */ - get(key: string, callback: (items: { [key: string]: any }) => void): void; - /** - * Gets one or more items from storage. - * @param keys A list of keys to get. An empty list or object will return an empty result object. Pass in null to get the entire contents of storage. - * @param callback Callback with storage items, or on failure (in which case runtime.lastError will be set). - * Parameter items: Object with items in their key-value mappings. - */ - get(keys: string[], callback: (items: { [key: string]: any }) => void): void; - /** - * Gets one or more items from storage. - * @param keys A dictionary specifying default values. Pass in null to get the entire contents of storage. - * @param callback Callback with storage items, or on failure (in which case runtime.lastError will be set). - * Parameter items: Object with items in their key-value mappings. - */ - get(keys: Object, callback: (items: { [key: string]: any }) => void): void; + get(keys: string | string[] | Object | null, callback: (items: { [key: string]: any }) => void): void; } interface StorageChange { @@ -5816,7 +5796,7 @@ declare namespace chrome.tabCapture { * @param options Configures the returned media stream. * @param callback Callback with either the tab capture stream or null. */ - export function capture(options: CaptureOptions, callback: (stream: MediaStream) => void): void; + export function capture(options: CaptureOptions, callback: (stream: MediaStream | null) => void): void; /** * Returns a list of tabs that have requested capture or are being captured, i.e. status != stopped and status != error. This allows extensions to inform the user that there is an existing tab capture that would prevent a new tab capture from succeeding (or to prevent redundant requests for the same tab). * @param callback Callback invoked with CaptureInfo[] for captured tabs. @@ -7107,14 +7087,14 @@ declare namespace chrome.webNavigation { * @param callback * Optional parameter details: Information about the requested frame, null if the specified frame ID and/or tab ID are invalid. */ - export function getFrame(details: GetFrameDetails, callback: (details?: GetFrameResultDetails) => void): void; + export function getFrame(details: GetFrameDetails, callback: (details: GetFrameResultDetails | null) => void): void; /** * Retrieves information about all frames of a given tab. * @param details Information about the tab to retrieve all frames from. * @param callback * Optional parameter details: A list of frames in the given tab, null if the specified tab ID is invalid. */ - export function getAllFrames(details: GetAllFrameDetails, callback: (details?: GetAllFrameResultDetails[]) => void): void; + export function getAllFrames(details: GetAllFrameDetails, callback: (details: GetAllFrameResultDetails[] | null) => void): void; /** Fired when the reference fragment of a frame was updated. All future events for that frame will use the updated URL. */ var onReferenceFragmentUpdated: WebNavigationTransitionalEvent; From 989cbc1b80fb2cd5e15994c2d8ffa92f81985684 Mon Sep 17 00:00:00 2001 From: Joel Spadin Date: Sun, 4 Sep 2016 21:07:33 -0500 Subject: [PATCH 2/2] Fix Chrome tests with strict null checking enabled Enabled strict null checking for Chrome tests and fixed some issues with the existing tests. The example code at https://developer.chrome.com/extensions/examples/api/bookmarks/basic/popup.js would use an uninitialized 'span' variable if bookmarkNode.title was empty. Since I have no idea what it was supposed to do in that case, I just moved the declaration of span up a block. --- chrome/chrome-tests.ts | 8 ++++---- chrome/tsconfig.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chrome/chrome-tests.ts b/chrome/chrome-tests.ts index e7efd0b604..7788397a4c 100644 --- a/chrome/chrome-tests.ts +++ b/chrome/chrome-tests.ts @@ -26,6 +26,7 @@ function bookmarksExample() { return list; } function dumpNode(bookmarkNode, query) { + var span = $(''); if (bookmarkNode.title) { if (query && !bookmarkNode.children) { if (String(bookmarkNode.title).indexOf(query) == -1) { @@ -42,7 +43,6 @@ function bookmarksExample() { anchor.click(function () { chrome.tabs.create({ url: bookmarkNode.url }); }); - var span = $(''); var options = bookmarkNode.children ? $('[Add]') : $('[Edit { + chrome.proxy.settings.get({ incognito: true }, (details) => { var val = details.value; var level: string = details.levelOfControl; - var incognito: boolean = details.incognitoSpecific; + var incognito: boolean = details.incognitoSpecific!; }); // bare minimum set call diff --git a/chrome/tsconfig.json b/chrome/tsconfig.json index f40142d6ae..120b5d78dc 100644 --- a/chrome/tsconfig.json +++ b/chrome/tsconfig.json @@ -3,7 +3,7 @@ "module": "commonjs", "target": "es6", "noImplicitAny": false, - "strictNullChecks": false, + "strictNullChecks": true, "baseUrl": "../", "typeRoots": [ "../"