diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index 602b1c1773..461649c53c 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -53,8 +53,7 @@ jobs: - name: Setup Glean Dependency shell: bash run: | - pip3 install "glean_parser==3.5" - pip3 install pyyaml + pip3 install -r requirements.txt - name: Compilation run: | diff --git a/.github/workflows/functional_tests.yaml b/.github/workflows/functional_tests.yaml index ea97f439df..ba941bfb33 100644 --- a/.github/workflows/functional_tests.yaml +++ b/.github/workflows/functional_tests.yaml @@ -77,8 +77,7 @@ jobs: run: | export PATH=/opt/qt515/bin:$PATH - pip3 install glean_parser - pip3 install pyyaml + pip3 install -r requirements.txt git submodule update --remote --depth 1 i18n python3 scripts/importLanguages.py @@ -120,7 +119,7 @@ jobs: run: | sudo dpkg -i build/archive/*.deb sudo apt install --no-upgrade firefox xvfb -y - pip3 install flask + pip3 install -r requirements.txt npm install dotenv npm install selenium-webdriver npm install mocha diff --git a/.github/workflows/linters.yaml b/.github/workflows/linters.yaml index e43a8d6a5d..b470517019 100644 --- a/.github/workflows/linters.yaml +++ b/.github/workflows/linters.yaml @@ -34,13 +34,11 @@ jobs: - name: Install Python dependencies run: | - pip install lxml + pip3 install -r requirements.txt - name: Generating glean samples shell: bash run: | - pip3 install glean_parser - pip3 install pyyaml python3 scripts/generate_glean.py - name: Importing translation files diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index 28ae42742f..7f87fb1878 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -21,8 +21,7 @@ jobs: shell: bash run: | sudo apt-get install golang debhelper -y - pip3 install "glean_parser==3.5" - pip3 install pyyaml + pip3 install -r requirements.txt - name: Build source bundle shell: bash diff --git a/.github/workflows/macos-build.yaml b/.github/workflows/macos-build.yaml index fa7f70a5b6..f0cf9de0e3 100644 --- a/.github/workflows/macos-build.yaml +++ b/.github/workflows/macos-build.yaml @@ -44,8 +44,7 @@ jobs: - name: Generating glean samples shell: bash run: | - pip3 install "glean_parser==3.5" - pip3 install pyyaml + pip3 install -r requirements.txt python3 scripts/generate_glean.py - name: Importing translation files @@ -193,8 +192,7 @@ jobs: - name: Generating glean samples shell: bash run: | - pip3 install "glean_parser==3.5" - pip3 install pyyaml + pip3 install -r requirements.txt python3 scripts/generate_glean.py - name: Importing translation files diff --git a/.github/workflows/screencapture.yaml b/.github/workflows/screencapture.yaml index 7dea6fc07e..cbd2de9db8 100644 --- a/.github/workflows/screencapture.yaml +++ b/.github/workflows/screencapture.yaml @@ -40,8 +40,7 @@ jobs: - name: Generating glean samples shell: bash run: | - pip3 install glean_parser - pip3 install pyyaml + pip3 install -r requirements.txt python3 scripts/generate_glean.py - name: Importing translation files diff --git a/.github/workflows/test_coverage.yaml b/.github/workflows/test_coverage.yaml index f83912790e..15cac78f27 100644 --- a/.github/workflows/test_coverage.yaml +++ b/.github/workflows/test_coverage.yaml @@ -33,8 +33,7 @@ jobs: - name: Generating glean samples shell: bash run: | - pip3 install glean_parser - pip3 install pyyaml + pip3 install -r requirements.txt python3 scripts/generate_glean.py - name: Importing translation files diff --git a/.github/workflows/wasm.yaml b/.github/workflows/wasm.yaml index 03079db695..e1eaee2c5a 100644 --- a/.github/workflows/wasm.yaml +++ b/.github/workflows/wasm.yaml @@ -31,12 +31,11 @@ jobs: python3 -m pip install aqtinstall python3 -m aqt install --outputdir /opt 5.15.0 linux desktop wasm_32 -m qtcharts - - name: Install glean depedencies + - name: Install python dependencies shell: bash run: | - pip3 install "glean_parser==3.5" - pip3 install pyyaml - + pip install -r requirements.txt + - name: Setup emsdk uses: mymindstorm/setup-emsdk@v7 with: diff --git a/.github/workflows/windows-build.yaml b/.github/workflows/windows-build.yaml index d0682974d1..37ff724f76 100644 --- a/.github/workflows/windows-build.yaml +++ b/.github/workflows/windows-build.yaml @@ -47,8 +47,7 @@ jobs: - name: Install glean depedencies shell: bash run: | - pip3 install "glean_parser==3.5" - pip3 install pyyaml + pip3 install -r requirements.txt - name: Adding msbuild to PATH uses: ilammy/msvc-dev-cmd@v1 diff --git a/README.md b/README.md index 5feb89a649..6348a8c1a7 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,9 @@ following dependencies: - wireguard-tools >=1.0.20200513 - resolvconf >= 1.82 - golang >= 1.13 +- python >= 3.6 -Python3 (pip) depedencies: - -- glean_parser==3.5 -- pyyaml +For Python depedencies see requirements.txt #### QT5 @@ -152,8 +150,7 @@ This step needs to be updated each time XCode updates. ``` 4. Install python3 dependencies: ``` - $ pip3 install 'glean_parser==3.5' - $ pip3 install pyyaml + $ pip3 install -r requirements.txt --user ``` 5. Copy `xcode.xconfig.template` to `xcode.xconfig` ``` @@ -212,8 +209,7 @@ Once Qt has been installed, the IOS procedure is similar to the macOS one: 3. Install python3 dependencies: ``` - $ pip3 install 'glean_parser==3.5' - $ pip3 install pyyaml + $ pip3 install -r requirements.txt --user ``` 4. Copy `xcode.xconfig.template` to `xcode.xconfig` @@ -259,8 +255,7 @@ Add the Adjust SDK token with `-a | --adjust ` 5. Install python3 dependencies: ``` - $ pip3 install 'glean_parser==3.5' - $ pip3 install pyyaml + $ pip3 install -r requirements.txt --user ``` 6. Build the apk @@ -286,7 +281,7 @@ The dependencies are: 2. nasm: https://www.nasm.us/ 3. python3: https://www.python.org/downloads/windows/ 4. visual studio 2019: https://visualstudio.microsoft.com/vs/ -5. Install python3 dependencies (pip install "glean_parser==3.5" pyyaml) +5. Install python3 dependencies (pip3 install -r requirements.txt --user) Openssl can be obtained from here: https://www.openssl.org/source/ Qt5.15 can be obtained from: https://download.qt.io/archive/qt/5.15/5.15.1/single/qt-everywhere-src-5.15.1.tar.xz @@ -306,6 +301,19 @@ When running MozillaVPN, go to http://localhost:8766 to view the inspector. From the inspector, type `help` to see the list of available commands. +## Glean + +When the client is built in debug mode, pings will have the applicationId `MozillaVPN-debug`. Additionally, ping contents will be logged to the client logs and will also be sent to the +[glean debug viewer](https://debug-ping-preview.firebaseapp.com/pings/MozillaVPN) (login required) where they are retained for 3 weeks. + +More info on debug view in [glean docs](https://mozilla.github.io/glean/book/user/debugging/index.html). + +When the client is in staging mode, but not debug mode, pings will have the applicationId `MozillaVPN-staging` which allows for filtering between staging and production pings. + +#### A note on glean embedding + +Qt only accepts `major.minor` versions for importing. So if, for example, you're embedding glean v0.21.2 then it will still, for Qt's purpose, be v0.21. + ## Bug report Please file bugs here: https://github.com/mozilla-mobile/mozilla-vpn-client/issues diff --git a/glean/org/mozilla/Glean/glean.js b/glean/org/mozilla/Glean/glean.js index 0c576fbe88..59e42af0fe 100644 --- a/glean/org/mozilla/Glean/glean.js +++ b/glean/org/mozilla/Glean/glean.js @@ -5,7 +5,7 @@ // This file is the actual entry point file for Glean.js in QML, that users will interact with. // // I was not able to figure out a way to simply use the Webpack generated file to -// be the entry point in Qt, because of the unusual syntax allowed in Qt Javascript. +// be the entry point in Qt, because of the unusual syntax allowed in Qt JavaScript. // Thus, we compile the Glean.js library normaly into the `glean.lib.js` file and then // we have this file which interacts opaquely with the Webpack generated one. // @@ -13,8 +13,11 @@ .pragma library +.import QtQuick.LocalStorage 2.0 as LocalStorage .import "glean.lib.js" as Glean +const ErrorType = Glean.Glean.default.ErrorType; + /** * Initialize Glean. This method should only be called once, subsequent calls will be no-op. * @@ -90,4 +93,33 @@ function setSourceTags(value) { Glean.Glean.default.setSourceTags(value); } +/** + * Finishes executing any ongoing tasks and shuts down Glean. + * + * This will attempt to send pending pings before resolving. + * + * @returns A promise which resolves once shutdown is complete. + */ +function shutdown() { + return Glean.Glean.default.shutdown(); +} + +/** + * Test-only API** + * + * Resets the Glean singleton to its initial state and re-initializes it. + * + * TODO: Only allow this function to be called on test mode (depends on Bug 1682771). + * + * @param applicationId The application ID (will be sanitized during initialization). + * @param uploadEnabled Determines whether telemetry is enabled. + * If disabled, all persisted metrics, events and queued pings (except + * first_run_date) are cleared. Default to `true`. + * @param config Glean configuration options. + * @returns A promise that resolves when the initialization is complete. + */ +function testResetGlean(applicationId, uploadEnabled, config) { + return Glean.Glean.default.testResetGlean(applicationId, uploadEnabled, config); +} + const _private = Glean.Glean.default._private; diff --git a/glean/org/mozilla/Glean/glean.lib.js b/glean/org/mozilla/Glean/glean.lib.js index 70f47d9460..94691eac09 100644 --- a/glean/org/mozilla/Glean/glean.lib.js +++ b/glean/org/mozilla/Glean/glean.lib.js @@ -1 +1 @@ -var Glean;(()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{e.r(t),e.d(t,{default:()=>Gt});var n,r="0.15.0",i="glean_ping_info",o="glean_client_info",a="c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0",s="deletion-request",u=new Uint8Array(16);function c(){if(!n&&!(n="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return n(u)}const l=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,f=function(e){return"string"==typeof e&&l.test(e)};for(var p=[],h=0;h<256;++h)p.push((h+256).toString(16).substr(1));const d=function(e,t,n){var r=(e=e||{}).random||(e.rng||c)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var i=0;i<16;++i)t[n+i]=r[i];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(p[e[t+0]]+p[e[t+1]]+p[e[t+2]]+p[e[t+3]]+"-"+p[e[t+4]]+p[e[t+5]]+"-"+p[e[t+6]]+p[e[t+7]]+"-"+p[e[t+8]]+p[e[t+9]]+"-"+p[e[t+10]]+p[e[t+11]]+p[e[t+12]]+p[e[t+13]]+p[e[t+14]]+p[e[t+15]]).toLowerCase();if(!f(n))throw TypeError("Stringified UUID is invalid");return n}(r)};var b,v,y=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},g=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&(this.state=2,this.currentJob=this.execute(),this.currentJob.then((function(){e.currentJob=void 0,2===e.state&&(e.state=1)})).catch((function(e){console.error("IMPOSSIBLE: Something went wrong while the dispatcher was executing the tasks queue.",e)})))},e.prototype.launchInternal=function(e,t){return void 0===t&&(t=!1),!t&&0===this.state&&this.queue.length>=this.maxPreInitQueueSize?(console.warn("Unable to enqueue task, pre init queue is full."),!1):(t?this.queue.unshift(e):this.queue.push(e),this.triggerExecution(),!0)},e.prototype.launch=function(e){this.launchInternal({task:e,command:0})},e.prototype.flushInit=function(e){0===this.state?(e&&this.launchInternal({task:e,command:0},!0),this.state=1,this.triggerExecution()):console.warn("Attempted to initialize the Dispatcher, but it is already initialized. Ignoring.")},e.prototype.clear=function(){this.launchInternal({command:2},!0),this.resume()},e.prototype.stop=function(){this.launchInternal({command:1},!0)},e.prototype.resume=function(){3===this.state&&(this.state=1,this.triggerExecution())},e.prototype.testBlockOnQueue=function(){return y(this,void 0,void 0,(function(){var e;return g(this,(function(t){switch(t.label){case 0:return(e=this.currentJob)?[4,this.currentJob]:[3,2];case 1:e=t.sent(),t.label=2;case 2:return[2,e]}}))}))},e.prototype.testUninitialize=function(){return y(this,void 0,void 0,(function(){return g(this,(function(e){switch(e.label){case 0:return 0===this.state?[2]:(this.clear(),[4,this.testBlockOnQueue()]);case 1:return e.sent(),this.state=0,[2]}}))}))},e.prototype.testLaunch=function(e){var t=this;return new Promise((function(n,r){t.resume(),t.launchInternal({resolver:n,task:e,command:3})||r()}))},e}();var m,_=function(){function e(){this._initialized=!1}return Object.defineProperty(e,"instance",{get:function(){return e._instance||(e._instance=new e),e._instance},enumerable:!1,configurable:!0}),e.testUninitialize=function(){return t=this,n=void 0,i=function(){return function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]5)return console.error("A list of tags cannot contain more than 5 elements."),!1;for(var t=0,n=e;t0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]=16?(l=!0,[3,13]):[3,9];case 9:return t.dynamicLabel.length>61?(l=!0,[4,_.errorManager.record(t,m.InvalidLabel,"Label length "+t.dynamicLabel.length+" exceeds maximum of 61.")]):[3,11];case 10:return f.sent(),[3,13];case 11:return L.test(t.dynamicLabel)?[3,13]:(l=!0,[4,_.errorManager.record(t,m.InvalidLabel,"Label must be snake_case, got '"+t.dynamicLabel+"'.")]);case 12:f.sent(),f.label=13;case 13:return[2,l?N(t.baseIdentifier(),R):n]}}))},new((i=void 0)||(i=Promise))((function(e,t){function a(e){try{u(o.next(e))}catch(e){t(e)}}function s(e){try{u(o.throw(e))}catch(e){t(e)}}function u(t){var n;t.done?e(t.value):(n=t.value,n instanceof i?n:new i((function(e){e(n)}))).then(a,s)}u((o=o.apply(n,r||[])).next())}));var n,r,i,o}const J=function(){function e(t,n,r){return new Proxy(this,{get:function(i,o){return r?e.createFromStaticLabel(t,n,r,o):e.createFromDynamicLabel(t,n,o)}})}return e.createFromStaticLabel=function(e,t,n,r){var i=n.includes(r)?r:R;return new t(A(A({},e),{name:N(e.name,i)}))},e.createFromDynamicLabel=function(e,t,n){return new t(A(A({},e),{dynamicLabel:n}))},e}();var F=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},B=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0?this.category+"."+this.name:this.name},e.prototype.identifier=function(e){return F(this,void 0,void 0,(function(){var t;return B(this,(function(n){switch(n.label){case 0:return t=this.baseIdentifier(),S(this.dynamicLabel)?[3,2]:[4,q(e,this)];case 1:return[2,n.sent()];case 2:return[2,t]}}))}))},e.prototype.shouldRecord=function(e){return e&&!this.disabled},e.prototype.testGetNumRecordedErrors=function(e,t){return void 0===t&&(t=this.sendInPings[0]),F(this,void 0,void 0,(function(){return B(this,(function(n){return[2,_.errorManager.testGetNumRecordedErrors(this,e,t)]}))}))},e}(),X=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),K=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},W=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]Number.MAX_SAFE_INTEGER&&(r=Number.MAX_SAFE_INTEGER),n.set(r),n}}(t),[4,_.metricsDatabase.transform(e,n)];case 3:return r.sent(),[2]}}))}))},t.prototype.add=function(e){var n=this;_.dispatcher.launch((function(){return Z(n,void 0,void 0,(function(){return ee(this,(function(n){return[2,t._private_addUndispatched(this,e)]}))}))}))},t.prototype.testGetValue=function(e){return void 0===e&&(e=this.sendInPings[0]),Z(this,void 0,void 0,(function(){var t,n=this;return ee(this,(function(r){switch(r.label){case 0:return[4,_.dispatcher.testLaunch((function(){return Z(n,void 0,void 0,(function(){return ee(this,(function(n){switch(n.label){case 0:return[4,_.metricsDatabase.getMetric(e,this)];case 1:return t=n.sent(),[2]}}))}))}))];case 1:return r.sent(),[2,t]}}))}))},t}(Q);var re;!function(e){e.Nanosecond="nanosecond",e.Microsecond="microsecond",e.Millisecond="millisecond",e.Second="second",e.Minute="minute",e.Hour="hour",e.Day="day"}(re||(re={}));const ie=re;var oe=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),ae=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},se=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0?"+":"-")+Math.abs(t).toString().padStart(2,"0")+":00"}(this.timezone),r=t.getFullYear().toString().padStart(2,"0"),i=(t.getMonth()+1).toString().padStart(2,"0"),o=t.getDate().toString().padStart(2,"0");if(this.timeUnit===ie.Day)return r+"-"+i+"-"+o+n;var a=t.getHours().toString().padStart(2,"0");if(this.timeUnit===ie.Hour)return r+"-"+i+"-"+o+"T"+a+n;var s=t.getMinutes().toString().padStart(2,"0");if(this.timeUnit===ie.Minute)return r+"-"+i+"-"+o+"T"+a+":"+s+n;var u=t.getSeconds().toString().padStart(2,"0");if(this.timeUnit===ie.Second)return r+"-"+i+"-"+o+"T"+a+":"+s+":"+u+n;var c=t.getMilliseconds().toString().padStart(3,"0");return this.timeUnit===ie.Millisecond?r+"-"+i+"-"+o+"T"+a+":"+s+":"+u+"."+c+n:this.timeUnit===ie.Microsecond?r+"-"+i+"-"+o+"T"+a+":"+s+":"+u+"."+c+"000"+n:r+"-"+i+"-"+o+"T"+a+":"+s+":"+u+"."+c+"000000"+n},t}(G);const ce=function(e){function t(t,n){var r=e.call(this,"datetime",t)||this;return r.timeUnit=n,r}return oe(t,e),t._private_setUndispatched=function(e,t){return ae(this,void 0,void 0,(function(){var n,r;return se(this,(function(i){switch(i.label){case 0:if(!e.shouldRecord(_.uploadEnabled))return[2];switch(t||(t=new Date),n=t,e.timeUnit){case ie.Day:n.setMilliseconds(0),n.setSeconds(0),n.setMinutes(0),n.setMilliseconds(0);case ie.Hour:n.setMilliseconds(0),n.setSeconds(0),n.setMinutes(0);case ie.Minute:n.setMilliseconds(0),n.setSeconds(0);case ie.Second:n.setMilliseconds(0)}return r=ue.fromDate(t,e.timeUnit),[4,_.metricsDatabase.record(e,r)];case 1:return i.sent(),[2]}}))}))},t.prototype.set=function(e){var n=this;_.dispatcher.launch((function(){return t._private_setUndispatched(n,e)}))},t.prototype.testGetValueAsDatetimeMetric=function(e){return ae(this,void 0,void 0,(function(){var t,n=this;return se(this,(function(r){switch(r.label){case 0:return[4,_.dispatcher.testLaunch((function(){return ae(n,void 0,void 0,(function(){return se(this,(function(n){switch(n.label){case 0:return[4,_.metricsDatabase.getMetric(e,this)];case 1:return t=n.sent(),[2]}}))}))}))];case 1:return r.sent(),t?[2,new ue(t)]:[2]}}))}))},t.prototype.testGetValueAsString=function(e){return void 0===e&&(e=this.sendInPings[0]),ae(this,void 0,void 0,(function(){var t;return se(this,(function(n){switch(n.label){case 0:return[4,this.testGetValueAsDatetimeMetric(e)];case 1:return[2,(t=n.sent())?t.payload():void 0]}}))}))},t.prototype.testGetValue=function(e){return void 0===e&&(e=this.sendInPings[0]),ae(this,void 0,void 0,(function(){var t;return se(this,(function(n){switch(n.label){case 0:return[4,this.testGetValueAsDatetimeMetric(e)];case 1:return[2,(t=n.sent())?t.date:void 0]}}))}))},t}(Q);var le=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),fe=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},pe=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]Number.MAX_SAFE_INTEGER&&(t=Number.MAX_SAFE_INTEGER),n=new he(t),[4,_.metricsDatabase.record(e,n)];case 3:return r.sent(),[2]}}))}))},t.prototype.set=function(e){var n=this;_.dispatcher.launch((function(){return t._private_setUndispatched(n,e)}))},t.prototype.testGetValue=function(e){return void 0===e&&(e=this.sendInPings[0]),fe(this,void 0,void 0,(function(){var t,n=this;return pe(this,(function(r){switch(r.label){case 0:return[4,_.dispatcher.testLaunch((function(){return fe(n,void 0,void 0,(function(){return pe(this,(function(n){switch(n.label){case 0:return[4,_.metricsDatabase.getMetric(e,this)];case 1:return t=n.sent(),[2]}}))}))}))];case 1:return r.sent(),[2,t]}}))}))},t}(Q);var be=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),ve=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},ye=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]100)},t.prototype.payload=function(){return this._inner},t}(G);const we=function(e){function t(t){return e.call(this,"string",t)||this}return be(t,e),t._private_setUndispatched=function(e,t){return ve(this,void 0,void 0,(function(){var n,r;return ye(this,(function(i){switch(i.label){case 0:return e.shouldRecord(_.uploadEnabled)?[4,M(e,t,100)]:[2];case 1:return n=i.sent(),r=new ge(n),[4,_.metricsDatabase.record(e,r)];case 2:return i.sent(),[2]}}))}))},t.prototype.set=function(e){var n=this;_.dispatcher.launch((function(){return t._private_setUndispatched(n,e)}))},t.prototype.testGetValue=function(e){return void 0===e&&(e=this.sendInPings[0]),ve(this,void 0,void 0,(function(){var t,n=this;return ye(this,(function(r){switch(r.label){case 0:return[4,_.dispatcher.testLaunch((function(){return ve(n,void 0,void 0,(function(){return ye(this,(function(n){switch(n.label){case 0:return[4,_.metricsDatabase.getMetric(e,this)];case 1:return t=n.sent(),[2]}}))}))}))];case 1:return r.sent(),[2,t]}}))}))},t}(Q);var me=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),_e=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},Pe=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]=0;return!(!t||!n)},t.prototype.payload=function(){return{time_unit:this._inner.timeUnit,value:this.timespan}},t}(G);const Se=function(e){function t(t,n){var r=e.call(this,"timespan",t)||this;return r.timeUnit=n,r}return me(t,e),t._private_setRawUndispatched=function(e,t){return _e(this,void 0,void 0,(function(){var n,r;return Pe(this,(function(i){switch(i.label){case 0:return e.shouldRecord(_.uploadEnabled)?S(e.startTime)?[3,2]:[4,_.errorManager.record(e,m.InvalidState,"Timespan already running. Raw value not recorded.")]:[2];case 1:return i.sent(),[2];case 2:return n=!1,r=function(t){return function(r){var i;try{i=new xe(r),n=!0}catch(n){i=new xe({timespan:t,timeUnit:e.timeUnit})}return i}}(t),[4,_.metricsDatabase.transform(e,r)];case 3:return i.sent(),n?[4,_.errorManager.record(e,m.InvalidState,"Timespan value already recorded. New value discarded.")]:[3,5];case 4:i.sent(),i.label=5;case 5:return[2]}}))}))},t.prototype.start=function(){var e=this,t=D();_.dispatcher.launch((function(){return _e(e,void 0,void 0,(function(){return Pe(this,(function(e){switch(e.label){case 0:return this.shouldRecord(_.uploadEnabled)?S(this.startTime)?[3,2]:[4,_.errorManager.record(this,m.InvalidState,"Timespan already started")]:[2];case 1:return e.sent(),[2];case 2:return this.startTime=t,[2,Promise.resolve()]}}))}))}))},t.prototype.stop=function(){var e=this,n=D();_.dispatcher.launch((function(){return _e(e,void 0,void 0,(function(){var e;return Pe(this,(function(r){switch(r.label){case 0:return this.shouldRecord(_.uploadEnabled)?S(this.startTime)?[4,_.errorManager.record(this,m.InvalidState,"Timespan not running")]:[3,2]:(this.startTime=void 0,[2]);case 1:return r.sent(),[2];case 2:return e=n-this.startTime,this.startTime=void 0,e<0?[4,_.errorManager.record(this,m.InvalidState,"Timespan was negative.")]:[3,4];case 3:return r.sent(),[2];case 4:return[4,t._private_setRawUndispatched(this,e)];case 5:return r.sent(),[2]}}))}))}))},t.prototype.cancel=function(){var e=this;_.dispatcher.launch((function(){return e.startTime=void 0,Promise.resolve()}))},t.prototype.setRawNanos=function(e){var n=this;_.dispatcher.launch((function(){return _e(n,void 0,void 0,(function(){var n;return Pe(this,(function(r){switch(r.label){case 0:return n=e*Math.pow(10,-6),[4,t._private_setRawUndispatched(this,n)];case 1:return r.sent(),[2]}}))}))}))},t.prototype.testGetValue=function(e){return void 0===e&&(e=this.sendInPings[0]),_e(this,void 0,void 0,(function(){var t,n=this;return Pe(this,(function(r){switch(r.label){case 0:return[4,_.dispatcher.testLaunch((function(){return _e(n,void 0,void 0,(function(){return Pe(this,(function(n){switch(n.label){case 0:return[4,_.metricsDatabase.getMetric(e,this)];case 1:return t=n.sent(),[2]}}))}))}))];case 1:return r.sent(),t?[2,new xe(t).timespan]:[2]}}))}))},t}(Q);var Oe=function(){var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)};return function(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),Ie=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},Te=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]=200&&n<300?(console.info("Ping "+e+" succesfully sent "+n+"."),[4,this.pingsDatabase.deletePing(e)]):[3,2];case 1:return i.sent(),[2,!1];case 2:return 1===r||n&&n>=400&&n<500?(console.warn("Unrecoverable upload failure while attempting to send ping "+e+". Error was "+(null!=n?n:"no status")+"."),[4,this.pingsDatabase.deletePing(e)]):[3,4];case 3:return i.sent(),[2,!1];case 4:return console.warn("Recoverable upload failure while attempting to send ping "+e+", will retry. Error was "+(null!=n?n:"no status")+"."),[2,!0]}}))}))},e.prototype.triggerUploadInternal=function(){return Fe(this,void 0,void 0,(function(){var e,t,n;return Be(this,(function(r){switch(r.label){case 0:e=0,t=this.getNextPing(),r.label=1;case 1:return t&&2!==this.status?[4,this.attemptPingUpload(t)]:[3,4];case 2:return n=r.sent(),[4,this.processPingUploadResponse(t.identifier,n)];case 3:return r.sent()&&(e++,this.enqueuePing(t)),e>=3?(console.info("Reached maximum recoverable failures for the current uploading window. You are done."),[2]):(t=this.getNextPing(),[3,1]);case 4:return[2]}}))}))},e.prototype.triggerUpload=function(){return Fe(this,void 0,void 0,(function(){return Be(this,(function(e){switch(e.label){case 0:if(0!==this.status)return[2];this.status=1,e.label=1;case 1:return e.trys.push([1,,3,4]),this.currentJob=this.triggerUploadInternal(),[4,this.currentJob];case 2:return e.sent(),[3,4];case 3:return this.status=0,[7];case 4:return[2]}}))}))},e.prototype.cancelUpload=function(){return Fe(this,void 0,void 0,(function(){return Be(this,(function(e){switch(e.label){case 0:return 1!==this.status?[3,2]:(this.status=2,[4,this.currentJob]);case 1:e.sent(),e.label=2;case 2:return[2]}}))}))},e.prototype.clearPendingPingsQueue=function(){return Fe(this,void 0,void 0,(function(){return Be(this,(function(e){switch(e.label){case 0:return[4,this.cancelUpload()];case 1:return e.sent(),this.queue=[],[2]}}))}))},e.prototype.update=function(e,t){this.enqueuePing(Je({identifier:e},t)),this.triggerUpload()},e}();var Xe=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},Ke=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0)return t}(_.debugOptions),[2,_.pingsDatabase.recordPing(lt(_.applicationId,e,t),e,a,s)]}}))}))};var pt=function(e,t,n,r){return new(n||(n=Promise))((function(i,o){function a(e){try{u(r.next(e))}catch(e){o(e)}}function s(e){try{u(r.throw(e))}catch(e){o(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((r=r.apply(e,t||[])).next())}))},ht=function(e,t){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!((i=(i=a.trys).length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0?[4,ne._private_addUndispatched(i,r)]:[3,2];case 1:return o.sent(),[3,2];case 2:return[2]}}))}))},e.prototype.testGetNumRecordedErrors=function(e,t,n){return vt(this,void 0,void 0,(function(){return yt(this,(function(r){switch(r.label){case 0:return[4,gt(e,t).testGetValue(n)];case 1:return[2,r.sent()||0]}}))}))},e}();var mt=function(){return(mt=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]{var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{var i;e.r(t),e.d(t,{default:()=>$i}),function(e){e.InvalidValue="invalid_value",e.InvalidLabel="invalid_label",e.InvalidState="invalid_state",e.InvalidOverflow="invalid_overflow"}(i||(i={}));function n(e,t,i,n){var r,s=arguments.length,a=s<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,i,n);else for(var o=e.length-1;o>=0;o--)(r=e[o])&&(a=(s<3?r(a):s>3?r(t,i,a):r(t,i))||a);return s>3&&a&&Object.defineProperty(t,i,a),a}function r(e,t,i,n){return new(i||(i=Promise))((function(r,s){function a(e){try{c(n.next(e))}catch(e){s(e)}}function o(e){try{c(n.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(a,o)}c((n=n.apply(e,t||[])).next())}))}Object.create;var s;Object.create;function a(e,t,i=s.Debug){const n=`(Glean.${e})`;Array.isArray(t)?console[i](n,...t):console[i](n,t)}!function(e){e.Debug="debug",e.Info="info",e.Warn="warn",e.Error="error"}(s||(s={}));var o;!function(e){e[e.RecoverableFailure=0]="RecoverableFailure",e[e.UnrecoverableFailure=1]="UnrecoverableFailure",e[e.Success=2]="Success"}(o||(o={}));class c{constructor(e,t){this.result=e,this.status=t}}const d=class{};var l,u=new Uint8Array(16);function h(){if(!l&&!(l="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return l(u)}const g=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;const p=function(e){return"string"==typeof e&&g.test(e)};for(var f=[],v=0;v<256;++v)f.push((v+256).toString(16).substr(1));const m=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=(f[e[t+0]]+f[e[t+1]]+f[e[t+2]]+f[e[t+3]]+"-"+f[e[t+4]]+f[e[t+5]]+"-"+f[e[t+6]]+f[e[t+7]]+"-"+f[e[t+8]]+f[e[t+9]]+"-"+f[e[t+10]]+f[e[t+11]]+f[e[t+12]]+f[e[t+13]]+f[e[t+14]]+f[e[t+15]]).toLowerCase();if(!p(i))throw TypeError("Stringified UUID is invalid");return i};const y=function(e,t,i){var n=(e=e||{}).random||(e.rng||h)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,t){i=i||0;for(var r=0;r<16;++r)t[i+r]=n[r];return t}return m(n)};var b,w;!function(e){e.Uninitialized="Uninitialized",e.Idle="Idle",e.Processing="Processing",e.Stopped="Stopped",e.Shutdown="Shutdown"}(b||(b={})),function(e){e.Task="Task",e.PersistentTask="PersistentTask",e.InitTask="InitTask",e.Stop="Stop",e.Clear="Clear",e.Shutdown="Shutdown",e.TestTask="TestTask"}(w||(w={}));const _=class{constructor(e=100,t="core.Dispatcher"){this.maxPreInitQueueSize=e,this.logTag=t,this.shuttingDown=!1,this.currentJob=Promise.resolve(),this.queue=[],this.state="Uninitialized"}getNextCommand(){return this.queue.shift()}executeTask(e){return r(this,void 0,void 0,(function*(){try{return yield e(),!0}catch(e){return a(this.logTag,["Error executing task:",e],s.Error),!1}}))}unblockTestResolvers(){this.queue.forEach((e=>{"TestTask"===e.command&&e.resolver()}))}execute(){return r(this,void 0,void 0,(function*(){let e=this.getNextCommand();for(;e;){switch(e.command){case"Stop":return void(this.state="Stopped");case"Shutdown":return this.unblockTestResolvers(),this.queue=[],this.state="Shutdown",void(this.shuttingDown=!1);case"Clear":this.unblockTestResolvers(),this.queue=this.queue.filter((e=>["PersistentTask","Shutdown"].includes(e.command)));break;case"TestTask":yield this.executeTask(e.task),e.resolver();break;case"InitTask":(yield this.executeTask(e.task))||(a(this.logTag,["Error initializing dispatcher, won't execute anything further.","There might be more error logs above."],s.Error),this.clear(),this.shutdown());break;case"PersistentTask":case"Task":yield this.executeTask(e.task)}e=this.getNextCommand()}}))}triggerExecution(){"Idle"===this.state&&this.queue.length>0&&(this.state="Processing",this.currentJob=this.execute(),this.currentJob.then((()=>{const e=this;"Processing"===this.state&&(e.state="Idle")})).catch((e=>{a(this.logTag,["IMPOSSIBLE: Something went wrong while the dispatcher was executing the tasks queue.",e],s.Error)})))}launchInternal(e,t=!1){return"Shutdown"===this.state?(a(this.logTag,"Attempted to enqueue a new task but the dispatcher is shutdown. Ignoring.",s.Warn),!1):!t&&"Uninitialized"===this.state&&this.queue.length>=this.maxPreInitQueueSize?(a(this.logTag,"Unable to enqueue task, pre init queue is full.",s.Warn),!1):(t?this.queue.unshift(e):this.queue.push(e),this.triggerExecution(),!0)}launch(e){this.launchInternal({task:e,command:"Task"})}launchPersistent(e){this.launchInternal({task:e,command:"PersistentTask"})}flushInit(e){"Uninitialized"===this.state?(e&&this.launchInternal({task:e,command:"InitTask"},!0),this.state="Idle",this.triggerExecution()):a(this.logTag,"Attempted to initialize the Dispatcher, but it is already initialized. Ignoring.",s.Warn)}clear(e=!0){this.launchInternal({command:"Clear"},e),this.resume()}stop(e=!0){this.shuttingDown?this.clear(e):this.launchInternal({command:"Stop"},e)}resume(){"Stopped"===this.state&&(this.state="Idle",this.triggerExecution())}shutdown(){return this.shuttingDown=!0,this.launchInternal({command:"Shutdown"}),this.resume(),this.currentJob}testBlockOnQueue(){return r(this,void 0,void 0,(function*(){return yield this.currentJob}))}testUninitialize(){return r(this,void 0,void 0,(function*(){"Uninitialized"!==this.state&&(this.clear(),yield this.shutdown(),this.state="Uninitialized")}))}testLaunch(e){return new Promise(((t,i)=>{this.resume();this.launchInternal({resolver:t,task:e,command:"TestTask"})||i()}))}},T="core.Context";class I{constructor(){this._initialized=!1,this._testing=!1,this._startTime=new Date,this._dispatcher=new _}static get instance(){return I._instance||(I._instance=new I),I._instance}static testUninitialize(){I._instance=void 0}static get dispatcher(){return I.instance._dispatcher}static get uploadEnabled(){return void 0===I.instance._uploadEnabled&&a(T,["Attempted to access Context.uploadEnabled before it was set. This may cause unexpected behaviour."],s.Error),I.instance._uploadEnabled}static set uploadEnabled(e){I.instance._uploadEnabled=e}static get metricsDatabase(){return void 0===I.instance._metricsDatabase&&a(T,["Attempted to access Context.metricsDatabase before it was set. This may cause unexpected behaviour."],s.Error),I.instance._metricsDatabase}static set metricsDatabase(e){I.instance._metricsDatabase=e}static get eventsDatabase(){return void 0===I.instance._eventsDatabase&&a(T,["Attempted to access Context.eventsDatabase before it was set. This may cause unexpected behaviour."],s.Error),I.instance._eventsDatabase}static set eventsDatabase(e){I.instance._eventsDatabase=e}static get pingsDatabase(){return void 0===I.instance._pingsDatabase&&a(T,["Attempted to access Context.pingsDatabase before it was set. This may cause unexpected behaviour."],s.Error),I.instance._pingsDatabase}static set pingsDatabase(e){I.instance._pingsDatabase=e}static get errorManager(){return void 0===I.instance._errorManager&&a(T,["Attempted to access Context.errorManager before it was set. This may cause unexpected behaviour."],s.Error),I.instance._errorManager}static set errorManager(e){I.instance._errorManager=e}static get applicationId(){return void 0===I.instance._applicationId&&a(T,["Attempted to access Context.applicationId before it was set. This may cause unexpected behaviour."],s.Error),I.instance._applicationId}static set applicationId(e){I.instance._applicationId=e}static get initialized(){return I.instance._initialized}static set initialized(e){I.instance._initialized=e}static get debugOptions(){return void 0===I.instance._debugOptions&&a(T,["Attempted to access Context.debugOptions before it was set. This may cause unexpected behaviour."],s.Error),I.instance._debugOptions}static set debugOptions(e){I.instance._debugOptions=e}static get startTime(){return I.instance._startTime}static get testing(){return I.instance._testing}static set testing(e){I.instance._testing=e}}function S(e){if(P(e)||D(e)||M(e))return!0;if(x(e)){if(0===Object.keys(e).length)return!0;for(const t in e)return S(e[t])}return!!Array.isArray(e)&&e.every((e=>S(e)))}function x(e){return"object"==typeof e&&null!==e&&e.constructor===Object}function E(e){return void 0===e}function P(e){return"string"==typeof e}function D(e){return"boolean"==typeof e}function M(e){return"number"==typeof e&&!isNaN(e)}function $(e){return M(e)&&Number.isInteger(e)}function U(e){return/^[a-z0-9-]{1,20}$/i.test(e)}function O(){return"undefined"!=typeof crypto?y():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}))}const k=Date.now();function z(){return"undefined"==typeof performance?Date.now()-k:performance.now()}function R(e,t,n){return r(this,void 0,void 0,(function*(){const r=t.substr(0,n);return r!==t&&(yield I.errorManager.record(e,i.InvalidOverflow,`Value length ${t.length} exceeds maximum of ${n}.`)),r}))}function A(e="core.utils"){return(t,i,n)=>{const r=n.value;return n.value=function(...t){return I.testing?r?r.apply(this,t):Promise.resolve():(a(e,[`Attempted to access test only method \`${i||"unknown"}\`,`,"but Glean is not in testing mode. Ignoring. Make sure to put Glean in testing mode","before accessing such methods, by calling `Glean.testResetGlean`."],s.Error),Promise.resolve())},n}}const V="platform.qt.Uploader";const j=new class extends d{post(e,t,i={}){return r(this,void 0,void 0,(function*(){return new Promise((n=>{const r=new XMLHttpRequest;r.timeout=1e4,r.open("POST",e);for(const e in i)r.setRequestHeader(e,i[e]);r.ontimeout=function(e){a(V,["Timeout while attempting to upload ping.\n",e.type],s.Error),n(new c(0))},r.onerror=function(e){a(V,["Network error while attempting to upload ping.\n",e.type],s.Error),n(new c(0))},r.onabort=function(e){a(V,["The attempt to upload ping is aborted.\n",e.type],s.Error),n(new c(0))},r.onload=()=>{n(new c(2,r.status))},P(t)?r.send(t):r.send(t.buffer)}))}))}},L={os(){return r(this,void 0,void 0,(function*(){switch(Qt.platform.os){case"android":return"Android";case"ios":return"iOS";case"tvos":return"tvOS";case"linux":return"Linux";case"osx":return"Darwin";case"qnx":return"QNX";case"windows":case"winrt":return"Windows";case"wasm":return"Wasm";default:return"Unknown"}}))},osVersion(e){return r(this,void 0,void 0,(function*(){return Promise.resolve(e||"Unknown")}))},arch(e){return r(this,void 0,void 0,(function*(){return Promise.resolve(e||"Unknown")}))},locale(){return r(this,void 0,void 0,(function*(){const e=Qt.locale();return Promise.resolve(e?e.name.replace("_","-"):"und")}))}};function G(e,t){if(0===t.length)throw Error("The index must contain at least one property to get.");let i=e;for(const e of t){if(!x(i)||!(e in i))return;{const t=i[e];S(t)&&(i=t)}}return i}function C(e,t,i){if(0===t.length)throw Error("The index must contain at least one property to update.");const n=Object.assign({},e);let r=n;for(const e of t.slice(0,t.length-1))x(r[e])||(r[e]={}),r=r[e];const o=t[t.length-1],c=r[o];try{const e=i(c);return r[o]=e,n}catch(e){return a("core.Storage.Utils",["Error while transforming stored value. Ignoring old value.",e],s.Error),r[o]=i(void 0),n}}function N(e,t="",i=[]){if(x(e)){const n=Object.keys(e);for(const r of n){const n=e[r];E(n)||N(n,`${t}${r}+`,i)}}else i.push([t.slice(0,-1),JSON.stringify(e)]);return i}function q(e){if(!e||0===e.rows.length)return;const t={};for(let i=0;i{try{t.transaction((t=>{const n=t.executeSql(e);i(n)}))}catch(t){a(this.logTag,[`Error executing LocalStorage query: ${e}.\n`,t],s.Debug),n()}}))}_executeOnceInitialized(e){return r(this,void 0,void 0,(function*(){return yield this.initialized,this._executeQuery(e)}))}_getFullResultObject(e){return r(this,void 0,void 0,(function*(){const t=this._createKeyFromIndex(e);return q(yield this._executeOnceInitialized(`SELECT * FROM ${this.tableName} WHERE key LIKE "${t}%"`))}))}_getWholeStore(){return r(this,void 0,void 0,(function*(){return q(yield this._executeOnceInitialized(`SELECT * FROM ${this.tableName}`))}))}get(e=[]){return r(this,void 0,void 0,(function*(){if(0===e.length)return this._getWholeStore();const t=(yield this._getFullResultObject(e))||{};try{return G(t,e)}catch(e){a(this.logTag,["Error getting value from database.",e])}}))}update(e,t){return r(this,void 0,void 0,(function*(){const i=N(C((yield this._getFullResultObject(e))||{},e,t));for(const e of i){const[t,i]=e,n=i.replace("'","''"),r=yield this._executeOnceInitialized(`UPDATE ${this.tableName} SET value='${n}' WHERE key='${t}'`);(null==r?void 0:r.rows.length)||(yield this._executeOnceInitialized(`INSERT INTO ${this.tableName}(key, value) VALUES('${t}', '${n}');`))}}))}delete(e){return r(this,void 0,void 0,(function*(){const t=this._createKeyFromIndex(e);yield this._executeOnceInitialized(`DELETE FROM ${this.tableName} WHERE key LIKE "${t}%"`)}))}},uploader:j,info:L,name:"Qt"},W="0.23.0",B="glean_ping_info",J="glean_client_info",Q="c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0",H="deletion-request",K="#glean_reference_time",X="#glean_execution_counter",Z=[X,K],Y="core.Config";class ee{constructor(e){if(this.channel=null==e?void 0:e.channel,this.appBuild=null==e?void 0:e.appBuild,this.appDisplayVersion=null==e?void 0:e.appDisplayVersion,this.architecture=null==e?void 0:e.architecture,this.osVersion=null==e?void 0:e.osVersion,this.debug=ee.sanitizeDebugOptions(null==e?void 0:e.debug),(null==e?void 0:e.serverEndpoint)&&(t=e.serverEndpoint,!/^(http|https):\/\/[a-zA-Z0-9._-]+(:\d+){0,1}(\/{0,1})$/i.test(t)))throw new Error(`Unable to initialize Glean, serverEndpoint ${e.serverEndpoint} is an invalid URL.`);var t;this.serverEndpoint=e&&e.serverEndpoint?e.serverEndpoint:"https://incoming.telemetry.mozilla.org",this.httpClient=null==e?void 0:e.httpClient}static sanitizeDebugOptions(e){const t=e||{};return void 0===(null==e?void 0:e.debugViewTag)||ee.validateDebugViewTag(null==e?void 0:e.debugViewTag)||delete t.debugViewTag,void 0===(null==e?void 0:e.sourceTags)||ee.validateSourceTags(null==e?void 0:e.sourceTags)||delete t.sourceTags,t}static validateDebugViewTag(e){const t=U(e);return t||a(Y,[`"${e}" is not a valid \`debugViewTag\` value.`,"Please make sure the value passed satisfies the regex `^[a-zA-Z0-9-]{1,20}$`."],s.Error),t}static validateSourceTags(e){if(e.length<1||e.length>5)return a(Y,"A list of tags cannot contain more than 5 elements.",s.Error),!1;for(const t of e){if(t.startsWith("glean"))return a(Y,"Tags starting with `glean` are reserved and must not be used.",s.Error),!1;if(!U(t))return!1}return!0}}class te{constructor(e){if(!this.validate(e))throw new Error("Unable to create new Metric instance, value is in unexpected format.");this._inner=e}get(){return this._inner}set(e){this.validate(e)?this._inner=e:a("core.Metrics.Metric",`Unable to set metric to ${JSON.stringify(e)}. Value is in unexpected format. Ignoring.`,s.Error)}}class ie extends te{constructor(e){super(e)}validate(e){return!0}payload(){return this._inner}}const ne="__other__",re=/^[a-z_][a-z0-9_-]{0,29}(\.[a-z_][a-z0-9_-]{0,29})*$/;function se(e,t){return`${e}/${t}`}class ae{constructor(e,t,i){return new Proxy(this,{get:(n,r)=>i?ae.createFromStaticLabel(e,t,i,r):ae.createFromDynamicLabel(e,t,r)})}static createFromStaticLabel(e,t,i,n){const r=i.includes(n)?n:ne;return new t(Object.assign(Object.assign({},e),{name:se(e.name,r)}))}static createFromDynamicLabel(e,t,i){return new t(Object.assign(Object.assign({},e),{dynamicLabel:i}))}}const oe=ae;class ce{constructor(e,t){this.type=e,this.name=t.name,this.category=t.category,this.sendInPings=t.sendInPings,this.lifetime=t.lifetime,this.disabled=t.disabled,this.dynamicLabel=t.dynamicLabel}baseIdentifier(){return this.category.length>0?`${this.category}.${this.name}`:this.name}identifier(){return r(this,void 0,void 0,(function*(){const e=this.baseIdentifier();return E(this.dynamicLabel)?e:yield function(e){return r(this,void 0,void 0,(function*(){if(void 0===e.dynamicLabel)throw new Error("This point should never be reached.");const t=se(e.baseIdentifier(),e.dynamicLabel);for(const i of e.sendInPings)if(yield I.metricsDatabase.hasMetric(e.lifetime,i,e.type,t))return t;let n=0;for(const t of e.sendInPings)n+=(yield I.metricsDatabase.countByBaseIdentifier(e.lifetime,t,e.type,e.baseIdentifier()));let r=!1;return n>=16?r=!0:e.dynamicLabel.length>61?(r=!0,yield I.errorManager.record(e,i.InvalidLabel,`Label length ${e.dynamicLabel.length} exceeds maximum of 61.`)):re.test(e.dynamicLabel)||(r=!0,yield I.errorManager.record(e,i.InvalidLabel,`Label must be snake_case, got '${e.dynamicLabel}'.`)),r?se(e.baseIdentifier(),ne):t}))}(this)}))}shouldRecord(e){return e&&!this.disabled}testGetNumRecordedErrors(e,t=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){return I.errorManager.testGetNumRecordedErrors(this,e,t)}))}}n([A()],ce.prototype,"testGetNumRecordedErrors",null);class de extends te{constructor(e){super(e)}validate(e){return D(e)}payload(){return this._inner}}class le extends ce{constructor(e){super("boolean",e)}set(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){if(!this.shouldRecord(I.uploadEnabled))return;const t=new de(e);yield I.metricsDatabase.record(this,t)}))))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.BooleanMetricType")],le.prototype,"testGetValue",null);const ue=le;class he extends te{constructor(e){super(e)}validate(e){return!!$(e)&&!(e<=0)}payload(){return this._inner}}class ge extends ce{constructor(e){super("counter",e)}static _private_addUndispatched(e,t){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;if(E(t)&&(t=1),t<=0)return void(yield I.errorManager.record(e,i.InvalidValue,`Added negative and zero value ${t}`));const n=(e=>t=>{let i,n;try{i=new he(t),n=i.get()+e}catch(t){i=new he(e),n=e}return n>Number.MAX_SAFE_INTEGER&&(n=Number.MAX_SAFE_INTEGER),i.set(n),i})(t);yield I.metricsDatabase.transform(e,n)}))}add(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){return ge._private_addUndispatched(this,e)}))))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.CounterMetricType")],ge.prototype,"testGetValue",null);const pe=ge;var fe;!function(e){e.Nanosecond="nanosecond",e.Microsecond="microsecond",e.Millisecond="millisecond",e.Second="second",e.Minute="minute",e.Hour="hour",e.Day="day"}(fe||(fe={}));const ve=fe,me="core.metrics.DatetimeMetricType";class ye extends te{constructor(e){super(e)}static fromDate(e,t){return new ye({timeUnit:t,timezone:e.getTimezoneOffset(),date:e.toISOString()})}get date(){return new Date(this._inner.date)}get timezone(){return this._inner.timezone}get timeUnit(){return this._inner.timeUnit}get dateISOString(){return this._inner.date}validate(e){if(!x(e)||3!==Object.keys(e).length)return!1;const t="timeUnit"in e&&P(e.timeUnit)&&Object.values(ve).includes(e.timeUnit),i="timezone"in e&&M(e.timezone),n="date"in e&&P(e.date)&&24===e.date.length&&!isNaN(Date.parse(e.date));return!!(t&&i&&n)}payload(){const e=this.dateISOString.match(/\d+/g);if(!e||e.length<0)throw new Error("IMPOSSIBLE: Unable to extract date information from DatetimeMetric.");const t=new Date(parseInt(e[0]),parseInt(e[1])-1,parseInt(e[2]),parseInt(e[3])-this.timezone/60,parseInt(e[4]),parseInt(e[5]),parseInt(e[6])),i=function(e){const t=e/60*-1;return`${t>0?"+":"-"}${Math.abs(t).toString().padStart(2,"0")}:00`}(this.timezone),n=t.getFullYear().toString().padStart(2,"0"),r=(t.getMonth()+1).toString().padStart(2,"0"),s=t.getDate().toString().padStart(2,"0");if(this.timeUnit===ve.Day)return`${n}-${r}-${s}${i}`;const a=t.getHours().toString().padStart(2,"0");if(this.timeUnit===ve.Hour)return`${n}-${r}-${s}T${a}${i}`;const o=t.getMinutes().toString().padStart(2,"0");if(this.timeUnit===ve.Minute)return`${n}-${r}-${s}T${a}:${o}${i}`;const c=t.getSeconds().toString().padStart(2,"0");if(this.timeUnit===ve.Second)return`${n}-${r}-${s}T${a}:${o}:${c}${i}`;const d=t.getMilliseconds().toString().padStart(3,"0");return this.timeUnit===ve.Millisecond?`${n}-${r}-${s}T${a}:${o}:${c}.${d}${i}`:this.timeUnit===ve.Microsecond?`${n}-${r}-${s}T${a}:${o}:${c}.${d}000${i}`:`${n}-${r}-${s}T${a}:${o}:${c}.${d}000000${i}`}}class be extends ce{constructor(e,t){super("datetime",e),this.timeUnit=t}static _private_setUndispatched(e,t){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;t||(t=new Date);const i=t;switch(e.timeUnit){case ve.Day:i.setMilliseconds(0),i.setSeconds(0),i.setMinutes(0),i.setMilliseconds(0);case ve.Hour:i.setMilliseconds(0),i.setSeconds(0),i.setMinutes(0);case ve.Minute:i.setMilliseconds(0),i.setSeconds(0);case ve.Second:i.setMilliseconds(0)}const n=ye.fromDate(t,e.timeUnit);yield I.metricsDatabase.record(e,n)}))}set(e){I.dispatcher.launch((()=>be._private_setUndispatched(this,e)))}testGetValueAsDatetimeMetric(e){return r(this,void 0,void 0,(function*(){let t;if(yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t)return new ye(t)}))}testGetValueAsString(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){const t=yield this.testGetValueAsDatetimeMetric(e);return t?t.payload():void 0}))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){const t=yield this.testGetValueAsDatetimeMetric(e);return t?t.date:void 0}))}}n([A(me)],be.prototype,"testGetValueAsDatetimeMetric",null),n([A(me)],be.prototype,"testGetValueAsString",null),n([A(me)],be.prototype,"testGetValue",null);const we=be;class _e extends te{constructor(e){super(e)}validate(e){return!!$(e)&&!(e<0)}payload(){return this._inner}}class Te extends ce{constructor(e){super("quantity",e)}static _private_setUndispatched(e,t){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;if(t<0)return void(yield I.errorManager.record(e,i.InvalidValue,`Set negative value ${t}`));t>Number.MAX_SAFE_INTEGER&&(t=Number.MAX_SAFE_INTEGER);const n=new _e(t);yield I.metricsDatabase.record(e,n)}))}set(e){I.dispatcher.launch((()=>Te._private_setUndispatched(this,e)))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.QuantityMetricType")],Te.prototype,"testGetValue",null);const Ie=Te;class Se extends te{constructor(e){super(e)}validate(e){return!!P(e)&&!(e.length>100)}payload(){return this._inner}}class xe extends ce{constructor(e){super("string",e)}static _private_setUndispatched(e,t){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;const i=yield R(e,t,100),n=new Se(i);yield I.metricsDatabase.record(e,n)}))}set(e){I.dispatcher.launch((()=>xe._private_setUndispatched(this,e)))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.StringMetricType")],xe.prototype,"testGetValue",null);const Ee=xe,Pe=20;class De extends te{constructor(e){super(e)}validate(e){if(!Array.isArray(e))return!1;if(e.length>Pe)return!1;for(const t of e)if(!P(t)||t.length>50)return!1;return!0}payload(){return this._inner}}class Me extends ce{constructor(e){super("string_list",e)}set(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){if(!this.shouldRecord(I.uploadEnabled))return;const t=[];e.length>Pe&&(yield I.errorManager.record(this,i.InvalidValue,`String list length of ${e.length} exceeds maximum of 20.`));for(let i=0;ir(this,void 0,void 0,(function*(){if(!this.shouldRecord(I.uploadEnabled))return;const t=yield R(this,e,50);let n=0;const r=(e=>t=>{let i,r;try{i=new De(t),r=i.get(),n=r.length,r.length=Pe&&(yield I.errorManager.record(this,i.InvalidValue,`String list length of ${n+1} exceeds maximum of 20.`))}))))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.StringListMetricType")],Me.prototype,"testGetValue",null);const $e=Me,Ue=204800;class Oe extends te{constructor(e){super(e)}validate(e){return!!P(e)&&!(e.length>Ue)}payload(){return this._inner}}class ke extends ce{constructor(e){super("text",e)}set(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){if(!this.shouldRecord(I.uploadEnabled))return;const t=yield R(this,e,Ue),i=new Oe(t);yield I.metricsDatabase.record(this,i)}))))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.TextMetricType")],ke.prototype,"testGetValue",null);const ze=ke;class Re extends te{constructor(e){super(e)}get timespan(){switch(this._inner.timeUnit){case ve.Nanosecond:return this._inner.timespan*10**6;case ve.Microsecond:return 1e3*this._inner.timespan;case ve.Millisecond:return this._inner.timespan;case ve.Second:return Math.round(this._inner.timespan/1e3);case ve.Minute:return Math.round(this._inner.timespan/1e3/60);case ve.Hour:return Math.round(this._inner.timespan/1e3/60/60);case ve.Day:return Math.round(this._inner.timespan/1e3/60/60/24)}}validate(e){if(!x(e)||2!==Object.keys(e).length)return!1;const t="timeUnit"in e&&P(e.timeUnit)&&Object.values(ve).includes(e.timeUnit),i="timespan"in e&&M(e.timespan)&&e.timespan>=0;return!(!t||!i)}payload(){return{time_unit:this._inner.timeUnit,value:this.timespan}}}class Ae extends ce{constructor(e,t){super("timespan",e),this.timeUnit=t}static _private_setRawUndispatched(e,t){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;if(!E(e.startTime))return void(yield I.errorManager.record(e,i.InvalidState,"Timespan already running. Raw value not recorded."));let n=!1;const r=(t=>i=>{let r;try{r=new Re(i),n=!0}catch(i){r=new Re({timespan:t,timeUnit:e.timeUnit})}return r})(t);yield I.metricsDatabase.transform(e,r),n&&(yield I.errorManager.record(e,i.InvalidState,"Timespan value already recorded. New value discarded."))}))}start(){const e=z();I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){if(this.shouldRecord(I.uploadEnabled)){if(E(this.startTime))return this.startTime=e,Promise.resolve();yield I.errorManager.record(this,i.InvalidState,"Timespan already started")}}))))}stop(){const e=z();I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){if(!this.shouldRecord(I.uploadEnabled))return void(this.startTime=void 0);if(E(this.startTime))return void(yield I.errorManager.record(this,i.InvalidState,"Timespan not running."));const t=e-this.startTime;this.startTime=void 0,t<0?yield I.errorManager.record(this,i.InvalidState,"Timespan was negative."):yield Ae._private_setRawUndispatched(this,t)}))))}cancel(){I.dispatcher.launch((()=>(this.startTime=void 0,Promise.resolve())))}setRawNanos(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){const t=e*10**-6;yield Ae._private_setRawUndispatched(this,t)}))))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;if(yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t)return new Re(t).timespan}))}}n([A("core.metrics.TimespanMetricType")],Ae.prototype,"testGetValue",null);const Ve=Ae,je=/^[a-zA-Z][a-zA-Z0-9-\+\.]*:(.*)$/;class Le extends Error{constructor(e,t){super(t),this.type=e,this.name="UrlMetricError"}}class Ge extends te{constructor(e){super(e)}validate(e){if(!P(e))return!1;if(e.length>2048)throw new Le(i.InvalidOverflow,`URL length ${e.length} exceeds maximum of 2048.`);if(e.startsWith("data:"))throw new Le(i.InvalidValue,"URL metric does not support data URLs.");if(!je.test(e))throw new Le(i.InvalidValue,`"${e}" does not start with a valid URL scheme.`);return!0}payload(){return this._inner}}class Ce extends ce{constructor(e){super("url",e)}set(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){if(this.shouldRecord(I.uploadEnabled))try{const t=new Ge(e);yield I.metricsDatabase.record(this,t)}catch(e){e instanceof Le&&(yield I.errorManager.record(this,e.type,e))}}))))}setUrl(e){this.set(e.toString())}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.URLMetricType")],Ce.prototype,"testGetValue",null);const Ne=Ce,qe=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;class Fe extends te{constructor(e){super(e)}validate(e){return!!P(e)&&qe.test(e)}payload(){return this._inner}}class We extends ce{constructor(e){super("uuid",e)}static _private_setUndispatched(e,t){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;let n;t||(t=O());try{n=new Fe(t)}catch(n){return void(yield I.errorManager.record(e,i.InvalidValue,`"${t}" is not a valid UUID.`))}yield I.metricsDatabase.record(e,n)}))}set(e){I.dispatcher.launch((()=>We._private_setUndispatched(this,e)))}generateAndSet(){if(!this.shouldRecord(I.uploadEnabled))return;const e=O();return this.set(e),e}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.metricsDatabase.getMetric(e,this)})))),t}))}}n([A("core.metrics.UUIDMetricType")],We.prototype,"testGetValue",null);const Be=We,Je=Object.freeze({boolean:de,counter:he,datetime:ye,labeled_boolean:ie,labeled_counter:ie,labeled_string:ie,quantity:_e,string:Se,string_list:De,text:Oe,timespan:Re,url:Ge,uuid:Fe});function Qe(e,t){if(!(e in Je))throw new Error(`Unable to create metric of unknown type ${e}`);return new Je[e](t)}function He(e,t){try{return Qe(e,t),!0}catch(e){return!1}}const Ke="core.Metrics.Database";const Xe=class{constructor(e){this.userStore=new e("userLifetimeMetrics"),this.pingStore=new e("pingLifetimeMetrics"),this.appStore=new e("appLifetimeMetrics")}_chooseStore(e){switch(e){case"user":return this.userStore;case"ping":return this.pingStore;case"application":return this.appStore}}record(e,t){return r(this,void 0,void 0,(function*(){yield this.transform(e,(()=>t))}))}transform(e,t){return r(this,void 0,void 0,(function*(){if(e.disabled)return;const i=this._chooseStore(e.lifetime),n=yield e.identifier();for(const r of e.sendInPings){const s=e=>t(e).get();yield i.update([r,e.type,n],s)}}))}hasMetric(e,t,i,n){return r(this,void 0,void 0,(function*(){const r=this._chooseStore(e);return!E(yield r.get([t,i,n]))}))}countByBaseIdentifier(e,t,i,n){return r(this,void 0,void 0,(function*(){const r=this._chooseStore(e),s=yield r.get([t,i]);return E(s)?0:Object.keys(s).filter((e=>e.startsWith(n))).length}))}getMetric(e,t){return r(this,void 0,void 0,(function*(){const i=this._chooseStore(t.lifetime),n=yield t.identifier(),r=yield i.get([e,t.type,n]);return E(r)||He(t.type,r)?r:(a(Ke,`Unexpected value found for metric ${n}: ${JSON.stringify(r)}. Clearing.`,s.Error),void(yield i.delete([e,t.type,n])))}))}getAndValidatePingData(e,t){return r(this,void 0,void 0,(function*(){const i=this._chooseStore(t),n=yield i.get([e]);return E(n)?{}:function(e){if(x(e)){for(const t in e){const i=e[t];if(!x(i))return!1;for(const e in i)if(!He(t,i[e]))return a(Ke,`Invalid metric representation found for metric "${e}"`,s.Debug),!1}return!0}return!1}(n)?n:(a(Ke,`Unexpected value found for ping "${e}" in "${t}" store: ${JSON.stringify(n)}. Clearing.`,s.Debug),yield i.delete([e]),{})}))}processLabeledMetric(e,t,i,n){const r=`labeled_${t}`,s=i.split("/",2),a=s[0],o=s[1];if(r in e&&a in e[r]){const t=e[r][a];e[r][a]=Object.assign(Object.assign({},t),{[o]:n})}else e[r]=Object.assign(Object.assign({},e[r]),{[a]:{[o]:n}})}getPingMetrics(e,t){return r(this,void 0,void 0,(function*(){const i=yield this.getAndValidatePingData(e,"user"),n=yield this.getAndValidatePingData(e,"ping"),r=yield this.getAndValidatePingData(e,"application");t&&Object.keys(n).length>0&&(yield this.clear("ping",e));const s={};for(const e of[i,n,r])for(const t in e)for(const i in e[t])i.startsWith("glean.reserved#")||(i.includes("/")?this.processLabeledMetric(s,t,i,e[t][i]):s[t]=Object.assign(Object.assign({},s[t]),{[i]:e[t][i]}));return 0===Object.keys(s).length?void 0:function(e){const t={};for(const i in e){const n=e[i];t[i]={};for(const e in n){const r=Qe(i,n[e]);t[i][e]=r.payload()}}return t}(s)}))}clear(e,t){return r(this,void 0,void 0,(function*(){const i=this._chooseStore(e),n=t?[t]:[];yield i.delete(n)}))}clearAll(){return r(this,void 0,void 0,(function*(){yield this.userStore.delete([]),yield this.pingStore.delete([]),yield this.appStore.delete([])}))}};var Ze=Uint8Array,Ye=Uint16Array,et=Uint32Array,tt=new Ze([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),it=new Ze([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),nt=new Ze([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),rt=function(e,t){for(var i=new Ye(31),n=0;n<31;++n)i[n]=t+=1<>>1|(21845&ut)<<1;ht=(61680&(ht=(52428&ht)>>>2|(13107&ht)<<2))>>>4|(3855&ht)<<4,lt[ut]=((65280&ht)>>>8|(255&ht)<<8)>>>1}var gt=function(e,t,i){for(var n=e.length,r=0,s=new Ye(t);r>>c]=d}else for(a=new Ye(n),r=0;r>>15-e[r]);return a},pt=new Ze(288);for(ut=0;ut<144;++ut)pt[ut]=8;for(ut=144;ut<256;++ut)pt[ut]=9;for(ut=256;ut<280;++ut)pt[ut]=7;for(ut=280;ut<288;++ut)pt[ut]=8;var ft=new Ze(32);for(ut=0;ut<32;++ut)ft[ut]=5;var vt=gt(pt,9,0),mt=gt(ft,5,0),yt=function(e){return(e+7)/8|0},bt=function(e,t,i){(null==t||t<0)&&(t=0),(null==i||i>e.length)&&(i=e.length);var n=new(e instanceof Ye?Ye:e instanceof et?et:Ze)(i-t);return n.set(e.subarray(t,i)),n},wt=function(e,t,i){i<<=7&t;var n=t/8|0;e[n]|=i,e[n+1]|=i>>>8},_t=function(e,t,i){i<<=7&t;var n=t/8|0;e[n]|=i,e[n+1]|=i>>>8,e[n+2]|=i>>>16},Tt=function(e,t){for(var i=[],n=0;nh&&(h=s[n].s);var g=new Ye(h+1),p=It(i[l-1],g,0);if(p>t){n=0;var f=0,v=p-t,m=1<t))break;f+=m-(1<>>=v;f>0;){var b=s[n].s;g[b]=0&&f;--n){var w=s[n].s;g[w]==t&&(--g[w],++f)}p=t}return[new Ze(g),p]},It=function(e,t,i){return-1==e.s?Math.max(It(e.l,t,i+1),It(e.r,t,i+1)):t[e.s]=i},St=function(e){for(var t=e.length;t&&!e[--t];);for(var i=new Ye(++t),n=0,r=e[0],s=1,a=function(e){i[n++]=e},o=1;o<=t;++o)if(e[o]==r&&o!=t)++s;else{if(!r&&s>2){for(;s>138;s-=138)a(32754);s>2&&(a(s>10?s-11<<5|28690:s-3<<5|12305),s=0)}else if(s>3){for(a(r),--s;s>6;s-=6)a(8304);s>2&&(a(s-3<<5|8208),s=0)}for(;s--;)a(r);s=1,r=e[o]}return[i.subarray(0,n),t]},xt=function(e,t){for(var i=0,n=0;n>>8,e[r+2]=255^e[r],e[r+3]=255^e[r+1];for(var s=0;s4&&!E[nt[D-1]];--D);var M,$,U,O,k=d+5<<3,z=xt(r,pt)+xt(s,ft)+a,R=xt(r,h)+xt(s,f)+a+14+3*D+xt(I,E)+(2*I[16]+3*I[17]+7*I[18]);if(k<=z&&k<=R)return Et(t,l,e.subarray(c,c+d));if(wt(t,l,1+(R15&&(wt(t,l,L[S]>>>5&127),l+=L[S]>>>12)}}}else M=vt,$=pt,U=mt,O=ft;for(S=0;S255){G=n[S]>>>18&31;_t(t,l,M[G+257]),l+=$[G+257],G>7&&(wt(t,l,n[S]>>>23&31),l+=tt[G]);var C=31&n[S];_t(t,l,U[C]),l+=O[C],C>3&&(_t(t,l,n[S]>>>5&8191),l+=it[C])}else _t(t,l,M[n[S]]),l+=$[n[S]];return _t(t,l,M[256]),l+$[256]},Dt=new et([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),Mt=new Ze(0),$t=function(e,t,i,n,r,s){var a=e.length,o=new Ze(n+a+5*(1+Math.ceil(a/7e3))+r),c=o.subarray(n,o.length-r),d=0;if(!t||a<8)for(var l=0;l<=a;l+=65535){var u=l+65535;u>>13,p=8191&h,f=(1<7e3||E>24576)&&O>423){d=Pt(e,c,0,_,T,I,x,E,D,l-D,d),E=S=x=0,D=l;for(var k=0;k<286;++k)T[k]=0;for(k=0;k<30;++k)I[k]=0}var z=2,R=0,A=p,V=$-U&32767;if(O>2&&M==w(l-V))for(var j=Math.min(g,O)-1,L=Math.min(32767,l),G=Math.min(258,O);V<=L&&--A&&$!=U;){if(e[l+z]==e[l+z-V]){for(var C=0;Cz){if(z=C,R=V,C>j)break;var N=Math.min(V,C-2),q=0;for(k=0;kq&&(q=W,U=F)}}}V+=($=U)-(U=v[$])+32768&32767}if(R){_[E++]=268435456|ot[z]<<18|dt[R];var B=31&ot[z],J=31&dt[R];x+=tt[B]+it[J],++T[257+B],++I[J],P=l+z,++S}else _[E++]=e[l],++T[e[l]]}}d=Pt(e,c,s,_,T,I,x,E,D,l-D,d),!s&&7&d&&(d=Et(c,d+1,Mt))}return bt(o,0,n+yt(d)+r)},Ut=function(){for(var e=new Int32Array(256),t=0;t<256;++t){for(var i=t,n=9;--n;)i=(1&i&&-306674912)^i>>>1;e[t]=i}return e}(),Ot=function(){var e=-1;return{p:function(t){for(var i=e,n=0;n>>8;e=i},d:function(){return~e}}},kt=function(e,t,i,n,r){return $t(e,null==t.level?6:t.level,null==t.mem?Math.ceil(1.5*Math.max(8,Math.min(13,Math.log(e.length)))):12+t.mem,i,n,!r)},zt=function(e,t,i){for(;i;++t)e[t]=i,i>>>=8},Rt=function(e,t){var i=t.filename;if(e[0]=31,e[1]=139,e[2]=8,e[8]=t.level<2?4:9==t.level?2:0,e[9]=3,0!=t.mtime&&zt(e,4,Math.floor(new Date(t.mtime||Date.now())/1e3)),i){e[3]=8;for(var n=0;n<=i.length;++n)e[n+10]=i.charCodeAt(n)}},At=function(e){return 10+(e.filename&&e.filename.length+1||0)};function Vt(e,t){t||(t={});var i=Ot(),n=e.length;i.p(e);var r=kt(e,t,At(t),8),s=r.length;return Rt(r,t),zt(r,s-8,i.d()),zt(r,s-4,n),r}var jt="undefined"!=typeof TextEncoder&&new TextEncoder,Lt="undefined"!=typeof TextDecoder&&new TextDecoder;try{Lt.decode(Mt,{stream:!0}),1}catch(e){}function Gt(e,t){if(t){for(var i=new Ze(e.length),n=0;n>1)),a=0,o=function(e){s[a++]=e};for(n=0;ns.length){var c=new Ze(a+8+(r-n<<1));c.set(s),s=c}var d=e.charCodeAt(n);d<128||t?o(d):d<2048?(o(192|d>>6),o(128|63&d)):d>55295&&d<57344?(o(240|(d=65536+(1047552&d)|1023&e.charCodeAt(++n))>>18),o(128|d>>12&63),o(128|d>>6&63),o(128|63&d)):(o(224|d>>12),o(128|d>>6&63),o(128|63&d))}return bt(s,0,a)}"function"==typeof queueMicrotask?queueMicrotask:"function"==typeof setTimeout&&setTimeout;const Ct="core.Pings.Database";function Nt(e){return e.path.split("/")[3]===H}function qt(e){return Gt(JSON.stringify(e)).length}function Ft(e){if(x(e)&&(2===Object.keys(e).length||3===Object.keys(e).length)){const t="path"in e&&P(e.path),i="payload"in e&&S(e.payload)&&x(e.payload),n=!("headers"in e)||S(e.headers)&&x(e.headers);return!!(t&&i&&n)}return!1}const Wt=class{constructor(e){this.store=new e("pings")}attachObserver(e){this.observer=e}recordPing(e,t,i,n){return r(this,void 0,void 0,(function*(){const r={collectionDate:(new Date).toISOString(),path:e,payload:i};n&&(r.headers=n),yield this.store.update([t],(()=>r)),this.observer&&this.observer.update(t,r)}))}deletePing(e){return r(this,void 0,void 0,(function*(){yield this.store.delete([e])}))}getAllPings(){return r(this,void 0,void 0,(function*(){const e=yield this.store.get(),t={};if(x(e))for(const i in e){const n=e[i];Ft(n)?t[i]=n:(a(Ct,"Unexpected data found in pings database. Deleting.",s.Warn),yield this.store.delete([i]))}return Object.entries(t).sort((([e,{collectionDate:t}],[i,{collectionDate:n}])=>new Date(t).getTime()-new Date(n).getTime()))}))}getAllPingsWithoutSurplus(e=250,t=10485760){return r(this,void 0,void 0,(function*(){const i=yield this.getAllPings(),n=i.filter((([e,t])=>!Nt(t))).reverse(),r=i.filter((([e,t])=>Nt(t))),o=n.length;o>e&&a(Ct,[`More than ${e} pending pings in the pings database,`,`will delete ${o-e} old pings.`],s.Warn);let c=!1,d=0,l=0;const u=[];for(const[i,r]of n)d++,l+=qt(r),!c&&l>t&&(a(Ct,[`Pending pings database has reached the size quota of ${t} bytes,`,"outstanding pings will be deleted."],s.Warn),c=!0),d>e&&(c=!0),c?yield this.deletePing(i):u.unshift([i,r]);return[...r,...u]}))}scanPendingPings(){return r(this,void 0,void 0,(function*(){if(!this.observer)return;const e=yield this.getAllPingsWithoutSurplus();for(const[t,i]of e)this.observer.update(t,i)}))}clearAll(){return r(this,void 0,void 0,(function*(){yield this.store.delete([])}))}};var Bt;!function(e){e[e.Incrementing=0]="Incrementing",e[e.Stopped=1]="Stopped",e[e.Throttled=2]="Throttled"}(Bt||(Bt={}));const Jt=class{constructor(e,t,i=0,n){this.interval=e,this.maxCount=t,this.count=i,this.started=n,this.stopped=!1}get elapsed(){if(E(this.started))return NaN;const e=z()-this.started;return e<0?NaN:e}reset(){this.started=z(),this.count=0,this.stopped=!1}shouldReset(){return!!E(this.started)||!!(isNaN(this.elapsed)||this.elapsed>this.interval)}getState(){this.shouldReset()&&this.reset();const e=this.interval-this.elapsed;return this.stopped?{state:1,remainingTime:e}:this.count>=this.maxCount?{state:2,remainingTime:e}:(this.count++,{state:0})}stop(){this.stopped=!0}},Ht="core.Upload";function Kt(){const e=new _(100,"core.Upload.Dispatcher");return e.flushInit(),e}class Xt{constructor(e=3,t=1048576){this.maxRecoverableFailures=e,this.maxPingBodySize=t}}class Zt extends Error{constructor(e){super(e),this.name="PingBodyOverflow"}}const Yt=class{constructor(e,t,i,n=new Xt,r=new Jt(6e4,15)){this.pingsDatabase=i,this.policy=n,this.rateLimiter=r,this.processing=[],this.uploader=e.httpClient?e.httpClient:t.uploader,this.platformInfo=t.info,this.serverEndpoint=e.serverEndpoint,this.dispatcher=Kt()}enqueuePing(e){for(const t of this.processing)if(t.identifier===e.identifier)return;this.processing.push(e);const{state:t,remainingTime:i}=this.rateLimiter.getState();0===t?this.dispatcher.resume():(this.dispatcher.stop(!1),2===t?a(Ht,["Attempted to upload a ping, but Glean is currently throttled.",`Pending pings will be processed in ${(i||0)/1e3}s.`],s.Debug):1===t&&a(Ht,["Attempted to upload a ping, but Glean has reached maximum recoverable upload failures","for the current uploading window.",`Will retry in ${(i||0)/1e3}s.`],s.Debug));(Nt(e)?this.dispatcher.launchPersistent:this.dispatcher.launch).bind(this.dispatcher)((()=>r(this,void 0,void 0,(function*(){const t=yield this.attemptPingUpload(e);(yield this.processPingUploadResponse(e.identifier,t))&&(e.retries++,this.enqueuePing(e)),e.retries>=this.policy.maxRecoverableFailures&&(a(Ht,`Reached maximum recoverable failures for ping "${JSON.stringify(e.name)}". You are done.`,s.Info),this.rateLimiter.stop(),this.dispatcher.stop(),e.retries=0)}))))}preparePingForUpload(e){return r(this,void 0,void 0,(function*(){let t=e.headers||{};t=Object.assign(Object.assign({},e.headers),{"Content-Type":"application/json; charset=utf-8",Date:(new Date).toISOString(),"X-Client-Type":"Glean.js","X-Client-Version":W,"X-Telemetry-Agent":`Glean/0.23.0 (JS on ${yield this.platformInfo.os()})`});const i=JSON.stringify(e.payload),n=Gt(i);let r,s;try{r=Vt(n),s=r.length,t["Content-Encoding"]="gzip"}catch(e){r=i,s=n.length}if(s>this.policy.maxPingBodySize)throw new Zt(`Body for ping ${e.identifier} exceeds ${this.policy.maxPingBodySize}bytes. Discarding.`);return t["Content-Length"]=s.toString(),{headers:t,payload:r}}))}attemptPingUpload(e){return r(this,void 0,void 0,(function*(){if(!I.initialized)return a(Ht,"Attempted to upload a ping, but Glean is not initialized yet. Ignoring.",s.Warn),new c(0);try{const t=yield this.preparePingForUpload(e);return yield this.uploader.post(`${this.serverEndpoint}${e.path}`,t.payload,t.headers)}catch(e){return a(Ht,["Error trying to build ping request:",e],s.Warn),new c(0)}}))}concludePingProcessing(e){this.processing=this.processing.filter((t=>t.identifier!==e))}processPingUploadResponse(e,t){return r(this,void 0,void 0,(function*(){this.concludePingProcessing(e);const{status:i,result:n}=t;return i&&i>=200&&i<300?(a(Ht,`Ping ${e} succesfully sent ${i}.`,s.Info),yield this.pingsDatabase.deletePing(e),!1):1===n||i&&i>=400&&i<500?(a(Ht,`Unrecoverable upload failure while attempting to send ping ${e}. Error was: ${null!=i?i:"no status"}.`,s.Warn),yield this.pingsDatabase.deletePing(e),!1):(a(Ht,[`Recoverable upload failure while attempting to send ping ${e}, will retry.`,`Error was ${null!=i?i:"no status"}.`],s.Warn),!0)}))}update(e,t){this.dispatcher.resume(),this.enqueuePing(Object.assign({identifier:e,retries:0},t))}shutdown(){return this.dispatcher.shutdown()}clearPendingPingsQueue(){return r(this,void 0,void 0,(function*(){this.dispatcher.clear(),yield this.dispatcher.shutdown(),this.processing=[],this.dispatcher=Kt()}))}testBlockOnPingsQueue(){return r(this,void 0,void 0,(function*(){return this.dispatcher.testBlockOnQueue()}))}};class ei{constructor(){this.clientId=new Be({name:"client_id",category:"",sendInPings:["glean_client_info"],lifetime:"user",disabled:!1}),this.firstRunDate=new we({name:"first_run_date",category:"",sendInPings:["glean_client_info"],lifetime:"user",disabled:!1},ve.Day),this.os=new Ee({name:"os",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1}),this.osVersion=new Ee({name:"os_version",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1}),this.architecture=new Ee({name:"architecture",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1}),this.locale=new Ee({name:"locale",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1}),this.appChannel=new Ee({name:"app_channel",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1}),this.appBuild=new Ee({name:"app_build",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1}),this.appDisplayVersion=new Ee({name:"app_display_version",category:"",sendInPings:["glean_client_info"],lifetime:"application",disabled:!1})}initialize(e,t){return r(this,void 0,void 0,(function*(){yield this.initializeClientId(),yield this.initializeFirstRunDate(),yield Ee._private_setUndispatched(this.os,yield t.info.os()),yield Ee._private_setUndispatched(this.osVersion,yield t.info.osVersion(e.osVersion)),yield Ee._private_setUndispatched(this.architecture,yield t.info.arch(e.architecture)),yield Ee._private_setUndispatched(this.locale,yield t.info.locale()),yield Ee._private_setUndispatched(this.appBuild,e.appBuild||"Unknown"),yield Ee._private_setUndispatched(this.appDisplayVersion,e.appDisplayVersion||"Unknown"),e.channel&&(yield Ee._private_setUndispatched(this.appChannel,e.channel))}))}initializeClientId(){return r(this,void 0,void 0,(function*(){let e=!1;const t=yield I.metricsDatabase.getMetric(J,this.clientId);if(t)try{Qe("uuid",t).payload()===Q&&(e=!0)}catch(t){a("core.InternalMetrics","Unexpected value found for Glean clientId. Ignoring.",s.Warn),e=!0}else e=!0;e&&(yield Be._private_setUndispatched(this.clientId,O()))}))}initializeFirstRunDate(){return r(this,void 0,void 0,(function*(){(yield I.metricsDatabase.getMetric(J,this.firstRunDate))||(yield we._private_setUndispatched(this.firstRunDate))}))}}class ti{constructor(e,t,i,n){this.category=e,this.name=t,this.timestamp=i,this.extra=n}static toJSONObject(e){return{category:e.category,name:e.name,timestamp:e.timestamp,extra:e.extra}}static fromJSONObject(e){return new ti(e.category,e.name,e.timestamp,e.extra)}static withTransformedExtras(e,t){const i=t(e.extra||{});return new ti(e.category,e.name,e.timestamp,i&&Object.keys(i).length>0?i:void 0)}addExtra(e,t){this.extra||(this.extra={}),this.extra[e]=t}withoutReservedExtras(){return ti.withTransformedExtras(this,(e=>Object.keys(e).filter((e=>!Z.includes(e))).reduce(((t,i)=>(t[i]=e[i],t)),{})))}payload(){return ti.withTransformedExtras(this.withoutReservedExtras(),(e=>Object.keys(e).reduce(((t,i)=>(t[i]=e[i].toString(),t)),{})))}}class ii extends ce{constructor(e,t){super("event",e),this.allowedExtraKeys=t}static _private_recordUndispatched(e,t,n=z()){return r(this,void 0,void 0,(function*(){if(!e.shouldRecord(I.uploadEnabled))return;let r;if(t&&e.allowedExtraKeys){r={};for(const[n,s]of Object.entries(t))e.allowedExtraKeys.includes(n)?P(s)?r[n]=yield R(e,s,100):r[n]=s:yield I.errorManager.record(e,i.InvalidValue,`Invalid key index: ${n}`)}return I.eventsDatabase.record(e,new ti(e.category,e.name,n,r))}))}record(e){const t=z();I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){yield ii._private_recordUndispatched(this,e,t)}))))}testGetValue(e=this.sendInPings[0]){return r(this,void 0,void 0,(function*(){let t;return yield I.dispatcher.testLaunch((()=>r(this,void 0,void 0,(function*(){t=yield I.eventsDatabase.getEvents(e,this)})))),t}))}}n([A("core.metrics.EventMetricType")],ii.prototype,"testGetValue",null);const ni=ii;function ri(e){P(e)||(e="");const t=new Date(e);if(isNaN(t.getTime()))throw new Error(`Error attempting to generate Date object from string: ${e}`);return t}function si(e){return new pe(Object.assign(Object.assign({},{category:"glean",name:`reserved#${"execution_counter"}`}),{sendInPings:e,lifetime:"ping",disabled:!1}))}function ai(e){return new ni({category:"glean",name:"restarted",sendInPings:e,lifetime:"ping",disabled:!1},[K])}function oi(e,t=I.startTime){return r(this,void 0,void 0,(function*(){const i=ai(e);yield ni._private_recordUndispatched(i,{[K]:t.toISOString()},0)}))}const ci=class{constructor(e){this.initialized=!1,this.eventsStore=new e("events")}getAvailableStoreNames(){return r(this,void 0,void 0,(function*(){const e=yield this.eventsStore.get([]);return E(e)?[]:Object.keys(e)}))}initialize(){return r(this,void 0,void 0,(function*(){if(this.initialized)return;const e=yield this.getAvailableStoreNames();yield pe._private_addUndispatched(si(e),1),yield oi(e),this.initialized=!0}))}record(e,t){return r(this,void 0,void 0,(function*(){if(!e.disabled)for(const i of e.sendInPings){const e=si([i]);let n=yield I.metricsDatabase.getMetric(i,e);n||(yield pe._private_addUndispatched(e,1),n=1,yield oi([i],new Date)),t.addExtra(X,n);const r=e=>{var i;const n=null!==(i=e)&&void 0!==i?i:[];return n.push(ti.toJSONObject(t)),n};yield this.eventsStore.update([i],r)}}))}getEvents(e,t){return r(this,void 0,void 0,(function*(){const i=yield this.getAndValidatePingData(e);if(0!==i.length)return i.filter((e=>e.category===t.category&&e.name===t.name)).map((e=>e.withoutReservedExtras()))}))}getAndValidatePingData(e){return r(this,void 0,void 0,(function*(){const t=yield this.eventsStore.get([e]);return E(t)?[]:Array.isArray(t)?t.map((e=>ti.fromJSONObject(e))):(a("core.Metric.EventsDatabase",`Unexpected value found for ping ${e}: ${JSON.stringify(t)}. Clearing.`,s.Error),yield this.eventsStore.delete([e]),[])}))}getPingEvents(e,t){return r(this,void 0,void 0,(function*(){const i=yield this.getAndValidatePingData(e);if(t&&Object.keys(i).length>0&&(yield this.eventsStore.delete([e])),0===i.length)return;const n=yield this.prepareEventsPayload(e,i);return n.length>0?n:void 0}))}prepareEventsPayload(e,t){var n,s,a,o;return r(this,void 0,void 0,(function*(){const r=t.sort(((e,t)=>{var i,n;const r=Number(null===(i=e.extra)||void 0===i?void 0:i["#glean_execution_counter"]),s=Number(null===(n=t.extra)||void 0===n?void 0:n["#glean_execution_counter"]);return r!==s?r-s:e.timestamp-t.timestamp}));let c;try{c=ri(null===(n=r[0].extra)||void 0===n?void 0:n["#glean_reference_time"]),r.shift()}catch(e){c=I.startTime}const d=(null===(s=r[0])||void 0===s?void 0:s.timestamp)||0;let l=0;for(const[t,n]of r.entries()){try{const s=ri(null===(a=n.extra)||void 0===a?void 0:a["#glean_reference_time"]),o=s.getTime()-c.getTime();c=s;const d=l+o,u=r[t-1].timestamp;d<=u?(l=u+1,yield I.errorManager.record(ai([e]),i.InvalidValue,`Invalid time offset between application sessions found for ping "${e}". Ignoring.`)):l=d}catch(e){}let s;s=1===Number((null===(o=n.extra)||void 0===o?void 0:o["#glean_execution_counter"])||1)?n.timestamp-d:n.timestamp+l,r[t]=new ti(n.category,n.name,s,n.extra)}return r.map((e=>ti.toJSONObject(e.payload())))}))}clearAll(){return r(this,void 0,void 0,(function*(){yield this.eventsStore.delete([])}))}};const di={afterPingCollection:new class{constructor(e){this.name=e}get registeredPluginIdentifier(){var e;return null===(e=this.plugin)||void 0===e?void 0:e.name}registerPlugin(e){this.plugin?a("core.Events",[`Attempted to register plugin '${e.name}', which listens to the event '${e.event}'.`,`That event is already watched by plugin '${this.plugin.name}'`,`Plugin '${e.name}' will be ignored.`],s.Error):this.plugin=e}deregisterPlugin(){this.plugin=void 0}trigger(...e){if(this.plugin)return this.plugin.action(...e)}}("afterPingCollection")},li=di,ui="core.Pings.Maker";function hi(e){return r(this,void 0,void 0,(function*(){const{startTimeMetric:t,startTime:i}=yield function(e){return r(this,void 0,void 0,(function*(){const t=new we({category:"",name:`${e.name}#start`,sendInPings:[B],lifetime:"user",disabled:!1},ve.Minute),i=yield I.metricsDatabase.getMetric(B,t);let n;return n=i?new ye(i):ye.fromDate(I.startTime,ve.Minute),{startTimeMetric:t,startTime:n}}))}(e),n=new Date;yield we._private_setUndispatched(t,n);const s=ye.fromDate(n,ve.Minute);return{startTime:i.payload(),endTime:s.payload()}}))}function gi(e,t){return r(this,void 0,void 0,(function*(){const i=yield function(e){return r(this,void 0,void 0,(function*(){const t=new pe({category:"",name:`${e.name}#sequence`,sendInPings:[B],lifetime:"user",disabled:!1}),i=yield I.metricsDatabase.getMetric(B,t);if(yield pe._private_addUndispatched(t,1),i)try{return new he(i).payload()}catch(t){a(ui,`Unexpected value found for sequence number in ping ${e.name}. Ignoring.`,s.Warn)}return 0}))}(e),{startTime:n,endTime:o}=yield hi(e),c={seq:i,start_time:n,end_time:o};return t&&(c.reason=t),c}))}function pi(e,t){return r(this,void 0,void 0,(function*(){const i=yield I.eventsDatabase.getPingEvents(e.name,!0),n=yield I.metricsDatabase.getPingMetrics(e.name,!0);if(!n&&!i){if(!e.sendIfEmpty)return void a(ui,`Storage for ${e.name} empty. Bailing out.`,s.Info);a(ui,`Storage for ${e.name} empty. Ping will still be sent.`,s.Info)}const o=n?{metrics:n}:{},c=i?{events:i}:{},d=yield gi(e,t),l=yield function(e){return r(this,void 0,void 0,(function*(){let t=yield I.metricsDatabase.getPingMetrics(J,!0);t||(a(ui,"Empty client info data. Will submit anyways.",s.Warn),t={});let i={telemetry_sdk_build:W};for(const e in t)i=Object.assign(Object.assign({},i),t[e]);return e.includeClientId||delete i.client_id,i}))}(e);return Object.assign(Object.assign(Object.assign({},o),c),{ping_info:d,client_info:l})}))}const fi=function(e,t,i){return r(this,void 0,void 0,(function*(){const n=yield pi(t,i);if(!n)return;let r;try{r=yield li.afterPingCollection.trigger(n)}catch(e){return void a(ui,[`Error while attempting to modify ping payload for the "${t.name}" ping using`,`the ${JSON.stringify(li.afterPingCollection.registeredPluginIdentifier)} plugin.`,"Ping will not be submitted. See more logs below.\n\n",e],s.Error)}I.debugOptions.logPings&&a(ui,JSON.stringify(n,null,2),s.Info);const o=r||n,c=function(){var e,t;const i={};if((null===(e=I.debugOptions)||void 0===e?void 0:e.debugViewTag)&&(i["X-Debug-ID"]=I.debugOptions.debugViewTag),(null===(t=I.debugOptions)||void 0===t?void 0:t.sourceTags)&&(i["X-Source-Tags"]=I.debugOptions.sourceTags.toString()),Object.keys(i).length>0)return i}();return I.pingsDatabase.recordPing(function(e,t){return`/submit/${I.applicationId}/${t.name}/1/${e}`}(e,t),e,o,c)}))},vi="core.Pings.PingType";class mi{constructor(e){var t;this.name=e.name,this.includeClientId=e.includeClientId,this.sendIfEmpty=e.sendIfEmpty,this.reasonCodes=null!==(t=e.reasonCodes)&&void 0!==t?t:[]}isDeletionRequest(){return this.name===H}submit(e){this.testCallback?this.testCallback(e).then((()=>{mi._private_internalSubmit(this,e,this.resolveTestPromiseFunction)})).catch((t=>{a(vi,[`There was an error validating "${this.name}" (${null!=e?e:"no reason"}):`,t],s.Error),mi._private_internalSubmit(this,e,this.rejectTestPromiseFunction)})):mi._private_internalSubmit(this,e)}static _private_submitUndispatched(e,t,i){return r(this,void 0,void 0,(function*(){if(!I.initialized)return void a(vi,"Glean must be initialized before submitting pings.",s.Info);if(!I.uploadEnabled&&!e.isDeletionRequest())return void a(vi,"Glean disabled: not submitting pings. Glean may still submit the deletion-request ping.",s.Info);let n=t;t&&!e.reasonCodes.includes(t)&&(a(vi,`Invalid reason code ${t} from ${this.name}. Ignoring.`,s.Warn),n=void 0);const r=O();yield fi(r,e,n),i&&(i(),e.resolveTestPromiseFunction=void 0,e.rejectTestPromiseFunction=void 0,e.testCallback=void 0)}))}static _private_internalSubmit(e,t,i){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){yield mi._private_submitUndispatched(e,t,i)}))))}testBeforeNextSubmit(e){return r(this,void 0,void 0,(function*(){if(!this.testCallback)return new Promise(((t,i)=>{this.resolveTestPromiseFunction=t,this.rejectTestPromiseFunction=i,this.testCallback=e}));a(vi,`There is an existing test call for ping "${this.name}". Ignoring.`,s.Error)}))}}n([A(vi)],mi.prototype,"testBeforeNextSubmit",null);const yi=mi;const bi=class{constructor(){this.deletionRequest=new yi({name:H,includeClientId:!0,sendIfEmpty:!0})}};function wi(e){const t=e.event;if(t in li){li[t].registerPlugin(e)}else a("core.Events.Utils",[`Attempted to register plugin '${e.name}', which listens to the event '${e.event}'.`,"That is not a valid Glean event. Ignoring"],s.Error)}function _i(e,t){const i=function(e){return e.split("/")[0]}(e.baseIdentifier());return new pe({name:se(t,i),category:"glean.error",lifetime:"ping",sendInPings:e.sendInPings,disabled:!1})}class Ti{record(e,t,i,n=1){return r(this,void 0,void 0,(function*(){const r=_i(e,t);a(function(e){return`core.Metrics.${e.type.charAt(0).toUpperCase()+e.type.slice(1)}`}(e),[`${e.baseIdentifier()}:`,i]),n>0&&(yield pe._private_addUndispatched(r,n))}))}testGetNumRecordedErrors(e,t,i){return r(this,void 0,void 0,(function*(){const n=_i(e,t);return(yield n.testGetValue(i))||0}))}}let Ii={};const Si=class{constructor(e){this.rootKey=e}get(e=[]){try{const t=G(Ii,[this.rootKey,...e]);return Promise.resolve(t)}catch(e){return Promise.reject(e)}}update(e,t){try{return Ii=C(Ii,[this.rootKey,...e],t),Promise.resolve()}catch(e){return Promise.reject(e)}}delete(e){try{Ii=function(e,t){if(0===t.length)return{};const i=Object.assign({},e);let n=i;for(const e of t.slice(0,t.length-1)){const i=n[e];if(!x(i))throw Error(`Attempted to delete an entry from an inexistent index: ${JSON.stringify(t)}.`);n=i}return delete n[t[t.length-1]],i}(Ii,[this.rootKey,...e])}catch(t){a("plaftom.test.Storage",[`Error attempting to delete key ${e.toString()} from storage. Ignoring.`,t],s.Warn)}return Promise.resolve()}};const xi={os:()=>Promise.resolve("Unknown"),osVersion:()=>Promise.resolve("Unknown"),arch:()=>Promise.resolve("Unknown"),locale:()=>Promise.resolve("Unknown")},Ei={Storage:Si,uploader:new class extends d{post(e,t,i){const n=new c(2,200);return Promise.resolve(n)}},info:xi,name:"test"},Pi="core.Glean";class Di{constructor(){if(!E(Di._instance))throw new Error("Tried to instantiate Glean through `new`.\n Use Glean.instance instead to access the Glean singleton.");this._coreMetrics=new ei,this._corePings=new bi}static get instance(){return Di._instance||(Di._instance=new Di),Di._instance}static get pingUploader(){return Di.instance._pingUploader}static get coreMetrics(){return Di.instance._coreMetrics}static get corePings(){return Di.instance._corePings}static onUploadEnabled(){return r(this,void 0,void 0,(function*(){I.uploadEnabled=!0,yield Di.coreMetrics.initialize(Di.instance._config,Di.platform)}))}static onUploadDisabled(){return r(this,void 0,void 0,(function*(){I.uploadEnabled=!1,yield yi._private_submitUndispatched(Di.corePings.deletionRequest),yield Di.clearMetrics()}))}static clearMetrics(){return r(this,void 0,void 0,(function*(){let e;yield Di.pingUploader.clearPendingPingsQueue();try{e=new ye(yield I.metricsDatabase.getMetric(J,Di.coreMetrics.firstRunDate)).date}catch(t){e=new Date}yield I.eventsDatabase.clearAll(),yield I.metricsDatabase.clearAll(),yield I.pingsDatabase.clearAll(),I.uploadEnabled=!0,yield Be._private_setUndispatched(Di.coreMetrics.clientId,Q),yield we._private_setUndispatched(Di.coreMetrics.firstRunDate,e),I.uploadEnabled=!1}))}static initialize(e,t,i){if(I.initialized)return void a(Pi,"Attempted to initialize Glean, but it has already been initialized. Ignoring.",s.Warn);if(0===e.length)return void a(Pi,"Unable to initialize Glean, applicationId cannot be an empty string.",s.Error);if(!Di.instance._platform)return void a(Pi,"Unable to initialize Glean, environment has not been set.",s.Error);I.applicationId=function(e){return e.replace(/[^a-z0-9]+/gi,"-").toLowerCase()}(e);const n=new ee(i);if(I.debugOptions=n.debug,Di.instance._config=n,I.metricsDatabase=new Xe(Di.platform.Storage),I.eventsDatabase=new ci(Di.platform.Storage),I.pingsDatabase=new Wt(Di.platform.Storage),I.errorManager=new Ti,Di.instance._pingUploader=new Yt(n,Di.platform,I.pingsDatabase),I.pingsDatabase.attachObserver(Di.pingUploader),null==i?void 0:i.plugins)for(const e of i.plugins)wi(e);I.dispatcher.flushInit((()=>r(this,void 0,void 0,(function*(){if(I.initialized=!0,yield I.metricsDatabase.clear("application"),t)yield Di.onUploadEnabled();else{const e=yield I.metricsDatabase.getMetric(J,Di.coreMetrics.clientId);e?e!==Q&&(yield Di.onUploadDisabled()):yield Di.clearMetrics()}yield I.eventsDatabase.initialize(),yield I.pingsDatabase.scanPendingPings()}))))}static get serverEndpoint(){var e;return null===(e=Di.instance._config)||void 0===e?void 0:e.serverEndpoint}static get logPings(){var e,t;return(null===(t=null===(e=Di.instance._config)||void 0===e?void 0:e.debug)||void 0===t?void 0:t.logPings)||!1}static get debugViewTag(){var e;return null===(e=Di.instance._config)||void 0===e?void 0:e.debug.debugViewTag}static get sourceTags(){var e,t;return null===(t=null===(e=Di.instance._config)||void 0===e?void 0:e.debug.sourceTags)||void 0===t?void 0:t.toString()}static get platform(){if(!Di.instance._platform)throw new Error("IMPOSSIBLE: Attempted to access environment specific APIs before Glean was initialized.");return Di.instance._platform}static setUploadEnabled(e){I.dispatcher.launch((()=>r(this,void 0,void 0,(function*(){I.initialized?I.uploadEnabled!==e&&(e?yield Di.onUploadEnabled():yield Di.onUploadDisabled()):a(Pi,["Changing upload enabled before Glean is initialized is not supported.\n","Pass the correct state into `Glean.initialize\n`.","See documentation at https://mozilla.github.io/glean/book/user/general-api.html#initializing-the-glean-sdk`"],s.Error)}))))}static setLogPings(e){I.dispatcher.launch((()=>(Di.instance._config.debug.logPings=e,Promise.resolve())))}static setDebugViewTag(e){ee.validateDebugViewTag(e)?I.dispatcher.launch((()=>(Di.instance._config.debug.debugViewTag=e,Promise.resolve()))):a(Pi,`Invalid \`debugViewTag\` ${e}. Ignoring.`,s.Error)}static setSourceTags(e){ee.validateSourceTags(e)?I.dispatcher.launch((()=>(Di.instance._config.debug.sourceTags=e,Promise.resolve()))):a(Pi,`Invalid \`sourceTags\` ${e.toString()}. Ignoring.`,s.Error)}static shutdown(){return r(this,void 0,void 0,(function*(){yield I.dispatcher.shutdown(),yield Di.pingUploader.shutdown()}))}static setPlatform(e){I.initialized||(Di.instance._platform&&Di.instance._platform.name!==e.name&&!I.testing&&a(Pi,[`IMPOSSIBLE: Attempted to change Glean's targeted platform",\n "from "${Di.platform.name}" to "${e.name}". Ignoring.`],s.Error),Di.instance._platform=e)}static testInitialize(e,t=!0,i){return r(this,void 0,void 0,(function*(){I.testing=!0,Di.setPlatform(Ei),Di.initialize(e,t,i),yield I.dispatcher.testBlockOnQueue()}))}static testUninitialize(e=!0){return r(this,void 0,void 0,(function*(){I.initialized&&(yield Di.shutdown(),e&&(yield I.eventsDatabase.clearAll(),yield I.metricsDatabase.clearAll(),yield I.pingsDatabase.clearAll()),I.testUninitialize(),function(){for(const e in li)li[e].deregisterPlugin()}())}))}static testResetGlean(e,t=!0,i,n=!0){return r(this,void 0,void 0,(function*(){yield Di.testUninitialize(n),yield Di.testInitialize(e,t,i)}))}}const Mi=Di,$i=Object.assign(Object.assign({},(Ui=F,{initialize(e,t,i){Mi.setPlatform(Ui),Mi.initialize(e,t,i)},setUploadEnabled(e){Mi.setUploadEnabled(e)},setLogPings(e){Mi.setLogPings(e)},setDebugViewTag(e){Mi.setDebugViewTag(e)},shutdown:()=>Mi.shutdown(),setSourceTags(e){Mi.setSourceTags(e)},testResetGlean(e,t=!0,i){return r(this,void 0,void 0,(function*(){return Mi.testResetGlean(e,t,i)}))}})),{ErrorType:i,_private:{PingType:yi,BooleanMetricType:ue,CounterMetricType:pe,DatetimeMetricType:we,EventMetricType:ni,LabeledMetricType:oe,QuantityMetricType:Ie,StringMetricType:Ee,StringListMetricType:$e,TimespanMetricType:Ve,TextMetricType:ze,UUIDMetricType:Be,URLMetricType:Ne}});var Ui})(),Glean=t})(); \ No newline at end of file diff --git a/glean/org/mozilla/Glean/qmldir b/glean/org/mozilla/Glean/qmldir index 5a70ce5183..0fd1ad4b28 100644 --- a/glean/org/mozilla/Glean/qmldir +++ b/glean/org/mozilla/Glean/qmldir @@ -1,2 +1,2 @@ module org.mozilla.Glean -Glean 0.15 glean.js +Glean 0.23 glean.js diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..37f6fdb085 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask +glean_parser==4.1.1 +lxml +PyYAML \ No newline at end of file diff --git a/scripts/generate_glean.py b/scripts/generate_glean.py index 620398d7d6..519a59b3b1 100755 --- a/scripts/generate_glean.py +++ b/scripts/generate_glean.py @@ -51,8 +51,8 @@ def camelize(string): try: subprocess.call(["glean_parser", "translate", "glean/metrics.yaml", "glean/pings.yaml", "-f", "javascript", "-o", "glean/telemetry", "--option", "platform=qt", - "--option", "version=0.15"]) + "--option", "version=0.23"]) except: - print("glean_parser failed. Is it installed? Try with:\n\tpip3 install glean_parser"); + print("glean_parser failed. Is it installed? Try with:\n\tpip3 install -r requirements.txt --user"); exit(1) diff --git a/scripts/xcode_patcher.rb b/scripts/xcode_patcher.rb index 5511fc5a15..77578cce83 100644 --- a/scripts/xcode_patcher.rb +++ b/scripts/xcode_patcher.rb @@ -96,6 +96,8 @@ def setup_target_main(shortVersion, fullVersion, platform, networkExtension, con else groupId = configHash['GROUP_ID_IOS'] config.build_settings['GROUP_ID_IOS'] ||= configHash['GROUP_ID_IOS'] + # Force xcode to not set QT_LIBRARY_SUFFIX to "_debug", which causes crash + config.build_settings['QT_LIBRARY_SUFFIX'] = "" end config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ diff --git a/src/adjust/adjustfiltering.cpp b/src/adjust/adjustfiltering.cpp index 8c6f44fdda..fd976405bc 100644 --- a/src/adjust/adjustfiltering.cpp +++ b/src/adjust/adjustfiltering.cpp @@ -58,7 +58,7 @@ QUrlQuery AdjustFiltering::filterParameters(QUrlQuery& parameters, QStringList& unknownParameters) { QUrlQuery newParameters; -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG // We use the binary-search algorithm. The arrays must be alphabetically // sorted. Let's check this in debug builds. diff --git a/src/closeeventhandler.cpp b/src/closeeventhandler.cpp index 0decaa284d..7978c707e5 100644 --- a/src/closeeventhandler.cpp +++ b/src/closeeventhandler.cpp @@ -92,21 +92,21 @@ void CloseEventHandler::removeItem(QObject* item) { logger.debug() << "Remove item"; Q_ASSERT(item); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG bool found = false; #endif for (int i = 0; i < m_layers.length(); ++i) { if (m_layers.at(i).m_layer == item) { m_layers.removeAt(i); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG found = true; #endif break; } } -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG Q_ASSERT(found); #endif } diff --git a/src/commandlineparser.cpp b/src/commandlineparser.cpp index 9d720ae7b0..84f893a013 100644 --- a/src/commandlineparser.cpp +++ b/src/commandlineparser.cpp @@ -43,7 +43,7 @@ int CommandLineParser::parse(int argc, char* argv[]) { QStringList tokens; for (int i = 0; i < argc; ++i) { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG if (QString(argv[i]).startsWith("-qmljsdebugger")) { continue; } diff --git a/src/commands/commandui.cpp b/src/commands/commandui.cpp index 928b904dec..da45c4c6ac 100644 --- a/src/commands/commandui.cpp +++ b/src/commands/commandui.cpp @@ -64,11 +64,6 @@ #include -#ifdef QT_DEBUG -# include "gleantest.h" -# include -#endif - namespace { Logger logger(LOG_MAIN, "CommandUI"); } @@ -138,11 +133,6 @@ int CommandUI::run(QStringList& tokens) { MozillaVPN vpn; vpn.setStartMinimized(minimizedOption.m_set); -#ifdef QT_DEBUG - // This is a collector of glean HTTP requests to see if we leak something. - GleanTest gleanTest; -#endif - #if defined(MVPN_WINDOWS) || defined(MVPN_LINUX) // If there is another instance, the execution terminates here. if (!EventListener::checkOtherInstances()) { @@ -379,16 +369,6 @@ int CommandUI::run(QStringList& tokens) { }); } -#ifdef QT_DEBUG - qmlRegisterSingletonType( - "Mozilla.VPN", 1, 0, "VPNGleanTest", - [](QQmlEngine*, QJSEngine*) -> QObject* { - QObject* obj = GleanTest::instance(); - QQmlEngine::setObjectOwnership(obj, QQmlEngine::CppOwnership); - return obj; - }); -#endif - qmlRegisterSingletonType( "Mozilla.VPN", 1, 0, "VPNAuthInApp", [](QQmlEngine*, QJSEngine*) -> QObject* { diff --git a/src/connectionhealth.cpp b/src/connectionhealth.cpp index 3e54c1c0a4..016900ef65 100644 --- a/src/connectionhealth.cpp +++ b/src/connectionhealth.cpp @@ -76,10 +76,10 @@ void ConnectionHealth::setStability(ConnectionStability stability) { if (stability == Unstable) { MozillaVPN::instance()->silentSwitch(); - emit MozillaVPN::instance()->triggerGleanSample( + emit MozillaVPN::instance()->recordGleanEvent( GleanSample::connectionHealthUnstable); } else if (stability == NoSignal) { - emit MozillaVPN::instance()->triggerGleanSample( + emit MozillaVPN::instance()->recordGleanEvent( GleanSample::connectionHealthNoSignal); } @@ -107,7 +107,7 @@ void ConnectionHealth::connectionStateChanged() { } void ConnectionHealth::pingSentAndReceived(qint64 msec) { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG logger.debug() << "Ping answer received in msec:" << msec; #else Q_UNUSED(msec); diff --git a/src/constants.h b/src/constants.h index ba9c2107e3..7345bc0522 100644 --- a/src/constants.h +++ b/src/constants.h @@ -62,7 +62,7 @@ CONSTEXPR(uint32_t, captivePortalRequestTimeoutMsec, 10000, 4000, 0) CONSTEXPR(uint32_t, statusIconAnimationMsec, 200, 200, 0) // How often glean pings are sent -CONSTEXPR(uint32_t, gleanTimeoutMsec, 1200000, 1000, 0) +CONSTEXPR(uint32_t, gleanTimeoutMsec, 1200000, 4000, 0) // How often we check the surveys to be executed (no network requests are done // for this check) diff --git a/src/featurelist.cpp b/src/featurelist.cpp index c6cef93d57..a022041201 100644 --- a/src/featurelist.cpp +++ b/src/featurelist.cpp @@ -10,7 +10,6 @@ #include "features/featureappreview.h" #include "features/featurecaptiveportal.h" #include "features/featurecustomdns.h" -#include "features/featureglean.h" #include "features/featureinappaccountcreate.h" #include "features/featureinappauth.h" #include "features/featureinapppurchase.h" @@ -44,7 +43,6 @@ void FeatureList::initialize() { new FeatureAppReview(); new FeatureCaptivePortal(); new FeatureCustomDNS(); - new FeatureGlean(); new FeatureInAppAccountCreate(); new FeatureInAppAuth(); new FeatureInAppPurchase(); diff --git a/src/features/featureglean.h b/src/features/featureglean.h deleted file mode 100644 index bc24a4f6b8..0000000000 --- a/src/features/featureglean.h +++ /dev/null @@ -1,40 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef FEATURE_GLEAN_H -#define FEATURE_GLEAN_H - -#include "models/feature.h" - -constexpr const char* FEATURE_GLEAN = "glean"; - -class FeatureGlean : public Feature { - public: - FeatureGlean() - : Feature(FEATURE_GLEAN, "Glean", - false, // Is Major Feature - L18nStrings::Empty, // Display name - L18nStrings::Empty, // Description - L18nStrings::Empty, // LongDescr - "", // ImagePath - "", // IconPath - "2.5", // released - false // Can be enabled in devmode - ) {} - - bool checkSupportCallback() const override { -#if defined(MVPN_IOS) - // https://github.com/mozilla-mobile/mozilla-vpn-client/issues/1599 - return false; -#else - return true; -#endif - } - - static const FeatureGlean* instance() { - return static_cast(get(FEATURE_GLEAN)); - } -}; - -#endif // FEATURE_GLEAN_H diff --git a/src/gleantest.cpp b/src/gleantest.cpp deleted file mode 100644 index 8f82c4c4ac..0000000000 --- a/src/gleantest.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "gleantest.h" -#include "leakdetector.h" -#include "logger.h" - -namespace { -Logger logger(LOG_MAIN, "GleanTest"); - -GleanTest* s_instance = nullptr; -} // namespace - -GleanTest::GleanTest() { - MVPN_COUNT_CTOR(GleanTest); - - Q_ASSERT(!s_instance); - s_instance = this; -} - -GleanTest::~GleanTest() { - MVPN_COUNT_DTOR(GleanTest); - - Q_ASSERT(s_instance == this); - s_instance = nullptr; -} - -// static -GleanTest* GleanTest::instance() { - Q_ASSERT(s_instance); - return s_instance; -} - -void GleanTest::requestDone(const QByteArray& url, const QByteArray& data) { - logger.debug() << "Glean request stored"; - - m_lastUrl = url; - m_lastData = data; -} - -void GleanTest::reset() { - m_lastUrl.clear(); - m_lastData.clear(); -} diff --git a/src/gleantest.h b/src/gleantest.h deleted file mode 100644 index 5616866bc2..0000000000 --- a/src/gleantest.h +++ /dev/null @@ -1,32 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef GLEANTEST_H -#define GLEANTEST_H - -#include - -class GleanTest final : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(GleanTest) - - public: - GleanTest(); - ~GleanTest(); - - static GleanTest* instance(); - - const QByteArray& lastUrl() const { return m_lastUrl; } - const QByteArray& lastData() const { return m_lastData; } - - void reset(); - - Q_INVOKABLE void requestDone(const QByteArray& url, const QByteArray& data); - - private: - QByteArray m_lastUrl; - QByteArray m_lastData; -}; - -#endif // GLEANTEST_H diff --git a/src/inspector/inspectorwebsocketconnection.cpp b/src/inspector/inspectorwebsocketconnection.cpp index c6d0ed26fb..967a7a8614 100644 --- a/src/inspector/inspectorwebsocketconnection.cpp +++ b/src/inspector/inspectorwebsocketconnection.cpp @@ -13,10 +13,6 @@ #include "serveri18n.h" #include "settingsholder.h" -#ifdef QT_DEBUG -# include "gleantest.h" -#endif - #include #include @@ -218,6 +214,17 @@ static QList s_settingCommands{ return SettingsHolder::instance()->gleanEnabled() ? "true" : "false"; }}, + // telemetry-policy-shown + WebSocketSettingCommand{ + "telemetry-policy-shown", WebSocketSettingCommand::Boolean, + [](const QByteArray& value) { + SettingsHolder::instance()->setTelemetryPolicyShown(value == "true"); + }, + []() { + return SettingsHolder::instance()->telemetryPolicyShown() ? "true" + : "false"; + }}, + }; struct WebSocketCommand { @@ -646,23 +653,6 @@ static QList s_commands{ return QJsonObject(); }}, -#ifdef QT_DEBUG - WebSocketCommand{"last_glean_request", "Retrieve the last glean request", 0, - [](const QList&) { - GleanTest* gt = GleanTest::instance(); - - QJsonObject glean; - glean["url"] = QString(gt->lastUrl()); - glean["data"] = QString(gt->lastData()); - - gt->reset(); - - QJsonObject obj; - obj["value"] = glean; - return obj; - }}, -#endif - WebSocketCommand{"devices", "Retrieve the list of devices", 0, [](const QList&) { MozillaVPN* vpn = MozillaVPN::instance(); diff --git a/src/leakdetector.cpp b/src/leakdetector.cpp index 146c7282e6..7a092a8a74 100644 --- a/src/leakdetector.cpp +++ b/src/leakdetector.cpp @@ -9,20 +9,20 @@ #include #include -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG static QMutex s_leakDetector; QHash> s_leaks; #endif LeakDetector::LeakDetector() { -#ifndef QT_DEBUG +#ifndef MVPN_DEBUG qFatal("LeakDetector _must_ be created in debug builds only!"); #endif } LeakDetector::~LeakDetector() { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG QTextStream out(stderr); out << "== Mozilla VPN - Leak report ===================" << Qt::endl; @@ -49,7 +49,7 @@ LeakDetector::~LeakDetector() { #endif } -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG void LeakDetector::logCtor(void* ptr, const char* typeName, uint32_t size) { QMutexLocker lock(&s_leakDetector); diff --git a/src/leakdetector.h b/src/leakdetector.h index b2657d93cc..3016137c19 100644 --- a/src/leakdetector.h +++ b/src/leakdetector.h @@ -7,7 +7,7 @@ #include -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG # define MVPN_COUNT_CTOR(_type) \ do { \ static_assert(std::is_class<_type>(), \ @@ -32,7 +32,7 @@ class LeakDetector { LeakDetector(); ~LeakDetector(); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG static void logCtor(void* ptr, const char* typeName, uint32_t size); static void logDtor(void* ptr, const char* typeName, uint32_t size); #endif diff --git a/src/logger.cpp b/src/logger.cpp index 317789bb30..3620bab6d5 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -53,7 +53,7 @@ Logger::Log& Logger::Log::operator<<(QTextStreamFunction t) { // static QString Logger::sensitive(const QString& input) { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG return input; #else return QString(input.length(), 'X'); diff --git a/src/loghandler.cpp b/src/loghandler.cpp index 299d340c50..c3e877c145 100644 --- a/src/loghandler.cpp +++ b/src/loghandler.cpp @@ -167,7 +167,7 @@ LogHandler::LogHandler(LogLevel minLogLevel, const QStringList& modules, : m_minLogLevel(minLogLevel), m_modules(modules) { Q_UNUSED(proofOfLock); -#if defined(QT_DEBUG) || defined(MVPN_WASM) +#if defined(MVPN_DEBUG) || defined(MVPN_WASM) m_showDebug = true; #endif @@ -202,7 +202,7 @@ void LogHandler::addLog(const Log& log, const MutexLocker& proofOfLock) { emit logEntryAdded(buffer); -#if defined(MVPN_ANDROID) && defined(QT_DEBUG) +#if defined(MVPN_ANDROID) && defined(MVPN_DEBUG) const char* str = buffer.constData(); if (str) { __android_log_write(ANDROID_LOG_DEBUG, "mozillavpn", str); diff --git a/src/main.cpp b/src/main.cpp index fb1f957035..f8d3c16d19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,7 @@ #include "leakdetector.h" int main(int argc, char* argv[]) { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG LeakDetector leakDetector; Q_UNUSED(leakDetector); #endif diff --git a/src/models/device.cpp b/src/models/device.cpp index 46f57f1b68..0c158b3adb 100644 --- a/src/models/device.cpp +++ b/src/models/device.cpp @@ -15,7 +15,7 @@ # include #endif -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG # include #endif @@ -60,6 +60,7 @@ QString Device::currentDeviceReport() { QTextStream out(&buffer); out << "Name -> " << currentDeviceName() << Qt::endl; out << "ABI -> " << QSysInfo::buildAbi() << Qt::endl; + out << "Machine arch -> " << QSysInfo::currentCpuArchitecture() << Qt::endl; out << "OS -> " << QSysInfo::productType() << Qt::endl; #ifdef MVPN_WINDOWS out << "OS Version -> " << WindowsCommons::WindowsVersion() << Qt::endl; diff --git a/src/mozillavpn.cpp b/src/mozillavpn.cpp index d0d54f1ea2..f1a5d65f18 100644 --- a/src/mozillavpn.cpp +++ b/src/mozillavpn.cpp @@ -178,10 +178,6 @@ MozillaVPN::MozillaVPN() : m_private(new Private()) { connect(iap, &IAPHandler::subscriptionNotValidated, this, &MozillaVPN::subscriptionNotValidated); } - - connect(&m_gleanTimer, &QTimer::timeout, this, &MozillaVPN::sendGleanPings); - m_gleanTimer.start(Constants::gleanTimeoutMsec()); - m_gleanTimer.setSingleShot(false); } MozillaVPN::~MozillaVPN() { @@ -199,6 +195,14 @@ MozillaVPN::State MozillaVPN::state() const { return m_state; } bool MozillaVPN::stagingMode() const { return !Constants::inProduction(); } +bool MozillaVPN::debugMode() const { +#ifdef MVPN_DEBUG + return true; +#else + return false; +#endif +} + void MozillaVPN::initialize() { logger.debug() << "MozillaVPN Initialization"; @@ -356,7 +360,7 @@ void MozillaVPN::maybeStateMain() { if (!m_private->m_deviceModel.hasCurrentDevice(keys())) { Q_ASSERT(m_private->m_deviceModel.activeDevices() == m_private->m_user.maxDevices()); - emit triggerGleanSample(GleanSample::maxDeviceReached); + emit recordGleanEvent(GleanSample::maxDeviceReached); setState(StateDeviceLimit); return; } @@ -416,7 +420,7 @@ void MozillaVPN::authenticateWithType( return; } - emit triggerGleanSample(GleanSample::authenticationStarted); + emit recordGleanEvent(GleanSample::authenticationStarted); scheduleTask(new TaskHeartbeat()); @@ -428,7 +432,7 @@ void MozillaVPN::abortAuthentication() { Q_ASSERT(m_state == StateAuthenticating); setState(StateInitialize); - emit triggerGleanSample(GleanSample::authenticationAborted); + emit recordGleanEvent(GleanSample::authenticationAborted); } void MozillaVPN::openLink(LinkType linkType) { @@ -560,7 +564,7 @@ void MozillaVPN::authenticationCompleted(const QByteArray& json, const QString& token) { logger.debug() << "Authentication completed"; - emit triggerGleanSample(GleanSample::authenticationCompleted); + emit recordGleanEvent(GleanSample::authenticationCompleted); if (!m_private->m_user.fromJson(json)) { logger.error() << "Failed to parse the User JSON data"; @@ -855,7 +859,7 @@ void MozillaVPN::cancelAuthentication() { return; } - emit triggerGleanSample(GleanSample::authenticationAborted); + emit recordGleanEvent(GleanSample::authenticationAborted); reset(true); } @@ -997,9 +1001,9 @@ void MozillaVPN::errorHandle(ErrorHandler::ErrorType error) { // Any error in authenticating state sends to the Initial state. if (m_state == StateAuthenticating) { if (alert == GeoIpRestrictionAlert) { - emit triggerGleanSample(GleanSample::authenticationFailureByGeo); + emit recordGleanEvent(GleanSample::authenticationFailureByGeo); } else { - emit triggerGleanSample(GleanSample::authenticationFailure); + emit recordGleanEvent(GleanSample::authenticationFailure); } setState(StateInitialize); return; @@ -1067,6 +1071,21 @@ void MozillaVPN::postAuthenticationCompleted() { maybeStateMain(); } +void MozillaVPN::mainWindowLoaded() { + logger.debug() << "main window loaded"; + +#ifndef MVPN_WASM + // Initialize glean + logger.debug() << "Initializing Glean"; + emit initializeGlean(); + + // Setup regular glean ping sending + connect(&m_gleanTimer, &QTimer::timeout, this, &MozillaVPN::sendGleanPings); + m_gleanTimer.start(Constants::gleanTimeoutMsec()); + m_gleanTimer.setSingleShot(false); +#endif +} + void MozillaVPN::telemetryPolicyCompleted() { logger.debug() << "telemetry policy completed"; diff --git a/src/mozillavpn.h b/src/mozillavpn.h index f3d80c997d..8e17cd2175 100644 --- a/src/mozillavpn.h +++ b/src/mozillavpn.h @@ -96,6 +96,8 @@ class MozillaVPN final : public QObject { Q_PROPERTY(AlertType alert READ alert NOTIFY alertChanged) Q_PROPERTY(QString versionString READ versionString CONSTANT) Q_PROPERTY(QString buildNumber READ buildNumber CONSTANT) + Q_PROPERTY(QString osVersion READ osVersion CONSTANT) + Q_PROPERTY(QString architecture READ architecture CONSTANT) Q_PROPERTY(bool updateRecommended READ updateRecommended NOTIFY updateRecommendedChanged) Q_PROPERTY(bool userAuthenticated READ userAuthenticated NOTIFY @@ -103,6 +105,7 @@ class MozillaVPN final : public QObject { Q_PROPERTY(bool startMinimized READ startMinimized CONSTANT) Q_PROPERTY(bool updating READ updating NOTIFY updatingChanged) Q_PROPERTY(bool stagingMode READ stagingMode CONSTANT) + Q_PROPERTY(bool debugMode READ debugMode CONSTANT) Q_PROPERTY(QString currentView READ currentView WRITE setCurrentView NOTIFY currentViewChanged) @@ -124,6 +127,7 @@ class MozillaVPN final : public QObject { const QString& serverPublicKey() const { return m_serverPublicKey; } bool stagingMode() const; + bool debugMode() const; enum AuthenticationType { AuthenticationInBrowser, @@ -140,6 +144,7 @@ class MozillaVPN final : public QObject { Q_INVOKABLE void hideUpdateRecommendedAlert() { setUpdateRecommended(false); } Q_INVOKABLE void postAuthenticationCompleted(); Q_INVOKABLE void telemetryPolicyCompleted(); + Q_INVOKABLE void mainWindowLoaded(); Q_INVOKABLE bool viewLogs(); Q_INVOKABLE void retrieveLogs(); Q_INVOKABLE void cleanupLogs(); @@ -232,8 +237,17 @@ class MozillaVPN final : public QObject { void silentSwitch(); const QString versionString() const { return QString(APP_VERSION); } - const QString buildNumber() const { return QString(BUILD_ID); } + const QString osVersion() const { +#ifdef MVPN_WINDOWS + return WindowsCommons::WindowsVersion(); +#else + return QSysInfo::productVersion(); +#endif + } + const QString architecture() const { + return QSysInfo::currentCpuArchitecture(); + } void logout(); @@ -350,8 +364,9 @@ class MozillaVPN final : public QObject { void updatingChanged(); // For Glean + void initializeGlean(); void sendGleanPings(); - void triggerGleanSample(const QString& gleanSampleName); + void recordGleanEvent(const QString& gleanSampleName); void aboutToQuit(); diff --git a/src/networkrequest.cpp b/src/networkrequest.cpp index 36060d1836..2a46df5836 100644 --- a/src/networkrequest.cpp +++ b/src/networkrequest.cpp @@ -215,7 +215,7 @@ NetworkRequest* NetworkRequest::createForDeviceRemoval(QObject* parent, QUrl u(url); r->m_request.setUrl(QUrl(url)); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG logger.debug() << "Network starting" << r->m_request.url().toString(); #endif diff --git a/src/pinghelper.cpp b/src/pinghelper.cpp index c4422f9a93..3222db6c3c 100644 --- a/src/pinghelper.cpp +++ b/src/pinghelper.cpp @@ -94,7 +94,7 @@ void PingHelper::stop() { } void PingHelper::nextPing() { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG logger.debug() << "Sending ping seq:" << m_sequence; #endif @@ -116,7 +116,7 @@ void PingHelper::pingReceived(quint16 sequence) { qint64 sendTime = m_pingData[index].timestamp; m_pingData[index].latency = QDateTime::currentMSecsSinceEpoch() - sendTime; emit pingSentAndReceived(m_pingData[index].latency); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG logger.debug() << "Ping answer received seq:" << sequence << "avg:" << latency() << "loss:" << QString("%1%").arg(loss() * 100.0) diff --git a/src/platforms/android/androiddatamigration.cpp b/src/platforms/android/androiddatamigration.cpp index cc5c2a8b02..a8076f2fb6 100644 --- a/src/platforms/android/androiddatamigration.cpp +++ b/src/platforms/android/androiddatamigration.cpp @@ -22,7 +22,7 @@ namespace { Logger logger(LOG_ANDROID, "AndroidDataMigration"); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG const QString MIGRATION_FILE = "org.mozilla.firefox.vpn.debug_preferences.xml"; #else const QString MIGRATION_FILE = "org.mozilla.firefox.vpn_preferences.xml"; diff --git a/src/platforms/android/androidsharedprefs.cpp b/src/platforms/android/androidsharedprefs.cpp index 5ee182c16f..1353c637ac 100644 --- a/src/platforms/android/androidsharedprefs.cpp +++ b/src/platforms/android/androidsharedprefs.cpp @@ -13,7 +13,7 @@ namespace { Logger logger(LOG_ANDROID, "AndroidSharedPrefs"); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG const QString SHARED_PREF_FOLDER = "/data/data/org.mozilla.firefox.vpn.debug/shared_prefs"; #else diff --git a/src/platforms/android/androidwebview.cpp b/src/platforms/android/androidwebview.cpp index 72e1c5841b..63f8a507a6 100644 --- a/src/platforms/android/androidwebview.cpp +++ b/src/platforms/android/androidwebview.cpp @@ -11,7 +11,6 @@ #include "networkmanager.h" #include -#include #include #include #include diff --git a/src/platforms/ios/iosauthenticationlistener.mm b/src/platforms/ios/iosauthenticationlistener.mm index 0ea2d94c54..300ea80809 100644 --- a/src/platforms/ios/iosauthenticationlistener.mm +++ b/src/platforms/ios/iosauthenticationlistener.mm @@ -72,7 +72,7 @@ QUrl url(createAuthenticationUrl(MozillaVPN::AuthenticationInBrowser, codeChalle query.addQueryItem("platform", "ios"); url.setQuery(query); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG logger.debug() << "Authentication URL:" << url.toString(); #endif diff --git a/src/platforms/linux/daemon/polkithelper.cpp b/src/platforms/linux/daemon/polkithelper.cpp index 34838de0e6..ea8bc0d501 100644 --- a/src/platforms/linux/daemon/polkithelper.cpp +++ b/src/platforms/linux/daemon/polkithelper.cpp @@ -3,13 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "polkithelper.h" - -#include +#include "logger.h" // No extra QT includes after this line! #undef Q_SIGNALS #include "polkit/polkit.h" +namespace { +Logger logger(LOG_LINUX, "PolkitHelper"); +} // namespace + class Helper final { public: Helper() = default; @@ -40,14 +43,14 @@ PolkitHelper* PolkitHelper::instance() { } bool PolkitHelper::checkAuthorization(const QString& actionId) { - qDebug() << "Check Authorization for" << actionId; + logger.debug() << "Check Authorization for" << actionId; Helper h; PolkitAuthority* authority = polkit_authority_get_sync(NULL, &h.m_error); if (h.m_error) { - qDebug() << "Fail to generate a polkit authority object:" - << h.m_error->message; + logger.debug() << "Fail to generate a polkit authority object:" + << h.m_error->message; return false; } @@ -58,7 +61,7 @@ bool PolkitHelper::checkAuthorization(const QString& actionId) { POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, nullptr, &h.m_error); if (h.m_error) { - qDebug() << "Authorization sync failed:" << h.m_error->message; + logger.debug() << "Authorization sync failed:" << h.m_error->message; return false; } diff --git a/src/platforms/macos/daemon/wireguardutilsmacos.cpp b/src/platforms/macos/daemon/wireguardutilsmacos.cpp index 017e1e411a..bda917d0c1 100644 --- a/src/platforms/macos/daemon/wireguardutilsmacos.cpp +++ b/src/platforms/macos/daemon/wireguardutilsmacos.cpp @@ -68,7 +68,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) { QProcessEnvironment pe = QProcessEnvironment::systemEnvironment(); QString wgNameFile = wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name"); pe.insert("WG_TUN_NAME_FILE", wgNameFile); -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG pe.insert("LOG_LEVEL", "debug"); #endif m_tunnel.setProcessEnvironment(pe); diff --git a/src/settingslist.h b/src/settingslist.h index 554e99b324..5449b08688 100644 --- a/src/settingslist.h +++ b/src/settingslist.h @@ -156,6 +156,9 @@ SETTING_BOOL(featuresTourShown, // getter false // remove when reset ) +// TODO - This would be better named "telemetryEnabled", but as we already +// shipped with it called gleanEnabled it's non-trivial to change +// the name. https://github.com/mozilla-mobile/mozilla-vpn-client/issues/2050 SETTING_BOOL(gleanEnabled, // getter setGleanEnabled, // setter hasGleanEnabled, // has diff --git a/src/src.pro b/src/src.pro index ca1f89defb..194a9a4aed 100644 --- a/src/src.pro +++ b/src/src.pro @@ -22,8 +22,9 @@ QT += quick QT += widgets QT += charts QT += websockets +QT += sql -# for the inspector +# For the inspector QT+= testlib QT.testlib.CONFIG -= console CONFIG += no_testcase_installs @@ -207,7 +208,6 @@ HEADERS += \ features/featureappreview.h \ features/featurecaptiveportal.h \ features/featurecustomdns.h \ - features/featureglean.h \ features/featureinappaccountCreate.h \ features/featureinappauth.h \ features/featureinapppurchase.h \ @@ -901,6 +901,8 @@ else:wasm { TARGET = mozillavpn QT += svg + # sql not available for wasm. + QT -= sql CONFIG += c++1z @@ -962,13 +964,18 @@ QMAKE_LRELEASE_FLAGS += -idbased CONFIG += lrelease CONFIG += embed_translations -debug { - SOURCES += gleantest.cpp - HEADERS += gleantest.h -} - coverage { message(Coverage enabled) QMAKE_CXXFLAGS += -fprofile-instr-generate -fcoverage-mapping QMAKE_LFLAGS += -fprofile-instr-generate -fcoverage-mapping } + +debug { + # If in debug mode, set mvpn_debug flag too. + CONFIG += mvpn_debug +} + +mvpn_debug { + message(MVPN Debug enabled) + DEFINES += MVPN_DEBUG +} diff --git a/src/ui/components/VPNConnectionStability.qml b/src/ui/components/VPNConnectionStability.qml index d0d80b06f4..e1adf9285b 100644 --- a/src/ui/components/VPNConnectionStability.qml +++ b/src/ui/components/VPNConnectionStability.qml @@ -8,8 +8,8 @@ import QtQuick.Layouts 1.14 import Mozilla.VPN 1.0 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { property var gridFlow: grid.flow diff --git a/src/ui/components/VPNControllerView.qml b/src/ui/components/VPNControllerView.qml index 52e467320f..4291df006c 100644 --- a/src/ui/components/VPNControllerView.qml +++ b/src/ui/components/VPNControllerView.qml @@ -9,8 +9,8 @@ import QtQuick.Layouts 1.14 import Mozilla.VPN 1.0 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { id: box diff --git a/src/ui/components/VPNToggleCard.qml b/src/ui/components/VPNToggleCard.qml index 14bd9d403f..e12befe1e7 100644 --- a/src/ui/components/VPNToggleCard.qml +++ b/src/ui/components/VPNToggleCard.qml @@ -10,8 +10,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { property alias labelText: label.text diff --git a/src/ui/components/VPNViewDNSSettings.qml b/src/ui/components/VPNViewDNSSettings.qml index 33305d0884..d61d17b146 100644 --- a/src/ui/components/VPNViewDNSSettings.qml +++ b/src/ui/components/VPNViewDNSSettings.qml @@ -11,8 +11,8 @@ import components 0.1 import components.forms 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNFlickable { diff --git a/src/ui/developerMenu/ViewFeatureList.qml b/src/ui/developerMenu/ViewFeatureList.qml index f15f64a129..3664a0715d 100644 --- a/src/ui/developerMenu/ViewFeatureList.qml +++ b/src/ui/developerMenu/ViewFeatureList.qml @@ -10,8 +10,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { diff --git a/src/ui/main.qml b/src/ui/main.qml index 28c013b438..ec6aef394a 100644 --- a/src/ui/main.qml +++ b/src/ui/main.qml @@ -7,12 +7,12 @@ import QtQuick.Controls 2.14 import QtQuick.Window 2.12 import Mozilla.VPN 1.0 +import compat 0.1 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import compat 0.1 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Window { id: window @@ -57,42 +57,13 @@ Window { if (VPN.startMinimized) { this.showMinimized(); } - if (!fullscreenRequired()) { - maximumHeight = Theme.desktopAppHeight - minimumHeight = Theme.desktopAppHeight - maximumWidth = Theme.desktopAppWidth - minimumWidth = Theme.desktopAppWidth + maximumHeight = Theme.desktopAppHeight; + minimumHeight = Theme.desktopAppHeight; + maximumWidth = Theme.desktopAppWidth; + minimumWidth = Theme.desktopAppWidth; } - - Glean.initialize('MozillaVPN', VPNSettings.gleanEnabled && VPNFeatureList.get("glean").isSupported, { - appBuild: `MozillaVPN/${VPN.versionString}`, - appDisplayVersion: VPN.versionString, - httpClient: { - post(url, body, headers) { - if (typeof(VPNGleanTest) !== "undefined") { - VPNGleanTest.requestDone(url, body); - } - if (VPN.stagingMode) { - return Promise.reject('Glean disabled in staging mode'); - } - - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.open("POST", url); - - for (const header in headers) { - xhr.setRequestHeader(header, headers[header]); - } - xhr.onloadend = () => { - resolve({status: xhr.status, result: 2 /* UploadResultStatus.Success */ }); - } - xhr.send(body); - - }); - } - } - }); + VPN.mainWindowLoaded() } MouseArea { @@ -314,29 +285,49 @@ Window { mainStackView.push("qrc:/ui/platforms/android/androidauthenticationview.qml", StackView.Immediate) } - function onSendGleanPings() { - if (VPNSettings.gleanEnabled && VPNFeatureList.get("glean").isSupported) { - Pings.main.submit(); + function onInitializeGlean() { + var debug = {}; + if (VPN.debugMode) { + console.debug("Initializing glean with debug mode"); + debug = { + logPings: true, + debugViewTag: "MozillaVPN" + }; } + var channel = VPN.stagingMode ? "staging" : "production"; + console.debug("Initializing glean with channel set to:", channel); + Glean.initialize("mozillavpn", VPNSettings.gleanEnabled, { + appBuild: "MozillaVPN/" + VPN.versionString, + appDisplayVersion: VPN.versionString, + channel: channel, + debug: debug, + osVersion: VPN.osVersion, + architecture: VPN.architecture, + }); } - function onTriggerGleanSample(sample) { + function onSendGleanPings() { + console.debug("sending Glean pings"); + Pings.main.submit(); + } + + function onRecordGleanEvent(sample) { + console.debug("recording Glean event"); Sample[sample].record(); } function onAboutToQuit() { - // We are about to quit. Let's see if we are fast enough to send - // the last chunck of data to the glean servers. - if (VPNSettings.gleanEnabled && VPNFeatureList.get("glean").isSupported) { - Pings.main.submit(); - } + console.debug("about to quit, shutdown Glean"); + // Use glean's built-in shutdown method - https://mozilla.github.io/glean/book/reference/general/shutdown.html + Glean.shutdown(); } } Connections { target: VPNSettings function onGleanEnabledChanged() { - Glean.setUploadEnabled(VPNSettings.gleanEnabled && VPNFeatureList.get("glean").isSupported); + console.debug("Glean - onGleanEnabledChanged", VPNSettings.gleanEnabled); + Glean.setUploadEnabled(VPNSettings.gleanEnabled); } } diff --git a/src/ui/settings/ViewAdvancedDNSSettings.qml b/src/ui/settings/ViewAdvancedDNSSettings.qml index 4ef350e141..3e7d1ca432 100644 --- a/src/ui/settings/ViewAdvancedDNSSettings.qml +++ b/src/ui/settings/ViewAdvancedDNSSettings.qml @@ -11,8 +11,8 @@ import components 0.1 import components.forms 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { diff --git a/src/ui/settings/ViewAppPermissions.qml b/src/ui/settings/ViewAppPermissions.qml index 42c17a2bfe..e913aef3ac 100644 --- a/src/ui/settings/ViewAppPermissions.qml +++ b/src/ui/settings/ViewAppPermissions.qml @@ -10,8 +10,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { diff --git a/src/ui/settings/ViewNetworkSettings.qml b/src/ui/settings/ViewNetworkSettings.qml index 32e485462d..e93453e3b3 100644 --- a/src/ui/settings/ViewNetworkSettings.qml +++ b/src/ui/settings/ViewNetworkSettings.qml @@ -10,8 +10,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { diff --git a/src/ui/settings/ViewNotifications.qml b/src/ui/settings/ViewNotifications.qml index 5cd913dd51..fdb16b5a8c 100644 --- a/src/ui/settings/ViewNotifications.qml +++ b/src/ui/settings/ViewNotifications.qml @@ -10,8 +10,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { id: root diff --git a/src/ui/settings/ViewSettingsMenu.qml b/src/ui/settings/ViewSettingsMenu.qml index 75efa82b4c..161dc3991f 100644 --- a/src/ui/settings/ViewSettingsMenu.qml +++ b/src/ui/settings/ViewSettingsMenu.qml @@ -10,8 +10,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNFlickable { id: vpnFlickable diff --git a/src/ui/states/StateBackendFailure.qml b/src/ui/states/StateBackendFailure.qml index 46e6e6024c..6513c1c1c2 100644 --- a/src/ui/states/StateBackendFailure.qml +++ b/src/ui/states/StateBackendFailure.qml @@ -8,8 +8,8 @@ import QtQuick.Controls 2.14 import Mozilla.VPN 1.0 import components 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNStackView { diff --git a/src/ui/states/StateBillingNotAvailable.qml b/src/ui/states/StateBillingNotAvailable.qml index 9e21620821..532a152d09 100644 --- a/src/ui/states/StateBillingNotAvailable.qml +++ b/src/ui/states/StateBillingNotAvailable.qml @@ -9,8 +9,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNStackView { id: stackview diff --git a/src/ui/states/StateSubscriptionBlocked.qml b/src/ui/states/StateSubscriptionBlocked.qml index 4dc93b79b1..2a7099470a 100644 --- a/src/ui/states/StateSubscriptionBlocked.qml +++ b/src/ui/states/StateSubscriptionBlocked.qml @@ -9,8 +9,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNStackView { id: stackview diff --git a/src/ui/states/StateSubscriptionNotValidated.qml b/src/ui/states/StateSubscriptionNotValidated.qml index 1cc613a247..3b74f33ef5 100644 --- a/src/ui/states/StateSubscriptionNotValidated.qml +++ b/src/ui/states/StateSubscriptionNotValidated.qml @@ -9,8 +9,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNStackView { id: stackview diff --git a/src/ui/views/ViewErrorFullScreen.qml b/src/ui/views/ViewErrorFullScreen.qml index 73beb5c55e..1081508018 100644 --- a/src/ui/views/ViewErrorFullScreen.qml +++ b/src/ui/views/ViewErrorFullScreen.qml @@ -9,8 +9,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 VPNFlickable { property var headlineText diff --git a/src/ui/views/ViewInitialize.qml b/src/ui/views/ViewInitialize.qml index 05ccec29ab..1eff0a53e8 100644 --- a/src/ui/views/ViewInitialize.qml +++ b/src/ui/views/ViewInitialize.qml @@ -8,8 +8,8 @@ import Mozilla.VPN 1.0 import components 0.1 import themes 0.1 -import org.mozilla.Glean 0.15 -import telemetry 0.15 +import org.mozilla.Glean 0.23 +import telemetry 0.23 Item { diff --git a/src/wgquickprocess.cpp b/src/wgquickprocess.cpp index 96e2d48610..232591f6c5 100644 --- a/src/wgquickprocess.cpp +++ b/src/wgquickprocess.cpp @@ -94,7 +94,7 @@ bool WgQuickProcess::createConfigFile(const QString& outputFile, out << "AllowedIPs = " << ranges.join(", ") << "\n"; #endif -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG logger.debug() << content; #endif diff --git a/tests/auth/auth.pro b/tests/auth/auth.pro index 7c48711b29..8587e097cc 100644 --- a/tests/auth/auth.pro +++ b/tests/auth/auth.pro @@ -15,7 +15,7 @@ DEFINES += QT_DEPRECATED_WARNINGS DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050F00 DEFINES += UNIT_TEST -config += debug +config += mvpn_debug TEMPLATE = app TARGET = tests diff --git a/tests/auth/main.cpp b/tests/auth/main.cpp index 8e625063ea..1788df591d 100644 --- a/tests/auth/main.cpp +++ b/tests/auth/main.cpp @@ -17,7 +17,7 @@ #include int main(int argc, char* argv[]) { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG LeakDetector leakDetector; Q_UNUSED(leakDetector); #endif diff --git a/tests/auth/mocmozillavpn.cpp b/tests/auth/mocmozillavpn.cpp index 6fb1bbfb04..9e3e124383 100644 --- a/tests/auth/mocmozillavpn.cpp +++ b/tests/auth/mocmozillavpn.cpp @@ -26,6 +26,7 @@ MozillaVPN::~MozillaVPN() {} MozillaVPN::State MozillaVPN::state() const { return StateInitialize; } bool MozillaVPN::stagingMode() const { return true; } +bool MozillaVPN::debugMode() const { return true; } void MozillaVPN::initialize() {} @@ -84,6 +85,8 @@ void MozillaVPN::changeServer(const QString&, const QString&, const QString&, void MozillaVPN::postAuthenticationCompleted() {} +void MozillaVPN::mainWindowLoaded() {} + void MozillaVPN::telemetryPolicyCompleted() {} void MozillaVPN::setUpdateRecommended(bool) {} diff --git a/tests/functional/helper.js b/tests/functional/helper.js index 5c0283a033..5a41c948c2 100644 --- a/tests/functional/helper.js +++ b/tests/functional/helper.js @@ -176,6 +176,10 @@ module.exports = { } }, + async setGleanAutomationHeader() { + await this._writeCommand('set_glean_automation_header'); + }, + async getLastGleanRequest() { const json = await this._writeCommand('last_glean_request'); assert( diff --git a/tests/functional/testGlean.js b/tests/functional/testGlean.js index 7e54d657cc..d29f10a406 100644 --- a/tests/functional/testGlean.js +++ b/tests/functional/testGlean.js @@ -3,94 +3,51 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const assert = require('assert'); -const fs = require('fs'); -const util = require('util'); const vpn = require('./helper.js'); describe('Glean event logging', function() { - this.timeout(60000); + this.timeout(5000); before(async () => { await vpn.connect(); }); - beforeEach(() => {}); - - afterEach(vpn.dumpFailure); - - after(async () => { - vpn.disconnect(); + beforeEach(async () => { + await vpn.reset(); + await vpn.setGleanAutomationHeader(); }); - it('reset the app', async () => await vpn.reset()); - - it('initial view', async () => { - const glean = await vpn.getLastGleanRequest(); - assert('url' in glean); - assert(glean.url === ''); - assert('data' in glean); - assert(glean.data === ''); - - await vpn.waitForElement('getHelpLink'); - await vpn.waitForElementProperty('getHelpLink', 'visible', 'true'); - await vpn.wait(); + afterEach(async () => { + await vpn.dumpFailure; }); - it('Start and abort the authentication (logging)', async () => { - await vpn.setSetting('glean-enabled', true); - await vpn.wait(); - - await vpn.authenticate(); - - await vpn.waitForElement('postAuthenticationButton'); - await vpn.clickOnElement('postAuthenticationButton'); - await vpn.wait(); - - await vpn.waitForElement('telemetryPolicyButton'); - await vpn.waitForElementProperty( - 'telemetryPolicyButton', 'visible', 'true'); - await vpn.clickOnElement('telemetryPolicyButton'); - - await vpn.wait(); - - await vpn.waitForCondition(async () => { - let glean = await vpn.getLastGleanRequest(); - if (glean.url === '') return false; - - assert(glean.url !== ''); - assert(glean.data !== ''); - return true; - }); - - glean = await vpn.getLastGleanRequest(); - assert(glean.url === ''); - assert(glean.data === ''); - }); - - it('reset the app', async () => await vpn.reset()); - - it('Start and abort the authentication (no logging)', async () => { - let glean = await vpn.getLastGleanRequest(); - if (glean.url !== '') { - assert(glean.url.includes('deletion-request')); - } - - await vpn.authenticate(); - - await vpn.waitForElement('postAuthenticationButton'); - await vpn.clickOnElement('postAuthenticationButton'); - await vpn.wait(); - - await vpn.waitForElement('telemetryPolicyButton'); - await vpn.waitForElementProperty( - 'telemetryPolicyButton', 'visible', 'true'); - await vpn.clickOnElement('declineTelemetryLink'); - - await vpn.wait(); - - glean = await vpn.getLastGleanRequest(); - assert(glean.url.includes('deletion-request')); - }); - - it('quit the app', async () => await vpn.quit()); + after(async () => { + await vpn.quit(); + vpn.disconnect(); + }) + + /* + (UPDATE THIS BASED ON NEW TELEMETRY PAGE POSITION) + + I would like to do the following suite of tests: + * set debugMode to true/false, ensure glean is initialized appropriately + * set stagingMode to true/false, ensure glean is initialized appropriately + * change VPNSettings.glean-enabled and ensure glean upload enabled has been + changed + * make an event and ensure the right data is in the ping + * ensure that a call to sendPings, sends pings + * ensure Glean.shutdown is called onAboutToQuit + + Update the testTelemetryView tests to + * ensure that "Accept telemetry" and "Reject telemetry" result in the correct + glean state change + + Possible c++ unit tests: + * ensure that timer to sendPings is setup + + We do not need to test that glean does the right thing e.g. testing for a + deletion request. As long as we check that we've told glean to + setUploadEnabled to false, then its handling of that is a glean implementation + detail and glean tests their own code. + */ }); diff --git a/tests/unit/main.cpp b/tests/unit/main.cpp index 996aa7c153..90cd4c2aed 100644 --- a/tests/unit/main.cpp +++ b/tests/unit/main.cpp @@ -15,7 +15,7 @@ QVector TestHelper::testList; TestHelper::TestHelper() { testList.append(this); } int main(int argc, char* argv[]) { -#ifdef QT_DEBUG +#ifdef MVPN_DEBUG LeakDetector leakDetector; Q_UNUSED(leakDetector); #endif diff --git a/tests/unit/mocmozillavpn.cpp b/tests/unit/mocmozillavpn.cpp index d09760da50..a39cdd4b7d 100644 --- a/tests/unit/mocmozillavpn.cpp +++ b/tests/unit/mocmozillavpn.cpp @@ -28,6 +28,7 @@ MozillaVPN::~MozillaVPN() {} MozillaVPN::State MozillaVPN::state() const { return TestHelper::vpnState; } bool MozillaVPN::stagingMode() const { return true; } +bool MozillaVPN::debugMode() const { return true; } void MozillaVPN::initialize() {} @@ -86,6 +87,8 @@ void MozillaVPN::changeServer(const QString&, const QString&, const QString&, void MozillaVPN::postAuthenticationCompleted() {} +void MozillaVPN::mainWindowLoaded() {} + void MozillaVPN::telemetryPolicyCompleted() {} void MozillaVPN::setUpdateRecommended(bool) {}